##// END OF EJS Templates
Merge pull request #2549 from dwf/delete_undo...
Bussonnier Matthias -
r8704:22fdb276 merge
parent child Browse files
Show More
@@ -1,1322 +1,1371 b''
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 var key = IPython.utils.keycodes;
16 16
17 17 var Notebook = function (selector) {
18 18 this.read_only = IPython.read_only;
19 19 this.element = $(selector);
20 20 this.element.scroll();
21 21 this.element.data("notebook", this);
22 22 this.next_prompt_number = 1;
23 23 this.kernel = null;
24 24 this.clipboard = null;
25 this.undelete_backup = null;
26 this.undelete_index = null;
27 this.undelete_below = false;
25 28 this.paste_enabled = false;
26 29 this.dirty = false;
27 30 this.metadata = {};
28 31 // single worksheet for now
29 32 this.worksheet_metadata = {};
30 33 this.control_key_active = false;
31 34 this.notebook_id = null;
32 35 this.notebook_name = null;
33 36 this.notebook_name_blacklist_re = /[\/\\:]/;
34 37 this.nbformat = 3 // Increment this when changing the nbformat
35 38 this.nbformat_minor = 0 // Increment this when changing the nbformat
36 39 this.style();
37 40 this.create_elements();
38 41 this.bind_events();
39 42 };
40 43
41 44
42 45 Notebook.prototype.style = function () {
43 46 $('div#notebook').addClass('border-box-sizing');
44 47 };
45 48
46 49
47 50 Notebook.prototype.create_elements = function () {
48 51 // We add this end_space div to the end of the notebook div to:
49 52 // i) provide a margin between the last cell and the end of the notebook
50 53 // ii) to prevent the div from scrolling up when the last cell is being
51 54 // edited, but is too low on the page, which browsers will do automatically.
52 55 var that = this;
53 56 var end_space = $('<div/>').addClass('end_space').height("30%");
54 57 end_space.dblclick(function (e) {
55 58 if (that.read_only) return;
56 59 var ncells = that.ncells();
57 60 that.insert_cell_below('code',ncells-1);
58 61 });
59 62 this.element.append(end_space);
60 63 $('div#notebook').addClass('border-box-sizing');
61 64 };
62 65
63 66
64 67 Notebook.prototype.bind_events = function () {
65 68 var that = this;
66 69
67 70 $([IPython.events]).on('set_next_input.Notebook', function (event, data) {
68 71 var index = that.find_cell_index(data.cell);
69 72 var new_cell = that.insert_cell_below('code',index);
70 73 new_cell.set_text(data.text);
71 74 that.dirty = true;
72 75 });
73 76
74 77 $([IPython.events]).on('set_dirty.Notebook', function (event, data) {
75 78 that.dirty = data.value;
76 79 });
77 80
78 81 $([IPython.events]).on('select.Cell', function (event, data) {
79 82 var index = that.find_cell_index(data.cell);
80 83 that.select(index);
81 84 });
82 85
83 86
84 87 $(document).keydown(function (event) {
85 88 // console.log(event);
86 89 if (that.read_only) return true;
87 90
88 91 // Save (CTRL+S) or (AppleKey+S)
89 92 //metaKey = applekey on mac
90 93 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
91 94 that.save_notebook();
92 95 event.preventDefault();
93 96 return false;
94 97 } else if (event.which === key.ESC) {
95 98 // Intercept escape at highest level to avoid closing
96 99 // websocket connection with firefox
97 100 event.preventDefault();
98 101 } else if (event.which === key.SHIFT) {
99 102 // ignore shift keydown
100 103 return true;
101 104 }
102 105 if (event.which === key.UPARROW && !event.shiftKey) {
103 106 var cell = that.get_selected_cell();
104 107 if (cell.at_top()) {
105 108 event.preventDefault();
106 109 that.select_prev();
107 110 };
108 111 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
109 112 var cell = that.get_selected_cell();
110 113 if (cell.at_bottom()) {
111 114 event.preventDefault();
112 115 that.select_next();
113 116 };
114 117 } else if (event.which === key.ENTER && event.shiftKey) {
115 118 that.execute_selected_cell();
116 119 return false;
117 120 } else if (event.which === key.ENTER && event.altKey) {
118 121 // Execute code cell, and insert new in place
119 122 that.execute_selected_cell();
120 123 // Only insert a new cell, if we ended up in an already populated cell
121 124 if (/\S/.test(that.get_selected_cell().get_text()) == true) {
122 125 that.insert_cell_above('code');
123 126 }
124 127 return false;
125 128 } else if (event.which === key.ENTER && event.ctrlKey) {
126 129 that.execute_selected_cell({terminal:true});
127 130 return false;
128 131 } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
129 132 that.control_key_active = true;
130 133 return false;
131 134 } else if (event.which === 88 && that.control_key_active) {
132 135 // Cut selected cell = x
133 136 that.cut_cell();
134 137 that.control_key_active = false;
135 138 return false;
136 139 } else if (event.which === 67 && that.control_key_active) {
137 140 // Copy selected cell = c
138 141 that.copy_cell();
139 142 that.control_key_active = false;
140 143 return false;
141 144 } else if (event.which === 86 && that.control_key_active) {
142 145 // Paste selected cell = v
143 146 that.paste_cell();
144 147 that.control_key_active = false;
145 148 return false;
146 149 } else if (event.which === 68 && that.control_key_active) {
147 150 // Delete selected cell = d
148 151 that.delete_cell();
149 152 that.control_key_active = false;
150 153 return false;
151 154 } else if (event.which === 65 && that.control_key_active) {
152 155 // Insert code cell above selected = a
153 156 that.insert_cell_above('code');
154 157 that.control_key_active = false;
155 158 return false;
156 159 } else if (event.which === 66 && that.control_key_active) {
157 160 // Insert code cell below selected = b
158 161 that.insert_cell_below('code');
159 162 that.control_key_active = false;
160 163 return false;
161 164 } else if (event.which === 89 && that.control_key_active) {
162 165 // To code = y
163 166 that.to_code();
164 167 that.control_key_active = false;
165 168 return false;
166 169 } else if (event.which === 77 && that.control_key_active) {
167 170 // To markdown = m
168 171 that.to_markdown();
169 172 that.control_key_active = false;
170 173 return false;
171 174 } else if (event.which === 84 && that.control_key_active) {
172 175 // To Raw = t
173 176 that.to_raw();
174 177 that.control_key_active = false;
175 178 return false;
176 179 } else if (event.which === 49 && that.control_key_active) {
177 180 // To Heading 1 = 1
178 181 that.to_heading(undefined, 1);
179 182 that.control_key_active = false;
180 183 return false;
181 184 } else if (event.which === 50 && that.control_key_active) {
182 185 // To Heading 2 = 2
183 186 that.to_heading(undefined, 2);
184 187 that.control_key_active = false;
185 188 return false;
186 189 } else if (event.which === 51 && that.control_key_active) {
187 190 // To Heading 3 = 3
188 191 that.to_heading(undefined, 3);
189 192 that.control_key_active = false;
190 193 return false;
191 194 } else if (event.which === 52 && that.control_key_active) {
192 195 // To Heading 4 = 4
193 196 that.to_heading(undefined, 4);
194 197 that.control_key_active = false;
195 198 return false;
196 199 } else if (event.which === 53 && that.control_key_active) {
197 200 // To Heading 5 = 5
198 201 that.to_heading(undefined, 5);
199 202 that.control_key_active = false;
200 203 return false;
201 204 } else if (event.which === 54 && that.control_key_active) {
202 205 // To Heading 6 = 6
203 206 that.to_heading(undefined, 6);
204 207 that.control_key_active = false;
205 208 return false;
206 209 } else if (event.which === 79 && that.control_key_active) {
207 210 // Toggle output = o
208 211 if (event.shiftKey){
209 212 that.toggle_output_scroll();
210 213 } else {
211 214 that.toggle_output();
212 215 }
213 216 that.control_key_active = false;
214 217 return false;
215 218 } else if (event.which === 83 && that.control_key_active) {
216 219 // Save notebook = s
217 220 that.save_notebook();
218 221 that.control_key_active = false;
219 222 return false;
220 223 } else if (event.which === 74 && that.control_key_active) {
221 224 // Move cell down = j
222 225 that.move_cell_down();
223 226 that.control_key_active = false;
224 227 return false;
225 228 } else if (event.which === 75 && that.control_key_active) {
226 229 // Move cell up = k
227 230 that.move_cell_up();
228 231 that.control_key_active = false;
229 232 return false;
230 233 } else if (event.which === 80 && that.control_key_active) {
231 234 // Select previous = p
232 235 that.select_prev();
233 236 that.control_key_active = false;
234 237 return false;
235 238 } else if (event.which === 78 && that.control_key_active) {
236 239 // Select next = n
237 240 that.select_next();
238 241 that.control_key_active = false;
239 242 return false;
240 243 } else if (event.which === 76 && that.control_key_active) {
241 244 // Toggle line numbers = l
242 245 that.cell_toggle_line_numbers();
243 246 that.control_key_active = false;
244 247 return false;
245 248 } else if (event.which === 73 && that.control_key_active) {
246 249 // Interrupt kernel = i
247 250 that.kernel.interrupt();
248 251 that.control_key_active = false;
249 252 return false;
250 253 } else if (event.which === 190 && that.control_key_active) {
251 254 // Restart kernel = . # matches qt console
252 255 that.restart_kernel();
253 256 that.control_key_active = false;
254 257 return false;
255 258 } else if (event.which === 72 && that.control_key_active) {
256 259 // Show keyboard shortcuts = h
257 260 IPython.quick_help.show_keyboard_shortcuts();
258 261 that.control_key_active = false;
259 262 return false;
263 } else if (event.which === 90 && that.control_key_active) {
264 // Undo last cell delete = z
265 that.undelete();
266 that.control_key_active = false;
267 return false;
260 268 } else if (that.control_key_active) {
261 269 that.control_key_active = false;
262 270 return true;
263 271 };
264 272 return true;
265 273 });
266 274
267 275 var collapse_time = function(time){
268 276 var app_height = $('div#main_app').height(); // content height
269 277 var splitter_height = $('div#pager_splitter').outerHeight(true);
270 278 var new_height = app_height - splitter_height;
271 279 that.element.animate({height : new_height + 'px'}, time);
272 280 }
273 281
274 282 this.element.bind('collapse_pager', function (event,extrap) {
275 283 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
276 284 collapse_time(time);
277 285 });
278 286
279 287 var expand_time = function(time) {
280 288 var app_height = $('div#main_app').height(); // content height
281 289 var splitter_height = $('div#pager_splitter').outerHeight(true);
282 290 var pager_height = $('div#pager').outerHeight(true);
283 291 var new_height = app_height - pager_height - splitter_height;
284 292 that.element.animate({height : new_height + 'px'}, time);
285 293 }
286 294
287 295 this.element.bind('expand_pager', function (event, extrap) {
288 296 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
289 297 expand_time(time);
290 298 });
291 299
292 300 $(window).bind('beforeunload', function () {
293 301 // TODO: Make killing the kernel configurable.
294 302 var kill_kernel = false;
295 303 if (kill_kernel) {
296 304 that.kernel.kill();
297 305 }
298 306 if (that.dirty && ! that.read_only) {
299 307 return "You have unsaved changes that will be lost if you leave this page.";
300 308 };
301 309 // Null is the *only* return value that will make the browser not
302 310 // pop up the "don't leave" dialog.
303 311 return null;
304 312 });
305 313 };
306 314
307 315 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
308 316 var cells = this.get_cells();
309 317 var time = time || 0;
310 318 cell_number = Math.min(cells.length-1,cell_number);
311 319 cell_number = Math.max(0 ,cell_number);
312 320 scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
313 321 this.element.animate({scrollTop:scroll_value}, time);
314 322 return scroll_value;
315 323 };
316 324
317 325
318 326 Notebook.prototype.scroll_to_bottom = function () {
319 327 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
320 328 };
321 329
322 330
323 331 Notebook.prototype.scroll_to_top = function () {
324 332 this.element.animate({scrollTop:0}, 0);
325 333 };
326 334
327 335
328 336 // Cell indexing, retrieval, etc.
329 337
330 338 Notebook.prototype.get_cell_elements = function () {
331 339 return this.element.children("div.cell");
332 340 };
333 341
334 342
335 343 Notebook.prototype.get_cell_element = function (index) {
336 344 var result = null;
337 345 var e = this.get_cell_elements().eq(index);
338 346 if (e.length !== 0) {
339 347 result = e;
340 348 }
341 349 return result;
342 350 };
343 351
344 352
345 353 Notebook.prototype.ncells = function (cell) {
346 354 return this.get_cell_elements().length;
347 355 };
348 356
349 357
350 358 // TODO: we are often calling cells as cells()[i], which we should optimize
351 359 // to cells(i) or a new method.
352 360 Notebook.prototype.get_cells = function () {
353 361 return this.get_cell_elements().toArray().map(function (e) {
354 362 return $(e).data("cell");
355 363 });
356 364 };
357 365
358 366
359 367 Notebook.prototype.get_cell = function (index) {
360 368 var result = null;
361 369 var ce = this.get_cell_element(index);
362 370 if (ce !== null) {
363 371 result = ce.data('cell');
364 372 }
365 373 return result;
366 374 }
367 375
368 376
369 377 Notebook.prototype.get_next_cell = function (cell) {
370 378 var result = null;
371 379 var index = this.find_cell_index(cell);
372 380 if (index !== null && index < this.ncells()) {
373 381 result = this.get_cell(index+1);
374 382 }
375 383 return result;
376 384 }
377 385
378 386
379 387 Notebook.prototype.get_prev_cell = function (cell) {
380 388 var result = null;
381 389 var index = this.find_cell_index(cell);
382 390 if (index !== null && index > 1) {
383 391 result = this.get_cell(index-1);
384 392 }
385 393 return result;
386 394 }
387 395
388 396 Notebook.prototype.find_cell_index = function (cell) {
389 397 var result = null;
390 398 this.get_cell_elements().filter(function (index) {
391 399 if ($(this).data("cell") === cell) {
392 400 result = index;
393 401 };
394 402 });
395 403 return result;
396 404 };
397 405
398 406
399 407 Notebook.prototype.index_or_selected = function (index) {
400 408 var i;
401 409 if (index === undefined || index === null) {
402 410 i = this.get_selected_index();
403 411 if (i === null) {
404 412 i = 0;
405 413 }
406 414 } else {
407 415 i = index;
408 416 }
409 417 return i;
410 418 };
411 419
412 420
413 421 Notebook.prototype.get_selected_cell = function () {
414 422 var index = this.get_selected_index();
415 423 return this.get_cell(index);
416 424 };
417 425
418 426
419 427 Notebook.prototype.is_valid_cell_index = function (index) {
420 428 if (index !== null && index >= 0 && index < this.ncells()) {
421 429 return true;
422 430 } else {
423 431 return false;
424 432 };
425 433 }
426 434
427 435 Notebook.prototype.get_selected_index = function () {
428 436 var result = null;
429 437 this.get_cell_elements().filter(function (index) {
430 438 if ($(this).data("cell").selected === true) {
431 439 result = index;
432 440 };
433 441 });
434 442 return result;
435 443 };
436 444
437 445
438 446 // Cell selection.
439 447
440 448 Notebook.prototype.select = function (index) {
441 449 if (index !== undefined && index >= 0 && index < this.ncells()) {
442 450 sindex = this.get_selected_index()
443 451 if (sindex !== null && index !== sindex) {
444 452 this.get_cell(sindex).unselect();
445 453 };
446 454 var cell = this.get_cell(index)
447 455 cell.select();
448 456 if (cell.cell_type === 'heading') {
449 457 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
450 458 {'cell_type':cell.cell_type,level:cell.level}
451 459 );
452 460 } else {
453 461 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
454 462 {'cell_type':cell.cell_type}
455 463 );
456 464 };
457 465 };
458 466 return this;
459 467 };
460 468
461 469
462 470 Notebook.prototype.select_next = function () {
463 471 var index = this.get_selected_index();
464 472 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
465 473 this.select(index+1);
466 474 };
467 475 return this;
468 476 };
469 477
470 478
471 479 Notebook.prototype.select_prev = function () {
472 480 var index = this.get_selected_index();
473 481 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
474 482 this.select(index-1);
475 483 };
476 484 return this;
477 485 };
478 486
479 487
480 488 // Cell movement
481 489
482 490 Notebook.prototype.move_cell_up = function (index) {
483 491 var i = this.index_or_selected();
484 492 if (i !== null && i < this.ncells() && i > 0) {
485 493 var pivot = this.get_cell_element(i-1);
486 494 var tomove = this.get_cell_element(i);
487 495 if (pivot !== null && tomove !== null) {
488 496 tomove.detach();
489 497 pivot.before(tomove);
490 498 this.select(i-1);
491 499 };
492 500 };
493 501 this.dirty = true;
494 502 return this;
495 503 };
496 504
497 505
498 506 Notebook.prototype.move_cell_down = function (index) {
499 507 var i = this.index_or_selected();
500 508 if (i !== null && i < (this.ncells()-1) && i >= 0) {
501 509 var pivot = this.get_cell_element(i+1);
502 510 var tomove = this.get_cell_element(i);
503 511 if (pivot !== null && tomove !== null) {
504 512 tomove.detach();
505 513 pivot.after(tomove);
506 514 this.select(i+1);
507 515 };
508 516 };
509 517 this.dirty = true;
510 518 return this;
511 519 };
512 520
513 521
514 522 Notebook.prototype.sort_cells = function () {
515 523 // This is not working right now. Calling this will actually crash
516 524 // the browser. I think there is an infinite loop in here...
517 525 var ncells = this.ncells();
518 526 var sindex = this.get_selected_index();
519 527 var swapped;
520 528 do {
521 529 swapped = false;
522 530 for (var i=1; i<ncells; i++) {
523 531 current = this.get_cell(i);
524 532 previous = this.get_cell(i-1);
525 533 if (previous.input_prompt_number > current.input_prompt_number) {
526 534 this.move_cell_up(i);
527 535 swapped = true;
528 536 };
529 537 };
530 538 } while (swapped);
531 539 this.select(sindex);
532 540 return this;
533 541 };
534 542
535 543 // Insertion, deletion.
536 544
537 545 Notebook.prototype.delete_cell = function (index) {
538 546 var i = this.index_or_selected(index);
547 var cell = this.get_selected_cell();
548 this.undelete_backup = cell.toJSON();
539 549 if (this.is_valid_cell_index(i)) {
540 550 var ce = this.get_cell_element(i);
541 551 ce.remove();
542 552 if (i === (this.ncells())) {
543 553 this.select(i-1);
554 this.undelete_index = i - 1;
555 this.undelete_below = true;
544 556 } else {
545 557 this.select(i);
558 this.undelete_index = i;
559 this.undelete_below = false;
546 560 };
547 561 this.dirty = true;
548 562 };
549 563 return this;
550 564 };
551 565
552 566
553 567 Notebook.prototype.insert_cell_at_bottom = function (type){
554 568 var len = this.ncells();
555 569 return this.insert_cell_below(type,len-1);
556 570 }
557 571
558 572 Notebook.prototype.insert_cell_below = function (type, index) {
559 573 // type = ('code','html','markdown')
560 574 // index = cell index or undefined to insert below selected
561 575 index = this.index_or_selected(index);
562 576 var cell = null;
577 // This is intentionally < rather than <= for the sake of more
578 // sensible behavior in some cases.
579 if (this.undelete_index !== null && index < this.undelete_index) {
580 this.undelete_index = this.undelete_index + 1;
581 }
563 582 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
564 583 if (type === 'code') {
565 584 cell = new IPython.CodeCell(this.kernel);
566 585 cell.set_input_prompt();
567 586 } else if (type === 'markdown') {
568 587 cell = new IPython.MarkdownCell();
569 588 } else if (type === 'html') {
570 589 cell = new IPython.HTMLCell();
571 590 } else if (type === 'raw') {
572 591 cell = new IPython.RawCell();
573 592 } else if (type === 'heading') {
574 593 cell = new IPython.HeadingCell();
575 594 };
576 595 if (cell !== null) {
577 596 if (this.ncells() === 0) {
578 597 this.element.find('div.end_space').before(cell.element);
579 598 } else if (this.is_valid_cell_index(index)) {
580 599 this.get_cell_element(index).after(cell.element);
581 600 };
582 601 cell.render();
583 602 this.select(this.find_cell_index(cell));
584 603 this.dirty = true;
585 604 return cell;
586 605 };
587 606 };
588 607 return cell;
589 608 };
590 609
591 610
592 611 Notebook.prototype.insert_cell_above = function (type, index) {
593 612 // type = ('code','html','markdown')
594 613 // index = cell index or undefined to insert above selected
595 614 index = this.index_or_selected(index);
596 615 var cell = null;
616 if (this.undelete_index !== null && index <= this.undelete_index) {
617 this.undelete_index = this.undelete_index + 1;
618 }
597 619 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
598 620 if (type === 'code') {
599 621 cell = new IPython.CodeCell(this.kernel);
600 622 cell.set_input_prompt();
601 623 } else if (type === 'markdown') {
602 624 cell = new IPython.MarkdownCell();
603 625 } else if (type === 'html') {
604 626 cell = new IPython.HTMLCell();
605 627 } else if (type === 'raw') {
606 628 cell = new IPython.RawCell();
607 629 } else if (type === 'heading') {
608 630 cell = new IPython.HeadingCell();
609 631 };
610 632 if (cell !== null) {
611 633 if (this.ncells() === 0) {
612 634 this.element.find('div.end_space').before(cell.element);
613 635 } else if (this.is_valid_cell_index(index)) {
614 636 this.get_cell_element(index).before(cell.element);
615 637 };
616 638 cell.render();
617 639 this.select(this.find_cell_index(cell));
618 640 this.dirty = true;
619 641 return cell;
620 642 };
621 643 };
622 644 return cell;
623 645 };
624 646
625 647
626 648 Notebook.prototype.to_code = function (index) {
627 649 var i = this.index_or_selected(index);
628 650 if (this.is_valid_cell_index(i)) {
629 651 var source_element = this.get_cell_element(i);
630 652 var source_cell = source_element.data("cell");
631 653 if (!(source_cell instanceof IPython.CodeCell)) {
632 654 target_cell = this.insert_cell_below('code',i);
633 655 var text = source_cell.get_text();
634 656 if (text === source_cell.placeholder) {
635 657 text = '';
636 658 }
637 659 target_cell.set_text(text);
638 660 // make this value the starting point, so that we can only undo
639 661 // to this state, instead of a blank cell
640 662 target_cell.code_mirror.clearHistory();
641 663 source_element.remove();
642 664 this.dirty = true;
643 665 };
644 666 };
645 667 };
646 668
647 669
648 670 Notebook.prototype.to_markdown = function (index) {
649 671 var i = this.index_or_selected(index);
650 672 if (this.is_valid_cell_index(i)) {
651 673 var source_element = this.get_cell_element(i);
652 674 var source_cell = source_element.data("cell");
653 675 if (!(source_cell instanceof IPython.MarkdownCell)) {
654 676 target_cell = this.insert_cell_below('markdown',i);
655 677 var text = source_cell.get_text();
656 678 if (text === source_cell.placeholder) {
657 679 text = '';
658 680 };
659 681 // The edit must come before the set_text.
660 682 target_cell.edit();
661 683 target_cell.set_text(text);
662 684 // make this value the starting point, so that we can only undo
663 685 // to this state, instead of a blank cell
664 686 target_cell.code_mirror.clearHistory();
665 687 source_element.remove();
666 688 this.dirty = true;
667 689 };
668 690 };
669 691 };
670 692
671 693
672 694 Notebook.prototype.to_html = function (index) {
673 695 var i = this.index_or_selected(index);
674 696 if (this.is_valid_cell_index(i)) {
675 697 var source_element = this.get_cell_element(i);
676 698 var source_cell = source_element.data("cell");
677 699 var target_cell = null;
678 700 if (!(source_cell instanceof IPython.HTMLCell)) {
679 701 target_cell = this.insert_cell_below('html',i);
680 702 var text = source_cell.get_text();
681 703 if (text === source_cell.placeholder) {
682 704 text = '';
683 705 };
684 706 // The edit must come before the set_text.
685 707 target_cell.edit();
686 708 target_cell.set_text(text);
687 709 // make this value the starting point, so that we can only undo
688 710 // to this state, instead of a blank cell
689 711 target_cell.code_mirror.clearHistory();
690 712 source_element.remove();
691 713 this.dirty = true;
692 714 };
693 715 };
694 716 };
695 717
696 718
697 719 Notebook.prototype.to_raw = function (index) {
698 720 var i = this.index_or_selected(index);
699 721 if (this.is_valid_cell_index(i)) {
700 722 var source_element = this.get_cell_element(i);
701 723 var source_cell = source_element.data("cell");
702 724 var target_cell = null;
703 725 if (!(source_cell instanceof IPython.RawCell)) {
704 726 target_cell = this.insert_cell_below('raw',i);
705 727 var text = source_cell.get_text();
706 728 if (text === source_cell.placeholder) {
707 729 text = '';
708 730 };
709 731 // The edit must come before the set_text.
710 732 target_cell.edit();
711 733 target_cell.set_text(text);
712 734 // make this value the starting point, so that we can only undo
713 735 // to this state, instead of a blank cell
714 736 target_cell.code_mirror.clearHistory();
715 737 source_element.remove();
716 738 this.dirty = true;
717 739 };
718 740 };
719 741 };
720 742
721 743
722 744 Notebook.prototype.to_heading = function (index, level) {
723 745 level = level || 1;
724 746 var i = this.index_or_selected(index);
725 747 if (this.is_valid_cell_index(i)) {
726 748 var source_element = this.get_cell_element(i);
727 749 var source_cell = source_element.data("cell");
728 750 var target_cell = null;
729 751 if (source_cell instanceof IPython.HeadingCell) {
730 752 source_cell.set_level(level);
731 753 } else {
732 754 target_cell = this.insert_cell_below('heading',i);
733 755 var text = source_cell.get_text();
734 756 if (text === source_cell.placeholder) {
735 757 text = '';
736 758 };
737 759 // The edit must come before the set_text.
738 760 target_cell.set_level(level);
739 761 target_cell.edit();
740 762 target_cell.set_text(text);
741 763 // make this value the starting point, so that we can only undo
742 764 // to this state, instead of a blank cell
743 765 target_cell.code_mirror.clearHistory();
744 766 source_element.remove();
745 767 this.dirty = true;
746 768 };
747 769 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
748 770 {'cell_type':'heading',level:level}
749 771 );
750 772 };
751 773 };
752 774
753 775
754 776 // Cut/Copy/Paste
755 777
756 778 Notebook.prototype.enable_paste = function () {
757 779 var that = this;
758 780 if (!this.paste_enabled) {
759 781 $('#paste_cell').removeClass('ui-state-disabled')
760 782 .on('click', function () {that.paste_cell();});
761 783 $('#paste_cell_above').removeClass('ui-state-disabled')
762 784 .on('click', function () {that.paste_cell_above();});
763 785 $('#paste_cell_below').removeClass('ui-state-disabled')
764 786 .on('click', function () {that.paste_cell_below();});
765 787 this.paste_enabled = true;
766 788 };
767 789 };
768 790
769 791
770 792 Notebook.prototype.disable_paste = function () {
771 793 if (this.paste_enabled) {
772 794 $('#paste_cell').addClass('ui-state-disabled').off('click');
773 795 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
774 796 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
775 797 this.paste_enabled = false;
776 798 };
777 799 };
778 800
779 801
780 802 Notebook.prototype.cut_cell = function () {
781 803 this.copy_cell();
782 804 this.delete_cell();
783 805 }
784 806
785 807 Notebook.prototype.copy_cell = function () {
786 808 var cell = this.get_selected_cell();
787 809 this.clipboard = cell.toJSON();
788 810 this.enable_paste();
789 811 };
790 812
791 813
792 814 Notebook.prototype.paste_cell = function () {
793 815 if (this.clipboard !== null && this.paste_enabled) {
794 816 var cell_data = this.clipboard;
795 817 var new_cell = this.insert_cell_above(cell_data.cell_type);
796 818 new_cell.fromJSON(cell_data);
797 819 old_cell = this.get_next_cell(new_cell);
798 820 this.delete_cell(this.find_cell_index(old_cell));
799 821 this.select(this.find_cell_index(new_cell));
800 822 };
801 823 };
802 824
803 825
804 826 Notebook.prototype.paste_cell_above = function () {
805 827 if (this.clipboard !== null && this.paste_enabled) {
806 828 var cell_data = this.clipboard;
807 829 var new_cell = this.insert_cell_above(cell_data.cell_type);
808 830 new_cell.fromJSON(cell_data);
809 831 };
810 832 };
811 833
812 834
813 835 Notebook.prototype.paste_cell_below = function () {
814 836 if (this.clipboard !== null && this.paste_enabled) {
815 837 var cell_data = this.clipboard;
816 838 var new_cell = this.insert_cell_below(cell_data.cell_type);
817 839 new_cell.fromJSON(cell_data);
818 840 };
819 841 };
820 842
843 // Cell undelete
844
845 Notebook.prototype.undelete = function() {
846 if (this.undelete_backup !== null && this.undelete_index !== null) {
847 var current_index = this.get_selected_index();
848 if (this.undelete_index < current_index) {
849 current_index = current_index + 1;
850 }
851 if (this.undelete_index >= this.ncells()) {
852 this.select(this.ncells() - 1);
853 }
854 else {
855 this.select(this.undelete_index);
856 }
857 var cell_data = this.undelete_backup;
858 var new_cell = null;
859 if (this.undelete_below) {
860 new_cell = this.insert_cell_below(cell_data.cell_type);
861 } else {
862 new_cell = this.insert_cell_above(cell_data.cell_type);
863 }
864 new_cell.fromJSON(cell_data);
865 this.select(current_index);
866 this.undelete_backup = null;
867 this.undelete_index = null;
868 }
869 }
821 870
822 871 // Split/merge
823 872
824 873 Notebook.prototype.split_cell = function () {
825 874 // Todo: implement spliting for other cell types.
826 875 var cell = this.get_selected_cell();
827 876 if (cell.is_splittable()) {
828 877 texta = cell.get_pre_cursor();
829 878 textb = cell.get_post_cursor();
830 879 if (cell instanceof IPython.CodeCell) {
831 880 cell.set_text(texta);
832 881 var new_cell = this.insert_cell_below('code');
833 882 new_cell.set_text(textb);
834 883 } else if (cell instanceof IPython.MarkdownCell) {
835 884 cell.set_text(texta);
836 885 cell.render();
837 886 var new_cell = this.insert_cell_below('markdown');
838 887 new_cell.edit(); // editor must be visible to call set_text
839 888 new_cell.set_text(textb);
840 889 new_cell.render();
841 890 } else if (cell instanceof IPython.HTMLCell) {
842 891 cell.set_text(texta);
843 892 cell.render();
844 893 var new_cell = this.insert_cell_below('html');
845 894 new_cell.edit(); // editor must be visible to call set_text
846 895 new_cell.set_text(textb);
847 896 new_cell.render();
848 897 };
849 898 };
850 899 };
851 900
852 901
853 902 Notebook.prototype.merge_cell_above = function () {
854 903 var index = this.get_selected_index();
855 904 var cell = this.get_cell(index);
856 905 if (index > 0) {
857 906 upper_cell = this.get_cell(index-1);
858 907 upper_text = upper_cell.get_text();
859 908 text = cell.get_text();
860 909 if (cell instanceof IPython.CodeCell) {
861 910 cell.set_text(upper_text+'\n'+text);
862 911 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
863 912 cell.edit();
864 913 cell.set_text(upper_text+'\n'+text);
865 914 cell.render();
866 915 };
867 916 this.delete_cell(index-1);
868 917 this.select(this.find_cell_index(cell));
869 918 };
870 919 };
871 920
872 921
873 922 Notebook.prototype.merge_cell_below = function () {
874 923 var index = this.get_selected_index();
875 924 var cell = this.get_cell(index);
876 925 if (index < this.ncells()-1) {
877 926 lower_cell = this.get_cell(index+1);
878 927 lower_text = lower_cell.get_text();
879 928 text = cell.get_text();
880 929 if (cell instanceof IPython.CodeCell) {
881 930 cell.set_text(text+'\n'+lower_text);
882 931 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
883 932 cell.edit();
884 933 cell.set_text(text+'\n'+lower_text);
885 934 cell.render();
886 935 };
887 936 this.delete_cell(index+1);
888 937 this.select(this.find_cell_index(cell));
889 938 };
890 939 };
891 940
892 941
893 942 // Cell collapsing and output clearing
894 943
895 944 Notebook.prototype.collapse = function (index) {
896 945 var i = this.index_or_selected(index);
897 946 this.get_cell(i).collapse();
898 947 this.dirty = true;
899 948 };
900 949
901 950
902 951 Notebook.prototype.expand = function (index) {
903 952 var i = this.index_or_selected(index);
904 953 this.get_cell(i).expand();
905 954 this.dirty = true;
906 955 };
907 956
908 957
909 958 Notebook.prototype.toggle_output = function (index) {
910 959 var i = this.index_or_selected(index);
911 960 this.get_cell(i).toggle_output();
912 961 this.dirty = true;
913 962 };
914 963
915 964
916 965 Notebook.prototype.toggle_output_scroll = function (index) {
917 966 var i = this.index_or_selected(index);
918 967 this.get_cell(i).toggle_output_scroll();
919 968 };
920 969
921 970
922 971 Notebook.prototype.collapse_all_output = function () {
923 972 var ncells = this.ncells();
924 973 var cells = this.get_cells();
925 974 for (var i=0; i<ncells; i++) {
926 975 if (cells[i] instanceof IPython.CodeCell) {
927 976 cells[i].output_area.collapse();
928 977 }
929 978 };
930 979 // this should not be set if the `collapse` key is removed from nbformat
931 980 this.dirty = true;
932 981 };
933 982
934 983
935 984 Notebook.prototype.scroll_all_output = function () {
936 985 var ncells = this.ncells();
937 986 var cells = this.get_cells();
938 987 for (var i=0; i<ncells; i++) {
939 988 if (cells[i] instanceof IPython.CodeCell) {
940 989 cells[i].output_area.expand();
941 990 cells[i].output_area.scroll_if_long(20);
942 991 }
943 992 };
944 993 // this should not be set if the `collapse` key is removed from nbformat
945 994 this.dirty = true;
946 995 };
947 996
948 997
949 998 Notebook.prototype.expand_all_output = function () {
950 999 var ncells = this.ncells();
951 1000 var cells = this.get_cells();
952 1001 for (var i=0; i<ncells; i++) {
953 1002 if (cells[i] instanceof IPython.CodeCell) {
954 1003 cells[i].output_area.expand();
955 1004 cells[i].output_area.unscroll_area();
956 1005 }
957 1006 };
958 1007 // this should not be set if the `collapse` key is removed from nbformat
959 1008 this.dirty = true;
960 1009 };
961 1010
962 1011
963 1012 Notebook.prototype.clear_all_output = function () {
964 1013 var ncells = this.ncells();
965 1014 var cells = this.get_cells();
966 1015 for (var i=0; i<ncells; i++) {
967 1016 if (cells[i] instanceof IPython.CodeCell) {
968 1017 cells[i].clear_output(true,true,true);
969 1018 // Make all In[] prompts blank, as well
970 1019 // TODO: make this configurable (via checkbox?)
971 1020 cells[i].set_input_prompt();
972 1021 }
973 1022 };
974 1023 this.dirty = true;
975 1024 };
976 1025
977 1026
978 1027 // Other cell functions: line numbers, ...
979 1028
980 1029 Notebook.prototype.cell_toggle_line_numbers = function() {
981 1030 this.get_selected_cell().toggle_line_numbers();
982 1031 };
983 1032
984 1033 // Kernel related things
985 1034
986 1035 Notebook.prototype.start_kernel = function () {
987 1036 var base_url = $('body').data('baseKernelUrl') + "kernels";
988 1037 this.kernel = new IPython.Kernel(base_url);
989 1038 this.kernel.start(this.notebook_id);
990 1039 // Now that the kernel has been created, tell the CodeCells about it.
991 1040 var ncells = this.ncells();
992 1041 for (var i=0; i<ncells; i++) {
993 1042 var cell = this.get_cell(i);
994 1043 if (cell instanceof IPython.CodeCell) {
995 1044 cell.set_kernel(this.kernel)
996 1045 };
997 1046 };
998 1047 };
999 1048
1000 1049
1001 1050 Notebook.prototype.restart_kernel = function () {
1002 1051 var that = this;
1003 1052 var dialog = $('<div/>');
1004 1053 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
1005 1054 $(document).append(dialog);
1006 1055 dialog.dialog({
1007 1056 resizable: false,
1008 1057 modal: true,
1009 1058 title: "Restart kernel or continue running?",
1010 1059 closeText: '',
1011 1060 buttons : {
1012 1061 "Restart": function () {
1013 1062 that.kernel.restart();
1014 1063 $(this).dialog('close');
1015 1064 },
1016 1065 "Continue running": function () {
1017 1066 $(this).dialog('close');
1018 1067 }
1019 1068 }
1020 1069 });
1021 1070 };
1022 1071
1023 1072
1024 1073 Notebook.prototype.execute_selected_cell = function (options) {
1025 1074 // add_new: should a new cell be added if we are at the end of the nb
1026 1075 // terminal: execute in terminal mode, which stays in the current cell
1027 1076 default_options = {terminal: false, add_new: true};
1028 1077 $.extend(default_options, options);
1029 1078 var that = this;
1030 1079 var cell = that.get_selected_cell();
1031 1080 var cell_index = that.find_cell_index(cell);
1032 1081 if (cell instanceof IPython.CodeCell) {
1033 1082 cell.execute();
1034 1083 } else if (cell instanceof IPython.HTMLCell) {
1035 1084 cell.render();
1036 1085 }
1037 1086 if (default_options.terminal) {
1038 1087 cell.select_all();
1039 1088 } else {
1040 1089 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1041 1090 that.insert_cell_below('code');
1042 1091 // If we are adding a new cell at the end, scroll down to show it.
1043 1092 that.scroll_to_bottom();
1044 1093 } else {
1045 1094 that.select(cell_index+1);
1046 1095 };
1047 1096 };
1048 1097 this.dirty = true;
1049 1098 };
1050 1099
1051 1100
1052 1101 Notebook.prototype.execute_cells_below = function () {
1053 1102 this.execute_cell_range(this.get_selected_index(), this.ncells());
1054 1103 that.scroll_to_bottom();
1055 1104 };
1056 1105
1057 1106 Notebook.prototype.execute_cells_above = function () {
1058 1107 this.execute_cell_range(0, this.get_selected_index());
1059 1108 };
1060 1109
1061 1110 Notebook.prototype.execute_all_cells = function () {
1062 1111 this.execute_cell_range(0, this.ncells());
1063 1112 that.scroll_to_bottom();
1064 1113 };
1065 1114
1066 1115 Notebook.prototype.execute_cell_range = function (start, end) {
1067 1116 for (var i=start; i<end; i++) {
1068 1117 this.select(i);
1069 1118 this.execute_selected_cell({add_new:false});
1070 1119 };
1071 1120 };
1072 1121
1073 1122 // Persistance and loading
1074 1123
1075 1124 Notebook.prototype.get_notebook_id = function () {
1076 1125 return this.notebook_id;
1077 1126 };
1078 1127
1079 1128
1080 1129 Notebook.prototype.get_notebook_name = function () {
1081 1130 return this.notebook_name;
1082 1131 };
1083 1132
1084 1133
1085 1134 Notebook.prototype.set_notebook_name = function (name) {
1086 1135 this.notebook_name = name;
1087 1136 };
1088 1137
1089 1138
1090 1139 Notebook.prototype.test_notebook_name = function (nbname) {
1091 1140 nbname = nbname || '';
1092 1141 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1093 1142 return true;
1094 1143 } else {
1095 1144 return false;
1096 1145 };
1097 1146 };
1098 1147
1099 1148
1100 1149 Notebook.prototype.fromJSON = function (data) {
1101 1150 var ncells = this.ncells();
1102 1151 var i;
1103 1152 for (i=0; i<ncells; i++) {
1104 1153 // Always delete cell 0 as they get renumbered as they are deleted.
1105 1154 this.delete_cell(0);
1106 1155 };
1107 1156 // Save the metadata and name.
1108 1157 this.metadata = data.metadata;
1109 1158 this.notebook_name = data.metadata.name;
1110 1159 // Only handle 1 worksheet for now.
1111 1160 var worksheet = data.worksheets[0];
1112 1161 if (worksheet !== undefined) {
1113 1162 if (worksheet.metadata) {
1114 1163 this.worksheet_metadata = worksheet.metadata;
1115 1164 }
1116 1165 var new_cells = worksheet.cells;
1117 1166 ncells = new_cells.length;
1118 1167 var cell_data = null;
1119 1168 var new_cell = null;
1120 1169 for (i=0; i<ncells; i++) {
1121 1170 cell_data = new_cells[i];
1122 1171 // VERSIONHACK: plaintext -> raw
1123 1172 // handle never-released plaintext name for raw cells
1124 1173 if (cell_data.cell_type === 'plaintext'){
1125 1174 cell_data.cell_type = 'raw';
1126 1175 }
1127 1176
1128 1177 new_cell = this.insert_cell_below(cell_data.cell_type);
1129 1178 new_cell.fromJSON(cell_data);
1130 1179 };
1131 1180 };
1132 1181 if (data.worksheets.length > 1) {
1133 1182 var dialog = $('<div/>');
1134 1183 dialog.html("This notebook has " + data.worksheets.length + " worksheets, " +
1135 1184 "but this version of IPython can only handle the first. " +
1136 1185 "If you save this notebook, worksheets after the first will be lost."
1137 1186 );
1138 1187 this.element.append(dialog);
1139 1188 dialog.dialog({
1140 1189 resizable: false,
1141 1190 modal: true,
1142 1191 title: "Multiple worksheets",
1143 1192 closeText: "",
1144 1193 close: function(event, ui) {$(this).dialog('destroy').remove();},
1145 1194 buttons : {
1146 1195 "OK": function () {
1147 1196 $(this).dialog('close');
1148 1197 }
1149 1198 },
1150 1199 width: 400
1151 1200 });
1152 1201 }
1153 1202 };
1154 1203
1155 1204
1156 1205 Notebook.prototype.toJSON = function () {
1157 1206 var cells = this.get_cells();
1158 1207 var ncells = cells.length;
1159 1208 var cell_array = new Array(ncells);
1160 1209 for (var i=0; i<ncells; i++) {
1161 1210 cell_array[i] = cells[i].toJSON();
1162 1211 };
1163 1212 var data = {
1164 1213 // Only handle 1 worksheet for now.
1165 1214 worksheets : [{
1166 1215 cells: cell_array,
1167 1216 metadata: this.worksheet_metadata
1168 1217 }],
1169 1218 metadata : this.metadata
1170 1219 };
1171 1220 return data;
1172 1221 };
1173 1222
1174 1223 Notebook.prototype.save_notebook = function () {
1175 1224 // We may want to move the name/id/nbformat logic inside toJSON?
1176 1225 var data = this.toJSON();
1177 1226 data.metadata.name = this.notebook_name;
1178 1227 data.nbformat = this.nbformat;
1179 1228 data.nbformat_minor = this.nbformat_minor;
1180 1229 // We do the call with settings so we can set cache to false.
1181 1230 var settings = {
1182 1231 processData : false,
1183 1232 cache : false,
1184 1233 type : "PUT",
1185 1234 data : JSON.stringify(data),
1186 1235 headers : {'Content-Type': 'application/json'},
1187 1236 success : $.proxy(this.save_notebook_success,this),
1188 1237 error : $.proxy(this.save_notebook_error,this)
1189 1238 };
1190 1239 $([IPython.events]).trigger('notebook_saving.Notebook');
1191 1240 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1192 1241 $.ajax(url, settings);
1193 1242 };
1194 1243
1195 1244
1196 1245 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1197 1246 this.dirty = false;
1198 1247 $([IPython.events]).trigger('notebook_saved.Notebook');
1199 1248 };
1200 1249
1201 1250
1202 1251 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1203 1252 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1204 1253 };
1205 1254
1206 1255
1207 1256 Notebook.prototype.load_notebook = function (notebook_id) {
1208 1257 var that = this;
1209 1258 this.notebook_id = notebook_id;
1210 1259 // We do the call with settings so we can set cache to false.
1211 1260 var settings = {
1212 1261 processData : false,
1213 1262 cache : false,
1214 1263 type : "GET",
1215 1264 dataType : "json",
1216 1265 success : $.proxy(this.load_notebook_success,this),
1217 1266 error : $.proxy(this.load_notebook_error,this),
1218 1267 };
1219 1268 $([IPython.events]).trigger('notebook_loading.Notebook');
1220 1269 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1221 1270 $.ajax(url, settings);
1222 1271 };
1223 1272
1224 1273
1225 1274 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1226 1275 this.fromJSON(data);
1227 1276 if (this.ncells() === 0) {
1228 1277 this.insert_cell_below('code');
1229 1278 };
1230 1279 this.dirty = false;
1231 1280 this.select(0);
1232 1281 this.scroll_to_top();
1233 1282 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1234 1283 msg = "This notebook has been converted from an older " +
1235 1284 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1236 1285 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1237 1286 "newer notebook format will be used and older verions of IPython " +
1238 1287 "may not be able to read it. To keep the older version, close the " +
1239 1288 "notebook without saving it.";
1240 1289 var dialog = $('<div/>');
1241 1290 dialog.html(msg);
1242 1291 this.element.append(dialog);
1243 1292 dialog.dialog({
1244 1293 resizable: false,
1245 1294 modal: true,
1246 1295 title: "Notebook converted",
1247 1296 closeText: "",
1248 1297 close: function(event, ui) {$(this).dialog('destroy').remove();},
1249 1298 buttons : {
1250 1299 "OK": function () {
1251 1300 $(this).dialog('close');
1252 1301 }
1253 1302 },
1254 1303 width: 400
1255 1304 });
1256 1305 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1257 1306 var that = this;
1258 1307 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1259 1308 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1260 1309 msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1261 1310 this_vs + ". You can still work with this notebook, but some features " +
1262 1311 "introduced in later notebook versions may not be available."
1263 1312
1264 1313 var dialog = $('<div/>');
1265 1314 dialog.html(msg);
1266 1315 this.element.append(dialog);
1267 1316 dialog.dialog({
1268 1317 resizable: false,
1269 1318 modal: true,
1270 1319 title: "Newer Notebook",
1271 1320 closeText: "",
1272 1321 close: function(event, ui) {$(this).dialog('destroy').remove();},
1273 1322 buttons : {
1274 1323 "OK": function () {
1275 1324 $(this).dialog('close');
1276 1325 }
1277 1326 },
1278 1327 width: 400
1279 1328 });
1280 1329
1281 1330 }
1282 1331 // Create the kernel after the notebook is completely loaded to prevent
1283 1332 // code execution upon loading, which is a security risk.
1284 1333 if (! this.read_only) {
1285 1334 this.start_kernel();
1286 1335 }
1287 1336 $([IPython.events]).trigger('notebook_loaded.Notebook');
1288 1337 };
1289 1338
1290 1339
1291 1340 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1292 1341 if (xhr.status === 500) {
1293 1342 msg = "An error occurred while loading this notebook. Most likely " +
1294 1343 "this notebook is in a newer format than is supported by this " +
1295 1344 "version of IPython. This version can load notebook formats " +
1296 1345 "v"+this.nbformat+" or earlier.";
1297 1346 var dialog = $('<div/>');
1298 1347 dialog.html(msg);
1299 1348 this.element.append(dialog);
1300 1349 dialog.dialog({
1301 1350 resizable: false,
1302 1351 modal: true,
1303 1352 title: "Error loading notebook",
1304 1353 closeText: "",
1305 1354 close: function(event, ui) {$(this).dialog('destroy').remove();},
1306 1355 buttons : {
1307 1356 "OK": function () {
1308 1357 $(this).dialog('close');
1309 1358 }
1310 1359 },
1311 1360 width: 400
1312 1361 });
1313 1362 }
1314 1363 }
1315 1364
1316 1365 IPython.Notebook = Notebook;
1317 1366
1318 1367
1319 1368 return IPython;
1320 1369
1321 1370 }(IPython));
1322 1371
@@ -1,72 +1,73 b''
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 // QuickHelp button
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var QuickHelp = function (selector) {
15 15 };
16 16
17 17 QuickHelp.prototype.show_keyboard_shortcuts = function () {
18 18 // toggles display of keyboard shortcut dialog
19 19 var that = this;
20 20 if ( this.shortcut_dialog ){
21 21 // if dialog is already shown, close it
22 22 this.shortcut_dialog.dialog("close");
23 23 this.shortcut_dialog = null;
24 24 return;
25 25 }
26 26 var dialog = $('<div/>');
27 27 this.shortcut_dialog = dialog;
28 28 var shortcuts = [
29 29 {key: 'Shift-Enter', help: 'run cell'},
30 30 {key: 'Ctrl-Enter', help: 'run cell in-place'},
31 31 {key: 'Alt-Enter', help: 'run cell, insert below'},
32 32 {key: 'Ctrl-m x', help: 'cut cell'},
33 33 {key: 'Ctrl-m c', help: 'copy cell'},
34 34 {key: 'Ctrl-m v', help: 'paste cell'},
35 35 {key: 'Ctrl-m d', help: 'delete cell'},
36 {key: 'Ctrl-m z', help: 'undo last cell deletion'},
36 37 {key: 'Ctrl-m a', help: 'insert cell above'},
37 38 {key: 'Ctrl-m b', help: 'insert cell below'},
38 39 {key: 'Ctrl-m o', help: 'toggle output'},
39 40 {key: 'Ctrl-m O', help: 'toggle output scroll'},
40 41 {key: 'Ctrl-m l', help: 'toggle line numbers'},
41 42 {key: 'Ctrl-m s', help: 'save notebook'},
42 43 {key: 'Ctrl-m j', help: 'move cell down'},
43 44 {key: 'Ctrl-m k', help: 'move cell up'},
44 45 {key: 'Ctrl-m y', help: 'code cell'},
45 46 {key: 'Ctrl-m m', help: 'markdown cell'},
46 47 {key: 'Ctrl-m t', help: 'raw cell'},
47 48 {key: 'Ctrl-m 1-6', help: 'heading 1-6 cell'},
48 49 {key: 'Ctrl-m p', help: 'select previous'},
49 50 {key: 'Ctrl-m n', help: 'select next'},
50 51 {key: 'Ctrl-m i', help: 'interrupt kernel'},
51 52 {key: 'Ctrl-m .', help: 'restart kernel'},
52 53 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
53 54 ];
54 55 for (var i=0; i<shortcuts.length; i++) {
55 56 dialog.append($('<div>').
56 57 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
57 58 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
58 59 );
59 60 };
60 61 dialog.bind('dialogclose', function(event) {
61 62 // dialog has been closed, allow it to be drawn again.
62 63 that.shortcut_dialog = null;
63 64 });
64 65 dialog.dialog({title: 'Keyboard shortcuts', closeText: ''});
65 66 };
66 67
67 68 // Set module variables
68 69 IPython.QuickHelp = QuickHelp;
69 70
70 71 return IPython;
71 72
72 73 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now