##// END OF EJS Templates
don't swallow regular key events in read-only mode
MinRK -
Show More
@@ -1,1079 +1,1079
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Notebook
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var utils = IPython.utils;
15 15
16 16 var Notebook = function (selector) {
17 17 this.read_only = IPython.read_only;
18 18 this.element = $(selector);
19 19 this.element.scroll();
20 20 this.element.data("notebook", this);
21 21 this.next_prompt_number = 1;
22 22 this.kernel = null;
23 23 this.dirty = false;
24 24 this.msg_cell_map = {};
25 25 this.metadata = {};
26 26 this.control_key_active = false;
27 27 this.style();
28 28 this.create_elements();
29 29 this.bind_events();
30 30 this.set_tooltipontab(true);
31 31 this.set_smartcompleter(true);
32 32 this.set_timebeforetooltip(1200);
33 33 };
34 34
35 35
36 36 Notebook.prototype.style = function () {
37 37 $('div#notebook').addClass('border-box-sizing');
38 38 };
39 39
40 40
41 41 Notebook.prototype.create_elements = function () {
42 42 // We add this end_space div to the end of the notebook div to:
43 43 // i) provide a margin between the last cell and the end of the notebook
44 44 // ii) to prevent the div from scrolling up when the last cell is being
45 45 // edited, but is too low on the page, which browsers will do automatically.
46 46 var that = this;
47 47 var end_space = $('<div class="end_space"></div>').height("30%");
48 48 end_space.dblclick(function (e) {
49 49 if (that.read_only) return;
50 50 var ncells = that.ncells();
51 51 that.insert_code_cell_below(ncells-1);
52 52 });
53 53 this.element.append(end_space);
54 54 $('div#notebook').addClass('border-box-sizing');
55 55 };
56 56
57 57
58 58 Notebook.prototype.bind_events = function () {
59 59 var that = this;
60 60 $(document).keydown(function (event) {
61 61 // console.log(event);
62 if (that.read_only) return false;
62 if (that.read_only) return true;
63 63 if (event.which === 27) {
64 64 // Intercept escape at highest level to avoid closing
65 65 // websocket connection with firefox
66 66 event.preventDefault();
67 67 }
68 68 if (event.which === 38 && !event.shiftKey) {
69 69 var cell = that.selected_cell();
70 70 if (cell.at_top()) {
71 71 event.preventDefault();
72 72 that.select_prev();
73 73 };
74 74 } else if (event.which === 40 && !event.shiftKey) {
75 75 var cell = that.selected_cell();
76 76 if (cell.at_bottom()) {
77 77 event.preventDefault();
78 78 that.select_next();
79 79 };
80 80 } else if (event.which === 13 && event.shiftKey) {
81 81 that.execute_selected_cell();
82 82 return false;
83 83 } else if (event.which === 13 && event.ctrlKey) {
84 84 that.execute_selected_cell({terminal:true});
85 85 return false;
86 86 } else if (event.which === 77 && event.ctrlKey) {
87 87 that.control_key_active = true;
88 88 return false;
89 89 } else if (event.which === 68 && that.control_key_active) {
90 90 // Delete selected cell = d
91 91 that.delete_cell();
92 92 that.control_key_active = false;
93 93 return false;
94 94 } else if (event.which === 65 && that.control_key_active) {
95 95 // Insert code cell above selected = a
96 96 that.insert_code_cell_above();
97 97 that.control_key_active = false;
98 98 return false;
99 99 } else if (event.which === 66 && that.control_key_active) {
100 100 // Insert code cell below selected = b
101 101 that.insert_code_cell_below();
102 102 that.control_key_active = false;
103 103 return false;
104 104 } else if (event.which === 67 && that.control_key_active) {
105 105 // To code = c
106 106 that.to_code();
107 107 that.control_key_active = false;
108 108 return false;
109 109 } else if (event.which === 77 && that.control_key_active) {
110 110 // To markdown = m
111 111 that.to_markdown();
112 112 that.control_key_active = false;
113 113 return false;
114 114 } else if (event.which === 84 && that.control_key_active) {
115 115 // Toggle output = t
116 116 that.toggle_output();
117 117 that.control_key_active = false;
118 118 return false;
119 119 } else if (event.which === 83 && that.control_key_active) {
120 120 // Save notebook = s
121 121 IPython.save_widget.save_notebook();
122 122 that.control_key_active = false;
123 123 return false;
124 124 } else if (event.which === 74 && that.control_key_active) {
125 125 // Move cell down = j
126 126 that.move_cell_down();
127 127 that.control_key_active = false;
128 128 return false;
129 129 } else if (event.which === 75 && that.control_key_active) {
130 130 // Move cell up = k
131 131 that.move_cell_up();
132 132 that.control_key_active = false;
133 133 return false;
134 134 } else if (event.which === 80 && that.control_key_active) {
135 135 // Select previous = p
136 136 that.select_prev();
137 137 that.control_key_active = false;
138 138 return false;
139 139 } else if (event.which === 78 && that.control_key_active) {
140 140 // Select next = n
141 141 that.select_next();
142 142 that.control_key_active = false;
143 143 return false;
144 144 } else if (event.which === 76 && that.control_key_active) {
145 145 // Toggle line numbers = l
146 146 that.cell_toggle_line_numbers();
147 147 that.control_key_active = false;
148 148 return false;
149 149 } else if (event.which === 73 && that.control_key_active) {
150 150 // Interrupt kernel = i
151 151 IPython.notebook.kernel.interrupt();
152 152 that.control_key_active = false;
153 153 return false;
154 154 } else if (event.which === 190 && that.control_key_active) {
155 155 // Restart kernel = . # matches qt console
156 156 IPython.notebook.restart_kernel();
157 157 that.control_key_active = false;
158 158 return false;
159 159 } else if (event.which === 72 && that.control_key_active) {
160 160 // Show keyboard shortcuts = h
161 161 that.toggle_keyboard_shortcuts();
162 162 that.control_key_active = false;
163 163 return false;
164 164 } else if (that.control_key_active) {
165 165 that.control_key_active = false;
166 166 return true;
167 167 };
168 168 return true;
169 169 });
170 170
171 171 this.element.bind('collapse_pager', function () {
172 172 var app_height = $('div#main_app').height(); // content height
173 173 var splitter_height = $('div#pager_splitter').outerHeight(true);
174 174 var new_height = app_height - splitter_height;
175 175 that.element.animate({height : new_height + 'px'}, 'fast');
176 176 });
177 177
178 178 this.element.bind('expand_pager', function () {
179 179 var app_height = $('div#main_app').height(); // content height
180 180 var splitter_height = $('div#pager_splitter').outerHeight(true);
181 181 var pager_height = $('div#pager').outerHeight(true);
182 182 var new_height = app_height - pager_height - splitter_height;
183 183 that.element.animate({height : new_height + 'px'}, 'fast');
184 184 });
185 185
186 186 this.element.bind('collapse_left_panel', function () {
187 187 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
188 188 var new_margin = splitter_width;
189 189 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
190 190 });
191 191
192 192 this.element.bind('expand_left_panel', function () {
193 193 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
194 194 var left_panel_width = IPython.left_panel.width;
195 195 var new_margin = splitter_width + left_panel_width;
196 196 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
197 197 });
198 198
199 199 $(window).bind('beforeunload', function () {
200 200 var kill_kernel = $('#kill_kernel').prop('checked');
201 201 if (kill_kernel) {
202 202 that.kernel.kill();
203 203 }
204 204 if (that.dirty && ! that.read_only) {
205 205 return "You have unsaved changes that will be lost if you leave this page.";
206 206 };
207 207 // Null is the *only* return value that will make the browser not
208 208 // pop up the "don't leave" dialog.
209 209 return null;
210 210 });
211 211 };
212 212
213 213
214 214 Notebook.prototype.toggle_keyboard_shortcuts = function () {
215 215 // toggles display of keyboard shortcut dialog
216 216 var that = this;
217 217 if ( this.shortcut_dialog ){
218 218 // if dialog is already shown, close it
219 219 this.shortcut_dialog.dialog("close");
220 220 this.shortcut_dialog = null;
221 221 return;
222 222 }
223 223 var dialog = $('<div/>');
224 224 this.shortcut_dialog = dialog;
225 225 var shortcuts = [
226 226 {key: 'Shift-Enter', help: 'run cell'},
227 227 {key: 'Ctrl-Enter', help: 'run cell in-place'},
228 228 {key: 'Ctrl-m d', help: 'delete cell'},
229 229 {key: 'Ctrl-m a', help: 'insert cell above'},
230 230 {key: 'Ctrl-m b', help: 'insert cell below'},
231 231 {key: 'Ctrl-m t', help: 'toggle output'},
232 232 {key: 'Ctrl-m l', help: 'toggle line numbers'},
233 233 {key: 'Ctrl-m s', help: 'save notebook'},
234 234 {key: 'Ctrl-m j', help: 'move cell down'},
235 235 {key: 'Ctrl-m k', help: 'move cell up'},
236 236 {key: 'Ctrl-m c', help: 'code cell'},
237 237 {key: 'Ctrl-m m', help: 'markdown cell'},
238 238 {key: 'Ctrl-m p', help: 'select previous'},
239 239 {key: 'Ctrl-m n', help: 'select next'},
240 240 {key: 'Ctrl-m i', help: 'interrupt kernel'},
241 241 {key: 'Ctrl-m .', help: 'restart kernel'},
242 242 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
243 243 ];
244 244 for (var i=0; i<shortcuts.length; i++) {
245 245 dialog.append($('<div>').
246 246 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
247 247 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
248 248 );
249 249 };
250 250 dialog.bind('dialogclose', function(event) {
251 251 // dialog has been closed, allow it to be drawn again.
252 252 that.shortcut_dialog = null;
253 253 });
254 254 dialog.dialog({title: 'Keyboard shortcuts'});
255 255 };
256 256
257 257
258 258 Notebook.prototype.scroll_to_bottom = function () {
259 259 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
260 260 };
261 261
262 262
263 263 Notebook.prototype.scroll_to_top = function () {
264 264 this.element.animate({scrollTop:0}, 0);
265 265 };
266 266
267 267
268 268 // Cell indexing, retrieval, etc.
269 269
270 270
271 271 Notebook.prototype.cell_elements = function () {
272 272 return this.element.children("div.cell");
273 273 };
274 274
275 275
276 276 Notebook.prototype.ncells = function (cell) {
277 277 return this.cell_elements().length;
278 278 };
279 279
280 280
281 281 // TODO: we are often calling cells as cells()[i], which we should optimize
282 282 // to cells(i) or a new method.
283 283 Notebook.prototype.cells = function () {
284 284 return this.cell_elements().toArray().map(function (e) {
285 285 return $(e).data("cell");
286 286 });
287 287 };
288 288
289 289
290 290 Notebook.prototype.find_cell_index = function (cell) {
291 291 var result = null;
292 292 this.cell_elements().filter(function (index) {
293 293 if ($(this).data("cell") === cell) {
294 294 result = index;
295 295 };
296 296 });
297 297 return result;
298 298 };
299 299
300 300
301 301 Notebook.prototype.index_or_selected = function (index) {
302 302 return index || this.selected_index() || 0;
303 303 };
304 304
305 305
306 306 Notebook.prototype.select = function (index) {
307 307 if (index !== undefined && index >= 0 && index < this.ncells()) {
308 308 if (this.selected_index() !== null) {
309 309 this.selected_cell().unselect();
310 310 };
311 311 this.cells()[index].select();
312 312 };
313 313 return this;
314 314 };
315 315
316 316
317 317 Notebook.prototype.select_next = function () {
318 318 var index = this.selected_index();
319 319 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
320 320 this.select(index+1);
321 321 };
322 322 return this;
323 323 };
324 324
325 325
326 326 Notebook.prototype.select_prev = function () {
327 327 var index = this.selected_index();
328 328 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
329 329 this.select(index-1);
330 330 };
331 331 return this;
332 332 };
333 333
334 334
335 335 Notebook.prototype.selected_index = function () {
336 336 var result = null;
337 337 this.cell_elements().filter(function (index) {
338 338 if ($(this).data("cell").selected === true) {
339 339 result = index;
340 340 };
341 341 });
342 342 return result;
343 343 };
344 344
345 345
346 346 Notebook.prototype.cell_for_msg = function (msg_id) {
347 347 var cell_id = this.msg_cell_map[msg_id];
348 348 var result = null;
349 349 this.cell_elements().filter(function (index) {
350 350 cell = $(this).data("cell");
351 351 if (cell.cell_id === cell_id) {
352 352 result = cell;
353 353 };
354 354 });
355 355 return result;
356 356 };
357 357
358 358
359 359 Notebook.prototype.selected_cell = function () {
360 360 return this.cell_elements().eq(this.selected_index()).data("cell");
361 361 };
362 362
363 363
364 364 // Cell insertion, deletion and moving.
365 365
366 366
367 367 Notebook.prototype.delete_cell = function (index) {
368 368 var i = index || this.selected_index();
369 369 if (i !== null && i >= 0 && i < this.ncells()) {
370 370 this.cell_elements().eq(i).remove();
371 371 if (i === (this.ncells())) {
372 372 this.select(i-1);
373 373 } else {
374 374 this.select(i);
375 375 };
376 376 };
377 377 this.dirty = true;
378 378 return this;
379 379 };
380 380
381 381
382 382 Notebook.prototype.append_cell = function (cell) {
383 383 this.element.find('div.end_space').before(cell.element);
384 384 this.dirty = true;
385 385 return this;
386 386 };
387 387
388 388
389 389 Notebook.prototype.insert_cell_below = function (cell, index) {
390 390 var ncells = this.ncells();
391 391 if (ncells === 0) {
392 392 this.append_cell(cell);
393 393 return this;
394 394 };
395 395 if (index >= 0 && index < ncells) {
396 396 this.cell_elements().eq(index).after(cell.element);
397 397 };
398 398 this.dirty = true;
399 399 return this;
400 400 };
401 401
402 402
403 403 Notebook.prototype.insert_cell_above = function (cell, index) {
404 404 var ncells = this.ncells();
405 405 if (ncells === 0) {
406 406 this.append_cell(cell);
407 407 return this;
408 408 };
409 409 if (index >= 0 && index < ncells) {
410 410 this.cell_elements().eq(index).before(cell.element);
411 411 };
412 412 this.dirty = true;
413 413 return this;
414 414 };
415 415
416 416
417 417 Notebook.prototype.move_cell_up = function (index) {
418 418 var i = index || this.selected_index();
419 419 if (i !== null && i < this.ncells() && i > 0) {
420 420 var pivot = this.cell_elements().eq(i-1);
421 421 var tomove = this.cell_elements().eq(i);
422 422 if (pivot !== null && tomove !== null) {
423 423 tomove.detach();
424 424 pivot.before(tomove);
425 425 this.select(i-1);
426 426 };
427 427 };
428 428 this.dirty = true;
429 429 return this;
430 430 };
431 431
432 432
433 433 Notebook.prototype.move_cell_down = function (index) {
434 434 var i = index || this.selected_index();
435 435 if (i !== null && i < (this.ncells()-1) && i >= 0) {
436 436 var pivot = this.cell_elements().eq(i+1);
437 437 var tomove = this.cell_elements().eq(i);
438 438 if (pivot !== null && tomove !== null) {
439 439 tomove.detach();
440 440 pivot.after(tomove);
441 441 this.select(i+1);
442 442 };
443 443 };
444 444 this.dirty = true;
445 445 return this;
446 446 };
447 447
448 448
449 449 Notebook.prototype.sort_cells = function () {
450 450 var ncells = this.ncells();
451 451 var sindex = this.selected_index();
452 452 var swapped;
453 453 do {
454 454 swapped = false;
455 455 for (var i=1; i<ncells; i++) {
456 456 current = this.cell_elements().eq(i).data("cell");
457 457 previous = this.cell_elements().eq(i-1).data("cell");
458 458 if (previous.input_prompt_number > current.input_prompt_number) {
459 459 this.move_cell_up(i);
460 460 swapped = true;
461 461 };
462 462 };
463 463 } while (swapped);
464 464 this.select(sindex);
465 465 return this;
466 466 };
467 467
468 468
469 469 Notebook.prototype.insert_code_cell_above = function (index) {
470 470 // TODO: Bounds check for i
471 471 var i = this.index_or_selected(index);
472 472 var cell = new IPython.CodeCell(this);
473 473 cell.set_input_prompt();
474 474 this.insert_cell_above(cell, i);
475 475 this.select(this.find_cell_index(cell));
476 476 return cell;
477 477 };
478 478
479 479
480 480 Notebook.prototype.insert_code_cell_below = function (index) {
481 481 // TODO: Bounds check for i
482 482 var i = this.index_or_selected(index);
483 483 var cell = new IPython.CodeCell(this);
484 484 cell.set_input_prompt();
485 485 this.insert_cell_below(cell, i);
486 486 this.select(this.find_cell_index(cell));
487 487 return cell;
488 488 };
489 489
490 490
491 491 Notebook.prototype.insert_html_cell_above = function (index) {
492 492 // TODO: Bounds check for i
493 493 var i = this.index_or_selected(index);
494 494 var cell = new IPython.HTMLCell(this);
495 495 cell.config_mathjax();
496 496 this.insert_cell_above(cell, i);
497 497 this.select(this.find_cell_index(cell));
498 498 return cell;
499 499 };
500 500
501 501
502 502 Notebook.prototype.insert_html_cell_below = function (index) {
503 503 // TODO: Bounds check for i
504 504 var i = this.index_or_selected(index);
505 505 var cell = new IPython.HTMLCell(this);
506 506 cell.config_mathjax();
507 507 this.insert_cell_below(cell, i);
508 508 this.select(this.find_cell_index(cell));
509 509 return cell;
510 510 };
511 511
512 512
513 513 Notebook.prototype.insert_markdown_cell_above = function (index) {
514 514 // TODO: Bounds check for i
515 515 var i = this.index_or_selected(index);
516 516 var cell = new IPython.MarkdownCell(this);
517 517 cell.config_mathjax();
518 518 this.insert_cell_above(cell, i);
519 519 this.select(this.find_cell_index(cell));
520 520 return cell;
521 521 };
522 522
523 523
524 524 Notebook.prototype.insert_markdown_cell_below = function (index) {
525 525 // TODO: Bounds check for i
526 526 var i = this.index_or_selected(index);
527 527 var cell = new IPython.MarkdownCell(this);
528 528 cell.config_mathjax();
529 529 this.insert_cell_below(cell, i);
530 530 this.select(this.find_cell_index(cell));
531 531 return cell;
532 532 };
533 533
534 534
535 535 Notebook.prototype.to_code = function (index) {
536 536 // TODO: Bounds check for i
537 537 var i = this.index_or_selected(index);
538 538 var source_element = this.cell_elements().eq(i);
539 539 var source_cell = source_element.data("cell");
540 540 if (source_cell instanceof IPython.HTMLCell ||
541 541 source_cell instanceof IPython.MarkdownCell) {
542 542 this.insert_code_cell_below(i);
543 543 var target_cell = this.cells()[i+1];
544 544 target_cell.set_code(source_cell.get_source());
545 545 source_element.remove();
546 546 target_cell.select();
547 547 };
548 548 this.dirty = true;
549 549 };
550 550
551 551
552 552 Notebook.prototype.to_markdown = function (index) {
553 553 // TODO: Bounds check for i
554 554 var i = this.index_or_selected(index);
555 555 var source_element = this.cell_elements().eq(i);
556 556 var source_cell = source_element.data("cell");
557 557 var target_cell = null;
558 558 if (source_cell instanceof IPython.CodeCell) {
559 559 this.insert_markdown_cell_below(i);
560 560 target_cell = this.cells()[i+1];
561 561 var text = source_cell.get_code();
562 562 } else if (source_cell instanceof IPython.HTMLCell) {
563 563 this.insert_markdown_cell_below(i);
564 564 target_cell = this.cells()[i+1];
565 565 var text = source_cell.get_source();
566 566 if (text === source_cell.placeholder) {
567 567 text = target_cell.placeholder;
568 568 }
569 569 }
570 570 if (target_cell !== null) {
571 571 if (text === "") {text = target_cell.placeholder;};
572 572 target_cell.set_source(text);
573 573 source_element.remove();
574 574 target_cell.edit();
575 575 }
576 576 this.dirty = true;
577 577 };
578 578
579 579
580 580 Notebook.prototype.to_html = function (index) {
581 581 // TODO: Bounds check for i
582 582 var i = this.index_or_selected(index);
583 583 var source_element = this.cell_elements().eq(i);
584 584 var source_cell = source_element.data("cell");
585 585 var target_cell = null;
586 586 if (source_cell instanceof IPython.CodeCell) {
587 587 this.insert_html_cell_below(i);
588 588 target_cell = this.cells()[i+1];
589 589 var text = source_cell.get_code();
590 590 } else if (source_cell instanceof IPython.MarkdownCell) {
591 591 this.insert_html_cell_below(i);
592 592 target_cell = this.cells()[i+1];
593 593 var text = source_cell.get_source();
594 594 if (text === source_cell.placeholder) {
595 595 text = target_cell.placeholder;
596 596 }
597 597 }
598 598 if (target_cell !== null) {
599 599 if (text === "") {text = target_cell.placeholder;};
600 600 target_cell.set_source(text);
601 601 source_element.remove();
602 602 target_cell.edit();
603 603 }
604 604 this.dirty = true;
605 605 };
606 606
607 607
608 608 // Cell collapsing and output clearing
609 609
610 610 Notebook.prototype.collapse = function (index) {
611 611 var i = this.index_or_selected(index);
612 612 this.cells()[i].collapse();
613 613 this.dirty = true;
614 614 };
615 615
616 616
617 617 Notebook.prototype.expand = function (index) {
618 618 var i = this.index_or_selected(index);
619 619 this.cells()[i].expand();
620 620 this.dirty = true;
621 621 };
622 622
623 623
624 624 Notebook.prototype.toggle_output = function (index) {
625 625 var i = this.index_or_selected(index);
626 626 this.cells()[i].toggle_output();
627 627 this.dirty = true;
628 628 };
629 629
630 630
631 631 Notebook.prototype.set_timebeforetooltip = function (time) {
632 632 console.log("change time before tooltip to : "+time);
633 633 this.time_before_tooltip = time;
634 634 };
635 635
636 636 Notebook.prototype.set_tooltipontab = function (state) {
637 637 console.log("change tooltip on tab to : "+state);
638 638 this.tooltip_on_tab = state;
639 639 };
640 640
641 641 Notebook.prototype.set_smartcompleter = function (state) {
642 642 console.log("Smart completion (kwargs first) changed to to : "+state);
643 643 this.smart_completer = state;
644 644 };
645 645
646 646 Notebook.prototype.set_autoindent = function (state) {
647 647 var cells = this.cells();
648 648 len = cells.length;
649 649 for (var i=0; i<len; i++) {
650 650 cells[i].set_autoindent(state);
651 651 };
652 652 };
653 653
654 654
655 655 Notebook.prototype.clear_all_output = function () {
656 656 var ncells = this.ncells();
657 657 var cells = this.cells();
658 658 for (var i=0; i<ncells; i++) {
659 659 if (cells[i] instanceof IPython.CodeCell) {
660 660 cells[i].clear_output(true,true,true);
661 661 }
662 662 };
663 663 this.dirty = true;
664 664 };
665 665
666 666 // Other cell functions: line numbers, ...
667 667
668 668 Notebook.prototype.cell_toggle_line_numbers = function() {
669 669 this.selected_cell().toggle_line_numbers();
670 670 };
671 671
672 672 // Kernel related things
673 673
674 674 Notebook.prototype.start_kernel = function () {
675 675 this.kernel = new IPython.Kernel();
676 676 var notebook_id = IPython.save_widget.get_notebook_id();
677 677 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
678 678 };
679 679
680 680
681 681 Notebook.prototype.restart_kernel = function () {
682 682 var that = this;
683 683 var notebook_id = IPython.save_widget.get_notebook_id();
684 684
685 685 var dialog = $('<div/>');
686 686 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
687 687 $(document).append(dialog);
688 688 dialog.dialog({
689 689 resizable: false,
690 690 modal: true,
691 691 title: "Restart kernel or continue running?",
692 692 buttons : {
693 693 "Restart": function () {
694 694 that.kernel.restart($.proxy(that.kernel_started, that));
695 695 $(this).dialog('close');
696 696 },
697 697 "Continue running": function () {
698 698 $(this).dialog('close');
699 699 }
700 700 }
701 701 });
702 702 };
703 703
704 704
705 705 Notebook.prototype.kernel_started = function () {
706 706 console.log("Kernel started: ", this.kernel.kernel_id);
707 707 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
708 708 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
709 709 };
710 710
711 711
712 712 Notebook.prototype.handle_shell_reply = function (e) {
713 713 reply = $.parseJSON(e.data);
714 714 var header = reply.header;
715 715 var content = reply.content;
716 716 var msg_type = header.msg_type;
717 717 // console.log(reply);
718 718 var cell = this.cell_for_msg(reply.parent_header.msg_id);
719 719 if (msg_type === "execute_reply") {
720 720 cell.set_input_prompt(content.execution_count);
721 721 cell.element.removeClass("running");
722 722 this.dirty = true;
723 723 } else if (msg_type === "complete_reply") {
724 724 cell.finish_completing(content.matched_text, content.matches);
725 725 } else if (msg_type === "object_info_reply"){
726 726 //console.log('back from object_info_request : ')
727 727 rep = reply.content;
728 728 if(rep.found)
729 729 {
730 730 cell.finish_tooltip(rep);
731 731 }
732 732 } else {
733 733 //console.log("unknown reply:"+msg_type);
734 734 }
735 735 // when having a rely from object_info_reply,
736 736 // no payload so no nned to handle it
737 737 if(typeof(content.payload)!='undefined') {
738 738 var payload = content.payload || [];
739 739 this.handle_payload(cell, payload);
740 740 }
741 741 };
742 742
743 743
744 744 Notebook.prototype.handle_payload = function (cell, payload) {
745 745 var l = payload.length;
746 746 for (var i=0; i<l; i++) {
747 747 if (payload[i].source === 'IPython.zmq.page.page') {
748 748 if (payload[i].text.trim() !== '') {
749 749 IPython.pager.clear();
750 750 IPython.pager.expand();
751 751 IPython.pager.append_text(payload[i].text);
752 752 }
753 753 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
754 754 var index = this.find_cell_index(cell);
755 755 var new_cell = this.insert_code_cell_below(index);
756 756 new_cell.set_code(payload[i].text);
757 757 this.dirty = true;
758 758 }
759 759 };
760 760 };
761 761
762 762
763 763 Notebook.prototype.handle_iopub_reply = function (e) {
764 764 reply = $.parseJSON(e.data);
765 765 var content = reply.content;
766 766 // console.log(reply);
767 767 var msg_type = reply.header.msg_type;
768 768 var cell = this.cell_for_msg(reply.parent_header.msg_id);
769 769 if (!cell){
770 770 // message not from this notebook
771 771 console.log("Received IOPub message not caused by one of my cells");
772 772 return;
773 773 }
774 774 var output_types = ['stream','display_data','pyout','pyerr'];
775 775 if (output_types.indexOf(msg_type) >= 0) {
776 776 this.handle_output(cell, msg_type, content);
777 777 } else if (msg_type === 'status') {
778 778 if (content.execution_state === 'busy') {
779 779 IPython.kernel_status_widget.status_busy();
780 780 } else if (content.execution_state === 'idle') {
781 781 IPython.kernel_status_widget.status_idle();
782 782 } else if (content.execution_state === 'dead') {
783 783 this.handle_status_dead();
784 784 };
785 785 } else if (msg_type === 'clear_output') {
786 786 cell.clear_output(content.stdout, content.stderr, content.other);
787 787 };
788 788 };
789 789
790 790
791 791 Notebook.prototype.handle_status_dead = function () {
792 792 var that = this;
793 793 this.kernel.stop_channels();
794 794 var dialog = $('<div/>');
795 795 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.');
796 796 $(document).append(dialog);
797 797 dialog.dialog({
798 798 resizable: false,
799 799 modal: true,
800 800 title: "Dead kernel",
801 801 buttons : {
802 802 "Restart": function () {
803 803 that.start_kernel();
804 804 $(this).dialog('close');
805 805 },
806 806 "Continue running": function () {
807 807 $(this).dialog('close');
808 808 }
809 809 }
810 810 });
811 811 };
812 812
813 813
814 814 Notebook.prototype.handle_output = function (cell, msg_type, content) {
815 815 var json = {};
816 816 json.output_type = msg_type;
817 817 if (msg_type === "stream") {
818 818 json.text = utils.fixConsole(content.data);
819 819 json.stream = content.name;
820 820 } else if (msg_type === "display_data") {
821 821 json = this.convert_mime_types(json, content.data);
822 822 } else if (msg_type === "pyout") {
823 823 json.prompt_number = content.execution_count;
824 824 json = this.convert_mime_types(json, content.data);
825 825 } else if (msg_type === "pyerr") {
826 826 json.ename = content.ename;
827 827 json.evalue = content.evalue;
828 828 var traceback = [];
829 829 for (var i=0; i<content.traceback.length; i++) {
830 830 traceback.push(utils.fixConsole(content.traceback[i]));
831 831 }
832 832 json.traceback = traceback;
833 833 };
834 834 cell.append_output(json);
835 835 this.dirty = true;
836 836 };
837 837
838 838
839 839 Notebook.prototype.convert_mime_types = function (json, data) {
840 840 if (data['text/plain'] !== undefined) {
841 841 json.text = utils.fixConsole(data['text/plain']);
842 842 };
843 843 if (data['text/html'] !== undefined) {
844 844 json.html = data['text/html'];
845 845 };
846 846 if (data['image/svg+xml'] !== undefined) {
847 847 json.svg = data['image/svg+xml'];
848 848 };
849 849 if (data['image/png'] !== undefined) {
850 850 json.png = data['image/png'];
851 851 };
852 852 if (data['image/jpeg'] !== undefined) {
853 853 json.jpeg = data['image/jpeg'];
854 854 };
855 855 if (data['text/latex'] !== undefined) {
856 856 json.latex = data['text/latex'];
857 857 };
858 858 if (data['application/json'] !== undefined) {
859 859 json.json = data['application/json'];
860 860 };
861 861 if (data['application/javascript'] !== undefined) {
862 862 json.javascript = data['application/javascript'];
863 863 }
864 864 return json;
865 865 };
866 866
867 867
868 868 Notebook.prototype.execute_selected_cell = function (options) {
869 869 // add_new: should a new cell be added if we are at the end of the nb
870 870 // terminal: execute in terminal mode, which stays in the current cell
871 871 default_options = {terminal: false, add_new: true};
872 872 $.extend(default_options, options);
873 873 var that = this;
874 874 var cell = that.selected_cell();
875 875 var cell_index = that.find_cell_index(cell);
876 876 if (cell instanceof IPython.CodeCell) {
877 877 cell.clear_output(true, true, true);
878 878 cell.set_input_prompt('*');
879 879 cell.element.addClass("running");
880 880 var code = cell.get_code();
881 881 var msg_id = that.kernel.execute(cell.get_code());
882 882 that.msg_cell_map[msg_id] = cell.cell_id;
883 883 } else if (cell instanceof IPython.HTMLCell) {
884 884 cell.render();
885 885 }
886 886 if (default_options.terminal) {
887 887 cell.select_all();
888 888 } else {
889 889 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
890 890 that.insert_code_cell_below();
891 891 // If we are adding a new cell at the end, scroll down to show it.
892 892 that.scroll_to_bottom();
893 893 } else {
894 894 that.select(cell_index+1);
895 895 };
896 896 };
897 897 this.dirty = true;
898 898 };
899 899
900 900
901 901 Notebook.prototype.execute_all_cells = function () {
902 902 var ncells = this.ncells();
903 903 for (var i=0; i<ncells; i++) {
904 904 this.select(i);
905 905 this.execute_selected_cell({add_new:false});
906 906 };
907 907 this.scroll_to_bottom();
908 908 };
909 909
910 910
911 911 Notebook.prototype.request_tool_tip = function (cell,func) {
912 912 // Feel free to shorten this logic if you are better
913 913 // than me in regEx
914 914 // basicaly you shoul be able to get xxx.xxx.xxx from
915 915 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
916 916 // remove everything between matchin bracket (need to iterate)
917 917 matchBracket = /\([^\(\)]+\)/g;
918 918 oldfunc = func;
919 919 func = func.replace(matchBracket,"");
920 920 while( oldfunc != func )
921 921 {
922 922 oldfunc = func;
923 923 func = func.replace(matchBracket,"");
924 924 }
925 925 // remove everythin after last open bracket
926 926 endBracket = /\([^\(]*$/g;
927 927 func = func.replace(endBracket,"");
928 928 var re = /[a-zA-Z._]+$/g;
929 929 var msg_id = this.kernel.object_info_request(re.exec(func));
930 930 if(typeof(msg_id)!='undefined'){
931 931 this.msg_cell_map[msg_id] = cell.cell_id;
932 932 }
933 933 };
934 934
935 935 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
936 936 var msg_id = this.kernel.complete(line, cursor_pos);
937 937 this.msg_cell_map[msg_id] = cell.cell_id;
938 938 };
939 939
940 940 // Persistance and loading
941 941
942 942
943 943 Notebook.prototype.fromJSON = function (data) {
944 944 var ncells = this.ncells();
945 945 var i;
946 946 for (i=0; i<ncells; i++) {
947 947 // Always delete cell 0 as they get renumbered as they are deleted.
948 948 this.delete_cell(0);
949 949 };
950 950 // Save the metadata
951 951 this.metadata = data.metadata;
952 952 // Only handle 1 worksheet for now.
953 953 var worksheet = data.worksheets[0];
954 954 if (worksheet !== undefined) {
955 955 var new_cells = worksheet.cells;
956 956 ncells = new_cells.length;
957 957 var cell_data = null;
958 958 var new_cell = null;
959 959 for (i=0; i<ncells; i++) {
960 960 cell_data = new_cells[i];
961 961 if (cell_data.cell_type == 'code') {
962 962 new_cell = this.insert_code_cell_below();
963 963 new_cell.fromJSON(cell_data);
964 964 } else if (cell_data.cell_type === 'html') {
965 965 new_cell = this.insert_html_cell_below();
966 966 new_cell.fromJSON(cell_data);
967 967 } else if (cell_data.cell_type === 'markdown') {
968 968 new_cell = this.insert_markdown_cell_below();
969 969 new_cell.fromJSON(cell_data);
970 970 };
971 971 };
972 972 };
973 973 };
974 974
975 975
976 976 Notebook.prototype.toJSON = function () {
977 977 var cells = this.cells();
978 978 var ncells = cells.length;
979 979 cell_array = new Array(ncells);
980 980 for (var i=0; i<ncells; i++) {
981 981 cell_array[i] = cells[i].toJSON();
982 982 };
983 983 data = {
984 984 // Only handle 1 worksheet for now.
985 985 worksheets : [{cells:cell_array}],
986 986 metadata : this.metadata
987 987 };
988 988 return data;
989 989 };
990 990
991 991 Notebook.prototype.save_notebook = function () {
992 992 if (IPython.save_widget.test_notebook_name()) {
993 993 var notebook_id = IPython.save_widget.get_notebook_id();
994 994 var nbname = IPython.save_widget.get_notebook_name();
995 995 // We may want to move the name/id/nbformat logic inside toJSON?
996 996 var data = this.toJSON();
997 997 data.metadata.name = nbname;
998 998 data.nbformat = 2;
999 999 // We do the call with settings so we can set cache to false.
1000 1000 var settings = {
1001 1001 processData : false,
1002 1002 cache : false,
1003 1003 type : "PUT",
1004 1004 data : JSON.stringify(data),
1005 1005 headers : {'Content-Type': 'application/json'},
1006 1006 success : $.proxy(this.notebook_saved,this),
1007 1007 error : $.proxy(this.notebook_save_failed,this)
1008 1008 };
1009 1009 IPython.save_widget.status_saving();
1010 1010 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id;
1011 1011 $.ajax(url, settings);
1012 1012 };
1013 1013 };
1014 1014
1015 1015
1016 1016 Notebook.prototype.notebook_saved = function (data, status, xhr) {
1017 1017 this.dirty = false;
1018 1018 IPython.save_widget.notebook_saved();
1019 1019 IPython.save_widget.status_save();
1020 1020 };
1021 1021
1022 1022
1023 1023 Notebook.prototype.notebook_save_failed = function (xhr, status, error_msg) {
1024 1024 // Notify the user and reset the save button
1025 1025 // TODO: Handle different types of errors (timeout etc.)
1026 1026 alert('An unexpected error occured while saving the notebook.');
1027 1027 IPython.save_widget.reset_status();
1028 1028 };
1029 1029
1030 1030
1031 1031 Notebook.prototype.load_notebook = function (callback) {
1032 1032 var that = this;
1033 1033 var notebook_id = IPython.save_widget.get_notebook_id();
1034 1034 // We do the call with settings so we can set cache to false.
1035 1035 var settings = {
1036 1036 processData : false,
1037 1037 cache : false,
1038 1038 type : "GET",
1039 1039 dataType : "json",
1040 1040 success : function (data, status, xhr) {
1041 1041 that.notebook_loaded(data, status, xhr);
1042 1042 if (callback !== undefined) {
1043 1043 callback();
1044 1044 };
1045 1045 }
1046 1046 };
1047 1047 IPython.save_widget.status_loading();
1048 1048 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id;
1049 1049 $.ajax(url, settings);
1050 1050 };
1051 1051
1052 1052
1053 1053 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
1054 1054 var allowed = xhr.getResponseHeader('Allow');
1055 1055 this.fromJSON(data);
1056 1056 if (this.ncells() === 0) {
1057 1057 this.insert_code_cell_below();
1058 1058 };
1059 1059 IPython.save_widget.status_save();
1060 1060 IPython.save_widget.set_notebook_name(data.metadata.name);
1061 1061 this.dirty = false;
1062 1062 if (! this.read_only) {
1063 1063 this.start_kernel();
1064 1064 }
1065 1065 // fromJSON always selects the last cell inserted. We need to wait
1066 1066 // until that is done before scrolling to the top.
1067 1067 setTimeout(function () {
1068 1068 IPython.notebook.select(0);
1069 1069 IPython.notebook.scroll_to_top();
1070 1070 }, 50);
1071 1071 };
1072 1072
1073 1073 IPython.Notebook = Notebook;
1074 1074
1075 1075
1076 1076 return IPython;
1077 1077
1078 1078 }(IPython));
1079 1079
General Comments 0
You need to be logged in to leave comments. Login now