##// 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 3 // Notebook
68 4 //============================================================================
69 5
70 6
71 7 var Notebook = function (selector) {
72 8 this.element = $(selector);
73 9 this.element.scroll();
74 10 this.element.data("notebook", this);
75 11 this.next_prompt_number = 1;
76 12 this.kernel = null;
77 13 this.msg_cell_map = {};
78 14 this.filename = null;
79 15 this.notebook_load_re = /%notebook load/
80 16 this.notebook_save_re = /%notebook save/
81 17 this.notebook_filename_re = /(\w)+.ipynb/
82 18 this.bind_events();
83 19 this.start_kernel();
84 20 };
85 21
86 22
87 23 Notebook.prototype.bind_events = function () {
88 24 var that = this;
89 25 $(document).keydown(function (event) {
90 26 // console.log(event);
91 27 if (event.which === 38) {
92 28 var cell = that.selected_cell();
93 29 if (cell.at_top()) {
94 30 event.preventDefault();
95 31 that.select_prev();
96 32 };
97 33 } else if (event.which === 40) {
98 34 var cell = that.selected_cell();
99 35 if (cell.at_bottom()) {
100 36 event.preventDefault();
101 37 that.select_next();
102 38 };
103 39 } else if (event.which === 13 && event.shiftKey) {
104 40 // The focus is not quite working here.
105 41 var cell = that.selected_cell();
106 42 var cell_index = that.find_cell_index(cell);
107 43 // TODO: the logic here needs to be moved into appropriate
108 44 // methods of Notebook.
109 45 if (cell instanceof CodeCell) {
110 46 event.preventDefault();
111 47 cell.clear_output();
112 48 var code = cell.get_code();
113 49 if (that.notebook_load_re.test(code)) {
114 50 var code_parts = code.split(' ');
115 51 if (code_parts.length === 3) {
116 52 that.load_notebook(code_parts[2]);
117 53 };
118 54 } else if (that.notebook_save_re.test(code)) {
119 55 var code_parts = code.split(' ');
120 56 if (code_parts.length === 3) {
121 57 that.save_notebook(code_parts[2]);
122 58 } else {
123 59 that.save_notebook()
124 60 };
125 61 } else {
126 62 var msg_id = that.kernel.execute(cell.get_code());
127 63 that.msg_cell_map[msg_id] = cell.cell_id;
128 64 };
129 65 } else if (cell instanceof TextCell) {
130 66 event.preventDefault();
131 67 cell.render();
132 68 }
133 69 if (cell_index === (that.ncells()-1)) {
134 70 that.insert_code_cell_after();
135 71 } else {
136 72 that.select(cell_index+1);
137 73 };
138 74 };
139 75 });
140 76 };
141 77
142 78
143 79 // Cell indexing, retrieval, etc.
144 80
145 81
146 82 Notebook.prototype.cell_elements = function () {
147 83 return this.element.children("div.cell");
148 84 }
149 85
150 86
151 87 Notebook.prototype.ncells = function (cell) {
152 88 return this.cell_elements().length;
153 89 }
154 90
155 91
156 92 // TODO: we are often calling cells as cells()[i], which we should optimize
157 93 // to cells(i) or a new method.
158 94 Notebook.prototype.cells = function () {
159 95 return this.cell_elements().toArray().map(function (e) {
160 96 return $(e).data("cell");
161 97 });
162 98 }
163 99
164 100
165 101 Notebook.prototype.find_cell_index = function (cell) {
166 102 var result = null;
167 103 this.cell_elements().filter(function (index) {
168 104 if ($(this).data("cell") === cell) {
169 105 result = index;
170 106 };
171 107 });
172 108 return result;
173 109 };
174 110
175 111
176 112 Notebook.prototype.index_or_selected = function (index) {
177 113 return index || this.selected_index() || 0;
178 114 }
179 115
180 116
181 117 Notebook.prototype.select = function (index) {
182 118 if (index !== undefined && index >= 0 && index < this.ncells()) {
183 119 if (this.selected_index() !== null) {
184 120 this.selected_cell().unselect();
185 121 };
186 122 this.cells()[index].select();
187 123 };
188 124 return this;
189 125 };
190 126
191 127
192 128 Notebook.prototype.select_next = function () {
193 129 var index = this.selected_index();
194 130 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
195 131 this.select(index+1);
196 132 };
197 133 return this;
198 134 };
199 135
200 136
201 137 Notebook.prototype.select_prev = function () {
202 138 var index = this.selected_index();
203 139 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
204 140 this.select(index-1);
205 141 };
206 142 return this;
207 143 };
208 144
209 145
210 146 Notebook.prototype.selected_index = function () {
211 147 var result = null;
212 148 this.cell_elements().filter(function (index) {
213 149 if ($(this).data("cell").selected === true) {
214 150 result = index;
215 151 };
216 152 });
217 153 return result;
218 154 };
219 155
220 156
221 157 Notebook.prototype.cell_for_msg = function (msg_id) {
222 158 var cell_id = this.msg_cell_map[msg_id];
223 159 var result = null;
224 160 this.cell_elements().filter(function (index) {
225 161 cell = $(this).data("cell");
226 162 if (cell.cell_id === cell_id) {
227 163 result = cell;
228 164 };
229 165 });
230 166 return result;
231 167 };
232 168
233 169
234 170 Notebook.prototype.selected_cell = function () {
235 171 return this.cell_elements().eq(this.selected_index()).data("cell");
236 172 }
237 173
238 174
239 175 // Cell insertion, deletion and moving.
240 176
241 177
242 178 Notebook.prototype.delete_cell = function (index) {
243 179 var i = index || this.selected_index();
244 180 if (i !== null && i >= 0 && i < this.ncells()) {
245 181 this.cell_elements().eq(i).remove();
246 182 if (i === (this.ncells())) {
247 183 this.select(i-1);
248 184 } else {
249 185 this.select(i);
250 186 };
251 187 };
252 188 return this;
253 189 };
254 190
255 191
256 192 Notebook.prototype.append_cell = function (cell) {
257 193 this.element.append(cell.element);
258 194 return this;
259 195 };
260 196
261 197
262 198 Notebook.prototype.insert_cell_after = function (cell, index) {
263 199 var ncells = this.ncells();
264 200 if (ncells === 0) {
265 201 this.append_cell(cell);
266 202 return this;
267 203 };
268 204 if (index >= 0 && index < ncells) {
269 205 this.cell_elements().eq(index).after(cell.element);
270 206 };
271 207 return this
272 208 };
273 209
274 210
275 211 Notebook.prototype.insert_cell_before = function (cell, index) {
276 212 var ncells = this.ncells();
277 213 if (ncells === 0) {
278 214 this.append_cell(cell);
279 215 return this;
280 216 };
281 217 if (index >= 0 && index < ncells) {
282 218 this.cell_elements().eq(index).before(cell.element);
283 219 };
284 220 return this;
285 221 };
286 222
287 223
288 224 Notebook.prototype.move_cell_up = function (index) {
289 225 var i = index || this.selected_index();
290 226 if (i !== null && i < this.ncells() && i > 0) {
291 227 var pivot = this.cell_elements().eq(i-1);
292 228 var tomove = this.cell_elements().eq(i);
293 229 if (pivot !== null && tomove !== null) {
294 230 tomove.detach();
295 231 pivot.before(tomove);
296 232 this.select(i-1);
297 233 };
298 234 };
299 235 return this;
300 236 }
301 237
302 238
303 239 Notebook.prototype.move_cell_down = function (index) {
304 240 var i = index || this.selected_index();
305 241 if (i !== null && i < (this.ncells()-1) && i >= 0) {
306 242 var pivot = this.cell_elements().eq(i+1)
307 243 var tomove = this.cell_elements().eq(i)
308 244 if (pivot !== null && tomove !== null) {
309 245 tomove.detach();
310 246 pivot.after(tomove);
311 247 this.select(i+1);
312 248 };
313 249 };
314 250 return this;
315 251 }
316 252
317 253
318 254 Notebook.prototype.sort_cells = function () {
319 255 var ncells = this.ncells();
320 256 var sindex = this.selected_index();
321 257 var swapped;
322 258 do {
323 259 swapped = false
324 260 for (var i=1; i<ncells; i++) {
325 261 current = this.cell_elements().eq(i).data("cell");
326 262 previous = this.cell_elements().eq(i-1).data("cell");
327 263 if (previous.input_prompt_number > current.input_prompt_number) {
328 264 this.move_cell_up(i);
329 265 swapped = true;
330 266 };
331 267 };
332 268 } while (swapped);
333 269 this.select(sindex);
334 270 return this;
335 271 };
336 272
337 273
338 274 Notebook.prototype.insert_code_cell_before = function (index) {
339 275 // TODO: Bounds check for i
340 276 var i = this.index_or_selected(index);
341 277 var cell = new CodeCell(this);
342 278 cell.set_input_prompt(this.next_prompt_number);
343 279 this.next_prompt_number = this.next_prompt_number + 1;
344 280 this.insert_cell_before(cell, i);
345 281 this.select(this.find_cell_index(cell));
346 282 return this;
347 283 }
348 284
349 285
350 286 Notebook.prototype.insert_code_cell_after = function (index) {
351 287 // TODO: Bounds check for i
352 288 var i = this.index_or_selected(index);
353 289 var cell = new CodeCell(this);
354 290 cell.set_input_prompt(this.next_prompt_number);
355 291 this.next_prompt_number = this.next_prompt_number + 1;
356 292 this.insert_cell_after(cell, i);
357 293 this.select(this.find_cell_index(cell));
358 294 return this;
359 295 }
360 296
361 297
362 298 Notebook.prototype.insert_text_cell_before = function (index) {
363 299 // TODO: Bounds check for i
364 300 var i = this.index_or_selected(index);
365 301 var cell = new TextCell(this);
366 302 cell.config_mathjax();
367 303 this.insert_cell_before(cell, i);
368 304 this.select(this.find_cell_index(cell));
369 305 return this;
370 306 }
371 307
372 308
373 309 Notebook.prototype.insert_text_cell_after = function (index) {
374 310 // TODO: Bounds check for i
375 311 var i = this.index_or_selected(index);
376 312 var cell = new TextCell(this);
377 313 cell.config_mathjax();
378 314 this.insert_cell_after(cell, i);
379 315 this.select(this.find_cell_index(cell));
380 316 return this;
381 317 }
382 318
383 319
384 320 Notebook.prototype.text_to_code = function (index) {
385 321 // TODO: Bounds check for i
386 322 var i = this.index_or_selected(index);
387 323 var source_element = this.cell_elements().eq(i);
388 324 var source_cell = source_element.data("cell");
389 325 if (source_cell instanceof TextCell) {
390 326 this.insert_code_cell_after(i);
391 327 var target_cell = this.cells()[i+1];
392 328 target_cell.set_code(source_cell.get_text());
393 329 source_element.remove();
394 330 };
395 331 };
396 332
397 333
398 334 Notebook.prototype.code_to_text = function (index) {
399 335 // TODO: Bounds check for i
400 336 var i = this.index_or_selected(index);
401 337 var source_element = this.cell_elements().eq(i);
402 338 var source_cell = source_element.data("cell");
403 339 if (source_cell instanceof CodeCell) {
404 340 this.insert_text_cell_after(i);
405 341 var target_cell = this.cells()[i+1];
406 342 var text = source_cell.get_code();
407 343 if (text === "") {text = target_cell.placeholder;};
408 344 target_cell.set_text(text);
409 345 source_element.remove();
410 346 target_cell.edit();
411 347 };
412 348 };
413 349
414 350
415 351 // Cell collapsing
416 352
417 353 Notebook.prototype.collapse = function (index) {
418 354 var i = this.index_or_selected(index);
419 355 this.cells()[i].collapse();
420 356 };
421 357
422 358
423 359 Notebook.prototype.expand = function (index) {
424 360 var i = this.index_or_selected(index);
425 361 this.cells()[i].expand();
426 362 };
427 363
428 364
429 365 // Kernel related things
430 366
431 367 Notebook.prototype.start_kernel = function () {
432 368 this.kernel = new Kernel();
433 369 this.kernel.start_kernel(this._kernel_started, this);
434 370 };
435 371
436 372
437 373 Notebook.prototype._kernel_started = function () {
438 374 console.log("Kernel started: ", this.kernel.kernel_id);
439 375 var that = this;
440 376
441 377 this.kernel.shell_channel.onmessage = function (e) {
442 378 reply = $.parseJSON(e.data);
443 379 // console.log(reply);
444 380 var msg_type = reply.header.msg_type;
445 381 var cell = that.cell_for_msg(reply.parent_header.msg_id);
446 382 if (msg_type === "execute_reply") {
447 383 cell.set_input_prompt(reply.content.execution_count);
448 384 };
449 385 };
450 386
451 387 this.kernel.iopub_channel.onmessage = function (e) {
452 388 reply = $.parseJSON(e.data);
453 389 var content = reply.content;
454 390 console.log(reply);
455 391 var msg_type = reply.header.msg_type;
456 392 var cell = that.cell_for_msg(reply.parent_header.msg_id);
457 393 if (msg_type === "stream") {
458 394 cell.expand();
459 395 cell.append_stream(content.data + "\n");
460 396 } else if (msg_type === "display_data") {
461 397 cell.expand();
462 398 cell.append_display_data(content.data);
463 399 } else if (msg_type === "pyout") {
464 400 cell.expand();
465 401 cell.append_pyout(content.data, content.execution_count)
466 402 } else if (msg_type === "pyerr") {
467 403 cell.expand();
468 404 cell.append_pyerr(content.ename, content.evalue, content.traceback);
469 405 } else if (msg_type === "status") {
470 406 if (content.execution_state === "busy") {
471 407 that.kernel.status_busy();
472 408 } else if (content.execution_state === "idle") {
473 409 that.kernel.status_idle();
474 410 };
475 411 }
476 412 };
477 413 };
478 414
479 415
480 416 // Persistance and loading
481 417
482 418
483 419 Notebook.prototype.fromJSON = function (data) {
484 420 var ncells = this.ncells();
485 421 for (var i=0; i<ncells; i++) {
486 422 // Always delete cell 0 as they get renumbered as they are deleted.
487 423 this.delete_cell(0);
488 424 };
489 425 var new_cells = data.cells;
490 426 ncells = new_cells.length;
491 427 var cell_data = null;
492 428 for (var i=0; i<ncells; i++) {
493 429 cell_data = new_cells[i];
494 430 if (cell_data.cell_type == 'code') {
495 431 this.insert_code_cell_after();
496 432 this.selected_cell().fromJSON(cell_data);
497 433 } else if (cell_data.cell_type === 'text') {
498 434 this.insert_text_cell_after();
499 435 this.selected_cell().fromJSON(cell_data);
500 436 };
501 437 };
502 438 };
503 439
504 440
505 441 Notebook.prototype.toJSON = function () {
506 442 var cells = this.cells();
507 443 var ncells = cells.length;
508 444 cell_array = new Array(ncells);
509 445 for (var i=0; i<ncells; i++) {
510 446 cell_array[i] = cells[i].toJSON();
511 447 };
512 448 json = {
513 449 cells : cell_array
514 450 };
515 451 return json
516 452 };
517 453
518 454
519 455 Notebook.prototype.test_filename = function (filename) {
520 456 if (this.notebook_filename_re.test(filename)) {
521 457 return true;
522 458 } else {
523 459 var bad_filename = $('<div/>');
524 460 bad_filename.html(
525 461 "The filename you entered (" + filename + ") is not valid. Notebook filenames must have the following form: foo.ipynb"
526 462 );
527 463 bad_filename.dialog({title: 'Invalid filename', modal: true});
528 464 return false;
529 465 };
530 466 };
531 467
532 468 Notebook.prototype.save_notebook = function (filename) {
533 469 this.filename = filename || this.filename || '';
534 470 if (this.filename === '') {
535 471 var no_filename = $('<div/>');
536 472 no_filename.html(
537 473 "This notebook has no filename, please specify a filename of the form: foo.ipynb"
538 474 );
539 475 no_filename.dialog({title: 'Missing filename', modal: true});
540 476 return;
541 477 }
542 478 if (!this.test_filename(this.filename)) {return;}
543 479 var thedata = this.toJSON();
544 480 var settings = {
545 481 processData : false,
546 482 cache : false,
547 483 type : "PUT",
548 484 data : JSON.stringify(thedata),
549 485 success : function (data, status, xhr) {console.log(data);}
550 486 };
551 487 $.ajax("/notebooks/" + this.filename, settings);
552 488 };
553 489
554 490
555 491 Notebook.prototype.load_notebook = function (filename) {
556 492 if (!this.test_filename(filename)) {return;}
557 493 var that = this;
558 494 // We do the call with settings so we can set cache to false.
559 495 var settings = {
560 496 processData : false,
561 497 cache : false,
562 498 type : "GET",
563 499 dataType : "json",
564 500 success : function (data, status, xhr) {
565 501 that.fromJSON(data);
566 502 that.filename = filename;
567 503 that.kernel.restart();
568 504 }
569 505 };
570 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