##// END OF EJS Templates
Splitting notebook.js into muliple files for development ease.
Brian E. Granger -
Show More
@@ -0,0 +1,72 b''
1
2 //============================================================================
3 // Cell
4 //============================================================================
5
6
7 var Cell = function (notebook) {
8 this.notebook = notebook;
9 this.selected = false;
10 this.element;
11 this.create_element();
12 if (this.element !== undefined) {
13 this.element.data("cell", this);
14 this.bind_events();
15 }
16 this.cell_id = uuid();
17 };
18
19
20 Cell.prototype.grow = function(element) {
21 // Grow the cell by hand. This is used upon reloading from JSON, when the
22 // autogrow handler is not called.
23 var dom = element.get(0);
24 var lines_count = 0;
25 // modified split rule from
26 // http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424
27 var lines = dom.value.split(/\r|\r\n|\n/);
28 lines_count = lines.length;
29 if (lines_count >= 1) {
30 dom.rows = lines_count;
31 } else {
32 dom.rows = 1;
33 }
34 };
35
36
37 Cell.prototype.select = function () {
38 this.element.addClass('ui-widget-content ui-corner-all');
39 this.selected = true;
40 // TODO: we need t test across browsers to see if both of these are needed.
41 // In the meantime, there should not be any harm in having them both.
42 this.element.find('textarea').trigger('focusin');
43 this.element.find('textarea').trigger('focus');
44 };
45
46
47 Cell.prototype.unselect = function () {
48 this.element.removeClass('ui-widget-content ui-corner-all');
49 this.selected = false;
50 };
51
52
53 Cell.prototype.bind_events = function () {
54 var that = this;
55 var nb = that.notebook
56 that.element.click(function (event) {
57 if (that.selected === false) {
58 nb.select(nb.find_cell_index(that));
59 };
60 });
61 that.element.focusin(function (event) {
62 if (that.selected === false) {
63 nb.select(nb.find_cell_index(that));
64 };
65 });
66 };
67
68
69 // Subclasses must implement create_element.
70 Cell.prototype.create_element = function () {};
71
72
@@ -0,0 +1,195 b''
1
2 //============================================================================
3 // CodeCell
4 //============================================================================
5
6
7 var CodeCell = function (notebook) {
8 this.code_mirror = null;
9 this.input_prompt_number = ' ';
10 Cell.apply(this, arguments);
11 };
12
13
14 CodeCell.prototype = new Cell();
15
16
17 CodeCell.prototype.create_element = function () {
18 var cell = $('<div></div>').addClass('cell code_cell vbox border-box-sizing');
19 var input = $('<div></div>').addClass('input hbox border-box-sizing');
20 input.append($('<div/>').addClass('prompt input_prompt monospace-font'));
21 var input_area = $('<div/>').addClass('input_area box-flex1 border-box-sizing');
22 this.code_mirror = CodeMirror(input_area.get(0), {
23 indentUnit : 4,
24 enterMode : 'flat',
25 tabMode: 'shift'
26 });
27 input.append(input_area);
28 var output = $('<div></div>').addClass('output vbox border-box-sizing');
29 cell.append(input).append(output);
30 this.element = cell;
31 this.collapse()
32 };
33
34
35 CodeCell.prototype.select = function () {
36 Cell.prototype.select.apply(this);
37 this.code_mirror.focus();
38 };
39
40
41 CodeCell.prototype.append_pyout = function (data, n) {
42 var toinsert = $("<div/>").addClass("output_area output_pyout hbox monospace-font");
43 toinsert.append($('<div/>').
44 addClass('prompt output_prompt').
45 html('Out[' + n + ']:')
46 );
47 this.append_display_data(data, toinsert);
48 toinsert.children().last().addClass("box_flex1");
49 this.element.find("div.output").append(toinsert);
50 // If we just output latex, typeset it.
51 if (data["text/latex"] !== undefined) {
52 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
53 };
54 };
55
56
57 CodeCell.prototype.append_pyerr = function (ename, evalue, tb) {
58 var s = '';
59 var len = tb.length;
60 for (var i=0; i<len; i++) {
61 s = s + tb[i] + '\n';
62 }
63 s = s + '\n';
64 this.append_stream(s);
65 };
66
67
68 CodeCell.prototype.append_display_data = function (data, element) {
69 console.log(data);
70 if (data["text/latex"] !== undefined) {
71 this.append_latex(data["text/latex"], element);
72 // If it is undefined, then we just appended to div.output, which
73 // makes the latex visible and we can typeset it. The typesetting
74 // has to be done after the latex is on the page.
75 if (element === undefined) {
76 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
77 };
78 } else if (data["image/svg+xml"] !== undefined) {
79 this.append_svg(data["image/svg+xml"], element);
80 } else if (data["image/png"] !== undefined) {
81 this.append_png(data["image/png"], element);
82 } else if (data["text/plain"] !== undefined) {
83 this.append_stream(data["text/plain"], element);
84 };
85 return element;
86 };
87
88
89 CodeCell.prototype.append_stream = function (data, element) {
90 element = element || this.element.find("div.output");
91 var toinsert = $("<div/>").addClass("output_area output_stream monospace-font");
92 toinsert.append($("<pre/>").addClass("monospace-font").html(fixConsole(data)));
93 element.append(toinsert);
94 return element;
95 };
96
97
98 CodeCell.prototype.append_svg = function (svg, element) {
99 element = element || this.element.find("div.output");
100 var toinsert = $("<div/>").addClass("output_area output_svg");
101 toinsert.append(svg);
102 element.append(toinsert);
103 return element;
104 };
105
106
107 CodeCell.prototype.append_png = function (png, element) {
108 element = element || this.element.find("div.output");
109 var toinsert = $("<div/>").addClass("output_area output_png");
110 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
111 element.append(toinsert);
112 return element;
113 };
114
115
116 CodeCell.prototype.append_latex = function (latex, element) {
117 // This method cannot do the typesetting because the latex first has to
118 // be on the page.
119 element = element || this.element.find("div.output");
120 var toinsert = $("<div/>").addClass("output_area output_latex monospace-font");
121 toinsert.append(latex);
122 element.append(toinsert);
123 return element;
124 }
125
126
127 CodeCell.prototype.clear_output = function () {
128 this.element.find("div.output").html("");
129 };
130
131
132 CodeCell.prototype.collapse = function () {
133 this.element.find('div.output').hide();
134 };
135
136
137 CodeCell.prototype.expand = function () {
138 this.element.find('div.output').show();
139 };
140
141
142 CodeCell.prototype.set_input_prompt = function (number) {
143 var n = number || ' ';
144 this.input_prompt_number = n
145 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
146 };
147
148
149 CodeCell.prototype.get_code = function () {
150 return this.code_mirror.getValue();
151 };
152
153
154 CodeCell.prototype.set_code = function (code) {
155 return this.code_mirror.setValue(code);
156 };
157
158
159 CodeCell.prototype.at_top = function () {
160 var cursor = this.code_mirror.getCursor();
161 if (cursor.line === 0) {
162 return true;
163 } else {
164 return false;
165 }
166 };
167
168
169 CodeCell.prototype.at_bottom = function () {
170 var cursor = this.code_mirror.getCursor();
171 if (cursor.line === (this.code_mirror.lineCount()-1)) {
172 return true;
173 } else {
174 return false;
175 }
176 };
177
178
179 CodeCell.prototype.fromJSON = function (data) {
180 if (data.cell_type === 'code') {
181 this.set_code(data.code);
182 this.set_input_prompt(data.prompt_number);
183 };
184 };
185
186
187 CodeCell.prototype.toJSON = function () {
188 return {
189 code : this.get_code(),
190 cell_type : 'code',
191 prompt_number : this.input_prompt_number
192 };
193 };
194
195
@@ -0,0 +1,107 b''
1
2 //============================================================================
3 // Kernel
4 //============================================================================
5
6
7 var Kernel = function () {
8 this.kernel_id = null;
9 this.base_url = "/kernels";
10 this.kernel_url = null;
11 };
12
13
14 Kernel.prototype.get_msg = function (msg_type, content) {
15 var msg = {
16 header : {
17 msg_id : uuid(),
18 username : "bgranger",
19 session: this.session_id,
20 msg_type : msg_type
21 },
22 content : content,
23 parent_header : {}
24 };
25 return msg;
26 }
27
28 Kernel.prototype.start_kernel = function (callback, context) {
29 var that = this;
30 $.post(this.base_url,
31 function (kernel_id) {
32 that._handle_start_kernel(kernel_id, callback, context);
33 },
34 'json'
35 );
36 };
37
38
39 Kernel.prototype._handle_start_kernel = function (kernel_id, callback, context) {
40 this.kernel_id = kernel_id;
41 this.kernel_url = this.base_url + "/" + this.kernel_id;
42 this._start_channels();
43 callback.call(context);
44 };
45
46
47 Kernel.prototype._start_channels = function () {
48 var ws_url = "ws://127.0.0.1:8888" + this.kernel_url;
49 this.shell_channel = new WebSocket(ws_url + "/shell");
50 this.iopub_channel = new WebSocket(ws_url + "/iopub");
51 }
52
53
54 Kernel.prototype.execute = function (code) {
55 var content = {
56 code : code,
57 silent : false,
58 user_variables : [],
59 user_expressions : {}
60 };
61 var msg = this.get_msg("execute_request", content);
62 this.shell_channel.send(JSON.stringify(msg));
63 return msg.header.msg_id;
64 }
65
66
67 Kernel.prototype.interrupt = function () {
68 $.post(this.kernel_url + "/interrupt");
69 };
70
71
72 Kernel.prototype.restart = function () {
73 this.status_restarting();
74 url = this.kernel_url + "/restart"
75 var that = this;
76 $.post(url, function (kernel_id) {
77 console.log("Kernel restarted: " + kernel_id);
78 that.kernel_id = kernel_id;
79 that.kernel_url = that.base_url + "/" + that.kernel_id;
80 that.status_idle();
81 }, 'json');
82 };
83
84
85 Kernel.prototype.status_busy = function () {
86 $("#kernel_status").removeClass("status_idle");
87 $("#kernel_status").removeClass("status_restarting");
88 $("#kernel_status").addClass("status_busy");
89 $("#kernel_status").text("Busy");
90 };
91
92
93 Kernel.prototype.status_idle = function () {
94 $("#kernel_status").removeClass("status_busy");
95 $("#kernel_status").removeClass("status_restarting");
96 $("#kernel_status").addClass("status_idle");
97 $("#kernel_status").text("Idle");
98 };
99
100 Kernel.prototype.status_restarting = function () {
101 $("#kernel_status").removeClass("status_busy");
102 $("#kernel_status").removeClass("status_idle");
103 $("#kernel_status").addClass("status_restarting");
104 $("#kernel_status").text("Restarting");
105 };
106
107
@@ -0,0 +1,4 b''
1 var IPYTHON = {};
2
3
4
@@ -0,0 +1,62 b''
1
2 //============================================================================
3 // On document ready
4 //============================================================================
5
6
7 $(document).ready(function () {
8
9 $('div#wrapper').addClass('vbox border-box-sizing')
10 $('div.notebook').addClass('box-flex1 border-box-sizing')
11
12 MathJax.Hub.Config({
13 tex2jax: {
14 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
15 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
16 },
17 displayAlign: 'left', // Change this to 'center' to center equations.
18 "HTML-CSS": {
19 styles: {'.MathJax_Display': {"margin": 0}}
20 }
21 });
22
23 IPYTHON.notebook = new Notebook('div.notebook');
24 IPYTHON.notebook.insert_code_cell_after();
25
26 $("#menu_tabs").tabs();
27
28 $("#help_toolbar").buttonset();
29
30 $("#kernel_toolbar").buttonset();
31 $("#interrupt_kernel").click(function () {IPYTHON.notebook.kernel.interrupt();});
32 $("#restart_kernel").click(function () {IPYTHON.notebook.kernel.restart();});
33 $("#kernel_status").addClass("status_idle");
34
35 $("#move_cell").buttonset();
36 $("#move_up").button("option", "icons", {primary:"ui-icon-arrowthick-1-n"});
37 $("#move_up").button("option", "text", false);
38 $("#move_up").click(function () {IPYTHON.notebook.move_cell_up();});
39 $("#move_down").button("option", "icons", {primary:"ui-icon-arrowthick-1-s"});
40 $("#move_down").button("option", "text", false);
41 $("#move_down").click(function () {IPYTHON.notebook.move_cell_down();});
42
43 $("#insert_delete").buttonset();
44 $("#insert_cell_before").click(function () {IPYTHON.notebook.insert_code_cell_before();});
45 $("#insert_cell_after").click(function () {IPYTHON.notebook.insert_code_cell_after();});
46 $("#delete_cell").button("option", "icons", {primary:"ui-icon-closethick"});
47 $("#delete_cell").button("option", "text", false);
48 $("#delete_cell").click(function () {IPYTHON.notebook.delete_cell();});
49
50 $("#cell_type").buttonset();
51 $("#to_code").click(function () {IPYTHON.notebook.text_to_code();});
52 $("#to_text").click(function () {IPYTHON.notebook.code_to_text();});
53
54 $("#sort").buttonset();
55 $("#sort_cells").click(function () {IPYTHON.notebook.sort_cells();});
56
57 $("#toggle").buttonset();
58 $("#collapse").click(function () {IPYTHON.notebook.collapse();});
59 $("#expand").click(function () {IPYTHON.notebook.expand();});
60
61 });
62
@@ -0,0 +1,144 b''
1
2 //============================================================================
3 // TextCell
4 //============================================================================
5
6
7 var TextCell = function (notebook) {
8 Cell.apply(this, arguments);
9 this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$"
10 this.rendered = false;
11 };
12
13
14 TextCell.prototype = new Cell();
15
16
17 TextCell.prototype.create_element = function () {
18 var cell = $("<div>").addClass('cell text_cell').
19 append(
20 $("<textarea>" + this.placeholder + "</textarea>").
21 addClass('text_cell_input monospace-font').
22 attr('rows',1).
23 attr('cols',80).
24 autogrow()
25 ).append(
26 // The tabindex=-1 makes this div focusable.
27 $('<div></div>').addClass('text_cell_render').attr('tabindex','-1')
28 )
29 this.element = cell;
30 };
31
32
33 TextCell.prototype.bind_events = function () {
34 Cell.prototype.bind_events.apply(this);
35 var that = this;
36 this.element.keydown(function (event) {
37 if (event.which === 13) {
38 if (that.rendered) {
39 that.edit();
40 event.preventDefault();
41 };
42 };
43 });
44 };
45
46
47 TextCell.prototype.select = function () {
48 Cell.prototype.select.apply(this);
49 var output = this.element.find("div.text_cell_render");
50 output.trigger('focus');
51 };
52
53
54 TextCell.prototype.edit = function () {
55 if (this.rendered === true) {
56 var text_cell = this.element;
57 var input = text_cell.find("textarea.text_cell_input");
58 var output = text_cell.find("div.text_cell_render");
59 output.hide();
60 input.show().trigger('focus');
61 this.rendered = false;
62 };
63 };
64
65
66 TextCell.prototype.render = function () {
67 if (this.rendered === false) {
68 var text_cell = this.element;
69 var input = text_cell.find("textarea.text_cell_input");
70 var output = text_cell.find("div.text_cell_render");
71 var text = input.val();
72 if (text === "") {
73 text = this.placeholder;
74 input.val(text);
75 };
76 output.html(text)
77 input.html(text);
78 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
79 input.hide();
80 output.show();
81 this.rendered = true;
82 };
83 };
84
85
86 TextCell.prototype.config_mathjax = function () {
87 var text_cell = this.element;
88 var that = this;
89 text_cell.click(function () {
90 that.edit();
91 }).focusout(function () {
92 that.render();
93 });
94
95 text_cell.trigger("focusout");
96 };
97
98
99 TextCell.prototype.get_text = function() {
100 return this.element.find("textarea.text_cell_input").val();
101 };
102
103
104 TextCell.prototype.set_text = function(text) {
105 this.element.find("textarea.text_cell_input").val(text);
106 this.element.find("textarea.text_cell_input").html(text);
107 this.element.find("div.text_cell_render").html(text);
108 };
109
110
111 TextCell.prototype.at_top = function () {
112 if (this.rendered) {
113 return true;
114 } else {
115 return false;
116 }
117 };
118
119
120 TextCell.prototype.at_bottom = function () {
121 if (this.rendered) {
122 return true;
123 } else {
124 return false;
125 }
126 };
127
128
129 TextCell.prototype.fromJSON = function (data) {
130 if (data.cell_type === 'text') {
131 this.set_text(data.text);
132 this.grow(this.element.find("textarea.text_cell_input"));
133 };
134 }
135
136
137 TextCell.prototype.toJSON = function () {
138 return {
139 cell_type : 'text',
140 text : this.get_text(),
141 };
142 };
143
144
@@ -0,0 +1,62 b''
1
2 //============================================================================
3 // Utilities
4 //============================================================================
5
6
7 var uuid = function () {
8 // http://www.ietf.org/rfc/rfc4122.txt
9 var s = [];
10 var hexDigits = "0123456789ABCDEF";
11 for (var i = 0; i < 32; i++) {
12 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
13 }
14 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
15 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
16
17 var uuid = s.join("");
18 return uuid;
19 };
20
21
22 //Fix raw text to parse correctly in crazy XML
23 function xmlencode(string) {
24 return string.replace(/\&/g,'&'+'amp;')
25 .replace(/</g,'&'+'lt;')
26 .replace(/>/g,'&'+'gt;')
27 .replace(/\'/g,'&'+'apos;')
28 .replace(/\"/g,'&'+'quot;')
29 .replace(/`/g,'&'+'#96;')
30 }
31
32 //Map from terminal commands to CSS classes
33 attrib = {
34 "30":"cblack", "31":"cred",
35 "32":"cgreen", "33":"cyellow",
36 "34":"cblue", "36":"ccyan",
37 "37":"cwhite", "01":"cbold"}
38
39 //Fixes escaped console commands, IE colors. Turns them into HTML
40 function fixConsole(txt) {
41 txt = xmlencode(txt)
42 var re = /\033\[([\d;]*?)m/
43 var opened = false
44 var cmds = []
45 var opener = ""
46 var closer = ""
47
48 while (re.test(txt)) {
49 var cmds = txt.match(re)[1].split(";")
50 closer = opened?"</span>":""
51 opened = cmds.length > 1 || cmds[0] != 0
52 var rep = []
53 for (var i in cmds)
54 if (typeof(attrib[cmds[i]]) != "undefined")
55 rep.push(attrib[cmds[i]])
56 opener = rep.length > 0?"<span class=\""+rep.join(" ")+"\">":""
57 txt = txt.replace(re, closer + opener)
58 }
59 if (opened) txt += "</span>"
60 return txt.trim()
61 }
62
This diff has been collapsed as it changes many lines, (637 lines changed) Show them Hide them
@@ -1,1145 +1,508 b''
1 var IPYTHON = {};
2
3
4 //============================================================================
5 // Utilities
6 //============================================================================
7
8
9 var uuid = function () {
10 // http://www.ietf.org/rfc/rfc4122.txt
11 var s = [];
12 var hexDigits = "0123456789ABCDEF";
13 for (var i = 0; i < 32; i++) {
14 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
15 }
16 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
17 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
18
19 var uuid = s.join("");
20 return uuid;
21 };
22
23
24 //Fix raw text to parse correctly in crazy XML
25 function xmlencode(string) {
26 return string.replace(/\&/g,'&'+'amp;')
27 .replace(/</g,'&'+'lt;')
28 .replace(/>/g,'&'+'gt;')
29 .replace(/\'/g,'&'+'apos;')
30 .replace(/\"/g,'&'+'quot;')
31 .replace(/`/g,'&'+'#96;')
32 }
33
34 //Map from terminal commands to CSS classes
35 attrib = {
36 "30":"cblack", "31":"cred",
37 "32":"cgreen", "33":"cyellow",
38 "34":"cblue", "36":"ccyan",
39 "37":"cwhite", "01":"cbold"}
40
41 //Fixes escaped console commands, IE colors. Turns them into HTML
42 function fixConsole(txt) {
43 txt = xmlencode(txt)
44 var re = /\033\[([\d;]*?)m/
45 var opened = false
46 var cmds = []
47 var opener = ""
48 var closer = ""
49
50 while (re.test(txt)) {
51 var cmds = txt.match(re)[1].split(";")
52 closer = opened?"</span>":""
53 opened = cmds.length > 1 || cmds[0] != 0
54 var rep = []
55 for (var i in cmds)
56 if (typeof(attrib[cmds[i]]) != "undefined")
57 rep.push(attrib[cmds[i]])
58 opener = rep.length > 0?"<span class=\""+rep.join(" ")+"\">":""
59 txt = txt.replace(re, closer + opener)
60 }
61 if (opened) txt += "</span>"
62 return txt.trim()
63 }
64
65
1
66 //============================================================================
2 //============================================================================
67 // Notebook
3 // Notebook
68 //============================================================================
4 //============================================================================
69
5
70
6
71 var Notebook = function (selector) {
7 var Notebook = function (selector) {
72 this.element = $(selector);
8 this.element = $(selector);
73 this.element.scroll();
9 this.element.scroll();
74 this.element.data("notebook", this);
10 this.element.data("notebook", this);
75 this.next_prompt_number = 1;
11 this.next_prompt_number = 1;
76 this.kernel = null;
12 this.kernel = null;
77 this.msg_cell_map = {};
13 this.msg_cell_map = {};
78 this.filename = null;
14 this.filename = null;
79 this.notebook_load_re = /%notebook load/
15 this.notebook_load_re = /%notebook load/
80 this.notebook_save_re = /%notebook save/
16 this.notebook_save_re = /%notebook save/
81 this.notebook_filename_re = /(\w)+.ipynb/
17 this.notebook_filename_re = /(\w)+.ipynb/
82 this.bind_events();
18 this.bind_events();
83 this.start_kernel();
19 this.start_kernel();
84 };
20 };
85
21
86
22
87 Notebook.prototype.bind_events = function () {
23 Notebook.prototype.bind_events = function () {
88 var that = this;
24 var that = this;
89 $(document).keydown(function (event) {
25 $(document).keydown(function (event) {
90 // console.log(event);
26 // console.log(event);
91 if (event.which === 38) {
27 if (event.which === 38) {
92 var cell = that.selected_cell();
28 var cell = that.selected_cell();
93 if (cell.at_top()) {
29 if (cell.at_top()) {
94 event.preventDefault();
30 event.preventDefault();
95 that.select_prev();
31 that.select_prev();
96 };
32 };
97 } else if (event.which === 40) {
33 } else if (event.which === 40) {
98 var cell = that.selected_cell();
34 var cell = that.selected_cell();
99 if (cell.at_bottom()) {
35 if (cell.at_bottom()) {
100 event.preventDefault();
36 event.preventDefault();
101 that.select_next();
37 that.select_next();
102 };
38 };
103 } else if (event.which === 13 && event.shiftKey) {
39 } else if (event.which === 13 && event.shiftKey) {
104 // The focus is not quite working here.
40 // The focus is not quite working here.
105 var cell = that.selected_cell();
41 var cell = that.selected_cell();
106 var cell_index = that.find_cell_index(cell);
42 var cell_index = that.find_cell_index(cell);
107 // TODO: the logic here needs to be moved into appropriate
43 // TODO: the logic here needs to be moved into appropriate
108 // methods of Notebook.
44 // methods of Notebook.
109 if (cell instanceof CodeCell) {
45 if (cell instanceof CodeCell) {
110 event.preventDefault();
46 event.preventDefault();
111 cell.clear_output();
47 cell.clear_output();
112 var code = cell.get_code();
48 var code = cell.get_code();
113 if (that.notebook_load_re.test(code)) {
49 if (that.notebook_load_re.test(code)) {
114 var code_parts = code.split(' ');
50 var code_parts = code.split(' ');
115 if (code_parts.length === 3) {
51 if (code_parts.length === 3) {
116 that.load_notebook(code_parts[2]);
52 that.load_notebook(code_parts[2]);
117 };
53 };
118 } else if (that.notebook_save_re.test(code)) {
54 } else if (that.notebook_save_re.test(code)) {
119 var code_parts = code.split(' ');
55 var code_parts = code.split(' ');
120 if (code_parts.length === 3) {
56 if (code_parts.length === 3) {
121 that.save_notebook(code_parts[2]);
57 that.save_notebook(code_parts[2]);
122 } else {
58 } else {
123 that.save_notebook()
59 that.save_notebook()
124 };
60 };
125 } else {
61 } else {
126 var msg_id = that.kernel.execute(cell.get_code());
62 var msg_id = that.kernel.execute(cell.get_code());
127 that.msg_cell_map[msg_id] = cell.cell_id;
63 that.msg_cell_map[msg_id] = cell.cell_id;
128 };
64 };
129 } else if (cell instanceof TextCell) {
65 } else if (cell instanceof TextCell) {
130 event.preventDefault();
66 event.preventDefault();
131 cell.render();
67 cell.render();
132 }
68 }
133 if (cell_index === (that.ncells()-1)) {
69 if (cell_index === (that.ncells()-1)) {
134 that.insert_code_cell_after();
70 that.insert_code_cell_after();
135 } else {
71 } else {
136 that.select(cell_index+1);
72 that.select(cell_index+1);
137 };
73 };
138 };
74 };
139 });
75 });
140 };
76 };
141
77
142
78
143 // Cell indexing, retrieval, etc.
79 // Cell indexing, retrieval, etc.
144
80
145
81
146 Notebook.prototype.cell_elements = function () {
82 Notebook.prototype.cell_elements = function () {
147 return this.element.children("div.cell");
83 return this.element.children("div.cell");
148 }
84 }
149
85
150
86
151 Notebook.prototype.ncells = function (cell) {
87 Notebook.prototype.ncells = function (cell) {
152 return this.cell_elements().length;
88 return this.cell_elements().length;
153 }
89 }
154
90
155
91
156 // TODO: we are often calling cells as cells()[i], which we should optimize
92 // TODO: we are often calling cells as cells()[i], which we should optimize
157 // to cells(i) or a new method.
93 // to cells(i) or a new method.
158 Notebook.prototype.cells = function () {
94 Notebook.prototype.cells = function () {
159 return this.cell_elements().toArray().map(function (e) {
95 return this.cell_elements().toArray().map(function (e) {
160 return $(e).data("cell");
96 return $(e).data("cell");
161 });
97 });
162 }
98 }
163
99
164
100
165 Notebook.prototype.find_cell_index = function (cell) {
101 Notebook.prototype.find_cell_index = function (cell) {
166 var result = null;
102 var result = null;
167 this.cell_elements().filter(function (index) {
103 this.cell_elements().filter(function (index) {
168 if ($(this).data("cell") === cell) {
104 if ($(this).data("cell") === cell) {
169 result = index;
105 result = index;
170 };
106 };
171 });
107 });
172 return result;
108 return result;
173 };
109 };
174
110
175
111
176 Notebook.prototype.index_or_selected = function (index) {
112 Notebook.prototype.index_or_selected = function (index) {
177 return index || this.selected_index() || 0;
113 return index || this.selected_index() || 0;
178 }
114 }
179
115
180
116
181 Notebook.prototype.select = function (index) {
117 Notebook.prototype.select = function (index) {
182 if (index !== undefined && index >= 0 && index < this.ncells()) {
118 if (index !== undefined && index >= 0 && index < this.ncells()) {
183 if (this.selected_index() !== null) {
119 if (this.selected_index() !== null) {
184 this.selected_cell().unselect();
120 this.selected_cell().unselect();
185 };
121 };
186 this.cells()[index].select();
122 this.cells()[index].select();
187 };
123 };
188 return this;
124 return this;
189 };
125 };
190
126
191
127
192 Notebook.prototype.select_next = function () {
128 Notebook.prototype.select_next = function () {
193 var index = this.selected_index();
129 var index = this.selected_index();
194 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
130 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
195 this.select(index+1);
131 this.select(index+1);
196 };
132 };
197 return this;
133 return this;
198 };
134 };
199
135
200
136
201 Notebook.prototype.select_prev = function () {
137 Notebook.prototype.select_prev = function () {
202 var index = this.selected_index();
138 var index = this.selected_index();
203 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
139 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
204 this.select(index-1);
140 this.select(index-1);
205 };
141 };
206 return this;
142 return this;
207 };
143 };
208
144
209
145
210 Notebook.prototype.selected_index = function () {
146 Notebook.prototype.selected_index = function () {
211 var result = null;
147 var result = null;
212 this.cell_elements().filter(function (index) {
148 this.cell_elements().filter(function (index) {
213 if ($(this).data("cell").selected === true) {
149 if ($(this).data("cell").selected === true) {
214 result = index;
150 result = index;
215 };
151 };
216 });
152 });
217 return result;
153 return result;
218 };
154 };
219
155
220
156
221 Notebook.prototype.cell_for_msg = function (msg_id) {
157 Notebook.prototype.cell_for_msg = function (msg_id) {
222 var cell_id = this.msg_cell_map[msg_id];
158 var cell_id = this.msg_cell_map[msg_id];
223 var result = null;
159 var result = null;
224 this.cell_elements().filter(function (index) {
160 this.cell_elements().filter(function (index) {
225 cell = $(this).data("cell");
161 cell = $(this).data("cell");
226 if (cell.cell_id === cell_id) {
162 if (cell.cell_id === cell_id) {
227 result = cell;
163 result = cell;
228 };
164 };
229 });
165 });
230 return result;
166 return result;
231 };
167 };
232
168
233
169
234 Notebook.prototype.selected_cell = function () {
170 Notebook.prototype.selected_cell = function () {
235 return this.cell_elements().eq(this.selected_index()).data("cell");
171 return this.cell_elements().eq(this.selected_index()).data("cell");
236 }
172 }
237
173
238
174
239 // Cell insertion, deletion and moving.
175 // Cell insertion, deletion and moving.
240
176
241
177
242 Notebook.prototype.delete_cell = function (index) {
178 Notebook.prototype.delete_cell = function (index) {
243 var i = index || this.selected_index();
179 var i = index || this.selected_index();
244 if (i !== null && i >= 0 && i < this.ncells()) {
180 if (i !== null && i >= 0 && i < this.ncells()) {
245 this.cell_elements().eq(i).remove();
181 this.cell_elements().eq(i).remove();
246 if (i === (this.ncells())) {
182 if (i === (this.ncells())) {
247 this.select(i-1);
183 this.select(i-1);
248 } else {
184 } else {
249 this.select(i);
185 this.select(i);
250 };
186 };
251 };
187 };
252 return this;
188 return this;
253 };
189 };
254
190
255
191
256 Notebook.prototype.append_cell = function (cell) {
192 Notebook.prototype.append_cell = function (cell) {
257 this.element.append(cell.element);
193 this.element.append(cell.element);
258 return this;
194 return this;
259 };
195 };
260
196
261
197
262 Notebook.prototype.insert_cell_after = function (cell, index) {
198 Notebook.prototype.insert_cell_after = function (cell, index) {
263 var ncells = this.ncells();
199 var ncells = this.ncells();
264 if (ncells === 0) {
200 if (ncells === 0) {
265 this.append_cell(cell);
201 this.append_cell(cell);
266 return this;
202 return this;
267 };
203 };
268 if (index >= 0 && index < ncells) {
204 if (index >= 0 && index < ncells) {
269 this.cell_elements().eq(index).after(cell.element);
205 this.cell_elements().eq(index).after(cell.element);
270 };
206 };
271 return this
207 return this
272 };
208 };
273
209
274
210
275 Notebook.prototype.insert_cell_before = function (cell, index) {
211 Notebook.prototype.insert_cell_before = function (cell, index) {
276 var ncells = this.ncells();
212 var ncells = this.ncells();
277 if (ncells === 0) {
213 if (ncells === 0) {
278 this.append_cell(cell);
214 this.append_cell(cell);
279 return this;
215 return this;
280 };
216 };
281 if (index >= 0 && index < ncells) {
217 if (index >= 0 && index < ncells) {
282 this.cell_elements().eq(index).before(cell.element);
218 this.cell_elements().eq(index).before(cell.element);
283 };
219 };
284 return this;
220 return this;
285 };
221 };
286
222
287
223
288 Notebook.prototype.move_cell_up = function (index) {
224 Notebook.prototype.move_cell_up = function (index) {
289 var i = index || this.selected_index();
225 var i = index || this.selected_index();
290 if (i !== null && i < this.ncells() && i > 0) {
226 if (i !== null && i < this.ncells() && i > 0) {
291 var pivot = this.cell_elements().eq(i-1);
227 var pivot = this.cell_elements().eq(i-1);
292 var tomove = this.cell_elements().eq(i);
228 var tomove = this.cell_elements().eq(i);
293 if (pivot !== null && tomove !== null) {
229 if (pivot !== null && tomove !== null) {
294 tomove.detach();
230 tomove.detach();
295 pivot.before(tomove);
231 pivot.before(tomove);
296 this.select(i-1);
232 this.select(i-1);
297 };
233 };
298 };
234 };
299 return this;
235 return this;
300 }
236 }
301
237
302
238
303 Notebook.prototype.move_cell_down = function (index) {
239 Notebook.prototype.move_cell_down = function (index) {
304 var i = index || this.selected_index();
240 var i = index || this.selected_index();
305 if (i !== null && i < (this.ncells()-1) && i >= 0) {
241 if (i !== null && i < (this.ncells()-1) && i >= 0) {
306 var pivot = this.cell_elements().eq(i+1)
242 var pivot = this.cell_elements().eq(i+1)
307 var tomove = this.cell_elements().eq(i)
243 var tomove = this.cell_elements().eq(i)
308 if (pivot !== null && tomove !== null) {
244 if (pivot !== null && tomove !== null) {
309 tomove.detach();
245 tomove.detach();
310 pivot.after(tomove);
246 pivot.after(tomove);
311 this.select(i+1);
247 this.select(i+1);
312 };
248 };
313 };
249 };
314 return this;
250 return this;
315 }
251 }
316
252
317
253
318 Notebook.prototype.sort_cells = function () {
254 Notebook.prototype.sort_cells = function () {
319 var ncells = this.ncells();
255 var ncells = this.ncells();
320 var sindex = this.selected_index();
256 var sindex = this.selected_index();
321 var swapped;
257 var swapped;
322 do {
258 do {
323 swapped = false
259 swapped = false
324 for (var i=1; i<ncells; i++) {
260 for (var i=1; i<ncells; i++) {
325 current = this.cell_elements().eq(i).data("cell");
261 current = this.cell_elements().eq(i).data("cell");
326 previous = this.cell_elements().eq(i-1).data("cell");
262 previous = this.cell_elements().eq(i-1).data("cell");
327 if (previous.input_prompt_number > current.input_prompt_number) {
263 if (previous.input_prompt_number > current.input_prompt_number) {
328 this.move_cell_up(i);
264 this.move_cell_up(i);
329 swapped = true;
265 swapped = true;
330 };
266 };
331 };
267 };
332 } while (swapped);
268 } while (swapped);
333 this.select(sindex);
269 this.select(sindex);
334 return this;
270 return this;
335 };
271 };
336
272
337
273
338 Notebook.prototype.insert_code_cell_before = function (index) {
274 Notebook.prototype.insert_code_cell_before = function (index) {
339 // TODO: Bounds check for i
275 // TODO: Bounds check for i
340 var i = this.index_or_selected(index);
276 var i = this.index_or_selected(index);
341 var cell = new CodeCell(this);
277 var cell = new CodeCell(this);
342 cell.set_input_prompt(this.next_prompt_number);
278 cell.set_input_prompt(this.next_prompt_number);
343 this.next_prompt_number = this.next_prompt_number + 1;
279 this.next_prompt_number = this.next_prompt_number + 1;
344 this.insert_cell_before(cell, i);
280 this.insert_cell_before(cell, i);
345 this.select(this.find_cell_index(cell));
281 this.select(this.find_cell_index(cell));
346 return this;
282 return this;
347 }
283 }
348
284
349
285
350 Notebook.prototype.insert_code_cell_after = function (index) {
286 Notebook.prototype.insert_code_cell_after = function (index) {
351 // TODO: Bounds check for i
287 // TODO: Bounds check for i
352 var i = this.index_or_selected(index);
288 var i = this.index_or_selected(index);
353 var cell = new CodeCell(this);
289 var cell = new CodeCell(this);
354 cell.set_input_prompt(this.next_prompt_number);
290 cell.set_input_prompt(this.next_prompt_number);
355 this.next_prompt_number = this.next_prompt_number + 1;
291 this.next_prompt_number = this.next_prompt_number + 1;
356 this.insert_cell_after(cell, i);
292 this.insert_cell_after(cell, i);
357 this.select(this.find_cell_index(cell));
293 this.select(this.find_cell_index(cell));
358 return this;
294 return this;
359 }
295 }
360
296
361
297
362 Notebook.prototype.insert_text_cell_before = function (index) {
298 Notebook.prototype.insert_text_cell_before = function (index) {
363 // TODO: Bounds check for i
299 // TODO: Bounds check for i
364 var i = this.index_or_selected(index);
300 var i = this.index_or_selected(index);
365 var cell = new TextCell(this);
301 var cell = new TextCell(this);
366 cell.config_mathjax();
302 cell.config_mathjax();
367 this.insert_cell_before(cell, i);
303 this.insert_cell_before(cell, i);
368 this.select(this.find_cell_index(cell));
304 this.select(this.find_cell_index(cell));
369 return this;
305 return this;
370 }
306 }
371
307
372
308
373 Notebook.prototype.insert_text_cell_after = function (index) {
309 Notebook.prototype.insert_text_cell_after = function (index) {
374 // TODO: Bounds check for i
310 // TODO: Bounds check for i
375 var i = this.index_or_selected(index);
311 var i = this.index_or_selected(index);
376 var cell = new TextCell(this);
312 var cell = new TextCell(this);
377 cell.config_mathjax();
313 cell.config_mathjax();
378 this.insert_cell_after(cell, i);
314 this.insert_cell_after(cell, i);
379 this.select(this.find_cell_index(cell));
315 this.select(this.find_cell_index(cell));
380 return this;
316 return this;
381 }
317 }
382
318
383
319
384 Notebook.prototype.text_to_code = function (index) {
320 Notebook.prototype.text_to_code = function (index) {
385 // TODO: Bounds check for i
321 // TODO: Bounds check for i
386 var i = this.index_or_selected(index);
322 var i = this.index_or_selected(index);
387 var source_element = this.cell_elements().eq(i);
323 var source_element = this.cell_elements().eq(i);
388 var source_cell = source_element.data("cell");
324 var source_cell = source_element.data("cell");
389 if (source_cell instanceof TextCell) {
325 if (source_cell instanceof TextCell) {
390 this.insert_code_cell_after(i);
326 this.insert_code_cell_after(i);
391 var target_cell = this.cells()[i+1];
327 var target_cell = this.cells()[i+1];
392 target_cell.set_code(source_cell.get_text());
328 target_cell.set_code(source_cell.get_text());
393 source_element.remove();
329 source_element.remove();
394 };
330 };
395 };
331 };
396
332
397
333
398 Notebook.prototype.code_to_text = function (index) {
334 Notebook.prototype.code_to_text = function (index) {
399 // TODO: Bounds check for i
335 // TODO: Bounds check for i
400 var i = this.index_or_selected(index);
336 var i = this.index_or_selected(index);
401 var source_element = this.cell_elements().eq(i);
337 var source_element = this.cell_elements().eq(i);
402 var source_cell = source_element.data("cell");
338 var source_cell = source_element.data("cell");
403 if (source_cell instanceof CodeCell) {
339 if (source_cell instanceof CodeCell) {
404 this.insert_text_cell_after(i);
340 this.insert_text_cell_after(i);
405 var target_cell = this.cells()[i+1];
341 var target_cell = this.cells()[i+1];
406 var text = source_cell.get_code();
342 var text = source_cell.get_code();
407 if (text === "") {text = target_cell.placeholder;};
343 if (text === "") {text = target_cell.placeholder;};
408 target_cell.set_text(text);
344 target_cell.set_text(text);
409 source_element.remove();
345 source_element.remove();
410 target_cell.edit();
346 target_cell.edit();
411 };
347 };
412 };
348 };
413
349
414
350
415 // Cell collapsing
351 // Cell collapsing
416
352
417 Notebook.prototype.collapse = function (index) {
353 Notebook.prototype.collapse = function (index) {
418 var i = this.index_or_selected(index);
354 var i = this.index_or_selected(index);
419 this.cells()[i].collapse();
355 this.cells()[i].collapse();
420 };
356 };
421
357
422
358
423 Notebook.prototype.expand = function (index) {
359 Notebook.prototype.expand = function (index) {
424 var i = this.index_or_selected(index);
360 var i = this.index_or_selected(index);
425 this.cells()[i].expand();
361 this.cells()[i].expand();
426 };
362 };
427
363
428
364
429 // Kernel related things
365 // Kernel related things
430
366
431 Notebook.prototype.start_kernel = function () {
367 Notebook.prototype.start_kernel = function () {
432 this.kernel = new Kernel();
368 this.kernel = new Kernel();
433 this.kernel.start_kernel(this._kernel_started, this);
369 this.kernel.start_kernel(this._kernel_started, this);
434 };
370 };
435
371
436
372
437 Notebook.prototype._kernel_started = function () {
373 Notebook.prototype._kernel_started = function () {
438 console.log("Kernel started: ", this.kernel.kernel_id);
374 console.log("Kernel started: ", this.kernel.kernel_id);
439 var that = this;
375 var that = this;
440
376
441 this.kernel.shell_channel.onmessage = function (e) {
377 this.kernel.shell_channel.onmessage = function (e) {
442 reply = $.parseJSON(e.data);
378 reply = $.parseJSON(e.data);
443 // console.log(reply);
379 // console.log(reply);
444 var msg_type = reply.header.msg_type;
380 var msg_type = reply.header.msg_type;
445 var cell = that.cell_for_msg(reply.parent_header.msg_id);
381 var cell = that.cell_for_msg(reply.parent_header.msg_id);
446 if (msg_type === "execute_reply") {
382 if (msg_type === "execute_reply") {
447 cell.set_input_prompt(reply.content.execution_count);
383 cell.set_input_prompt(reply.content.execution_count);
448 };
384 };
449 };
385 };
450
386
451 this.kernel.iopub_channel.onmessage = function (e) {
387 this.kernel.iopub_channel.onmessage = function (e) {
452 reply = $.parseJSON(e.data);
388 reply = $.parseJSON(e.data);
453 var content = reply.content;
389 var content = reply.content;
454 console.log(reply);
390 console.log(reply);
455 var msg_type = reply.header.msg_type;
391 var msg_type = reply.header.msg_type;
456 var cell = that.cell_for_msg(reply.parent_header.msg_id);
392 var cell = that.cell_for_msg(reply.parent_header.msg_id);
457 if (msg_type === "stream") {
393 if (msg_type === "stream") {
458 cell.expand();
394 cell.expand();
459 cell.append_stream(content.data + "\n");
395 cell.append_stream(content.data + "\n");
460 } else if (msg_type === "display_data") {
396 } else if (msg_type === "display_data") {
461 cell.expand();
397 cell.expand();
462 cell.append_display_data(content.data);
398 cell.append_display_data(content.data);
463 } else if (msg_type === "pyout") {
399 } else if (msg_type === "pyout") {
464 cell.expand();
400 cell.expand();
465 cell.append_pyout(content.data, content.execution_count)
401 cell.append_pyout(content.data, content.execution_count)
466 } else if (msg_type === "pyerr") {
402 } else if (msg_type === "pyerr") {
467 cell.expand();
403 cell.expand();
468 cell.append_pyerr(content.ename, content.evalue, content.traceback);
404 cell.append_pyerr(content.ename, content.evalue, content.traceback);
469 } else if (msg_type === "status") {
405 } else if (msg_type === "status") {
470 if (content.execution_state === "busy") {
406 if (content.execution_state === "busy") {
471 that.kernel.status_busy();
407 that.kernel.status_busy();
472 } else if (content.execution_state === "idle") {
408 } else if (content.execution_state === "idle") {
473 that.kernel.status_idle();
409 that.kernel.status_idle();
474 };
410 };
475 }
411 }
476 };
412 };
477 };
413 };
478
414
479
415
480 // Persistance and loading
416 // Persistance and loading
481
417
482
418
483 Notebook.prototype.fromJSON = function (data) {
419 Notebook.prototype.fromJSON = function (data) {
484 var ncells = this.ncells();
420 var ncells = this.ncells();
485 for (var i=0; i<ncells; i++) {
421 for (var i=0; i<ncells; i++) {
486 // Always delete cell 0 as they get renumbered as they are deleted.
422 // Always delete cell 0 as they get renumbered as they are deleted.
487 this.delete_cell(0);
423 this.delete_cell(0);
488 };
424 };
489 var new_cells = data.cells;
425 var new_cells = data.cells;
490 ncells = new_cells.length;
426 ncells = new_cells.length;
491 var cell_data = null;
427 var cell_data = null;
492 for (var i=0; i<ncells; i++) {
428 for (var i=0; i<ncells; i++) {
493 cell_data = new_cells[i];
429 cell_data = new_cells[i];
494 if (cell_data.cell_type == 'code') {
430 if (cell_data.cell_type == 'code') {
495 this.insert_code_cell_after();
431 this.insert_code_cell_after();
496 this.selected_cell().fromJSON(cell_data);
432 this.selected_cell().fromJSON(cell_data);
497 } else if (cell_data.cell_type === 'text') {
433 } else if (cell_data.cell_type === 'text') {
498 this.insert_text_cell_after();
434 this.insert_text_cell_after();
499 this.selected_cell().fromJSON(cell_data);
435 this.selected_cell().fromJSON(cell_data);
500 };
436 };
501 };
437 };
502 };
438 };
503
439
504
440
505 Notebook.prototype.toJSON = function () {
441 Notebook.prototype.toJSON = function () {
506 var cells = this.cells();
442 var cells = this.cells();
507 var ncells = cells.length;
443 var ncells = cells.length;
508 cell_array = new Array(ncells);
444 cell_array = new Array(ncells);
509 for (var i=0; i<ncells; i++) {
445 for (var i=0; i<ncells; i++) {
510 cell_array[i] = cells[i].toJSON();
446 cell_array[i] = cells[i].toJSON();
511 };
447 };
512 json = {
448 json = {
513 cells : cell_array
449 cells : cell_array
514 };
450 };
515 return json
451 return json
516 };
452 };
517
453
518
454
519 Notebook.prototype.test_filename = function (filename) {
455 Notebook.prototype.test_filename = function (filename) {
520 if (this.notebook_filename_re.test(filename)) {
456 if (this.notebook_filename_re.test(filename)) {
521 return true;
457 return true;
522 } else {
458 } else {
523 var bad_filename = $('<div/>');
459 var bad_filename = $('<div/>');
524 bad_filename.html(
460 bad_filename.html(
525 "The filename you entered (" + filename + ") is not valid. Notebook filenames must have the following form: foo.ipynb"
461 "The filename you entered (" + filename + ") is not valid. Notebook filenames must have the following form: foo.ipynb"
526 );
462 );
527 bad_filename.dialog({title: 'Invalid filename', modal: true});
463 bad_filename.dialog({title: 'Invalid filename', modal: true});
528 return false;
464 return false;
529 };
465 };
530 };
466 };
531
467
532 Notebook.prototype.save_notebook = function (filename) {
468 Notebook.prototype.save_notebook = function (filename) {
533 this.filename = filename || this.filename || '';
469 this.filename = filename || this.filename || '';
534 if (this.filename === '') {
470 if (this.filename === '') {
535 var no_filename = $('<div/>');
471 var no_filename = $('<div/>');
536 no_filename.html(
472 no_filename.html(
537 "This notebook has no filename, please specify a filename of the form: foo.ipynb"
473 "This notebook has no filename, please specify a filename of the form: foo.ipynb"
538 );
474 );
539 no_filename.dialog({title: 'Missing filename', modal: true});
475 no_filename.dialog({title: 'Missing filename', modal: true});
540 return;
476 return;
541 }
477 }
542 if (!this.test_filename(this.filename)) {return;}
478 if (!this.test_filename(this.filename)) {return;}
543 var thedata = this.toJSON();
479 var thedata = this.toJSON();
544 var settings = {
480 var settings = {
545 processData : false,
481 processData : false,
546 cache : false,
482 cache : false,
547 type : "PUT",
483 type : "PUT",
548 data : JSON.stringify(thedata),
484 data : JSON.stringify(thedata),
549 success : function (data, status, xhr) {console.log(data);}
485 success : function (data, status, xhr) {console.log(data);}
550 };
486 };
551 $.ajax("/notebooks/" + this.filename, settings);
487 $.ajax("/notebooks/" + this.filename, settings);
552 };
488 };
553
489
554
490
555 Notebook.prototype.load_notebook = function (filename) {
491 Notebook.prototype.load_notebook = function (filename) {
556 if (!this.test_filename(filename)) {return;}
492 if (!this.test_filename(filename)) {return;}
557 var that = this;
493 var that = this;
558 // We do the call with settings so we can set cache to false.
494 // We do the call with settings so we can set cache to false.
559 var settings = {
495 var settings = {
560 processData : false,
496 processData : false,
561 cache : false,
497 cache : false,
562 type : "GET",
498 type : "GET",
563 dataType : "json",
499 dataType : "json",
564 success : function (data, status, xhr) {
500 success : function (data, status, xhr) {
565 that.fromJSON(data);
501 that.fromJSON(data);
566 that.filename = filename;
502 that.filename = filename;
567 that.kernel.restart();
503 that.kernel.restart();
568 }
504 }
569 };
505 };
570 $.ajax("/notebooks/" + filename, settings);
506 $.ajax("/notebooks/" + filename, settings);
571 }
507 }
572
508
573
574 //============================================================================
575 // Cell
576 //============================================================================
577
578
579 var Cell = function (notebook) {
580 this.notebook = notebook;
581 this.selected = false;
582 this.element;
583 this.create_element();
584 if (this.element !== undefined) {
585 this.element.data("cell", this);
586 this.bind_events();
587 }
588 this.cell_id = uuid();
589 };
590
591
592 Cell.prototype.grow = function(element) {
593 // Grow the cell by hand. This is used upon reloading from JSON, when the
594 // autogrow handler is not called.
595 var dom = element.get(0);
596 var lines_count = 0;
597 // modified split rule from
598 // http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424
599 var lines = dom.value.split(/\r|\r\n|\n/);
600 lines_count = lines.length;
601 if (lines_count >= 1) {
602 dom.rows = lines_count;
603 } else {
604 dom.rows = 1;
605 }
606 };
607
608
609 Cell.prototype.select = function () {
610 this.element.addClass('ui-widget-content ui-corner-all');
611 this.selected = true;
612 // TODO: we need t test across browsers to see if both of these are needed.
613 // In the meantime, there should not be any harm in having them both.
614 this.element.find('textarea').trigger('focusin');
615 this.element.find('textarea').trigger('focus');
616 };
617
618
619 Cell.prototype.unselect = function () {
620 this.element.removeClass('ui-widget-content ui-corner-all');
621 this.selected = false;
622 };
623
624
625 Cell.prototype.bind_events = function () {
626 var that = this;
627 var nb = that.notebook
628 that.element.click(function (event) {
629 if (that.selected === false) {
630 nb.select(nb.find_cell_index(that));
631 };
632 });
633 that.element.focusin(function (event) {
634 if (that.selected === false) {
635 nb.select(nb.find_cell_index(that));
636 };
637 });
638 };
639
640
641 // Subclasses must implement create_element.
642 Cell.prototype.create_element = function () {};
643
644
645 //============================================================================
646 // CodeCell
647 //============================================================================
648
649
650 var CodeCell = function (notebook) {
651 this.code_mirror = null;
652 this.input_prompt_number = ' ';
653 Cell.apply(this, arguments);
654 };
655
656
657 CodeCell.prototype = new Cell();
658
659
660 CodeCell.prototype.create_element = function () {
661 var cell = $('<div></div>').addClass('cell code_cell vbox border-box-sizing');
662 var input = $('<div></div>').addClass('input hbox border-box-sizing');
663 input.append($('<div/>').addClass('prompt input_prompt monospace-font'));
664 var input_area = $('<div/>').addClass('input_area box-flex1 border-box-sizing');
665 this.code_mirror = CodeMirror(input_area.get(0), {
666 indentUnit : 4,
667 enterMode : 'flat',
668 tabMode: 'shift'
669 });
670 input.append(input_area);
671 var output = $('<div></div>').addClass('output vbox border-box-sizing');
672 cell.append(input).append(output);
673 this.element = cell;
674 this.collapse()
675 };
676
677
678 CodeCell.prototype.select = function () {
679 Cell.prototype.select.apply(this);
680 this.code_mirror.focus();
681 };
682
683
684 CodeCell.prototype.append_pyout = function (data, n) {
685 var toinsert = $("<div/>").addClass("output_area output_pyout hbox monospace-font");
686 toinsert.append($('<div/>').
687 addClass('prompt output_prompt').
688 html('Out[' + n + ']:')
689 );
690 this.append_display_data(data, toinsert);
691 toinsert.children().last().addClass("box_flex1");
692 this.element.find("div.output").append(toinsert);
693 // If we just output latex, typeset it.
694 if (data["text/latex"] !== undefined) {
695 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
696 };
697 };
698
699
700 CodeCell.prototype.append_pyerr = function (ename, evalue, tb) {
701 var s = '';
702 var len = tb.length;
703 for (var i=0; i<len; i++) {
704 s = s + tb[i] + '\n';
705 }
706 s = s + '\n';
707 this.append_stream(s);
708 };
709
710
711 CodeCell.prototype.append_display_data = function (data, element) {
712 console.log(data);
713 if (data["text/latex"] !== undefined) {
714 this.append_latex(data["text/latex"], element);
715 // If it is undefined, then we just appended to div.output, which
716 // makes the latex visible and we can typeset it. The typesetting
717 // has to be done after the latex is on the page.
718 if (element === undefined) {
719 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
720 };
721 } else if (data["image/svg+xml"] !== undefined) {
722 this.append_svg(data["image/svg+xml"], element);
723 } else if (data["image/png"] !== undefined) {
724 this.append_png(data["image/png"], element);
725 } else if (data["text/plain"] !== undefined) {
726 this.append_stream(data["text/plain"], element);
727 };
728 return element;
729 };
730
731
732 CodeCell.prototype.append_stream = function (data, element) {
733 element = element || this.element.find("div.output");
734 var toinsert = $("<div/>").addClass("output_area output_stream monospace-font");
735 toinsert.append($("<pre/>").addClass("monospace-font").html(fixConsole(data)));
736 element.append(toinsert);
737 return element;
738 };
739
740
741 CodeCell.prototype.append_svg = function (svg, element) {
742 element = element || this.element.find("div.output");
743 var toinsert = $("<div/>").addClass("output_area output_svg");
744 toinsert.append(svg);
745 element.append(toinsert);
746 return element;
747 };
748
749
750 CodeCell.prototype.append_png = function (png, element) {
751 element = element || this.element.find("div.output");
752 var toinsert = $("<div/>").addClass("output_area output_png");
753 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
754 element.append(toinsert);
755 return element;
756 };
757
758
759 CodeCell.prototype.append_latex = function (latex, element) {
760 // This method cannot do the typesetting because the latex first has to
761 // be on the page.
762 element = element || this.element.find("div.output");
763 var toinsert = $("<div/>").addClass("output_area output_latex monospace-font");
764 toinsert.append(latex);
765 element.append(toinsert);
766 return element;
767 }
768
769
770 CodeCell.prototype.clear_output = function () {
771 this.element.find("div.output").html("");
772 };
773
774
775 CodeCell.prototype.collapse = function () {
776 this.element.find('div.output').hide();
777 };
778
779
780 CodeCell.prototype.expand = function () {
781 this.element.find('div.output').show();
782 };
783
784
785 CodeCell.prototype.set_input_prompt = function (number) {
786 var n = number || ' ';
787 this.input_prompt_number = n
788 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
789 };
790
791
792 CodeCell.prototype.get_code = function () {
793 return this.code_mirror.getValue();
794 };
795
796
797 CodeCell.prototype.set_code = function (code) {
798 return this.code_mirror.setValue(code);
799 };
800
801
802 CodeCell.prototype.at_top = function () {
803 var cursor = this.code_mirror.getCursor();
804 if (cursor.line === 0) {
805 return true;
806 } else {
807 return false;
808 }
809 };
810
811
812 CodeCell.prototype.at_bottom = function () {
813 var cursor = this.code_mirror.getCursor();
814 if (cursor.line === (this.code_mirror.lineCount()-1)) {
815 return true;
816 } else {
817 return false;
818 }
819 };
820
821
822 CodeCell.prototype.fromJSON = function (data) {
823 if (data.cell_type === 'code') {
824 this.set_code(data.code);
825 this.set_input_prompt(data.prompt_number);
826 };
827 };
828
829
830 CodeCell.prototype.toJSON = function () {
831 return {
832 code : this.get_code(),
833 cell_type : 'code',
834 prompt_number : this.input_prompt_number
835 };
836 };
837
838 //============================================================================
839 // TextCell
840 //============================================================================
841
842
843 var TextCell = function (notebook) {
844 Cell.apply(this, arguments);
845 this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$"
846 this.rendered = false;
847 };
848
849
850 TextCell.prototype = new Cell();
851
852
853 TextCell.prototype.create_element = function () {
854 var cell = $("<div>").addClass('cell text_cell').
855 append(
856 $("<textarea>" + this.placeholder + "</textarea>").
857 addClass('text_cell_input monospace-font').
858 attr('rows',1).
859 attr('cols',80).
860 autogrow()
861 ).append(
862 // The tabindex=-1 makes this div focusable.
863 $('<div></div>').addClass('text_cell_render').attr('tabindex','-1')
864 )
865 this.element = cell;
866 };
867
868
869 TextCell.prototype.bind_events = function () {
870 Cell.prototype.bind_events.apply(this);
871 var that = this;
872 this.element.keydown(function (event) {
873 if (event.which === 13) {
874 if (that.rendered) {
875 that.edit();
876 event.preventDefault();
877 };
878 };
879 });
880 };
881
882
883 TextCell.prototype.select = function () {
884 Cell.prototype.select.apply(this);
885 var output = this.element.find("div.text_cell_render");
886 output.trigger('focus');
887 };
888
889
890 TextCell.prototype.edit = function () {
891 if (this.rendered === true) {
892 var text_cell = this.element;
893 var input = text_cell.find("textarea.text_cell_input");
894 var output = text_cell.find("div.text_cell_render");
895 output.hide();
896 input.show().trigger('focus');
897 this.rendered = false;
898 };
899 };
900
901
902 TextCell.prototype.render = function () {
903 if (this.rendered === false) {
904 var text_cell = this.element;
905 var input = text_cell.find("textarea.text_cell_input");
906 var output = text_cell.find("div.text_cell_render");
907 var text = input.val();
908 if (text === "") {
909 text = this.placeholder;
910 input.val(text);
911 };
912 output.html(text)
913 input.html(text);
914 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
915 input.hide();
916 output.show();
917 this.rendered = true;
918 };
919 };
920
921
922 TextCell.prototype.config_mathjax = function () {
923 var text_cell = this.element;
924 var that = this;
925 text_cell.click(function () {
926 that.edit();
927 }).focusout(function () {
928 that.render();
929 });
930
931 text_cell.trigger("focusout");
932 };
933
934
935 TextCell.prototype.get_text = function() {
936 return this.element.find("textarea.text_cell_input").val();
937 };
938
939
940 TextCell.prototype.set_text = function(text) {
941 this.element.find("textarea.text_cell_input").val(text);
942 this.element.find("textarea.text_cell_input").html(text);
943 this.element.find("div.text_cell_render").html(text);
944 };
945
946
947 TextCell.prototype.at_top = function () {
948 if (this.rendered) {
949 return true;
950 } else {
951 return false;
952 }
953 };
954
955
956 TextCell.prototype.at_bottom = function () {
957 if (this.rendered) {
958 return true;
959 } else {
960 return false;
961 }
962 };
963
964
965 TextCell.prototype.fromJSON = function (data) {
966 if (data.cell_type === 'text') {
967 this.set_text(data.text);
968 this.grow(this.element.find("textarea.text_cell_input"));
969 };
970 }
971
972
973 TextCell.prototype.toJSON = function () {
974 return {
975 cell_type : 'text',
976 text : this.get_text(),
977 };
978 };
979
980 //============================================================================
981 // On document ready
982 //============================================================================
983
984
985 var Kernel = function () {
986 this.kernel_id = null;
987 this.base_url = "/kernels";
988 this.kernel_url = null;
989 };
990
991
992 Kernel.prototype.get_msg = function (msg_type, content) {
993 var msg = {
994 header : {
995 msg_id : uuid(),
996 username : "bgranger",
997 session: this.session_id,
998 msg_type : msg_type
999 },
1000 content : content,
1001 parent_header : {}
1002 };
1003 return msg;
1004 }
1005
1006 Kernel.prototype.start_kernel = function (callback, context) {
1007 var that = this;
1008 $.post(this.base_url,
1009 function (kernel_id) {
1010 that._handle_start_kernel(kernel_id, callback, context);
1011 },
1012 'json'
1013 );
1014 };
1015
1016
1017 Kernel.prototype._handle_start_kernel = function (kernel_id, callback, context) {
1018 this.kernel_id = kernel_id;
1019 this.kernel_url = this.base_url + "/" + this.kernel_id;
1020 this._start_channels();
1021 callback.call(context);
1022 };
1023
1024
1025 Kernel.prototype._start_channels = function () {
1026 var ws_url = "ws://127.0.0.1:8888" + this.kernel_url;
1027 this.shell_channel = new WebSocket(ws_url + "/shell");
1028 this.iopub_channel = new WebSocket(ws_url + "/iopub");
1029 }
1030
1031
1032 Kernel.prototype.execute = function (code) {
1033 var content = {
1034 code : code,
1035 silent : false,
1036 user_variables : [],
1037 user_expressions : {}
1038 };
1039 var msg = this.get_msg("execute_request", content);
1040 this.shell_channel.send(JSON.stringify(msg));
1041 return msg.header.msg_id;
1042 }
1043
1044
1045 Kernel.prototype.interrupt = function () {
1046 $.post(this.kernel_url + "/interrupt");
1047 };
1048
1049
1050 Kernel.prototype.restart = function () {
1051 this.status_restarting();
1052 url = this.kernel_url + "/restart"
1053 var that = this;
1054 $.post(url, function (kernel_id) {
1055 console.log("Kernel restarted: " + kernel_id);
1056 that.kernel_id = kernel_id;
1057 that.kernel_url = that.base_url + "/" + that.kernel_id;
1058 that.status_idle();
1059 }, 'json');
1060 };
1061
1062
1063 Kernel.prototype.status_busy = function () {
1064 $("#kernel_status").removeClass("status_idle");
1065 $("#kernel_status").removeClass("status_restarting");
1066 $("#kernel_status").addClass("status_busy");
1067 $("#kernel_status").text("Busy");
1068 };
1069
1070
1071 Kernel.prototype.status_idle = function () {
1072 $("#kernel_status").removeClass("status_busy");
1073 $("#kernel_status").removeClass("status_restarting");
1074 $("#kernel_status").addClass("status_idle");
1075 $("#kernel_status").text("Idle");
1076 };
1077
1078 Kernel.prototype.status_restarting = function () {
1079 $("#kernel_status").removeClass("status_busy");
1080 $("#kernel_status").removeClass("status_idle");
1081 $("#kernel_status").addClass("status_restarting");
1082 $("#kernel_status").text("Restarting");
1083 };
1084
1085 //============================================================================
1086 // On document ready
1087 //============================================================================
1088
1089
1090 $(document).ready(function () {
1091
1092 $('div#wrapper').addClass('vbox border-box-sizing')
1093 $('div.notebook').addClass('box-flex1 border-box-sizing')
1094
1095 MathJax.Hub.Config({
1096 tex2jax: {
1097 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
1098 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
1099 },
1100 displayAlign: 'left', // Change this to 'center' to center equations.
1101 "HTML-CSS": {
1102 styles: {'.MathJax_Display': {"margin": 0}}
1103 }
1104 });
1105
1106 IPYTHON.notebook = new Notebook('div.notebook');
1107 IPYTHON.notebook.insert_code_cell_after();
1108
1109 $("#menu_tabs").tabs();
1110
1111 $("#help_toolbar").buttonset();
1112
1113 $("#kernel_toolbar").buttonset();
1114 $("#interrupt_kernel").click(function () {IPYTHON.notebook.kernel.interrupt();});
1115 $("#restart_kernel").click(function () {IPYTHON.notebook.kernel.restart();});
1116 $("#kernel_status").addClass("status_idle");
1117
1118 $("#move_cell").buttonset();
1119 $("#move_up").button("option", "icons", {primary:"ui-icon-arrowthick-1-n"});
1120 $("#move_up").button("option", "text", false);
1121 $("#move_up").click(function () {IPYTHON.notebook.move_cell_up();});
1122 $("#move_down").button("option", "icons", {primary:"ui-icon-arrowthick-1-s"});
1123 $("#move_down").button("option", "text", false);
1124 $("#move_down").click(function () {IPYTHON.notebook.move_cell_down();});
1125
1126 $("#insert_delete").buttonset();
1127 $("#insert_cell_before").click(function () {IPYTHON.notebook.insert_code_cell_before();});
1128 $("#insert_cell_after").click(function () {IPYTHON.notebook.insert_code_cell_after();});
1129 $("#delete_cell").button("option", "icons", {primary:"ui-icon-closethick"});
1130 $("#delete_cell").button("option", "text", false);
1131 $("#delete_cell").click(function () {IPYTHON.notebook.delete_cell();});
1132
1133 $("#cell_type").buttonset();
1134 $("#to_code").click(function () {IPYTHON.notebook.text_to_code();});
1135 $("#to_text").click(function () {IPYTHON.notebook.code_to_text();});
1136
1137 $("#sort").buttonset();
1138 $("#sort_cells").click(function () {IPYTHON.notebook.sort_cells();});
1139
1140 $("#toggle").buttonset();
1141 $("#collapse").click(function () {IPYTHON.notebook.collapse();});
1142 $("#expand").click(function () {IPYTHON.notebook.expand();});
1143
1144 });
1145
General Comments 0
You need to be logged in to leave comments. Login now