Show More
The requested changes are too big and content was truncated. Show full diff
@@ -0,0 +1,75 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2013 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
7 | ||
|
8 | //============================================================================ | |
|
9 | // Utility for modal dialogs with bootstrap | |
|
10 | //============================================================================ | |
|
11 | ||
|
12 | IPython.namespace('IPython.dialog'); | |
|
13 | ||
|
14 | IPython.dialog = (function (IPython) { | |
|
15 | ||
|
16 | var modal = function (options) { | |
|
17 | var dialog = $("<div/>").addClass("modal").attr("role", "dialog"); | |
|
18 | dialog.append( | |
|
19 | $("<div/>") | |
|
20 | .addClass("modal-header") | |
|
21 | .append($("<button>") | |
|
22 | .addClass("close") | |
|
23 | .attr("data-dismiss", "modal") | |
|
24 | .html("×") | |
|
25 | ).append( | |
|
26 | $("<h3/>").text(options.title || "") | |
|
27 | ) | |
|
28 | ).append( | |
|
29 | $("<div/>").addClass("modal-body").append( | |
|
30 | options.body || $("<p/>") | |
|
31 | ) | |
|
32 | ); | |
|
33 | ||
|
34 | var footer = $("<div/>").addClass("modal-footer"); | |
|
35 | ||
|
36 | for (var label in options.buttons) { | |
|
37 | var btn_opts = options.buttons[label]; | |
|
38 | var button = $("<button/>") | |
|
39 | .addClass("btn") | |
|
40 | .attr("data-dismiss", "modal") | |
|
41 | .text(label); | |
|
42 | if (btn_opts.click) { | |
|
43 | button.click($.proxy(btn_opts.click, dialog)); | |
|
44 | } | |
|
45 | if (btn_opts.class) { | |
|
46 | button.addClass(btn_opts.class); | |
|
47 | } | |
|
48 | footer.append(button); | |
|
49 | } | |
|
50 | dialog.append(footer); | |
|
51 | // hook up on-open event | |
|
52 | dialog.on("shown", function() { | |
|
53 | setTimeout(function() { | |
|
54 | footer.find("button").last().focus(); | |
|
55 | if (options.open) { | |
|
56 | $.proxy(options.open, dialog)(); | |
|
57 | } | |
|
58 | }, 0); | |
|
59 | }); | |
|
60 | ||
|
61 | // destroy dialog on hide, unless explicitly asked not to | |
|
62 | if (options.destroy == undefined || options.destroy) { | |
|
63 | dialog.on("hidden", function () { | |
|
64 | dialog.remove(); | |
|
65 | }); | |
|
66 | } | |
|
67 | ||
|
68 | return dialog.modal(options); | |
|
69 | } | |
|
70 | ||
|
71 | return { | |
|
72 | modal : modal, | |
|
73 | }; | |
|
74 | ||
|
75 | }(IPython)); |
@@ -1,22 +1,21 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 | // On document ready |
|
10 | 10 | //============================================================================ |
|
11 | 11 | |
|
12 | 12 | |
|
13 | 13 | $(document).ready(function () { |
|
14 | 14 | |
|
15 | 15 | IPython.page = new IPython.Page(); |
|
16 |
$(' |
|
|
17 | $('#ipython-main-app').addClass('border-box-sizing ui-widget'); | |
|
16 | $('button#login_submit').addClass("btn"); | |
|
18 | 17 | IPython.page.show(); |
|
19 | 18 | $('input#password_input').focus(); |
|
20 | 19 | |
|
21 | 20 | }); |
|
22 | 21 |
@@ -1,46 +1,45 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 | // Login button |
|
10 | 10 | //============================================================================ |
|
11 | 11 | |
|
12 | 12 | var IPython = (function (IPython) { |
|
13 | 13 | |
|
14 | 14 | var LoginWidget = function (selector, options) { |
|
15 | 15 | var options = options || {}; |
|
16 | 16 | this.base_url = options.baseProjectUrl || $('body').data('baseProjectUrl') ; |
|
17 | 17 | this.selector = selector; |
|
18 | 18 | if (this.selector !== undefined) { |
|
19 | 19 | this.element = $(selector); |
|
20 | 20 | this.style(); |
|
21 | 21 | this.bind_events(); |
|
22 | 22 | } |
|
23 | 23 | }; |
|
24 | 24 | |
|
25 | 25 | LoginWidget.prototype.style = function () { |
|
26 |
this.element.find( |
|
|
27 | this.element.find('button#login').button(); | |
|
26 | this.element.find("button").addClass("btn btn-small"); | |
|
28 | 27 | }; |
|
29 | 28 | |
|
30 | 29 | |
|
31 | 30 | LoginWidget.prototype.bind_events = function () { |
|
32 | 31 | var that = this; |
|
33 | 32 | this.element.find("button#logout").click(function () { |
|
34 | 33 | window.location = that.base_url+"logout"; |
|
35 | 34 | }); |
|
36 | 35 | this.element.find("button#login").click(function () { |
|
37 | 36 | window.location = that.base_url+"login"; |
|
38 | 37 | }); |
|
39 | 38 | }; |
|
40 | 39 | |
|
41 | 40 | // Set module variables |
|
42 | 41 | IPython.LoginWidget = LoginWidget; |
|
43 | 42 | |
|
44 | 43 | return IPython; |
|
45 | 44 | |
|
46 | 45 | }(IPython)); |
@@ -1,20 +1,20 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 | // On document ready |
|
10 | 10 | //============================================================================ |
|
11 | 11 | |
|
12 | 12 | |
|
13 | 13 | $(document).ready(function () { |
|
14 | 14 | |
|
15 | 15 | IPython.page = new IPython.Page(); |
|
16 |
$('#ipython-main-app').addClass('border-box-sizing |
|
|
16 | $('#ipython-main-app').addClass('border-box-sizing'); | |
|
17 | 17 | IPython.page.show(); |
|
18 | 18 | |
|
19 | 19 | }); |
|
20 | 20 |
@@ -1,1 +1,6 b'' | |||
|
1 | // Custom styles for login.html No newline at end of file | |
|
1 | // Custom styles for login.html | |
|
2 | .center-nav { | |
|
3 | display: inline-block; | |
|
4 | // pull the lower margin back | |
|
5 | margin-bottom: -4px; | |
|
6 | } No newline at end of file |
@@ -1,59 +1,55 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 | // Global header/site setup. |
|
10 | 10 | //============================================================================ |
|
11 | 11 | |
|
12 | 12 | var IPython = (function (IPython) { |
|
13 | 13 | |
|
14 | 14 | var Page = function () { |
|
15 | 15 | this.style(); |
|
16 | 16 | this.bind_events(); |
|
17 | 17 | }; |
|
18 | 18 | |
|
19 | 19 | Page.prototype.style = function () { |
|
20 |
$('div#header').addClass('border-box-sizing') |
|
|
21 | addClass('ui-widget-content'). | |
|
22 | css('border-top-style','none'). | |
|
23 | css('border-left-style','none'). | |
|
24 | css('border-right-style','none'); | |
|
25 | $('div#site').addClass('border-box-sizing') | |
|
20 | $('div#header').addClass('border-box-sizing'); | |
|
21 | $('div#site').addClass('border-box-sizing'); | |
|
26 | 22 | }; |
|
27 | 23 | |
|
28 | 24 | |
|
29 | 25 | Page.prototype.bind_events = function () { |
|
30 | 26 | }; |
|
31 | 27 | |
|
32 | 28 | |
|
33 | 29 | Page.prototype.show = function () { |
|
34 | 30 | // The header and site divs start out hidden to prevent FLOUC. |
|
35 | 31 | // Main scripts should call this method after styling everything. |
|
36 | 32 | this.show_header(); |
|
37 | 33 | this.show_site(); |
|
38 | 34 | }; |
|
39 | 35 | |
|
40 | 36 | |
|
41 | 37 | Page.prototype.show_header = function () { |
|
42 | 38 | // The header and site divs start out hidden to prevent FLOUC. |
|
43 | 39 | // Main scripts should call this method after styling everything. |
|
44 | 40 | $('div#header').css('display','block'); |
|
45 | 41 | }; |
|
46 | 42 | |
|
47 | 43 | |
|
48 | 44 | Page.prototype.show_site = function () { |
|
49 | 45 | // The header and site divs start out hidden to prevent FLOUC. |
|
50 | 46 | // Main scripts should call this method after styling everything. |
|
51 | 47 | $('div#site').css('display','block'); |
|
52 | 48 | }; |
|
53 | 49 | |
|
54 | 50 | |
|
55 | 51 | IPython.Page = Page; |
|
56 | 52 | |
|
57 | 53 | return IPython; |
|
58 | 54 | |
|
59 | 55 | }(IPython)); |
@@ -1,70 +1,73 b'' | |||
|
1 | 1 | /** |
|
2 | 2 | * Primary styles |
|
3 | 3 | * |
|
4 | 4 | * Author: IPython Development Team |
|
5 | 5 | */ |
|
6 | 6 | |
|
7 | 7 | |
|
8 | 8 | body { |
|
9 | 9 | background-color: white; |
|
10 | 10 | /* This makes sure that the body covers the entire window and needs to |
|
11 | 11 | be in a different element than the display: box in wrapper below */ |
|
12 | 12 | position: absolute; |
|
13 | 13 | left: 0px; |
|
14 | 14 | right: 0px; |
|
15 | 15 | top: 0px; |
|
16 | 16 | bottom: 0px; |
|
17 | 17 | overflow: visible; |
|
18 | 18 | } |
|
19 | 19 | |
|
20 | ||
|
21 | 20 | div#header { |
|
22 | 21 | /* Initially hidden to prevent FLOUC */ |
|
23 | 22 | display: none; |
|
24 | position: relative; | |
|
25 | height: 40px; | |
|
26 | padding: 5px; | |
|
27 | margin: 0px; | |
|
28 | width: 100%; | |
|
29 | 23 | } |
|
30 | 24 | |
|
31 |
|
|
|
32 | position: absolute; | |
|
33 | padding: 2px 2px 2px 5px; | |
|
25 | #ipython_notebook { | |
|
26 | padding-left: 16px; | |
|
34 | 27 | } |
|
35 | 28 | |
|
36 |
|
|
|
29 | #ipython_notebook img { | |
|
37 | 30 | font-family: Verdana, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; |
|
38 | 31 | height: 24px; |
|
39 | 32 | text-decoration:none; |
|
40 | display: inline; | |
|
41 | 33 | color: black; |
|
42 | 34 | } |
|
43 | 35 | |
|
44 | 36 | #site { |
|
45 | 37 | width: 100%; |
|
46 | 38 | display: none; |
|
47 | 39 | } |
|
48 | 40 | |
|
49 | /* We set the fonts by hand here to override the values in the theme */ | |
|
50 | .ui-widget { | |
|
51 | font-family: "Lucinda Grande", "Lucinda Sans Unicode", Helvetica, Arial, Verdana, sans-serif; | |
|
52 | } | |
|
53 | ||
|
54 | .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { | |
|
55 | font-family: "Lucinda Grande", "Lucinda Sans Unicode", Helvetica, Arial, Verdana, sans-serif; | |
|
56 | } | |
|
57 | ||
|
58 | 41 | /* Smaller buttons */ |
|
59 | 42 | .ui-button .ui-button-text { |
|
60 | 43 | padding: 0.2em 0.8em; |
|
61 | 44 | font-size: 77%; |
|
62 | 45 | } |
|
63 | 46 | |
|
64 | 47 | input.ui-button { |
|
65 | 48 | padding: 0.3em 0.9em; |
|
66 | 49 | } |
|
50 | .navbar span { | |
|
51 | margin-top: 3px; | |
|
52 | } | |
|
67 | 53 | |
|
68 | 54 | span#login_widget { |
|
69 | 55 | float: right; |
|
70 | 56 | } |
|
57 | ||
|
58 | .nav-header { | |
|
59 | text-transform: none; | |
|
60 | } | |
|
61 | ||
|
62 | .navbar-nobg { | |
|
63 | background-color: transparent; | |
|
64 | background-image: none; | |
|
65 | } | |
|
66 | ||
|
67 | #header > span { | |
|
68 | margin-top: 10px; | |
|
69 | } | |
|
70 | ||
|
71 | .modal-body { | |
|
72 | max-height: 500px; | |
|
73 | } |
@@ -1,4 +1,6 b'' | |||
|
1 | 1 | @import "../base/less/variables.less"; |
|
2 | 2 | @import "../base/less/mixins.less"; |
|
3 | 3 | @import "../base/less/flexbox.less"; |
|
4 | 4 | @import "../base/less/page.less"; |
|
5 | @import "../components/font-awesome/build/assets/font-awesome/less/font-awesome.less"; | |
|
6 | @FontAwesomePath: "../components/font-awesome/build/assets/font-awesome/font"; |
@@ -1,10 +1,11 b'' | |||
|
1 | 1 | // Our customizations to bootstrap go here. |
|
2 | 2 | |
|
3 | 3 | @textColor: @black; |
|
4 | 4 | @baseFontSize: 13px; |
|
5 | @baseLineHeight: 1.231; | |
|
6 | @monoFontFamily: monospace; // to allow user to customize their fonts | |
|
5 | @monoFontFamily: monospace; // to allow user to customize their fonts | |
|
6 | @navbarHeight: 36px; | |
|
7 | 7 | |
|
8 | 8 | // Our own global variables for all pages go here |
|
9 | 9 | |
|
10 |
@corner_radius: |
|
|
10 | @corner_radius: 4px; | |
|
11 | @code_line_height: 1.231em; |
@@ -1,9 +1,8 b'' | |||
|
1 | 1 | /*This file contains any manual css for this page that needs to override the global styles. |
|
2 | 2 | This is only required when different pages style the same element differently. This is just |
|
3 | 3 | a hack to deal with our current css styles and no new styling should be added in this file.*/ |
|
4 | 4 | |
|
5 | 5 | #ipython-main-app { |
|
6 | width: 100%; | |
|
7 | 6 | position: relative; |
|
8 | 7 | font-size: 110%; |
|
9 | 8 | } No newline at end of file |
@@ -1,94 +1,90 b'' | |||
|
1 | 1 | //---------------------------------------------------------------------------- |
|
2 | 2 | // Copyright (C) 2012 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 | // CellToolbar Default |
|
10 | 10 | //============================================================================ |
|
11 | 11 | |
|
12 | 12 | /** |
|
13 | 13 | * Example Use for the CellToolbar library |
|
14 | 14 | */ |
|
15 | 15 | // IIFE without asignement, we don't modifiy the IPython namespace |
|
16 | 16 | (function (IPython) { |
|
17 | 17 | "use strict"; |
|
18 | 18 | |
|
19 | 19 | var CellToolbar = IPython.CellToolbar; |
|
20 | 20 | |
|
21 | 21 | var raw_edit = function(cell){ |
|
22 | 22 | |
|
23 | 23 | var md = cell.metadata |
|
24 | 24 | var error_div = $('<div/>').css('color','red') |
|
25 | 25 | |
|
26 | 26 | var textarea = $('<textarea/>') |
|
27 | 27 | .attr('rows','13') |
|
28 | 28 | .attr('cols','75') |
|
29 | 29 | .attr('name','metadata') |
|
30 | 30 | .text(JSON.stringify(md, null,4)||''); |
|
31 | 31 | var dialogform = $('<div/>').attr('title','Edit the metadata') |
|
32 | 32 | .append( |
|
33 | 33 | $('<form/>').append( |
|
34 | 34 | $('<fieldset/>').append( |
|
35 | 35 | $('<label/>') |
|
36 | 36 | .attr('for','metadata') |
|
37 | 37 | .text("Manually edit the JSON below to manipulate the metadata for this cell. This assumes you know what you are doing and won't complain if it breaks your notebook. We also recommend putting your metadata attributes in an appropriately named sub-structure, so they don't conflict with those of others.") |
|
38 | 38 | ) |
|
39 | 39 | .append(error_div) |
|
40 | 40 | .append($('<br/>')) |
|
41 | 41 | .append( |
|
42 | 42 | textarea |
|
43 | 43 | ) |
|
44 | 44 | ) |
|
45 | 45 | ); |
|
46 | 46 | var editor = CodeMirror.fromTextArea(textarea[0], { |
|
47 | 47 | lineNumbers: true, |
|
48 | 48 | matchBrackets: true, |
|
49 | 49 | }); |
|
50 |
|
|
|
51 | autoOpen: true, | |
|
52 | height: 300, | |
|
53 | width: 650, | |
|
54 | modal: true, | |
|
50 | IPython.dialog.modal({ | |
|
51 | title: "Edit Cell Metadata", | |
|
52 | body: dialogform, | |
|
55 | 53 | buttons: { |
|
56 |
"O |
|
|
54 | "OK": { class : "btn-primary", | |
|
55 | click: function() { | |
|
57 | 56 | //validate json and set it |
|
58 | 57 | try { |
|
59 | 58 | var json = JSON.parse(editor.getValue()); |
|
60 | 59 | cell.metadata = json; |
|
61 | $( this ).dialog( "close" ); | |
|
62 | } | |
|
63 | catch(e) | |
|
64 | { | |
|
60 | } catch(e) { | |
|
65 | 61 | error_div.text('Warning, invalid json, not saved'); |
|
62 | return false; | |
|
66 | 63 | } |
|
67 | }, | |
|
68 |
Cancel: |
|
|
69 | $( this ).dialog( "close" ); | |
|
70 | } | |
|
71 | }, | |
|
72 | close: function() { | |
|
73 | //cleanup on close | |
|
74 | $(this).remove(); | |
|
64 | }}, | |
|
65 | Cancel: {} | |
|
75 | 66 | } |
|
76 | 67 | }); |
|
77 | 68 | editor.refresh(); |
|
78 | 69 | } |
|
79 | 70 | |
|
80 | 71 | var add_raw_edit_button = function(div, cell) { |
|
81 | var button_container = div | |
|
82 |
var button = $('< |
|
|
83 | .click(function(){raw_edit(cell); return false;}) | |
|
72 | var button_container = div; | |
|
73 | var button = $('<button/>') | |
|
74 | .addClass("btn btn-mini") | |
|
75 | .text("Raw Edit") | |
|
76 | .click( function () { | |
|
77 | raw_edit(cell); | |
|
78 | return false; | |
|
79 | }); | |
|
84 | 80 | button_container.append(button); |
|
85 | 81 | } |
|
86 | 82 | |
|
87 | 83 | CellToolbar.register_callback('default.rawedit',add_raw_edit_button); |
|
88 | 84 | var example_preset = [] |
|
89 | 85 | example_preset.push('default.rawedit'); |
|
90 | 86 | |
|
91 | 87 | CellToolbar.register_preset('Default',example_preset); |
|
92 | 88 | console.log('Default extension for metadata editing loaded.'); |
|
93 | 89 | |
|
94 | 90 | }(IPython)); |
@@ -1,439 +1,440 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 | // CodeCell |
|
10 | 10 | //============================================================================ |
|
11 | 11 | /** |
|
12 | 12 | * An extendable module that provide base functionnality to create cell for notebook. |
|
13 | 13 | * @module IPython |
|
14 | 14 | * @namespace IPython |
|
15 | 15 | * @submodule CodeCell |
|
16 | 16 | */ |
|
17 | 17 | |
|
18 | 18 | |
|
19 | 19 | /* local util for codemirror */ |
|
20 | 20 | var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;} |
|
21 | 21 | |
|
22 | 22 | /** |
|
23 | 23 | * |
|
24 | 24 | * function to delete until previous non blanking space character |
|
25 | 25 | * or first multiple of 4 tabstop. |
|
26 | 26 | * @private |
|
27 | 27 | */ |
|
28 | 28 | CodeMirror.commands.delSpaceToPrevTabStop = function(cm){ |
|
29 | 29 | var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to); |
|
30 | 30 | if (!posEq(from, to)) {cm.replaceRange("", from, to); return} |
|
31 | 31 | var cur = cm.getCursor(), line = cm.getLine(cur.line); |
|
32 | 32 | var tabsize = cm.getOption('tabSize'); |
|
33 | 33 | var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize; |
|
34 | 34 | var from = {ch:cur.ch-chToPrevTabStop,line:cur.line} |
|
35 | 35 | var select = cm.getRange(from,cur) |
|
36 | 36 | if( select.match(/^\ +$/) != null){ |
|
37 | 37 | cm.replaceRange("",from,cur) |
|
38 | 38 | } else { |
|
39 | 39 | cm.deleteH(-1,"char") |
|
40 | 40 | } |
|
41 | 41 | }; |
|
42 | 42 | |
|
43 | 43 | |
|
44 | 44 | var IPython = (function (IPython) { |
|
45 | 45 | "use strict"; |
|
46 | 46 | |
|
47 | 47 | var utils = IPython.utils; |
|
48 | 48 | var key = IPython.utils.keycodes; |
|
49 | 49 | CodeMirror.modeURL = "/static/components/codemirror/mode/%N/%N.js"; |
|
50 | 50 | |
|
51 | 51 | /** |
|
52 | 52 | * A Cell conceived to write code. |
|
53 | 53 | * |
|
54 | 54 | * The kernel doesn't have to be set at creation time, in that case |
|
55 | 55 | * it will be null and set_kernel has to be called later. |
|
56 | 56 | * @class CodeCell |
|
57 | 57 | * @extends IPython.Cell |
|
58 | 58 | * |
|
59 | 59 | * @constructor |
|
60 | 60 | * @param {Object|null} kernel |
|
61 | 61 | * @param {object|undefined} [options] |
|
62 | 62 | * @param [options.cm_config] {object} config to pass to CodeMirror |
|
63 | 63 | */ |
|
64 | 64 | var CodeCell = function (kernel, options) { |
|
65 | 65 | this.kernel = kernel || null; |
|
66 | 66 | this.code_mirror = null; |
|
67 | 67 | this.input_prompt_number = null; |
|
68 | 68 | this.collapsed = false; |
|
69 | 69 | this.default_mode = 'ipython'; |
|
70 | this.cell_type = "code"; | |
|
70 | 71 | |
|
71 | 72 | |
|
72 | 73 | var cm_overwrite_options = { |
|
73 | 74 | onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this) |
|
74 | 75 | }; |
|
75 | 76 | |
|
76 | 77 | options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options}); |
|
77 | 78 | |
|
78 | 79 | IPython.Cell.apply(this,[options]); |
|
79 | 80 | |
|
80 | 81 | var that = this; |
|
81 | 82 | this.element.focusout( |
|
82 | 83 | function() { that.auto_highlight(); } |
|
83 | 84 | ); |
|
84 | 85 | }; |
|
85 | 86 | |
|
86 | 87 | CodeCell.options_default = { |
|
87 | 88 | cm_config : { |
|
88 | 89 | extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess",'Backspace':"delSpaceToPrevTabStop"}, |
|
89 | 90 | mode: 'ipython', |
|
90 | 91 | theme: 'ipython', |
|
91 | 92 | matchBrackets: true |
|
92 | 93 | } |
|
93 | 94 | }; |
|
94 | 95 | |
|
95 | 96 | |
|
96 | 97 | CodeCell.prototype = new IPython.Cell(); |
|
97 | 98 | |
|
98 | 99 | /** |
|
99 | 100 | * @method auto_highlight |
|
100 | 101 | */ |
|
101 | 102 | CodeCell.prototype.auto_highlight = function () { |
|
102 | 103 | this._auto_highlight(IPython.config.cell_magic_highlight) |
|
103 | 104 | }; |
|
104 | 105 | |
|
105 | 106 | /** @method create_element */ |
|
106 | 107 | CodeCell.prototype.create_element = function () { |
|
107 | 108 | IPython.Cell.prototype.create_element.apply(this, arguments); |
|
108 | 109 | |
|
109 | 110 | var cell = $('<div></div>').addClass('cell border-box-sizing code_cell'); |
|
110 | 111 | cell.attr('tabindex','2'); |
|
111 | 112 | |
|
112 | 113 | this.celltoolbar = new IPython.CellToolbar(this); |
|
113 | 114 | |
|
114 | 115 | var input = $('<div></div>').addClass('input'); |
|
115 | 116 | var vbox = $('<div/>').addClass('vbox box-flex1') |
|
116 | 117 | input.append($('<div/>').addClass('prompt input_prompt')); |
|
117 | 118 | vbox.append(this.celltoolbar.element); |
|
118 | 119 | var input_area = $('<div/>').addClass('input_area'); |
|
119 | 120 | this.code_mirror = CodeMirror(input_area.get(0), this.cm_config); |
|
120 | 121 | $(this.code_mirror.getInputField()).attr("spellcheck", "false"); |
|
121 | 122 | vbox.append(input_area); |
|
122 | 123 | input.append(vbox); |
|
123 | 124 | var output = $('<div></div>'); |
|
124 | 125 | cell.append(input).append(output); |
|
125 | 126 | this.element = cell; |
|
126 | 127 | this.output_area = new IPython.OutputArea(output, true); |
|
127 | 128 | |
|
128 | 129 | // construct a completer only if class exist |
|
129 | 130 | // otherwise no print view |
|
130 | 131 | if (IPython.Completer !== undefined) |
|
131 | 132 | { |
|
132 | 133 | this.completer = new IPython.Completer(this); |
|
133 | 134 | } |
|
134 | 135 | }; |
|
135 | 136 | |
|
136 | 137 | /** |
|
137 | 138 | * This method gets called in CodeMirror's onKeyDown/onKeyPress |
|
138 | 139 | * handlers and is used to provide custom key handling. Its return |
|
139 | 140 | * value is used to determine if CodeMirror should ignore the event: |
|
140 | 141 | * true = ignore, false = don't ignore. |
|
141 | 142 | * @method handle_codemirror_keyevent |
|
142 | 143 | */ |
|
143 | 144 | CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) { |
|
144 | 145 | |
|
145 | 146 | if (this.read_only){ |
|
146 | 147 | return false; |
|
147 | 148 | } |
|
148 | 149 | |
|
149 | 150 | var that = this; |
|
150 | 151 | // whatever key is pressed, first, cancel the tooltip request before |
|
151 | 152 | // they are sent, and remove tooltip if any, except for tab again |
|
152 | 153 | if (event.type === 'keydown' && event.which != key.TAB ) { |
|
153 | 154 | IPython.tooltip.remove_and_cancel_tooltip(); |
|
154 | 155 | }; |
|
155 | 156 | |
|
156 | 157 | var cur = editor.getCursor(); |
|
157 | 158 | if (event.keyCode === key.ENTER){ |
|
158 | 159 | this.auto_highlight(); |
|
159 | 160 | } |
|
160 | 161 | |
|
161 | 162 | if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) { |
|
162 | 163 | // Always ignore shift-enter in CodeMirror as we handle it. |
|
163 | 164 | return true; |
|
164 | 165 | } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) { |
|
165 | 166 | // triger on keypress (!) otherwise inconsistent event.which depending on plateform |
|
166 | 167 | // browser and keyboard layout ! |
|
167 | 168 | // Pressing '(' , request tooltip, don't forget to reappend it |
|
168 | 169 | IPython.tooltip.pending(that); |
|
169 | 170 | } else if (event.which === key.UPARROW && event.type === 'keydown') { |
|
170 | 171 | // If we are not at the top, let CM handle the up arrow and |
|
171 | 172 | // prevent the global keydown handler from handling it. |
|
172 | 173 | if (!that.at_top()) { |
|
173 | 174 | event.stop(); |
|
174 | 175 | return false; |
|
175 | 176 | } else { |
|
176 | 177 | return true; |
|
177 | 178 | }; |
|
178 | 179 | } else if (event.which === key.ESC) { |
|
179 | 180 | IPython.tooltip.remove_and_cancel_tooltip(true); |
|
180 | 181 | return true; |
|
181 | 182 | } else if (event.which === key.DOWNARROW && event.type === 'keydown') { |
|
182 | 183 | // If we are not at the bottom, let CM handle the down arrow and |
|
183 | 184 | // prevent the global keydown handler from handling it. |
|
184 | 185 | if (!that.at_bottom()) { |
|
185 | 186 | event.stop(); |
|
186 | 187 | return false; |
|
187 | 188 | } else { |
|
188 | 189 | return true; |
|
189 | 190 | }; |
|
190 | 191 | } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) { |
|
191 | 192 | if (editor.somethingSelected()){ |
|
192 | 193 | var anchor = editor.getCursor("anchor"); |
|
193 | 194 | var head = editor.getCursor("head"); |
|
194 | 195 | if( anchor.line != head.line){ |
|
195 | 196 | return false; |
|
196 | 197 | } |
|
197 | 198 | } |
|
198 | 199 | IPython.tooltip.request(that); |
|
199 | 200 | event.stop(); |
|
200 | 201 | return true; |
|
201 | 202 | } else if (event.keyCode === key.TAB && event.type == 'keydown') { |
|
202 | 203 | // Tab completion. |
|
203 | 204 | //Do not trim here because of tooltip |
|
204 | 205 | if (editor.somethingSelected()){return false} |
|
205 | 206 | var pre_cursor = editor.getRange({line:cur.line,ch:0},cur); |
|
206 | 207 | if (pre_cursor.trim() === "") { |
|
207 | 208 | // Don't autocomplete if the part of the line before the cursor |
|
208 | 209 | // is empty. In this case, let CodeMirror handle indentation. |
|
209 | 210 | return false; |
|
210 | 211 | } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && IPython.config.tooltip_on_tab ) { |
|
211 | 212 | IPython.tooltip.request(that); |
|
212 | 213 | // Prevent the event from bubbling up. |
|
213 | 214 | event.stop(); |
|
214 | 215 | // Prevent CodeMirror from handling the tab. |
|
215 | 216 | return true; |
|
216 | 217 | } else { |
|
217 | 218 | event.stop(); |
|
218 | 219 | this.completer.startCompletion(); |
|
219 | 220 | return true; |
|
220 | 221 | }; |
|
221 | 222 | } else { |
|
222 | 223 | // keypress/keyup also trigger on TAB press, and we don't want to |
|
223 | 224 | // use those to disable tab completion. |
|
224 | 225 | return false; |
|
225 | 226 | }; |
|
226 | 227 | return false; |
|
227 | 228 | }; |
|
228 | 229 | |
|
229 | 230 | |
|
230 | 231 | // Kernel related calls. |
|
231 | 232 | |
|
232 | 233 | CodeCell.prototype.set_kernel = function (kernel) { |
|
233 | 234 | this.kernel = kernel; |
|
234 | 235 | } |
|
235 | 236 | |
|
236 | 237 | /** |
|
237 | 238 | * Execute current code cell to the kernel |
|
238 | 239 | * @method execute |
|
239 | 240 | */ |
|
240 | 241 | CodeCell.prototype.execute = function () { |
|
241 | 242 | this.output_area.clear_output(true, true, true); |
|
242 | 243 | this.set_input_prompt('*'); |
|
243 | 244 | this.element.addClass("running"); |
|
244 | 245 | var callbacks = { |
|
245 | 246 | 'execute_reply': $.proxy(this._handle_execute_reply, this), |
|
246 | 247 | 'output': $.proxy(this.output_area.handle_output, this.output_area), |
|
247 | 248 | 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area), |
|
248 | 249 | 'set_next_input': $.proxy(this._handle_set_next_input, this), |
|
249 | 250 | 'input_request': $.proxy(this._handle_input_request, this) |
|
250 | 251 | }; |
|
251 | 252 | var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false}); |
|
252 | 253 | }; |
|
253 | 254 | |
|
254 | 255 | /** |
|
255 | 256 | * @method _handle_execute_reply |
|
256 | 257 | * @private |
|
257 | 258 | */ |
|
258 | 259 | CodeCell.prototype._handle_execute_reply = function (content) { |
|
259 | 260 | this.set_input_prompt(content.execution_count); |
|
260 | 261 | this.element.removeClass("running"); |
|
261 | 262 | $([IPython.events]).trigger('set_dirty.Notebook', {value: true}); |
|
262 | 263 | } |
|
263 | 264 | |
|
264 | 265 | /** |
|
265 | 266 | * @method _handle_set_next_input |
|
266 | 267 | * @private |
|
267 | 268 | */ |
|
268 | 269 | CodeCell.prototype._handle_set_next_input = function (text) { |
|
269 | 270 | var data = {'cell': this, 'text': text} |
|
270 | 271 | $([IPython.events]).trigger('set_next_input.Notebook', data); |
|
271 | 272 | } |
|
272 | 273 | |
|
273 | 274 | /** |
|
274 | 275 | * @method _handle_input_request |
|
275 | 276 | * @private |
|
276 | 277 | */ |
|
277 | 278 | CodeCell.prototype._handle_input_request = function (content) { |
|
278 | 279 | this.output_area.append_raw_input(content); |
|
279 | 280 | } |
|
280 | 281 | |
|
281 | 282 | |
|
282 | 283 | // Basic cell manipulation. |
|
283 | 284 | |
|
284 | 285 | CodeCell.prototype.select = function () { |
|
285 | 286 | IPython.Cell.prototype.select.apply(this); |
|
286 | 287 | this.code_mirror.refresh(); |
|
287 | 288 | this.code_mirror.focus(); |
|
288 | 289 | this.auto_highlight(); |
|
289 | 290 | // We used to need an additional refresh() after the focus, but |
|
290 | 291 | // it appears that this has been fixed in CM. This bug would show |
|
291 | 292 | // up on FF when a newly loaded markdown cell was edited. |
|
292 | 293 | }; |
|
293 | 294 | |
|
294 | 295 | |
|
295 | 296 | CodeCell.prototype.select_all = function () { |
|
296 | 297 | var start = {line: 0, ch: 0}; |
|
297 | 298 | var nlines = this.code_mirror.lineCount(); |
|
298 | 299 | var last_line = this.code_mirror.getLine(nlines-1); |
|
299 | 300 | var end = {line: nlines-1, ch: last_line.length}; |
|
300 | 301 | this.code_mirror.setSelection(start, end); |
|
301 | 302 | }; |
|
302 | 303 | |
|
303 | 304 | |
|
304 | 305 | CodeCell.prototype.collapse = function () { |
|
305 | 306 | this.collapsed = true; |
|
306 | 307 | this.output_area.collapse(); |
|
307 | 308 | }; |
|
308 | 309 | |
|
309 | 310 | |
|
310 | 311 | CodeCell.prototype.expand = function () { |
|
311 | 312 | this.collapsed = false; |
|
312 | 313 | this.output_area.expand(); |
|
313 | 314 | }; |
|
314 | 315 | |
|
315 | 316 | |
|
316 | 317 | CodeCell.prototype.toggle_output = function () { |
|
317 | 318 | this.collapsed = Boolean(1 - this.collapsed); |
|
318 | 319 | this.output_area.toggle_output(); |
|
319 | 320 | }; |
|
320 | 321 | |
|
321 | 322 | |
|
322 | 323 | CodeCell.prototype.toggle_output_scroll = function () { |
|
323 | 324 | this.output_area.toggle_scroll(); |
|
324 | 325 | }; |
|
325 | 326 | |
|
326 | 327 | |
|
327 | 328 | CodeCell.input_prompt_classical = function (prompt_value, lines_number) { |
|
328 | 329 | var ns = prompt_value || " "; |
|
329 | 330 | return 'In [' + ns + ']:' |
|
330 | 331 | }; |
|
331 | 332 | |
|
332 | 333 | CodeCell.input_prompt_continuation = function (prompt_value, lines_number) { |
|
333 | 334 | var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)]; |
|
334 | 335 | for(var i=1; i < lines_number; i++){html.push(['...:'])}; |
|
335 | 336 | return html.join('</br>') |
|
336 | 337 | }; |
|
337 | 338 | |
|
338 | 339 | CodeCell.input_prompt_function = CodeCell.input_prompt_classical; |
|
339 | 340 | |
|
340 | 341 | |
|
341 | 342 | CodeCell.prototype.set_input_prompt = function (number) { |
|
342 | 343 | var nline = 1 |
|
343 | 344 | if( this.code_mirror != undefined) { |
|
344 | 345 | nline = this.code_mirror.lineCount(); |
|
345 | 346 | } |
|
346 | 347 | this.input_prompt_number = number; |
|
347 | 348 | var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline); |
|
348 | 349 | this.element.find('div.input_prompt').html(prompt_html); |
|
349 | 350 | }; |
|
350 | 351 | |
|
351 | 352 | |
|
352 | 353 | CodeCell.prototype.clear_input = function () { |
|
353 | 354 | this.code_mirror.setValue(''); |
|
354 | 355 | }; |
|
355 | 356 | |
|
356 | 357 | |
|
357 | 358 | CodeCell.prototype.get_text = function () { |
|
358 | 359 | return this.code_mirror.getValue(); |
|
359 | 360 | }; |
|
360 | 361 | |
|
361 | 362 | |
|
362 | 363 | CodeCell.prototype.set_text = function (code) { |
|
363 | 364 | return this.code_mirror.setValue(code); |
|
364 | 365 | }; |
|
365 | 366 | |
|
366 | 367 | |
|
367 | 368 | CodeCell.prototype.at_top = function () { |
|
368 | 369 | var cursor = this.code_mirror.getCursor(); |
|
369 | 370 | if (cursor.line === 0 && cursor.ch === 0) { |
|
370 | 371 | return true; |
|
371 | 372 | } else { |
|
372 | 373 | return false; |
|
373 | 374 | } |
|
374 | 375 | }; |
|
375 | 376 | |
|
376 | 377 | |
|
377 | 378 | CodeCell.prototype.at_bottom = function () { |
|
378 | 379 | var cursor = this.code_mirror.getCursor(); |
|
379 | 380 | if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) { |
|
380 | 381 | return true; |
|
381 | 382 | } else { |
|
382 | 383 | return false; |
|
383 | 384 | } |
|
384 | 385 | }; |
|
385 | 386 | |
|
386 | 387 | |
|
387 | 388 | CodeCell.prototype.clear_output = function (stdout, stderr, other) { |
|
388 | 389 | this.output_area.clear_output(stdout, stderr, other); |
|
389 | 390 | }; |
|
390 | 391 | |
|
391 | 392 | |
|
392 | 393 | // JSON serialization |
|
393 | 394 | |
|
394 | 395 | CodeCell.prototype.fromJSON = function (data) { |
|
395 | 396 | IPython.Cell.prototype.fromJSON.apply(this, arguments); |
|
396 | 397 | if (data.cell_type === 'code') { |
|
397 | 398 | if (data.input !== undefined) { |
|
398 | 399 | this.set_text(data.input); |
|
399 | 400 | // make this value the starting point, so that we can only undo |
|
400 | 401 | // to this state, instead of a blank cell |
|
401 | 402 | this.code_mirror.clearHistory(); |
|
402 | 403 | this.auto_highlight(); |
|
403 | 404 | } |
|
404 | 405 | if (data.prompt_number !== undefined) { |
|
405 | 406 | this.set_input_prompt(data.prompt_number); |
|
406 | 407 | } else { |
|
407 | 408 | this.set_input_prompt(); |
|
408 | 409 | }; |
|
409 | 410 | this.output_area.fromJSON(data.outputs); |
|
410 | 411 | if (data.collapsed !== undefined) { |
|
411 | 412 | if (data.collapsed) { |
|
412 | 413 | this.collapse(); |
|
413 | 414 | } else { |
|
414 | 415 | this.expand(); |
|
415 | 416 | }; |
|
416 | 417 | }; |
|
417 | 418 | }; |
|
418 | 419 | }; |
|
419 | 420 | |
|
420 | 421 | |
|
421 | 422 | CodeCell.prototype.toJSON = function () { |
|
422 | 423 | var data = IPython.Cell.prototype.toJSON.apply(this); |
|
423 | 424 | data.input = this.get_text(); |
|
424 | 425 | data.cell_type = 'code'; |
|
425 | 426 | if (this.input_prompt_number) { |
|
426 | 427 | data.prompt_number = this.input_prompt_number; |
|
427 | 428 | }; |
|
428 | 429 | var outputs = this.output_area.toJSON(); |
|
429 | 430 | data.outputs = outputs; |
|
430 | 431 | data.language = 'python'; |
|
431 | 432 | data.collapsed = this.collapsed; |
|
432 | 433 | return data; |
|
433 | 434 | }; |
|
434 | 435 | |
|
435 | 436 | |
|
436 | 437 | IPython.CodeCell = CodeCell; |
|
437 | 438 | |
|
438 | 439 | return IPython; |
|
439 | 440 | }(IPython)); |
@@ -1,62 +1,62 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 | // Layout |
|
10 | 10 | //============================================================================ |
|
11 | 11 | |
|
12 | 12 | var IPython = (function (IPython) { |
|
13 | 13 | |
|
14 | 14 | var LayoutManager = function () { |
|
15 | 15 | this.bind_events(); |
|
16 | 16 | }; |
|
17 | 17 | |
|
18 | 18 | |
|
19 | 19 | LayoutManager.prototype.bind_events = function () { |
|
20 | 20 | $(window).resize($.proxy(this.do_resize,this)); |
|
21 | 21 | }; |
|
22 | 22 | |
|
23 | 23 | LayoutManager.prototype.app_height = function() { |
|
24 | 24 | var win = $(window); |
|
25 | 25 | var w = win.width(); |
|
26 | 26 | var h = win.height(); |
|
27 | 27 | var header_height; |
|
28 | 28 | if ($('div#header').css('display') === 'none') { |
|
29 | 29 | header_height = 0; |
|
30 | 30 | } else { |
|
31 | 31 | header_height = $('div#header').outerHeight(true); |
|
32 | 32 | } |
|
33 | 33 | var menubar_height = $('div#menubar').outerHeight(true); |
|
34 | 34 | var toolbar_height; |
|
35 | 35 | if ($('div#maintoolbar').css('display') === 'none') { |
|
36 | 36 | toolbar_height = 0; |
|
37 | 37 | } else { |
|
38 | 38 | toolbar_height = $('div#maintoolbar').outerHeight(true); |
|
39 | 39 | } |
|
40 | 40 | return h-header_height-menubar_height-toolbar_height; // content height |
|
41 | 41 | } |
|
42 | 42 | |
|
43 | 43 | LayoutManager.prototype.do_resize = function () { |
|
44 | 44 | var app_height = this.app_height() // content height |
|
45 | 45 | |
|
46 | 46 | $('#ipython-main-app').height(app_height); // content+padding+border height |
|
47 | 47 | |
|
48 | 48 | var pager_height = IPython.pager.percentage_height*app_height; |
|
49 | 49 | var pager_splitter_height = $('div#pager_splitter').outerHeight(true); |
|
50 |
$('div#pager'). |
|
|
50 | $('div#pager').outerHeight(pager_height); | |
|
51 | 51 | if (IPython.pager.expanded) { |
|
52 |
$('div#notebook'). |
|
|
52 | $('div#notebook').outerHeight(app_height-pager_height-pager_splitter_height); | |
|
53 | 53 | } else { |
|
54 |
$('div#notebook'). |
|
|
54 | $('div#notebook').outerHeight(app_height-pager_splitter_height); | |
|
55 | 55 | } |
|
56 | 56 | }; |
|
57 | 57 | |
|
58 | 58 | IPython.LayoutManager = LayoutManager; |
|
59 | 59 | |
|
60 | 60 | return IPython; |
|
61 | 61 | |
|
62 | 62 | }(IPython)); |
@@ -1,125 +1,123 b'' | |||
|
1 | 1 | //---------------------------------------------------------------------------- |
|
2 | 2 | // Copyright (C) 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 | // On document ready |
|
10 | 10 | //============================================================================ |
|
11 | 11 | "use strict"; |
|
12 | 12 | |
|
13 | 13 | // for the time beeing, we have to pass marked as a parameter here, |
|
14 | 14 | // as injecting require.js make marked not to put itself in the globals, |
|
15 | 15 | // which make both this file fail at setting marked configuration, and textcell.js |
|
16 | 16 | // which search marked into global. |
|
17 | 17 | require(['components/marked/lib/marked'], |
|
18 | 18 | |
|
19 | 19 | function (marked) { |
|
20 | 20 | |
|
21 | 21 | window.marked = marked |
|
22 | 22 | |
|
23 | 23 | // monkey patch CM to be able to syntax highlight cell magics |
|
24 | 24 | // bug reported upstream, |
|
25 | 25 | // see https://github.com/marijnh/CodeMirror2/issues/670 |
|
26 | 26 | if(CodeMirror.getMode(1,'text/plain').indent == undefined ){ |
|
27 | 27 | console.log('patching CM for undefined indent'); |
|
28 | 28 | CodeMirror.modes.null = function() { |
|
29 | 29 | return {token: function(stream) {stream.skipToEnd();},indent : function(){return 0}} |
|
30 | 30 | } |
|
31 | 31 | } |
|
32 | 32 | |
|
33 | 33 | CodeMirror.patchedGetMode = function(config, mode){ |
|
34 | 34 | var cmmode = CodeMirror.getMode(config, mode); |
|
35 | 35 | if(cmmode.indent == null) |
|
36 | 36 | { |
|
37 | 37 | console.log('patch mode "' , mode, '" on the fly'); |
|
38 | 38 | cmmode.indent = function(){return 0}; |
|
39 | 39 | } |
|
40 | 40 | return cmmode; |
|
41 | 41 | } |
|
42 | 42 | // end monkey patching CodeMirror |
|
43 | 43 | |
|
44 | 44 | IPython.mathjaxutils.init(); |
|
45 | 45 | |
|
46 | 46 | IPython.read_only = $('body').data('readOnly') === 'True'; |
|
47 | 47 | $('#ipython-main-app').addClass('border-box-sizing'); |
|
48 | 48 | $('div#notebook_panel').addClass('border-box-sizing'); |
|
49 | // The header's bottom border is provided by the menu bar so we remove it. | |
|
50 | $('div#header').css('border-bottom-style','none'); | |
|
51 | 49 | |
|
52 | 50 | var baseProjectUrl = $('body').data('baseProjectUrl') |
|
53 | 51 | |
|
54 | 52 | IPython.page = new IPython.Page(); |
|
55 | 53 | IPython.layout_manager = new IPython.LayoutManager(); |
|
56 | 54 | IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter'); |
|
57 | 55 | IPython.quick_help = new IPython.QuickHelp(); |
|
58 | 56 | IPython.login_widget = new IPython.LoginWidget('span#login_widget',{baseProjectUrl:baseProjectUrl}); |
|
59 | 57 | IPython.notebook = new IPython.Notebook('div#notebook',{baseProjectUrl:baseProjectUrl, read_only:IPython.read_only}); |
|
60 | 58 | IPython.save_widget = new IPython.SaveWidget('span#save_widget'); |
|
61 | 59 | IPython.menubar = new IPython.MenuBar('#menubar',{baseProjectUrl:baseProjectUrl}) |
|
62 | IPython.toolbar = new IPython.MainToolBar('#maintoolbar') | |
|
60 | IPython.toolbar = new IPython.MainToolBar('#maintoolbar-container') | |
|
63 | 61 | IPython.tooltip = new IPython.Tooltip() |
|
64 | 62 | IPython.notification_area = new IPython.NotificationArea('#notification_area') |
|
65 | 63 | IPython.notification_area.init_notification_widgets(); |
|
66 | 64 | |
|
67 | 65 | IPython.layout_manager.do_resize(); |
|
68 | 66 | |
|
69 | 67 | $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+ |
|
70 | 68 | '<span id="test2" style="font-weight: bold;">x</span>'+ |
|
71 | 69 | '<span id="test3" style="font-style: italic;">x</span></pre></div>') |
|
72 | 70 | var nh = $('#test1').innerHeight(); |
|
73 | 71 | var bh = $('#test2').innerHeight(); |
|
74 | 72 | var ih = $('#test3').innerHeight(); |
|
75 | 73 | if(nh != bh || nh != ih) { |
|
76 | 74 | $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>'); |
|
77 | 75 | } |
|
78 | 76 | $('#fonttest').remove(); |
|
79 | 77 | |
|
80 | 78 | if(IPython.read_only){ |
|
81 | 79 | // hide various elements from read-only view |
|
82 | 80 | $('div#pager').remove(); |
|
83 | 81 | $('div#pager_splitter').remove(); |
|
84 | 82 | |
|
85 | 83 | // set the notebook name field as not modifiable |
|
86 | 84 | $('#notebook_name').attr('disabled','disabled') |
|
87 | 85 | } |
|
88 | 86 | |
|
89 | 87 | IPython.page.show(); |
|
90 | 88 | |
|
91 | 89 | IPython.layout_manager.do_resize(); |
|
92 | 90 | var first_load = function () { |
|
93 | 91 | IPython.layout_manager.do_resize(); |
|
94 | 92 | var hash = document.location.hash; |
|
95 | 93 | if (hash) { |
|
96 | 94 | document.location.hash = ''; |
|
97 | 95 | document.location.hash = hash; |
|
98 | 96 | } |
|
99 | 97 | IPython.notebook.set_autosave_interval(IPython.notebook.minimum_autosave_interval); |
|
100 | 98 | // only do this once |
|
101 | 99 | $([IPython.events]).off('notebook_loaded.Notebook', first_load); |
|
102 | 100 | }; |
|
103 | 101 | |
|
104 | 102 | $([IPython.events]).on('notebook_loaded.Notebook', first_load); |
|
105 | 103 | IPython.notebook.load_notebook($('body').data('notebookId')); |
|
106 | 104 | |
|
107 | 105 | if (marked) { |
|
108 | 106 | marked.setOptions({ |
|
109 | 107 | gfm : true, |
|
110 | 108 | tables: true, |
|
111 | 109 | langPrefix: "language-", |
|
112 | 110 | highlight: function(code, lang) { |
|
113 | 111 | var highlighted; |
|
114 | 112 | try { |
|
115 | 113 | highlighted = hljs.highlight(lang, code, false); |
|
116 | 114 | } catch(err) { |
|
117 | 115 | highlighted = hljs.highlightAuto(code); |
|
118 | 116 | } |
|
119 | 117 | return highlighted.value; |
|
120 | 118 | } |
|
121 | 119 | }) |
|
122 | 120 | } |
|
123 | 121 | } |
|
124 | 122 | |
|
125 | 123 | ); |
@@ -1,206 +1,206 b'' | |||
|
1 | 1 | //---------------------------------------------------------------------------- |
|
2 | 2 | // Copyright (C) 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 | // ToolBar |
|
10 | 10 | //============================================================================ |
|
11 | 11 | |
|
12 | 12 | var IPython = (function (IPython) { |
|
13 | 13 | |
|
14 | 14 | var MainToolBar = function (selector) { |
|
15 | 15 | IPython.ToolBar.apply(this, arguments); |
|
16 | 16 | this.construct(); |
|
17 | 17 | this.add_celltype_list(); |
|
18 | 18 | this.add_celltoolbar_list(); |
|
19 | 19 | this.bind_events(); |
|
20 | 20 | }; |
|
21 | 21 | |
|
22 | 22 | MainToolBar.prototype = new IPython.ToolBar(); |
|
23 | 23 | |
|
24 | 24 | MainToolBar.prototype.construct = function () { |
|
25 | 25 | this.add_buttons_group([ |
|
26 | 26 | { |
|
27 | 27 | id : 'save_b', |
|
28 | 28 | label : 'Save and Checkpoint', |
|
29 |
icon : ' |
|
|
29 | icon : 'icon-hdd', | |
|
30 | 30 | callback : function () { |
|
31 | 31 | IPython.notebook.save_checkpoint(); |
|
32 | 32 | } |
|
33 | 33 | } |
|
34 | 34 | ]); |
|
35 | 35 | this.add_buttons_group([ |
|
36 | 36 | { |
|
37 | 37 | id : 'cut_b', |
|
38 | 38 | label : 'Cut Cell', |
|
39 |
icon : ' |
|
|
39 | icon : 'icon-cut', | |
|
40 | 40 | callback : function () { |
|
41 | 41 | IPython.notebook.cut_cell(); |
|
42 | 42 | } |
|
43 | 43 | }, |
|
44 | 44 | { |
|
45 | 45 | id : 'copy_b', |
|
46 | 46 | label : 'Copy Cell', |
|
47 |
icon : ' |
|
|
47 | icon : 'icon-copy', | |
|
48 | 48 | callback : function () { |
|
49 | 49 | IPython.notebook.copy_cell(); |
|
50 | 50 | } |
|
51 | 51 | }, |
|
52 | 52 | { |
|
53 | 53 | id : 'paste_b', |
|
54 | 54 | label : 'Paste Cell Below', |
|
55 |
icon : ' |
|
|
55 | icon : 'icon-paste', | |
|
56 | 56 | callback : function () { |
|
57 | 57 | IPython.notebook.paste_cell_below(); |
|
58 | 58 | } |
|
59 | 59 | } |
|
60 | 60 | ],'cut_copy_paste'); |
|
61 | 61 | |
|
62 | 62 | this.add_buttons_group([ |
|
63 | 63 | { |
|
64 | 64 | id : 'move_up_b', |
|
65 | 65 | label : 'Move Cell Up', |
|
66 |
icon : ' |
|
|
66 | icon : 'icon-arrow-up', | |
|
67 | 67 | callback : function () { |
|
68 | 68 | IPython.notebook.move_cell_up(); |
|
69 | 69 | } |
|
70 | 70 | }, |
|
71 | 71 | { |
|
72 | 72 | id : 'move_down_b', |
|
73 | 73 | label : 'Move Cell Down', |
|
74 |
icon : ' |
|
|
74 | icon : 'icon-arrow-down', | |
|
75 | 75 | callback : function () { |
|
76 | 76 | IPython.notebook.move_cell_down(); |
|
77 | 77 | } |
|
78 | 78 | } |
|
79 | 79 | ],'move_up_down'); |
|
80 | 80 | |
|
81 | 81 | this.add_buttons_group([ |
|
82 | 82 | { |
|
83 | 83 | id : 'insert_above_b', |
|
84 | 84 | label : 'Insert Cell Above', |
|
85 |
icon : ' |
|
|
85 | icon : 'icon-circle-arrow-up', | |
|
86 | 86 | callback : function () { |
|
87 | 87 | IPython.notebook.insert_cell_above('code'); |
|
88 | 88 | } |
|
89 | 89 | }, |
|
90 | 90 | { |
|
91 | 91 | id : 'insert_below_b', |
|
92 | 92 | label : 'Insert Cell Below', |
|
93 |
icon : ' |
|
|
93 | icon : 'icon-circle-arrow-down', | |
|
94 | 94 | callback : function () { |
|
95 | 95 | IPython.notebook.insert_cell_below('code'); |
|
96 | 96 | } |
|
97 | 97 | } |
|
98 | 98 | ],'insert_above_below'); |
|
99 | 99 | |
|
100 | 100 | this.add_buttons_group([ |
|
101 | 101 | { |
|
102 | 102 | id : 'run_b', |
|
103 | 103 | label : 'Run Cell', |
|
104 |
icon : ' |
|
|
104 | icon : 'icon-play', | |
|
105 | 105 | callback : function () { |
|
106 | 106 | IPython.notebook.execute_selected_cell(); |
|
107 | 107 | } |
|
108 | 108 | }, |
|
109 | 109 | { |
|
110 | 110 | id : 'interrupt_b', |
|
111 | 111 | label : 'Interrupt', |
|
112 |
icon : ' |
|
|
112 | icon : 'icon-stop', | |
|
113 | 113 | callback : function () { |
|
114 | 114 | IPython.notebook.kernel.interrupt(); |
|
115 | 115 | } |
|
116 | 116 | } |
|
117 | 117 | ],'run_int'); |
|
118 | 118 | }; |
|
119 | 119 | |
|
120 | 120 | MainToolBar.prototype.add_celltype_list = function () { |
|
121 | 121 | this.element |
|
122 | 122 | .append($('<select/>') |
|
123 | 123 | .attr('id','cell_type') |
|
124 | .addClass('ui-widget-content') | |
|
124 | // .addClass('ui-widget-content') | |
|
125 | 125 | .append($('<option/>').attr('value','code').text('Code')) |
|
126 | 126 | .append($('<option/>').attr('value','markdown').text('Markdown')) |
|
127 | 127 | .append($('<option/>').attr('value','raw').text('Raw Text')) |
|
128 | 128 | .append($('<option/>').attr('value','heading1').text('Heading 1')) |
|
129 | 129 | .append($('<option/>').attr('value','heading2').text('Heading 2')) |
|
130 | 130 | .append($('<option/>').attr('value','heading3').text('Heading 3')) |
|
131 | 131 | .append($('<option/>').attr('value','heading4').text('Heading 4')) |
|
132 | 132 | .append($('<option/>').attr('value','heading5').text('Heading 5')) |
|
133 | 133 | .append($('<option/>').attr('value','heading6').text('Heading 6')) |
|
134 | 134 | ); |
|
135 | 135 | }; |
|
136 | 136 | |
|
137 | 137 | |
|
138 | 138 | MainToolBar.prototype.add_celltoolbar_list = function () { |
|
139 |
var label = $('< |
|
|
139 | var label = $('<span/>').addClass("navbar-text").text('Cell Toolbar:'); | |
|
140 | 140 | var select = $('<select/>') |
|
141 | .addClass('ui-widget-content') | |
|
141 | // .addClass('ui-widget-content') | |
|
142 | 142 | .attr('id', 'ctb_select') |
|
143 | 143 | .append($('<option/>').attr('value', '').text('None')); |
|
144 | 144 | this.element.append(label).append(select); |
|
145 | 145 | select.change(function() { |
|
146 | 146 | var val = $(this).val() |
|
147 | 147 | if (val =='') { |
|
148 | 148 | IPython.CellToolbar.global_hide(); |
|
149 | 149 | } else { |
|
150 | 150 | IPython.CellToolbar.global_show(); |
|
151 | 151 | IPython.CellToolbar.activate_preset(val); |
|
152 | 152 | } |
|
153 | 153 | }); |
|
154 | 154 | // Setup the currently registered presets. |
|
155 | 155 | var presets = IPython.CellToolbar.list_presets(); |
|
156 | 156 | for (var i=0; i<presets.length; i++) { |
|
157 | 157 | var name = presets[i]; |
|
158 | 158 | select.append($('<option/>').attr('value', name).text(name)); |
|
159 | 159 | } |
|
160 | 160 | // Setup future preset registrations. |
|
161 | 161 | $([IPython.events]).on('preset_added.CellToolbar', function (event, data) { |
|
162 | 162 | var name = data.name; |
|
163 | 163 | select.append($('<option/>').attr('value', name).text(name)); |
|
164 | 164 | }); |
|
165 | 165 | }; |
|
166 | 166 | |
|
167 | 167 | |
|
168 | 168 | MainToolBar.prototype.bind_events = function () { |
|
169 | 169 | var that = this; |
|
170 | 170 | |
|
171 | 171 | this.element.find('#cell_type').change(function () { |
|
172 | 172 | var cell_type = $(this).val(); |
|
173 | 173 | if (cell_type === 'code') { |
|
174 | 174 | IPython.notebook.to_code(); |
|
175 | 175 | } else if (cell_type === 'markdown') { |
|
176 | 176 | IPython.notebook.to_markdown(); |
|
177 | 177 | } else if (cell_type === 'raw') { |
|
178 | 178 | IPython.notebook.to_raw(); |
|
179 | 179 | } else if (cell_type === 'heading1') { |
|
180 | 180 | IPython.notebook.to_heading(undefined, 1); |
|
181 | 181 | } else if (cell_type === 'heading2') { |
|
182 | 182 | IPython.notebook.to_heading(undefined, 2); |
|
183 | 183 | } else if (cell_type === 'heading3') { |
|
184 | 184 | IPython.notebook.to_heading(undefined, 3); |
|
185 | 185 | } else if (cell_type === 'heading4') { |
|
186 | 186 | IPython.notebook.to_heading(undefined, 4); |
|
187 | 187 | } else if (cell_type === 'heading5') { |
|
188 | 188 | IPython.notebook.to_heading(undefined, 5); |
|
189 | 189 | } else if (cell_type === 'heading6') { |
|
190 | 190 | IPython.notebook.to_heading(undefined, 6); |
|
191 | 191 | } |
|
192 | 192 | }); |
|
193 | 193 | $([IPython.events]).on('selected_cell_type_changed.Notebook', function (event, data) { |
|
194 | 194 | if (data.cell_type === 'heading') { |
|
195 | 195 | that.element.find('#cell_type').val(data.cell_type+data.level); |
|
196 | 196 | } else { |
|
197 | 197 | that.element.find('#cell_type').val(data.cell_type); |
|
198 | 198 | } |
|
199 | 199 | }); |
|
200 | 200 | }; |
|
201 | 201 | |
|
202 | 202 | IPython.MainToolBar = MainToolBar; |
|
203 | 203 | |
|
204 | 204 | return IPython; |
|
205 | 205 | |
|
206 | 206 | }(IPython)); |
@@ -1,242 +1,245 b'' | |||
|
1 | 1 | //---------------------------------------------------------------------------- |
|
2 | 2 | // Copyright (C) 2008-2012 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 | // MathJax utility functions |
|
10 | 10 | //============================================================================ |
|
11 | 11 | |
|
12 | 12 | IPython.namespace('IPython.mathjaxutils'); |
|
13 | 13 | |
|
14 | 14 | IPython.mathjaxutils = (function (IPython) { |
|
15 | 15 | |
|
16 | 16 | var init = function () { |
|
17 | 17 | if (window.MathJax) { |
|
18 | 18 | // MathJax loaded |
|
19 | 19 | MathJax.Hub.Config({ |
|
20 | 20 | tex2jax: { |
|
21 | 21 | inlineMath: [ ['$','$'], ["\\(","\\)"] ], |
|
22 | 22 | displayMath: [ ['$$','$$'], ["\\[","\\]"] ], |
|
23 | 23 | processEscapes: true, |
|
24 | 24 | processEnvironments: true |
|
25 | 25 | }, |
|
26 | 26 | displayAlign: 'left', // Change this to 'center' to center equations. |
|
27 | 27 | "HTML-CSS": { |
|
28 | 28 | styles: {'.MathJax_Display': {"margin": 0}} |
|
29 | 29 | } |
|
30 | 30 | }); |
|
31 | 31 | MathJax.Hub.Configured(); |
|
32 | 32 | } else if (window.mathjax_url != "") { |
|
33 | 33 | // Don't have MathJax, but should. Show dialog. |
|
34 |
var |
|
|
34 | var message = $('<div/>') | |
|
35 | 35 | .append( |
|
36 | $("<p></p>").addClass('dialog').html( | |
|
36 | $("<p/></p>").addClass('dialog').html( | |
|
37 | 37 | "Math/LaTeX rendering will be disabled." |
|
38 | 38 | ) |
|
39 | 39 | ).append( |
|
40 | 40 | $("<p></p>").addClass('dialog').html( |
|
41 | 41 | "If you have administrative access to the notebook server and" + |
|
42 | 42 | " a working internet connection, you can install a local copy" + |
|
43 | 43 | " of MathJax for offline use with the following command on the server" + |
|
44 | 44 | " at a Python or IPython prompt:" |
|
45 | 45 | ) |
|
46 | 46 | ).append( |
|
47 | 47 | $("<pre></pre>").addClass('dialog').html( |
|
48 | 48 | ">>> from IPython.external import mathjax; mathjax.install_mathjax()" |
|
49 | 49 | ) |
|
50 | 50 | ).append( |
|
51 | 51 | $("<p></p>").addClass('dialog').html( |
|
52 | 52 | "This will try to install MathJax into the IPython source directory." |
|
53 | 53 | ) |
|
54 | 54 | ).append( |
|
55 | 55 | $("<p></p>").addClass('dialog').html( |
|
56 | 56 | "If IPython is installed to a location that requires" + |
|
57 | 57 | " administrative privileges to write, you will need to make this call as" + |
|
58 | 58 | " an administrator, via 'sudo'." |
|
59 | 59 | ) |
|
60 | 60 | ).append( |
|
61 | 61 | $("<p></p>").addClass('dialog').html( |
|
62 | 62 | "When you start the notebook server, you can instruct it to disable MathJax support altogether:" |
|
63 | 63 | ) |
|
64 | 64 | ).append( |
|
65 | 65 | $("<pre></pre>").addClass('dialog').html( |
|
66 | 66 | "$ ipython notebook --no-mathjax" |
|
67 | 67 | ) |
|
68 | 68 | ).append( |
|
69 | 69 | $("<p></p>").addClass('dialog').html( |
|
70 | 70 | "which will prevent this dialog from appearing." |
|
71 | 71 | ) |
|
72 |
) |
|
|
73 | title: "Failed to retrieve MathJax from '" + window.mathjax_url + "'", | |
|
74 | width: "70%", | |
|
75 |
|
|
|
76 |
|
|
|
72 | ) | |
|
73 | IPython.dialog.modal({ | |
|
74 | title : "Failed to retrieve MathJax from '" + window.mathjax_url + "'", | |
|
75 | body : message, | |
|
76 | buttons : { | |
|
77 | OK : {class: "btn-danger"} | |
|
78 | } | |
|
79 | }); | |
|
77 | 80 | } else { |
|
78 | 81 | // No MathJax, but none expected. No dialog. |
|
79 | 82 | }; |
|
80 | 83 | }; |
|
81 | 84 | |
|
82 | 85 | // Some magic for deferring mathematical expressions to MathJax |
|
83 | 86 | // by hiding them from the Markdown parser. |
|
84 | 87 | // Some of the code here is adapted with permission from Davide Cervone |
|
85 | 88 | // under the terms of the Apache2 license governing the MathJax project. |
|
86 | 89 | // Other minor modifications are also due to StackExchange and are used with |
|
87 | 90 | // permission. |
|
88 | 91 | |
|
89 | 92 | var inline = "$"; // the inline math delimiter |
|
90 | 93 | var blocks, start, end, last, braces; // used in searching for math |
|
91 | 94 | var math; // stores math until pagedown (Markdown parser) is done |
|
92 | 95 | |
|
93 | 96 | // MATHSPLIT contains the pattern for math delimiters and special symbols |
|
94 | 97 | // needed for searching for math in the text input. |
|
95 | 98 | var MATHSPLIT = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@)/i; |
|
96 | 99 | |
|
97 | 100 | // The math is in blocks i through j, so |
|
98 | 101 | // collect it into one block and clear the others. |
|
99 | 102 | // Replace &, <, and > by named entities. |
|
100 | 103 | // For IE, put <br> at the ends of comments since IE removes \n. |
|
101 | 104 | // Clear the current math positions and store the index of the |
|
102 | 105 | // math, then push the math string onto the storage array. |
|
103 | 106 | // The preProcess function is called on all blocks if it has been passed in |
|
104 | 107 | var process_math = function (i, j, pre_process) { |
|
105 | 108 | var hub = MathJax.Hub; |
|
106 | 109 | var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&") // use HTML entity for & |
|
107 | 110 | .replace(/</g, "<") // use HTML entity for < |
|
108 | 111 | .replace(/>/g, ">") // use HTML entity for > |
|
109 | 112 | ; |
|
110 | 113 | if (hub.Browser.isMSIE) { |
|
111 | 114 | block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n") |
|
112 | 115 | } |
|
113 | 116 | while (j > i) { |
|
114 | 117 | blocks[j] = ""; |
|
115 | 118 | j--; |
|
116 | 119 | } |
|
117 | 120 | blocks[i] = "@@" + math.length + "@@"; // replace the current block text with a unique tag to find later |
|
118 | 121 | if (pre_process) |
|
119 | 122 | block = pre_process(block); |
|
120 | 123 | math.push(block); |
|
121 | 124 | start = end = last = null; |
|
122 | 125 | } |
|
123 | 126 | |
|
124 | 127 | // Break up the text into its component parts and search |
|
125 | 128 | // through them for math delimiters, braces, linebreaks, etc. |
|
126 | 129 | // Math delimiters must match and braces must balance. |
|
127 | 130 | // Don't allow math to pass through a double linebreak |
|
128 | 131 | // (which will be a paragraph). |
|
129 | 132 | // |
|
130 | 133 | var remove_math = function (text) { |
|
131 | 134 | if (!window.MathJax) { |
|
132 | 135 | return text; |
|
133 | 136 | } |
|
134 | 137 | |
|
135 | 138 | start = end = last = null; // for tracking math delimiters |
|
136 | 139 | math = []; // stores math strings for later |
|
137 | 140 | |
|
138 | 141 | // Except for extreme edge cases, this should catch precisely those pieces of the markdown |
|
139 | 142 | // source that will later be turned into code spans. While MathJax will not TeXify code spans, |
|
140 | 143 | // we still have to consider them at this point; the following issue has happened several times: |
|
141 | 144 | // |
|
142 | 145 | // `$foo` and `$bar` are varibales. --> <code>$foo ` and `$bar</code> are variables. |
|
143 | 146 | |
|
144 | 147 | var hasCodeSpans = /`/.test(text), |
|
145 | 148 | de_tilde; |
|
146 | 149 | if (hasCodeSpans) { |
|
147 | 150 | text = text.replace(/~/g, "~T").replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, function (wholematch) { |
|
148 | 151 | return wholematch.replace(/\$/g, "~D"); |
|
149 | 152 | }); |
|
150 | 153 | de_tilde = function (text) { return text.replace(/~([TD])/g, function (wholematch, character) { return { T: "~", D: "$" }[character]; }) }; |
|
151 | 154 | } else { |
|
152 | 155 | de_tilde = function (text) { return text; }; |
|
153 | 156 | } |
|
154 | 157 | |
|
155 | 158 | blocks = IPython.utils.regex_split(text.replace(/\r\n?/g, "\n"),MATHSPLIT); |
|
156 | 159 | |
|
157 | 160 | for (var i = 1, m = blocks.length; i < m; i += 2) { |
|
158 | 161 | var block = blocks[i]; |
|
159 | 162 | if (block.charAt(0) === "@") { |
|
160 | 163 | // |
|
161 | 164 | // Things that look like our math markers will get |
|
162 | 165 | // stored and then retrieved along with the math. |
|
163 | 166 | // |
|
164 | 167 | blocks[i] = "@@" + math.length + "@@"; |
|
165 | 168 | math.push(block); |
|
166 | 169 | } |
|
167 | 170 | else if (start) { |
|
168 | 171 | // |
|
169 | 172 | // If we are in math, look for the end delimiter, |
|
170 | 173 | // but don't go past double line breaks, and |
|
171 | 174 | // and balance braces within the math. |
|
172 | 175 | // |
|
173 | 176 | if (block === end) { |
|
174 | 177 | if (braces) { |
|
175 | 178 | last = i |
|
176 | 179 | } |
|
177 | 180 | else { |
|
178 | 181 | process_math(start, i, de_tilde) |
|
179 | 182 | } |
|
180 | 183 | } |
|
181 | 184 | else if (block.match(/\n.*\n/)) { |
|
182 | 185 | if (last) { |
|
183 | 186 | i = last; |
|
184 | 187 | process_math(start, i, de_tilde) |
|
185 | 188 | } |
|
186 | 189 | start = end = last = null; |
|
187 | 190 | braces = 0; |
|
188 | 191 | } |
|
189 | 192 | else if (block === "{") { |
|
190 | 193 | braces++ |
|
191 | 194 | } |
|
192 | 195 | else if (block === "}" && braces) { |
|
193 | 196 | braces-- |
|
194 | 197 | } |
|
195 | 198 | } |
|
196 | 199 | else { |
|
197 | 200 | // |
|
198 | 201 | // Look for math start delimiters and when |
|
199 | 202 | // found, set up the end delimiter. |
|
200 | 203 | // |
|
201 | 204 | if (block === inline || block === "$$") { |
|
202 | 205 | start = i; |
|
203 | 206 | end = block; |
|
204 | 207 | braces = 0; |
|
205 | 208 | } |
|
206 | 209 | else if (block.substr(1, 5) === "begin") { |
|
207 | 210 | start = i; |
|
208 | 211 | end = "\\end" + block.substr(6); |
|
209 | 212 | braces = 0; |
|
210 | 213 | } |
|
211 | 214 | } |
|
212 | 215 | } |
|
213 | 216 | if (last) { |
|
214 | 217 | process_math(start, last, de_tilde) |
|
215 | 218 | } |
|
216 | 219 | return de_tilde(blocks.join("")); |
|
217 | 220 | } |
|
218 | 221 | |
|
219 | 222 | // |
|
220 | 223 | // Put back the math strings that were saved, |
|
221 | 224 | // and clear the math array (no need to keep it around). |
|
222 | 225 | // |
|
223 | 226 | var replace_math = function (text) { |
|
224 | 227 | if (!window.MathJax) { |
|
225 | 228 | return text; |
|
226 | 229 | } |
|
227 | 230 | |
|
228 | 231 | text = text.replace(/@@(\d+)@@/g, function (match, n) { |
|
229 | 232 | return math[n] |
|
230 | 233 | }); |
|
231 | 234 | math = null; |
|
232 | 235 | return text; |
|
233 | 236 | } |
|
234 | 237 | |
|
235 | 238 | return { |
|
236 | 239 | init : init, |
|
237 | 240 | process_math : process_math, |
|
238 | 241 | remove_math : remove_math, |
|
239 | 242 | replace_math : replace_math |
|
240 | 243 | }; |
|
241 | 244 | |
|
242 | 245 | }(IPython)); No newline at end of file |
@@ -1,264 +1,263 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 | // MenuBar |
|
10 | 10 | //============================================================================ |
|
11 | 11 | |
|
12 | 12 | /** |
|
13 | 13 | * @module IPython |
|
14 | 14 | * @namespace IPython |
|
15 | 15 | * @submodule MenuBar |
|
16 | 16 | */ |
|
17 | 17 | |
|
18 | 18 | |
|
19 | 19 | var IPython = (function (IPython) { |
|
20 | 20 | |
|
21 | 21 | /** |
|
22 | 22 | * A MenuBar Class to generate the menubar of IPython noteboko |
|
23 | 23 | * @Class MenuBar |
|
24 | 24 | * |
|
25 | 25 | * @constructor |
|
26 | 26 | * |
|
27 | 27 | * |
|
28 | 28 | * @param selector {string} selector for the menubar element in DOM |
|
29 | 29 | * @param {object} [options] |
|
30 | 30 | * @param [options.baseProjectUrl] {String} String to use for the |
|
31 | 31 | * Base Project url, default would be to inspect |
|
32 | 32 | * $('body').data('baseProjectUrl'); |
|
33 | 33 | * does not support change for now is set through this option |
|
34 | 34 | */ |
|
35 | 35 | var MenuBar = function (selector, options) { |
|
36 | 36 | var options = options || {}; |
|
37 | 37 | if(options.baseProjectUrl!= undefined){ |
|
38 | 38 | this._baseProjectUrl = options.baseProjectUrl; |
|
39 | 39 | } |
|
40 | 40 | this.selector = selector; |
|
41 | 41 | if (this.selector !== undefined) { |
|
42 | 42 | this.element = $(selector); |
|
43 | 43 | this.style(); |
|
44 | 44 | this.bind_events(); |
|
45 | 45 | } |
|
46 | 46 | }; |
|
47 | 47 | |
|
48 | 48 | MenuBar.prototype.baseProjectUrl = function(){ |
|
49 | 49 | return this._baseProjectUrl || $('body').data('baseProjectUrl'); |
|
50 | 50 | }; |
|
51 | 51 | |
|
52 | 52 | |
|
53 | 53 | MenuBar.prototype.style = function () { |
|
54 | 54 | this.element.addClass('border-box-sizing'); |
|
55 | $('ul#menus').menubar({ | |
|
56 | select : function (event, ui) { | |
|
55 | this.element.find("li").click(function (event, ui) { | |
|
57 | 56 | // The selected cell loses focus when the menu is entered, so we |
|
58 | 57 | // re-select it upon selection. |
|
59 | 58 | var i = IPython.notebook.get_selected_index(); |
|
60 | 59 | IPython.notebook.select(i); |
|
61 | 60 | } |
|
62 |
|
|
|
63 | this.element.find("#restore_checkpoint").find("ul").find("li").hide(); | |
|
61 | ); | |
|
64 | 62 | }; |
|
65 | 63 | |
|
66 | 64 | |
|
67 | 65 | MenuBar.prototype.bind_events = function () { |
|
68 | 66 | // File |
|
69 | 67 | var that = this; |
|
70 | 68 | this.element.find('#new_notebook').click(function () { |
|
71 | 69 | window.open(that.baseProjectUrl()+'new'); |
|
72 | 70 | }); |
|
73 | 71 | this.element.find('#open_notebook').click(function () { |
|
74 | 72 | window.open(that.baseProjectUrl()); |
|
75 | 73 | }); |
|
76 | 74 | this.element.find('#rename_notebook').click(function () { |
|
77 | 75 | IPython.save_widget.rename_notebook(); |
|
78 | 76 | }); |
|
79 | 77 | this.element.find('#copy_notebook').click(function () { |
|
80 | 78 | var notebook_id = IPython.notebook.get_notebook_id(); |
|
81 | 79 | var url = that.baseProjectUrl() + notebook_id + '/copy'; |
|
82 | 80 | window.open(url,'_blank'); |
|
83 | 81 | return false; |
|
84 | 82 | }); |
|
85 | 83 | this.element.find('#save_checkpoint').click(function () { |
|
86 | 84 | IPython.notebook.save_checkpoint(); |
|
87 | 85 | }); |
|
88 | 86 | this.element.find('#restore_checkpoint').click(function () { |
|
89 | 87 | }); |
|
90 | 88 | this.element.find('#download_ipynb').click(function () { |
|
91 | 89 | var notebook_id = IPython.notebook.get_notebook_id(); |
|
92 | 90 | var url = that.baseProjectUrl() + 'notebooks/' + |
|
93 | 91 | notebook_id + '?format=json'; |
|
94 | 92 | window.location.assign(url); |
|
95 | 93 | }); |
|
96 | 94 | this.element.find('#download_py').click(function () { |
|
97 | 95 | var notebook_id = IPython.notebook.get_notebook_id(); |
|
98 | 96 | var url = that.baseProjectUrl() + 'notebooks/' + |
|
99 | 97 | notebook_id + '?format=py'; |
|
100 | 98 | window.location.assign(url); |
|
101 | 99 | }); |
|
102 | 100 | this.element.find('#kill_and_exit').click(function () { |
|
103 | 101 | IPython.notebook.kernel.kill(); |
|
104 | 102 | setTimeout(function(){window.close();}, 200); |
|
105 | 103 | }); |
|
106 | 104 | // Edit |
|
107 | 105 | this.element.find('#cut_cell').click(function () { |
|
108 | 106 | IPython.notebook.cut_cell(); |
|
109 | 107 | }); |
|
110 | 108 | this.element.find('#copy_cell').click(function () { |
|
111 | 109 | IPython.notebook.copy_cell(); |
|
112 | 110 | }); |
|
113 | 111 | this.element.find('#delete_cell').click(function () { |
|
114 | 112 | IPython.notebook.delete_cell(); |
|
115 | 113 | }); |
|
116 | 114 | this.element.find('#undelete_cell').click(function () { |
|
117 | 115 | IPython.notebook.undelete(); |
|
118 | 116 | }); |
|
119 | 117 | this.element.find('#split_cell').click(function () { |
|
120 | 118 | IPython.notebook.split_cell(); |
|
121 | 119 | }); |
|
122 | 120 | this.element.find('#merge_cell_above').click(function () { |
|
123 | 121 | IPython.notebook.merge_cell_above(); |
|
124 | 122 | }); |
|
125 | 123 | this.element.find('#merge_cell_below').click(function () { |
|
126 | 124 | IPython.notebook.merge_cell_below(); |
|
127 | 125 | }); |
|
128 | 126 | this.element.find('#move_cell_up').click(function () { |
|
129 | 127 | IPython.notebook.move_cell_up(); |
|
130 | 128 | }); |
|
131 | 129 | this.element.find('#move_cell_down').click(function () { |
|
132 | 130 | IPython.notebook.move_cell_down(); |
|
133 | 131 | }); |
|
134 | 132 | this.element.find('#select_previous').click(function () { |
|
135 | 133 | IPython.notebook.select_prev(); |
|
136 | 134 | }); |
|
137 | 135 | this.element.find('#select_next').click(function () { |
|
138 | 136 | IPython.notebook.select_next(); |
|
139 | 137 | }); |
|
140 | 138 | // View |
|
141 | 139 | this.element.find('#toggle_header').click(function () { |
|
142 | 140 | $('div#header').toggle(); |
|
143 | 141 | IPython.layout_manager.do_resize(); |
|
144 | 142 | }); |
|
145 | 143 | this.element.find('#toggle_toolbar').click(function () { |
|
146 |
|
|
|
144 | $('div#maintoolbar').toggle(); | |
|
145 | IPython.layout_manager.do_resize(); | |
|
147 | 146 | }); |
|
148 | 147 | // Insert |
|
149 | 148 | this.element.find('#insert_cell_above').click(function () { |
|
150 | 149 | IPython.notebook.insert_cell_above('code'); |
|
151 | 150 | }); |
|
152 | 151 | this.element.find('#insert_cell_below').click(function () { |
|
153 | 152 | IPython.notebook.insert_cell_below('code'); |
|
154 | 153 | }); |
|
155 | 154 | // Cell |
|
156 | 155 | this.element.find('#run_cell').click(function () { |
|
157 | 156 | IPython.notebook.execute_selected_cell(); |
|
158 | 157 | }); |
|
159 | 158 | this.element.find('#run_cell_in_place').click(function () { |
|
160 | 159 | IPython.notebook.execute_selected_cell({terminal:true}); |
|
161 | 160 | }); |
|
162 | 161 | this.element.find('#run_all_cells').click(function () { |
|
163 | 162 | IPython.notebook.execute_all_cells(); |
|
164 | 163 | }).attr('title', 'Run all cells in the notebook'); |
|
165 | 164 | this.element.find('#run_all_cells_above').click(function () { |
|
166 | 165 | IPython.notebook.execute_cells_above(); |
|
167 | 166 | }).attr('title', 'Run all cells above (but not including) this cell'); |
|
168 | 167 | this.element.find('#run_all_cells_below').click(function () { |
|
169 | 168 | IPython.notebook.execute_cells_below(); |
|
170 | 169 | }).attr('title', 'Run this cell and all cells below it'); |
|
171 | 170 | this.element.find('#to_code').click(function () { |
|
172 | 171 | IPython.notebook.to_code(); |
|
173 | 172 | }); |
|
174 | 173 | this.element.find('#to_markdown').click(function () { |
|
175 | 174 | IPython.notebook.to_markdown(); |
|
176 | 175 | }); |
|
177 | 176 | this.element.find('#to_raw').click(function () { |
|
178 | 177 | IPython.notebook.to_raw(); |
|
179 | 178 | }); |
|
180 | 179 | this.element.find('#to_heading1').click(function () { |
|
181 | 180 | IPython.notebook.to_heading(undefined, 1); |
|
182 | 181 | }); |
|
183 | 182 | this.element.find('#to_heading2').click(function () { |
|
184 | 183 | IPython.notebook.to_heading(undefined, 2); |
|
185 | 184 | }); |
|
186 | 185 | this.element.find('#to_heading3').click(function () { |
|
187 | 186 | IPython.notebook.to_heading(undefined, 3); |
|
188 | 187 | }); |
|
189 | 188 | this.element.find('#to_heading4').click(function () { |
|
190 | 189 | IPython.notebook.to_heading(undefined, 4); |
|
191 | 190 | }); |
|
192 | 191 | this.element.find('#to_heading5').click(function () { |
|
193 | 192 | IPython.notebook.to_heading(undefined, 5); |
|
194 | 193 | }); |
|
195 | 194 | this.element.find('#to_heading6').click(function () { |
|
196 | 195 | IPython.notebook.to_heading(undefined, 6); |
|
197 | 196 | }); |
|
198 | 197 | this.element.find('#toggle_output').click(function () { |
|
199 | 198 | IPython.notebook.toggle_output(); |
|
200 | 199 | }); |
|
201 | 200 | this.element.find('#collapse_all_output').click(function () { |
|
202 | 201 | IPython.notebook.collapse_all_output(); |
|
203 | 202 | }); |
|
204 | 203 | this.element.find('#scroll_all_output').click(function () { |
|
205 | 204 | IPython.notebook.scroll_all_output(); |
|
206 | 205 | }); |
|
207 | 206 | this.element.find('#expand_all_output').click(function () { |
|
208 | 207 | IPython.notebook.expand_all_output(); |
|
209 | 208 | }); |
|
210 | 209 | this.element.find('#clear_all_output').click(function () { |
|
211 | 210 | IPython.notebook.clear_all_output(); |
|
212 | 211 | }); |
|
213 | 212 | // Kernel |
|
214 | 213 | this.element.find('#int_kernel').click(function () { |
|
215 | 214 | IPython.notebook.kernel.interrupt(); |
|
216 | 215 | }); |
|
217 | 216 | this.element.find('#restart_kernel').click(function () { |
|
218 | 217 | IPython.notebook.restart_kernel(); |
|
219 | 218 | }); |
|
220 | 219 | // Help |
|
221 | 220 | this.element.find('#keyboard_shortcuts').click(function () { |
|
222 | 221 | IPython.quick_help.show_keyboard_shortcuts(); |
|
223 | 222 | }); |
|
224 | 223 | |
|
225 | 224 | this.update_restore_checkpoint(null); |
|
226 | 225 | |
|
227 | 226 | $([IPython.events]).on('checkpoints_listed.Notebook', function (event, data) { |
|
228 | 227 | that.update_restore_checkpoint(data); |
|
229 | 228 | }); |
|
230 | 229 | |
|
231 | 230 | $([IPython.events]).on('checkpoint_created.Notebook', function (event, data) { |
|
232 | 231 | that.update_restore_checkpoint([data]); |
|
233 | 232 | }); |
|
234 | 233 | }; |
|
235 | 234 | |
|
236 | 235 | MenuBar.prototype.update_restore_checkpoint = function(checkpoints) { |
|
237 | 236 | if (! checkpoints) { |
|
238 | 237 | checkpoints = []; |
|
239 | 238 | }; |
|
240 | 239 | this.element.find("#restore_checkpoint").find("ul").find("li").each(function(i) { |
|
241 | 240 | var li = $(this); |
|
242 | 241 | var a = li.find("a"); |
|
243 | 242 | a.off("click"); |
|
244 | 243 | if (checkpoints.length <= i) { |
|
245 | 244 | li.hide(); |
|
246 | 245 | return; |
|
247 | 246 | } else { |
|
248 | 247 | li.show(); |
|
249 | 248 | }; |
|
250 | 249 | var checkpoint = checkpoints[i]; |
|
251 | 250 | var d = new Date(checkpoint.last_modified); |
|
252 | 251 | li.find('a').text( |
|
253 | 252 | d.format("mmm dd HH:MM:ss") |
|
254 | 253 | ).click(function () { |
|
255 | 254 | IPython.notebook.restore_checkpoint_dialog(checkpoint); |
|
256 | 255 | }); |
|
257 | 256 | }); |
|
258 | 257 | }; |
|
259 | 258 | |
|
260 | 259 | IPython.MenuBar = MenuBar; |
|
261 | 260 | |
|
262 | 261 | return IPython; |
|
263 | 262 | |
|
264 | 263 | }(IPython)); |
@@ -1,2046 +1,2010 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 | /** |
|
18 | 18 | * A notebook contains and manages cells. |
|
19 | 19 | * |
|
20 | 20 | * @class Notebook |
|
21 | 21 | * @constructor |
|
22 | 22 | * @param {String} selector A jQuery selector for the notebook's DOM element |
|
23 | 23 | * @param {Object} [options] A config object |
|
24 | 24 | */ |
|
25 | 25 | var Notebook = function (selector, options) { |
|
26 | 26 | var options = options || {}; |
|
27 | 27 | this._baseProjectUrl = options.baseProjectUrl; |
|
28 | 28 | this.read_only = options.read_only || IPython.read_only; |
|
29 | 29 | |
|
30 | 30 | this.element = $(selector); |
|
31 | 31 | this.element.scroll(); |
|
32 | 32 | this.element.data("notebook", this); |
|
33 | 33 | this.next_prompt_number = 1; |
|
34 | 34 | this.kernel = null; |
|
35 | 35 | this.clipboard = null; |
|
36 | 36 | this.undelete_backup = null; |
|
37 | 37 | this.undelete_index = null; |
|
38 | 38 | this.undelete_below = false; |
|
39 | 39 | this.paste_enabled = false; |
|
40 | 40 | this.set_dirty(false); |
|
41 | 41 | this.metadata = {}; |
|
42 | 42 | this._checkpoint_after_save = false; |
|
43 | 43 | this.last_checkpoint = null; |
|
44 | 44 | this.autosave_interval = 0; |
|
45 | 45 | this.autosave_timer = null; |
|
46 | 46 | // autosave *at most* every two minutes |
|
47 | 47 | this.minimum_autosave_interval = 120000; |
|
48 | 48 | // single worksheet for now |
|
49 | 49 | this.worksheet_metadata = {}; |
|
50 | 50 | this.control_key_active = false; |
|
51 | 51 | this.notebook_id = null; |
|
52 | 52 | this.notebook_name = null; |
|
53 | 53 | this.notebook_name_blacklist_re = /[\/\\:]/; |
|
54 | 54 | this.nbformat = 3 // Increment this when changing the nbformat |
|
55 | 55 | this.nbformat_minor = 0 // Increment this when changing the nbformat |
|
56 | 56 | this.style(); |
|
57 | 57 | this.create_elements(); |
|
58 | 58 | this.bind_events(); |
|
59 | 59 | }; |
|
60 | 60 | |
|
61 | 61 | /** |
|
62 | 62 | * Tweak the notebook's CSS style. |
|
63 | 63 | * |
|
64 | 64 | * @method style |
|
65 | 65 | */ |
|
66 | 66 | Notebook.prototype.style = function () { |
|
67 | 67 | $('div#notebook').addClass('border-box-sizing'); |
|
68 | 68 | }; |
|
69 | 69 | |
|
70 | 70 | /** |
|
71 | 71 | * Get the root URL of the notebook server. |
|
72 | 72 | * |
|
73 | 73 | * @method baseProjectUrl |
|
74 | 74 | * @return {String} The base project URL |
|
75 | 75 | */ |
|
76 | 76 | Notebook.prototype.baseProjectUrl = function(){ |
|
77 | 77 | return this._baseProjectUrl || $('body').data('baseProjectUrl'); |
|
78 | 78 | }; |
|
79 | 79 | |
|
80 | 80 | /** |
|
81 | 81 | * Create an HTML and CSS representation of the notebook. |
|
82 | 82 | * |
|
83 | 83 | * @method create_elements |
|
84 | 84 | */ |
|
85 | 85 | Notebook.prototype.create_elements = function () { |
|
86 | 86 | // We add this end_space div to the end of the notebook div to: |
|
87 | 87 | // i) provide a margin between the last cell and the end of the notebook |
|
88 | 88 | // ii) to prevent the div from scrolling up when the last cell is being |
|
89 | 89 | // edited, but is too low on the page, which browsers will do automatically. |
|
90 | 90 | var that = this; |
|
91 | var end_space = $('<div/>').addClass('end_space').height("30%"); | |
|
91 | this.container = $("<div/>").addClass("container").attr("id", "notebook-container"); | |
|
92 | var end_space = $('<div/>').addClass('end_space'); | |
|
92 | 93 | end_space.dblclick(function (e) { |
|
93 | 94 | if (that.read_only) return; |
|
94 | 95 | var ncells = that.ncells(); |
|
95 | 96 | that.insert_cell_below('code',ncells-1); |
|
96 | 97 | }); |
|
97 |
this.element.append( |
|
|
98 | this.element.append(this.container); | |
|
99 | this.container.append(end_space); | |
|
98 | 100 | $('div#notebook').addClass('border-box-sizing'); |
|
99 | 101 | }; |
|
100 | 102 | |
|
101 | 103 | /** |
|
102 | 104 | * Bind JavaScript events: key presses and custom IPython events. |
|
103 | 105 | * |
|
104 | 106 | * @method bind_events |
|
105 | 107 | */ |
|
106 | 108 | Notebook.prototype.bind_events = function () { |
|
107 | 109 | var that = this; |
|
108 | 110 | |
|
109 | 111 | $([IPython.events]).on('set_next_input.Notebook', function (event, data) { |
|
110 | 112 | var index = that.find_cell_index(data.cell); |
|
111 | 113 | var new_cell = that.insert_cell_below('code',index); |
|
112 | 114 | new_cell.set_text(data.text); |
|
113 | 115 | that.dirty = true; |
|
114 | 116 | }); |
|
115 | 117 | |
|
116 | 118 | $([IPython.events]).on('set_dirty.Notebook', function (event, data) { |
|
117 | 119 | that.dirty = data.value; |
|
118 | 120 | }); |
|
119 | 121 | |
|
120 | 122 | $([IPython.events]).on('select.Cell', function (event, data) { |
|
121 | 123 | var index = that.find_cell_index(data.cell); |
|
122 | 124 | that.select(index); |
|
123 | 125 | }); |
|
124 | 126 | |
|
125 | 127 | |
|
126 | 128 | $(document).keydown(function (event) { |
|
127 | 129 | // console.log(event); |
|
128 | 130 | if (that.read_only) return true; |
|
129 | 131 | |
|
130 | 132 | // Save (CTRL+S) or (AppleKey+S) |
|
131 | 133 | //metaKey = applekey on mac |
|
132 | 134 | if ((event.ctrlKey || event.metaKey) && event.keyCode==83) { |
|
133 | 135 | that.save_checkpoint(); |
|
134 | 136 | event.preventDefault(); |
|
135 | 137 | return false; |
|
136 | 138 | } else if (event.which === key.ESC) { |
|
137 | 139 | // Intercept escape at highest level to avoid closing |
|
138 | 140 | // websocket connection with firefox |
|
139 | 141 | IPython.pager.collapse(); |
|
140 | 142 | event.preventDefault(); |
|
141 | 143 | } else if (event.which === key.SHIFT) { |
|
142 | 144 | // ignore shift keydown |
|
143 | 145 | return true; |
|
144 | 146 | } |
|
145 | 147 | if (event.which === key.UPARROW && !event.shiftKey) { |
|
146 | 148 | var cell = that.get_selected_cell(); |
|
147 | 149 | if (cell && cell.at_top()) { |
|
148 | 150 | event.preventDefault(); |
|
149 | 151 | that.select_prev(); |
|
150 | 152 | }; |
|
151 | 153 | } else if (event.which === key.DOWNARROW && !event.shiftKey) { |
|
152 | 154 | var cell = that.get_selected_cell(); |
|
153 | 155 | if (cell && cell.at_bottom()) { |
|
154 | 156 | event.preventDefault(); |
|
155 | 157 | that.select_next(); |
|
156 | 158 | }; |
|
157 | 159 | } else if (event.which === key.ENTER && event.shiftKey) { |
|
158 | 160 | that.execute_selected_cell(); |
|
159 | 161 | return false; |
|
160 | 162 | } else if (event.which === key.ENTER && event.altKey) { |
|
161 | 163 | // Execute code cell, and insert new in place |
|
162 | 164 | that.execute_selected_cell(); |
|
163 | 165 | // Only insert a new cell, if we ended up in an already populated cell |
|
164 | 166 | if (/\S/.test(that.get_selected_cell().get_text()) == true) { |
|
165 | 167 | that.insert_cell_above('code'); |
|
166 | 168 | } |
|
167 | 169 | return false; |
|
168 | 170 | } else if (event.which === key.ENTER && event.ctrlKey) { |
|
169 | 171 | that.execute_selected_cell({terminal:true}); |
|
170 | 172 | return false; |
|
171 | 173 | } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) { |
|
172 | 174 | that.control_key_active = true; |
|
173 | 175 | return false; |
|
174 | 176 | } else if (event.which === 88 && that.control_key_active) { |
|
175 | 177 | // Cut selected cell = x |
|
176 | 178 | that.cut_cell(); |
|
177 | 179 | that.control_key_active = false; |
|
178 | 180 | return false; |
|
179 | 181 | } else if (event.which === 67 && that.control_key_active) { |
|
180 | 182 | // Copy selected cell = c |
|
181 | 183 | that.copy_cell(); |
|
182 | 184 | that.control_key_active = false; |
|
183 | 185 | return false; |
|
184 | 186 | } else if (event.which === 86 && that.control_key_active) { |
|
185 | 187 | // Paste below selected cell = v |
|
186 | 188 | that.paste_cell_below(); |
|
187 | 189 | that.control_key_active = false; |
|
188 | 190 | return false; |
|
189 | 191 | } else if (event.which === 68 && that.control_key_active) { |
|
190 | 192 | // Delete selected cell = d |
|
191 | 193 | that.delete_cell(); |
|
192 | 194 | that.control_key_active = false; |
|
193 | 195 | return false; |
|
194 | 196 | } else if (event.which === 65 && that.control_key_active) { |
|
195 | 197 | // Insert code cell above selected = a |
|
196 | 198 | that.insert_cell_above('code'); |
|
197 | 199 | that.control_key_active = false; |
|
198 | 200 | return false; |
|
199 | 201 | } else if (event.which === 66 && that.control_key_active) { |
|
200 | 202 | // Insert code cell below selected = b |
|
201 | 203 | that.insert_cell_below('code'); |
|
202 | 204 | that.control_key_active = false; |
|
203 | 205 | return false; |
|
204 | 206 | } else if (event.which === 89 && that.control_key_active) { |
|
205 | 207 | // To code = y |
|
206 | 208 | that.to_code(); |
|
207 | 209 | that.control_key_active = false; |
|
208 | 210 | return false; |
|
209 | 211 | } else if (event.which === 77 && that.control_key_active) { |
|
210 | 212 | // To markdown = m |
|
211 | 213 | that.to_markdown(); |
|
212 | 214 | that.control_key_active = false; |
|
213 | 215 | return false; |
|
214 | 216 | } else if (event.which === 84 && that.control_key_active) { |
|
215 | 217 | // To Raw = t |
|
216 | 218 | that.to_raw(); |
|
217 | 219 | that.control_key_active = false; |
|
218 | 220 | return false; |
|
219 | 221 | } else if (event.which === 49 && that.control_key_active) { |
|
220 | 222 | // To Heading 1 = 1 |
|
221 | 223 | that.to_heading(undefined, 1); |
|
222 | 224 | that.control_key_active = false; |
|
223 | 225 | return false; |
|
224 | 226 | } else if (event.which === 50 && that.control_key_active) { |
|
225 | 227 | // To Heading 2 = 2 |
|
226 | 228 | that.to_heading(undefined, 2); |
|
227 | 229 | that.control_key_active = false; |
|
228 | 230 | return false; |
|
229 | 231 | } else if (event.which === 51 && that.control_key_active) { |
|
230 | 232 | // To Heading 3 = 3 |
|
231 | 233 | that.to_heading(undefined, 3); |
|
232 | 234 | that.control_key_active = false; |
|
233 | 235 | return false; |
|
234 | 236 | } else if (event.which === 52 && that.control_key_active) { |
|
235 | 237 | // To Heading 4 = 4 |
|
236 | 238 | that.to_heading(undefined, 4); |
|
237 | 239 | that.control_key_active = false; |
|
238 | 240 | return false; |
|
239 | 241 | } else if (event.which === 53 && that.control_key_active) { |
|
240 | 242 | // To Heading 5 = 5 |
|
241 | 243 | that.to_heading(undefined, 5); |
|
242 | 244 | that.control_key_active = false; |
|
243 | 245 | return false; |
|
244 | 246 | } else if (event.which === 54 && that.control_key_active) { |
|
245 | 247 | // To Heading 6 = 6 |
|
246 | 248 | that.to_heading(undefined, 6); |
|
247 | 249 | that.control_key_active = false; |
|
248 | 250 | return false; |
|
249 | 251 | } else if (event.which === 79 && that.control_key_active) { |
|
250 | 252 | // Toggle output = o |
|
251 | 253 | if (event.shiftKey){ |
|
252 | 254 | that.toggle_output_scroll(); |
|
253 | 255 | } else { |
|
254 | 256 | that.toggle_output(); |
|
255 | 257 | } |
|
256 | 258 | that.control_key_active = false; |
|
257 | 259 | return false; |
|
258 | 260 | } else if (event.which === 83 && that.control_key_active) { |
|
259 | 261 | // Save notebook = s |
|
260 | 262 | that.save_checkpoint(); |
|
261 | 263 | that.control_key_active = false; |
|
262 | 264 | return false; |
|
263 | 265 | } else if (event.which === 74 && that.control_key_active) { |
|
264 | 266 | // Move cell down = j |
|
265 | 267 | that.move_cell_down(); |
|
266 | 268 | that.control_key_active = false; |
|
267 | 269 | return false; |
|
268 | 270 | } else if (event.which === 75 && that.control_key_active) { |
|
269 | 271 | // Move cell up = k |
|
270 | 272 | that.move_cell_up(); |
|
271 | 273 | that.control_key_active = false; |
|
272 | 274 | return false; |
|
273 | 275 | } else if (event.which === 80 && that.control_key_active) { |
|
274 | 276 | // Select previous = p |
|
275 | 277 | that.select_prev(); |
|
276 | 278 | that.control_key_active = false; |
|
277 | 279 | return false; |
|
278 | 280 | } else if (event.which === 78 && that.control_key_active) { |
|
279 | 281 | // Select next = n |
|
280 | 282 | that.select_next(); |
|
281 | 283 | that.control_key_active = false; |
|
282 | 284 | return false; |
|
283 | 285 | } else if (event.which === 76 && that.control_key_active) { |
|
284 | 286 | // Toggle line numbers = l |
|
285 | 287 | that.cell_toggle_line_numbers(); |
|
286 | 288 | that.control_key_active = false; |
|
287 | 289 | return false; |
|
288 | 290 | } else if (event.which === 73 && that.control_key_active) { |
|
289 | 291 | // Interrupt kernel = i |
|
290 | 292 | that.kernel.interrupt(); |
|
291 | 293 | that.control_key_active = false; |
|
292 | 294 | return false; |
|
293 | 295 | } else if (event.which === 190 && that.control_key_active) { |
|
294 | 296 | // Restart kernel = . # matches qt console |
|
295 | 297 | that.restart_kernel(); |
|
296 | 298 | that.control_key_active = false; |
|
297 | 299 | return false; |
|
298 | 300 | } else if (event.which === 72 && that.control_key_active) { |
|
299 | 301 | // Show keyboard shortcuts = h |
|
300 | 302 | IPython.quick_help.show_keyboard_shortcuts(); |
|
301 | 303 | that.control_key_active = false; |
|
302 | 304 | return false; |
|
303 | 305 | } else if (event.which === 90 && that.control_key_active) { |
|
304 | 306 | // Undo last cell delete = z |
|
305 | 307 | that.undelete(); |
|
306 | 308 | that.control_key_active = false; |
|
307 | 309 | return false; |
|
308 | 310 | } else if (that.control_key_active) { |
|
309 | 311 | that.control_key_active = false; |
|
310 | 312 | return true; |
|
311 | 313 | } |
|
312 | 314 | return true; |
|
313 | 315 | }); |
|
314 | 316 | |
|
315 | 317 | var collapse_time = function(time){ |
|
316 | 318 | var app_height = $('#ipython-main-app').height(); // content height |
|
317 | 319 | var splitter_height = $('div#pager_splitter').outerHeight(true); |
|
318 | 320 | var new_height = app_height - splitter_height; |
|
319 | 321 | that.element.animate({height : new_height + 'px'}, time); |
|
320 | 322 | } |
|
321 | 323 | |
|
322 | 324 | this.element.bind('collapse_pager', function (event,extrap) { |
|
323 | 325 | var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast'; |
|
324 | 326 | collapse_time(time); |
|
325 | 327 | }); |
|
326 | 328 | |
|
327 | 329 | var expand_time = function(time) { |
|
328 | 330 | var app_height = $('#ipython-main-app').height(); // content height |
|
329 | 331 | var splitter_height = $('div#pager_splitter').outerHeight(true); |
|
330 | 332 | var pager_height = $('div#pager').outerHeight(true); |
|
331 | 333 | var new_height = app_height - pager_height - splitter_height; |
|
332 | 334 | that.element.animate({height : new_height + 'px'}, time); |
|
333 | 335 | } |
|
334 | 336 | |
|
335 | 337 | this.element.bind('expand_pager', function (event, extrap) { |
|
336 | 338 | var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast'; |
|
337 | 339 | expand_time(time); |
|
338 | 340 | }); |
|
339 | 341 | |
|
340 | 342 | $(window).bind('beforeunload', function () { |
|
341 | 343 | // TODO: Make killing the kernel configurable. |
|
342 | 344 | var kill_kernel = false; |
|
343 | 345 | if (kill_kernel) { |
|
344 | 346 | that.kernel.kill(); |
|
345 | 347 | } |
|
346 | 348 | // if we are autosaving, trigger an autosave on nav-away |
|
347 | 349 | if (that.dirty && that.autosave_interval && ! that.read_only) { |
|
348 | 350 | that.save_notebook(); |
|
349 | 351 | }; |
|
350 | 352 | // Null is the *only* return value that will make the browser not |
|
351 | 353 | // pop up the "don't leave" dialog. |
|
352 | 354 | return null; |
|
353 | 355 | }); |
|
354 | 356 | }; |
|
355 | 357 | |
|
356 | 358 | /** |
|
357 | 359 | * Set the dirty flag, and trigger the set_dirty.Notebook event |
|
358 | 360 | * |
|
359 | 361 | * @method set_dirty |
|
360 | 362 | */ |
|
361 | 363 | Notebook.prototype.set_dirty = function (value) { |
|
362 | 364 | if (value === undefined) { |
|
363 | 365 | value = true; |
|
364 | 366 | } |
|
365 | 367 | if (this.dirty == value) { |
|
366 | 368 | return; |
|
367 | 369 | } |
|
368 | 370 | $([IPython.events]).trigger('set_dirty.Notebook', {value: value}); |
|
369 | 371 | }; |
|
370 | 372 | |
|
371 | 373 | /** |
|
372 | 374 | * Scroll the top of the page to a given cell. |
|
373 | 375 | * |
|
374 | 376 | * @method scroll_to_cell |
|
375 | 377 | * @param {Number} cell_number An index of the cell to view |
|
376 | 378 | * @param {Number} time Animation time in milliseconds |
|
377 | 379 | * @return {Number} Pixel offset from the top of the container |
|
378 | 380 | */ |
|
379 | 381 | Notebook.prototype.scroll_to_cell = function (cell_number, time) { |
|
380 | 382 | var cells = this.get_cells(); |
|
381 | 383 | var time = time || 0; |
|
382 | 384 | cell_number = Math.min(cells.length-1,cell_number); |
|
383 | 385 | cell_number = Math.max(0 ,cell_number); |
|
384 | 386 | var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ; |
|
385 | 387 | this.element.animate({scrollTop:scroll_value}, time); |
|
386 | 388 | return scroll_value; |
|
387 | 389 | }; |
|
388 | 390 | |
|
389 | 391 | /** |
|
390 | 392 | * Scroll to the bottom of the page. |
|
391 | 393 | * |
|
392 | 394 | * @method scroll_to_bottom |
|
393 | 395 | */ |
|
394 | 396 | Notebook.prototype.scroll_to_bottom = function () { |
|
395 | 397 | this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0); |
|
396 | 398 | }; |
|
397 | 399 | |
|
398 | 400 | /** |
|
399 | 401 | * Scroll to the top of the page. |
|
400 | 402 | * |
|
401 | 403 | * @method scroll_to_top |
|
402 | 404 | */ |
|
403 | 405 | Notebook.prototype.scroll_to_top = function () { |
|
404 | 406 | this.element.animate({scrollTop:0}, 0); |
|
405 | 407 | }; |
|
406 | 408 | |
|
407 | 409 | |
|
408 | 410 | // Cell indexing, retrieval, etc. |
|
409 | 411 | |
|
410 | 412 | /** |
|
411 | 413 | * Get all cell elements in the notebook. |
|
412 | 414 | * |
|
413 | 415 | * @method get_cell_elements |
|
414 | 416 | * @return {jQuery} A selector of all cell elements |
|
415 | 417 | */ |
|
416 | 418 | Notebook.prototype.get_cell_elements = function () { |
|
417 |
return this. |
|
|
419 | return this.container.children("div.cell"); | |
|
418 | 420 | }; |
|
419 | 421 | |
|
420 | 422 | /** |
|
421 | 423 | * Get a particular cell element. |
|
422 | 424 | * |
|
423 | 425 | * @method get_cell_element |
|
424 | 426 | * @param {Number} index An index of a cell to select |
|
425 | 427 | * @return {jQuery} A selector of the given cell. |
|
426 | 428 | */ |
|
427 | 429 | Notebook.prototype.get_cell_element = function (index) { |
|
428 | 430 | var result = null; |
|
429 | 431 | var e = this.get_cell_elements().eq(index); |
|
430 | 432 | if (e.length !== 0) { |
|
431 | 433 | result = e; |
|
432 | 434 | } |
|
433 | 435 | return result; |
|
434 | 436 | }; |
|
435 | 437 | |
|
436 | 438 | /** |
|
437 | 439 | * Count the cells in this notebook. |
|
438 | 440 | * |
|
439 | 441 | * @method ncells |
|
440 | 442 | * @return {Number} The number of cells in this notebook |
|
441 | 443 | */ |
|
442 | 444 | Notebook.prototype.ncells = function () { |
|
443 | 445 | return this.get_cell_elements().length; |
|
444 | 446 | }; |
|
445 | 447 | |
|
446 | 448 | /** |
|
447 | 449 | * Get all Cell objects in this notebook. |
|
448 | 450 | * |
|
449 | 451 | * @method get_cells |
|
450 | 452 | * @return {Array} This notebook's Cell objects |
|
451 | 453 | */ |
|
452 | 454 | // TODO: we are often calling cells as cells()[i], which we should optimize |
|
453 | 455 | // to cells(i) or a new method. |
|
454 | 456 | Notebook.prototype.get_cells = function () { |
|
455 | 457 | return this.get_cell_elements().toArray().map(function (e) { |
|
456 | 458 | return $(e).data("cell"); |
|
457 | 459 | }); |
|
458 | 460 | }; |
|
459 | 461 | |
|
460 | 462 | /** |
|
461 | 463 | * Get a Cell object from this notebook. |
|
462 | 464 | * |
|
463 | 465 | * @method get_cell |
|
464 | 466 | * @param {Number} index An index of a cell to retrieve |
|
465 | 467 | * @return {Cell} A particular cell |
|
466 | 468 | */ |
|
467 | 469 | Notebook.prototype.get_cell = function (index) { |
|
468 | 470 | var result = null; |
|
469 | 471 | var ce = this.get_cell_element(index); |
|
470 | 472 | if (ce !== null) { |
|
471 | 473 | result = ce.data('cell'); |
|
472 | 474 | } |
|
473 | 475 | return result; |
|
474 | 476 | } |
|
475 | 477 | |
|
476 | 478 | /** |
|
477 | 479 | * Get the cell below a given cell. |
|
478 | 480 | * |
|
479 | 481 | * @method get_next_cell |
|
480 | 482 | * @param {Cell} cell The provided cell |
|
481 | 483 | * @return {Cell} The next cell |
|
482 | 484 | */ |
|
483 | 485 | Notebook.prototype.get_next_cell = function (cell) { |
|
484 | 486 | var result = null; |
|
485 | 487 | var index = this.find_cell_index(cell); |
|
486 | 488 | if (this.is_valid_cell_index(index+1)) { |
|
487 | 489 | result = this.get_cell(index+1); |
|
488 | 490 | } |
|
489 | 491 | return result; |
|
490 | 492 | } |
|
491 | 493 | |
|
492 | 494 | /** |
|
493 | 495 | * Get the cell above a given cell. |
|
494 | 496 | * |
|
495 | 497 | * @method get_prev_cell |
|
496 | 498 | * @param {Cell} cell The provided cell |
|
497 | 499 | * @return {Cell} The previous cell |
|
498 | 500 | */ |
|
499 | 501 | Notebook.prototype.get_prev_cell = function (cell) { |
|
500 | 502 | // TODO: off-by-one |
|
501 | 503 | // nb.get_prev_cell(nb.get_cell(1)) is null |
|
502 | 504 | var result = null; |
|
503 | 505 | var index = this.find_cell_index(cell); |
|
504 | 506 | if (index !== null && index > 1) { |
|
505 | 507 | result = this.get_cell(index-1); |
|
506 | 508 | } |
|
507 | 509 | return result; |
|
508 | 510 | } |
|
509 | 511 | |
|
510 | 512 | /** |
|
511 | 513 | * Get the numeric index of a given cell. |
|
512 | 514 | * |
|
513 | 515 | * @method find_cell_index |
|
514 | 516 | * @param {Cell} cell The provided cell |
|
515 | 517 | * @return {Number} The cell's numeric index |
|
516 | 518 | */ |
|
517 | 519 | Notebook.prototype.find_cell_index = function (cell) { |
|
518 | 520 | var result = null; |
|
519 | 521 | this.get_cell_elements().filter(function (index) { |
|
520 | 522 | if ($(this).data("cell") === cell) { |
|
521 | 523 | result = index; |
|
522 | 524 | }; |
|
523 | 525 | }); |
|
524 | 526 | return result; |
|
525 | 527 | }; |
|
526 | 528 | |
|
527 | 529 | /** |
|
528 | 530 | * Get a given index , or the selected index if none is provided. |
|
529 | 531 | * |
|
530 | 532 | * @method index_or_selected |
|
531 | 533 | * @param {Number} index A cell's index |
|
532 | 534 | * @return {Number} The given index, or selected index if none is provided. |
|
533 | 535 | */ |
|
534 | 536 | Notebook.prototype.index_or_selected = function (index) { |
|
535 | 537 | var i; |
|
536 | 538 | if (index === undefined || index === null) { |
|
537 | 539 | i = this.get_selected_index(); |
|
538 | 540 | if (i === null) { |
|
539 | 541 | i = 0; |
|
540 | 542 | } |
|
541 | 543 | } else { |
|
542 | 544 | i = index; |
|
543 | 545 | } |
|
544 | 546 | return i; |
|
545 | 547 | }; |
|
546 | 548 | |
|
547 | 549 | /** |
|
548 | 550 | * Get the currently selected cell. |
|
549 | 551 | * @method get_selected_cell |
|
550 | 552 | * @return {Cell} The selected cell |
|
551 | 553 | */ |
|
552 | 554 | Notebook.prototype.get_selected_cell = function () { |
|
553 | 555 | var index = this.get_selected_index(); |
|
554 | 556 | return this.get_cell(index); |
|
555 | 557 | }; |
|
556 | 558 | |
|
557 | 559 | /** |
|
558 | 560 | * Check whether a cell index is valid. |
|
559 | 561 | * |
|
560 | 562 | * @method is_valid_cell_index |
|
561 | 563 | * @param {Number} index A cell index |
|
562 | 564 | * @return True if the index is valid, false otherwise |
|
563 | 565 | */ |
|
564 | 566 | Notebook.prototype.is_valid_cell_index = function (index) { |
|
565 | 567 | if (index !== null && index >= 0 && index < this.ncells()) { |
|
566 | 568 | return true; |
|
567 | 569 | } else { |
|
568 | 570 | return false; |
|
569 | 571 | }; |
|
570 | 572 | } |
|
571 | 573 | |
|
572 | 574 | /** |
|
573 | 575 | * Get the index of the currently selected cell. |
|
574 | 576 | |
|
575 | 577 | * @method get_selected_index |
|
576 | 578 | * @return {Number} The selected cell's numeric index |
|
577 | 579 | */ |
|
578 | 580 | Notebook.prototype.get_selected_index = function () { |
|
579 | 581 | var result = null; |
|
580 | 582 | this.get_cell_elements().filter(function (index) { |
|
581 | 583 | if ($(this).data("cell").selected === true) { |
|
582 | 584 | result = index; |
|
583 | 585 | }; |
|
584 | 586 | }); |
|
585 | 587 | return result; |
|
586 | 588 | }; |
|
587 | 589 | |
|
588 | 590 | |
|
589 | 591 | // Cell selection. |
|
590 | 592 | |
|
591 | 593 | /** |
|
592 | 594 | * Programmatically select a cell. |
|
593 | 595 | * |
|
594 | 596 | * @method select |
|
595 | 597 | * @param {Number} index A cell's index |
|
596 | 598 | * @return {Notebook} This notebook |
|
597 | 599 | */ |
|
598 | 600 | Notebook.prototype.select = function (index) { |
|
599 | 601 | if (this.is_valid_cell_index(index)) { |
|
600 | 602 | var sindex = this.get_selected_index() |
|
601 | 603 | if (sindex !== null && index !== sindex) { |
|
602 | 604 | this.get_cell(sindex).unselect(); |
|
603 | 605 | }; |
|
604 | 606 | var cell = this.get_cell(index); |
|
605 | 607 | cell.select(); |
|
606 | 608 | if (cell.cell_type === 'heading') { |
|
607 | 609 | $([IPython.events]).trigger('selected_cell_type_changed.Notebook', |
|
608 | 610 | {'cell_type':cell.cell_type,level:cell.level} |
|
609 | 611 | ); |
|
610 | 612 | } else { |
|
611 | 613 | $([IPython.events]).trigger('selected_cell_type_changed.Notebook', |
|
612 | 614 | {'cell_type':cell.cell_type} |
|
613 | 615 | ); |
|
614 | 616 | }; |
|
615 | 617 | }; |
|
616 | 618 | return this; |
|
617 | 619 | }; |
|
618 | 620 | |
|
619 | 621 | /** |
|
620 | 622 | * Programmatically select the next cell. |
|
621 | 623 | * |
|
622 | 624 | * @method select_next |
|
623 | 625 | * @return {Notebook} This notebook |
|
624 | 626 | */ |
|
625 | 627 | Notebook.prototype.select_next = function () { |
|
626 | 628 | var index = this.get_selected_index(); |
|
627 | 629 | this.select(index+1); |
|
628 | 630 | return this; |
|
629 | 631 | }; |
|
630 | 632 | |
|
631 | 633 | /** |
|
632 | 634 | * Programmatically select the previous cell. |
|
633 | 635 | * |
|
634 | 636 | * @method select_prev |
|
635 | 637 | * @return {Notebook} This notebook |
|
636 | 638 | */ |
|
637 | 639 | Notebook.prototype.select_prev = function () { |
|
638 | 640 | var index = this.get_selected_index(); |
|
639 | 641 | this.select(index-1); |
|
640 | 642 | return this; |
|
641 | 643 | }; |
|
642 | 644 | |
|
643 | 645 | |
|
644 | 646 | // Cell movement |
|
645 | 647 | |
|
646 | 648 | /** |
|
647 | 649 | * Move given (or selected) cell up and select it. |
|
648 | 650 | * |
|
649 | 651 | * @method move_cell_up |
|
650 | 652 | * @param [index] {integer} cell index |
|
651 | 653 | * @return {Notebook} This notebook |
|
652 | 654 | **/ |
|
653 | 655 | Notebook.prototype.move_cell_up = function (index) { |
|
654 | 656 | var i = this.index_or_selected(index); |
|
655 | 657 | if (this.is_valid_cell_index(i) && i > 0) { |
|
656 | 658 | var pivot = this.get_cell_element(i-1); |
|
657 | 659 | var tomove = this.get_cell_element(i); |
|
658 | 660 | if (pivot !== null && tomove !== null) { |
|
659 | 661 | tomove.detach(); |
|
660 | 662 | pivot.before(tomove); |
|
661 | 663 | this.select(i-1); |
|
662 | 664 | }; |
|
663 | 665 | this.set_dirty(true); |
|
664 | 666 | }; |
|
665 | 667 | return this; |
|
666 | 668 | }; |
|
667 | 669 | |
|
668 | 670 | |
|
669 | 671 | /** |
|
670 | 672 | * Move given (or selected) cell down and select it |
|
671 | 673 | * |
|
672 | 674 | * @method move_cell_down |
|
673 | 675 | * @param [index] {integer} cell index |
|
674 | 676 | * @return {Notebook} This notebook |
|
675 | 677 | **/ |
|
676 | 678 | Notebook.prototype.move_cell_down = function (index) { |
|
677 | 679 | var i = this.index_or_selected(index); |
|
678 | 680 | if ( this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) { |
|
679 | 681 | var pivot = this.get_cell_element(i+1); |
|
680 | 682 | var tomove = this.get_cell_element(i); |
|
681 | 683 | if (pivot !== null && tomove !== null) { |
|
682 | 684 | tomove.detach(); |
|
683 | 685 | pivot.after(tomove); |
|
684 | 686 | this.select(i+1); |
|
685 | 687 | }; |
|
686 | 688 | }; |
|
687 | 689 | this.set_dirty(); |
|
688 | 690 | return this; |
|
689 | 691 | }; |
|
690 | 692 | |
|
691 | 693 | |
|
692 | 694 | // Insertion, deletion. |
|
693 | 695 | |
|
694 | 696 | /** |
|
695 | 697 | * Delete a cell from the notebook. |
|
696 | 698 | * |
|
697 | 699 | * @method delete_cell |
|
698 | 700 | * @param [index] A cell's numeric index |
|
699 | 701 | * @return {Notebook} This notebook |
|
700 | 702 | */ |
|
701 | 703 | Notebook.prototype.delete_cell = function (index) { |
|
702 | 704 | var i = this.index_or_selected(index); |
|
703 | 705 | var cell = this.get_selected_cell(); |
|
704 | 706 | this.undelete_backup = cell.toJSON(); |
|
705 | 707 | $('#undelete_cell').removeClass('ui-state-disabled'); |
|
706 | 708 | if (this.is_valid_cell_index(i)) { |
|
707 | 709 | var ce = this.get_cell_element(i); |
|
708 | 710 | ce.remove(); |
|
709 | 711 | if (i === (this.ncells())) { |
|
710 | 712 | this.select(i-1); |
|
711 | 713 | this.undelete_index = i - 1; |
|
712 | 714 | this.undelete_below = true; |
|
713 | 715 | } else { |
|
714 | 716 | this.select(i); |
|
715 | 717 | this.undelete_index = i; |
|
716 | 718 | this.undelete_below = false; |
|
717 | 719 | }; |
|
718 | 720 | this.set_dirty(true); |
|
719 | 721 | }; |
|
720 | 722 | return this; |
|
721 | 723 | }; |
|
722 | 724 | |
|
723 | 725 | /** |
|
724 | 726 | * Insert a cell so that after insertion the cell is at given index. |
|
725 | 727 | * |
|
726 | 728 | * Similar to insert_above, but index parameter is mandatory |
|
727 | 729 | * |
|
728 | 730 | * Index will be brought back into the accissible range [0,n] |
|
729 | 731 | * |
|
730 | 732 | * @method insert_cell_at_index |
|
731 | 733 | * @param type {string} in ['code','markdown','heading'] |
|
732 | 734 | * @param [index] {int} a valid index where to inser cell |
|
733 | 735 | * |
|
734 | 736 | * @return cell {cell|null} created cell or null |
|
735 | 737 | **/ |
|
736 | 738 | Notebook.prototype.insert_cell_at_index = function(type, index){ |
|
737 | 739 | |
|
738 | 740 | var ncells = this.ncells(); |
|
739 | 741 | var index = Math.min(index,ncells); |
|
740 | 742 | index = Math.max(index,0); |
|
741 | 743 | var cell = null; |
|
742 | 744 | |
|
743 | 745 | if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) { |
|
744 | 746 | if (type === 'code') { |
|
745 | 747 | cell = new IPython.CodeCell(this.kernel); |
|
746 | 748 | cell.set_input_prompt(); |
|
747 | 749 | } else if (type === 'markdown') { |
|
748 | 750 | cell = new IPython.MarkdownCell(); |
|
749 | 751 | } else if (type === 'raw') { |
|
750 | 752 | cell = new IPython.RawCell(); |
|
751 | 753 | } else if (type === 'heading') { |
|
752 | 754 | cell = new IPython.HeadingCell(); |
|
753 | 755 | } |
|
754 | 756 | |
|
755 | 757 | if(this._insert_element_at_index(cell.element,index)){ |
|
756 | 758 | cell.render(); |
|
757 | 759 | this.select(this.find_cell_index(cell)); |
|
758 | 760 | this.set_dirty(true); |
|
759 | 761 | } |
|
760 | 762 | } |
|
761 | 763 | return cell; |
|
762 | 764 | |
|
763 | 765 | }; |
|
764 | 766 | |
|
765 | 767 | /** |
|
766 | 768 | * Insert an element at given cell index. |
|
767 | 769 | * |
|
768 | 770 | * @method _insert_element_at_index |
|
769 | 771 | * @param element {dom element} a cell element |
|
770 | 772 | * @param [index] {int} a valid index where to inser cell |
|
771 | 773 | * @private |
|
772 | 774 | * |
|
773 | 775 | * return true if everything whent fine. |
|
774 | 776 | **/ |
|
775 | 777 | Notebook.prototype._insert_element_at_index = function(element, index){ |
|
776 | 778 | if (element === undefined){ |
|
777 | 779 | return false; |
|
778 | 780 | } |
|
779 | 781 | |
|
780 | 782 | var ncells = this.ncells(); |
|
781 | 783 | |
|
782 | 784 | if (ncells === 0) { |
|
783 | 785 | // special case append if empty |
|
784 | 786 | this.element.find('div.end_space').before(element); |
|
785 | 787 | } else if ( ncells === index ) { |
|
786 | 788 | // special case append it the end, but not empty |
|
787 | 789 | this.get_cell_element(index-1).after(element); |
|
788 | 790 | } else if (this.is_valid_cell_index(index)) { |
|
789 | 791 | // otherwise always somewhere to append to |
|
790 | 792 | this.get_cell_element(index).before(element); |
|
791 | 793 | } else { |
|
792 | 794 | return false; |
|
793 | 795 | } |
|
794 | 796 | |
|
795 | 797 | if (this.undelete_index !== null && index <= this.undelete_index) { |
|
796 | 798 | this.undelete_index = this.undelete_index + 1; |
|
797 | 799 | this.set_dirty(true); |
|
798 | 800 | } |
|
799 | 801 | return true; |
|
800 | 802 | }; |
|
801 | 803 | |
|
802 | 804 | /** |
|
803 | 805 | * Insert a cell of given type above given index, or at top |
|
804 | 806 | * of notebook if index smaller than 0. |
|
805 | 807 | * |
|
806 | 808 | * default index value is the one of currently selected cell |
|
807 | 809 | * |
|
808 | 810 | * @method insert_cell_above |
|
809 | 811 | * @param type {string} cell type |
|
810 | 812 | * @param [index] {integer} |
|
811 | 813 | * |
|
812 | 814 | * @return handle to created cell or null |
|
813 | 815 | **/ |
|
814 | 816 | Notebook.prototype.insert_cell_above = function (type, index) { |
|
815 | 817 | index = this.index_or_selected(index); |
|
816 | 818 | return this.insert_cell_at_index(type, index); |
|
817 | 819 | }; |
|
818 | 820 | |
|
819 | 821 | /** |
|
820 | 822 | * Insert a cell of given type below given index, or at bottom |
|
821 | 823 | * of notebook if index greater thatn number of cell |
|
822 | 824 | * |
|
823 | 825 | * default index value is the one of currently selected cell |
|
824 | 826 | * |
|
825 | 827 | * @method insert_cell_below |
|
826 | 828 | * @param type {string} cell type |
|
827 | 829 | * @param [index] {integer} |
|
828 | 830 | * |
|
829 | 831 | * @return handle to created cell or null |
|
830 | 832 | * |
|
831 | 833 | **/ |
|
832 | 834 | Notebook.prototype.insert_cell_below = function (type, index) { |
|
833 | 835 | index = this.index_or_selected(index); |
|
834 | 836 | return this.insert_cell_at_index(type, index+1); |
|
835 | 837 | }; |
|
836 | 838 | |
|
837 | 839 | |
|
838 | 840 | /** |
|
839 | 841 | * Insert cell at end of notebook |
|
840 | 842 | * |
|
841 | 843 | * @method insert_cell_at_bottom |
|
842 | 844 | * @param {String} type cell type |
|
843 | 845 | * |
|
844 | 846 | * @return the added cell; or null |
|
845 | 847 | **/ |
|
846 | 848 | Notebook.prototype.insert_cell_at_bottom = function (type){ |
|
847 | 849 | var len = this.ncells(); |
|
848 | 850 | return this.insert_cell_below(type,len-1); |
|
849 | 851 | }; |
|
850 | 852 | |
|
851 | 853 | /** |
|
852 | 854 | * Turn a cell into a code cell. |
|
853 | 855 | * |
|
854 | 856 | * @method to_code |
|
855 | 857 | * @param {Number} [index] A cell's index |
|
856 | 858 | */ |
|
857 | 859 | Notebook.prototype.to_code = function (index) { |
|
858 | 860 | var i = this.index_or_selected(index); |
|
859 | 861 | if (this.is_valid_cell_index(i)) { |
|
860 | 862 | var source_element = this.get_cell_element(i); |
|
861 | 863 | var source_cell = source_element.data("cell"); |
|
862 | 864 | if (!(source_cell instanceof IPython.CodeCell)) { |
|
863 | 865 | var target_cell = this.insert_cell_below('code',i); |
|
864 | 866 | var text = source_cell.get_text(); |
|
865 | 867 | if (text === source_cell.placeholder) { |
|
866 | 868 | text = ''; |
|
867 | 869 | } |
|
868 | 870 | target_cell.set_text(text); |
|
869 | 871 | // make this value the starting point, so that we can only undo |
|
870 | 872 | // to this state, instead of a blank cell |
|
871 | 873 | target_cell.code_mirror.clearHistory(); |
|
872 | 874 | source_element.remove(); |
|
873 | 875 | this.set_dirty(true); |
|
874 | 876 | }; |
|
875 | 877 | }; |
|
876 | 878 | }; |
|
877 | 879 | |
|
878 | 880 | /** |
|
879 | 881 | * Turn a cell into a Markdown cell. |
|
880 | 882 | * |
|
881 | 883 | * @method to_markdown |
|
882 | 884 | * @param {Number} [index] A cell's index |
|
883 | 885 | */ |
|
884 | 886 | Notebook.prototype.to_markdown = function (index) { |
|
885 | 887 | var i = this.index_or_selected(index); |
|
886 | 888 | if (this.is_valid_cell_index(i)) { |
|
887 | 889 | var source_element = this.get_cell_element(i); |
|
888 | 890 | var source_cell = source_element.data("cell"); |
|
889 | 891 | if (!(source_cell instanceof IPython.MarkdownCell)) { |
|
890 | 892 | var target_cell = this.insert_cell_below('markdown',i); |
|
891 | 893 | var text = source_cell.get_text(); |
|
892 | 894 | if (text === source_cell.placeholder) { |
|
893 | 895 | text = ''; |
|
894 | 896 | }; |
|
895 | 897 | // The edit must come before the set_text. |
|
896 | 898 | target_cell.edit(); |
|
897 | 899 | target_cell.set_text(text); |
|
898 | 900 | // make this value the starting point, so that we can only undo |
|
899 | 901 | // to this state, instead of a blank cell |
|
900 | 902 | target_cell.code_mirror.clearHistory(); |
|
901 | 903 | source_element.remove(); |
|
902 | 904 | this.set_dirty(true); |
|
903 | 905 | }; |
|
904 | 906 | }; |
|
905 | 907 | }; |
|
906 | 908 | |
|
907 | 909 | /** |
|
908 | 910 | * Turn a cell into a raw text cell. |
|
909 | 911 | * |
|
910 | 912 | * @method to_raw |
|
911 | 913 | * @param {Number} [index] A cell's index |
|
912 | 914 | */ |
|
913 | 915 | Notebook.prototype.to_raw = function (index) { |
|
914 | 916 | var i = this.index_or_selected(index); |
|
915 | 917 | if (this.is_valid_cell_index(i)) { |
|
916 | 918 | var source_element = this.get_cell_element(i); |
|
917 | 919 | var source_cell = source_element.data("cell"); |
|
918 | 920 | var target_cell = null; |
|
919 | 921 | if (!(source_cell instanceof IPython.RawCell)) { |
|
920 | 922 | target_cell = this.insert_cell_below('raw',i); |
|
921 | 923 | var text = source_cell.get_text(); |
|
922 | 924 | if (text === source_cell.placeholder) { |
|
923 | 925 | text = ''; |
|
924 | 926 | }; |
|
925 | 927 | // The edit must come before the set_text. |
|
926 | 928 | target_cell.edit(); |
|
927 | 929 | target_cell.set_text(text); |
|
928 | 930 | // make this value the starting point, so that we can only undo |
|
929 | 931 | // to this state, instead of a blank cell |
|
930 | 932 | target_cell.code_mirror.clearHistory(); |
|
931 | 933 | source_element.remove(); |
|
932 | 934 | this.set_dirty(true); |
|
933 | 935 | }; |
|
934 | 936 | }; |
|
935 | 937 | }; |
|
936 | 938 | |
|
937 | 939 | /** |
|
938 | 940 | * Turn a cell into a heading cell. |
|
939 | 941 | * |
|
940 | 942 | * @method to_heading |
|
941 | 943 | * @param {Number} [index] A cell's index |
|
942 | 944 | * @param {Number} [level] A heading level (e.g., 1 becomes <h1>) |
|
943 | 945 | */ |
|
944 | 946 | Notebook.prototype.to_heading = function (index, level) { |
|
945 | 947 | level = level || 1; |
|
946 | 948 | var i = this.index_or_selected(index); |
|
947 | 949 | if (this.is_valid_cell_index(i)) { |
|
948 | 950 | var source_element = this.get_cell_element(i); |
|
949 | 951 | var source_cell = source_element.data("cell"); |
|
950 | 952 | var target_cell = null; |
|
951 | 953 | if (source_cell instanceof IPython.HeadingCell) { |
|
952 | 954 | source_cell.set_level(level); |
|
953 | 955 | } else { |
|
954 | 956 | target_cell = this.insert_cell_below('heading',i); |
|
955 | 957 | var text = source_cell.get_text(); |
|
956 | 958 | if (text === source_cell.placeholder) { |
|
957 | 959 | text = ''; |
|
958 | 960 | }; |
|
959 | 961 | // The edit must come before the set_text. |
|
960 | 962 | target_cell.set_level(level); |
|
961 | 963 | target_cell.edit(); |
|
962 | 964 | target_cell.set_text(text); |
|
963 | 965 | // make this value the starting point, so that we can only undo |
|
964 | 966 | // to this state, instead of a blank cell |
|
965 | 967 | target_cell.code_mirror.clearHistory(); |
|
966 | 968 | source_element.remove(); |
|
967 | 969 | this.set_dirty(true); |
|
968 | 970 | }; |
|
969 | 971 | $([IPython.events]).trigger('selected_cell_type_changed.Notebook', |
|
970 | 972 | {'cell_type':'heading',level:level} |
|
971 | 973 | ); |
|
972 | 974 | }; |
|
973 | 975 | }; |
|
974 | 976 | |
|
975 | 977 | |
|
976 | 978 | // Cut/Copy/Paste |
|
977 | 979 | |
|
978 | 980 | /** |
|
979 | 981 | * Enable UI elements for pasting cells. |
|
980 | 982 | * |
|
981 | 983 | * @method enable_paste |
|
982 | 984 | */ |
|
983 | 985 | Notebook.prototype.enable_paste = function () { |
|
984 | 986 | var that = this; |
|
985 | 987 | if (!this.paste_enabled) { |
|
986 | 988 | $('#paste_cell_replace').removeClass('ui-state-disabled') |
|
987 | 989 | .on('click', function () {that.paste_cell_replace();}); |
|
988 | 990 | $('#paste_cell_above').removeClass('ui-state-disabled') |
|
989 | 991 | .on('click', function () {that.paste_cell_above();}); |
|
990 | 992 | $('#paste_cell_below').removeClass('ui-state-disabled') |
|
991 | 993 | .on('click', function () {that.paste_cell_below();}); |
|
992 | 994 | this.paste_enabled = true; |
|
993 | 995 | }; |
|
994 | 996 | }; |
|
995 | 997 | |
|
996 | 998 | /** |
|
997 | 999 | * Disable UI elements for pasting cells. |
|
998 | 1000 | * |
|
999 | 1001 | * @method disable_paste |
|
1000 | 1002 | */ |
|
1001 | 1003 | Notebook.prototype.disable_paste = function () { |
|
1002 | 1004 | if (this.paste_enabled) { |
|
1003 | 1005 | $('#paste_cell_replace').addClass('ui-state-disabled').off('click'); |
|
1004 | 1006 | $('#paste_cell_above').addClass('ui-state-disabled').off('click'); |
|
1005 | 1007 | $('#paste_cell_below').addClass('ui-state-disabled').off('click'); |
|
1006 | 1008 | this.paste_enabled = false; |
|
1007 | 1009 | }; |
|
1008 | 1010 | }; |
|
1009 | 1011 | |
|
1010 | 1012 | /** |
|
1011 | 1013 | * Cut a cell. |
|
1012 | 1014 | * |
|
1013 | 1015 | * @method cut_cell |
|
1014 | 1016 | */ |
|
1015 | 1017 | Notebook.prototype.cut_cell = function () { |
|
1016 | 1018 | this.copy_cell(); |
|
1017 | 1019 | this.delete_cell(); |
|
1018 | 1020 | } |
|
1019 | 1021 | |
|
1020 | 1022 | /** |
|
1021 | 1023 | * Copy a cell. |
|
1022 | 1024 | * |
|
1023 | 1025 | * @method copy_cell |
|
1024 | 1026 | */ |
|
1025 | 1027 | Notebook.prototype.copy_cell = function () { |
|
1026 | 1028 | var cell = this.get_selected_cell(); |
|
1027 | 1029 | this.clipboard = cell.toJSON(); |
|
1028 | 1030 | this.enable_paste(); |
|
1029 | 1031 | }; |
|
1030 | 1032 | |
|
1031 | 1033 | /** |
|
1032 | 1034 | * Replace the selected cell with a cell in the clipboard. |
|
1033 | 1035 | * |
|
1034 | 1036 | * @method paste_cell_replace |
|
1035 | 1037 | */ |
|
1036 | 1038 | Notebook.prototype.paste_cell_replace = function () { |
|
1037 | 1039 | if (this.clipboard !== null && this.paste_enabled) { |
|
1038 | 1040 | var cell_data = this.clipboard; |
|
1039 | 1041 | var new_cell = this.insert_cell_above(cell_data.cell_type); |
|
1040 | 1042 | new_cell.fromJSON(cell_data); |
|
1041 | 1043 | var old_cell = this.get_next_cell(new_cell); |
|
1042 | 1044 | this.delete_cell(this.find_cell_index(old_cell)); |
|
1043 | 1045 | this.select(this.find_cell_index(new_cell)); |
|
1044 | 1046 | }; |
|
1045 | 1047 | }; |
|
1046 | 1048 | |
|
1047 | 1049 | /** |
|
1048 | 1050 | * Paste a cell from the clipboard above the selected cell. |
|
1049 | 1051 | * |
|
1050 | 1052 | * @method paste_cell_above |
|
1051 | 1053 | */ |
|
1052 | 1054 | Notebook.prototype.paste_cell_above = function () { |
|
1053 | 1055 | if (this.clipboard !== null && this.paste_enabled) { |
|
1054 | 1056 | var cell_data = this.clipboard; |
|
1055 | 1057 | var new_cell = this.insert_cell_above(cell_data.cell_type); |
|
1056 | 1058 | new_cell.fromJSON(cell_data); |
|
1057 | 1059 | }; |
|
1058 | 1060 | }; |
|
1059 | 1061 | |
|
1060 | 1062 | /** |
|
1061 | 1063 | * Paste a cell from the clipboard below the selected cell. |
|
1062 | 1064 | * |
|
1063 | 1065 | * @method paste_cell_below |
|
1064 | 1066 | */ |
|
1065 | 1067 | Notebook.prototype.paste_cell_below = function () { |
|
1066 | 1068 | if (this.clipboard !== null && this.paste_enabled) { |
|
1067 | 1069 | var cell_data = this.clipboard; |
|
1068 | 1070 | var new_cell = this.insert_cell_below(cell_data.cell_type); |
|
1069 | 1071 | new_cell.fromJSON(cell_data); |
|
1070 | 1072 | }; |
|
1071 | 1073 | }; |
|
1072 | 1074 | |
|
1073 | 1075 | // Cell undelete |
|
1074 | 1076 | |
|
1075 | 1077 | /** |
|
1076 | 1078 | * Restore the most recently deleted cell. |
|
1077 | 1079 | * |
|
1078 | 1080 | * @method undelete |
|
1079 | 1081 | */ |
|
1080 | 1082 | Notebook.prototype.undelete = function() { |
|
1081 | 1083 | if (this.undelete_backup !== null && this.undelete_index !== null) { |
|
1082 | 1084 | var current_index = this.get_selected_index(); |
|
1083 | 1085 | if (this.undelete_index < current_index) { |
|
1084 | 1086 | current_index = current_index + 1; |
|
1085 | 1087 | } |
|
1086 | 1088 | if (this.undelete_index >= this.ncells()) { |
|
1087 | 1089 | this.select(this.ncells() - 1); |
|
1088 | 1090 | } |
|
1089 | 1091 | else { |
|
1090 | 1092 | this.select(this.undelete_index); |
|
1091 | 1093 | } |
|
1092 | 1094 | var cell_data = this.undelete_backup; |
|
1093 | 1095 | var new_cell = null; |
|
1094 | 1096 | if (this.undelete_below) { |
|
1095 | 1097 | new_cell = this.insert_cell_below(cell_data.cell_type); |
|
1096 | 1098 | } else { |
|
1097 | 1099 | new_cell = this.insert_cell_above(cell_data.cell_type); |
|
1098 | 1100 | } |
|
1099 | 1101 | new_cell.fromJSON(cell_data); |
|
1100 | 1102 | this.select(current_index); |
|
1101 | 1103 | this.undelete_backup = null; |
|
1102 | 1104 | this.undelete_index = null; |
|
1103 | 1105 | } |
|
1104 | 1106 | $('#undelete_cell').addClass('ui-state-disabled'); |
|
1105 | 1107 | } |
|
1106 | 1108 | |
|
1107 | 1109 | // Split/merge |
|
1108 | 1110 | |
|
1109 | 1111 | /** |
|
1110 | 1112 | * Split the selected cell into two, at the cursor. |
|
1111 | 1113 | * |
|
1112 | 1114 | * @method split_cell |
|
1113 | 1115 | */ |
|
1114 | 1116 | Notebook.prototype.split_cell = function () { |
|
1115 | 1117 | // Todo: implement spliting for other cell types. |
|
1116 | 1118 | var cell = this.get_selected_cell(); |
|
1117 | 1119 | if (cell.is_splittable()) { |
|
1118 | 1120 | var texta = cell.get_pre_cursor(); |
|
1119 | 1121 | var textb = cell.get_post_cursor(); |
|
1120 | 1122 | if (cell instanceof IPython.CodeCell) { |
|
1121 | 1123 | cell.set_text(texta); |
|
1122 | 1124 | var new_cell = this.insert_cell_below('code'); |
|
1123 | 1125 | new_cell.set_text(textb); |
|
1124 | 1126 | } else if (cell instanceof IPython.MarkdownCell) { |
|
1125 | 1127 | cell.set_text(texta); |
|
1126 | 1128 | cell.render(); |
|
1127 | 1129 | var new_cell = this.insert_cell_below('markdown'); |
|
1128 | 1130 | new_cell.edit(); // editor must be visible to call set_text |
|
1129 | 1131 | new_cell.set_text(textb); |
|
1130 | 1132 | new_cell.render(); |
|
1131 | 1133 | } |
|
1132 | 1134 | }; |
|
1133 | 1135 | }; |
|
1134 | 1136 | |
|
1135 | 1137 | /** |
|
1136 | 1138 | * Combine the selected cell into the cell above it. |
|
1137 | 1139 | * |
|
1138 | 1140 | * @method merge_cell_above |
|
1139 | 1141 | */ |
|
1140 | 1142 | Notebook.prototype.merge_cell_above = function () { |
|
1141 | 1143 | var index = this.get_selected_index(); |
|
1142 | 1144 | var cell = this.get_cell(index); |
|
1143 | 1145 | if (index > 0) { |
|
1144 | 1146 | var upper_cell = this.get_cell(index-1); |
|
1145 | 1147 | var upper_text = upper_cell.get_text(); |
|
1146 | 1148 | var text = cell.get_text(); |
|
1147 | 1149 | if (cell instanceof IPython.CodeCell) { |
|
1148 | 1150 | cell.set_text(upper_text+'\n'+text); |
|
1149 | 1151 | } else if (cell instanceof IPython.MarkdownCell) { |
|
1150 | 1152 | cell.edit(); |
|
1151 | 1153 | cell.set_text(upper_text+'\n'+text); |
|
1152 | 1154 | cell.render(); |
|
1153 | 1155 | }; |
|
1154 | 1156 | this.delete_cell(index-1); |
|
1155 | 1157 | this.select(this.find_cell_index(cell)); |
|
1156 | 1158 | }; |
|
1157 | 1159 | }; |
|
1158 | 1160 | |
|
1159 | 1161 | /** |
|
1160 | 1162 | * Combine the selected cell into the cell below it. |
|
1161 | 1163 | * |
|
1162 | 1164 | * @method merge_cell_below |
|
1163 | 1165 | */ |
|
1164 | 1166 | Notebook.prototype.merge_cell_below = function () { |
|
1165 | 1167 | var index = this.get_selected_index(); |
|
1166 | 1168 | var cell = this.get_cell(index); |
|
1167 | 1169 | if (index < this.ncells()-1) { |
|
1168 | 1170 | var lower_cell = this.get_cell(index+1); |
|
1169 | 1171 | var lower_text = lower_cell.get_text(); |
|
1170 | 1172 | var text = cell.get_text(); |
|
1171 | 1173 | if (cell instanceof IPython.CodeCell) { |
|
1172 | 1174 | cell.set_text(text+'\n'+lower_text); |
|
1173 | 1175 | } else if (cell instanceof IPython.MarkdownCell) { |
|
1174 | 1176 | cell.edit(); |
|
1175 | 1177 | cell.set_text(text+'\n'+lower_text); |
|
1176 | 1178 | cell.render(); |
|
1177 | 1179 | }; |
|
1178 | 1180 | this.delete_cell(index+1); |
|
1179 | 1181 | this.select(this.find_cell_index(cell)); |
|
1180 | 1182 | }; |
|
1181 | 1183 | }; |
|
1182 | 1184 | |
|
1183 | 1185 | |
|
1184 | 1186 | // Cell collapsing and output clearing |
|
1185 | 1187 | |
|
1186 | 1188 | /** |
|
1187 | 1189 | * Hide a cell's output. |
|
1188 | 1190 | * |
|
1189 | 1191 | * @method collapse |
|
1190 | 1192 | * @param {Number} index A cell's numeric index |
|
1191 | 1193 | */ |
|
1192 | 1194 | Notebook.prototype.collapse = function (index) { |
|
1193 | 1195 | var i = this.index_or_selected(index); |
|
1194 | 1196 | this.get_cell(i).collapse(); |
|
1195 | 1197 | this.set_dirty(true); |
|
1196 | 1198 | }; |
|
1197 | 1199 | |
|
1198 | 1200 | /** |
|
1199 | 1201 | * Show a cell's output. |
|
1200 | 1202 | * |
|
1201 | 1203 | * @method expand |
|
1202 | 1204 | * @param {Number} index A cell's numeric index |
|
1203 | 1205 | */ |
|
1204 | 1206 | Notebook.prototype.expand = function (index) { |
|
1205 | 1207 | var i = this.index_or_selected(index); |
|
1206 | 1208 | this.get_cell(i).expand(); |
|
1207 | 1209 | this.set_dirty(true); |
|
1208 | 1210 | }; |
|
1209 | 1211 | |
|
1210 | 1212 | /** Toggle whether a cell's output is collapsed or expanded. |
|
1211 | 1213 | * |
|
1212 | 1214 | * @method toggle_output |
|
1213 | 1215 | * @param {Number} index A cell's numeric index |
|
1214 | 1216 | */ |
|
1215 | 1217 | Notebook.prototype.toggle_output = function (index) { |
|
1216 | 1218 | var i = this.index_or_selected(index); |
|
1217 | 1219 | this.get_cell(i).toggle_output(); |
|
1218 | 1220 | this.set_dirty(true); |
|
1219 | 1221 | }; |
|
1220 | 1222 | |
|
1221 | 1223 | /** |
|
1222 | 1224 | * Toggle a scrollbar for long cell outputs. |
|
1223 | 1225 | * |
|
1224 | 1226 | * @method toggle_output_scroll |
|
1225 | 1227 | * @param {Number} index A cell's numeric index |
|
1226 | 1228 | */ |
|
1227 | 1229 | Notebook.prototype.toggle_output_scroll = function (index) { |
|
1228 | 1230 | var i = this.index_or_selected(index); |
|
1229 | 1231 | this.get_cell(i).toggle_output_scroll(); |
|
1230 | 1232 | }; |
|
1231 | 1233 | |
|
1232 | 1234 | /** |
|
1233 | 1235 | * Hide each code cell's output area. |
|
1234 | 1236 | * |
|
1235 | 1237 | * @method collapse_all_output |
|
1236 | 1238 | */ |
|
1237 | 1239 | Notebook.prototype.collapse_all_output = function () { |
|
1238 | 1240 | var ncells = this.ncells(); |
|
1239 | 1241 | var cells = this.get_cells(); |
|
1240 | 1242 | for (var i=0; i<ncells; i++) { |
|
1241 | 1243 | if (cells[i] instanceof IPython.CodeCell) { |
|
1242 | 1244 | cells[i].output_area.collapse(); |
|
1243 | 1245 | } |
|
1244 | 1246 | }; |
|
1245 | 1247 | // this should not be set if the `collapse` key is removed from nbformat |
|
1246 | 1248 | this.set_dirty(true); |
|
1247 | 1249 | }; |
|
1248 | 1250 | |
|
1249 | 1251 | /** |
|
1250 | 1252 | * Expand each code cell's output area, and add a scrollbar for long output. |
|
1251 | 1253 | * |
|
1252 | 1254 | * @method scroll_all_output |
|
1253 | 1255 | */ |
|
1254 | 1256 | Notebook.prototype.scroll_all_output = function () { |
|
1255 | 1257 | var ncells = this.ncells(); |
|
1256 | 1258 | var cells = this.get_cells(); |
|
1257 | 1259 | for (var i=0; i<ncells; i++) { |
|
1258 | 1260 | if (cells[i] instanceof IPython.CodeCell) { |
|
1259 | 1261 | cells[i].output_area.expand(); |
|
1260 | 1262 | cells[i].output_area.scroll_if_long(); |
|
1261 | 1263 | } |
|
1262 | 1264 | }; |
|
1263 | 1265 | // this should not be set if the `collapse` key is removed from nbformat |
|
1264 | 1266 | this.set_dirty(true); |
|
1265 | 1267 | }; |
|
1266 | 1268 | |
|
1267 | 1269 | /** |
|
1268 | 1270 | * Expand each code cell's output area, and remove scrollbars. |
|
1269 | 1271 | * |
|
1270 | 1272 | * @method expand_all_output |
|
1271 | 1273 | */ |
|
1272 | 1274 | Notebook.prototype.expand_all_output = function () { |
|
1273 | 1275 | var ncells = this.ncells(); |
|
1274 | 1276 | var cells = this.get_cells(); |
|
1275 | 1277 | for (var i=0; i<ncells; i++) { |
|
1276 | 1278 | if (cells[i] instanceof IPython.CodeCell) { |
|
1277 | 1279 | cells[i].output_area.expand(); |
|
1278 | 1280 | cells[i].output_area.unscroll_area(); |
|
1279 | 1281 | } |
|
1280 | 1282 | }; |
|
1281 | 1283 | // this should not be set if the `collapse` key is removed from nbformat |
|
1282 | 1284 | this.set_dirty(true); |
|
1283 | 1285 | }; |
|
1284 | 1286 | |
|
1285 | 1287 | /** |
|
1286 | 1288 | * Clear each code cell's output area. |
|
1287 | 1289 | * |
|
1288 | 1290 | * @method clear_all_output |
|
1289 | 1291 | */ |
|
1290 | 1292 | Notebook.prototype.clear_all_output = function () { |
|
1291 | 1293 | var ncells = this.ncells(); |
|
1292 | 1294 | var cells = this.get_cells(); |
|
1293 | 1295 | for (var i=0; i<ncells; i++) { |
|
1294 | 1296 | if (cells[i] instanceof IPython.CodeCell) { |
|
1295 | 1297 | cells[i].clear_output(true,true,true); |
|
1296 | 1298 | // Make all In[] prompts blank, as well |
|
1297 | 1299 | // TODO: make this configurable (via checkbox?) |
|
1298 | 1300 | cells[i].set_input_prompt(); |
|
1299 | 1301 | } |
|
1300 | 1302 | }; |
|
1301 | 1303 | this.set_dirty(true); |
|
1302 | 1304 | }; |
|
1303 | 1305 | |
|
1304 | 1306 | |
|
1305 | 1307 | // Other cell functions: line numbers, ... |
|
1306 | 1308 | |
|
1307 | 1309 | /** |
|
1308 | 1310 | * Toggle line numbers in the selected cell's input area. |
|
1309 | 1311 | * |
|
1310 | 1312 | * @method cell_toggle_line_numbers |
|
1311 | 1313 | */ |
|
1312 | 1314 | Notebook.prototype.cell_toggle_line_numbers = function() { |
|
1313 | 1315 | this.get_selected_cell().toggle_line_numbers(); |
|
1314 | 1316 | }; |
|
1315 | 1317 | |
|
1316 | 1318 | // Kernel related things |
|
1317 | 1319 | |
|
1318 | 1320 | /** |
|
1319 | 1321 | * Start a new kernel and set it on each code cell. |
|
1320 | 1322 | * |
|
1321 | 1323 | * @method start_kernel |
|
1322 | 1324 | */ |
|
1323 | 1325 | Notebook.prototype.start_kernel = function () { |
|
1324 | 1326 | var base_url = $('body').data('baseKernelUrl') + "kernels"; |
|
1325 | 1327 | this.kernel = new IPython.Kernel(base_url); |
|
1326 | 1328 | this.kernel.start(this.notebook_id); |
|
1327 | 1329 | // Now that the kernel has been created, tell the CodeCells about it. |
|
1328 | 1330 | var ncells = this.ncells(); |
|
1329 | 1331 | for (var i=0; i<ncells; i++) { |
|
1330 | 1332 | var cell = this.get_cell(i); |
|
1331 | 1333 | if (cell instanceof IPython.CodeCell) { |
|
1332 | 1334 | cell.set_kernel(this.kernel) |
|
1333 | 1335 | }; |
|
1334 | 1336 | }; |
|
1335 | 1337 | }; |
|
1336 | 1338 | |
|
1337 | 1339 | /** |
|
1338 | 1340 | * Prompt the user to restart the IPython kernel. |
|
1339 | 1341 | * |
|
1340 | 1342 | * @method restart_kernel |
|
1341 | 1343 | */ |
|
1342 | 1344 | Notebook.prototype.restart_kernel = function () { |
|
1343 | 1345 | var that = this; |
|
1344 | var dialog = $('<div/>'); | |
|
1345 | dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.'); | |
|
1346 | $(document).append(dialog); | |
|
1347 | dialog.dialog({ | |
|
1348 |
|
|
|
1349 | modal: true, | |
|
1350 | title: "Restart kernel or continue running?", | |
|
1351 | closeText: '', | |
|
1346 | IPython.dialog.modal({ | |
|
1347 | title : "Restart kernel or continue running?", | |
|
1348 | body : $("<p/>").html( | |
|
1349 | 'Do you want to restart the current kernel? You will lose all variables defined in it.' | |
|
1350 | ), | |
|
1352 | 1351 | buttons : { |
|
1353 | "Restart": function () { | |
|
1354 |
|
|
|
1355 | $(this).dialog('close'); | |
|
1356 | }, | |
|
1357 | "Continue running": function () { | |
|
1358 |
|
|
|
1352 | "Continue running" : {}, | |
|
1353 | "Restart" : { | |
|
1354 | "class" : "btn-danger", | |
|
1355 | "click" : function() { | |
|
1356 | that.kernel.restart(); | |
|
1357 | } | |
|
1359 | 1358 | } |
|
1360 | 1359 | } |
|
1361 | 1360 | }); |
|
1362 | 1361 | }; |
|
1363 | 1362 | |
|
1364 | 1363 | /** |
|
1365 | 1364 | * Run the selected cell. |
|
1366 | 1365 | * |
|
1367 | 1366 | * Execute or render cell outputs. |
|
1368 | 1367 | * |
|
1369 | 1368 | * @method execute_selected_cell |
|
1370 | 1369 | * @param {Object} options Customize post-execution behavior |
|
1371 | 1370 | */ |
|
1372 | 1371 | Notebook.prototype.execute_selected_cell = function (options) { |
|
1373 | 1372 | // add_new: should a new cell be added if we are at the end of the nb |
|
1374 | 1373 | // terminal: execute in terminal mode, which stays in the current cell |
|
1375 | 1374 | var default_options = {terminal: false, add_new: true}; |
|
1376 | 1375 | $.extend(default_options, options); |
|
1377 | 1376 | var that = this; |
|
1378 | 1377 | var cell = that.get_selected_cell(); |
|
1379 | 1378 | var cell_index = that.find_cell_index(cell); |
|
1380 | 1379 | if (cell instanceof IPython.CodeCell) { |
|
1381 | 1380 | cell.execute(); |
|
1382 | 1381 | } |
|
1383 | 1382 | if (default_options.terminal) { |
|
1384 | 1383 | cell.select_all(); |
|
1385 | 1384 | } else { |
|
1386 | 1385 | if ((cell_index === (that.ncells()-1)) && default_options.add_new) { |
|
1387 | 1386 | that.insert_cell_below('code'); |
|
1388 | 1387 | // If we are adding a new cell at the end, scroll down to show it. |
|
1389 | 1388 | that.scroll_to_bottom(); |
|
1390 | 1389 | } else { |
|
1391 | 1390 | that.select(cell_index+1); |
|
1392 | 1391 | }; |
|
1393 | 1392 | }; |
|
1394 | 1393 | this.set_dirty(true); |
|
1395 | 1394 | }; |
|
1396 | 1395 | |
|
1397 | 1396 | /** |
|
1398 | 1397 | * Execute all cells below the selected cell. |
|
1399 | 1398 | * |
|
1400 | 1399 | * @method execute_cells_below |
|
1401 | 1400 | */ |
|
1402 | 1401 | Notebook.prototype.execute_cells_below = function () { |
|
1403 | 1402 | this.execute_cell_range(this.get_selected_index(), this.ncells()); |
|
1404 | 1403 | this.scroll_to_bottom(); |
|
1405 | 1404 | }; |
|
1406 | 1405 | |
|
1407 | 1406 | /** |
|
1408 | 1407 | * Execute all cells above the selected cell. |
|
1409 | 1408 | * |
|
1410 | 1409 | * @method execute_cells_above |
|
1411 | 1410 | */ |
|
1412 | 1411 | Notebook.prototype.execute_cells_above = function () { |
|
1413 | 1412 | this.execute_cell_range(0, this.get_selected_index()); |
|
1414 | 1413 | }; |
|
1415 | 1414 | |
|
1416 | 1415 | /** |
|
1417 | 1416 | * Execute all cells. |
|
1418 | 1417 | * |
|
1419 | 1418 | * @method execute_all_cells |
|
1420 | 1419 | */ |
|
1421 | 1420 | Notebook.prototype.execute_all_cells = function () { |
|
1422 | 1421 | this.execute_cell_range(0, this.ncells()); |
|
1423 | 1422 | this.scroll_to_bottom(); |
|
1424 | 1423 | }; |
|
1425 | 1424 | |
|
1426 | 1425 | /** |
|
1427 | 1426 | * Execute a contiguous range of cells. |
|
1428 | 1427 | * |
|
1429 | 1428 | * @method execute_cell_range |
|
1430 | 1429 | * @param {Number} start Index of the first cell to execute (inclusive) |
|
1431 | 1430 | * @param {Number} end Index of the last cell to execute (exclusive) |
|
1432 | 1431 | */ |
|
1433 | 1432 | Notebook.prototype.execute_cell_range = function (start, end) { |
|
1434 | 1433 | for (var i=start; i<end; i++) { |
|
1435 | 1434 | this.select(i); |
|
1436 | 1435 | this.execute_selected_cell({add_new:false}); |
|
1437 | 1436 | }; |
|
1438 | 1437 | }; |
|
1439 | 1438 | |
|
1440 | 1439 | // Persistance and loading |
|
1441 | 1440 | |
|
1442 | 1441 | /** |
|
1443 | 1442 | * Getter method for this notebook's ID. |
|
1444 | 1443 | * |
|
1445 | 1444 | * @method get_notebook_id |
|
1446 | 1445 | * @return {String} This notebook's ID |
|
1447 | 1446 | */ |
|
1448 | 1447 | Notebook.prototype.get_notebook_id = function () { |
|
1449 | 1448 | return this.notebook_id; |
|
1450 | 1449 | }; |
|
1451 | 1450 | |
|
1452 | 1451 | /** |
|
1453 | 1452 | * Getter method for this notebook's name. |
|
1454 | 1453 | * |
|
1455 | 1454 | * @method get_notebook_name |
|
1456 | 1455 | * @return {String} This notebook's name |
|
1457 | 1456 | */ |
|
1458 | 1457 | Notebook.prototype.get_notebook_name = function () { |
|
1459 | 1458 | return this.notebook_name; |
|
1460 | 1459 | }; |
|
1461 | 1460 | |
|
1462 | 1461 | /** |
|
1463 | 1462 | * Setter method for this notebook's name. |
|
1464 | 1463 | * |
|
1465 | 1464 | * @method set_notebook_name |
|
1466 | 1465 | * @param {String} name A new name for this notebook |
|
1467 | 1466 | */ |
|
1468 | 1467 | Notebook.prototype.set_notebook_name = function (name) { |
|
1469 | 1468 | this.notebook_name = name; |
|
1470 | 1469 | }; |
|
1471 | 1470 | |
|
1472 | 1471 | /** |
|
1473 | 1472 | * Check that a notebook's name is valid. |
|
1474 | 1473 | * |
|
1475 | 1474 | * @method test_notebook_name |
|
1476 | 1475 | * @param {String} nbname A name for this notebook |
|
1477 | 1476 | * @return {Boolean} True if the name is valid, false if invalid |
|
1478 | 1477 | */ |
|
1479 | 1478 | Notebook.prototype.test_notebook_name = function (nbname) { |
|
1480 | 1479 | nbname = nbname || ''; |
|
1481 | 1480 | if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) { |
|
1482 | 1481 | return true; |
|
1483 | 1482 | } else { |
|
1484 | 1483 | return false; |
|
1485 | 1484 | }; |
|
1486 | 1485 | }; |
|
1487 | 1486 | |
|
1488 | 1487 | /** |
|
1489 | 1488 | * Load a notebook from JSON (.ipynb). |
|
1490 | 1489 | * |
|
1491 | 1490 | * This currently handles one worksheet: others are deleted. |
|
1492 | 1491 | * |
|
1493 | 1492 | * @method fromJSON |
|
1494 | 1493 | * @param {Object} data JSON representation of a notebook |
|
1495 | 1494 | */ |
|
1496 | 1495 | Notebook.prototype.fromJSON = function (data) { |
|
1497 | 1496 | var ncells = this.ncells(); |
|
1498 | 1497 | var i; |
|
1499 | 1498 | for (i=0; i<ncells; i++) { |
|
1500 | 1499 | // Always delete cell 0 as they get renumbered as they are deleted. |
|
1501 | 1500 | this.delete_cell(0); |
|
1502 | 1501 | }; |
|
1503 | 1502 | // Save the metadata and name. |
|
1504 | 1503 | this.metadata = data.metadata; |
|
1505 | 1504 | this.notebook_name = data.metadata.name; |
|
1506 | 1505 | // Only handle 1 worksheet for now. |
|
1507 | 1506 | var worksheet = data.worksheets[0]; |
|
1508 | 1507 | if (worksheet !== undefined) { |
|
1509 | 1508 | if (worksheet.metadata) { |
|
1510 | 1509 | this.worksheet_metadata = worksheet.metadata; |
|
1511 | 1510 | } |
|
1512 | 1511 | var new_cells = worksheet.cells; |
|
1513 | 1512 | ncells = new_cells.length; |
|
1514 | 1513 | var cell_data = null; |
|
1515 | 1514 | var new_cell = null; |
|
1516 | 1515 | for (i=0; i<ncells; i++) { |
|
1517 | 1516 | cell_data = new_cells[i]; |
|
1518 | 1517 | // VERSIONHACK: plaintext -> raw |
|
1519 | 1518 | // handle never-released plaintext name for raw cells |
|
1520 | 1519 | if (cell_data.cell_type === 'plaintext'){ |
|
1521 | 1520 | cell_data.cell_type = 'raw'; |
|
1522 | 1521 | } |
|
1523 | 1522 | |
|
1524 | 1523 | new_cell = this.insert_cell_below(cell_data.cell_type); |
|
1525 | 1524 | new_cell.fromJSON(cell_data); |
|
1526 | 1525 | }; |
|
1527 | 1526 | }; |
|
1528 | 1527 | if (data.worksheets.length > 1) { |
|
1529 | var dialog = $('<div/>'); | |
|
1530 | dialog.html("This notebook has " + data.worksheets.length + " worksheets, " + | |
|
1531 | "but this version of IPython can only handle the first. " + | |
|
1532 | "If you save this notebook, worksheets after the first will be lost." | |
|
1533 | ); | |
|
1534 | this.element.append(dialog); | |
|
1535 | dialog.dialog({ | |
|
1536 | resizable: false, | |
|
1537 | modal: true, | |
|
1538 | title: "Multiple worksheets", | |
|
1539 | closeText: "", | |
|
1540 | close: function(event, ui) {$(this).dialog('destroy').remove();}, | |
|
1528 | IPython.dialog.modal({ | |
|
1529 | title : "Multiple worksheets", | |
|
1530 | body : "This notebook has " + data.worksheets.length + " worksheets, " + | |
|
1531 | "but this version of IPython can only handle the first. " + | |
|
1532 | "If you save this notebook, worksheets after the first will be lost.", | |
|
1541 | 1533 | buttons : { |
|
1542 |
|
|
|
1543 |
|
|
|
1534 | OK : { | |
|
1535 | class : "btn-danger" | |
|
1544 | 1536 | } |
|
1545 |
} |
|
|
1546 | width: 400 | |
|
1537 | } | |
|
1547 | 1538 | }); |
|
1548 | 1539 | } |
|
1549 | 1540 | }; |
|
1550 | 1541 | |
|
1551 | 1542 | /** |
|
1552 | 1543 | * Dump this notebook into a JSON-friendly object. |
|
1553 | 1544 | * |
|
1554 | 1545 | * @method toJSON |
|
1555 | 1546 | * @return {Object} A JSON-friendly representation of this notebook. |
|
1556 | 1547 | */ |
|
1557 | 1548 | Notebook.prototype.toJSON = function () { |
|
1558 | 1549 | var cells = this.get_cells(); |
|
1559 | 1550 | var ncells = cells.length; |
|
1560 | 1551 | var cell_array = new Array(ncells); |
|
1561 | 1552 | for (var i=0; i<ncells; i++) { |
|
1562 | 1553 | cell_array[i] = cells[i].toJSON(); |
|
1563 | 1554 | }; |
|
1564 | 1555 | var data = { |
|
1565 | 1556 | // Only handle 1 worksheet for now. |
|
1566 | 1557 | worksheets : [{ |
|
1567 | 1558 | cells: cell_array, |
|
1568 | 1559 | metadata: this.worksheet_metadata |
|
1569 | 1560 | }], |
|
1570 | 1561 | metadata : this.metadata |
|
1571 | 1562 | }; |
|
1572 | 1563 | return data; |
|
1573 | 1564 | }; |
|
1574 | 1565 | |
|
1575 | 1566 | /** |
|
1576 | 1567 | * Start an autosave timer, for periodically saving the notebook. |
|
1577 | 1568 | * |
|
1578 | 1569 | * @method set_autosave_interval |
|
1579 | 1570 | * @param {Integer} interval the autosave interval in milliseconds |
|
1580 | 1571 | */ |
|
1581 | 1572 | Notebook.prototype.set_autosave_interval = function (interval) { |
|
1582 | 1573 | var that = this; |
|
1583 | 1574 | // clear previous interval, so we don't get simultaneous timers |
|
1584 | 1575 | if (this.autosave_timer) { |
|
1585 | 1576 | clearInterval(this.autosave_timer); |
|
1586 | 1577 | } |
|
1587 | 1578 | |
|
1588 | 1579 | this.autosave_interval = this.minimum_autosave_interval = interval; |
|
1589 | 1580 | if (interval) { |
|
1590 | 1581 | this.autosave_timer = setInterval(function() { |
|
1591 | 1582 | if (that.dirty) { |
|
1592 | 1583 | that.save_notebook(); |
|
1593 | 1584 | } |
|
1594 | 1585 | }, interval); |
|
1595 | 1586 | $([IPython.events]).trigger("autosave_enabled.Notebook", interval); |
|
1596 | 1587 | } else { |
|
1597 | 1588 | this.autosave_timer = null; |
|
1598 | 1589 | $([IPython.events]).trigger("autosave_disabled.Notebook"); |
|
1599 | 1590 | }; |
|
1600 | 1591 | }; |
|
1601 | 1592 | |
|
1602 | 1593 | /** |
|
1603 | 1594 | * Save this notebook on the server. |
|
1604 | 1595 | * |
|
1605 | 1596 | * @method save_notebook |
|
1606 | 1597 | */ |
|
1607 | 1598 | Notebook.prototype.save_notebook = function () { |
|
1608 | 1599 | // We may want to move the name/id/nbformat logic inside toJSON? |
|
1609 | 1600 | var data = this.toJSON(); |
|
1610 | 1601 | data.metadata.name = this.notebook_name; |
|
1611 | 1602 | data.nbformat = this.nbformat; |
|
1612 | 1603 | data.nbformat_minor = this.nbformat_minor; |
|
1613 | 1604 | |
|
1614 | 1605 | // time the ajax call for autosave tuning purposes. |
|
1615 | 1606 | var start = new Date().getTime(); |
|
1616 | 1607 | |
|
1617 | 1608 | // We do the call with settings so we can set cache to false. |
|
1618 | 1609 | var settings = { |
|
1619 | 1610 | processData : false, |
|
1620 | 1611 | cache : false, |
|
1621 | 1612 | type : "PUT", |
|
1622 | 1613 | data : JSON.stringify(data), |
|
1623 | 1614 | headers : {'Content-Type': 'application/json'}, |
|
1624 | 1615 | success : $.proxy(this.save_notebook_success, this, start), |
|
1625 | 1616 | error : $.proxy(this.save_notebook_error, this) |
|
1626 | 1617 | }; |
|
1627 | 1618 | $([IPython.events]).trigger('notebook_saving.Notebook'); |
|
1628 | 1619 | var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id; |
|
1629 | 1620 | $.ajax(url, settings); |
|
1630 | 1621 | }; |
|
1631 | 1622 | |
|
1632 | 1623 | /** |
|
1633 | 1624 | * Success callback for saving a notebook. |
|
1634 | 1625 | * |
|
1635 | 1626 | * @method save_notebook_success |
|
1636 | 1627 | * @param {Integer} start the time when the save request started |
|
1637 | 1628 | * @param {Object} data JSON representation of a notebook |
|
1638 | 1629 | * @param {String} status Description of response status |
|
1639 | 1630 | * @param {jqXHR} xhr jQuery Ajax object |
|
1640 | 1631 | */ |
|
1641 | 1632 | Notebook.prototype.save_notebook_success = function (start, data, status, xhr) { |
|
1642 | 1633 | this.set_dirty(false); |
|
1643 | 1634 | $([IPython.events]).trigger('notebook_saved.Notebook'); |
|
1644 | 1635 | this._update_autosave_interval(start); |
|
1645 | 1636 | if (this._checkpoint_after_save) { |
|
1646 | 1637 | this.create_checkpoint(); |
|
1647 | 1638 | this._checkpoint_after_save = false; |
|
1648 | 1639 | }; |
|
1649 | 1640 | }; |
|
1650 | 1641 | |
|
1651 | 1642 | /** |
|
1652 | 1643 | * update the autosave interval based on how long the last save took |
|
1653 | 1644 | * |
|
1654 | 1645 | * @method _update_autosave_interval |
|
1655 | 1646 | * @param {Integer} timestamp when the save request started |
|
1656 | 1647 | */ |
|
1657 | 1648 | Notebook.prototype._update_autosave_interval = function (start) { |
|
1658 | 1649 | var duration = (new Date().getTime() - start); |
|
1659 | 1650 | if (this.autosave_interval) { |
|
1660 | 1651 | // new save interval: higher of 10x save duration or parameter (default 30 seconds) |
|
1661 | 1652 | var interval = Math.max(10 * duration, this.minimum_autosave_interval); |
|
1662 | 1653 | // round to 10 seconds, otherwise we will be setting a new interval too often |
|
1663 | 1654 | interval = 10000 * Math.round(interval / 10000); |
|
1664 | 1655 | // set new interval, if it's changed |
|
1665 | 1656 | if (interval != this.autosave_interval) { |
|
1666 | 1657 | this.set_autosave_interval(interval); |
|
1667 | 1658 | } |
|
1668 | 1659 | } |
|
1669 | 1660 | }; |
|
1670 | 1661 | |
|
1671 | 1662 | /** |
|
1672 | 1663 | * Failure callback for saving a notebook. |
|
1673 | 1664 | * |
|
1674 | 1665 | * @method save_notebook_error |
|
1675 | 1666 | * @param {jqXHR} xhr jQuery Ajax object |
|
1676 | 1667 | * @param {String} status Description of response status |
|
1677 | 1668 | * @param {String} error_msg HTTP error message |
|
1678 | 1669 | */ |
|
1679 | 1670 | Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) { |
|
1680 | 1671 | $([IPython.events]).trigger('notebook_save_failed.Notebook'); |
|
1681 | 1672 | }; |
|
1682 | 1673 | |
|
1683 | 1674 | /** |
|
1684 | 1675 | * Request a notebook's data from the server. |
|
1685 | 1676 | * |
|
1686 | 1677 | * @method load_notebook |
|
1687 | 1678 | * @param {String} notebook_id A notebook to load |
|
1688 | 1679 | */ |
|
1689 | 1680 | Notebook.prototype.load_notebook = function (notebook_id) { |
|
1690 | 1681 | var that = this; |
|
1691 | 1682 | this.notebook_id = notebook_id; |
|
1692 | 1683 | // We do the call with settings so we can set cache to false. |
|
1693 | 1684 | var settings = { |
|
1694 | 1685 | processData : false, |
|
1695 | 1686 | cache : false, |
|
1696 | 1687 | type : "GET", |
|
1697 | 1688 | dataType : "json", |
|
1698 | 1689 | success : $.proxy(this.load_notebook_success,this), |
|
1699 | 1690 | error : $.proxy(this.load_notebook_error,this), |
|
1700 | 1691 | }; |
|
1701 | 1692 | $([IPython.events]).trigger('notebook_loading.Notebook'); |
|
1702 | 1693 | var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id; |
|
1703 | 1694 | $.ajax(url, settings); |
|
1704 | 1695 | }; |
|
1705 | 1696 | |
|
1706 | 1697 | /** |
|
1707 | 1698 | * Success callback for loading a notebook from the server. |
|
1708 | 1699 | * |
|
1709 | 1700 | * Load notebook data from the JSON response. |
|
1710 | 1701 | * |
|
1711 | 1702 | * @method load_notebook_success |
|
1712 | 1703 | * @param {Object} data JSON representation of a notebook |
|
1713 | 1704 | * @param {String} status Description of response status |
|
1714 | 1705 | * @param {jqXHR} xhr jQuery Ajax object |
|
1715 | 1706 | */ |
|
1716 | 1707 | Notebook.prototype.load_notebook_success = function (data, status, xhr) { |
|
1717 | 1708 | this.fromJSON(data); |
|
1718 | 1709 | if (this.ncells() === 0) { |
|
1719 | 1710 | this.insert_cell_below('code'); |
|
1720 | 1711 | }; |
|
1721 | 1712 | this.set_dirty(false); |
|
1722 | 1713 | this.select(0); |
|
1723 | 1714 | this.scroll_to_top(); |
|
1724 | 1715 | if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) { |
|
1725 | msg = "This notebook has been converted from an older " + | |
|
1716 | var msg = "This notebook has been converted from an older " + | |
|
1726 | 1717 | "notebook format (v"+data.orig_nbformat+") to the current notebook " + |
|
1727 | 1718 | "format (v"+data.nbformat+"). The next time you save this notebook, the " + |
|
1728 | "newer notebook format will be used and older verions of IPython " + | |
|
1719 | "newer notebook format will be used and older versions of IPython " + | |
|
1729 | 1720 | "may not be able to read it. To keep the older version, close the " + |
|
1730 | 1721 | "notebook without saving it."; |
|
1731 | var dialog = $('<div/>'); | |
|
1732 | dialog.html(msg); | |
|
1733 | this.element.append(dialog); | |
|
1734 | dialog.dialog({ | |
|
1735 | resizable: false, | |
|
1736 | modal: true, | |
|
1737 | title: "Notebook converted", | |
|
1738 | closeText: "", | |
|
1739 | close: function(event, ui) {$(this).dialog('destroy').remove();}, | |
|
1722 | IPython.dialog.modal({ | |
|
1723 | title : "Notebook converted", | |
|
1724 | body : msg, | |
|
1740 | 1725 | buttons : { |
|
1741 |
|
|
|
1742 | $(this).dialog('close'); | |
|
1726 | OK : { | |
|
1727 | class : "btn-primary" | |
|
1743 | 1728 | } |
|
1744 |
} |
|
|
1745 | width: 400 | |
|
1729 | } | |
|
1746 | 1730 | }); |
|
1747 | 1731 | } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) { |
|
1748 | 1732 | var that = this; |
|
1749 | 1733 | var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor; |
|
1750 | 1734 | var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor; |
|
1751 | 1735 | var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " + |
|
1752 | 1736 | this_vs + ". You can still work with this notebook, but some features " + |
|
1753 | 1737 | "introduced in later notebook versions may not be available." |
|
1754 | 1738 | |
|
1755 | var dialog = $('<div/>'); | |
|
1756 | dialog.html(msg); | |
|
1757 | this.element.append(dialog); | |
|
1758 | dialog.dialog({ | |
|
1759 | resizable: false, | |
|
1760 | modal: true, | |
|
1761 | title: "Newer Notebook", | |
|
1762 | closeText: "", | |
|
1763 | close: function(event, ui) {$(this).dialog('destroy').remove();}, | |
|
1739 | IPython.dialog.modal({ | |
|
1740 | title : "Newer Notebook", | |
|
1741 | body : msg, | |
|
1764 | 1742 | buttons : { |
|
1765 |
|
|
|
1766 |
|
|
|
1743 | OK : { | |
|
1744 | class : "btn-danger" | |
|
1767 | 1745 | } |
|
1768 |
} |
|
|
1769 | width: 400 | |
|
1746 | } | |
|
1770 | 1747 | }); |
|
1771 | 1748 | |
|
1772 | 1749 | } |
|
1773 | 1750 | |
|
1774 | 1751 | // Create the kernel after the notebook is completely loaded to prevent |
|
1775 | 1752 | // code execution upon loading, which is a security risk. |
|
1776 | 1753 | if (! this.read_only) { |
|
1777 | 1754 | this.start_kernel(); |
|
1778 | 1755 | // load our checkpoint list |
|
1779 | 1756 | IPython.notebook.list_checkpoints(); |
|
1780 | 1757 | } |
|
1781 | 1758 | $([IPython.events]).trigger('notebook_loaded.Notebook'); |
|
1782 | 1759 | }; |
|
1783 | 1760 | |
|
1784 | 1761 | /** |
|
1785 | 1762 | * Failure callback for loading a notebook from the server. |
|
1786 | 1763 | * |
|
1787 | 1764 | * @method load_notebook_error |
|
1788 | 1765 | * @param {jqXHR} xhr jQuery Ajax object |
|
1789 | 1766 | * @param {String} textStatus Description of response status |
|
1790 | 1767 | * @param {String} errorThrow HTTP error message |
|
1791 | 1768 | */ |
|
1792 | 1769 | Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) { |
|
1793 | 1770 | if (xhr.status === 500) { |
|
1794 | 1771 | var msg = "An error occurred while loading this notebook. Most likely " + |
|
1795 | 1772 | "this notebook is in a newer format than is supported by this " + |
|
1796 | 1773 | "version of IPython. This version can load notebook formats " + |
|
1797 | 1774 | "v"+this.nbformat+" or earlier."; |
|
1798 | var dialog = $('<div/>'); | |
|
1799 |
dialog. |
|
|
1800 | this.element.append(dialog); | |
|
1801 | dialog.dialog({ | |
|
1802 | resizable: false, | |
|
1803 | modal: true, | |
|
1775 | ||
|
1776 | IPython.dialog.modal({ | |
|
1804 | 1777 | title: "Error loading notebook", |
|
1805 |
|
|
|
1806 | close: function(event, ui) {$(this).dialog('destroy').remove();}, | |
|
1778 | body : msg, | |
|
1807 | 1779 | buttons : { |
|
1808 |
"OK": |
|
|
1809 | $(this).dialog('close'); | |
|
1810 | } | |
|
1811 | }, | |
|
1812 | width: 400 | |
|
1780 | "OK": {} | |
|
1781 | } | |
|
1813 | 1782 | }); |
|
1814 | 1783 | } |
|
1815 | 1784 | } |
|
1816 | 1785 | |
|
1817 | 1786 | /********************* checkpoint-related *********************/ |
|
1818 | 1787 | |
|
1819 | 1788 | /** |
|
1820 | 1789 | * Save the notebook then immediately create a checkpoint. |
|
1821 | 1790 | * |
|
1822 | 1791 | * @method save_checkpoint |
|
1823 | 1792 | */ |
|
1824 | 1793 | Notebook.prototype.save_checkpoint = function () { |
|
1825 | 1794 | this._checkpoint_after_save = true; |
|
1826 | 1795 | this.save_notebook(); |
|
1827 | 1796 | }; |
|
1828 | 1797 | |
|
1829 | 1798 | /** |
|
1830 | 1799 | * List checkpoints for this notebook. |
|
1831 | 1800 | * |
|
1832 | 1801 | * @method list_checkpoint |
|
1833 | 1802 | */ |
|
1834 | 1803 | Notebook.prototype.list_checkpoints = function () { |
|
1835 | 1804 | var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints'; |
|
1836 | 1805 | $.get(url).done( |
|
1837 | 1806 | $.proxy(this.list_checkpoints_success, this) |
|
1838 | 1807 | ).fail( |
|
1839 | 1808 | $.proxy(this.list_checkpoints_error, this) |
|
1840 | 1809 | ); |
|
1841 | 1810 | }; |
|
1842 | 1811 | |
|
1843 | 1812 | /** |
|
1844 | 1813 | * Success callback for listing checkpoints. |
|
1845 | 1814 | * |
|
1846 | 1815 | * @method list_checkpoint_success |
|
1847 | 1816 | * @param {Object} data JSON representation of a checkpoint |
|
1848 | 1817 | * @param {String} status Description of response status |
|
1849 | 1818 | * @param {jqXHR} xhr jQuery Ajax object |
|
1850 | 1819 | */ |
|
1851 | 1820 | Notebook.prototype.list_checkpoints_success = function (data, status, xhr) { |
|
1852 | 1821 | var data = $.parseJSON(data); |
|
1853 | 1822 | if (data.length) { |
|
1854 | 1823 | this.last_checkpoint = data[0]; |
|
1855 | 1824 | } else { |
|
1856 | 1825 | this.last_checkpoint = null; |
|
1857 | 1826 | } |
|
1858 | 1827 | $([IPython.events]).trigger('checkpoints_listed.Notebook', [data]); |
|
1859 | 1828 | }; |
|
1860 | 1829 | |
|
1861 | 1830 | /** |
|
1862 | 1831 | * Failure callback for listing a checkpoint. |
|
1863 | 1832 | * |
|
1864 | 1833 | * @method list_checkpoint_error |
|
1865 | 1834 | * @param {jqXHR} xhr jQuery Ajax object |
|
1866 | 1835 | * @param {String} status Description of response status |
|
1867 | 1836 | * @param {String} error_msg HTTP error message |
|
1868 | 1837 | */ |
|
1869 | 1838 | Notebook.prototype.list_checkpoints_error = function (xhr, status, error_msg) { |
|
1870 | 1839 | $([IPython.events]).trigger('list_checkpoints_failed.Notebook'); |
|
1871 | 1840 | }; |
|
1872 | 1841 | |
|
1873 | 1842 | /** |
|
1874 | 1843 | * Create a checkpoint of this notebook on the server from the most recent save. |
|
1875 | 1844 | * |
|
1876 | 1845 | * @method create_checkpoint |
|
1877 | 1846 | */ |
|
1878 | 1847 | Notebook.prototype.create_checkpoint = function () { |
|
1879 | 1848 | var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints'; |
|
1880 | 1849 | $.post(url).done( |
|
1881 | 1850 | $.proxy(this.create_checkpoint_success, this) |
|
1882 | 1851 | ).fail( |
|
1883 | 1852 | $.proxy(this.create_checkpoint_error, this) |
|
1884 | 1853 | ); |
|
1885 | 1854 | }; |
|
1886 | 1855 | |
|
1887 | 1856 | /** |
|
1888 | 1857 | * Success callback for creating a checkpoint. |
|
1889 | 1858 | * |
|
1890 | 1859 | * @method create_checkpoint_success |
|
1891 | 1860 | * @param {Object} data JSON representation of a checkpoint |
|
1892 | 1861 | * @param {String} status Description of response status |
|
1893 | 1862 | * @param {jqXHR} xhr jQuery Ajax object |
|
1894 | 1863 | */ |
|
1895 | 1864 | Notebook.prototype.create_checkpoint_success = function (data, status, xhr) { |
|
1896 | 1865 | var data = $.parseJSON(data); |
|
1897 | 1866 | this.last_checkpoint = data; |
|
1898 | 1867 | $([IPython.events]).trigger('checkpoint_created.Notebook', data); |
|
1899 | 1868 | }; |
|
1900 | 1869 | |
|
1901 | 1870 | /** |
|
1902 | 1871 | * Failure callback for creating a checkpoint. |
|
1903 | 1872 | * |
|
1904 | 1873 | * @method create_checkpoint_error |
|
1905 | 1874 | * @param {jqXHR} xhr jQuery Ajax object |
|
1906 | 1875 | * @param {String} status Description of response status |
|
1907 | 1876 | * @param {String} error_msg HTTP error message |
|
1908 | 1877 | */ |
|
1909 | 1878 | Notebook.prototype.create_checkpoint_error = function (xhr, status, error_msg) { |
|
1910 | 1879 | $([IPython.events]).trigger('checkpoint_failed.Notebook'); |
|
1911 | 1880 | }; |
|
1912 | 1881 | |
|
1913 | 1882 | Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) { |
|
1914 | 1883 | var that = this; |
|
1915 | 1884 | var checkpoint = checkpoint || this.last_checkpoint; |
|
1916 | 1885 | if ( ! checkpoint ) { |
|
1917 | 1886 | console.log("restore dialog, but no checkpoint to restore to!"); |
|
1918 | 1887 | return; |
|
1919 | 1888 | } |
|
1920 |
var |
|
|
1889 | var body = $('<div/>').append( | |
|
1921 | 1890 | $('<p/>').addClass("p-space").text( |
|
1922 | 1891 | "Are you sure you want to revert the notebook to " + |
|
1923 | 1892 | "the latest checkpoint?" |
|
1924 | 1893 | ).append( |
|
1925 | 1894 | $("<strong/>").text( |
|
1926 | 1895 | " This cannot be undone." |
|
1927 | 1896 | ) |
|
1928 | 1897 | ) |
|
1929 | 1898 | ).append( |
|
1930 | 1899 | $('<p/>').addClass("p-space").text("The checkpoint was last updated at:") |
|
1931 | 1900 | ).append( |
|
1932 | 1901 | $('<p/>').addClass("p-space").text( |
|
1933 | 1902 | Date(checkpoint.last_modified) |
|
1934 | 1903 | ).css("text-align", "center") |
|
1935 | 1904 | ); |
|
1936 | 1905 | |
|
1937 | $(document).append(dialog); | |
|
1938 | ||
|
1939 | dialog.dialog({ | |
|
1940 | resizable: false, | |
|
1941 | modal: true, | |
|
1942 | title: "Revert notebook to checkpoint", | |
|
1943 | closeText: '', | |
|
1906 | IPython.dialog.modal({ | |
|
1907 | title : "Revert notebook to checkpoint", | |
|
1908 | body : body, | |
|
1944 | 1909 | buttons : { |
|
1945 |
|
|
|
1946 | that.restore_checkpoint(checkpoint.checkpoint_id); | |
|
1947 | $(this).dialog('close'); | |
|
1910 | Revert : { | |
|
1911 | class : "btn-danger", | |
|
1912 | click : function () { | |
|
1913 | that.restore_checkpoint(checkpoint.checkpoint_id); | |
|
1914 | } | |
|
1948 | 1915 | }, |
|
1949 |
|
|
|
1950 | $(this).dialog('close'); | |
|
1916 | Cancel : {} | |
|
1951 | 1917 | } |
|
1952 | }, | |
|
1953 | width: 400 | |
|
1954 | 1918 | }); |
|
1955 | 1919 | } |
|
1956 | 1920 | |
|
1957 | 1921 | /** |
|
1958 | 1922 | * Restore the notebook to a checkpoint state. |
|
1959 | 1923 | * |
|
1960 | 1924 | * @method restore_checkpoint |
|
1961 | 1925 | * @param {String} checkpoint ID |
|
1962 | 1926 | */ |
|
1963 | 1927 | Notebook.prototype.restore_checkpoint = function (checkpoint) { |
|
1964 | 1928 | $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint); |
|
1965 | 1929 | var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints/' + checkpoint; |
|
1966 | 1930 | $.post(url).done( |
|
1967 | 1931 | $.proxy(this.restore_checkpoint_success, this) |
|
1968 | 1932 | ).fail( |
|
1969 | 1933 | $.proxy(this.restore_checkpoint_error, this) |
|
1970 | 1934 | ); |
|
1971 | 1935 | }; |
|
1972 | 1936 | |
|
1973 | 1937 | /** |
|
1974 | 1938 | * Success callback for restoring a notebook to a checkpoint. |
|
1975 | 1939 | * |
|
1976 | 1940 | * @method restore_checkpoint_success |
|
1977 | 1941 | * @param {Object} data (ignored, should be empty) |
|
1978 | 1942 | * @param {String} status Description of response status |
|
1979 | 1943 | * @param {jqXHR} xhr jQuery Ajax object |
|
1980 | 1944 | */ |
|
1981 | 1945 | Notebook.prototype.restore_checkpoint_success = function (data, status, xhr) { |
|
1982 | 1946 | $([IPython.events]).trigger('checkpoint_restored.Notebook'); |
|
1983 | 1947 | this.load_notebook(this.notebook_id); |
|
1984 | 1948 | }; |
|
1985 | 1949 | |
|
1986 | 1950 | /** |
|
1987 | 1951 | * Failure callback for restoring a notebook to a checkpoint. |
|
1988 | 1952 | * |
|
1989 | 1953 | * @method restore_checkpoint_error |
|
1990 | 1954 | * @param {jqXHR} xhr jQuery Ajax object |
|
1991 | 1955 | * @param {String} status Description of response status |
|
1992 | 1956 | * @param {String} error_msg HTTP error message |
|
1993 | 1957 | */ |
|
1994 | 1958 | Notebook.prototype.restore_checkpoint_error = function (xhr, status, error_msg) { |
|
1995 | 1959 | $([IPython.events]).trigger('checkpoint_restore_failed.Notebook'); |
|
1996 | 1960 | }; |
|
1997 | 1961 | |
|
1998 | 1962 | /** |
|
1999 | 1963 | * Delete a notebook checkpoint. |
|
2000 | 1964 | * |
|
2001 | 1965 | * @method delete_checkpoint |
|
2002 | 1966 | * @param {String} checkpoint ID |
|
2003 | 1967 | */ |
|
2004 | 1968 | Notebook.prototype.delete_checkpoint = function (checkpoint) { |
|
2005 | 1969 | $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint); |
|
2006 | 1970 | var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints/' + checkpoint; |
|
2007 | 1971 | $.ajax(url, { |
|
2008 | 1972 | type: 'DELETE', |
|
2009 | 1973 | success: $.proxy(this.delete_checkpoint_success, this), |
|
2010 | 1974 | error: $.proxy(this.delete_notebook_error,this) |
|
2011 | 1975 | }); |
|
2012 | 1976 | }; |
|
2013 | 1977 | |
|
2014 | 1978 | /** |
|
2015 | 1979 | * Success callback for deleting a notebook checkpoint |
|
2016 | 1980 | * |
|
2017 | 1981 | * @method delete_checkpoint_success |
|
2018 | 1982 | * @param {Object} data (ignored, should be empty) |
|
2019 | 1983 | * @param {String} status Description of response status |
|
2020 | 1984 | * @param {jqXHR} xhr jQuery Ajax object |
|
2021 | 1985 | */ |
|
2022 | 1986 | Notebook.prototype.delete_checkpoint_success = function (data, status, xhr) { |
|
2023 | 1987 | $([IPython.events]).trigger('checkpoint_deleted.Notebook', data); |
|
2024 | 1988 | this.load_notebook(this.notebook_id); |
|
2025 | 1989 | }; |
|
2026 | 1990 | |
|
2027 | 1991 | /** |
|
2028 | 1992 | * Failure callback for deleting a notebook checkpoint. |
|
2029 | 1993 | * |
|
2030 | 1994 | * @method delete_checkpoint_error |
|
2031 | 1995 | * @param {jqXHR} xhr jQuery Ajax object |
|
2032 | 1996 | * @param {String} status Description of response status |
|
2033 | 1997 | * @param {String} error_msg HTTP error message |
|
2034 | 1998 | */ |
|
2035 | 1999 | Notebook.prototype.delete_checkpoint_error = function (xhr, status, error_msg) { |
|
2036 | 2000 | $([IPython.events]).trigger('checkpoint_delete_failed.Notebook'); |
|
2037 | 2001 | }; |
|
2038 | 2002 | |
|
2039 | 2003 | |
|
2040 | 2004 | IPython.Notebook = Notebook; |
|
2041 | 2005 | |
|
2042 | 2006 | |
|
2043 | 2007 | return IPython; |
|
2044 | 2008 | |
|
2045 | 2009 | }(IPython)); |
|
2046 | 2010 |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now