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