##// END OF EJS Templates
Don't scroll to bottom when last cell is selected.
Brian E. Granger -
Show More
@@ -1,91 +1,87
1 1
2 2 //============================================================================
3 3 // Cell
4 4 //============================================================================
5 5
6 6 var IPython = (function (IPython) {
7 7
8 8 var utils = IPython.utils;
9 9
10 10 var Cell = function (notebook) {
11 11 this.notebook = notebook;
12 12 this.selected = false;
13 13 this.element = null;
14 14 this.create_element();
15 15 if (this.element !== null) {
16 16 this.set_autoindent(true);
17 17 this.element.data("cell", this);
18 18 this.bind_events();
19 19 }
20 20 this.cell_id = utils.uuid();
21 21 };
22 22
23 23
24 24 Cell.prototype.select = function () {
25 25 this.element.addClass('ui-widget-content ui-corner-all');
26 26 this.selected = true;
27 // TODO: we need to test across browsers to see if both of these are needed.
28 // In the meantime, there should not be any harm in having them both.
29 this.element.find('textarea').trigger('focusin');
30 this.element.find('textarea').trigger('focus');
31 27 };
32 28
33 29
34 30 Cell.prototype.unselect = function () {
35 31 this.element.removeClass('ui-widget-content ui-corner-all');
36 32 this.selected = false;
37 33 };
38 34
39 35
40 36 Cell.prototype.bind_events = function () {
41 37 var that = this;
42 38 var nb = that.notebook
43 39 that.element.click(function (event) {
44 40 if (that.selected === false) {
45 41 nb.select(nb.find_cell_index(that));
46 42 };
47 43 });
48 44 that.element.focusin(function (event) {
49 45 if (that.selected === false) {
50 46 nb.select(nb.find_cell_index(that));
51 47 };
52 48 });
53 49 };
54 50
55 51 Cell.prototype.grow = function(element) {
56 52 // Grow the cell by hand. This is used upon reloading from JSON, when the
57 53 // autogrow handler is not called.
58 54 var dom = element.get(0);
59 55 var lines_count = 0;
60 56 // modified split rule from
61 57 // http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424
62 58 var lines = dom.value.split(/\r|\r\n|\n/);
63 59 lines_count = lines.length;
64 60 if (lines_count >= 1) {
65 61 dom.rows = lines_count;
66 62 } else {
67 63 dom.rows = 1;
68 64 }
69 65 };
70 66
71 67
72 68 Cell.prototype.set_autoindent = function (state) {
73 69 if (state) {
74 70 this.code_mirror.setOption('tabMode', 'indent');
75 71 this.code_mirror.setOption('enterMode', 'indent');
76 72 } else {
77 73 this.code_mirror.setOption('tabMode', 'shift');
78 74 this.code_mirror.setOption('enterMode', 'flat');
79 75 }
80 76 };
81 77
82 78
83 79 // Subclasses must implement create_element.
84 80 Cell.prototype.create_element = function () {};
85 81
86 82 IPython.Cell = Cell;
87 83
88 84 return IPython;
89 85
90 86 }(IPython));
91 87
@@ -1,815 +1,813
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.dirty = false;
17 17 this.msg_cell_map = {};
18 18 this.style();
19 19 this.create_elements();
20 20 this.bind_events();
21 21 };
22 22
23 23
24 24 Notebook.prototype.style = function () {
25 25 $('div#notebook').addClass('border-box-sizing');
26 26 };
27 27
28 28
29 29 Notebook.prototype.create_elements = function () {
30 30 // We add this end_space div to the end of the notebook div to:
31 31 // i) provide a margin between the last cell and the end of the notebook
32 32 // ii) to prevent the div from scrolling up when the last cell is being
33 33 // edited, but is too low on the page, which browsers will do automatically.
34 this.element.append($('<div class="end_space"></div>').height(150));
34 var end_space = $('<div class="end_space"></div>').height(150);
35 this.element.append(end_space);
35 36 $('div#notebook').addClass('border-box-sizing');
36 37 };
37 38
38 39
39 40 Notebook.prototype.bind_events = function () {
40 41 var that = this;
41 42 $(document).keydown(function (event) {
42 43 // console.log(event);
43 44 if (event.which === 38) {
44 45 var cell = that.selected_cell();
45 46 if (cell.at_top()) {
46 47 event.preventDefault();
47 48 that.select_prev();
48 49 };
49 50 } else if (event.which === 40) {
50 51 var cell = that.selected_cell();
51 52 if (cell.at_bottom()) {
52 53 event.preventDefault();
53 54 that.select_next();
54 55 };
55 56 } else if (event.which === 13 && event.shiftKey) {
56 57 that.execute_selected_cell();
57 58 return false;
58 59 } else if (event.which === 13 && event.ctrlKey) {
59 60 that.execute_selected_cell({terminal:true});
60 61 return false;
61 62 };
62 63 });
63 64
64 65 this.element.bind('collapse_pager', function () {
65 66 var app_height = $('div#main_app').height(); // content height
66 67 var splitter_height = $('div#pager_splitter').outerHeight(true);
67 68 var new_height = app_height - splitter_height;
68 69 that.element.animate({height : new_height + 'px'}, 'fast');
69 70 });
70 71
71 72 this.element.bind('expand_pager', function () {
72 73 var app_height = $('div#main_app').height(); // content height
73 74 var splitter_height = $('div#pager_splitter').outerHeight(true);
74 75 var pager_height = $('div#pager').outerHeight(true);
75 76 var new_height = app_height - pager_height - splitter_height;
76 77 that.element.animate({height : new_height + 'px'}, 'fast');
77 78 });
78 79
79 80 this.element.bind('collapse_left_panel', function () {
80 81 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
81 82 var new_margin = splitter_width;
82 83 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
83 84 });
84 85
85 86 this.element.bind('expand_left_panel', function () {
86 87 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
87 88 var left_panel_width = IPython.left_panel.width;
88 89 var new_margin = splitter_width + left_panel_width;
89 90 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
90 91 });
91 92
92 93 $(window).bind('beforeunload', function () {
93 94 var kill_kernel = $('#kill_kernel').prop('checked');
94 95 if (kill_kernel) {
95 96 that.kernel.kill();
96 97 }
97 98 if (that.dirty) {
98 99 return "You have unsaved changes that will be lost if you leave this page.";
99 100 };
100 101 });
101 102 };
102 103
103 104
104 105 Notebook.prototype.scroll_to_bottom = function () {
105 106 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
106 107 };
107 108
108 109
109 110 Notebook.prototype.scroll_to_top = function () {
110 111 this.element.animate({scrollTop:0}, 0);
111 112 };
112 113
113 114
114 115 // Cell indexing, retrieval, etc.
115 116
116 117
117 118 Notebook.prototype.cell_elements = function () {
118 119 return this.element.children("div.cell");
119 120 }
120 121
121 122
122 123 Notebook.prototype.ncells = function (cell) {
123 124 return this.cell_elements().length;
124 125 }
125 126
126 127
127 128 // TODO: we are often calling cells as cells()[i], which we should optimize
128 129 // to cells(i) or a new method.
129 130 Notebook.prototype.cells = function () {
130 131 return this.cell_elements().toArray().map(function (e) {
131 132 return $(e).data("cell");
132 133 });
133 134 }
134 135
135 136
136 137 Notebook.prototype.find_cell_index = function (cell) {
137 138 var result = null;
138 139 this.cell_elements().filter(function (index) {
139 140 if ($(this).data("cell") === cell) {
140 141 result = index;
141 142 };
142 143 });
143 144 return result;
144 145 };
145 146
146 147
147 148 Notebook.prototype.index_or_selected = function (index) {
148 149 return index || this.selected_index() || 0;
149 150 }
150 151
151 152
152 153 Notebook.prototype.select = function (index) {
153 154 if (index !== undefined && index >= 0 && index < this.ncells()) {
154 155 if (this.selected_index() !== null) {
155 156 this.selected_cell().unselect();
156 157 };
157 158 this.cells()[index].select();
158 if (index === (this.ncells()-1)) {
159 this.scroll_to_bottom();
160 };
161 159 };
162 160 return this;
163 161 };
164 162
165 163
166 164 Notebook.prototype.select_next = function () {
167 165 var index = this.selected_index();
168 166 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
169 167 this.select(index+1);
170 168 };
171 169 return this;
172 170 };
173 171
174 172
175 173 Notebook.prototype.select_prev = function () {
176 174 var index = this.selected_index();
177 175 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
178 176 this.select(index-1);
179 177 };
180 178 return this;
181 179 };
182 180
183 181
184 182 Notebook.prototype.selected_index = function () {
185 183 var result = null;
186 184 this.cell_elements().filter(function (index) {
187 185 if ($(this).data("cell").selected === true) {
188 186 result = index;
189 187 };
190 188 });
191 189 return result;
192 190 };
193 191
194 192
195 193 Notebook.prototype.cell_for_msg = function (msg_id) {
196 194 var cell_id = this.msg_cell_map[msg_id];
197 195 var result = null;
198 196 this.cell_elements().filter(function (index) {
199 197 cell = $(this).data("cell");
200 198 if (cell.cell_id === cell_id) {
201 199 result = cell;
202 200 };
203 201 });
204 202 return result;
205 203 };
206 204
207 205
208 206 Notebook.prototype.selected_cell = function () {
209 207 return this.cell_elements().eq(this.selected_index()).data("cell");
210 208 }
211 209
212 210
213 211 // Cell insertion, deletion and moving.
214 212
215 213
216 214 Notebook.prototype.delete_cell = function (index) {
217 215 var i = index || this.selected_index();
218 216 if (i !== null && i >= 0 && i < this.ncells()) {
219 217 this.cell_elements().eq(i).remove();
220 218 if (i === (this.ncells())) {
221 219 this.select(i-1);
222 220 } else {
223 221 this.select(i);
224 222 };
225 223 };
226 224 this.dirty = true;
227 225 return this;
228 226 };
229 227
230 228
231 229 Notebook.prototype.append_cell = function (cell) {
232 230 this.element.find('div.end_space').before(cell.element);
233 231 this.dirty = true;
234 232 return this;
235 233 };
236 234
237 235
238 236 Notebook.prototype.insert_cell_after = function (cell, index) {
239 237 var ncells = this.ncells();
240 238 if (ncells === 0) {
241 239 this.append_cell(cell);
242 240 return this;
243 241 };
244 242 if (index >= 0 && index < ncells) {
245 243 this.cell_elements().eq(index).after(cell.element);
246 244 };
247 245 this.dirty = true;
248 246 return this
249 247 };
250 248
251 249
252 250 Notebook.prototype.insert_cell_before = function (cell, index) {
253 251 var ncells = this.ncells();
254 252 if (ncells === 0) {
255 253 this.append_cell(cell);
256 254 return this;
257 255 };
258 256 if (index >= 0 && index < ncells) {
259 257 this.cell_elements().eq(index).before(cell.element);
260 258 };
261 259 this.dirty = true;
262 260 return this;
263 261 };
264 262
265 263
266 264 Notebook.prototype.move_cell_up = function (index) {
267 265 var i = index || this.selected_index();
268 266 if (i !== null && i < this.ncells() && i > 0) {
269 267 var pivot = this.cell_elements().eq(i-1);
270 268 var tomove = this.cell_elements().eq(i);
271 269 if (pivot !== null && tomove !== null) {
272 270 tomove.detach();
273 271 pivot.before(tomove);
274 272 this.select(i-1);
275 273 };
276 274 };
277 275 this.dirty = true;
278 276 return this;
279 277 }
280 278
281 279
282 280 Notebook.prototype.move_cell_down = function (index) {
283 281 var i = index || this.selected_index();
284 282 if (i !== null && i < (this.ncells()-1) && i >= 0) {
285 283 var pivot = this.cell_elements().eq(i+1)
286 284 var tomove = this.cell_elements().eq(i)
287 285 if (pivot !== null && tomove !== null) {
288 286 tomove.detach();
289 287 pivot.after(tomove);
290 288 this.select(i+1);
291 289 };
292 290 };
293 291 this.dirty = true;
294 292 return this;
295 293 }
296 294
297 295
298 296 Notebook.prototype.sort_cells = function () {
299 297 var ncells = this.ncells();
300 298 var sindex = this.selected_index();
301 299 var swapped;
302 300 do {
303 301 swapped = false
304 302 for (var i=1; i<ncells; i++) {
305 303 current = this.cell_elements().eq(i).data("cell");
306 304 previous = this.cell_elements().eq(i-1).data("cell");
307 305 if (previous.input_prompt_number > current.input_prompt_number) {
308 306 this.move_cell_up(i);
309 307 swapped = true;
310 308 };
311 309 };
312 310 } while (swapped);
313 311 this.select(sindex);
314 312 return this;
315 313 };
316 314
317 315
318 316 Notebook.prototype.insert_code_cell_before = function (index) {
319 317 // TODO: Bounds check for i
320 318 var i = this.index_or_selected(index);
321 319 var cell = new IPython.CodeCell(this);
322 320 cell.set_input_prompt();
323 321 this.insert_cell_before(cell, i);
324 322 this.select(this.find_cell_index(cell));
325 323 return cell;
326 324 }
327 325
328 326
329 327 Notebook.prototype.insert_code_cell_after = function (index) {
330 328 // TODO: Bounds check for i
331 329 var i = this.index_or_selected(index);
332 330 var cell = new IPython.CodeCell(this);
333 331 cell.set_input_prompt();
334 332 this.insert_cell_after(cell, i);
335 333 this.select(this.find_cell_index(cell));
336 334 return cell;
337 335 }
338 336
339 337
340 338 Notebook.prototype.insert_html_cell_before = function (index) {
341 339 // TODO: Bounds check for i
342 340 var i = this.index_or_selected(index);
343 341 var cell = new IPython.HTMLCell(this);
344 342 cell.config_mathjax();
345 343 this.insert_cell_before(cell, i);
346 344 this.select(this.find_cell_index(cell));
347 345 return cell;
348 346 }
349 347
350 348
351 349 Notebook.prototype.insert_html_cell_after = function (index) {
352 350 // TODO: Bounds check for i
353 351 var i = this.index_or_selected(index);
354 352 var cell = new IPython.HTMLCell(this);
355 353 cell.config_mathjax();
356 354 this.insert_cell_after(cell, i);
357 355 this.select(this.find_cell_index(cell));
358 356 return cell;
359 357 }
360 358
361 359
362 360 Notebook.prototype.insert_markdown_cell_before = function (index) {
363 361 // TODO: Bounds check for i
364 362 var i = this.index_or_selected(index);
365 363 var cell = new IPython.MarkdownCell(this);
366 364 cell.config_mathjax();
367 365 this.insert_cell_before(cell, i);
368 366 this.select(this.find_cell_index(cell));
369 367 return cell;
370 368 }
371 369
372 370
373 371 Notebook.prototype.insert_markdown_cell_after = function (index) {
374 372 // TODO: Bounds check for i
375 373 var i = this.index_or_selected(index);
376 374 var cell = new IPython.MarkdownCell(this);
377 375 cell.config_mathjax();
378 376 this.insert_cell_after(cell, i);
379 377 this.select(this.find_cell_index(cell));
380 378 return cell;
381 379 }
382 380
383 381
384 382 Notebook.prototype.to_code = function (index) {
385 383 // TODO: Bounds check for i
386 384 var i = this.index_or_selected(index);
387 385 var source_element = this.cell_elements().eq(i);
388 386 var source_cell = source_element.data("cell");
389 387 if (source_cell instanceof IPython.HTMLCell ||
390 388 source_cell instanceof IPython.MarkdownCell) {
391 389 this.insert_code_cell_after(i);
392 390 var target_cell = this.cells()[i+1];
393 391 target_cell.set_code(source_cell.get_source());
394 392 source_element.remove();
395 393 target_cell.select();
396 394 };
397 395 this.dirty = true;
398 396 };
399 397
400 398
401 399 Notebook.prototype.to_markdown = function (index) {
402 400 // TODO: Bounds check for i
403 401 var i = this.index_or_selected(index);
404 402 var source_element = this.cell_elements().eq(i);
405 403 var source_cell = source_element.data("cell");
406 404 var target_cell = null;
407 405 if (source_cell instanceof IPython.CodeCell) {
408 406 this.insert_markdown_cell_after(i);
409 407 var target_cell = this.cells()[i+1];
410 408 var text = source_cell.get_code();
411 409 } else if (source_cell instanceof IPython.HTMLCell) {
412 410 this.insert_markdown_cell_after(i);
413 411 var target_cell = this.cells()[i+1];
414 412 var text = source_cell.get_source();
415 413 if (text === source_cell.placeholder) {
416 414 text = target_cell.placeholder;
417 415 }
418 416 }
419 417 if (target_cell !== null) {
420 418 if (text === "") {text = target_cell.placeholder;};
421 419 target_cell.set_source(text);
422 420 source_element.remove();
423 421 target_cell.edit();
424 422 }
425 423 this.dirty = true;
426 424 };
427 425
428 426
429 427 Notebook.prototype.to_html = function (index) {
430 428 // TODO: Bounds check for i
431 429 var i = this.index_or_selected(index);
432 430 var source_element = this.cell_elements().eq(i);
433 431 var source_cell = source_element.data("cell");
434 432 var target_cell = null;
435 433 if (source_cell instanceof IPython.CodeCell) {
436 434 this.insert_html_cell_after(i);
437 435 var target_cell = this.cells()[i+1];
438 436 var text = source_cell.get_code();
439 437 } else if (source_cell instanceof IPython.MarkdownCell) {
440 438 this.insert_html_cell_after(i);
441 439 var target_cell = this.cells()[i+1];
442 440 var text = source_cell.get_source();
443 441 if (text === source_cell.placeholder) {
444 442 text = target_cell.placeholder;
445 443 }
446 444 }
447 445 if (target_cell !== null) {
448 446 if (text === "") {text = target_cell.placeholder;};
449 447 target_cell.set_source(text);
450 448 source_element.remove();
451 449 target_cell.edit();
452 450 }
453 451 this.dirty = true;
454 452 };
455 453
456 454
457 455 // Cell collapsing and output clearing
458 456
459 457 Notebook.prototype.collapse = function (index) {
460 458 var i = this.index_or_selected(index);
461 459 this.cells()[i].collapse();
462 460 this.dirty = true;
463 461 };
464 462
465 463
466 464 Notebook.prototype.expand = function (index) {
467 465 var i = this.index_or_selected(index);
468 466 this.cells()[i].expand();
469 467 this.dirty = true;
470 468 };
471 469
472 470
473 471 Notebook.prototype.set_autoindent = function (state) {
474 472 var cells = this.cells();
475 473 len = cells.length;
476 474 for (var i=0; i<len; i++) {
477 475 cells[i].set_autoindent(state)
478 476 };
479 477 };
480 478
481 479
482 480 Notebook.prototype.clear_all_output = function () {
483 481 var ncells = this.ncells();
484 482 var cells = this.cells();
485 483 for (var i=0; i<ncells; i++) {
486 484 if (cells[i] instanceof IPython.CodeCell) {
487 485 cells[i].clear_output();
488 486 }
489 487 };
490 488 this.dirty = true;
491 489 };
492 490
493 491
494 492 // Kernel related things
495 493
496 494 Notebook.prototype.start_kernel = function () {
497 495 this.kernel = new IPython.Kernel();
498 496 var notebook_id = IPython.save_widget.get_notebook_id();
499 497 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
500 498 };
501 499
502 500
503 501 Notebook.prototype.restart_kernel = function () {
504 502 var notebook_id = IPython.save_widget.get_notebook_id();
505 503 this.kernel.restart($.proxy(this.kernel_started, this));
506 504 };
507 505
508 506
509 507 Notebook.prototype.kernel_started = function () {
510 508 console.log("Kernel started: ", this.kernel.kernel_id);
511 509 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
512 510 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
513 511 };
514 512
515 513
516 514 Notebook.prototype.handle_shell_reply = function (e) {
517 515 reply = $.parseJSON(e.data);
518 516 var header = reply.header;
519 517 var content = reply.content;
520 518 var msg_type = header.msg_type;
521 519 // console.log(reply);
522 520 var cell = this.cell_for_msg(reply.parent_header.msg_id);
523 521 if (msg_type === "execute_reply") {
524 522 cell.set_input_prompt(content.execution_count);
525 523 this.dirty = true;
526 524 } else if (msg_type === "complete_reply") {
527 525 cell.finish_completing(content.matched_text, content.matches);
528 526 };
529 527 var payload = content.payload || [];
530 528 this.handle_payload(cell, payload);
531 529 };
532 530
533 531
534 532 Notebook.prototype.handle_payload = function (cell, payload) {
535 533 var l = payload.length;
536 534 for (var i=0; i<l; i++) {
537 535 if (payload[i].source === 'IPython.zmq.page.page') {
538 536 if (payload[i].text.trim() !== '') {
539 537 IPython.pager.clear();
540 538 IPython.pager.expand();
541 539 IPython.pager.append_text(payload[i].text);
542 540 }
543 541 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
544 542 var index = this.find_cell_index(cell);
545 543 var new_cell = this.insert_code_cell_after(index);
546 544 new_cell.set_code(payload[i].text);
547 545 this.dirty = true;
548 546 }
549 547 };
550 548 };
551 549
552 550
553 551 Notebook.prototype.handle_iopub_reply = function (e) {
554 552 reply = $.parseJSON(e.data);
555 553 var content = reply.content;
556 554 // console.log(reply);
557 555 var msg_type = reply.header.msg_type;
558 556 var cell = this.cell_for_msg(reply.parent_header.msg_id);
559 557 var output_types = ['stream','display_data','pyout','pyerr'];
560 558 if (output_types.indexOf(msg_type) >= 0) {
561 559 this.handle_output(cell, msg_type, content);
562 560 } else if (msg_type === 'status') {
563 561 if (content.execution_state === 'busy') {
564 562 IPython.kernel_status_widget.status_busy();
565 563 } else if (content.execution_state === 'idle') {
566 564 IPython.kernel_status_widget.status_idle();
567 565 } else if (content.execution_state === 'dead') {
568 566 this.handle_status_dead();
569 567 };
570 568 }
571 569 };
572 570
573 571
574 572 Notebook.prototype.handle_status_dead = function () {
575 573 var that = this;
576 574 this.kernel.stop_channels();
577 575 var dialog = $('<div/>');
578 576 dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.');
579 577 $(document).append(dialog);
580 578 dialog.dialog({
581 579 resizable: false,
582 580 modal: true,
583 581 title: "Dead kernel",
584 582 buttons : {
585 583 "Yes": function () {
586 584 that.start_kernel();
587 585 $(this).dialog('close');
588 586 },
589 587 "No": function () {
590 588 $(this).dialog('close');
591 589 }
592 590 }
593 591 });
594 592 };
595 593
596 594
597 595 Notebook.prototype.handle_output = function (cell, msg_type, content) {
598 596 var json = {};
599 597 json.output_type = msg_type;
600 598 if (msg_type === "stream") {
601 599 json.text = utils.fixConsole(content.data + '\n');
602 600 } else if (msg_type === "display_data") {
603 601 json = this.convert_mime_types(json, content.data);
604 602 } else if (msg_type === "pyout") {
605 603 json.prompt_number = content.execution_count;
606 604 json = this.convert_mime_types(json, content.data);
607 605 } else if (msg_type === "pyerr") {
608 606 json.ename = content.ename;
609 607 json.evalue = content.evalue;
610 608 var traceback = [];
611 609 for (var i=0; i<content.traceback.length; i++) {
612 610 traceback.push(utils.fixConsole(content.traceback[i]));
613 611 }
614 612 json.traceback = traceback;
615 613 };
616 614 cell.append_output(json);
617 615 this.dirty = true;
618 616 };
619 617
620 618
621 619 Notebook.prototype.convert_mime_types = function (json, data) {
622 620 if (data['text/plain'] !== undefined) {
623 621 json.text = utils.fixConsole(data['text/plain']);
624 622 };
625 623 if (data['text/html'] !== undefined) {
626 624 json.html = data['text/html'];
627 625 };
628 626 if (data['image/svg+xml'] !== undefined) {
629 627 json.svg = data['image/svg+xml'];
630 628 };
631 629 if (data['image/png'] !== undefined) {
632 630 json.png = data['image/png'];
633 631 };
634 632 if (data['image/jpeg'] !== undefined) {
635 633 json.jpeg = data['image/jpeg'];
636 634 };
637 635 if (data['text/latex'] !== undefined) {
638 636 json.latex = data['text/latex'];
639 637 };
640 638 if (data['application/json'] !== undefined) {
641 639 json.json = data['application/json'];
642 640 };
643 641 if (data['application/javascript'] !== undefined) {
644 642 json.javascript = data['application/javascript'];
645 643 }
646 644 return json;
647 645 };
648 646
649 647
650 648 Notebook.prototype.execute_selected_cell = function (options) {
651 649 // add_new: should a new cell be added if we are at the end of the nb
652 650 // terminal: execute in terminal mode, which stays in the current cell
653 651 default_options = {terminal: false, add_new: true}
654 652 $.extend(default_options, options)
655 653 var that = this;
656 654 var cell = that.selected_cell();
657 655 var cell_index = that.find_cell_index(cell);
658 656 if (cell instanceof IPython.CodeCell) {
659 657 cell.clear_output();
660 658 var code = cell.get_code();
661 659 var msg_id = that.kernel.execute(cell.get_code());
662 660 that.msg_cell_map[msg_id] = cell.cell_id;
663 661 } else if (cell instanceof IPython.HTMLCell) {
664 662 cell.render();
665 663 }
666 664 if (default_options.terminal) {
667 665 cell.clear_input();
668 666 } else {
669 667 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
670 668 that.insert_code_cell_after();
671 669 // If we are adding a new cell at the end, scroll down to show it.
672 670 that.scroll_to_bottom();
673 671 } else {
674 672 that.select(cell_index+1);
675 673 };
676 674 };
677 675 this.dirty = true;
678 676 };
679 677
680 678
681 679 Notebook.prototype.execute_all_cells = function () {
682 680 var ncells = this.ncells();
683 681 for (var i=0; i<ncells; i++) {
684 682 this.select(i);
685 683 this.execute_selected_cell({add_new:false});
686 684 };
687 685 this.scroll_to_bottom();
688 686 };
689 687
690 688
691 689 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
692 690 var msg_id = this.kernel.complete(line, cursor_pos);
693 691 this.msg_cell_map[msg_id] = cell.cell_id;
694 692 };
695 693
696 694 // Persistance and loading
697 695
698 696
699 697 Notebook.prototype.fromJSON = function (data) {
700 698 var ncells = this.ncells();
701 699 for (var i=0; i<ncells; i++) {
702 700 // Always delete cell 0 as they get renumbered as they are deleted.
703 701 this.delete_cell(0);
704 702 };
705 703 // Only handle 1 worksheet for now.
706 704 var worksheet = data.worksheets[0];
707 705 if (worksheet !== undefined) {
708 706 var new_cells = worksheet.cells;
709 707 ncells = new_cells.length;
710 708 var cell_data = null;
711 709 var new_cell = null;
712 710 for (var i=0; i<ncells; i++) {
713 711 cell_data = new_cells[i];
714 712 if (cell_data.cell_type == 'code') {
715 713 new_cell = this.insert_code_cell_after();
716 714 new_cell.fromJSON(cell_data);
717 715 } else if (cell_data.cell_type === 'html') {
718 716 new_cell = this.insert_html_cell_after();
719 717 new_cell.fromJSON(cell_data);
720 718 } else if (cell_data.cell_type === 'markdown') {
721 719 new_cell = this.insert_markdown_cell_after();
722 720 new_cell.fromJSON(cell_data);
723 721 };
724 722 };
725 723 };
726 724 };
727 725
728 726
729 727 Notebook.prototype.toJSON = function () {
730 728 var cells = this.cells();
731 729 var ncells = cells.length;
732 730 cell_array = new Array(ncells);
733 731 for (var i=0; i<ncells; i++) {
734 732 cell_array[i] = cells[i].toJSON();
735 733 };
736 734 data = {
737 735 // Only handle 1 worksheet for now.
738 736 worksheets : [{cells:cell_array}]
739 737 }
740 738 return data
741 739 };
742 740
743 741 Notebook.prototype.save_notebook = function () {
744 742 if (IPython.save_widget.test_notebook_name()) {
745 743 var notebook_id = IPython.save_widget.get_notebook_id();
746 744 var nbname = IPython.save_widget.get_notebook_name();
747 745 // We may want to move the name/id/nbformat logic inside toJSON?
748 746 var data = this.toJSON();
749 747 data.name = nbname;
750 748 data.nbformat = 2;
751 749 // We do the call with settings so we can set cache to false.
752 750 var settings = {
753 751 processData : false,
754 752 cache : false,
755 753 type : "PUT",
756 754 data : JSON.stringify(data),
757 755 headers : {'Content-Type': 'application/json'},
758 756 success : $.proxy(this.notebook_saved,this)
759 757 };
760 758 IPython.save_widget.status_saving();
761 759 $.ajax("/notebooks/" + notebook_id, settings);
762 760 };
763 761 };
764 762
765 763
766 764 Notebook.prototype.notebook_saved = function (data, status, xhr) {
767 765 this.dirty = false;
768 766 setTimeout($.proxy(IPython.save_widget.status_save,IPython.save_widget),500);
769 767 }
770 768
771 769
772 770 Notebook.prototype.load_notebook = function (callback) {
773 771 var that = this;
774 772 var notebook_id = IPython.save_widget.get_notebook_id();
775 773 // We do the call with settings so we can set cache to false.
776 774 var settings = {
777 775 processData : false,
778 776 cache : false,
779 777 type : "GET",
780 778 dataType : "json",
781 779 success : function (data, status, xhr) {
782 780 that.notebook_loaded(data, status, xhr);
783 781 if (callback !== undefined) {
784 782 callback();
785 783 };
786 784 }
787 785 };
788 786 IPython.save_widget.status_loading();
789 787 $.ajax("/notebooks/" + notebook_id, settings);
790 788 }
791 789
792 790
793 791 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
794 792 this.fromJSON(data);
795 793 if (this.ncells() === 0) {
796 794 this.insert_code_cell_after();
797 795 };
798 796 IPython.save_widget.status_save();
799 797 IPython.save_widget.set_notebook_name(data.name);
800 798 this.start_kernel();
801 799 this.dirty = false;
802 800 // fromJSON always selects the last cell inserted. We need to wait
803 801 // until that is done before scrolling to the top.
804 802 setTimeout(function () {
805 803 IPython.notebook.select(0);
806 804 IPython.notebook.scroll_to_top();
807 805 }, 50);
808 806 };
809 807
810 808 IPython.Notebook = Notebook;
811 809
812 810 return IPython;
813 811
814 812 }(IPython));
815 813
General Comments 0
You need to be logged in to leave comments. Login now