##// END OF EJS Templates
Fixes to terminal mode execution (ctrl-enter).
Brian E. Granger -
Show More
@@ -1,313 +1,312
1 1
2 2 //============================================================================
3 3 // CodeCell
4 4 //============================================================================
5 5
6 6 var IPython = (function (IPython) {
7 7
8 8 var utils = IPython.utils;
9 9
10 10 var CodeCell = function (notebook) {
11 11 this.code_mirror = null;
12 12 this.input_prompt_number = ' ';
13 13 this.is_completing = false;
14 14 this.completion_cursor = null;
15 15 IPython.Cell.apply(this, arguments);
16 16 };
17 17
18 18
19 19 CodeCell.prototype = new IPython.Cell();
20 20
21 21
22 22 CodeCell.prototype.create_element = function () {
23 23 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
24 24 var input = $('<div></div>').addClass('input hbox');
25 25 input.append($('<div/>').addClass('prompt input_prompt'));
26 26 var input_area = $('<div/>').addClass('input_area box-flex1');
27 27 this.code_mirror = CodeMirror(input_area.get(0), {
28 28 indentUnit : 4,
29 29 enterMode : 'flat',
30 30 tabMode: 'shift',
31 31 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
32 32 });
33 33 input.append(input_area);
34 34 var output = $('<div></div>').addClass('output vbox');
35 35 cell.append(input).append(output);
36 36 this.element = cell;
37 37 this.collapse()
38 38 };
39 39
40 40
41 41 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
42 42 // This method gets called in CodeMirror's onKeyDown/onKeyPress handlers and
43 43 // is used to provide custom key handling. Its return value is used to determine
44 44 // if CodeMirror should ignore the event: true = ignore, false = don't ignore.
45 45 if (event.keyCode === 13 && event.shiftKey) {
46 46 // Always ignore shift-enter in CodeMirror as we handle it.
47 47 return true;
48 48 // } else if (event.keyCode == 32 && (event.ctrlKey || event.metaKey) && !event.altKey) {
49 49 } else if (event.keyCode == 9) {
50 50 var cur = editor.getCursor();
51 51 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim();
52 52 if (pre_cursor === "") {
53 53 // Don't autocomplete if the part of the line before the cursor is empty.
54 54 // In this case, let CodeMirror handle indentation.
55 55 return false;
56 56 } else {
57 57 // Autocomplete the current line.
58 58 event.stop();
59 59 var line = editor.getLine(cur.line);
60 60 this.is_completing = true;
61 61 this.completion_cursor = cur;
62 62 IPython.notebook.complete_cell(this, line, cur.ch);
63 63 return true;
64 64 }
65 65 } else {
66 66 if (this.is_completing && this.completion_cursor !== editor.getCursor()) {
67 67 this.is_completing = false;
68 68 this.completion_cursor = null;
69 console.log("Stopped completing early");
70 69 }
71 70 return false;
72 71 };
73 72 };
74 73
75 74
76 75 CodeCell.prototype.finish_completing = function (matched_text, matches) {
77 76 if (!this.is_completing || matches.length === 0) {return;}
78 77 // console.log("Got matches", matched_text, matches);
79 78
80 79 var that = this;
81 80 var cur = this.completion_cursor;
82 81 var complete = $('<div/>').addClass('completions');
83 82 var select = $('<select/>').attr('multiple','true');
84 83 for (var i=0; i<matches.length; ++i) {
85 84 select.append($('<option/>').text(matches[i]));
86 85 }
87 86 select.children().first().attr('selected','true');
88 87 select.attr('size',Math.min(10,matches.length));
89 88 var pos = this.code_mirror.cursorCoords();
90 89 complete.css('left',pos.x+'px');
91 90 complete.css('top',pos.yBot+'px');
92 91 complete.append(select);
93 92
94 93 $('body').append(complete);
95 94 var done = false;
96 95
97 96 var insert = function (selected_text) {
98 97 that.code_mirror.replaceRange(
99 98 selected_text,
100 99 {line: cur.line, ch: (cur.ch-matched_text.length)},
101 100 {line: cur.line, ch: cur.ch}
102 101 );
103 102 };
104 103
105 104 var close = function () {
106 105 if (done) return;
107 106 done = true;
108 107 complete.remove();
109 108 that.is_completing = false;
110 109 that.completion_cursor = null;
111 110 };
112 111
113 112 var pick = function () {
114 113 insert(select.val()[0]);
115 114 close();
116 115 setTimeout(function(){that.code_mirror.focus();}, 50);
117 116 };
118 117
119 118 select.blur(close);
120 119 select.keydown(function (event) {
121 120 var code = event.which;
122 121 if (code === 13 || code === 32) {
123 122 // Pressing SPACE or ENTER will cause a pick
124 123 event.stopPropagation();
125 124 event.preventDefault();
126 125 pick();
127 126 } else if (code === 38 || code === 40) {
128 127 // We don't want the document keydown handler to handle UP/DOWN,
129 128 // but we want the default action.
130 129 event.stopPropagation();
131 130 } else {
132 131 // All other key presses simple exit completion.
133 132 event.stopPropagation();
134 133 event.preventDefault();
135 134 close();
136 135 that.code_mirror.focus();
137 136 }
138 137 });
139 138 // Double click also causes a pick.
140 139 select.dblclick(pick);
141 140 select.focus();
142 141 };
143 142
144 143
145 144 CodeCell.prototype.select = function () {
146 145 IPython.Cell.prototype.select.apply(this);
147 146 this.code_mirror.focus();
148 147 };
149 148
150 149
151 150 CodeCell.prototype.append_pyout = function (data, n) {
152 151 var toinsert = $("<div/>").addClass("output_area output_pyout hbox");
153 152 toinsert.append($('<div/>').
154 153 addClass('prompt output_prompt').
155 154 html('Out[' + n + ']:')
156 155 );
157 156 this.append_display_data(data, toinsert);
158 157 toinsert.children().last().addClass("box_flex1");
159 158 this.element.find("div.output").append(toinsert);
160 159 // If we just output latex, typeset it.
161 160 if (data["text/latex"] !== undefined) {
162 161 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
163 162 };
164 163 };
165 164
166 165
167 166 CodeCell.prototype.append_pyerr = function (ename, evalue, tb) {
168 167 var s = '';
169 168 var len = tb.length;
170 169 for (var i=0; i<len; i++) {
171 170 s = s + tb[i] + '\n';
172 171 }
173 172 s = s + '\n';
174 173 this.append_stream(s);
175 174 };
176 175
177 176
178 177 CodeCell.prototype.append_display_data = function (data, element) {
179 178 if (data["text/latex"] !== undefined) {
180 179 this.append_latex(data["text/latex"], element);
181 180 // If it is undefined, then we just appended to div.output, which
182 181 // makes the latex visible and we can typeset it. The typesetting
183 182 // has to be done after the latex is on the page.
184 183 if (element === undefined) {
185 184 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
186 185 };
187 186 } else if (data["image/svg+xml"] !== undefined) {
188 187 this.append_svg(data["image/svg+xml"], element);
189 188 } else if (data["image/png"] !== undefined) {
190 189 this.append_png(data["image/png"], element);
191 190 } else if (data["text/plain"] !== undefined) {
192 191 this.append_stream(data["text/plain"], element);
193 192 };
194 193 return element;
195 194 };
196 195
197 196
198 197 CodeCell.prototype.append_stream = function (data, element) {
199 198 element = element || this.element.find("div.output");
200 199 var toinsert = $("<div/>").addClass("output_area output_stream");
201 200 toinsert.append($("<pre/>").html(utils.fixConsole(data)));
202 201 element.append(toinsert);
203 202 return element;
204 203 };
205 204
206 205
207 206 CodeCell.prototype.append_svg = function (svg, element) {
208 207 element = element || this.element.find("div.output");
209 208 var toinsert = $("<div/>").addClass("output_area output_svg");
210 209 toinsert.append(svg);
211 210 element.append(toinsert);
212 211 return element;
213 212 };
214 213
215 214
216 215 CodeCell.prototype.append_png = function (png, element) {
217 216 element = element || this.element.find("div.output");
218 217 var toinsert = $("<div/>").addClass("output_area output_png");
219 218 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
220 219 element.append(toinsert);
221 220 return element;
222 221 };
223 222
224 223
225 224 CodeCell.prototype.append_latex = function (latex, element) {
226 225 // This method cannot do the typesetting because the latex first has to
227 226 // be on the page.
228 227 element = element || this.element.find("div.output");
229 228 var toinsert = $("<div/>").addClass("output_area output_latex");
230 229 toinsert.append(latex);
231 230 element.append(toinsert);
232 231 return element;
233 232 }
234 233
235 234
236 235 CodeCell.prototype.clear_output = function () {
237 236 this.element.find("div.output").html("");
238 237 };
239 238
240 239
241 240 CodeCell.prototype.clear_input = function () {
242 241 this.code_mirror.setValue('');
243 242 };
244 243
245 244
246 245 CodeCell.prototype.collapse = function () {
247 246 this.element.find('div.output').hide();
248 247 };
249 248
250 249
251 250 CodeCell.prototype.expand = function () {
252 251 this.element.find('div.output').show();
253 252 };
254 253
255 254
256 255 CodeCell.prototype.set_input_prompt = function (number) {
257 256 var n = number || ' ';
258 257 this.input_prompt_number = n
259 258 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
260 259 };
261 260
262 261
263 262 CodeCell.prototype.get_code = function () {
264 263 return this.code_mirror.getValue();
265 264 };
266 265
267 266
268 267 CodeCell.prototype.set_code = function (code) {
269 268 return this.code_mirror.setValue(code);
270 269 };
271 270
272 271
273 272 CodeCell.prototype.at_top = function () {
274 273 var cursor = this.code_mirror.getCursor();
275 274 if (cursor.line === 0) {
276 275 return true;
277 276 } else {
278 277 return false;
279 278 }
280 279 };
281 280
282 281
283 282 CodeCell.prototype.at_bottom = function () {
284 283 var cursor = this.code_mirror.getCursor();
285 284 if (cursor.line === (this.code_mirror.lineCount()-1)) {
286 285 return true;
287 286 } else {
288 287 return false;
289 288 }
290 289 };
291 290
292 291
293 292 CodeCell.prototype.fromJSON = function (data) {
294 293 if (data.cell_type === 'code') {
295 294 this.set_code(data.code);
296 295 this.set_input_prompt(data.prompt_number);
297 296 };
298 297 };
299 298
300 299
301 300 CodeCell.prototype.toJSON = function () {
302 301 return {
303 302 code : this.get_code(),
304 303 cell_type : 'code',
305 304 prompt_number : this.input_prompt_number
306 305 };
307 306 };
308 307
309 308 IPython.CodeCell = CodeCell;
310 309
311 310 return IPython;
312 311 }(IPython));
313 312
@@ -1,618 +1,624
1 1
2 2 //============================================================================
3 3 // Notebook
4 4 //============================================================================
5 5
6 6 var IPython = (function (IPython) {
7 7
8 8 var utils = IPython.utils;
9 9
10 10 var Notebook = function (selector) {
11 11 this.element = $(selector);
12 12 this.element.scroll();
13 13 this.element.data("notebook", this);
14 14 this.next_prompt_number = 1;
15 15 this.kernel = null;
16 16 this.msg_cell_map = {};
17 17 this.filename = null;
18 18 this.notebook_load_re = /%notebook load/
19 19 this.notebook_save_re = /%notebook save/
20 20 this.notebook_filename_re = /(\w)+.ipynb/
21 21 this.style();
22 22 this.create_elements();
23 23 this.bind_events();
24 24 this.start_kernel();
25 25 };
26 26
27 27
28 28 Notebook.prototype.style = function () {
29 29 $('div#notebook').addClass('border-box-sizing');
30 30 };
31 31
32 32
33 33 Notebook.prototype.create_elements = function () {
34 34 // We add this end_space div to the end of the notebook div to:
35 35 // i) provide a margin between the last cell and the end of the notebook
36 36 // ii) to prevent the div from scrolling up when the last cell is being
37 37 // edited, but is too low on the page, which browsers will do automatically.
38 38 this.element.append($('<div class="end_space"></div>').height(50));
39 39 $('div#notebook').addClass('border-box-sizing');
40 40 };
41 41
42 42
43 43 Notebook.prototype.bind_events = function () {
44 44 var that = this;
45 45 $(document).keydown(function (event) {
46 46 // console.log(event);
47 47 if (event.which === 38) {
48 48 var cell = that.selected_cell();
49 49 if (cell.at_top()) {
50 50 event.preventDefault();
51 51 that.select_prev();
52 52 };
53 53 } else if (event.which === 40) {
54 54 var cell = that.selected_cell();
55 55 if (cell.at_bottom()) {
56 56 event.preventDefault();
57 57 that.select_next();
58 58 };
59 59 } else if (event.which === 13 && event.shiftKey) {
60 that.execute_selected_cell(true);
60 that.execute_selected_cell();
61 61 return false;
62 62 } else if (event.which === 13 && event.ctrlKey) {
63 that.execute_selected_cell(false);
64 that.selected_cell().clear_input();
63 that.execute_selected_cell({terminal:true});
65 64 return false;
66 65 };
67 66 });
68 67
69 68 this.element.bind('collapse_pager', function () {
70 69 var app_height = $('div#notebook_app').height(); // content height
71 70 var splitter_height = $('div#pager_splitter').outerHeight(true);
72 71 var new_height = app_height - splitter_height;
73 72 that.element.animate({height : new_height + 'px'}, 'fast');
74 73 });
75 74
76 75 this.element.bind('expand_pager', function () {
77 76 var app_height = $('div#notebook_app').height(); // content height
78 77 var splitter_height = $('div#pager_splitter').outerHeight(true);
79 78 var pager_height = $('div#pager').outerHeight(true);
80 79 var new_height = app_height - pager_height - splitter_height;
81 80 that.element.animate({height : new_height + 'px'}, 'fast');
82 81 });
83 82
84 83 this.element.bind('collapse_left_panel', function () {
85 84 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
86 85 var new_margin = splitter_width;
87 86 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
88 87 });
89 88
90 89 this.element.bind('expand_left_panel', function () {
91 90 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
92 91 var left_panel_width = IPython.left_panel.width;
93 92 var new_margin = splitter_width + left_panel_width;
94 93 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
95 94 });
96 95 };
97 96
98 97
99 98 Notebook.prototype.scroll_to_bottom = function () {
100 99 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
101 100 };
102 101
103 102 // Cell indexing, retrieval, etc.
104 103
105 104
106 105 Notebook.prototype.cell_elements = function () {
107 106 return this.element.children("div.cell");
108 107 }
109 108
110 109
111 110 Notebook.prototype.ncells = function (cell) {
112 111 return this.cell_elements().length;
113 112 }
114 113
115 114
116 115 // TODO: we are often calling cells as cells()[i], which we should optimize
117 116 // to cells(i) or a new method.
118 117 Notebook.prototype.cells = function () {
119 118 return this.cell_elements().toArray().map(function (e) {
120 119 return $(e).data("cell");
121 120 });
122 121 }
123 122
124 123
125 124 Notebook.prototype.find_cell_index = function (cell) {
126 125 var result = null;
127 126 this.cell_elements().filter(function (index) {
128 127 if ($(this).data("cell") === cell) {
129 128 result = index;
130 129 };
131 130 });
132 131 return result;
133 132 };
134 133
135 134
136 135 Notebook.prototype.index_or_selected = function (index) {
137 136 return index || this.selected_index() || 0;
138 137 }
139 138
140 139
141 140 Notebook.prototype.select = function (index) {
142 141 if (index !== undefined && index >= 0 && index < this.ncells()) {
143 142 if (this.selected_index() !== null) {
144 143 this.selected_cell().unselect();
145 144 };
146 145 this.cells()[index].select();
147 146 if (index === (this.ncells()-1)) {
148 147 this.scroll_to_bottom();
149 148 };
150 149 };
151 150 return this;
152 151 };
153 152
154 153
155 154 Notebook.prototype.select_next = function () {
156 155 var index = this.selected_index();
157 156 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
158 157 this.select(index+1);
159 158 };
160 159 return this;
161 160 };
162 161
163 162
164 163 Notebook.prototype.select_prev = function () {
165 164 var index = this.selected_index();
166 165 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
167 166 this.select(index-1);
168 167 };
169 168 return this;
170 169 };
171 170
172 171
173 172 Notebook.prototype.selected_index = function () {
174 173 var result = null;
175 174 this.cell_elements().filter(function (index) {
176 175 if ($(this).data("cell").selected === true) {
177 176 result = index;
178 177 };
179 178 });
180 179 return result;
181 180 };
182 181
183 182
184 183 Notebook.prototype.cell_for_msg = function (msg_id) {
185 184 var cell_id = this.msg_cell_map[msg_id];
186 185 var result = null;
187 186 this.cell_elements().filter(function (index) {
188 187 cell = $(this).data("cell");
189 188 if (cell.cell_id === cell_id) {
190 189 result = cell;
191 190 };
192 191 });
193 192 return result;
194 193 };
195 194
196 195
197 196 Notebook.prototype.selected_cell = function () {
198 197 return this.cell_elements().eq(this.selected_index()).data("cell");
199 198 }
200 199
201 200
202 201 // Cell insertion, deletion and moving.
203 202
204 203
205 204 Notebook.prototype.delete_cell = function (index) {
206 205 var i = index || this.selected_index();
207 206 if (i !== null && i >= 0 && i < this.ncells()) {
208 207 this.cell_elements().eq(i).remove();
209 208 if (i === (this.ncells())) {
210 209 this.select(i-1);
211 210 } else {
212 211 this.select(i);
213 212 };
214 213 };
215 214 return this;
216 215 };
217 216
218 217
219 218 Notebook.prototype.append_cell = function (cell) {
220 219 this.element.find('div.end_space').before(cell.element);
221 220 return this;
222 221 };
223 222
224 223
225 224 Notebook.prototype.insert_cell_after = function (cell, index) {
226 225 var ncells = this.ncells();
227 226 if (ncells === 0) {
228 227 this.append_cell(cell);
229 228 return this;
230 229 };
231 230 if (index >= 0 && index < ncells) {
232 231 this.cell_elements().eq(index).after(cell.element);
233 232 };
234 233 return this
235 234 };
236 235
237 236
238 237 Notebook.prototype.insert_cell_before = function (cell, index) {
239 238 var ncells = this.ncells();
240 239 if (ncells === 0) {
241 240 this.append_cell(cell);
242 241 return this;
243 242 };
244 243 if (index >= 0 && index < ncells) {
245 244 this.cell_elements().eq(index).before(cell.element);
246 245 };
247 246 return this;
248 247 };
249 248
250 249
251 250 Notebook.prototype.move_cell_up = function (index) {
252 251 var i = index || this.selected_index();
253 252 if (i !== null && i < this.ncells() && i > 0) {
254 253 var pivot = this.cell_elements().eq(i-1);
255 254 var tomove = this.cell_elements().eq(i);
256 255 if (pivot !== null && tomove !== null) {
257 256 tomove.detach();
258 257 pivot.before(tomove);
259 258 this.select(i-1);
260 259 };
261 260 };
262 261 return this;
263 262 }
264 263
265 264
266 265 Notebook.prototype.move_cell_down = function (index) {
267 266 var i = index || this.selected_index();
268 267 if (i !== null && i < (this.ncells()-1) && i >= 0) {
269 268 var pivot = this.cell_elements().eq(i+1)
270 269 var tomove = this.cell_elements().eq(i)
271 270 if (pivot !== null && tomove !== null) {
272 271 tomove.detach();
273 272 pivot.after(tomove);
274 273 this.select(i+1);
275 274 };
276 275 };
277 276 return this;
278 277 }
279 278
280 279
281 280 Notebook.prototype.sort_cells = function () {
282 281 var ncells = this.ncells();
283 282 var sindex = this.selected_index();
284 283 var swapped;
285 284 do {
286 285 swapped = false
287 286 for (var i=1; i<ncells; i++) {
288 287 current = this.cell_elements().eq(i).data("cell");
289 288 previous = this.cell_elements().eq(i-1).data("cell");
290 289 if (previous.input_prompt_number > current.input_prompt_number) {
291 290 this.move_cell_up(i);
292 291 swapped = true;
293 292 };
294 293 };
295 294 } while (swapped);
296 295 this.select(sindex);
297 296 return this;
298 297 };
299 298
300 299
301 300 Notebook.prototype.insert_code_cell_before = function (index) {
302 301 // TODO: Bounds check for i
303 302 var i = this.index_or_selected(index);
304 303 var cell = new IPython.CodeCell(this);
305 304 // cell.set_input_prompt(this.next_prompt_number);
306 305 cell.set_input_prompt();
307 306 this.next_prompt_number = this.next_prompt_number + 1;
308 307 this.insert_cell_before(cell, i);
309 308 this.select(this.find_cell_index(cell));
310 309 return this;
311 310 }
312 311
313 312
314 313 Notebook.prototype.insert_code_cell_after = function (index) {
315 314 // TODO: Bounds check for i
316 315 var i = this.index_or_selected(index);
317 316 var cell = new IPython.CodeCell(this);
318 317 // cell.set_input_prompt(this.next_prompt_number);
319 318 cell.set_input_prompt();
320 319 this.next_prompt_number = this.next_prompt_number + 1;
321 320 this.insert_cell_after(cell, i);
322 321 this.select(this.find_cell_index(cell));
323 322 return this;
324 323 }
325 324
326 325
327 326 Notebook.prototype.insert_text_cell_before = function (index) {
328 327 // TODO: Bounds check for i
329 328 var i = this.index_or_selected(index);
330 329 var cell = new IPython.TextCell(this);
331 330 cell.config_mathjax();
332 331 this.insert_cell_before(cell, i);
333 332 this.select(this.find_cell_index(cell));
334 333 return this;
335 334 }
336 335
337 336
338 337 Notebook.prototype.insert_text_cell_after = function (index) {
339 338 // TODO: Bounds check for i
340 339 var i = this.index_or_selected(index);
341 340 var cell = new IPython.TextCell(this);
342 341 cell.config_mathjax();
343 342 this.insert_cell_after(cell, i);
344 343 this.select(this.find_cell_index(cell));
345 344 return this;
346 345 }
347 346
348 347
349 348 Notebook.prototype.text_to_code = function (index) {
350 349 // TODO: Bounds check for i
351 350 var i = this.index_or_selected(index);
352 351 var source_element = this.cell_elements().eq(i);
353 352 var source_cell = source_element.data("cell");
354 353 if (source_cell instanceof IPython.TextCell) {
355 354 this.insert_code_cell_after(i);
356 355 var target_cell = this.cells()[i+1];
357 356 target_cell.set_code(source_cell.get_text());
358 357 source_element.remove();
359 358 };
360 359 };
361 360
362 361
363 362 Notebook.prototype.code_to_text = function (index) {
364 363 // TODO: Bounds check for i
365 364 var i = this.index_or_selected(index);
366 365 var source_element = this.cell_elements().eq(i);
367 366 var source_cell = source_element.data("cell");
368 367 if (source_cell instanceof IPython.CodeCell) {
369 368 this.insert_text_cell_after(i);
370 369 var target_cell = this.cells()[i+1];
371 370 var text = source_cell.get_code();
372 371 if (text === "") {text = target_cell.placeholder;};
373 372 target_cell.set_text(text);
374 373 source_element.remove();
375 374 target_cell.edit();
376 375 };
377 376 };
378 377
379 378
380 379 // Cell collapsing
381 380
382 381 Notebook.prototype.collapse = function (index) {
383 382 var i = this.index_or_selected(index);
384 383 this.cells()[i].collapse();
385 384 };
386 385
387 386
388 387 Notebook.prototype.expand = function (index) {
389 388 var i = this.index_or_selected(index);
390 389 this.cells()[i].expand();
391 390 };
392 391
393 392
394 393 // Kernel related things
395 394
396 395 Notebook.prototype.start_kernel = function () {
397 396 this.kernel = new IPython.Kernel();
398 397 this.kernel.start_kernel($.proxy(this.kernel_started, this));
399 398 };
400 399
401 400
402 401 Notebook.prototype.handle_shell_reply = function (e) {
403 402 reply = $.parseJSON(e.data);
404 403 var header = reply.header;
405 404 var content = reply.content;
406 405 var msg_type = header.msg_type;
407 406 // console.log(reply);
408 407 var cell = this.cell_for_msg(reply.parent_header.msg_id);
409 408 if (msg_type === "execute_reply") {
410 409 cell.set_input_prompt(content.execution_count);
411 410 } else if (msg_type === "complete_reply") {
412 411 cell.finish_completing(content.matched_text, content.matches);
413 412 };
414 413 var payload = content.payload || [];
415 414 this.handle_payload(payload);
416 415 };
417 416
418 417
419 418 Notebook.prototype.handle_payload = function (payload) {
420 419 var l = payload.length;
421 420 if (l > 0) {
422 421 IPython.pager.clear();
423 422 IPython.pager.expand();
424 423 };
425 424 for (var i=0; i<l; i++) {
426 425 IPython.pager.append_text(payload[i].text);
427 426 };
428 427 };
429 428
430 429
431 430 Notebook.prototype.handle_iopub_reply = function (e) {
432 431 reply = $.parseJSON(e.data);
433 432 var content = reply.content;
434 433 // console.log(reply);
435 434 var msg_type = reply.header.msg_type;
436 435 var cell = this.cell_for_msg(reply.parent_header.msg_id);
437 436 if (msg_type === "stream") {
438 437 cell.expand();
439 438 cell.append_stream(content.data + "\n");
440 439 } else if (msg_type === "display_data") {
441 440 cell.expand();
442 441 cell.append_display_data(content.data);
443 442 } else if (msg_type === "pyout") {
444 443 cell.expand();
445 444 cell.append_pyout(content.data, content.execution_count)
446 445 } else if (msg_type === "pyerr") {
447 446 cell.expand();
448 447 cell.append_pyerr(content.ename, content.evalue, content.traceback);
449 448 } else if (msg_type === "status") {
450 449 if (content.execution_state === "busy") {
451 450 IPython.kernel_status_widget.status_busy();
452 451 } else if (content.execution_state === "idle") {
453 452 IPython.kernel_status_widget.status_idle();
454 453 };
455 454 }
456 455 };
457 456
458 457
459 458 Notebook.prototype.kernel_started = function () {
460 459 console.log("Kernel started: ", this.kernel.kernel_id);
461 460 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
462 461 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
463 462 };
464 463
465 464
466 Notebook.prototype.execute_selected_cell = function (add_new) {
467 if (add_new === undefined) {add_new = true;};
465 Notebook.prototype.execute_selected_cell = function (options) {
466 // add_new: should a new cell be added if we are at the end of the nb
467 // terminal: execute in terminal mode, which stays in the current cell
468 default_options = {terminal: false, add_new: true}
469 $.extend(default_options, options)
468 470 var that = this;
469 471 var cell = that.selected_cell();
470 472 var cell_index = that.find_cell_index(cell);
471 473 // TODO: the logic here needs to be moved into appropriate
472 474 // methods of Notebook.
473 475 if (cell instanceof IPython.CodeCell) {
474 476 cell.clear_output();
475 477 var code = cell.get_code();
476 478 if (that.notebook_load_re.test(code)) {
477 479 var code_parts = code.split(' ');
478 480 if (code_parts.length === 3) {
479 481 that.load_notebook(code_parts[2]);
480 482 };
481 483 } else if (that.notebook_save_re.test(code)) {
482 484 var code_parts = code.split(' ');
483 485 if (code_parts.length === 3) {
484 486 that.save_notebook(code_parts[2]);
485 487 } else {
486 488 that.save_notebook()
487 489 };
488 490 } else {
489 491 var msg_id = that.kernel.execute(cell.get_code());
490 492 that.msg_cell_map[msg_id] = cell.cell_id;
491 493 };
492 494 } else if (cell instanceof IPython.TextCell) {
493 495 cell.render();
494 496 }
495 if ((cell_index === (that.ncells()-1)) && add_new) {
496 that.insert_code_cell_after();
497 // If we are adding a new cell at the end, scroll down to show it.
498 that.scroll_to_bottom();
497 if (default_options.terminal) {
498 cell.clear_input();
499 499 } else {
500 that.select(cell_index+1);
500 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
501 that.insert_code_cell_after();
502 // If we are adding a new cell at the end, scroll down to show it.
503 that.scroll_to_bottom();
504 } else {
505 that.select(cell_index+1);
506 };
501 507 };
502 508 };
503 509
504 510
505 511 Notebook.prototype.execute_all_cells = function () {
506 512 var ncells = this.ncells();
507 513 for (var i=0; i<ncells; i++) {
508 514 this.select(i);
509 this.execute_selected_cell(false);
515 this.execute_selected_cell({add_new:false});
510 516 };
511 517 this.scroll_to_bottom();
512 518 };
513 519
514 520
515 521 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
516 522 var msg_id = this.kernel.complete(line, cursor_pos);
517 523 this.msg_cell_map[msg_id] = cell.cell_id;
518 524 };
519 525
520 526 // Persistance and loading
521 527
522 528
523 529 Notebook.prototype.fromJSON = function (data) {
524 530 var ncells = this.ncells();
525 531 for (var i=0; i<ncells; i++) {
526 532 // Always delete cell 0 as they get renumbered as they are deleted.
527 533 this.delete_cell(0);
528 534 };
529 535 var new_cells = data.cells;
530 536 ncells = new_cells.length;
531 537 var cell_data = null;
532 538 for (var i=0; i<ncells; i++) {
533 539 cell_data = new_cells[i];
534 540 if (cell_data.cell_type == 'code') {
535 541 this.insert_code_cell_after();
536 542 this.selected_cell().fromJSON(cell_data);
537 543 } else if (cell_data.cell_type === 'text') {
538 544 this.insert_text_cell_after();
539 545 this.selected_cell().fromJSON(cell_data);
540 546 };
541 547 };
542 548 };
543 549
544 550
545 551 Notebook.prototype.toJSON = function () {
546 552 var cells = this.cells();
547 553 var ncells = cells.length;
548 554 cell_array = new Array(ncells);
549 555 for (var i=0; i<ncells; i++) {
550 556 cell_array[i] = cells[i].toJSON();
551 557 };
552 558 json = {
553 559 cells : cell_array
554 560 };
555 561 return json
556 562 };
557 563
558 564
559 565 Notebook.prototype.test_filename = function (filename) {
560 566 if (this.notebook_filename_re.test(filename)) {
561 567 return true;
562 568 } else {
563 569 var bad_filename = $('<div/>');
564 570 bad_filename.html(
565 571 "The filename you entered (" + filename + ") is not valid. Notebook filenames must have the following form: foo.ipynb"
566 572 );
567 573 bad_filename.dialog({title: 'Invalid filename', modal: true});
568 574 return false;
569 575 };
570 576 };
571 577
572 578 Notebook.prototype.save_notebook = function (filename) {
573 579 this.filename = filename || this.filename || '';
574 580 if (this.filename === '') {
575 581 var no_filename = $('<div/>');
576 582 no_filename.html(
577 583 "This notebook has no filename, please specify a filename of the form: foo.ipynb"
578 584 );
579 585 no_filename.dialog({title: 'Missing filename', modal: true});
580 586 return;
581 587 }
582 588 if (!this.test_filename(this.filename)) {return;}
583 589 var thedata = this.toJSON();
584 590 var settings = {
585 591 processData : false,
586 592 cache : false,
587 593 type : "PUT",
588 594 data : JSON.stringify(thedata),
589 595 success : function (data, status, xhr) {console.log(data);}
590 596 };
591 597 $.ajax("/notebooks/" + this.filename, settings);
592 598 };
593 599
594 600
595 601 Notebook.prototype.load_notebook = function (filename) {
596 602 if (!this.test_filename(filename)) {return;}
597 603 var that = this;
598 604 // We do the call with settings so we can set cache to false.
599 605 var settings = {
600 606 processData : false,
601 607 cache : false,
602 608 type : "GET",
603 609 dataType : "json",
604 610 success : function (data, status, xhr) {
605 611 that.fromJSON(data);
606 612 that.filename = filename;
607 613 that.kernel.restart();
608 614 }
609 615 };
610 616 $.ajax("/notebooks/" + filename, settings);
611 617 }
612 618
613 619 IPython.Notebook = Notebook;
614 620
615 621 return IPython;
616 622
617 623 }(IPython));
618 624
General Comments 0
You need to be logged in to leave comments. Login now