1
//----------------------------------------------------------------------------
2
// Copyright (C) 2008-2011 The IPython Development Team
4
// Distributed under the terms of the BSD License. The full license is in
5
// the file COPYING, distributed as part of this software.
6
//----------------------------------------------------------------------------
8
//============================================================================
10
//============================================================================
12
var IPython = (function (IPython) {
14
var utils = IPython.utils;
16
var Notebook = function (selector) {
17
this.read_only = IPython.read_only;
18
this.element = $(selector);
19
this.element.scroll();
20
this.element.data("notebook", this);
21
this.next_prompt_number = 1;
23
this.clipboard = null;
24
this.paste_enabled = false;
26
this.msg_cell_map = {};
28
this.control_key_active = false;
29
this.notebook_id = null;
30
this.notebook_name = null;
31
this.notebook_name_blacklist_re = /[\/\\]/;
32
this.nbformat = 3 // Increment this when changing the nbformat
34
this.create_elements();
36
this.set_tooltipontab(true);
37
this.set_smartcompleter(true);
38
this.set_timebeforetooltip(1200);
42
Notebook.prototype.style = function () {
43
$('div#notebook').addClass('border-box-sizing');
47
Notebook.prototype.create_elements = function () {
48
// We add this end_space div to the end of the notebook div to:
49
// i) provide a margin between the last cell and the end of the notebook
50
// ii) to prevent the div from scrolling up when the last cell is being
51
// edited, but is too low on the page, which browsers will do automatically.
53
var end_space = $('<div/>').addClass('end_space').height("30%");
54
end_space.dblclick(function (e) {
55
if (that.read_only) return;
56
var ncells = that.ncells();
57
that.insert_cell_below('code',ncells-1);
59
this.element.append(end_space);
60
$('div#notebook').addClass('border-box-sizing');
64
Notebook.prototype.bind_events = function () {
66
$(document).keydown(function (event) {
67
// console.log(event);
68
if (that.read_only) return true;
70
// Save (CTRL+S) or (AppleKey+S)
71
//metaKey = applekey on mac
72
if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
74
event.preventDefault();
76
} else if (event.which === 27) {
77
// Intercept escape at highest level to avoid closing
78
// websocket connection with firefox
79
event.preventDefault();
81
if (event.which === 38 && !event.shiftKey) {
82
var cell = that.get_selected_cell();
84
event.preventDefault();
87
} else if (event.which === 40 && !event.shiftKey) {
88
var cell = that.get_selected_cell();
89
if (cell.at_bottom()) {
90
event.preventDefault();
93
} else if (event.which === 13 && event.shiftKey) {
94
that.execute_selected_cell();
96
} else if (event.which === 13 && event.ctrlKey) {
97
that.execute_selected_cell({terminal:true});
99
} else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
100
that.control_key_active = true;
102
} else if (event.which === 88 && that.control_key_active) {
103
// Cut selected cell = x
105
that.control_key_active = false;
107
} else if (event.which === 67 && that.control_key_active) {
108
// Copy selected cell = c
110
that.control_key_active = false;
112
} else if (event.which === 86 && that.control_key_active) {
113
// Paste selected cell = v
115
that.control_key_active = false;
117
} else if (event.which === 68 && that.control_key_active) {
118
// Delete selected cell = d
120
that.control_key_active = false;
122
} else if (event.which === 65 && that.control_key_active) {
123
// Insert code cell above selected = a
124
that.insert_cell_above('code');
125
that.control_key_active = false;
127
} else if (event.which === 66 && that.control_key_active) {
128
// Insert code cell below selected = b
129
that.insert_cell_below('code');
130
that.control_key_active = false;
132
} else if (event.which === 89 && that.control_key_active) {
135
that.control_key_active = false;
137
} else if (event.which === 77 && that.control_key_active) {
140
that.control_key_active = false;
142
} else if (event.which === 84 && that.control_key_active) {
145
that.control_key_active = false;
147
} else if (event.which === 49 && that.control_key_active) {
149
that.to_heading(undefined, 1);
150
that.control_key_active = false;
152
} else if (event.which === 50 && that.control_key_active) {
154
that.to_heading(undefined, 2);
155
that.control_key_active = false;
157
} else if (event.which === 51 && that.control_key_active) {
159
that.to_heading(undefined, 3);
160
that.control_key_active = false;
162
} else if (event.which === 52 && that.control_key_active) {
164
that.to_heading(undefined, 4);
165
that.control_key_active = false;
167
} else if (event.which === 53 && that.control_key_active) {
169
that.to_heading(undefined, 5);
170
that.control_key_active = false;
172
} else if (event.which === 54 && that.control_key_active) {
174
that.to_heading(undefined, 6);
175
that.control_key_active = false;
177
} else if (event.which === 79 && that.control_key_active) {
179
that.toggle_output();
180
that.control_key_active = false;
182
} else if (event.which === 83 && that.control_key_active) {
184
that.save_notebook();
185
that.control_key_active = false;
187
} else if (event.which === 74 && that.control_key_active) {
188
// Move cell down = j
189
that.move_cell_down();
190
that.control_key_active = false;
192
} else if (event.which === 75 && that.control_key_active) {
195
that.control_key_active = false;
197
} else if (event.which === 80 && that.control_key_active) {
198
// Select previous = p
200
that.control_key_active = false;
202
} else if (event.which === 78 && that.control_key_active) {
205
that.control_key_active = false;
207
} else if (event.which === 76 && that.control_key_active) {
208
// Toggle line numbers = l
209
that.cell_toggle_line_numbers();
210
that.control_key_active = false;
212
} else if (event.which === 73 && that.control_key_active) {
213
// Interrupt kernel = i
214
that.kernel.interrupt();
215
that.control_key_active = false;
217
} else if (event.which === 190 && that.control_key_active) {
218
// Restart kernel = . # matches qt console
219
that.restart_kernel();
220
that.control_key_active = false;
222
} else if (event.which === 72 && that.control_key_active) {
223
// Show keyboard shortcuts = h
224
IPython.quick_help.show_keyboard_shortcuts();
225
that.control_key_active = false;
227
} else if (that.control_key_active) {
228
that.control_key_active = false;
234
this.element.bind('collapse_pager', function () {
235
var app_height = $('div#main_app').height(); // content height
236
var splitter_height = $('div#pager_splitter').outerHeight(true);
237
var new_height = app_height - splitter_height;
238
that.element.animate({height : new_height + 'px'}, 'fast');
241
this.element.bind('expand_pager', function () {
242
var app_height = $('div#main_app').height(); // content height
243
var splitter_height = $('div#pager_splitter').outerHeight(true);
244
var pager_height = $('div#pager').outerHeight(true);
245
var new_height = app_height - pager_height - splitter_height;
246
that.element.animate({height : new_height + 'px'}, 'fast');
249
$(window).bind('beforeunload', function () {
250
// TODO: Make killing the kernel configurable.
251
var kill_kernel = false;
255
if (that.dirty && ! that.read_only) {
256
return "You have unsaved changes that will be lost if you leave this page.";
258
// Null is the *only* return value that will make the browser not
259
// pop up the "don't leave" dialog.
265
Notebook.prototype.scroll_to_bottom = function () {
266
this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
270
Notebook.prototype.scroll_to_top = function () {
271
this.element.animate({scrollTop:0}, 0);
275
// Cell indexing, retrieval, etc.
277
Notebook.prototype.get_cell_elements = function () {
278
return this.element.children("div.cell");
282
Notebook.prototype.get_cell_element = function (index) {
284
var e = this.get_cell_elements().eq(index);
285
if (e.length !== 0) {
292
Notebook.prototype.ncells = function (cell) {
293
return this.get_cell_elements().length;
297
// TODO: we are often calling cells as cells()[i], which we should optimize
298
// to cells(i) or a new method.
299
Notebook.prototype.get_cells = function () {
300
return this.get_cell_elements().toArray().map(function (e) {
301
return $(e).data("cell");
306
Notebook.prototype.get_cell = function (index) {
308
var ce = this.get_cell_element(index);
310
result = ce.data('cell');
316
Notebook.prototype.get_next_cell = function (cell) {
318
var index = this.find_cell_index(cell);
319
if (index !== null && index < this.ncells()) {
320
result = this.get_cell(index+1);
326
Notebook.prototype.get_prev_cell = function (cell) {
328
var index = this.find_cell_index(cell);
329
if (index !== null && index > 1) {
330
result = this.get_cell(index-1);
335
Notebook.prototype.find_cell_index = function (cell) {
337
this.get_cell_elements().filter(function (index) {
338
if ($(this).data("cell") === cell) {
346
Notebook.prototype.index_or_selected = function (index) {
348
if (index === undefined || index === null) {
349
i = this.get_selected_index();
360
Notebook.prototype.get_selected_cell = function () {
361
var index = this.get_selected_index();
362
return this.get_cell(index);
366
Notebook.prototype.is_valid_cell_index = function (index) {
367
if (index !== null && index >= 0 && index < this.ncells()) {
374
Notebook.prototype.get_selected_index = function () {
376
this.get_cell_elements().filter(function (index) {
377
if ($(this).data("cell").selected === true) {
385
Notebook.prototype.cell_for_msg = function (msg_id) {
386
var cell_id = this.msg_cell_map[msg_id];
388
this.get_cell_elements().filter(function (index) {
389
cell = $(this).data("cell");
390
if (cell.cell_id === cell_id) {
400
Notebook.prototype.select = function (index) {
401
if (index !== undefined && index >= 0 && index < this.ncells()) {
402
sindex = this.get_selected_index()
403
if (sindex !== null && index !== sindex) {
404
this.get_cell(sindex).unselect();
406
var cell = this.get_cell(index)
408
if (cell.cell_type === 'heading') {
409
$([IPython.events]).trigger('selected_cell_type_changed.Notebook',
410
{'cell_type':cell.cell_type,level:cell.level}
413
$([IPython.events]).trigger('selected_cell_type_changed.Notebook',
414
{'cell_type':cell.cell_type}
422
Notebook.prototype.select_next = function () {
423
var index = this.get_selected_index();
424
if (index !== null && index >= 0 && (index+1) < this.ncells()) {
425
this.select(index+1);
431
Notebook.prototype.select_prev = function () {
432
var index = this.get_selected_index();
433
if (index !== null && index >= 0 && (index-1) < this.ncells()) {
434
this.select(index-1);
442
Notebook.prototype.move_cell_up = function (index) {
443
var i = this.index_or_selected();
444
if (i !== null && i < this.ncells() && i > 0) {
445
var pivot = this.get_cell_element(i-1);
446
var tomove = this.get_cell_element(i);
447
if (pivot !== null && tomove !== null) {
449
pivot.before(tomove);
458
Notebook.prototype.move_cell_down = function (index) {
459
var i = this.index_or_selected();
460
if (i !== null && i < (this.ncells()-1) && i >= 0) {
461
var pivot = this.get_cell_element(i+1);
462
var tomove = this.get_cell_element(i);
463
if (pivot !== null && tomove !== null) {
474
Notebook.prototype.sort_cells = function () {
475
// This is not working right now. Calling this will actually crash
476
// the browser. I think there is an infinite loop in here...
477
var ncells = this.ncells();
478
var sindex = this.get_selected_index();
482
for (var i=1; i<ncells; i++) {
483
current = this.get_cell(i);
484
previous = this.get_cell(i-1);
485
if (previous.input_prompt_number > current.input_prompt_number) {
486
this.move_cell_up(i);
495
// Insertion, deletion.
497
Notebook.prototype.delete_cell = function (index) {
498
var i = this.index_or_selected(index);
499
if (this.is_valid_cell_index(i)) {
500
var ce = this.get_cell_element(i);
502
if (i === (this.ncells())) {
513
Notebook.prototype.insert_cell_below = function (type, index) {
514
// type = ('code','html','markdown')
515
// index = cell index or undefined to insert below selected
516
index = this.index_or_selected(index);
518
if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
519
if (type === 'code') {
520
cell = new IPython.CodeCell(this);
521
cell.set_input_prompt();
522
} else if (type === 'markdown') {
523
cell = new IPython.MarkdownCell(this);
524
} else if (type === 'html') {
525
cell = new IPython.HTMLCell(this);
526
} else if (type === 'plaintext') {
527
cell = new IPython.PlaintextCell(this);
528
} else if (type === 'heading') {
529
cell = new IPython.HeadingCell(this);
532
if (this.ncells() === 0) {
533
this.element.find('div.end_space').before(cell.element);
534
} else if (this.is_valid_cell_index(index)) {
535
this.get_cell_element(index).after(cell.element);
538
this.select(this.find_cell_index(cell));
547
Notebook.prototype.insert_cell_above = function (type, index) {
548
// type = ('code','html','markdown')
549
// index = cell index or undefined to insert above selected
550
index = this.index_or_selected(index);
552
if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
553
if (type === 'code') {
554
cell = new IPython.CodeCell(this);
555
cell.set_input_prompt();
556
} else if (type === 'markdown') {
557
cell = new IPython.MarkdownCell(this);
558
} else if (type === 'html') {
559
cell = new IPython.HTMLCell(this);
560
} else if (type === 'plaintext') {
561
cell = new IPython.PlaintextCell(this);
562
} else if (type === 'heading') {
563
cell = new IPython.HeadingCell(this);
566
if (this.ncells() === 0) {
567
this.element.find('div.end_space').before(cell.element);
568
} else if (this.is_valid_cell_index(index)) {
569
this.get_cell_element(index).before(cell.element);
572
this.select(this.find_cell_index(cell));
581
Notebook.prototype.to_code = function (index) {
582
var i = this.index_or_selected(index);
583
if (this.is_valid_cell_index(i)) {
584
var source_element = this.get_cell_element(i);
585
var source_cell = source_element.data("cell");
586
if (!(source_cell instanceof IPython.CodeCell)) {
587
target_cell = this.insert_cell_below('code',i);
588
var text = source_cell.get_text();
589
if (text === source_cell.placeholder) {
592
target_cell.set_text(text);
593
source_element.remove();
600
Notebook.prototype.to_markdown = function (index) {
601
var i = this.index_or_selected(index);
602
if (this.is_valid_cell_index(i)) {
603
var source_element = this.get_cell_element(i);
604
var source_cell = source_element.data("cell");
605
if (!(source_cell instanceof IPython.MarkdownCell)) {
606
target_cell = this.insert_cell_below('markdown',i);
607
var text = source_cell.get_text();
608
if (text === source_cell.placeholder) {
611
// The edit must come before the set_text.
613
target_cell.set_text(text);
614
source_element.remove();
621
Notebook.prototype.to_html = function (index) {
622
var i = this.index_or_selected(index);
623
if (this.is_valid_cell_index(i)) {
624
var source_element = this.get_cell_element(i);
625
var source_cell = source_element.data("cell");
626
var target_cell = null;
627
if (!(source_cell instanceof IPython.HTMLCell)) {
628
target_cell = this.insert_cell_below('html',i);
629
var text = source_cell.get_text();
630
if (text === source_cell.placeholder) {
633
// The edit must come before the set_text.
635
target_cell.set_text(text);
636
source_element.remove();
643
Notebook.prototype.to_plaintext = function (index) {
644
var i = this.index_or_selected(index);
645
if (this.is_valid_cell_index(i)) {
646
var source_element = this.get_cell_element(i);
647
var source_cell = source_element.data("cell");
648
var target_cell = null;
649
if (!(source_cell instanceof IPython.PlaintextCell)) {
650
target_cell = this.insert_cell_below('plaintext',i);
651
var text = source_cell.get_text();
652
if (text === source_cell.placeholder) {
655
// The edit must come before the set_text.
657
target_cell.set_text(text);
658
source_element.remove();
665
Notebook.prototype.to_heading = function (index, level) {
667
var i = this.index_or_selected(index);
668
if (this.is_valid_cell_index(i)) {
669
var source_element = this.get_cell_element(i);
670
var source_cell = source_element.data("cell");
671
var target_cell = null;
672
if (source_cell instanceof IPython.HeadingCell) {
673
source_cell.set_level(level);
675
target_cell = this.insert_cell_below('heading',i);
676
var text = source_cell.get_text();
677
if (text === source_cell.placeholder) {
680
// The edit must come before the set_text.
681
target_cell.set_level(level);
683
target_cell.set_text(text);
684
source_element.remove();
687
$([IPython.events]).trigger('selected_cell_type_changed.Notebook',
688
{'cell_type':'heading',level:level}
696
Notebook.prototype.enable_paste = function () {
698
if (!this.paste_enabled) {
699
$('#paste_cell').removeClass('ui-state-disabled')
700
.on('click', function () {that.paste_cell();});
701
$('#paste_cell_above').removeClass('ui-state-disabled')
702
.on('click', function () {that.paste_cell_above();});
703
$('#paste_cell_below').removeClass('ui-state-disabled')
704
.on('click', function () {that.paste_cell_below();});
705
this.paste_enabled = true;
710
Notebook.prototype.disable_paste = function () {
711
if (this.paste_enabled) {
712
$('#paste_cell').addClass('ui-state-disabled').off('click');
713
$('#paste_cell_above').addClass('ui-state-disabled').off('click');
714
$('#paste_cell_below').addClass('ui-state-disabled').off('click');
715
this.paste_enabled = false;
720
Notebook.prototype.cut_cell = function () {
725
Notebook.prototype.copy_cell = function () {
726
var cell = this.get_selected_cell();
727
this.clipboard = cell.toJSON();
732
Notebook.prototype.paste_cell = function () {
733
if (this.clipboard !== null && this.paste_enabled) {
734
var cell_data = this.clipboard;
735
var new_cell = this.insert_cell_above(cell_data.cell_type);
736
new_cell.fromJSON(cell_data);
737
old_cell = this.get_next_cell(new_cell);
738
this.delete_cell(this.find_cell_index(old_cell));
739
this.select(this.find_cell_index(new_cell));
744
Notebook.prototype.paste_cell_above = function () {
745
if (this.clipboard !== null && this.paste_enabled) {
746
var cell_data = this.clipboard;
747
var new_cell = this.insert_cell_above(cell_data.cell_type);
748
new_cell.fromJSON(cell_data);
753
Notebook.prototype.paste_cell_below = function () {
754
if (this.clipboard !== null && this.paste_enabled) {
755
var cell_data = this.clipboard;
756
var new_cell = this.insert_cell_below(cell_data.cell_type);
757
new_cell.fromJSON(cell_data);
764
Notebook.prototype.split_cell = function () {
765
// Todo: implement spliting for other cell types.
766
var cell = this.get_selected_cell();
767
if (cell.is_splittable()) {
768
texta = cell.get_pre_cursor();
769
textb = cell.get_post_cursor();
770
if (cell instanceof IPython.CodeCell) {
771
cell.set_text(texta);
772
var new_cell = this.insert_cell_below('code');
773
new_cell.set_text(textb);
774
} else if (cell instanceof IPython.MarkdownCell) {
775
cell.set_text(texta);
777
var new_cell = this.insert_cell_below('markdown');
778
new_cell.edit(); // editor must be visible to call set_text
779
new_cell.set_text(textb);
781
} else if (cell instanceof IPython.HTMLCell) {
782
cell.set_text(texta);
784
var new_cell = this.insert_cell_below('html');
785
new_cell.edit(); // editor must be visible to call set_text
786
new_cell.set_text(textb);
793
Notebook.prototype.merge_cell_above = function () {
794
var index = this.get_selected_index();
795
var cell = this.get_cell(index);
797
upper_cell = this.get_cell(index-1);
798
upper_text = upper_cell.get_text();
799
text = cell.get_text();
800
if (cell instanceof IPython.CodeCell) {
801
cell.set_text(upper_text+'\n'+text);
802
} else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
804
cell.set_text(upper_text+'\n'+text);
807
this.delete_cell(index-1);
808
this.select(this.find_cell_index(cell));
813
Notebook.prototype.merge_cell_below = function () {
814
var index = this.get_selected_index();
815
var cell = this.get_cell(index);
816
if (index < this.ncells()-1) {
817
lower_cell = this.get_cell(index+1);
818
lower_text = lower_cell.get_text();
819
text = cell.get_text();
820
if (cell instanceof IPython.CodeCell) {
821
cell.set_text(text+'\n'+lower_text);
822
} else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
824
cell.set_text(text+'\n'+lower_text);
827
this.delete_cell(index+1);
828
this.select(this.find_cell_index(cell));
833
// Cell collapsing and output clearing
835
Notebook.prototype.collapse = function (index) {
836
var i = this.index_or_selected(index);
837
this.get_cell(i).collapse();
842
Notebook.prototype.expand = function (index) {
843
var i = this.index_or_selected(index);
844
this.get_cell(i).expand();
849
Notebook.prototype.toggle_output = function (index) {
850
var i = this.index_or_selected(index);
851
this.get_cell(i).toggle_output();
856
Notebook.prototype.set_timebeforetooltip = function (time) {
857
this.time_before_tooltip = time;
861
Notebook.prototype.set_tooltipontab = function (state) {
862
this.tooltip_on_tab = state;
866
Notebook.prototype.set_smartcompleter = function (state) {
867
this.smart_completer = state;
871
Notebook.prototype.clear_all_output = function () {
872
var ncells = this.ncells();
873
var cells = this.get_cells();
874
for (var i=0; i<ncells; i++) {
875
if (cells[i] instanceof IPython.CodeCell) {
876
cells[i].clear_output(true,true,true);
883
// Other cell functions: line numbers, ...
885
Notebook.prototype.cell_toggle_line_numbers = function() {
886
this.get_selected_cell().toggle_line_numbers();
889
// Kernel related things
891
Notebook.prototype.start_kernel = function () {
892
this.kernel = new IPython.Kernel();
893
this.kernel.start(this.notebook_id, $.proxy(this.kernel_started, this));
897
Notebook.prototype.restart_kernel = function () {
899
var dialog = $('<div/>');
900
dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
901
$(document).append(dialog);
905
title: "Restart kernel or continue running?",
908
"Restart": function () {
909
that.kernel.restart($.proxy(that.kernel_started, that));
910
$(this).dialog('close');
912
"Continue running": function () {
913
$(this).dialog('close');
920
Notebook.prototype.kernel_started = function () {
921
console.log("Kernel started: ", this.kernel.kernel_id);
922
this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
923
this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
927
Notebook.prototype.handle_shell_reply = function (e) {
928
reply = $.parseJSON(e.data);
929
var header = reply.header;
930
var content = reply.content;
931
var msg_type = header.msg_type;
932
// console.log(reply);
933
var cell = this.cell_for_msg(reply.parent_header.msg_id);
934
if (msg_type === "execute_reply") {
935
cell.set_input_prompt(content.execution_count);
936
cell.element.removeClass("running");
938
} else if (msg_type === "complete_reply") {
939
cell.finish_completing(content.matched_text, content.matches);
940
} else if (msg_type === "object_info_reply"){
941
//console.log('back from object_info_request : ')
945
cell.finish_tooltip(rep);
948
//console.log("unknown reply:"+msg_type);
950
// when having a rely from object_info_reply,
951
// no payload so no nned to handle it
952
if(typeof(content.payload)!='undefined') {
953
var payload = content.payload || [];
954
this.handle_payload(cell, payload);
959
Notebook.prototype.handle_payload = function (cell, payload) {
960
var l = payload.length;
961
for (var i=0; i<l; i++) {
962
if (payload[i].source === 'IPython.zmq.page.page') {
963
if (payload[i].text.trim() !== '') {
964
IPython.pager.clear();
965
IPython.pager.expand();
966
IPython.pager.append_text(payload[i].text);
968
} else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
969
var index = this.find_cell_index(cell);
970
var new_cell = this.insert_cell_below('code',index);
971
new_cell.set_text(payload[i].text);
978
Notebook.prototype.handle_iopub_reply = function (e) {
979
reply = $.parseJSON(e.data);
980
var content = reply.content;
981
// console.log(reply);
982
var msg_type = reply.header.msg_type;
983
var cell = this.cell_for_msg(reply.parent_header.msg_id);
984
if (msg_type !== 'status' && !cell){
985
// message not from this notebook, but should be attached to a cell
986
// console.log("Received IOPub message not caused by one of my cells");
987
// console.log(reply);
990
var output_types = ['stream','display_data','pyout','pyerr'];
991
if (output_types.indexOf(msg_type) >= 0) {
992
this.handle_output(cell, msg_type, content);
993
} else if (msg_type === 'status') {
994
if (content.execution_state === 'busy') {
995
$([IPython.events]).trigger('status_busy.Kernel');
996
} else if (content.execution_state === 'idle') {
997
$([IPython.events]).trigger('status_idle.Kernel');
998
} else if (content.execution_state === 'dead') {
999
this.handle_status_dead();
1001
} else if (msg_type === 'clear_output') {
1002
cell.clear_output(content.stdout, content.stderr, content.other);
1007
Notebook.prototype.handle_status_dead = function () {
1009
this.kernel.stop_channels();
1010
var dialog = $('<div/>');
1011
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.');
1012
$(document).append(dialog);
1016
title: "Dead kernel",
1018
"Restart": function () {
1019
that.start_kernel();
1020
$(this).dialog('close');
1022
"Continue running": function () {
1023
$(this).dialog('close');
1030
Notebook.prototype.handle_output = function (cell, msg_type, content) {
1032
json.output_type = msg_type;
1033
if (msg_type === "stream") {
1034
json.text = content.data;
1035
json.stream = content.name;
1036
} else if (msg_type === "display_data") {
1037
json = this.convert_mime_types(json, content.data);
1038
} else if (msg_type === "pyout") {
1039
json.prompt_number = content.execution_count;
1040
json = this.convert_mime_types(json, content.data);
1041
} else if (msg_type === "pyerr") {
1042
json.ename = content.ename;
1043
json.evalue = content.evalue;
1044
json.traceback = content.traceback;
1046
// append with dynamic=true
1047
cell.append_output(json, true);
1052
Notebook.prototype.convert_mime_types = function (json, data) {
1053
if (data['text/plain'] !== undefined) {
1054
json.text = data['text/plain'];
1056
if (data['text/html'] !== undefined) {
1057
json.html = data['text/html'];
1059
if (data['image/svg+xml'] !== undefined) {
1060
json.svg = data['image/svg+xml'];
1062
if (data['image/png'] !== undefined) {
1063
json.png = data['image/png'];
1065
if (data['image/jpeg'] !== undefined) {
1066
json.jpeg = data['image/jpeg'];
1068
if (data['text/latex'] !== undefined) {
1069
json.latex = data['text/latex'];
1071
if (data['application/json'] !== undefined) {
1072
json.json = data['application/json'];
1074
if (data['application/javascript'] !== undefined) {
1075
json.javascript = data['application/javascript'];
1081
Notebook.prototype.execute_selected_cell = function (options) {
1082
// add_new: should a new cell be added if we are at the end of the nb
1083
// terminal: execute in terminal mode, which stays in the current cell
1084
default_options = {terminal: false, add_new: true};
1085
$.extend(default_options, options);
1087
var cell = that.get_selected_cell();
1088
var cell_index = that.find_cell_index(cell);
1089
if (cell instanceof IPython.CodeCell) {
1090
cell.clear_output(true, true, true);
1091
cell.set_input_prompt('*');
1092
cell.element.addClass("running");
1093
var code = cell.get_text();
1094
var msg_id = that.kernel.execute(cell.get_text());
1095
that.msg_cell_map[msg_id] = cell.cell_id;
1096
} else if (cell instanceof IPython.HTMLCell) {
1099
if (default_options.terminal) {
1102
if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1103
that.insert_cell_below('code');
1104
// If we are adding a new cell at the end, scroll down to show it.
1105
that.scroll_to_bottom();
1107
that.select(cell_index+1);
1114
Notebook.prototype.execute_all_cells = function () {
1115
var ncells = this.ncells();
1116
for (var i=0; i<ncells; i++) {
1118
this.execute_selected_cell({add_new:false});
1120
this.scroll_to_bottom();
1124
Notebook.prototype.request_tool_tip = function (cell,func) {
1125
// Feel free to shorten this logic if you are better
1127
// basicaly you shoul be able to get xxx.xxx.xxx from
1128
// something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
1129
// remove everything between matchin bracket (need to iterate)
1130
matchBracket = /\([^\(\)]+\)/g;
1132
func = func.replace(matchBracket,"");
1133
while( oldfunc != func )
1136
func = func.replace(matchBracket,"");
1138
// remove everythin after last open bracket
1139
endBracket = /\([^\(]*$/g;
1140
func = func.replace(endBracket,"");
1141
var re = /[a-zA-Z._]+$/g;
1142
var msg_id = this.kernel.object_info_request(re.exec(func));
1143
if(typeof(msg_id)!='undefined'){
1144
this.msg_cell_map[msg_id] = cell.cell_id;
1148
Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
1149
var msg_id = this.kernel.complete(line, cursor_pos);
1150
this.msg_cell_map[msg_id] = cell.cell_id;
1154
// Persistance and loading
1156
Notebook.prototype.get_notebook_id = function () {
1157
return this.notebook_id;
1161
Notebook.prototype.get_notebook_name = function () {
1162
return this.notebook_name;
1166
Notebook.prototype.set_notebook_name = function (name) {
1167
this.notebook_name = name;
1171
Notebook.prototype.test_notebook_name = function (nbname) {
1172
nbname = nbname || '';
1173
if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1181
Notebook.prototype.fromJSON = function (data) {
1182
var ncells = this.ncells();
1184
for (i=0; i<ncells; i++) {
1185
// Always delete cell 0 as they get renumbered as they are deleted.
1186
this.delete_cell(0);
1188
// Save the metadata and name.
1189
this.metadata = data.metadata;
1190
this.notebook_name = data.metadata.name;
1191
// Only handle 1 worksheet for now.
1192
var worksheet = data.worksheets[0];
1193
if (worksheet !== undefined) {
1194
var new_cells = worksheet.cells;
1195
ncells = new_cells.length;
1196
var cell_data = null;
1197
var new_cell = null;
1198
for (i=0; i<ncells; i++) {
1199
cell_data = new_cells[i];
1200
new_cell = this.insert_cell_below(cell_data.cell_type);
1201
new_cell.fromJSON(cell_data);
1207
Notebook.prototype.toJSON = function () {
1208
var cells = this.get_cells();
1209
var ncells = cells.length;
1210
cell_array = new Array(ncells);
1211
for (var i=0; i<ncells; i++) {
1212
cell_array[i] = cells[i].toJSON();
1215
// Only handle 1 worksheet for now.
1216
worksheets : [{cells:cell_array}],
1217
metadata : this.metadata
1222
Notebook.prototype.save_notebook = function () {
1223
// We may want to move the name/id/nbformat logic inside toJSON?
1224
var data = this.toJSON();
1225
data.metadata.name = this.notebook_name;
1226
data.nbformat = this.nbformat;
1227
// We do the call with settings so we can set cache to false.
1229
processData : false,
1232
data : JSON.stringify(data),
1233
headers : {'Content-Type': 'application/json'},
1234
success : $.proxy(this.save_notebook_success,this),
1235
error : $.proxy(this.save_notebook_error,this)
1237
$([IPython.events]).trigger('notebook_saving.Notebook');
1238
var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1239
$.ajax(url, settings);
1243
Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1245
$([IPython.events]).trigger('notebook_saved.Notebook');
1249
Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1250
$([IPython.events]).trigger('notebook_save_failed.Notebook');
1254
Notebook.prototype.load_notebook = function (notebook_id) {
1256
this.notebook_id = notebook_id;
1257
// We do the call with settings so we can set cache to false.
1259
processData : false,
1263
success : $.proxy(this.load_notebook_success,this),
1264
error : $.proxy(this.load_notebook_error,this),
1266
$([IPython.events]).trigger('notebook_loading.Notebook');
1267
var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1268
$.ajax(url, settings);
1272
Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1273
this.fromJSON(data);
1274
if (this.ncells() === 0) {
1275
this.insert_cell_below('code');
1278
if (! this.read_only) {
1279
this.start_kernel();
1282
this.scroll_to_top();
1283
if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1284
msg = "This notebook has been converted from an older " +
1285
"notebook format (v"+data.orig_nbformat+") to the current notebook " +
1286
"format (v"+data.nbformat+"). The next time you save this notebook, the " +
1287
"newer notebook format will be used and older verions of IPython " +
1288
"may not be able to read it. To keep the older version, close the " +
1289
"notebook without saving it.";
1290
var dialog = $('<div/>');
1292
this.element.append(dialog);
1296
title: "Notebook converted",
1298
close: function(event, ui) {$(this).dialog('destroy').remove();},
1301
$(this).dialog('close');
1307
$([IPython.events]).trigger('notebook_loaded.Notebook');
1311
Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1312
if (xhr.status === 500) {
1313
msg = "An error occurred while loading this notebook. Most likely " +
1314
"this notebook is in a newer format than is supported by this " +
1315
"version of IPython. This version can load notebook formats " +
1316
"v"+this.nbformat+" or earlier.";
1317
var dialog = $('<div/>');
1319
this.element.append(dialog);
1323
title: "Error loading notebook",
1325
close: function(event, ui) {$(this).dialog('destroy').remove();},
1328
$(this).dialog('close');
1336
IPython.Notebook = Notebook;