##// END OF EJS Templates
load the per kernel kernel.js and kernel.css...
Matthias BUSSONNIER -
Show More
@@ -1,111 +1,156 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'base/js/utils',
7 'base/js/utils',
8 ], function(IPython, $, utils) {
8 ], function(IPython, $, utils) {
9 "use strict";
9 "use strict";
10
10
11 var KernelSelector = function(selector, notebook) {
11 var KernelSelector = function(selector, notebook) {
12 this.selector = selector;
12 this.selector = selector;
13 this.notebook = notebook;
13 this.notebook = notebook;
14 this.events = notebook.events;
14 this.events = notebook.events;
15 this.current_selection = null;
15 this.current_selection = null;
16 this.kernelspecs = {};
16 this.kernelspecs = {};
17 if (this.selector !== undefined) {
17 if (this.selector !== undefined) {
18 this.element = $(selector);
18 this.element = $(selector);
19 this.request_kernelspecs();
19 this.request_kernelspecs();
20 }
20 }
21 this.bind_events();
21 this.bind_events();
22 // Make the object globally available for user convenience & inspection
22 // Make the object globally available for user convenience & inspection
23 IPython.kernelselector = this;
23 IPython.kernelselector = this;
24 Object.seal(this);
24 };
25 };
25
26
26 KernelSelector.prototype.request_kernelspecs = function() {
27 KernelSelector.prototype.request_kernelspecs = function() {
27 var url = utils.url_join_encode(this.notebook.base_url, 'api/kernelspecs');
28 var url = utils.url_join_encode(this.notebook.base_url, 'api/kernelspecs');
28 utils.promising_ajax(url).then($.proxy(this._got_kernelspecs, this));
29 utils.promising_ajax(url).then($.proxy(this._got_kernelspecs, this));
29 };
30 };
30
31
31 KernelSelector.prototype._got_kernelspecs = function(data) {
32 KernelSelector.prototype._got_kernelspecs = function(data) {
32 this.kernelspecs = data.kernelspecs;
33 this.kernelspecs = data.kernelspecs;
33 var menu = this.element.find("#kernel_selector");
34 var menu = this.element.find("#kernel_selector");
34 var change_kernel_submenu = $("#menu-change-kernel-submenu");
35 var change_kernel_submenu = $("#menu-change-kernel-submenu");
35 var keys = Object.keys(data.kernelspecs).sort(function (a, b) {
36 var keys = Object.keys(data.kernelspecs).sort(function (a, b) {
36 // sort by display_name
37 // sort by display_name
37 var da = data.kernelspecs[a].display_name;
38 var da = data.kernelspecs[a].display_name;
38 var db = data.kernelspecs[b].display_name;
39 var db = data.kernelspecs[b].display_name;
39 if (da === db) {
40 if (da === db) {
40 return 0;
41 return 0;
41 } else if (da > db) {
42 } else if (da > db) {
42 return 1;
43 return 1;
43 } else {
44 } else {
44 return -1;
45 return -1;
45 }
46 }
46 });
47 });
47 for (var i = 0; i < keys.length; i++) {
48 for (var i = 0; i < keys.length; i++) {
48 var ks = this.kernelspecs[keys[i]];
49 var ks = this.kernelspecs[keys[i]];
49 var ksentry = $("<li>").attr("id", "kernel-" +ks.name).append($('<a>')
50 var ksentry = $("<li>").attr("id", "kernel-" +ks.name).append($('<a>')
50 .attr('href', '#')
51 .attr('href', '#')
51 .click($.proxy(this.change_kernel, this, ks.name))
52 .click($.proxy(this.change_kernel, this, ks.name))
52 .text(ks.display_name));
53 .text(ks.display_name));
53 menu.append(ksentry);
54 menu.append(ksentry);
54
55
55 var ks_submenu_entry = $("<li>").attr("id", "kernel-submenu-"+ks.name).append($('<a>')
56 var ks_submenu_entry = $("<li>").attr("id", "kernel-submenu-"+ks.name).append($('<a>')
56 .attr('href', '#')
57 .attr('href', '#')
57 .click($.proxy(this.change_kernel, this, ks.name))
58 .click($.proxy(this.change_kernel, this, ks.name))
58 .text(ks.display_name));
59 .text(ks.display_name));
59 change_kernel_submenu.append(ks_submenu_entry);
60 change_kernel_submenu.append(ks_submenu_entry);
60 }
61 }
61 };
62 };
62
63
63 KernelSelector.prototype.change_kernel = function(kernel_name) {
64 KernelSelector.prototype.change_kernel = function(kernel_name) {
65 /**
66 * TODO, have a methods to set kernel spec directly ?
67 **/
68 var that = this;
64 if (kernel_name === this.current_selection) {
69 if (kernel_name === this.current_selection) {
65 return;
70 return;
66 }
71 }
67 var ks = this.kernelspecs[kernel_name];
72 var ks = this.kernelspecs[kernel_name];
73 var new_mode_url = 'kernelspecs/'+ks.name+'/kernel';
74
75 var css_url = this.notebook.base_url+new_mode_url+'.css';
76 $.ajax({
77 type: 'HEAD',
78 url: css_url,
79 success: function(){
80 $('#kernel-css')
81 .attr('href',css_url);
82 },
83 error:function(){
84 console.warn(new_mode_url+' does not provide custom URL, you might see a 404 error that shoudl not prevent '+
85 ' the Jupyter notebook from working :' ,css_url );
86 }
87 });
88
68 try {
89 try {
69 this.notebook.start_session(kernel_name);
90 this.notebook.start_session(kernel_name);
70 } catch (e) {
91 } catch (e) {
71 if (e.name === 'SessionAlreadyStarting') {
92 if (e.name === 'SessionAlreadyStarting') {
72 console.log("Cannot change kernel while waiting for pending session start.");
93 console.log("Cannot change kernel while waiting for pending session start.");
73 } else {
94 } else {
74 // unhandled error
95 // unhandled error
75 throw e;
96 throw e;
76 }
97 }
77 // only trigger spec_changed if change was successful
98 // only trigger spec_changed if change was successful
78 return;
99 return;
79 }
100 }
80 this.events.trigger('spec_changed.Kernel', ks);
101 this.events.trigger('spec_changed.Kernel', ks);
102
103
104 // load new mode kernel.js if exist
105 require([new_mode_url],
106 // if new mode has custom.js
107 function(new_mode){
108 that.lock_switch();
109 if(new_mode && new_mode.onload){
110 new_mode.onload();
111 } else {
112 console.warn("The current kernel seem to define a kernel.js file; though this file does"+
113 "not contain any asynchronous module definition. This is undefined behavior"+
114 "which is not recommeneded");
115 }
116 },
117 function(err){
118 // if new mode does not have custom.js
119 console.warn('Any above 404 on '+new_mode_url+'.js is normal');
120 }
121 );
81 };
122 };
82
123
124 KernelSelector.prototype.lock_switch = function() {
125 console.warn('switching kernel is not guarantied to work !');
126 };
127
83 KernelSelector.prototype.bind_events = function() {
128 KernelSelector.prototype.bind_events = function() {
84 var that = this;
129 var that = this;
85 this.events.on('spec_changed.Kernel', function(event, data) {
130 this.events.on('spec_changed.Kernel', function(event, data) {
86 that.current_selection = data.name;
131 that.current_selection = data.name;
87 that.element.find("#current_kernel_spec").find('.kernel_name').text(data.display_name);
132 that.element.find("#current_kernel_spec").find('.kernel_name').text(data.display_name);
88 that.element.find("#current_kernel_logo").attr("src", "/kernelspecs/"+data.name+"/logo-64x64.png");
133 that.element.find("#current_kernel_logo").attr("src", "/kernelspecs/"+data.name+"/logo-64x64.png");
89 });
134 });
90
135
91 this.events.on('kernel_created.Session', function(event, data) {
136 this.events.on('kernel_created.Session', function(event, data) {
92 if (data.kernel.name !== that.current_selection) {
137 if (data.kernel.name !== that.current_selection) {
93 // If we created a 'python' session, we only know if it's Python
138 // If we created a 'python' session, we only know if it's Python
94 // 3 or 2 on the server's reply, so we fire the event again to
139 // 3 or 2 on the server's reply, so we fire the event again to
95 // set things up.
140 // set things up.
96 var ks = that.kernelspecs[data.kernel.name];
141 var ks = that.kernelspecs[data.kernel.name];
97 that.events.trigger('spec_changed.Kernel', ks);
142 that.events.trigger('spec_changed.Kernel', ks);
98 }
143 }
99 });
144 });
100
145
101 var logo_img = this.element.find("#current_kernel_logo")
146 var logo_img = this.element.find("#current_kernel_logo")
102 logo_img.on("load", function() {
147 logo_img.on("load", function() {
103 logo_img.show();
148 logo_img.show();
104 });
149 });
105 logo_img.on("error", function() {
150 logo_img.on("error", function() {
106 logo_img.hide();
151 logo_img.hide();
107 });
152 });
108 };
153 };
109
154
110 return {'KernelSelector': KernelSelector};
155 return {'KernelSelector': KernelSelector};
111 });
156 });
@@ -1,166 +1,167 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 require([
4 require([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'notebook/js/notebook',
7 'notebook/js/notebook',
8 'contents',
8 'contents',
9 'services/config',
9 'services/config',
10 'base/js/utils',
10 'base/js/utils',
11 'base/js/page',
11 'base/js/page',
12 'base/js/events',
12 'base/js/events',
13 'auth/js/loginwidget',
13 'auth/js/loginwidget',
14 'notebook/js/maintoolbar',
14 'notebook/js/maintoolbar',
15 'notebook/js/pager',
15 'notebook/js/pager',
16 'notebook/js/quickhelp',
16 'notebook/js/quickhelp',
17 'notebook/js/menubar',
17 'notebook/js/menubar',
18 'notebook/js/notificationarea',
18 'notebook/js/notificationarea',
19 'notebook/js/savewidget',
19 'notebook/js/savewidget',
20 'notebook/js/actions',
20 'notebook/js/actions',
21 'notebook/js/keyboardmanager',
21 'notebook/js/keyboardmanager',
22 'notebook/js/config',
22 'notebook/js/config',
23 'notebook/js/kernelselector',
23 'notebook/js/kernelselector',
24 'codemirror/lib/codemirror',
24 'codemirror/lib/codemirror',
25 'notebook/js/about',
25 'notebook/js/about',
26 // only loaded, not used, please keep sure this is loaded last
26 // only loaded, not used, please keep sure this is loaded last
27 'custom/custom'
27 'custom/custom'
28 ], function(
28 ], function(
29 IPython,
29 IPython,
30 $,
30 $,
31 notebook,
31 notebook,
32 contents,
32 contents,
33 configmod,
33 configmod,
34 utils,
34 utils,
35 page,
35 page,
36 events,
36 events,
37 loginwidget,
37 loginwidget,
38 maintoolbar,
38 maintoolbar,
39 pager,
39 pager,
40 quickhelp,
40 quickhelp,
41 menubar,
41 menubar,
42 notificationarea,
42 notificationarea,
43 savewidget,
43 savewidget,
44 actions,
44 actions,
45 keyboardmanager,
45 keyboardmanager,
46 config,
46 config,
47 kernelselector,
47 kernelselector,
48 CodeMirror,
48 CodeMirror,
49 about,
49 about,
50 // please keep sure that even if not used, this is loaded last
50 // please keep sure that even if not used, this is loaded last
51 custom
51 custom
52 ) {
52 ) {
53 "use strict";
53 "use strict";
54
54
55 // compat with old IPython, remove for IPython > 3.0
55 // compat with old IPython, remove for IPython > 3.0
56 window.CodeMirror = CodeMirror;
56 window.CodeMirror = CodeMirror;
57
57
58 var common_options = {
58 var common_options = {
59 ws_url : utils.get_body_data("wsUrl"),
59 ws_url : utils.get_body_data("wsUrl"),
60 base_url : utils.get_body_data("baseUrl"),
60 base_url : utils.get_body_data("baseUrl"),
61 notebook_path : utils.get_body_data("notebookPath"),
61 notebook_path : utils.get_body_data("notebookPath"),
62 notebook_name : utils.get_body_data('notebookName')
62 notebook_name : utils.get_body_data('notebookName')
63 };
63 };
64
64
65 var user_config = $.extend({}, config.default_config);
65 var user_config = $.extend({}, config.default_config);
66 var page = new page.Page();
66 var page = new page.Page();
67 var pager = new pager.Pager('div#pager', {
67 var pager = new pager.Pager('div#pager', {
68 events: events});
68 events: events});
69 var acts = new actions.init();
69 var acts = new actions.init();
70 var keyboard_manager = new keyboardmanager.KeyboardManager({
70 var keyboard_manager = new keyboardmanager.KeyboardManager({
71 pager: pager,
71 pager: pager,
72 events: events,
72 events: events,
73 actions: acts });
73 actions: acts });
74 var save_widget = new savewidget.SaveWidget('span#save_widget', {
74 var save_widget = new savewidget.SaveWidget('span#save_widget', {
75 events: events,
75 events: events,
76 keyboard_manager: keyboard_manager});
76 keyboard_manager: keyboard_manager});
77 var contents = new contents.Contents($.extend({
77 var contents = new contents.Contents($.extend({
78 events: events},
78 events: events},
79 common_options));
79 common_options));
80 var config_section = new configmod.ConfigSection('notebook', common_options);
80 var config_section = new configmod.ConfigSection('notebook', common_options);
81 config_section.load();
81 config_section.load();
82 var notebook = new notebook.Notebook('div#notebook', $.extend({
82 var notebook = new notebook.Notebook('div#notebook', $.extend({
83 events: events,
83 events: events,
84 keyboard_manager: keyboard_manager,
84 keyboard_manager: keyboard_manager,
85 save_widget: save_widget,
85 save_widget: save_widget,
86 contents: contents,
86 contents: contents,
87 config: user_config},
87 config: user_config},
88 common_options));
88 common_options));
89 var login_widget = new loginwidget.LoginWidget('span#login_widget', common_options);
89 var login_widget = new loginwidget.LoginWidget('span#login_widget', common_options);
90 var toolbar = new maintoolbar.MainToolBar('#maintoolbar-container', {
90 var toolbar = new maintoolbar.MainToolBar('#maintoolbar-container', {
91 notebook: notebook,
91 notebook: notebook,
92 events: events,
92 events: events,
93 actions: acts});
93 actions: acts});
94 var quick_help = new quickhelp.QuickHelp({
94 var quick_help = new quickhelp.QuickHelp({
95 keyboard_manager: keyboard_manager,
95 keyboard_manager: keyboard_manager,
96 events: events,
96 events: events,
97 notebook: notebook});
97 notebook: notebook});
98 keyboard_manager.set_notebook(notebook);
98 keyboard_manager.set_notebook(notebook);
99 keyboard_manager.set_quickhelp(quick_help);
99 keyboard_manager.set_quickhelp(quick_help);
100 var menubar = new menubar.MenuBar('#menubar', $.extend({
100 var menubar = new menubar.MenuBar('#menubar', $.extend({
101 notebook: notebook,
101 notebook: notebook,
102 contents: contents,
102 contents: contents,
103 events: events,
103 events: events,
104 save_widget: save_widget,
104 save_widget: save_widget,
105 quick_help: quick_help},
105 quick_help: quick_help},
106 common_options));
106 common_options));
107 var notification_area = new notificationarea.NotebookNotificationArea(
107 var notification_area = new notificationarea.NotebookNotificationArea(
108 '#notification_area', {
108 '#notification_area', {
109 events: events,
109 events: events,
110 save_widget: save_widget,
110 save_widget: save_widget,
111 notebook: notebook,
111 notebook: notebook,
112 keyboard_manager: keyboard_manager});
112 keyboard_manager: keyboard_manager});
113 notification_area.init_notification_widgets();
113 notification_area.init_notification_widgets();
114 var kernel_selector = new kernelselector.KernelSelector(
114 var kernel_selector = new kernelselector.KernelSelector(
115 '#kernel_selector_widget', notebook);
115 '#kernel_selector_widget', notebook);
116 notebook.set_kernelselector(kernel_selector);
116
117
117 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
118 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
118 '<span id="test2" style="font-weight: bold;">x</span>'+
119 '<span id="test2" style="font-weight: bold;">x</span>'+
119 '<span id="test3" style="font-style: italic;">x</span></pre></div>');
120 '<span id="test3" style="font-style: italic;">x</span></pre></div>');
120 var nh = $('#test1').innerHeight();
121 var nh = $('#test1').innerHeight();
121 var bh = $('#test2').innerHeight();
122 var bh = $('#test2').innerHeight();
122 var ih = $('#test3').innerHeight();
123 var ih = $('#test3').innerHeight();
123 if(nh != bh || nh != ih) {
124 if(nh != bh || nh != ih) {
124 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
125 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
125 }
126 }
126 $('#fonttest').remove();
127 $('#fonttest').remove();
127
128
128 page.show();
129 page.show();
129
130
130 var first_load = function () {
131 var first_load = function () {
131 var hash = document.location.hash;
132 var hash = document.location.hash;
132 if (hash) {
133 if (hash) {
133 document.location.hash = '';
134 document.location.hash = '';
134 document.location.hash = hash;
135 document.location.hash = hash;
135 }
136 }
136 notebook.set_autosave_interval(notebook.minimum_autosave_interval);
137 notebook.set_autosave_interval(notebook.minimum_autosave_interval);
137 // only do this once
138 // only do this once
138 events.off('notebook_loaded.Notebook', first_load);
139 events.off('notebook_loaded.Notebook', first_load);
139 };
140 };
140 events.on('notebook_loaded.Notebook', first_load);
141 events.on('notebook_loaded.Notebook', first_load);
141
142
142 IPython.page = page;
143 IPython.page = page;
143 IPython.notebook = notebook;
144 IPython.notebook = notebook;
144 IPython.contents = contents;
145 IPython.contents = contents;
145 IPython.pager = pager;
146 IPython.pager = pager;
146 IPython.quick_help = quick_help;
147 IPython.quick_help = quick_help;
147 IPython.login_widget = login_widget;
148 IPython.login_widget = login_widget;
148 IPython.menubar = menubar;
149 IPython.menubar = menubar;
149 IPython.toolbar = toolbar;
150 IPython.toolbar = toolbar;
150 IPython.notification_area = notification_area;
151 IPython.notification_area = notification_area;
151 IPython.keyboard_manager = keyboard_manager;
152 IPython.keyboard_manager = keyboard_manager;
152 IPython.save_widget = save_widget;
153 IPython.save_widget = save_widget;
153 IPython.config = user_config;
154 IPython.config = user_config;
154 IPython.tooltip = notebook.tooltip;
155 IPython.tooltip = notebook.tooltip;
155
156
156 events.trigger('app_initialized.NotebookApp');
157 events.trigger('app_initialized.NotebookApp');
157 config_section.loaded.then(function() {
158 config_section.loaded.then(function() {
158 if (config_section.data.load_extensions) {
159 if (config_section.data.load_extensions) {
159 var nbextension_paths = Object.getOwnPropertyNames(
160 var nbextension_paths = Object.getOwnPropertyNames(
160 config_section.data.load_extensions);
161 config_section.data.load_extensions);
161 IPython.load_extensions.apply(this, nbextension_paths);
162 IPython.load_extensions.apply(this, nbextension_paths);
162 }
163 }
163 });
164 });
164 notebook.load_notebook(common_options.notebook_path);
165 notebook.load_notebook(common_options.notebook_path);
165
166
166 });
167 });
@@ -1,2530 +1,2541 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'base/js/utils',
7 'base/js/utils',
8 'base/js/dialog',
8 'base/js/dialog',
9 'notebook/js/cell',
9 'notebook/js/cell',
10 'notebook/js/textcell',
10 'notebook/js/textcell',
11 'notebook/js/codecell',
11 'notebook/js/codecell',
12 'services/sessions/session',
12 'services/sessions/session',
13 'notebook/js/celltoolbar',
13 'notebook/js/celltoolbar',
14 'components/marked/lib/marked',
14 'components/marked/lib/marked',
15 'codemirror/lib/codemirror',
15 'codemirror/lib/codemirror',
16 'codemirror/addon/runmode/runmode',
16 'codemirror/addon/runmode/runmode',
17 'notebook/js/mathjaxutils',
17 'notebook/js/mathjaxutils',
18 'base/js/keyboard',
18 'base/js/keyboard',
19 'notebook/js/tooltip',
19 'notebook/js/tooltip',
20 'notebook/js/celltoolbarpresets/default',
20 'notebook/js/celltoolbarpresets/default',
21 'notebook/js/celltoolbarpresets/rawcell',
21 'notebook/js/celltoolbarpresets/rawcell',
22 'notebook/js/celltoolbarpresets/slideshow',
22 'notebook/js/celltoolbarpresets/slideshow',
23 'notebook/js/scrollmanager'
23 'notebook/js/scrollmanager'
24 ], function (
24 ], function (
25 IPython,
25 IPython,
26 $,
26 $,
27 utils,
27 utils,
28 dialog,
28 dialog,
29 cellmod,
29 cellmod,
30 textcell,
30 textcell,
31 codecell,
31 codecell,
32 session,
32 session,
33 celltoolbar,
33 celltoolbar,
34 marked,
34 marked,
35 CodeMirror,
35 CodeMirror,
36 runMode,
36 runMode,
37 mathjaxutils,
37 mathjaxutils,
38 keyboard,
38 keyboard,
39 tooltip,
39 tooltip,
40 default_celltoolbar,
40 default_celltoolbar,
41 rawcell_celltoolbar,
41 rawcell_celltoolbar,
42 slideshow_celltoolbar,
42 slideshow_celltoolbar,
43 scrollmanager
43 scrollmanager
44 ) {
44 ) {
45 "use strict";
45 "use strict";
46
46
47 var Notebook = function (selector, options) {
47 var Notebook = function (selector, options) {
48 /**
48 /**
49 * Constructor
49 * Constructor
50 *
50 *
51 * A notebook contains and manages cells.
51 * A notebook contains and manages cells.
52 *
52 *
53 * Parameters:
53 * Parameters:
54 * selector: string
54 * selector: string
55 * options: dictionary
55 * options: dictionary
56 * Dictionary of keyword arguments.
56 * Dictionary of keyword arguments.
57 * events: $(Events) instance
57 * events: $(Events) instance
58 * keyboard_manager: KeyboardManager instance
58 * keyboard_manager: KeyboardManager instance
59 * contents: Contents instance
59 * contents: Contents instance
60 * save_widget: SaveWidget instance
60 * save_widget: SaveWidget instance
61 * config: dictionary
61 * config: dictionary
62 * base_url : string
62 * base_url : string
63 * notebook_path : string
63 * notebook_path : string
64 * notebook_name : string
64 * notebook_name : string
65 */
65 */
66 this.config = utils.mergeopt(Notebook, options.config);
66 this.config = utils.mergeopt(Notebook, options.config);
67 this.base_url = options.base_url;
67 this.base_url = options.base_url;
68 this.notebook_path = options.notebook_path;
68 this.notebook_path = options.notebook_path;
69 this.notebook_name = options.notebook_name;
69 this.notebook_name = options.notebook_name;
70 this.events = options.events;
70 this.events = options.events;
71 this.keyboard_manager = options.keyboard_manager;
71 this.keyboard_manager = options.keyboard_manager;
72 this.contents = options.contents;
72 this.contents = options.contents;
73 this.save_widget = options.save_widget;
73 this.save_widget = options.save_widget;
74 this.tooltip = new tooltip.Tooltip(this.events);
74 this.tooltip = new tooltip.Tooltip(this.events);
75 this.ws_url = options.ws_url;
75 this.ws_url = options.ws_url;
76 this._session_starting = false;
76 this._session_starting = false;
77 this.default_cell_type = this.config.default_cell_type || 'code';
77 this.default_cell_type = this.config.default_cell_type || 'code';
78
78
79 // Create default scroll manager.
79 // Create default scroll manager.
80 this.scroll_manager = new scrollmanager.ScrollManager(this);
80 this.scroll_manager = new scrollmanager.ScrollManager(this);
81
81
82 // TODO: This code smells (and the other `= this` line a couple lines down)
82 // TODO: This code smells (and the other `= this` line a couple lines down)
83 // We need a better way to deal with circular instance references.
83 // We need a better way to deal with circular instance references.
84 this.keyboard_manager.notebook = this;
84 this.keyboard_manager.notebook = this;
85 this.save_widget.notebook = this;
85 this.save_widget.notebook = this;
86
86
87 mathjaxutils.init();
87 mathjaxutils.init();
88
88
89 if (marked) {
89 if (marked) {
90 marked.setOptions({
90 marked.setOptions({
91 gfm : true,
91 gfm : true,
92 tables: true,
92 tables: true,
93 // FIXME: probably want central config for CodeMirror theme when we have js config
93 // FIXME: probably want central config for CodeMirror theme when we have js config
94 langPrefix: "cm-s-ipython language-",
94 langPrefix: "cm-s-ipython language-",
95 highlight: function(code, lang, callback) {
95 highlight: function(code, lang, callback) {
96 if (!lang) {
96 if (!lang) {
97 // no language, no highlight
97 // no language, no highlight
98 if (callback) {
98 if (callback) {
99 callback(null, code);
99 callback(null, code);
100 return;
100 return;
101 } else {
101 } else {
102 return code;
102 return code;
103 }
103 }
104 }
104 }
105 utils.requireCodeMirrorMode(lang, function (spec) {
105 utils.requireCodeMirrorMode(lang, function (spec) {
106 var el = document.createElement("div");
106 var el = document.createElement("div");
107 var mode = CodeMirror.getMode({}, spec);
107 var mode = CodeMirror.getMode({}, spec);
108 if (!mode) {
108 if (!mode) {
109 console.log("No CodeMirror mode: " + lang);
109 console.log("No CodeMirror mode: " + lang);
110 callback(null, code);
110 callback(null, code);
111 return;
111 return;
112 }
112 }
113 try {
113 try {
114 CodeMirror.runMode(code, spec, el);
114 CodeMirror.runMode(code, spec, el);
115 callback(null, el.innerHTML);
115 callback(null, el.innerHTML);
116 } catch (err) {
116 } catch (err) {
117 console.log("Failed to highlight " + lang + " code", err);
117 console.log("Failed to highlight " + lang + " code", err);
118 callback(err, code);
118 callback(err, code);
119 }
119 }
120 }, function (err) {
120 }, function (err) {
121 console.log("No CodeMirror mode: " + lang);
121 console.log("No CodeMirror mode: " + lang);
122 callback(err, code);
122 callback(err, code);
123 });
123 });
124 }
124 }
125 });
125 });
126 }
126 }
127
127
128 this.element = $(selector);
128 this.element = $(selector);
129 this.element.scroll();
129 this.element.scroll();
130 this.element.data("notebook", this);
130 this.element.data("notebook", this);
131 this.next_prompt_number = 1;
131 this.next_prompt_number = 1;
132 this.session = null;
132 this.session = null;
133 this.kernel = null;
133 this.kernel = null;
134 this.clipboard = null;
134 this.clipboard = null;
135 this.undelete_backup = null;
135 this.undelete_backup = null;
136 this.undelete_index = null;
136 this.undelete_index = null;
137 this.undelete_below = false;
137 this.undelete_below = false;
138 this.paste_enabled = false;
138 this.paste_enabled = false;
139 this.writable = false;
139 this.writable = false;
140 // It is important to start out in command mode to match the intial mode
140 // It is important to start out in command mode to match the intial mode
141 // of the KeyboardManager.
141 // of the KeyboardManager.
142 this.mode = 'command';
142 this.mode = 'command';
143 this.set_dirty(false);
143 this.set_dirty(false);
144 this.metadata = {};
144 this.metadata = {};
145 this._checkpoint_after_save = false;
145 this._checkpoint_after_save = false;
146 this.last_checkpoint = null;
146 this.last_checkpoint = null;
147 this.checkpoints = [];
147 this.checkpoints = [];
148 this.autosave_interval = 0;
148 this.autosave_interval = 0;
149 this.autosave_timer = null;
149 this.autosave_timer = null;
150 // autosave *at most* every two minutes
150 // autosave *at most* every two minutes
151 this.minimum_autosave_interval = 120000;
151 this.minimum_autosave_interval = 120000;
152 this.notebook_name_blacklist_re = /[\/\\:]/;
152 this.notebook_name_blacklist_re = /[\/\\:]/;
153 this.nbformat = 4; // Increment this when changing the nbformat
153 this.nbformat = 4; // Increment this when changing the nbformat
154 this.nbformat_minor = this.current_nbformat_minor = 0; // Increment this when changing the nbformat
154 this.nbformat_minor = this.current_nbformat_minor = 0; // Increment this when changing the nbformat
155 this.codemirror_mode = 'ipython';
155 this.codemirror_mode = 'ipython';
156 this.create_elements();
156 this.create_elements();
157 this.bind_events();
157 this.bind_events();
158 this.kernel_selector = null;
158 this.kernel_selector = null;
159 this.dirty = null;
159 this.dirty = null;
160 this.trusted = null;
160 this.trusted = null;
161 this._fully_loaded = false;
161 this._fully_loaded = false;
162
162
163 // Trigger cell toolbar registration.
163 // Trigger cell toolbar registration.
164 default_celltoolbar.register(this);
164 default_celltoolbar.register(this);
165 rawcell_celltoolbar.register(this);
165 rawcell_celltoolbar.register(this);
166 slideshow_celltoolbar.register(this);
166 slideshow_celltoolbar.register(this);
167
167
168 // prevent assign to miss-typed properties.
168 // prevent assign to miss-typed properties.
169 Object.seal(this);
169 Object.seal(this);
170 };
170 };
171
171
172 Notebook.options_default = {
172 Notebook.options_default = {
173 // can be any cell type, or the special values of
173 // can be any cell type, or the special values of
174 // 'above', 'below', or 'selected' to get the value from another cell.
174 // 'above', 'below', or 'selected' to get the value from another cell.
175 Notebook: {
175 Notebook: {
176 default_cell_type: 'code'
176 default_cell_type: 'code'
177 }
177 }
178 };
178 };
179
179
180
180
181 /**
181 /**
182 * Create an HTML and CSS representation of the notebook.
182 * Create an HTML and CSS representation of the notebook.
183 *
183 *
184 * @method create_elements
184 * @method create_elements
185 */
185 */
186 Notebook.prototype.create_elements = function () {
186 Notebook.prototype.create_elements = function () {
187 var that = this;
187 var that = this;
188 this.element.attr('tabindex','-1');
188 this.element.attr('tabindex','-1');
189 this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
189 this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
190 // We add this end_space div to the end of the notebook div to:
190 // We add this end_space div to the end of the notebook div to:
191 // i) provide a margin between the last cell and the end of the notebook
191 // i) provide a margin between the last cell and the end of the notebook
192 // ii) to prevent the div from scrolling up when the last cell is being
192 // ii) to prevent the div from scrolling up when the last cell is being
193 // edited, but is too low on the page, which browsers will do automatically.
193 // edited, but is too low on the page, which browsers will do automatically.
194 var end_space = $('<div/>').addClass('end_space');
194 var end_space = $('<div/>').addClass('end_space');
195 end_space.dblclick(function (e) {
195 end_space.dblclick(function (e) {
196 var ncells = that.ncells();
196 var ncells = that.ncells();
197 that.insert_cell_below('code',ncells-1);
197 that.insert_cell_below('code',ncells-1);
198 });
198 });
199 this.element.append(this.container);
199 this.element.append(this.container);
200 this.container.append(end_space);
200 this.container.append(end_space);
201 };
201 };
202
202
203 /**
203 /**
204 * Bind JavaScript events: key presses and custom IPython events.
204 * Bind JavaScript events: key presses and custom IPython events.
205 *
205 *
206 * @method bind_events
206 * @method bind_events
207 */
207 */
208 Notebook.prototype.bind_events = function () {
208 Notebook.prototype.bind_events = function () {
209 var that = this;
209 var that = this;
210
210
211 this.events.on('set_next_input.Notebook', function (event, data) {
211 this.events.on('set_next_input.Notebook', function (event, data) {
212 if (data.replace) {
212 if (data.replace) {
213 data.cell.set_text(data.text);
213 data.cell.set_text(data.text);
214 data.cell.clear_output();
214 data.cell.clear_output();
215 } else {
215 } else {
216 var index = that.find_cell_index(data.cell);
216 var index = that.find_cell_index(data.cell);
217 var new_cell = that.insert_cell_below('code',index);
217 var new_cell = that.insert_cell_below('code',index);
218 new_cell.set_text(data.text);
218 new_cell.set_text(data.text);
219 }
219 }
220 that.dirty = true;
220 that.dirty = true;
221 });
221 });
222
222
223 this.events.on('unrecognized_cell.Cell', function () {
223 this.events.on('unrecognized_cell.Cell', function () {
224 that.warn_nbformat_minor();
224 that.warn_nbformat_minor();
225 });
225 });
226
226
227 this.events.on('unrecognized_output.OutputArea', function () {
227 this.events.on('unrecognized_output.OutputArea', function () {
228 that.warn_nbformat_minor();
228 that.warn_nbformat_minor();
229 });
229 });
230
230
231 this.events.on('set_dirty.Notebook', function (event, data) {
231 this.events.on('set_dirty.Notebook', function (event, data) {
232 that.dirty = data.value;
232 that.dirty = data.value;
233 });
233 });
234
234
235 this.events.on('trust_changed.Notebook', function (event, trusted) {
235 this.events.on('trust_changed.Notebook', function (event, trusted) {
236 that.trusted = trusted;
236 that.trusted = trusted;
237 });
237 });
238
238
239 this.events.on('select.Cell', function (event, data) {
239 this.events.on('select.Cell', function (event, data) {
240 var index = that.find_cell_index(data.cell);
240 var index = that.find_cell_index(data.cell);
241 that.select(index);
241 that.select(index);
242 });
242 });
243
243
244 this.events.on('edit_mode.Cell', function (event, data) {
244 this.events.on('edit_mode.Cell', function (event, data) {
245 that.handle_edit_mode(data.cell);
245 that.handle_edit_mode(data.cell);
246 });
246 });
247
247
248 this.events.on('command_mode.Cell', function (event, data) {
248 this.events.on('command_mode.Cell', function (event, data) {
249 that.handle_command_mode(data.cell);
249 that.handle_command_mode(data.cell);
250 });
250 });
251
251
252 this.events.on('spec_changed.Kernel', function(event, data) {
252 this.events.on('spec_changed.Kernel', function(event, data) {
253 that.metadata.kernelspec =
253 that.metadata.kernelspec =
254 {name: data.name, display_name: data.display_name};
254 {name: data.name, display_name: data.display_name};
255 });
255 });
256
256
257 this.events.on('kernel_ready.Kernel', function(event, data) {
257 this.events.on('kernel_ready.Kernel', function(event, data) {
258 var kinfo = data.kernel.info_reply;
258 var kinfo = data.kernel.info_reply;
259 var langinfo = kinfo.language_info || {};
259 var langinfo = kinfo.language_info || {};
260 that.metadata.language_info = langinfo;
260 that.metadata.language_info = langinfo;
261 // Mode 'null' should be plain, unhighlighted text.
261 // Mode 'null' should be plain, unhighlighted text.
262 var cm_mode = langinfo.codemirror_mode || langinfo.name || 'null';
262 var cm_mode = langinfo.codemirror_mode || langinfo.name || 'null';
263 that.set_codemirror_mode(cm_mode);
263 that.set_codemirror_mode(cm_mode);
264 });
264 });
265
265
266 var collapse_time = function (time) {
266 var collapse_time = function (time) {
267 var app_height = $('#ipython-main-app').height(); // content height
267 var app_height = $('#ipython-main-app').height(); // content height
268 var splitter_height = $('div#pager_splitter').outerHeight(true);
268 var splitter_height = $('div#pager_splitter').outerHeight(true);
269 var new_height = app_height - splitter_height;
269 var new_height = app_height - splitter_height;
270 that.element.animate({height : new_height + 'px'}, time);
270 that.element.animate({height : new_height + 'px'}, time);
271 };
271 };
272
272
273 this.element.bind('collapse_pager', function (event, extrap) {
273 this.element.bind('collapse_pager', function (event, extrap) {
274 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
274 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
275 collapse_time(time);
275 collapse_time(time);
276 });
276 });
277
277
278 var expand_time = function (time) {
278 var expand_time = function (time) {
279 var app_height = $('#ipython-main-app').height(); // content height
279 var app_height = $('#ipython-main-app').height(); // content height
280 var splitter_height = $('div#pager_splitter').outerHeight(true);
280 var splitter_height = $('div#pager_splitter').outerHeight(true);
281 var pager_height = $('div#pager').outerHeight(true);
281 var pager_height = $('div#pager').outerHeight(true);
282 var new_height = app_height - pager_height - splitter_height;
282 var new_height = app_height - pager_height - splitter_height;
283 that.element.animate({height : new_height + 'px'}, time);
283 that.element.animate({height : new_height + 'px'}, time);
284 };
284 };
285
285
286 this.element.bind('expand_pager', function (event, extrap) {
286 this.element.bind('expand_pager', function (event, extrap) {
287 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
287 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
288 expand_time(time);
288 expand_time(time);
289 });
289 });
290
290
291 // Firefox 22 broke $(window).on("beforeunload")
291 // Firefox 22 broke $(window).on("beforeunload")
292 // I'm not sure why or how.
292 // I'm not sure why or how.
293 window.onbeforeunload = function (e) {
293 window.onbeforeunload = function (e) {
294 // TODO: Make killing the kernel configurable.
294 // TODO: Make killing the kernel configurable.
295 var kill_kernel = false;
295 var kill_kernel = false;
296 if (kill_kernel) {
296 if (kill_kernel) {
297 that.session.delete();
297 that.session.delete();
298 }
298 }
299 // if we are autosaving, trigger an autosave on nav-away.
299 // if we are autosaving, trigger an autosave on nav-away.
300 // still warn, because if we don't the autosave may fail.
300 // still warn, because if we don't the autosave may fail.
301 if (that.dirty) {
301 if (that.dirty) {
302 if ( that.autosave_interval ) {
302 if ( that.autosave_interval ) {
303 // schedule autosave in a timeout
303 // schedule autosave in a timeout
304 // this gives you a chance to forcefully discard changes
304 // this gives you a chance to forcefully discard changes
305 // by reloading the page if you *really* want to.
305 // by reloading the page if you *really* want to.
306 // the timer doesn't start until you *dismiss* the dialog.
306 // the timer doesn't start until you *dismiss* the dialog.
307 setTimeout(function () {
307 setTimeout(function () {
308 if (that.dirty) {
308 if (that.dirty) {
309 that.save_notebook();
309 that.save_notebook();
310 }
310 }
311 }, 1000);
311 }, 1000);
312 return "Autosave in progress, latest changes may be lost.";
312 return "Autosave in progress, latest changes may be lost.";
313 } else {
313 } else {
314 return "Unsaved changes will be lost.";
314 return "Unsaved changes will be lost.";
315 }
315 }
316 }
316 }
317 // Null is the *only* return value that will make the browser not
317 // Null is the *only* return value that will make the browser not
318 // pop up the "don't leave" dialog.
318 // pop up the "don't leave" dialog.
319 return null;
319 return null;
320 };
320 };
321 };
321 };
322
322
323 Notebook.prototype.warn_nbformat_minor = function (event) {
323 Notebook.prototype.warn_nbformat_minor = function (event) {
324 /**
324 /**
325 * trigger a warning dialog about missing functionality from newer minor versions
325 * trigger a warning dialog about missing functionality from newer minor versions
326 */
326 */
327 var v = 'v' + this.nbformat + '.';
327 var v = 'v' + this.nbformat + '.';
328 var orig_vs = v + this.nbformat_minor;
328 var orig_vs = v + this.nbformat_minor;
329 var this_vs = v + this.current_nbformat_minor;
329 var this_vs = v + this.current_nbformat_minor;
330 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
330 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
331 this_vs + ". You can still work with this notebook, but cell and output types " +
331 this_vs + ". You can still work with this notebook, but cell and output types " +
332 "introduced in later notebook versions will not be available.";
332 "introduced in later notebook versions will not be available.";
333
333
334 dialog.modal({
334 dialog.modal({
335 notebook: this,
335 notebook: this,
336 keyboard_manager: this.keyboard_manager,
336 keyboard_manager: this.keyboard_manager,
337 title : "Newer Notebook",
337 title : "Newer Notebook",
338 body : msg,
338 body : msg,
339 buttons : {
339 buttons : {
340 OK : {
340 OK : {
341 "class" : "btn-danger"
341 "class" : "btn-danger"
342 }
342 }
343 }
343 }
344 });
344 });
345 }
345 }
346
346
347 /**
347 /**
348 * Set the dirty flag, and trigger the set_dirty.Notebook event
348 * Set the dirty flag, and trigger the set_dirty.Notebook event
349 *
349 *
350 * @method set_dirty
350 * @method set_dirty
351 */
351 */
352 Notebook.prototype.set_dirty = function (value) {
352 Notebook.prototype.set_dirty = function (value) {
353 if (value === undefined) {
353 if (value === undefined) {
354 value = true;
354 value = true;
355 }
355 }
356 if (this.dirty == value) {
356 if (this.dirty == value) {
357 return;
357 return;
358 }
358 }
359 this.events.trigger('set_dirty.Notebook', {value: value});
359 this.events.trigger('set_dirty.Notebook', {value: value});
360 };
360 };
361
361
362 /**
362 /**
363 * Scroll the top of the page to a given cell.
363 * Scroll the top of the page to a given cell.
364 *
364 *
365 * @method scroll_to_cell
365 * @method scroll_to_cell
366 * @param {Number} cell_number An index of the cell to view
366 * @param {Number} cell_number An index of the cell to view
367 * @param {Number} time Animation time in milliseconds
367 * @param {Number} time Animation time in milliseconds
368 * @return {Number} Pixel offset from the top of the container
368 * @return {Number} Pixel offset from the top of the container
369 */
369 */
370 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
370 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
371 var cells = this.get_cells();
371 var cells = this.get_cells();
372 time = time || 0;
372 time = time || 0;
373 cell_number = Math.min(cells.length-1,cell_number);
373 cell_number = Math.min(cells.length-1,cell_number);
374 cell_number = Math.max(0 ,cell_number);
374 cell_number = Math.max(0 ,cell_number);
375 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
375 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
376 this.element.animate({scrollTop:scroll_value}, time);
376 this.element.animate({scrollTop:scroll_value}, time);
377 return scroll_value;
377 return scroll_value;
378 };
378 };
379
379
380 /**
380 /**
381 * Scroll to the bottom of the page.
381 * Scroll to the bottom of the page.
382 *
382 *
383 * @method scroll_to_bottom
383 * @method scroll_to_bottom
384 */
384 */
385 Notebook.prototype.scroll_to_bottom = function () {
385 Notebook.prototype.scroll_to_bottom = function () {
386 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
386 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
387 };
387 };
388
388
389 /**
389 /**
390 * Scroll to the top of the page.
390 * Scroll to the top of the page.
391 *
391 *
392 * @method scroll_to_top
392 * @method scroll_to_top
393 */
393 */
394 Notebook.prototype.scroll_to_top = function () {
394 Notebook.prototype.scroll_to_top = function () {
395 this.element.animate({scrollTop:0}, 0);
395 this.element.animate({scrollTop:0}, 0);
396 };
396 };
397
397
398 // Edit Notebook metadata
398 // Edit Notebook metadata
399
399
400 Notebook.prototype.edit_metadata = function () {
400 Notebook.prototype.edit_metadata = function () {
401 var that = this;
401 var that = this;
402 dialog.edit_metadata({
402 dialog.edit_metadata({
403 md: this.metadata,
403 md: this.metadata,
404 callback: function (md) {
404 callback: function (md) {
405 that.metadata = md;
405 that.metadata = md;
406 },
406 },
407 name: 'Notebook',
407 name: 'Notebook',
408 notebook: this,
408 notebook: this,
409 keyboard_manager: this.keyboard_manager});
409 keyboard_manager: this.keyboard_manager});
410 };
410 };
411
411
412 // Cell indexing, retrieval, etc.
412 // Cell indexing, retrieval, etc.
413
413
414 /**
414 /**
415 * Get all cell elements in the notebook.
415 * Get all cell elements in the notebook.
416 *
416 *
417 * @method get_cell_elements
417 * @method get_cell_elements
418 * @return {jQuery} A selector of all cell elements
418 * @return {jQuery} A selector of all cell elements
419 */
419 */
420 Notebook.prototype.get_cell_elements = function () {
420 Notebook.prototype.get_cell_elements = function () {
421 return this.container.find(".cell").not('.cell .cell');
421 return this.container.find(".cell").not('.cell .cell');
422 };
422 };
423
423
424 /**
424 /**
425 * Get a particular cell element.
425 * Get a particular cell element.
426 *
426 *
427 * @method get_cell_element
427 * @method get_cell_element
428 * @param {Number} index An index of a cell to select
428 * @param {Number} index An index of a cell to select
429 * @return {jQuery} A selector of the given cell.
429 * @return {jQuery} A selector of the given cell.
430 */
430 */
431 Notebook.prototype.get_cell_element = function (index) {
431 Notebook.prototype.get_cell_element = function (index) {
432 var result = null;
432 var result = null;
433 var e = this.get_cell_elements().eq(index);
433 var e = this.get_cell_elements().eq(index);
434 if (e.length !== 0) {
434 if (e.length !== 0) {
435 result = e;
435 result = e;
436 }
436 }
437 return result;
437 return result;
438 };
438 };
439
439
440 /**
440 /**
441 * Try to get a particular cell by msg_id.
441 * Try to get a particular cell by msg_id.
442 *
442 *
443 * @method get_msg_cell
443 * @method get_msg_cell
444 * @param {String} msg_id A message UUID
444 * @param {String} msg_id A message UUID
445 * @return {Cell} Cell or null if no cell was found.
445 * @return {Cell} Cell or null if no cell was found.
446 */
446 */
447 Notebook.prototype.get_msg_cell = function (msg_id) {
447 Notebook.prototype.get_msg_cell = function (msg_id) {
448 return codecell.CodeCell.msg_cells[msg_id] || null;
448 return codecell.CodeCell.msg_cells[msg_id] || null;
449 };
449 };
450
450
451 /**
451 /**
452 * Count the cells in this notebook.
452 * Count the cells in this notebook.
453 *
453 *
454 * @method ncells
454 * @method ncells
455 * @return {Number} The number of cells in this notebook
455 * @return {Number} The number of cells in this notebook
456 */
456 */
457 Notebook.prototype.ncells = function () {
457 Notebook.prototype.ncells = function () {
458 return this.get_cell_elements().length;
458 return this.get_cell_elements().length;
459 };
459 };
460
460
461 /**
461 /**
462 * Get all Cell objects in this notebook.
462 * Get all Cell objects in this notebook.
463 *
463 *
464 * @method get_cells
464 * @method get_cells
465 * @return {Array} This notebook's Cell objects
465 * @return {Array} This notebook's Cell objects
466 */
466 */
467 // TODO: we are often calling cells as cells()[i], which we should optimize
467 // TODO: we are often calling cells as cells()[i], which we should optimize
468 // to cells(i) or a new method.
468 // to cells(i) or a new method.
469 Notebook.prototype.get_cells = function () {
469 Notebook.prototype.get_cells = function () {
470 return this.get_cell_elements().toArray().map(function (e) {
470 return this.get_cell_elements().toArray().map(function (e) {
471 return $(e).data("cell");
471 return $(e).data("cell");
472 });
472 });
473 };
473 };
474
474
475 /**
475 /**
476 * Get a Cell object from this notebook.
476 * Get a Cell object from this notebook.
477 *
477 *
478 * @method get_cell
478 * @method get_cell
479 * @param {Number} index An index of a cell to retrieve
479 * @param {Number} index An index of a cell to retrieve
480 * @return {Cell} Cell or null if no cell was found.
480 * @return {Cell} Cell or null if no cell was found.
481 */
481 */
482 Notebook.prototype.get_cell = function (index) {
482 Notebook.prototype.get_cell = function (index) {
483 var result = null;
483 var result = null;
484 var ce = this.get_cell_element(index);
484 var ce = this.get_cell_element(index);
485 if (ce !== null) {
485 if (ce !== null) {
486 result = ce.data('cell');
486 result = ce.data('cell');
487 }
487 }
488 return result;
488 return result;
489 };
489 };
490
490
491 /**
491 /**
492 * Get the cell below a given cell.
492 * Get the cell below a given cell.
493 *
493 *
494 * @method get_next_cell
494 * @method get_next_cell
495 * @param {Cell} cell The provided cell
495 * @param {Cell} cell The provided cell
496 * @return {Cell} the next cell or null if no cell was found.
496 * @return {Cell} the next cell or null if no cell was found.
497 */
497 */
498 Notebook.prototype.get_next_cell = function (cell) {
498 Notebook.prototype.get_next_cell = function (cell) {
499 var result = null;
499 var result = null;
500 var index = this.find_cell_index(cell);
500 var index = this.find_cell_index(cell);
501 if (this.is_valid_cell_index(index+1)) {
501 if (this.is_valid_cell_index(index+1)) {
502 result = this.get_cell(index+1);
502 result = this.get_cell(index+1);
503 }
503 }
504 return result;
504 return result;
505 };
505 };
506
506
507 /**
507 /**
508 * Get the cell above a given cell.
508 * Get the cell above a given cell.
509 *
509 *
510 * @method get_prev_cell
510 * @method get_prev_cell
511 * @param {Cell} cell The provided cell
511 * @param {Cell} cell The provided cell
512 * @return {Cell} The previous cell or null if no cell was found.
512 * @return {Cell} The previous cell or null if no cell was found.
513 */
513 */
514 Notebook.prototype.get_prev_cell = function (cell) {
514 Notebook.prototype.get_prev_cell = function (cell) {
515 var result = null;
515 var result = null;
516 var index = this.find_cell_index(cell);
516 var index = this.find_cell_index(cell);
517 if (index !== null && index > 0) {
517 if (index !== null && index > 0) {
518 result = this.get_cell(index-1);
518 result = this.get_cell(index-1);
519 }
519 }
520 return result;
520 return result;
521 };
521 };
522
522
523 /**
523 /**
524 * Get the numeric index of a given cell.
524 * Get the numeric index of a given cell.
525 *
525 *
526 * @method find_cell_index
526 * @method find_cell_index
527 * @param {Cell} cell The provided cell
527 * @param {Cell} cell The provided cell
528 * @return {Number} The cell's numeric index or null if no cell was found.
528 * @return {Number} The cell's numeric index or null if no cell was found.
529 */
529 */
530 Notebook.prototype.find_cell_index = function (cell) {
530 Notebook.prototype.find_cell_index = function (cell) {
531 var result = null;
531 var result = null;
532 this.get_cell_elements().filter(function (index) {
532 this.get_cell_elements().filter(function (index) {
533 if ($(this).data("cell") === cell) {
533 if ($(this).data("cell") === cell) {
534 result = index;
534 result = index;
535 }
535 }
536 });
536 });
537 return result;
537 return result;
538 };
538 };
539
539
540 /**
540 /**
541 * Get a given index , or the selected index if none is provided.
541 * Get a given index , or the selected index if none is provided.
542 *
542 *
543 * @method index_or_selected
543 * @method index_or_selected
544 * @param {Number} index A cell's index
544 * @param {Number} index A cell's index
545 * @return {Number} The given index, or selected index if none is provided.
545 * @return {Number} The given index, or selected index if none is provided.
546 */
546 */
547 Notebook.prototype.index_or_selected = function (index) {
547 Notebook.prototype.index_or_selected = function (index) {
548 var i;
548 var i;
549 if (index === undefined || index === null) {
549 if (index === undefined || index === null) {
550 i = this.get_selected_index();
550 i = this.get_selected_index();
551 if (i === null) {
551 if (i === null) {
552 i = 0;
552 i = 0;
553 }
553 }
554 } else {
554 } else {
555 i = index;
555 i = index;
556 }
556 }
557 return i;
557 return i;
558 };
558 };
559
559
560 /**
560 /**
561 * Get the currently selected cell.
561 * Get the currently selected cell.
562 * @method get_selected_cell
562 * @method get_selected_cell
563 * @return {Cell} The selected cell
563 * @return {Cell} The selected cell
564 */
564 */
565 Notebook.prototype.get_selected_cell = function () {
565 Notebook.prototype.get_selected_cell = function () {
566 var index = this.get_selected_index();
566 var index = this.get_selected_index();
567 return this.get_cell(index);
567 return this.get_cell(index);
568 };
568 };
569
569
570 /**
570 /**
571 * Check whether a cell index is valid.
571 * Check whether a cell index is valid.
572 *
572 *
573 * @method is_valid_cell_index
573 * @method is_valid_cell_index
574 * @param {Number} index A cell index
574 * @param {Number} index A cell index
575 * @return True if the index is valid, false otherwise
575 * @return True if the index is valid, false otherwise
576 */
576 */
577 Notebook.prototype.is_valid_cell_index = function (index) {
577 Notebook.prototype.is_valid_cell_index = function (index) {
578 if (index !== null && index >= 0 && index < this.ncells()) {
578 if (index !== null && index >= 0 && index < this.ncells()) {
579 return true;
579 return true;
580 } else {
580 } else {
581 return false;
581 return false;
582 }
582 }
583 };
583 };
584
584
585 /**
585 /**
586 * Get the index of the currently selected cell.
586 * Get the index of the currently selected cell.
587
587
588 * @method get_selected_index
588 * @method get_selected_index
589 * @return {Number} The selected cell's numeric index
589 * @return {Number} The selected cell's numeric index
590 */
590 */
591 Notebook.prototype.get_selected_index = function () {
591 Notebook.prototype.get_selected_index = function () {
592 var result = null;
592 var result = null;
593 this.get_cell_elements().filter(function (index) {
593 this.get_cell_elements().filter(function (index) {
594 if ($(this).data("cell").selected === true) {
594 if ($(this).data("cell").selected === true) {
595 result = index;
595 result = index;
596 }
596 }
597 });
597 });
598 return result;
598 return result;
599 };
599 };
600
600
601
601
602 // Cell selection.
602 // Cell selection.
603
603
604 /**
604 /**
605 * Programmatically select a cell.
605 * Programmatically select a cell.
606 *
606 *
607 * @method select
607 * @method select
608 * @param {Number} index A cell's index
608 * @param {Number} index A cell's index
609 * @return {Notebook} This notebook
609 * @return {Notebook} This notebook
610 */
610 */
611 Notebook.prototype.select = function (index) {
611 Notebook.prototype.select = function (index) {
612 if (this.is_valid_cell_index(index)) {
612 if (this.is_valid_cell_index(index)) {
613 var sindex = this.get_selected_index();
613 var sindex = this.get_selected_index();
614 if (sindex !== null && index !== sindex) {
614 if (sindex !== null && index !== sindex) {
615 // If we are about to select a different cell, make sure we are
615 // If we are about to select a different cell, make sure we are
616 // first in command mode.
616 // first in command mode.
617 if (this.mode !== 'command') {
617 if (this.mode !== 'command') {
618 this.command_mode();
618 this.command_mode();
619 }
619 }
620 this.get_cell(sindex).unselect();
620 this.get_cell(sindex).unselect();
621 }
621 }
622 var cell = this.get_cell(index);
622 var cell = this.get_cell(index);
623 cell.select();
623 cell.select();
624 if (cell.cell_type === 'heading') {
624 if (cell.cell_type === 'heading') {
625 this.events.trigger('selected_cell_type_changed.Notebook',
625 this.events.trigger('selected_cell_type_changed.Notebook',
626 {'cell_type':cell.cell_type,level:cell.level}
626 {'cell_type':cell.cell_type,level:cell.level}
627 );
627 );
628 } else {
628 } else {
629 this.events.trigger('selected_cell_type_changed.Notebook',
629 this.events.trigger('selected_cell_type_changed.Notebook',
630 {'cell_type':cell.cell_type}
630 {'cell_type':cell.cell_type}
631 );
631 );
632 }
632 }
633 }
633 }
634 return this;
634 return this;
635 };
635 };
636
636
637 /**
637 /**
638 * Programmatically select the next cell.
638 * Programmatically select the next cell.
639 *
639 *
640 * @method select_next
640 * @method select_next
641 * @return {Notebook} This notebook
641 * @return {Notebook} This notebook
642 */
642 */
643 Notebook.prototype.select_next = function () {
643 Notebook.prototype.select_next = function () {
644 var index = this.get_selected_index();
644 var index = this.get_selected_index();
645 this.select(index+1);
645 this.select(index+1);
646 return this;
646 return this;
647 };
647 };
648
648
649 /**
649 /**
650 * Programmatically select the previous cell.
650 * Programmatically select the previous cell.
651 *
651 *
652 * @method select_prev
652 * @method select_prev
653 * @return {Notebook} This notebook
653 * @return {Notebook} This notebook
654 */
654 */
655 Notebook.prototype.select_prev = function () {
655 Notebook.prototype.select_prev = function () {
656 var index = this.get_selected_index();
656 var index = this.get_selected_index();
657 this.select(index-1);
657 this.select(index-1);
658 return this;
658 return this;
659 };
659 };
660
660
661
661
662 // Edit/Command mode
662 // Edit/Command mode
663
663
664 /**
664 /**
665 * Gets the index of the cell that is in edit mode.
665 * Gets the index of the cell that is in edit mode.
666 *
666 *
667 * @method get_edit_index
667 * @method get_edit_index
668 *
668 *
669 * @return index {int}
669 * @return index {int}
670 **/
670 **/
671 Notebook.prototype.get_edit_index = function () {
671 Notebook.prototype.get_edit_index = function () {
672 var result = null;
672 var result = null;
673 this.get_cell_elements().filter(function (index) {
673 this.get_cell_elements().filter(function (index) {
674 if ($(this).data("cell").mode === 'edit') {
674 if ($(this).data("cell").mode === 'edit') {
675 result = index;
675 result = index;
676 }
676 }
677 });
677 });
678 return result;
678 return result;
679 };
679 };
680
680
681 /**
681 /**
682 * Handle when a a cell blurs and the notebook should enter command mode.
682 * Handle when a a cell blurs and the notebook should enter command mode.
683 *
683 *
684 * @method handle_command_mode
684 * @method handle_command_mode
685 * @param [cell] {Cell} Cell to enter command mode on.
685 * @param [cell] {Cell} Cell to enter command mode on.
686 **/
686 **/
687 Notebook.prototype.handle_command_mode = function (cell) {
687 Notebook.prototype.handle_command_mode = function (cell) {
688 if (this.mode !== 'command') {
688 if (this.mode !== 'command') {
689 cell.command_mode();
689 cell.command_mode();
690 this.mode = 'command';
690 this.mode = 'command';
691 this.events.trigger('command_mode.Notebook');
691 this.events.trigger('command_mode.Notebook');
692 this.keyboard_manager.command_mode();
692 this.keyboard_manager.command_mode();
693 }
693 }
694 };
694 };
695
695
696 /**
696 /**
697 * Make the notebook enter command mode.
697 * Make the notebook enter command mode.
698 *
698 *
699 * @method command_mode
699 * @method command_mode
700 **/
700 **/
701 Notebook.prototype.command_mode = function () {
701 Notebook.prototype.command_mode = function () {
702 var cell = this.get_cell(this.get_edit_index());
702 var cell = this.get_cell(this.get_edit_index());
703 if (cell && this.mode !== 'command') {
703 if (cell && this.mode !== 'command') {
704 // We don't call cell.command_mode, but rather call cell.focus_cell()
704 // We don't call cell.command_mode, but rather call cell.focus_cell()
705 // which will blur and CM editor and trigger the call to
705 // which will blur and CM editor and trigger the call to
706 // handle_command_mode.
706 // handle_command_mode.
707 cell.focus_cell();
707 cell.focus_cell();
708 }
708 }
709 };
709 };
710
710
711 /**
711 /**
712 * Handle when a cell fires it's edit_mode event.
712 * Handle when a cell fires it's edit_mode event.
713 *
713 *
714 * @method handle_edit_mode
714 * @method handle_edit_mode
715 * @param [cell] {Cell} Cell to enter edit mode on.
715 * @param [cell] {Cell} Cell to enter edit mode on.
716 **/
716 **/
717 Notebook.prototype.handle_edit_mode = function (cell) {
717 Notebook.prototype.handle_edit_mode = function (cell) {
718 if (cell && this.mode !== 'edit') {
718 if (cell && this.mode !== 'edit') {
719 cell.edit_mode();
719 cell.edit_mode();
720 this.mode = 'edit';
720 this.mode = 'edit';
721 this.events.trigger('edit_mode.Notebook');
721 this.events.trigger('edit_mode.Notebook');
722 this.keyboard_manager.edit_mode();
722 this.keyboard_manager.edit_mode();
723 }
723 }
724 };
724 };
725
725
726 /**
726 /**
727 * Make a cell enter edit mode.
727 * Make a cell enter edit mode.
728 *
728 *
729 * @method edit_mode
729 * @method edit_mode
730 **/
730 **/
731 Notebook.prototype.edit_mode = function () {
731 Notebook.prototype.edit_mode = function () {
732 var cell = this.get_selected_cell();
732 var cell = this.get_selected_cell();
733 if (cell && this.mode !== 'edit') {
733 if (cell && this.mode !== 'edit') {
734 cell.unrender();
734 cell.unrender();
735 cell.focus_editor();
735 cell.focus_editor();
736 }
736 }
737 };
737 };
738
738
739 /**
739 /**
740 * Focus the currently selected cell.
740 * Focus the currently selected cell.
741 *
741 *
742 * @method focus_cell
742 * @method focus_cell
743 **/
743 **/
744 Notebook.prototype.focus_cell = function () {
744 Notebook.prototype.focus_cell = function () {
745 var cell = this.get_selected_cell();
745 var cell = this.get_selected_cell();
746 if (cell === null) {return;} // No cell is selected
746 if (cell === null) {return;} // No cell is selected
747 cell.focus_cell();
747 cell.focus_cell();
748 };
748 };
749
749
750 // Cell movement
750 // Cell movement
751
751
752 /**
752 /**
753 * Move given (or selected) cell up and select it.
753 * Move given (or selected) cell up and select it.
754 *
754 *
755 * @method move_cell_up
755 * @method move_cell_up
756 * @param [index] {integer} cell index
756 * @param [index] {integer} cell index
757 * @return {Notebook} This notebook
757 * @return {Notebook} This notebook
758 **/
758 **/
759 Notebook.prototype.move_cell_up = function (index) {
759 Notebook.prototype.move_cell_up = function (index) {
760 var i = this.index_or_selected(index);
760 var i = this.index_or_selected(index);
761 if (this.is_valid_cell_index(i) && i > 0) {
761 if (this.is_valid_cell_index(i) && i > 0) {
762 var pivot = this.get_cell_element(i-1);
762 var pivot = this.get_cell_element(i-1);
763 var tomove = this.get_cell_element(i);
763 var tomove = this.get_cell_element(i);
764 if (pivot !== null && tomove !== null) {
764 if (pivot !== null && tomove !== null) {
765 tomove.detach();
765 tomove.detach();
766 pivot.before(tomove);
766 pivot.before(tomove);
767 this.select(i-1);
767 this.select(i-1);
768 var cell = this.get_selected_cell();
768 var cell = this.get_selected_cell();
769 cell.focus_cell();
769 cell.focus_cell();
770 }
770 }
771 this.set_dirty(true);
771 this.set_dirty(true);
772 }
772 }
773 return this;
773 return this;
774 };
774 };
775
775
776
776
777 /**
777 /**
778 * Move given (or selected) cell down and select it
778 * Move given (or selected) cell down and select it
779 *
779 *
780 * @method move_cell_down
780 * @method move_cell_down
781 * @param [index] {integer} cell index
781 * @param [index] {integer} cell index
782 * @return {Notebook} This notebook
782 * @return {Notebook} This notebook
783 **/
783 **/
784 Notebook.prototype.move_cell_down = function (index) {
784 Notebook.prototype.move_cell_down = function (index) {
785 var i = this.index_or_selected(index);
785 var i = this.index_or_selected(index);
786 if (this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
786 if (this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
787 var pivot = this.get_cell_element(i+1);
787 var pivot = this.get_cell_element(i+1);
788 var tomove = this.get_cell_element(i);
788 var tomove = this.get_cell_element(i);
789 if (pivot !== null && tomove !== null) {
789 if (pivot !== null && tomove !== null) {
790 tomove.detach();
790 tomove.detach();
791 pivot.after(tomove);
791 pivot.after(tomove);
792 this.select(i+1);
792 this.select(i+1);
793 var cell = this.get_selected_cell();
793 var cell = this.get_selected_cell();
794 cell.focus_cell();
794 cell.focus_cell();
795 }
795 }
796 }
796 }
797 this.set_dirty();
797 this.set_dirty();
798 return this;
798 return this;
799 };
799 };
800
800
801
801
802 // Insertion, deletion.
802 // Insertion, deletion.
803
803
804 /**
804 /**
805 * Delete a cell from the notebook.
805 * Delete a cell from the notebook.
806 *
806 *
807 * @method delete_cell
807 * @method delete_cell
808 * @param [index] A cell's numeric index
808 * @param [index] A cell's numeric index
809 * @return {Notebook} This notebook
809 * @return {Notebook} This notebook
810 */
810 */
811 Notebook.prototype.delete_cell = function (index) {
811 Notebook.prototype.delete_cell = function (index) {
812 var i = this.index_or_selected(index);
812 var i = this.index_or_selected(index);
813 var cell = this.get_cell(i);
813 var cell = this.get_cell(i);
814 if (!cell.is_deletable()) {
814 if (!cell.is_deletable()) {
815 return this;
815 return this;
816 }
816 }
817
817
818 this.undelete_backup = cell.toJSON();
818 this.undelete_backup = cell.toJSON();
819 $('#undelete_cell').removeClass('disabled');
819 $('#undelete_cell').removeClass('disabled');
820 if (this.is_valid_cell_index(i)) {
820 if (this.is_valid_cell_index(i)) {
821 var old_ncells = this.ncells();
821 var old_ncells = this.ncells();
822 var ce = this.get_cell_element(i);
822 var ce = this.get_cell_element(i);
823 ce.remove();
823 ce.remove();
824 if (i === 0) {
824 if (i === 0) {
825 // Always make sure we have at least one cell.
825 // Always make sure we have at least one cell.
826 if (old_ncells === 1) {
826 if (old_ncells === 1) {
827 this.insert_cell_below('code');
827 this.insert_cell_below('code');
828 }
828 }
829 this.select(0);
829 this.select(0);
830 this.undelete_index = 0;
830 this.undelete_index = 0;
831 this.undelete_below = false;
831 this.undelete_below = false;
832 } else if (i === old_ncells-1 && i !== 0) {
832 } else if (i === old_ncells-1 && i !== 0) {
833 this.select(i-1);
833 this.select(i-1);
834 this.undelete_index = i - 1;
834 this.undelete_index = i - 1;
835 this.undelete_below = true;
835 this.undelete_below = true;
836 } else {
836 } else {
837 this.select(i);
837 this.select(i);
838 this.undelete_index = i;
838 this.undelete_index = i;
839 this.undelete_below = false;
839 this.undelete_below = false;
840 }
840 }
841 this.events.trigger('delete.Cell', {'cell': cell, 'index': i});
841 this.events.trigger('delete.Cell', {'cell': cell, 'index': i});
842 this.set_dirty(true);
842 this.set_dirty(true);
843 }
843 }
844 return this;
844 return this;
845 };
845 };
846
846
847 /**
847 /**
848 * Restore the most recently deleted cell.
848 * Restore the most recently deleted cell.
849 *
849 *
850 * @method undelete
850 * @method undelete
851 */
851 */
852 Notebook.prototype.undelete_cell = function() {
852 Notebook.prototype.undelete_cell = function() {
853 if (this.undelete_backup !== null && this.undelete_index !== null) {
853 if (this.undelete_backup !== null && this.undelete_index !== null) {
854 var current_index = this.get_selected_index();
854 var current_index = this.get_selected_index();
855 if (this.undelete_index < current_index) {
855 if (this.undelete_index < current_index) {
856 current_index = current_index + 1;
856 current_index = current_index + 1;
857 }
857 }
858 if (this.undelete_index >= this.ncells()) {
858 if (this.undelete_index >= this.ncells()) {
859 this.select(this.ncells() - 1);
859 this.select(this.ncells() - 1);
860 }
860 }
861 else {
861 else {
862 this.select(this.undelete_index);
862 this.select(this.undelete_index);
863 }
863 }
864 var cell_data = this.undelete_backup;
864 var cell_data = this.undelete_backup;
865 var new_cell = null;
865 var new_cell = null;
866 if (this.undelete_below) {
866 if (this.undelete_below) {
867 new_cell = this.insert_cell_below(cell_data.cell_type);
867 new_cell = this.insert_cell_below(cell_data.cell_type);
868 } else {
868 } else {
869 new_cell = this.insert_cell_above(cell_data.cell_type);
869 new_cell = this.insert_cell_above(cell_data.cell_type);
870 }
870 }
871 new_cell.fromJSON(cell_data);
871 new_cell.fromJSON(cell_data);
872 if (this.undelete_below) {
872 if (this.undelete_below) {
873 this.select(current_index+1);
873 this.select(current_index+1);
874 } else {
874 } else {
875 this.select(current_index);
875 this.select(current_index);
876 }
876 }
877 this.undelete_backup = null;
877 this.undelete_backup = null;
878 this.undelete_index = null;
878 this.undelete_index = null;
879 }
879 }
880 $('#undelete_cell').addClass('disabled');
880 $('#undelete_cell').addClass('disabled');
881 };
881 };
882
882
883 /**
883 /**
884 * Insert a cell so that after insertion the cell is at given index.
884 * Insert a cell so that after insertion the cell is at given index.
885 *
885 *
886 * If cell type is not provided, it will default to the type of the
886 * If cell type is not provided, it will default to the type of the
887 * currently active cell.
887 * currently active cell.
888 *
888 *
889 * Similar to insert_above, but index parameter is mandatory
889 * Similar to insert_above, but index parameter is mandatory
890 *
890 *
891 * Index will be brought back into the accessible range [0,n]
891 * Index will be brought back into the accessible range [0,n]
892 *
892 *
893 * @method insert_cell_at_index
893 * @method insert_cell_at_index
894 * @param [type] {string} in ['code','markdown', 'raw'], defaults to 'code'
894 * @param [type] {string} in ['code','markdown', 'raw'], defaults to 'code'
895 * @param [index] {int} a valid index where to insert cell
895 * @param [index] {int} a valid index where to insert cell
896 *
896 *
897 * @return cell {cell|null} created cell or null
897 * @return cell {cell|null} created cell or null
898 **/
898 **/
899 Notebook.prototype.insert_cell_at_index = function(type, index){
899 Notebook.prototype.insert_cell_at_index = function(type, index){
900
900
901 var ncells = this.ncells();
901 var ncells = this.ncells();
902 index = Math.min(index, ncells);
902 index = Math.min(index, ncells);
903 index = Math.max(index, 0);
903 index = Math.max(index, 0);
904 var cell = null;
904 var cell = null;
905 type = type || this.default_cell_type;
905 type = type || this.default_cell_type;
906 if (type === 'above') {
906 if (type === 'above') {
907 if (index > 0) {
907 if (index > 0) {
908 type = this.get_cell(index-1).cell_type;
908 type = this.get_cell(index-1).cell_type;
909 } else {
909 } else {
910 type = 'code';
910 type = 'code';
911 }
911 }
912 } else if (type === 'below') {
912 } else if (type === 'below') {
913 if (index < ncells) {
913 if (index < ncells) {
914 type = this.get_cell(index).cell_type;
914 type = this.get_cell(index).cell_type;
915 } else {
915 } else {
916 type = 'code';
916 type = 'code';
917 }
917 }
918 } else if (type === 'selected') {
918 } else if (type === 'selected') {
919 type = this.get_selected_cell().cell_type;
919 type = this.get_selected_cell().cell_type;
920 }
920 }
921
921
922 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
922 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
923 var cell_options = {
923 var cell_options = {
924 events: this.events,
924 events: this.events,
925 config: this.config,
925 config: this.config,
926 keyboard_manager: this.keyboard_manager,
926 keyboard_manager: this.keyboard_manager,
927 notebook: this,
927 notebook: this,
928 tooltip: this.tooltip
928 tooltip: this.tooltip
929 };
929 };
930 switch(type) {
930 switch(type) {
931 case 'code':
931 case 'code':
932 cell = new codecell.CodeCell(this.kernel, cell_options);
932 cell = new codecell.CodeCell(this.kernel, cell_options);
933 cell.set_input_prompt();
933 cell.set_input_prompt();
934 break;
934 break;
935 case 'markdown':
935 case 'markdown':
936 cell = new textcell.MarkdownCell(cell_options);
936 cell = new textcell.MarkdownCell(cell_options);
937 break;
937 break;
938 case 'raw':
938 case 'raw':
939 cell = new textcell.RawCell(cell_options);
939 cell = new textcell.RawCell(cell_options);
940 break;
940 break;
941 default:
941 default:
942 console.log("Unrecognized cell type: ", type, cellmod);
942 console.log("Unrecognized cell type: ", type, cellmod);
943 cell = new cellmod.UnrecognizedCell(cell_options);
943 cell = new cellmod.UnrecognizedCell(cell_options);
944 }
944 }
945
945
946 if(this._insert_element_at_index(cell.element,index)) {
946 if(this._insert_element_at_index(cell.element,index)) {
947 cell.render();
947 cell.render();
948 this.events.trigger('create.Cell', {'cell': cell, 'index': index});
948 this.events.trigger('create.Cell', {'cell': cell, 'index': index});
949 cell.refresh();
949 cell.refresh();
950 // We used to select the cell after we refresh it, but there
950 // We used to select the cell after we refresh it, but there
951 // are now cases were this method is called where select is
951 // are now cases were this method is called where select is
952 // not appropriate. The selection logic should be handled by the
952 // not appropriate. The selection logic should be handled by the
953 // caller of the the top level insert_cell methods.
953 // caller of the the top level insert_cell methods.
954 this.set_dirty(true);
954 this.set_dirty(true);
955 }
955 }
956 }
956 }
957 return cell;
957 return cell;
958
958
959 };
959 };
960
960
961 /**
961 /**
962 * Insert an element at given cell index.
962 * Insert an element at given cell index.
963 *
963 *
964 * @method _insert_element_at_index
964 * @method _insert_element_at_index
965 * @param element {dom_element} a cell element
965 * @param element {dom_element} a cell element
966 * @param [index] {int} a valid index where to inser cell
966 * @param [index] {int} a valid index where to inser cell
967 * @private
967 * @private
968 *
968 *
969 * return true if everything whent fine.
969 * return true if everything whent fine.
970 **/
970 **/
971 Notebook.prototype._insert_element_at_index = function(element, index){
971 Notebook.prototype._insert_element_at_index = function(element, index){
972 if (element === undefined){
972 if (element === undefined){
973 return false;
973 return false;
974 }
974 }
975
975
976 var ncells = this.ncells();
976 var ncells = this.ncells();
977
977
978 if (ncells === 0) {
978 if (ncells === 0) {
979 // special case append if empty
979 // special case append if empty
980 this.element.find('div.end_space').before(element);
980 this.element.find('div.end_space').before(element);
981 } else if ( ncells === index ) {
981 } else if ( ncells === index ) {
982 // special case append it the end, but not empty
982 // special case append it the end, but not empty
983 this.get_cell_element(index-1).after(element);
983 this.get_cell_element(index-1).after(element);
984 } else if (this.is_valid_cell_index(index)) {
984 } else if (this.is_valid_cell_index(index)) {
985 // otherwise always somewhere to append to
985 // otherwise always somewhere to append to
986 this.get_cell_element(index).before(element);
986 this.get_cell_element(index).before(element);
987 } else {
987 } else {
988 return false;
988 return false;
989 }
989 }
990
990
991 if (this.undelete_index !== null && index <= this.undelete_index) {
991 if (this.undelete_index !== null && index <= this.undelete_index) {
992 this.undelete_index = this.undelete_index + 1;
992 this.undelete_index = this.undelete_index + 1;
993 this.set_dirty(true);
993 this.set_dirty(true);
994 }
994 }
995 return true;
995 return true;
996 };
996 };
997
997
998 /**
998 /**
999 * Insert a cell of given type above given index, or at top
999 * Insert a cell of given type above given index, or at top
1000 * of notebook if index smaller than 0.
1000 * of notebook if index smaller than 0.
1001 *
1001 *
1002 * default index value is the one of currently selected cell
1002 * default index value is the one of currently selected cell
1003 *
1003 *
1004 * @method insert_cell_above
1004 * @method insert_cell_above
1005 * @param [type] {string} cell type
1005 * @param [type] {string} cell type
1006 * @param [index] {integer}
1006 * @param [index] {integer}
1007 *
1007 *
1008 * @return handle to created cell or null
1008 * @return handle to created cell or null
1009 **/
1009 **/
1010 Notebook.prototype.insert_cell_above = function (type, index) {
1010 Notebook.prototype.insert_cell_above = function (type, index) {
1011 index = this.index_or_selected(index);
1011 index = this.index_or_selected(index);
1012 return this.insert_cell_at_index(type, index);
1012 return this.insert_cell_at_index(type, index);
1013 };
1013 };
1014
1014
1015 /**
1015 /**
1016 * Insert a cell of given type below given index, or at bottom
1016 * Insert a cell of given type below given index, or at bottom
1017 * of notebook if index greater than number of cells
1017 * of notebook if index greater than number of cells
1018 *
1018 *
1019 * default index value is the one of currently selected cell
1019 * default index value is the one of currently selected cell
1020 *
1020 *
1021 * @method insert_cell_below
1021 * @method insert_cell_below
1022 * @param [type] {string} cell type
1022 * @param [type] {string} cell type
1023 * @param [index] {integer}
1023 * @param [index] {integer}
1024 *
1024 *
1025 * @return handle to created cell or null
1025 * @return handle to created cell or null
1026 *
1026 *
1027 **/
1027 **/
1028 Notebook.prototype.insert_cell_below = function (type, index) {
1028 Notebook.prototype.insert_cell_below = function (type, index) {
1029 index = this.index_or_selected(index);
1029 index = this.index_or_selected(index);
1030 return this.insert_cell_at_index(type, index+1);
1030 return this.insert_cell_at_index(type, index+1);
1031 };
1031 };
1032
1032
1033
1033
1034 /**
1034 /**
1035 * Insert cell at end of notebook
1035 * Insert cell at end of notebook
1036 *
1036 *
1037 * @method insert_cell_at_bottom
1037 * @method insert_cell_at_bottom
1038 * @param {String} type cell type
1038 * @param {String} type cell type
1039 *
1039 *
1040 * @return the added cell; or null
1040 * @return the added cell; or null
1041 **/
1041 **/
1042 Notebook.prototype.insert_cell_at_bottom = function (type){
1042 Notebook.prototype.insert_cell_at_bottom = function (type){
1043 var len = this.ncells();
1043 var len = this.ncells();
1044 return this.insert_cell_below(type,len-1);
1044 return this.insert_cell_below(type,len-1);
1045 };
1045 };
1046
1046
1047 /**
1047 /**
1048 * Turn a cell into a code cell.
1048 * Turn a cell into a code cell.
1049 *
1049 *
1050 * @method to_code
1050 * @method to_code
1051 * @param {Number} [index] A cell's index
1051 * @param {Number} [index] A cell's index
1052 */
1052 */
1053 Notebook.prototype.to_code = function (index) {
1053 Notebook.prototype.to_code = function (index) {
1054 var i = this.index_or_selected(index);
1054 var i = this.index_or_selected(index);
1055 if (this.is_valid_cell_index(i)) {
1055 if (this.is_valid_cell_index(i)) {
1056 var source_cell = this.get_cell(i);
1056 var source_cell = this.get_cell(i);
1057 if (!(source_cell instanceof codecell.CodeCell)) {
1057 if (!(source_cell instanceof codecell.CodeCell)) {
1058 var target_cell = this.insert_cell_below('code',i);
1058 var target_cell = this.insert_cell_below('code',i);
1059 var text = source_cell.get_text();
1059 var text = source_cell.get_text();
1060 if (text === source_cell.placeholder) {
1060 if (text === source_cell.placeholder) {
1061 text = '';
1061 text = '';
1062 }
1062 }
1063 //metadata
1063 //metadata
1064 target_cell.metadata = source_cell.metadata;
1064 target_cell.metadata = source_cell.metadata;
1065
1065
1066 target_cell.set_text(text);
1066 target_cell.set_text(text);
1067 // make this value the starting point, so that we can only undo
1067 // make this value the starting point, so that we can only undo
1068 // to this state, instead of a blank cell
1068 // to this state, instead of a blank cell
1069 target_cell.code_mirror.clearHistory();
1069 target_cell.code_mirror.clearHistory();
1070 source_cell.element.remove();
1070 source_cell.element.remove();
1071 this.select(i);
1071 this.select(i);
1072 var cursor = source_cell.code_mirror.getCursor();
1072 var cursor = source_cell.code_mirror.getCursor();
1073 target_cell.code_mirror.setCursor(cursor);
1073 target_cell.code_mirror.setCursor(cursor);
1074 this.set_dirty(true);
1074 this.set_dirty(true);
1075 }
1075 }
1076 }
1076 }
1077 };
1077 };
1078
1078
1079 /**
1079 /**
1080 * Turn a cell into a Markdown cell.
1080 * Turn a cell into a Markdown cell.
1081 *
1081 *
1082 * @method to_markdown
1082 * @method to_markdown
1083 * @param {Number} [index] A cell's index
1083 * @param {Number} [index] A cell's index
1084 */
1084 */
1085 Notebook.prototype.to_markdown = function (index) {
1085 Notebook.prototype.to_markdown = function (index) {
1086 var i = this.index_or_selected(index);
1086 var i = this.index_or_selected(index);
1087 if (this.is_valid_cell_index(i)) {
1087 if (this.is_valid_cell_index(i)) {
1088 var source_cell = this.get_cell(i);
1088 var source_cell = this.get_cell(i);
1089
1089
1090 if (!(source_cell instanceof textcell.MarkdownCell)) {
1090 if (!(source_cell instanceof textcell.MarkdownCell)) {
1091 var target_cell = this.insert_cell_below('markdown',i);
1091 var target_cell = this.insert_cell_below('markdown',i);
1092 var text = source_cell.get_text();
1092 var text = source_cell.get_text();
1093
1093
1094 if (text === source_cell.placeholder) {
1094 if (text === source_cell.placeholder) {
1095 text = '';
1095 text = '';
1096 }
1096 }
1097 // metadata
1097 // metadata
1098 target_cell.metadata = source_cell.metadata;
1098 target_cell.metadata = source_cell.metadata;
1099 // We must show the editor before setting its contents
1099 // We must show the editor before setting its contents
1100 target_cell.unrender();
1100 target_cell.unrender();
1101 target_cell.set_text(text);
1101 target_cell.set_text(text);
1102 // make this value the starting point, so that we can only undo
1102 // make this value the starting point, so that we can only undo
1103 // to this state, instead of a blank cell
1103 // to this state, instead of a blank cell
1104 target_cell.code_mirror.clearHistory();
1104 target_cell.code_mirror.clearHistory();
1105 source_cell.element.remove();
1105 source_cell.element.remove();
1106 this.select(i);
1106 this.select(i);
1107 if ((source_cell instanceof textcell.TextCell) && source_cell.rendered) {
1107 if ((source_cell instanceof textcell.TextCell) && source_cell.rendered) {
1108 target_cell.render();
1108 target_cell.render();
1109 }
1109 }
1110 var cursor = source_cell.code_mirror.getCursor();
1110 var cursor = source_cell.code_mirror.getCursor();
1111 target_cell.code_mirror.setCursor(cursor);
1111 target_cell.code_mirror.setCursor(cursor);
1112 this.set_dirty(true);
1112 this.set_dirty(true);
1113 }
1113 }
1114 }
1114 }
1115 };
1115 };
1116
1116
1117 /**
1117 /**
1118 * Turn a cell into a raw text cell.
1118 * Turn a cell into a raw text cell.
1119 *
1119 *
1120 * @method to_raw
1120 * @method to_raw
1121 * @param {Number} [index] A cell's index
1121 * @param {Number} [index] A cell's index
1122 */
1122 */
1123 Notebook.prototype.to_raw = function (index) {
1123 Notebook.prototype.to_raw = function (index) {
1124 var i = this.index_or_selected(index);
1124 var i = this.index_or_selected(index);
1125 if (this.is_valid_cell_index(i)) {
1125 if (this.is_valid_cell_index(i)) {
1126 var target_cell = null;
1126 var target_cell = null;
1127 var source_cell = this.get_cell(i);
1127 var source_cell = this.get_cell(i);
1128
1128
1129 if (!(source_cell instanceof textcell.RawCell)) {
1129 if (!(source_cell instanceof textcell.RawCell)) {
1130 target_cell = this.insert_cell_below('raw',i);
1130 target_cell = this.insert_cell_below('raw',i);
1131 var text = source_cell.get_text();
1131 var text = source_cell.get_text();
1132 if (text === source_cell.placeholder) {
1132 if (text === source_cell.placeholder) {
1133 text = '';
1133 text = '';
1134 }
1134 }
1135 //metadata
1135 //metadata
1136 target_cell.metadata = source_cell.metadata;
1136 target_cell.metadata = source_cell.metadata;
1137 // We must show the editor before setting its contents
1137 // We must show the editor before setting its contents
1138 target_cell.unrender();
1138 target_cell.unrender();
1139 target_cell.set_text(text);
1139 target_cell.set_text(text);
1140 // make this value the starting point, so that we can only undo
1140 // make this value the starting point, so that we can only undo
1141 // to this state, instead of a blank cell
1141 // to this state, instead of a blank cell
1142 target_cell.code_mirror.clearHistory();
1142 target_cell.code_mirror.clearHistory();
1143 source_cell.element.remove();
1143 source_cell.element.remove();
1144 this.select(i);
1144 this.select(i);
1145 var cursor = source_cell.code_mirror.getCursor();
1145 var cursor = source_cell.code_mirror.getCursor();
1146 target_cell.code_mirror.setCursor(cursor);
1146 target_cell.code_mirror.setCursor(cursor);
1147 this.set_dirty(true);
1147 this.set_dirty(true);
1148 }
1148 }
1149 }
1149 }
1150 };
1150 };
1151
1151
1152 Notebook.prototype._warn_heading = function () {
1152 Notebook.prototype._warn_heading = function () {
1153 /**
1153 /**
1154 * warn about heading cells being removed
1154 * warn about heading cells being removed
1155 */
1155 */
1156 dialog.modal({
1156 dialog.modal({
1157 notebook: this,
1157 notebook: this,
1158 keyboard_manager: this.keyboard_manager,
1158 keyboard_manager: this.keyboard_manager,
1159 title : "Use markdown headings",
1159 title : "Use markdown headings",
1160 body : $("<p/>").text(
1160 body : $("<p/>").text(
1161 'IPython no longer uses special heading cells. ' +
1161 'IPython no longer uses special heading cells. ' +
1162 'Instead, write your headings in Markdown cells using # characters:'
1162 'Instead, write your headings in Markdown cells using # characters:'
1163 ).append($('<pre/>').text(
1163 ).append($('<pre/>').text(
1164 '## This is a level 2 heading'
1164 '## This is a level 2 heading'
1165 )),
1165 )),
1166 buttons : {
1166 buttons : {
1167 "OK" : {}
1167 "OK" : {}
1168 }
1168 }
1169 });
1169 });
1170 };
1170 };
1171
1171
1172 /**
1172 /**
1173 * Turn a cell into a markdown cell with a heading.
1173 * Turn a cell into a markdown cell with a heading.
1174 *
1174 *
1175 * @method to_heading
1175 * @method to_heading
1176 * @param {Number} [index] A cell's index
1176 * @param {Number} [index] A cell's index
1177 * @param {Number} [level] A heading level (e.g., 1 for h1)
1177 * @param {Number} [level] A heading level (e.g., 1 for h1)
1178 */
1178 */
1179 Notebook.prototype.to_heading = function (index, level) {
1179 Notebook.prototype.to_heading = function (index, level) {
1180 this.to_markdown(index);
1180 this.to_markdown(index);
1181 level = level || 1;
1181 level = level || 1;
1182 var i = this.index_or_selected(index);
1182 var i = this.index_or_selected(index);
1183 if (this.is_valid_cell_index(i)) {
1183 if (this.is_valid_cell_index(i)) {
1184 var cell = this.get_cell(i);
1184 var cell = this.get_cell(i);
1185 cell.set_heading_level(level);
1185 cell.set_heading_level(level);
1186 this.set_dirty(true);
1186 this.set_dirty(true);
1187 }
1187 }
1188 };
1188 };
1189
1189
1190
1190
1191 // Cut/Copy/Paste
1191 // Cut/Copy/Paste
1192
1192
1193 /**
1193 /**
1194 * Enable UI elements for pasting cells.
1194 * Enable UI elements for pasting cells.
1195 *
1195 *
1196 * @method enable_paste
1196 * @method enable_paste
1197 */
1197 */
1198 Notebook.prototype.enable_paste = function () {
1198 Notebook.prototype.enable_paste = function () {
1199 var that = this;
1199 var that = this;
1200 if (!this.paste_enabled) {
1200 if (!this.paste_enabled) {
1201 $('#paste_cell_replace').removeClass('disabled')
1201 $('#paste_cell_replace').removeClass('disabled')
1202 .on('click', function () {that.paste_cell_replace();});
1202 .on('click', function () {that.paste_cell_replace();});
1203 $('#paste_cell_above').removeClass('disabled')
1203 $('#paste_cell_above').removeClass('disabled')
1204 .on('click', function () {that.paste_cell_above();});
1204 .on('click', function () {that.paste_cell_above();});
1205 $('#paste_cell_below').removeClass('disabled')
1205 $('#paste_cell_below').removeClass('disabled')
1206 .on('click', function () {that.paste_cell_below();});
1206 .on('click', function () {that.paste_cell_below();});
1207 this.paste_enabled = true;
1207 this.paste_enabled = true;
1208 }
1208 }
1209 };
1209 };
1210
1210
1211 /**
1211 /**
1212 * Disable UI elements for pasting cells.
1212 * Disable UI elements for pasting cells.
1213 *
1213 *
1214 * @method disable_paste
1214 * @method disable_paste
1215 */
1215 */
1216 Notebook.prototype.disable_paste = function () {
1216 Notebook.prototype.disable_paste = function () {
1217 if (this.paste_enabled) {
1217 if (this.paste_enabled) {
1218 $('#paste_cell_replace').addClass('disabled').off('click');
1218 $('#paste_cell_replace').addClass('disabled').off('click');
1219 $('#paste_cell_above').addClass('disabled').off('click');
1219 $('#paste_cell_above').addClass('disabled').off('click');
1220 $('#paste_cell_below').addClass('disabled').off('click');
1220 $('#paste_cell_below').addClass('disabled').off('click');
1221 this.paste_enabled = false;
1221 this.paste_enabled = false;
1222 }
1222 }
1223 };
1223 };
1224
1224
1225 /**
1225 /**
1226 * Cut a cell.
1226 * Cut a cell.
1227 *
1227 *
1228 * @method cut_cell
1228 * @method cut_cell
1229 */
1229 */
1230 Notebook.prototype.cut_cell = function () {
1230 Notebook.prototype.cut_cell = function () {
1231 this.copy_cell();
1231 this.copy_cell();
1232 this.delete_cell();
1232 this.delete_cell();
1233 };
1233 };
1234
1234
1235 /**
1235 /**
1236 * Copy a cell.
1236 * Copy a cell.
1237 *
1237 *
1238 * @method copy_cell
1238 * @method copy_cell
1239 */
1239 */
1240 Notebook.prototype.copy_cell = function () {
1240 Notebook.prototype.copy_cell = function () {
1241 var cell = this.get_selected_cell();
1241 var cell = this.get_selected_cell();
1242 this.clipboard = cell.toJSON();
1242 this.clipboard = cell.toJSON();
1243 // remove undeletable status from the copied cell
1243 // remove undeletable status from the copied cell
1244 if (this.clipboard.metadata.deletable !== undefined) {
1244 if (this.clipboard.metadata.deletable !== undefined) {
1245 delete this.clipboard.metadata.deletable;
1245 delete this.clipboard.metadata.deletable;
1246 }
1246 }
1247 this.enable_paste();
1247 this.enable_paste();
1248 };
1248 };
1249
1249
1250 /**
1250 /**
1251 * Replace the selected cell with a cell in the clipboard.
1251 * Replace the selected cell with a cell in the clipboard.
1252 *
1252 *
1253 * @method paste_cell_replace
1253 * @method paste_cell_replace
1254 */
1254 */
1255 Notebook.prototype.paste_cell_replace = function () {
1255 Notebook.prototype.paste_cell_replace = function () {
1256 if (this.clipboard !== null && this.paste_enabled) {
1256 if (this.clipboard !== null && this.paste_enabled) {
1257 var cell_data = this.clipboard;
1257 var cell_data = this.clipboard;
1258 var new_cell = this.insert_cell_above(cell_data.cell_type);
1258 var new_cell = this.insert_cell_above(cell_data.cell_type);
1259 new_cell.fromJSON(cell_data);
1259 new_cell.fromJSON(cell_data);
1260 var old_cell = this.get_next_cell(new_cell);
1260 var old_cell = this.get_next_cell(new_cell);
1261 this.delete_cell(this.find_cell_index(old_cell));
1261 this.delete_cell(this.find_cell_index(old_cell));
1262 this.select(this.find_cell_index(new_cell));
1262 this.select(this.find_cell_index(new_cell));
1263 }
1263 }
1264 };
1264 };
1265
1265
1266 /**
1266 /**
1267 * Paste a cell from the clipboard above the selected cell.
1267 * Paste a cell from the clipboard above the selected cell.
1268 *
1268 *
1269 * @method paste_cell_above
1269 * @method paste_cell_above
1270 */
1270 */
1271 Notebook.prototype.paste_cell_above = function () {
1271 Notebook.prototype.paste_cell_above = function () {
1272 if (this.clipboard !== null && this.paste_enabled) {
1272 if (this.clipboard !== null && this.paste_enabled) {
1273 var cell_data = this.clipboard;
1273 var cell_data = this.clipboard;
1274 var new_cell = this.insert_cell_above(cell_data.cell_type);
1274 var new_cell = this.insert_cell_above(cell_data.cell_type);
1275 new_cell.fromJSON(cell_data);
1275 new_cell.fromJSON(cell_data);
1276 new_cell.focus_cell();
1276 new_cell.focus_cell();
1277 }
1277 }
1278 };
1278 };
1279
1279
1280 /**
1280 /**
1281 * Paste a cell from the clipboard below the selected cell.
1281 * Paste a cell from the clipboard below the selected cell.
1282 *
1282 *
1283 * @method paste_cell_below
1283 * @method paste_cell_below
1284 */
1284 */
1285 Notebook.prototype.paste_cell_below = function () {
1285 Notebook.prototype.paste_cell_below = function () {
1286 if (this.clipboard !== null && this.paste_enabled) {
1286 if (this.clipboard !== null && this.paste_enabled) {
1287 var cell_data = this.clipboard;
1287 var cell_data = this.clipboard;
1288 var new_cell = this.insert_cell_below(cell_data.cell_type);
1288 var new_cell = this.insert_cell_below(cell_data.cell_type);
1289 new_cell.fromJSON(cell_data);
1289 new_cell.fromJSON(cell_data);
1290 new_cell.focus_cell();
1290 new_cell.focus_cell();
1291 }
1291 }
1292 };
1292 };
1293
1293
1294 // Split/merge
1294 // Split/merge
1295
1295
1296 /**
1296 /**
1297 * Split the selected cell into two, at the cursor.
1297 * Split the selected cell into two, at the cursor.
1298 *
1298 *
1299 * @method split_cell
1299 * @method split_cell
1300 */
1300 */
1301 Notebook.prototype.split_cell = function () {
1301 Notebook.prototype.split_cell = function () {
1302 var cell = this.get_selected_cell();
1302 var cell = this.get_selected_cell();
1303 if (cell.is_splittable()) {
1303 if (cell.is_splittable()) {
1304 var texta = cell.get_pre_cursor();
1304 var texta = cell.get_pre_cursor();
1305 var textb = cell.get_post_cursor();
1305 var textb = cell.get_post_cursor();
1306 cell.set_text(textb);
1306 cell.set_text(textb);
1307 var new_cell = this.insert_cell_above(cell.cell_type);
1307 var new_cell = this.insert_cell_above(cell.cell_type);
1308 // Unrender the new cell so we can call set_text.
1308 // Unrender the new cell so we can call set_text.
1309 new_cell.unrender();
1309 new_cell.unrender();
1310 new_cell.set_text(texta);
1310 new_cell.set_text(texta);
1311 }
1311 }
1312 };
1312 };
1313
1313
1314 /**
1314 /**
1315 * Combine the selected cell into the cell above it.
1315 * Combine the selected cell into the cell above it.
1316 *
1316 *
1317 * @method merge_cell_above
1317 * @method merge_cell_above
1318 */
1318 */
1319 Notebook.prototype.merge_cell_above = function () {
1319 Notebook.prototype.merge_cell_above = function () {
1320 var index = this.get_selected_index();
1320 var index = this.get_selected_index();
1321 var cell = this.get_cell(index);
1321 var cell = this.get_cell(index);
1322 var render = cell.rendered;
1322 var render = cell.rendered;
1323 if (!cell.is_mergeable()) {
1323 if (!cell.is_mergeable()) {
1324 return;
1324 return;
1325 }
1325 }
1326 if (index > 0) {
1326 if (index > 0) {
1327 var upper_cell = this.get_cell(index-1);
1327 var upper_cell = this.get_cell(index-1);
1328 if (!upper_cell.is_mergeable()) {
1328 if (!upper_cell.is_mergeable()) {
1329 return;
1329 return;
1330 }
1330 }
1331 var upper_text = upper_cell.get_text();
1331 var upper_text = upper_cell.get_text();
1332 var text = cell.get_text();
1332 var text = cell.get_text();
1333 if (cell instanceof codecell.CodeCell) {
1333 if (cell instanceof codecell.CodeCell) {
1334 cell.set_text(upper_text+'\n'+text);
1334 cell.set_text(upper_text+'\n'+text);
1335 } else {
1335 } else {
1336 cell.unrender(); // Must unrender before we set_text.
1336 cell.unrender(); // Must unrender before we set_text.
1337 cell.set_text(upper_text+'\n\n'+text);
1337 cell.set_text(upper_text+'\n\n'+text);
1338 if (render) {
1338 if (render) {
1339 // The rendered state of the final cell should match
1339 // The rendered state of the final cell should match
1340 // that of the original selected cell;
1340 // that of the original selected cell;
1341 cell.render();
1341 cell.render();
1342 }
1342 }
1343 }
1343 }
1344 this.delete_cell(index-1);
1344 this.delete_cell(index-1);
1345 this.select(this.find_cell_index(cell));
1345 this.select(this.find_cell_index(cell));
1346 }
1346 }
1347 };
1347 };
1348
1348
1349 /**
1349 /**
1350 * Combine the selected cell into the cell below it.
1350 * Combine the selected cell into the cell below it.
1351 *
1351 *
1352 * @method merge_cell_below
1352 * @method merge_cell_below
1353 */
1353 */
1354 Notebook.prototype.merge_cell_below = function () {
1354 Notebook.prototype.merge_cell_below = function () {
1355 var index = this.get_selected_index();
1355 var index = this.get_selected_index();
1356 var cell = this.get_cell(index);
1356 var cell = this.get_cell(index);
1357 var render = cell.rendered;
1357 var render = cell.rendered;
1358 if (!cell.is_mergeable()) {
1358 if (!cell.is_mergeable()) {
1359 return;
1359 return;
1360 }
1360 }
1361 if (index < this.ncells()-1) {
1361 if (index < this.ncells()-1) {
1362 var lower_cell = this.get_cell(index+1);
1362 var lower_cell = this.get_cell(index+1);
1363 if (!lower_cell.is_mergeable()) {
1363 if (!lower_cell.is_mergeable()) {
1364 return;
1364 return;
1365 }
1365 }
1366 var lower_text = lower_cell.get_text();
1366 var lower_text = lower_cell.get_text();
1367 var text = cell.get_text();
1367 var text = cell.get_text();
1368 if (cell instanceof codecell.CodeCell) {
1368 if (cell instanceof codecell.CodeCell) {
1369 cell.set_text(text+'\n'+lower_text);
1369 cell.set_text(text+'\n'+lower_text);
1370 } else {
1370 } else {
1371 cell.unrender(); // Must unrender before we set_text.
1371 cell.unrender(); // Must unrender before we set_text.
1372 cell.set_text(text+'\n\n'+lower_text);
1372 cell.set_text(text+'\n\n'+lower_text);
1373 if (render) {
1373 if (render) {
1374 // The rendered state of the final cell should match
1374 // The rendered state of the final cell should match
1375 // that of the original selected cell;
1375 // that of the original selected cell;
1376 cell.render();
1376 cell.render();
1377 }
1377 }
1378 }
1378 }
1379 this.delete_cell(index+1);
1379 this.delete_cell(index+1);
1380 this.select(this.find_cell_index(cell));
1380 this.select(this.find_cell_index(cell));
1381 }
1381 }
1382 };
1382 };
1383
1383
1384
1384
1385 // Cell collapsing and output clearing
1385 // Cell collapsing and output clearing
1386
1386
1387 /**
1387 /**
1388 * Hide a cell's output.
1388 * Hide a cell's output.
1389 *
1389 *
1390 * @method collapse_output
1390 * @method collapse_output
1391 * @param {Number} index A cell's numeric index
1391 * @param {Number} index A cell's numeric index
1392 */
1392 */
1393 Notebook.prototype.collapse_output = function (index) {
1393 Notebook.prototype.collapse_output = function (index) {
1394 var i = this.index_or_selected(index);
1394 var i = this.index_or_selected(index);
1395 var cell = this.get_cell(i);
1395 var cell = this.get_cell(i);
1396 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1396 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1397 cell.collapse_output();
1397 cell.collapse_output();
1398 this.set_dirty(true);
1398 this.set_dirty(true);
1399 }
1399 }
1400 };
1400 };
1401
1401
1402 /**
1402 /**
1403 * Hide each code cell's output area.
1403 * Hide each code cell's output area.
1404 *
1404 *
1405 * @method collapse_all_output
1405 * @method collapse_all_output
1406 */
1406 */
1407 Notebook.prototype.collapse_all_output = function () {
1407 Notebook.prototype.collapse_all_output = function () {
1408 this.get_cells().map(function (cell, i) {
1408 this.get_cells().map(function (cell, i) {
1409 if (cell instanceof codecell.CodeCell) {
1409 if (cell instanceof codecell.CodeCell) {
1410 cell.collapse_output();
1410 cell.collapse_output();
1411 }
1411 }
1412 });
1412 });
1413 // this should not be set if the `collapse` key is removed from nbformat
1413 // this should not be set if the `collapse` key is removed from nbformat
1414 this.set_dirty(true);
1414 this.set_dirty(true);
1415 };
1415 };
1416
1416
1417 /**
1417 /**
1418 * Show a cell's output.
1418 * Show a cell's output.
1419 *
1419 *
1420 * @method expand_output
1420 * @method expand_output
1421 * @param {Number} index A cell's numeric index
1421 * @param {Number} index A cell's numeric index
1422 */
1422 */
1423 Notebook.prototype.expand_output = function (index) {
1423 Notebook.prototype.expand_output = function (index) {
1424 var i = this.index_or_selected(index);
1424 var i = this.index_or_selected(index);
1425 var cell = this.get_cell(i);
1425 var cell = this.get_cell(i);
1426 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1426 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1427 cell.expand_output();
1427 cell.expand_output();
1428 this.set_dirty(true);
1428 this.set_dirty(true);
1429 }
1429 }
1430 };
1430 };
1431
1431
1432 /**
1432 /**
1433 * Expand each code cell's output area, and remove scrollbars.
1433 * Expand each code cell's output area, and remove scrollbars.
1434 *
1434 *
1435 * @method expand_all_output
1435 * @method expand_all_output
1436 */
1436 */
1437 Notebook.prototype.expand_all_output = function () {
1437 Notebook.prototype.expand_all_output = function () {
1438 this.get_cells().map(function (cell, i) {
1438 this.get_cells().map(function (cell, i) {
1439 if (cell instanceof codecell.CodeCell) {
1439 if (cell instanceof codecell.CodeCell) {
1440 cell.expand_output();
1440 cell.expand_output();
1441 }
1441 }
1442 });
1442 });
1443 // this should not be set if the `collapse` key is removed from nbformat
1443 // this should not be set if the `collapse` key is removed from nbformat
1444 this.set_dirty(true);
1444 this.set_dirty(true);
1445 };
1445 };
1446
1446
1447 /**
1447 /**
1448 * Clear the selected CodeCell's output area.
1448 * Clear the selected CodeCell's output area.
1449 *
1449 *
1450 * @method clear_output
1450 * @method clear_output
1451 * @param {Number} index A cell's numeric index
1451 * @param {Number} index A cell's numeric index
1452 */
1452 */
1453 Notebook.prototype.clear_output = function (index) {
1453 Notebook.prototype.clear_output = function (index) {
1454 var i = this.index_or_selected(index);
1454 var i = this.index_or_selected(index);
1455 var cell = this.get_cell(i);
1455 var cell = this.get_cell(i);
1456 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1456 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1457 cell.clear_output();
1457 cell.clear_output();
1458 this.set_dirty(true);
1458 this.set_dirty(true);
1459 }
1459 }
1460 };
1460 };
1461
1461
1462 /**
1462 /**
1463 * Clear each code cell's output area.
1463 * Clear each code cell's output area.
1464 *
1464 *
1465 * @method clear_all_output
1465 * @method clear_all_output
1466 */
1466 */
1467 Notebook.prototype.clear_all_output = function () {
1467 Notebook.prototype.clear_all_output = function () {
1468 this.get_cells().map(function (cell, i) {
1468 this.get_cells().map(function (cell, i) {
1469 if (cell instanceof codecell.CodeCell) {
1469 if (cell instanceof codecell.CodeCell) {
1470 cell.clear_output();
1470 cell.clear_output();
1471 }
1471 }
1472 });
1472 });
1473 this.set_dirty(true);
1473 this.set_dirty(true);
1474 };
1474 };
1475
1475
1476 /**
1476 /**
1477 * Scroll the selected CodeCell's output area.
1477 * Scroll the selected CodeCell's output area.
1478 *
1478 *
1479 * @method scroll_output
1479 * @method scroll_output
1480 * @param {Number} index A cell's numeric index
1480 * @param {Number} index A cell's numeric index
1481 */
1481 */
1482 Notebook.prototype.scroll_output = function (index) {
1482 Notebook.prototype.scroll_output = function (index) {
1483 var i = this.index_or_selected(index);
1483 var i = this.index_or_selected(index);
1484 var cell = this.get_cell(i);
1484 var cell = this.get_cell(i);
1485 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1485 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1486 cell.scroll_output();
1486 cell.scroll_output();
1487 this.set_dirty(true);
1487 this.set_dirty(true);
1488 }
1488 }
1489 };
1489 };
1490
1490
1491 /**
1491 /**
1492 * Expand each code cell's output area, and add a scrollbar for long output.
1492 * Expand each code cell's output area, and add a scrollbar for long output.
1493 *
1493 *
1494 * @method scroll_all_output
1494 * @method scroll_all_output
1495 */
1495 */
1496 Notebook.prototype.scroll_all_output = function () {
1496 Notebook.prototype.scroll_all_output = function () {
1497 this.get_cells().map(function (cell, i) {
1497 this.get_cells().map(function (cell, i) {
1498 if (cell instanceof codecell.CodeCell) {
1498 if (cell instanceof codecell.CodeCell) {
1499 cell.scroll_output();
1499 cell.scroll_output();
1500 }
1500 }
1501 });
1501 });
1502 // this should not be set if the `collapse` key is removed from nbformat
1502 // this should not be set if the `collapse` key is removed from nbformat
1503 this.set_dirty(true);
1503 this.set_dirty(true);
1504 };
1504 };
1505
1505
1506 /** Toggle whether a cell's output is collapsed or expanded.
1506 /** Toggle whether a cell's output is collapsed or expanded.
1507 *
1507 *
1508 * @method toggle_output
1508 * @method toggle_output
1509 * @param {Number} index A cell's numeric index
1509 * @param {Number} index A cell's numeric index
1510 */
1510 */
1511 Notebook.prototype.toggle_output = function (index) {
1511 Notebook.prototype.toggle_output = function (index) {
1512 var i = this.index_or_selected(index);
1512 var i = this.index_or_selected(index);
1513 var cell = this.get_cell(i);
1513 var cell = this.get_cell(i);
1514 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1514 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1515 cell.toggle_output();
1515 cell.toggle_output();
1516 this.set_dirty(true);
1516 this.set_dirty(true);
1517 }
1517 }
1518 };
1518 };
1519
1519
1520 /**
1520 /**
1521 * Hide/show the output of all cells.
1521 * Hide/show the output of all cells.
1522 *
1522 *
1523 * @method toggle_all_output
1523 * @method toggle_all_output
1524 */
1524 */
1525 Notebook.prototype.toggle_all_output = function () {
1525 Notebook.prototype.toggle_all_output = function () {
1526 this.get_cells().map(function (cell, i) {
1526 this.get_cells().map(function (cell, i) {
1527 if (cell instanceof codecell.CodeCell) {
1527 if (cell instanceof codecell.CodeCell) {
1528 cell.toggle_output();
1528 cell.toggle_output();
1529 }
1529 }
1530 });
1530 });
1531 // this should not be set if the `collapse` key is removed from nbformat
1531 // this should not be set if the `collapse` key is removed from nbformat
1532 this.set_dirty(true);
1532 this.set_dirty(true);
1533 };
1533 };
1534
1534
1535 /**
1535 /**
1536 * Toggle a scrollbar for long cell outputs.
1536 * Toggle a scrollbar for long cell outputs.
1537 *
1537 *
1538 * @method toggle_output_scroll
1538 * @method toggle_output_scroll
1539 * @param {Number} index A cell's numeric index
1539 * @param {Number} index A cell's numeric index
1540 */
1540 */
1541 Notebook.prototype.toggle_output_scroll = function (index) {
1541 Notebook.prototype.toggle_output_scroll = function (index) {
1542 var i = this.index_or_selected(index);
1542 var i = this.index_or_selected(index);
1543 var cell = this.get_cell(i);
1543 var cell = this.get_cell(i);
1544 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1544 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1545 cell.toggle_output_scroll();
1545 cell.toggle_output_scroll();
1546 this.set_dirty(true);
1546 this.set_dirty(true);
1547 }
1547 }
1548 };
1548 };
1549
1549
1550 /**
1550 /**
1551 * Toggle the scrolling of long output on all cells.
1551 * Toggle the scrolling of long output on all cells.
1552 *
1552 *
1553 * @method toggle_all_output_scrolling
1553 * @method toggle_all_output_scrolling
1554 */
1554 */
1555 Notebook.prototype.toggle_all_output_scroll = function () {
1555 Notebook.prototype.toggle_all_output_scroll = function () {
1556 this.get_cells().map(function (cell, i) {
1556 this.get_cells().map(function (cell, i) {
1557 if (cell instanceof codecell.CodeCell) {
1557 if (cell instanceof codecell.CodeCell) {
1558 cell.toggle_output_scroll();
1558 cell.toggle_output_scroll();
1559 }
1559 }
1560 });
1560 });
1561 // this should not be set if the `collapse` key is removed from nbformat
1561 // this should not be set if the `collapse` key is removed from nbformat
1562 this.set_dirty(true);
1562 this.set_dirty(true);
1563 };
1563 };
1564
1564
1565 // Other cell functions: line numbers, ...
1565 // Other cell functions: line numbers, ...
1566
1566
1567 /**
1567 /**
1568 * Toggle line numbers in the selected cell's input area.
1568 * Toggle line numbers in the selected cell's input area.
1569 *
1569 *
1570 * @method cell_toggle_line_numbers
1570 * @method cell_toggle_line_numbers
1571 */
1571 */
1572 Notebook.prototype.cell_toggle_line_numbers = function() {
1572 Notebook.prototype.cell_toggle_line_numbers = function() {
1573 this.get_selected_cell().toggle_line_numbers();
1573 this.get_selected_cell().toggle_line_numbers();
1574 };
1574 };
1575
1575
1576 /**
1576 /**
1577 * Set the codemirror mode for all code cells, including the default for
1577 * Set the codemirror mode for all code cells, including the default for
1578 * new code cells.
1578 * new code cells.
1579 *
1579 *
1580 * @method set_codemirror_mode
1580 * @method set_codemirror_mode
1581 */
1581 */
1582 Notebook.prototype.set_codemirror_mode = function(newmode){
1582 Notebook.prototype.set_codemirror_mode = function(newmode){
1583 if (newmode === this.codemirror_mode) {
1583 if (newmode === this.codemirror_mode) {
1584 return;
1584 return;
1585 }
1585 }
1586 this.codemirror_mode = newmode;
1586 this.codemirror_mode = newmode;
1587 codecell.CodeCell.options_default.cm_config.mode = newmode;
1587 codecell.CodeCell.options_default.cm_config.mode = newmode;
1588
1588
1589 var that = this;
1589 var that = this;
1590 utils.requireCodeMirrorMode(newmode, function (spec) {
1590 utils.requireCodeMirrorMode(newmode, function (spec) {
1591 that.get_cells().map(function(cell, i) {
1591 that.get_cells().map(function(cell, i) {
1592 if (cell.cell_type === 'code'){
1592 if (cell.cell_type === 'code'){
1593 cell.code_mirror.setOption('mode', spec);
1593 cell.code_mirror.setOption('mode', spec);
1594 // This is currently redundant, because cm_config ends up as
1594 // This is currently redundant, because cm_config ends up as
1595 // codemirror's own .options object, but I don't want to
1595 // codemirror's own .options object, but I don't want to
1596 // rely on that.
1596 // rely on that.
1597 cell.cm_config.mode = spec;
1597 cell.cm_config.mode = spec;
1598 }
1598 }
1599 });
1599 });
1600 });
1600 });
1601 };
1601 };
1602
1602
1603 // Session related things
1603 // Session related things
1604
1604
1605 /**
1605 /**
1606 * Start a new session and set it on each code cell.
1606 * Start a new session and set it on each code cell.
1607 *
1607 *
1608 * @method start_session
1608 * @method start_session
1609 */
1609 */
1610 Notebook.prototype.start_session = function (kernel_name) {
1610 Notebook.prototype.start_session = function (kernel_name) {
1611 if (this._session_starting) {
1611 if (this._session_starting) {
1612 throw new session.SessionAlreadyStarting();
1612 throw new session.SessionAlreadyStarting();
1613 }
1613 }
1614 this._session_starting = true;
1614 this._session_starting = true;
1615
1615
1616 var options = {
1616 var options = {
1617 base_url: this.base_url,
1617 base_url: this.base_url,
1618 ws_url: this.ws_url,
1618 ws_url: this.ws_url,
1619 notebook_path: this.notebook_path,
1619 notebook_path: this.notebook_path,
1620 notebook_name: this.notebook_name,
1620 notebook_name: this.notebook_name,
1621 kernel_name: kernel_name,
1621 kernel_name: kernel_name,
1622 notebook: this
1622 notebook: this
1623 };
1623 };
1624
1624
1625 var success = $.proxy(this._session_started, this);
1625 var success = $.proxy(this._session_started, this);
1626 var failure = $.proxy(this._session_start_failed, this);
1626 var failure = $.proxy(this._session_start_failed, this);
1627
1627
1628 if (this.session !== null) {
1628 if (this.session !== null) {
1629 this.session.restart(options, success, failure);
1629 this.session.restart(options, success, failure);
1630 } else {
1630 } else {
1631 this.session = new session.Session(options);
1631 this.session = new session.Session(options);
1632 this.session.start(success, failure);
1632 this.session.start(success, failure);
1633 }
1633 }
1634 };
1634 };
1635
1635
1636
1636
1637 /**
1637 /**
1638 * Once a session is started, link the code cells to the kernel and pass the
1638 * Once a session is started, link the code cells to the kernel and pass the
1639 * comm manager to the widget manager
1639 * comm manager to the widget manager
1640 *
1640 *
1641 */
1641 */
1642 Notebook.prototype._session_started = function (){
1642 Notebook.prototype._session_started = function (){
1643 this._session_starting = false;
1643 this._session_starting = false;
1644 this.kernel = this.session.kernel;
1644 this.kernel = this.session.kernel;
1645 var ncells = this.ncells();
1645 var ncells = this.ncells();
1646 for (var i=0; i<ncells; i++) {
1646 for (var i=0; i<ncells; i++) {
1647 var cell = this.get_cell(i);
1647 var cell = this.get_cell(i);
1648 if (cell instanceof codecell.CodeCell) {
1648 if (cell instanceof codecell.CodeCell) {
1649 cell.set_kernel(this.session.kernel);
1649 cell.set_kernel(this.session.kernel);
1650 }
1650 }
1651 }
1651 }
1652 };
1652 };
1653 Notebook.prototype._session_start_failed = function (jqxhr, status, error){
1653 Notebook.prototype._session_start_failed = function (jqxhr, status, error){
1654 this._session_starting = false;
1654 this._session_starting = false;
1655 utils.log_ajax_error(jqxhr, status, error);
1655 utils.log_ajax_error(jqxhr, status, error);
1656 };
1656 };
1657
1657
1658 /**
1658 /**
1659 * Prompt the user to restart the IPython kernel.
1659 * Prompt the user to restart the IPython kernel.
1660 *
1660 *
1661 * @method restart_kernel
1661 * @method restart_kernel
1662 */
1662 */
1663 Notebook.prototype.restart_kernel = function () {
1663 Notebook.prototype.restart_kernel = function () {
1664 var that = this;
1664 var that = this;
1665 dialog.modal({
1665 dialog.modal({
1666 notebook: this,
1666 notebook: this,
1667 keyboard_manager: this.keyboard_manager,
1667 keyboard_manager: this.keyboard_manager,
1668 title : "Restart kernel or continue running?",
1668 title : "Restart kernel or continue running?",
1669 body : $("<p/>").text(
1669 body : $("<p/>").text(
1670 'Do you want to restart the current kernel? You will lose all variables defined in it.'
1670 'Do you want to restart the current kernel? You will lose all variables defined in it.'
1671 ),
1671 ),
1672 buttons : {
1672 buttons : {
1673 "Continue running" : {},
1673 "Continue running" : {},
1674 "Restart" : {
1674 "Restart" : {
1675 "class" : "btn-danger",
1675 "class" : "btn-danger",
1676 "click" : function() {
1676 "click" : function() {
1677 that.kernel.restart();
1677 that.kernel.restart();
1678 }
1678 }
1679 }
1679 }
1680 }
1680 }
1681 });
1681 });
1682 };
1682 };
1683
1683
1684 /**
1684 /**
1685 * Execute or render cell outputs and go into command mode.
1685 * Execute or render cell outputs and go into command mode.
1686 *
1686 *
1687 * @method execute_cell
1687 * @method execute_cell
1688 */
1688 */
1689 Notebook.prototype.execute_cell = function () {
1689 Notebook.prototype.execute_cell = function () {
1690 /**
1690 /**
1691 * mode = shift, ctrl, alt
1691 * mode = shift, ctrl, alt
1692 */
1692 */
1693 var cell = this.get_selected_cell();
1693 var cell = this.get_selected_cell();
1694
1694
1695 cell.execute();
1695 cell.execute();
1696 this.command_mode();
1696 this.command_mode();
1697 this.set_dirty(true);
1697 this.set_dirty(true);
1698 };
1698 };
1699
1699
1700 /**
1700 /**
1701 * Execute or render cell outputs and insert a new cell below.
1701 * Execute or render cell outputs and insert a new cell below.
1702 *
1702 *
1703 * @method execute_cell_and_insert_below
1703 * @method execute_cell_and_insert_below
1704 */
1704 */
1705 Notebook.prototype.execute_cell_and_insert_below = function () {
1705 Notebook.prototype.execute_cell_and_insert_below = function () {
1706 var cell = this.get_selected_cell();
1706 var cell = this.get_selected_cell();
1707 var cell_index = this.find_cell_index(cell);
1707 var cell_index = this.find_cell_index(cell);
1708
1708
1709 cell.execute();
1709 cell.execute();
1710
1710
1711 // If we are at the end always insert a new cell and return
1711 // If we are at the end always insert a new cell and return
1712 if (cell_index === (this.ncells()-1)) {
1712 if (cell_index === (this.ncells()-1)) {
1713 this.command_mode();
1713 this.command_mode();
1714 this.insert_cell_below();
1714 this.insert_cell_below();
1715 this.select(cell_index+1);
1715 this.select(cell_index+1);
1716 this.edit_mode();
1716 this.edit_mode();
1717 this.scroll_to_bottom();
1717 this.scroll_to_bottom();
1718 this.set_dirty(true);
1718 this.set_dirty(true);
1719 return;
1719 return;
1720 }
1720 }
1721
1721
1722 this.command_mode();
1722 this.command_mode();
1723 this.insert_cell_below();
1723 this.insert_cell_below();
1724 this.select(cell_index+1);
1724 this.select(cell_index+1);
1725 this.edit_mode();
1725 this.edit_mode();
1726 this.set_dirty(true);
1726 this.set_dirty(true);
1727 };
1727 };
1728
1728
1729 /**
1729 /**
1730 * Execute or render cell outputs and select the next cell.
1730 * Execute or render cell outputs and select the next cell.
1731 *
1731 *
1732 * @method execute_cell_and_select_below
1732 * @method execute_cell_and_select_below
1733 */
1733 */
1734 Notebook.prototype.execute_cell_and_select_below = function () {
1734 Notebook.prototype.execute_cell_and_select_below = function () {
1735
1735
1736 var cell = this.get_selected_cell();
1736 var cell = this.get_selected_cell();
1737 var cell_index = this.find_cell_index(cell);
1737 var cell_index = this.find_cell_index(cell);
1738
1738
1739 cell.execute();
1739 cell.execute();
1740
1740
1741 // If we are at the end always insert a new cell and return
1741 // If we are at the end always insert a new cell and return
1742 if (cell_index === (this.ncells()-1)) {
1742 if (cell_index === (this.ncells()-1)) {
1743 this.command_mode();
1743 this.command_mode();
1744 this.insert_cell_below();
1744 this.insert_cell_below();
1745 this.select(cell_index+1);
1745 this.select(cell_index+1);
1746 this.edit_mode();
1746 this.edit_mode();
1747 this.scroll_to_bottom();
1747 this.scroll_to_bottom();
1748 this.set_dirty(true);
1748 this.set_dirty(true);
1749 return;
1749 return;
1750 }
1750 }
1751
1751
1752 this.command_mode();
1752 this.command_mode();
1753 this.select(cell_index+1);
1753 this.select(cell_index+1);
1754 this.focus_cell();
1754 this.focus_cell();
1755 this.set_dirty(true);
1755 this.set_dirty(true);
1756 };
1756 };
1757
1757
1758 /**
1758 /**
1759 * Execute all cells below the selected cell.
1759 * Execute all cells below the selected cell.
1760 *
1760 *
1761 * @method execute_cells_below
1761 * @method execute_cells_below
1762 */
1762 */
1763 Notebook.prototype.execute_cells_below = function () {
1763 Notebook.prototype.execute_cells_below = function () {
1764 this.execute_cell_range(this.get_selected_index(), this.ncells());
1764 this.execute_cell_range(this.get_selected_index(), this.ncells());
1765 this.scroll_to_bottom();
1765 this.scroll_to_bottom();
1766 };
1766 };
1767
1767
1768 /**
1768 /**
1769 * Execute all cells above the selected cell.
1769 * Execute all cells above the selected cell.
1770 *
1770 *
1771 * @method execute_cells_above
1771 * @method execute_cells_above
1772 */
1772 */
1773 Notebook.prototype.execute_cells_above = function () {
1773 Notebook.prototype.execute_cells_above = function () {
1774 this.execute_cell_range(0, this.get_selected_index());
1774 this.execute_cell_range(0, this.get_selected_index());
1775 };
1775 };
1776
1776
1777 /**
1777 /**
1778 * Execute all cells.
1778 * Execute all cells.
1779 *
1779 *
1780 * @method execute_all_cells
1780 * @method execute_all_cells
1781 */
1781 */
1782 Notebook.prototype.execute_all_cells = function () {
1782 Notebook.prototype.execute_all_cells = function () {
1783 this.execute_cell_range(0, this.ncells());
1783 this.execute_cell_range(0, this.ncells());
1784 this.scroll_to_bottom();
1784 this.scroll_to_bottom();
1785 };
1785 };
1786
1786
1787 /**
1787 /**
1788 * Execute a contiguous range of cells.
1788 * Execute a contiguous range of cells.
1789 *
1789 *
1790 * @method execute_cell_range
1790 * @method execute_cell_range
1791 * @param {Number} start Index of the first cell to execute (inclusive)
1791 * @param {Number} start Index of the first cell to execute (inclusive)
1792 * @param {Number} end Index of the last cell to execute (exclusive)
1792 * @param {Number} end Index of the last cell to execute (exclusive)
1793 */
1793 */
1794 Notebook.prototype.execute_cell_range = function (start, end) {
1794 Notebook.prototype.execute_cell_range = function (start, end) {
1795 this.command_mode();
1795 this.command_mode();
1796 for (var i=start; i<end; i++) {
1796 for (var i=start; i<end; i++) {
1797 this.select(i);
1797 this.select(i);
1798 this.execute_cell();
1798 this.execute_cell();
1799 }
1799 }
1800 };
1800 };
1801
1801
1802 // Persistance and loading
1802 // Persistance and loading
1803
1803
1804 /**
1804 /**
1805 * Getter method for this notebook's name.
1805 * Getter method for this notebook's name.
1806 *
1806 *
1807 * @method get_notebook_name
1807 * @method get_notebook_name
1808 * @return {String} This notebook's name (excluding file extension)
1808 * @return {String} This notebook's name (excluding file extension)
1809 */
1809 */
1810 Notebook.prototype.get_notebook_name = function () {
1810 Notebook.prototype.get_notebook_name = function () {
1811 var nbname = this.notebook_name.substring(0,this.notebook_name.length-6);
1811 var nbname = this.notebook_name.substring(0,this.notebook_name.length-6);
1812 return nbname;
1812 return nbname;
1813 };
1813 };
1814
1814
1815 /**
1815 /**
1816 * Setter method for this notebook's name.
1816 * Setter method for this notebook's name.
1817 *
1817 *
1818 * @method set_notebook_name
1818 * @method set_notebook_name
1819 * @param {String} name A new name for this notebook
1819 * @param {String} name A new name for this notebook
1820 */
1820 */
1821 Notebook.prototype.set_notebook_name = function (name) {
1821 Notebook.prototype.set_notebook_name = function (name) {
1822 var parent = utils.url_path_split(this.notebook_path)[0];
1822 var parent = utils.url_path_split(this.notebook_path)[0];
1823 this.notebook_name = name;
1823 this.notebook_name = name;
1824 this.notebook_path = utils.url_path_join(parent, name);
1824 this.notebook_path = utils.url_path_join(parent, name);
1825 };
1825 };
1826
1826
1827 /**
1827 /**
1828 * Check that a notebook's name is valid.
1828 * Check that a notebook's name is valid.
1829 *
1829 *
1830 * @method test_notebook_name
1830 * @method test_notebook_name
1831 * @param {String} nbname A name for this notebook
1831 * @param {String} nbname A name for this notebook
1832 * @return {Boolean} True if the name is valid, false if invalid
1832 * @return {Boolean} True if the name is valid, false if invalid
1833 */
1833 */
1834 Notebook.prototype.test_notebook_name = function (nbname) {
1834 Notebook.prototype.test_notebook_name = function (nbname) {
1835 nbname = nbname || '';
1835 nbname = nbname || '';
1836 if (nbname.length>0 && !this.notebook_name_blacklist_re.test(nbname)) {
1836 if (nbname.length>0 && !this.notebook_name_blacklist_re.test(nbname)) {
1837 return true;
1837 return true;
1838 } else {
1838 } else {
1839 return false;
1839 return false;
1840 }
1840 }
1841 };
1841 };
1842
1842
1843 /**
1843 /**
1844 * Load a notebook from JSON (.ipynb).
1844 * Load a notebook from JSON (.ipynb).
1845 *
1845 *
1846 * @method fromJSON
1846 * @method fromJSON
1847 * @param {Object} data JSON representation of a notebook
1847 * @param {Object} data JSON representation of a notebook
1848 */
1848 */
1849 Notebook.prototype.fromJSON = function (data) {
1849 Notebook.prototype.fromJSON = function (data) {
1850
1850
1851 var content = data.content;
1851 var content = data.content;
1852 var ncells = this.ncells();
1852 var ncells = this.ncells();
1853 var i;
1853 var i;
1854 for (i=0; i<ncells; i++) {
1854 for (i=0; i<ncells; i++) {
1855 // Always delete cell 0 as they get renumbered as they are deleted.
1855 // Always delete cell 0 as they get renumbered as they are deleted.
1856 this.delete_cell(0);
1856 this.delete_cell(0);
1857 }
1857 }
1858 // Save the metadata and name.
1858 // Save the metadata and name.
1859 this.metadata = content.metadata;
1859 this.metadata = content.metadata;
1860 this.notebook_name = data.name;
1860 this.notebook_name = data.name;
1861 this.notebook_path = data.path;
1861 this.notebook_path = data.path;
1862 var trusted = true;
1862 var trusted = true;
1863
1863
1864 // Trigger an event changing the kernel spec - this will set the default
1864 // Trigger an event changing the kernel spec - this will set the default
1865 // codemirror mode
1865 // codemirror mode
1866 if (this.metadata.kernelspec !== undefined) {
1866 if (this.metadata.kernelspec !== undefined) {
1867 this.events.trigger('spec_changed.Kernel', this.metadata.kernelspec);
1867 // TODO shoudl probably not trigger here,
1868 // should call the kernel selector, or custom.{js|css} not loaded.
1869 if(this.kernel_selector){
1870 // technically not perfect, we should check that the kernelspec matches
1871 this.kernel_selector.change_kernel(this.metadata.kernelspec.name);
1872 } else {
1873 console.log('do not have handle on kernnel_selector');
1874 }
1868 }
1875 }
1869
1876
1870 // Set the codemirror mode from language_info metadata
1877 // Set the codemirror mode from language_info metadata
1871 if (this.metadata.language_info !== undefined) {
1878 if (this.metadata.language_info !== undefined) {
1872 var langinfo = this.metadata.language_info;
1879 var langinfo = this.metadata.language_info;
1873 // Mode 'null' should be plain, unhighlighted text.
1880 // Mode 'null' should be plain, unhighlighted text.
1874 var cm_mode = langinfo.codemirror_mode || langinfo.name || 'null';
1881 var cm_mode = langinfo.codemirror_mode || langinfo.name || 'null';
1875 this.set_codemirror_mode(cm_mode);
1882 this.set_codemirror_mode(cm_mode);
1876 }
1883 }
1877
1884
1878 var new_cells = content.cells;
1885 var new_cells = content.cells;
1879 ncells = new_cells.length;
1886 ncells = new_cells.length;
1880 var cell_data = null;
1887 var cell_data = null;
1881 var new_cell = null;
1888 var new_cell = null;
1882 for (i=0; i<ncells; i++) {
1889 for (i=0; i<ncells; i++) {
1883 cell_data = new_cells[i];
1890 cell_data = new_cells[i];
1884 new_cell = this.insert_cell_at_index(cell_data.cell_type, i);
1891 new_cell = this.insert_cell_at_index(cell_data.cell_type, i);
1885 new_cell.fromJSON(cell_data);
1892 new_cell.fromJSON(cell_data);
1886 if (new_cell.cell_type == 'code' && !new_cell.output_area.trusted) {
1893 if (new_cell.cell_type == 'code' && !new_cell.output_area.trusted) {
1887 trusted = false;
1894 trusted = false;
1888 }
1895 }
1889 }
1896 }
1890 if (trusted !== this.trusted) {
1897 if (trusted !== this.trusted) {
1891 this.trusted = trusted;
1898 this.trusted = trusted;
1892 this.events.trigger("trust_changed.Notebook", trusted);
1899 this.events.trigger("trust_changed.Notebook", trusted);
1893 }
1900 }
1894 };
1901 };
1895
1902
1896 /**
1903 /**
1897 * Dump this notebook into a JSON-friendly object.
1904 * Dump this notebook into a JSON-friendly object.
1898 *
1905 *
1899 * @method toJSON
1906 * @method toJSON
1900 * @return {Object} A JSON-friendly representation of this notebook.
1907 * @return {Object} A JSON-friendly representation of this notebook.
1901 */
1908 */
1902 Notebook.prototype.toJSON = function () {
1909 Notebook.prototype.toJSON = function () {
1903 /**
1910 /**
1904 * remove the conversion indicator, which only belongs in-memory
1911 * remove the conversion indicator, which only belongs in-memory
1905 */
1912 */
1906 delete this.metadata.orig_nbformat;
1913 delete this.metadata.orig_nbformat;
1907 delete this.metadata.orig_nbformat_minor;
1914 delete this.metadata.orig_nbformat_minor;
1908
1915
1909 var cells = this.get_cells();
1916 var cells = this.get_cells();
1910 var ncells = cells.length;
1917 var ncells = cells.length;
1911 var cell_array = new Array(ncells);
1918 var cell_array = new Array(ncells);
1912 var trusted = true;
1919 var trusted = true;
1913 for (var i=0; i<ncells; i++) {
1920 for (var i=0; i<ncells; i++) {
1914 var cell = cells[i];
1921 var cell = cells[i];
1915 if (cell.cell_type == 'code' && !cell.output_area.trusted) {
1922 if (cell.cell_type == 'code' && !cell.output_area.trusted) {
1916 trusted = false;
1923 trusted = false;
1917 }
1924 }
1918 cell_array[i] = cell.toJSON();
1925 cell_array[i] = cell.toJSON();
1919 }
1926 }
1920 var data = {
1927 var data = {
1921 cells: cell_array,
1928 cells: cell_array,
1922 metadata: this.metadata,
1929 metadata: this.metadata,
1923 nbformat: this.nbformat,
1930 nbformat: this.nbformat,
1924 nbformat_minor: this.nbformat_minor
1931 nbformat_minor: this.nbformat_minor
1925 };
1932 };
1926 if (trusted != this.trusted) {
1933 if (trusted != this.trusted) {
1927 this.trusted = trusted;
1934 this.trusted = trusted;
1928 this.events.trigger("trust_changed.Notebook", trusted);
1935 this.events.trigger("trust_changed.Notebook", trusted);
1929 }
1936 }
1930 return data;
1937 return data;
1931 };
1938 };
1932
1939
1933 /**
1940 /**
1934 * Start an autosave timer, for periodically saving the notebook.
1941 * Start an autosave timer, for periodically saving the notebook.
1935 *
1942 *
1936 * @method set_autosave_interval
1943 * @method set_autosave_interval
1937 * @param {Integer} interval the autosave interval in milliseconds
1944 * @param {Integer} interval the autosave interval in milliseconds
1938 */
1945 */
1939 Notebook.prototype.set_autosave_interval = function (interval) {
1946 Notebook.prototype.set_autosave_interval = function (interval) {
1940 var that = this;
1947 var that = this;
1941 // clear previous interval, so we don't get simultaneous timers
1948 // clear previous interval, so we don't get simultaneous timers
1942 if (this.autosave_timer) {
1949 if (this.autosave_timer) {
1943 clearInterval(this.autosave_timer);
1950 clearInterval(this.autosave_timer);
1944 }
1951 }
1945 if (!this.writable) {
1952 if (!this.writable) {
1946 // disable autosave if not writable
1953 // disable autosave if not writable
1947 interval = 0;
1954 interval = 0;
1948 }
1955 }
1949
1956
1950 this.autosave_interval = this.minimum_autosave_interval = interval;
1957 this.autosave_interval = this.minimum_autosave_interval = interval;
1951 if (interval) {
1958 if (interval) {
1952 this.autosave_timer = setInterval(function() {
1959 this.autosave_timer = setInterval(function() {
1953 if (that.dirty) {
1960 if (that.dirty) {
1954 that.save_notebook();
1961 that.save_notebook();
1955 }
1962 }
1956 }, interval);
1963 }, interval);
1957 this.events.trigger("autosave_enabled.Notebook", interval);
1964 this.events.trigger("autosave_enabled.Notebook", interval);
1958 } else {
1965 } else {
1959 this.autosave_timer = null;
1966 this.autosave_timer = null;
1960 this.events.trigger("autosave_disabled.Notebook");
1967 this.events.trigger("autosave_disabled.Notebook");
1961 }
1968 }
1962 };
1969 };
1963
1970
1964 /**
1971 /**
1965 * Save this notebook on the server. This becomes a notebook instance's
1972 * Save this notebook on the server. This becomes a notebook instance's
1966 * .save_notebook method *after* the entire notebook has been loaded.
1973 * .save_notebook method *after* the entire notebook has been loaded.
1967 *
1974 *
1968 * @method save_notebook
1975 * @method save_notebook
1969 */
1976 */
1970 Notebook.prototype.save_notebook = function () {
1977 Notebook.prototype.save_notebook = function () {
1971 if (!this._fully_loaded) {
1978 if (!this._fully_loaded) {
1972 this.events.trigger('notebook_save_failed.Notebook',
1979 this.events.trigger('notebook_save_failed.Notebook',
1973 new Error("Load failed, save is disabled")
1980 new Error("Load failed, save is disabled")
1974 );
1981 );
1975 return;
1982 return;
1976 } else if (!this.writable) {
1983 } else if (!this.writable) {
1977 this.events.trigger('notebook_save_failed.Notebook',
1984 this.events.trigger('notebook_save_failed.Notebook',
1978 new Error("Notebook is read-only")
1985 new Error("Notebook is read-only")
1979 );
1986 );
1980 return;
1987 return;
1981 }
1988 }
1982
1989
1983 // Trigger an event before save, which allows listeners to modify
1990 // Trigger an event before save, which allows listeners to modify
1984 // the notebook as needed.
1991 // the notebook as needed.
1985 this.events.trigger('before_save.Notebook');
1992 this.events.trigger('before_save.Notebook');
1986
1993
1987 // Create a JSON model to be sent to the server.
1994 // Create a JSON model to be sent to the server.
1988 var model = {
1995 var model = {
1989 type : "notebook",
1996 type : "notebook",
1990 content : this.toJSON()
1997 content : this.toJSON()
1991 };
1998 };
1992 // time the ajax call for autosave tuning purposes.
1999 // time the ajax call for autosave tuning purposes.
1993 var start = new Date().getTime();
2000 var start = new Date().getTime();
1994
2001
1995 var that = this;
2002 var that = this;
1996 return this.contents.save(this.notebook_path, model).then(
2003 return this.contents.save(this.notebook_path, model).then(
1997 $.proxy(this.save_notebook_success, this, start),
2004 $.proxy(this.save_notebook_success, this, start),
1998 function (error) {
2005 function (error) {
1999 that.events.trigger('notebook_save_failed.Notebook', error);
2006 that.events.trigger('notebook_save_failed.Notebook', error);
2000 }
2007 }
2001 );
2008 );
2002 };
2009 };
2003
2010
2004 /**
2011 /**
2005 * Success callback for saving a notebook.
2012 * Success callback for saving a notebook.
2006 *
2013 *
2007 * @method save_notebook_success
2014 * @method save_notebook_success
2008 * @param {Integer} start Time when the save request start
2015 * @param {Integer} start Time when the save request start
2009 * @param {Object} data JSON representation of a notebook
2016 * @param {Object} data JSON representation of a notebook
2010 */
2017 */
2011 Notebook.prototype.save_notebook_success = function (start, data) {
2018 Notebook.prototype.save_notebook_success = function (start, data) {
2012 this.set_dirty(false);
2019 this.set_dirty(false);
2013 if (data.message) {
2020 if (data.message) {
2014 // save succeeded, but validation failed.
2021 // save succeeded, but validation failed.
2015 var body = $("<div>");
2022 var body = $("<div>");
2016 var title = "Notebook validation failed";
2023 var title = "Notebook validation failed";
2017
2024
2018 body.append($("<p>").text(
2025 body.append($("<p>").text(
2019 "The save operation succeeded," +
2026 "The save operation succeeded," +
2020 " but the notebook does not appear to be valid." +
2027 " but the notebook does not appear to be valid." +
2021 " The validation error was:"
2028 " The validation error was:"
2022 )).append($("<div>").addClass("validation-error").append(
2029 )).append($("<div>").addClass("validation-error").append(
2023 $("<pre>").text(data.message)
2030 $("<pre>").text(data.message)
2024 ));
2031 ));
2025 dialog.modal({
2032 dialog.modal({
2026 notebook: this,
2033 notebook: this,
2027 keyboard_manager: this.keyboard_manager,
2034 keyboard_manager: this.keyboard_manager,
2028 title: title,
2035 title: title,
2029 body: body,
2036 body: body,
2030 buttons : {
2037 buttons : {
2031 OK : {
2038 OK : {
2032 "class" : "btn-primary"
2039 "class" : "btn-primary"
2033 }
2040 }
2034 }
2041 }
2035 });
2042 });
2036 }
2043 }
2037 this.events.trigger('notebook_saved.Notebook');
2044 this.events.trigger('notebook_saved.Notebook');
2038 this._update_autosave_interval(start);
2045 this._update_autosave_interval(start);
2039 if (this._checkpoint_after_save) {
2046 if (this._checkpoint_after_save) {
2040 this.create_checkpoint();
2047 this.create_checkpoint();
2041 this._checkpoint_after_save = false;
2048 this._checkpoint_after_save = false;
2042 }
2049 }
2043 };
2050 };
2044
2051
2045 /**
2052 /**
2046 * update the autosave interval based on how long the last save took
2053 * update the autosave interval based on how long the last save took
2047 *
2054 *
2048 * @method _update_autosave_interval
2055 * @method _update_autosave_interval
2049 * @param {Integer} timestamp when the save request started
2056 * @param {Integer} timestamp when the save request started
2050 */
2057 */
2051 Notebook.prototype._update_autosave_interval = function (start) {
2058 Notebook.prototype._update_autosave_interval = function (start) {
2052 var duration = (new Date().getTime() - start);
2059 var duration = (new Date().getTime() - start);
2053 if (this.autosave_interval) {
2060 if (this.autosave_interval) {
2054 // new save interval: higher of 10x save duration or parameter (default 30 seconds)
2061 // new save interval: higher of 10x save duration or parameter (default 30 seconds)
2055 var interval = Math.max(10 * duration, this.minimum_autosave_interval);
2062 var interval = Math.max(10 * duration, this.minimum_autosave_interval);
2056 // round to 10 seconds, otherwise we will be setting a new interval too often
2063 // round to 10 seconds, otherwise we will be setting a new interval too often
2057 interval = 10000 * Math.round(interval / 10000);
2064 interval = 10000 * Math.round(interval / 10000);
2058 // set new interval, if it's changed
2065 // set new interval, if it's changed
2059 if (interval != this.autosave_interval) {
2066 if (interval != this.autosave_interval) {
2060 this.set_autosave_interval(interval);
2067 this.set_autosave_interval(interval);
2061 }
2068 }
2062 }
2069 }
2063 };
2070 };
2064
2071
2065 /**
2072 /**
2066 * Explicitly trust the output of this notebook.
2073 * Explicitly trust the output of this notebook.
2067 *
2074 *
2068 * @method trust_notebook
2075 * @method trust_notebook
2069 */
2076 */
2070 Notebook.prototype.trust_notebook = function () {
2077 Notebook.prototype.trust_notebook = function () {
2071 var body = $("<div>").append($("<p>")
2078 var body = $("<div>").append($("<p>")
2072 .text("A trusted IPython notebook may execute hidden malicious code ")
2079 .text("A trusted IPython notebook may execute hidden malicious code ")
2073 .append($("<strong>")
2080 .append($("<strong>")
2074 .append(
2081 .append(
2075 $("<em>").text("when you open it")
2082 $("<em>").text("when you open it")
2076 )
2083 )
2077 ).append(".").append(
2084 ).append(".").append(
2078 " Selecting trust will immediately reload this notebook in a trusted state."
2085 " Selecting trust will immediately reload this notebook in a trusted state."
2079 ).append(
2086 ).append(
2080 " For more information, see the "
2087 " For more information, see the "
2081 ).append($("<a>").attr("href", "http://ipython.org/ipython-doc/2/notebook/security.html")
2088 ).append($("<a>").attr("href", "http://ipython.org/ipython-doc/2/notebook/security.html")
2082 .text("IPython security documentation")
2089 .text("IPython security documentation")
2083 ).append(".")
2090 ).append(".")
2084 );
2091 );
2085
2092
2086 var nb = this;
2093 var nb = this;
2087 dialog.modal({
2094 dialog.modal({
2088 notebook: this,
2095 notebook: this,
2089 keyboard_manager: this.keyboard_manager,
2096 keyboard_manager: this.keyboard_manager,
2090 title: "Trust this notebook?",
2097 title: "Trust this notebook?",
2091 body: body,
2098 body: body,
2092
2099
2093 buttons: {
2100 buttons: {
2094 Cancel : {},
2101 Cancel : {},
2095 Trust : {
2102 Trust : {
2096 class : "btn-danger",
2103 class : "btn-danger",
2097 click : function () {
2104 click : function () {
2098 var cells = nb.get_cells();
2105 var cells = nb.get_cells();
2099 for (var i = 0; i < cells.length; i++) {
2106 for (var i = 0; i < cells.length; i++) {
2100 var cell = cells[i];
2107 var cell = cells[i];
2101 if (cell.cell_type == 'code') {
2108 if (cell.cell_type == 'code') {
2102 cell.output_area.trusted = true;
2109 cell.output_area.trusted = true;
2103 }
2110 }
2104 }
2111 }
2105 nb.events.on('notebook_saved.Notebook', function () {
2112 nb.events.on('notebook_saved.Notebook', function () {
2106 window.location.reload();
2113 window.location.reload();
2107 });
2114 });
2108 nb.save_notebook();
2115 nb.save_notebook();
2109 }
2116 }
2110 }
2117 }
2111 }
2118 }
2112 });
2119 });
2113 };
2120 };
2114
2121
2115 Notebook.prototype.copy_notebook = function () {
2122 Notebook.prototype.copy_notebook = function () {
2116 var that = this;
2123 var that = this;
2117 var base_url = this.base_url;
2124 var base_url = this.base_url;
2118 var w = window.open();
2125 var w = window.open();
2119 var parent = utils.url_path_split(this.notebook_path)[0];
2126 var parent = utils.url_path_split(this.notebook_path)[0];
2120 this.contents.copy(this.notebook_path, parent).then(
2127 this.contents.copy(this.notebook_path, parent).then(
2121 function (data) {
2128 function (data) {
2122 w.location = utils.url_join_encode(
2129 w.location = utils.url_join_encode(
2123 base_url, 'notebooks', data.path
2130 base_url, 'notebooks', data.path
2124 );
2131 );
2125 },
2132 },
2126 function(error) {
2133 function(error) {
2127 w.close();
2134 w.close();
2128 that.events.trigger('notebook_copy_failed', error);
2135 that.events.trigger('notebook_copy_failed', error);
2129 }
2136 }
2130 );
2137 );
2131 };
2138 };
2132
2139
2133 Notebook.prototype.rename = function (new_name) {
2140 Notebook.prototype.rename = function (new_name) {
2134 if (!new_name.match(/\.ipynb$/)) {
2141 if (!new_name.match(/\.ipynb$/)) {
2135 new_name = new_name + ".ipynb";
2142 new_name = new_name + ".ipynb";
2136 }
2143 }
2137
2144
2138 var that = this;
2145 var that = this;
2139 var parent = utils.url_path_split(this.notebook_path)[0];
2146 var parent = utils.url_path_split(this.notebook_path)[0];
2140 var new_path = utils.url_path_join(parent, new_name);
2147 var new_path = utils.url_path_join(parent, new_name);
2141 return this.contents.rename(this.notebook_path, new_path).then(
2148 return this.contents.rename(this.notebook_path, new_path).then(
2142 function (json) {
2149 function (json) {
2143 that.notebook_name = json.name;
2150 that.notebook_name = json.name;
2144 that.notebook_path = json.path;
2151 that.notebook_path = json.path;
2145 that.session.rename_notebook(json.path);
2152 that.session.rename_notebook(json.path);
2146 that.events.trigger('notebook_renamed.Notebook', json);
2153 that.events.trigger('notebook_renamed.Notebook', json);
2147 }
2154 }
2148 );
2155 );
2149 };
2156 };
2150
2157
2151 Notebook.prototype.delete = function () {
2158 Notebook.prototype.delete = function () {
2152 this.contents.delete(this.notebook_path);
2159 this.contents.delete(this.notebook_path);
2153 };
2160 };
2154
2161
2155 /**
2162 /**
2156 * Request a notebook's data from the server.
2163 * Request a notebook's data from the server.
2157 *
2164 *
2158 * @method load_notebook
2165 * @method load_notebook
2159 * @param {String} notebook_path A notebook to load
2166 * @param {String} notebook_path A notebook to load
2160 */
2167 */
2161 Notebook.prototype.load_notebook = function (notebook_path) {
2168 Notebook.prototype.load_notebook = function (notebook_path) {
2162 this.notebook_path = notebook_path;
2169 this.notebook_path = notebook_path;
2163 this.notebook_name = utils.url_path_split(this.notebook_path)[1];
2170 this.notebook_name = utils.url_path_split(this.notebook_path)[1];
2164 this.events.trigger('notebook_loading.Notebook');
2171 this.events.trigger('notebook_loading.Notebook');
2165 this.contents.get(notebook_path, {type: 'notebook'}).then(
2172 this.contents.get(notebook_path, {type: 'notebook'}).then(
2166 $.proxy(this.load_notebook_success, this),
2173 $.proxy(this.load_notebook_success, this),
2167 $.proxy(this.load_notebook_error, this)
2174 $.proxy(this.load_notebook_error, this)
2168 );
2175 );
2169 };
2176 };
2170
2177
2171 /**
2178 /**
2172 * Success callback for loading a notebook from the server.
2179 * Success callback for loading a notebook from the server.
2173 *
2180 *
2174 * Load notebook data from the JSON response.
2181 * Load notebook data from the JSON response.
2175 *
2182 *
2176 * @method load_notebook_success
2183 * @method load_notebook_success
2177 * @param {Object} data JSON representation of a notebook
2184 * @param {Object} data JSON representation of a notebook
2178 */
2185 */
2179 Notebook.prototype.load_notebook_success = function (data) {
2186 Notebook.prototype.load_notebook_success = function (data) {
2180 var failed, msg;
2187 var failed, msg;
2181 try {
2188 try {
2182 this.fromJSON(data);
2189 this.fromJSON(data);
2183 } catch (e) {
2190 } catch (e) {
2184 failed = e;
2191 failed = e;
2185 console.log("Notebook failed to load from JSON:", e);
2192 console.log("Notebook failed to load from JSON:", e);
2186 }
2193 }
2187 if (failed || data.message) {
2194 if (failed || data.message) {
2188 // *either* fromJSON failed or validation failed
2195 // *either* fromJSON failed or validation failed
2189 var body = $("<div>");
2196 var body = $("<div>");
2190 var title;
2197 var title;
2191 if (failed) {
2198 if (failed) {
2192 title = "Notebook failed to load";
2199 title = "Notebook failed to load";
2193 body.append($("<p>").text(
2200 body.append($("<p>").text(
2194 "The error was: "
2201 "The error was: "
2195 )).append($("<div>").addClass("js-error").text(
2202 )).append($("<div>").addClass("js-error").text(
2196 failed.toString()
2203 failed.toString()
2197 )).append($("<p>").text(
2204 )).append($("<p>").text(
2198 "See the error console for details."
2205 "See the error console for details."
2199 ));
2206 ));
2200 } else {
2207 } else {
2201 title = "Notebook validation failed";
2208 title = "Notebook validation failed";
2202 }
2209 }
2203
2210
2204 if (data.message) {
2211 if (data.message) {
2205 if (failed) {
2212 if (failed) {
2206 msg = "The notebook also failed validation:";
2213 msg = "The notebook also failed validation:";
2207 } else {
2214 } else {
2208 msg = "An invalid notebook may not function properly." +
2215 msg = "An invalid notebook may not function properly." +
2209 " The validation error was:";
2216 " The validation error was:";
2210 }
2217 }
2211 body.append($("<p>").text(
2218 body.append($("<p>").text(
2212 msg
2219 msg
2213 )).append($("<div>").addClass("validation-error").append(
2220 )).append($("<div>").addClass("validation-error").append(
2214 $("<pre>").text(data.message)
2221 $("<pre>").text(data.message)
2215 ));
2222 ));
2216 }
2223 }
2217
2224
2218 dialog.modal({
2225 dialog.modal({
2219 notebook: this,
2226 notebook: this,
2220 keyboard_manager: this.keyboard_manager,
2227 keyboard_manager: this.keyboard_manager,
2221 title: title,
2228 title: title,
2222 body: body,
2229 body: body,
2223 buttons : {
2230 buttons : {
2224 OK : {
2231 OK : {
2225 "class" : "btn-primary"
2232 "class" : "btn-primary"
2226 }
2233 }
2227 }
2234 }
2228 });
2235 });
2229 }
2236 }
2230 if (this.ncells() === 0) {
2237 if (this.ncells() === 0) {
2231 this.insert_cell_below('code');
2238 this.insert_cell_below('code');
2232 this.edit_mode(0);
2239 this.edit_mode(0);
2233 } else {
2240 } else {
2234 this.select(0);
2241 this.select(0);
2235 this.handle_command_mode(this.get_cell(0));
2242 this.handle_command_mode(this.get_cell(0));
2236 }
2243 }
2237 this.set_dirty(false);
2244 this.set_dirty(false);
2238 this.scroll_to_top();
2245 this.scroll_to_top();
2239 this.writable = data.writable || false;
2246 this.writable = data.writable || false;
2240 var nbmodel = data.content;
2247 var nbmodel = data.content;
2241 var orig_nbformat = nbmodel.metadata.orig_nbformat;
2248 var orig_nbformat = nbmodel.metadata.orig_nbformat;
2242 var orig_nbformat_minor = nbmodel.metadata.orig_nbformat_minor;
2249 var orig_nbformat_minor = nbmodel.metadata.orig_nbformat_minor;
2243 if (orig_nbformat !== undefined && nbmodel.nbformat !== orig_nbformat) {
2250 if (orig_nbformat !== undefined && nbmodel.nbformat !== orig_nbformat) {
2244 var src;
2251 var src;
2245 if (nbmodel.nbformat > orig_nbformat) {
2252 if (nbmodel.nbformat > orig_nbformat) {
2246 src = " an older notebook format ";
2253 src = " an older notebook format ";
2247 } else {
2254 } else {
2248 src = " a newer notebook format ";
2255 src = " a newer notebook format ";
2249 }
2256 }
2250
2257
2251 msg = "This notebook has been converted from" + src +
2258 msg = "This notebook has been converted from" + src +
2252 "(v"+orig_nbformat+") to the current notebook " +
2259 "(v"+orig_nbformat+") to the current notebook " +
2253 "format (v"+nbmodel.nbformat+"). The next time you save this notebook, the " +
2260 "format (v"+nbmodel.nbformat+"). The next time you save this notebook, the " +
2254 "current notebook format will be used.";
2261 "current notebook format will be used.";
2255
2262
2256 if (nbmodel.nbformat > orig_nbformat) {
2263 if (nbmodel.nbformat > orig_nbformat) {
2257 msg += " Older versions of IPython may not be able to read the new format.";
2264 msg += " Older versions of IPython may not be able to read the new format.";
2258 } else {
2265 } else {
2259 msg += " Some features of the original notebook may not be available.";
2266 msg += " Some features of the original notebook may not be available.";
2260 }
2267 }
2261 msg += " To preserve the original version, close the " +
2268 msg += " To preserve the original version, close the " +
2262 "notebook without saving it.";
2269 "notebook without saving it.";
2263 dialog.modal({
2270 dialog.modal({
2264 notebook: this,
2271 notebook: this,
2265 keyboard_manager: this.keyboard_manager,
2272 keyboard_manager: this.keyboard_manager,
2266 title : "Notebook converted",
2273 title : "Notebook converted",
2267 body : msg,
2274 body : msg,
2268 buttons : {
2275 buttons : {
2269 OK : {
2276 OK : {
2270 class : "btn-primary"
2277 class : "btn-primary"
2271 }
2278 }
2272 }
2279 }
2273 });
2280 });
2274 } else if (this.nbformat_minor < nbmodel.nbformat_minor) {
2281 } else if (this.nbformat_minor < nbmodel.nbformat_minor) {
2275 this.nbformat_minor = nbmodel.nbformat_minor;
2282 this.nbformat_minor = nbmodel.nbformat_minor;
2276 }
2283 }
2277
2284
2278 // Create the session after the notebook is completely loaded to prevent
2285 // Create the session after the notebook is completely loaded to prevent
2279 // code execution upon loading, which is a security risk.
2286 // code execution upon loading, which is a security risk.
2280 if (this.session === null) {
2287 if (this.session === null) {
2281 var kernel_name;
2288 var kernel_name;
2282 if (this.metadata.kernelspec) {
2289 if (this.metadata.kernelspec) {
2283 var kernelspec = this.metadata.kernelspec || {};
2290 var kernelspec = this.metadata.kernelspec || {};
2284 kernel_name = kernelspec.name;
2291 kernel_name = kernelspec.name;
2285 } else {
2292 } else {
2286 kernel_name = utils.get_url_param('kernel_name');
2293 kernel_name = utils.get_url_param('kernel_name');
2287 }
2294 }
2288 this.start_session(kernel_name);
2295 this.start_session(kernel_name);
2289 }
2296 }
2290 // load our checkpoint list
2297 // load our checkpoint list
2291 this.list_checkpoints();
2298 this.list_checkpoints();
2292
2299
2293 // load toolbar state
2300 // load toolbar state
2294 if (this.metadata.celltoolbar) {
2301 if (this.metadata.celltoolbar) {
2295 celltoolbar.CellToolbar.global_show();
2302 celltoolbar.CellToolbar.global_show();
2296 celltoolbar.CellToolbar.activate_preset(this.metadata.celltoolbar);
2303 celltoolbar.CellToolbar.activate_preset(this.metadata.celltoolbar);
2297 } else {
2304 } else {
2298 celltoolbar.CellToolbar.global_hide();
2305 celltoolbar.CellToolbar.global_hide();
2299 }
2306 }
2300
2307
2301 if (!this.writable) {
2308 if (!this.writable) {
2302 this.set_autosave_interval(0);
2309 this.set_autosave_interval(0);
2303 this.events.trigger('notebook_read_only.Notebook');
2310 this.events.trigger('notebook_read_only.Notebook');
2304 }
2311 }
2305
2312
2306 // now that we're fully loaded, it is safe to restore save functionality
2313 // now that we're fully loaded, it is safe to restore save functionality
2307 this._fully_loaded = true;
2314 this._fully_loaded = true;
2308 this.events.trigger('notebook_loaded.Notebook');
2315 this.events.trigger('notebook_loaded.Notebook');
2309 };
2316 };
2310
2317
2318 Notebook.prototype.set_kernelselector = function(k_selector){
2319 this.kernel_selector = k_selector;
2320 };
2321
2311 /**
2322 /**
2312 * Failure callback for loading a notebook from the server.
2323 * Failure callback for loading a notebook from the server.
2313 *
2324 *
2314 * @method load_notebook_error
2325 * @method load_notebook_error
2315 * @param {Error} error
2326 * @param {Error} error
2316 */
2327 */
2317 Notebook.prototype.load_notebook_error = function (error) {
2328 Notebook.prototype.load_notebook_error = function (error) {
2318 this.events.trigger('notebook_load_failed.Notebook', error);
2329 this.events.trigger('notebook_load_failed.Notebook', error);
2319 var msg;
2330 var msg;
2320 if (error.name === utils.XHR_ERROR && error.xhr.status === 500) {
2331 if (error.name === utils.XHR_ERROR && error.xhr.status === 500) {
2321 utils.log_ajax_error(error.xhr, error.xhr_status, error.xhr_error);
2332 utils.log_ajax_error(error.xhr, error.xhr_status, error.xhr_error);
2322 msg = "An unknown error occurred while loading this notebook. " +
2333 msg = "An unknown error occurred while loading this notebook. " +
2323 "This version can load notebook formats " +
2334 "This version can load notebook formats " +
2324 "v" + this.nbformat + " or earlier. See the server log for details.";
2335 "v" + this.nbformat + " or earlier. See the server log for details.";
2325 } else {
2336 } else {
2326 msg = error.message;
2337 msg = error.message;
2327 }
2338 }
2328 dialog.modal({
2339 dialog.modal({
2329 notebook: this,
2340 notebook: this,
2330 keyboard_manager: this.keyboard_manager,
2341 keyboard_manager: this.keyboard_manager,
2331 title: "Error loading notebook",
2342 title: "Error loading notebook",
2332 body : msg,
2343 body : msg,
2333 buttons : {
2344 buttons : {
2334 "OK": {}
2345 "OK": {}
2335 }
2346 }
2336 });
2347 });
2337 };
2348 };
2338
2349
2339 /********************* checkpoint-related *********************/
2350 /********************* checkpoint-related *********************/
2340
2351
2341 /**
2352 /**
2342 * Save the notebook then immediately create a checkpoint.
2353 * Save the notebook then immediately create a checkpoint.
2343 *
2354 *
2344 * @method save_checkpoint
2355 * @method save_checkpoint
2345 */
2356 */
2346 Notebook.prototype.save_checkpoint = function () {
2357 Notebook.prototype.save_checkpoint = function () {
2347 this._checkpoint_after_save = true;
2358 this._checkpoint_after_save = true;
2348 this.save_notebook();
2359 this.save_notebook();
2349 };
2360 };
2350
2361
2351 /**
2362 /**
2352 * Add a checkpoint for this notebook.
2363 * Add a checkpoint for this notebook.
2353 * for use as a callback from checkpoint creation.
2364 * for use as a callback from checkpoint creation.
2354 *
2365 *
2355 * @method add_checkpoint
2366 * @method add_checkpoint
2356 */
2367 */
2357 Notebook.prototype.add_checkpoint = function (checkpoint) {
2368 Notebook.prototype.add_checkpoint = function (checkpoint) {
2358 var found = false;
2369 var found = false;
2359 for (var i = 0; i < this.checkpoints.length; i++) {
2370 for (var i = 0; i < this.checkpoints.length; i++) {
2360 var existing = this.checkpoints[i];
2371 var existing = this.checkpoints[i];
2361 if (existing.id == checkpoint.id) {
2372 if (existing.id == checkpoint.id) {
2362 found = true;
2373 found = true;
2363 this.checkpoints[i] = checkpoint;
2374 this.checkpoints[i] = checkpoint;
2364 break;
2375 break;
2365 }
2376 }
2366 }
2377 }
2367 if (!found) {
2378 if (!found) {
2368 this.checkpoints.push(checkpoint);
2379 this.checkpoints.push(checkpoint);
2369 }
2380 }
2370 this.last_checkpoint = this.checkpoints[this.checkpoints.length - 1];
2381 this.last_checkpoint = this.checkpoints[this.checkpoints.length - 1];
2371 };
2382 };
2372
2383
2373 /**
2384 /**
2374 * List checkpoints for this notebook.
2385 * List checkpoints for this notebook.
2375 *
2386 *
2376 * @method list_checkpoints
2387 * @method list_checkpoints
2377 */
2388 */
2378 Notebook.prototype.list_checkpoints = function () {
2389 Notebook.prototype.list_checkpoints = function () {
2379 var that = this;
2390 var that = this;
2380 this.contents.list_checkpoints(this.notebook_path).then(
2391 this.contents.list_checkpoints(this.notebook_path).then(
2381 $.proxy(this.list_checkpoints_success, this),
2392 $.proxy(this.list_checkpoints_success, this),
2382 function(error) {
2393 function(error) {
2383 that.events.trigger('list_checkpoints_failed.Notebook', error);
2394 that.events.trigger('list_checkpoints_failed.Notebook', error);
2384 }
2395 }
2385 );
2396 );
2386 };
2397 };
2387
2398
2388 /**
2399 /**
2389 * Success callback for listing checkpoints.
2400 * Success callback for listing checkpoints.
2390 *
2401 *
2391 * @method list_checkpoint_success
2402 * @method list_checkpoint_success
2392 * @param {Object} data JSON representation of a checkpoint
2403 * @param {Object} data JSON representation of a checkpoint
2393 */
2404 */
2394 Notebook.prototype.list_checkpoints_success = function (data) {
2405 Notebook.prototype.list_checkpoints_success = function (data) {
2395 this.checkpoints = data;
2406 this.checkpoints = data;
2396 if (data.length) {
2407 if (data.length) {
2397 this.last_checkpoint = data[data.length - 1];
2408 this.last_checkpoint = data[data.length - 1];
2398 } else {
2409 } else {
2399 this.last_checkpoint = null;
2410 this.last_checkpoint = null;
2400 }
2411 }
2401 this.events.trigger('checkpoints_listed.Notebook', [data]);
2412 this.events.trigger('checkpoints_listed.Notebook', [data]);
2402 };
2413 };
2403
2414
2404 /**
2415 /**
2405 * Create a checkpoint of this notebook on the server from the most recent save.
2416 * Create a checkpoint of this notebook on the server from the most recent save.
2406 *
2417 *
2407 * @method create_checkpoint
2418 * @method create_checkpoint
2408 */
2419 */
2409 Notebook.prototype.create_checkpoint = function () {
2420 Notebook.prototype.create_checkpoint = function () {
2410 var that = this;
2421 var that = this;
2411 this.contents.create_checkpoint(this.notebook_path).then(
2422 this.contents.create_checkpoint(this.notebook_path).then(
2412 $.proxy(this.create_checkpoint_success, this),
2423 $.proxy(this.create_checkpoint_success, this),
2413 function (error) {
2424 function (error) {
2414 that.events.trigger('checkpoint_failed.Notebook', error);
2425 that.events.trigger('checkpoint_failed.Notebook', error);
2415 }
2426 }
2416 );
2427 );
2417 };
2428 };
2418
2429
2419 /**
2430 /**
2420 * Success callback for creating a checkpoint.
2431 * Success callback for creating a checkpoint.
2421 *
2432 *
2422 * @method create_checkpoint_success
2433 * @method create_checkpoint_success
2423 * @param {Object} data JSON representation of a checkpoint
2434 * @param {Object} data JSON representation of a checkpoint
2424 */
2435 */
2425 Notebook.prototype.create_checkpoint_success = function (data) {
2436 Notebook.prototype.create_checkpoint_success = function (data) {
2426 this.add_checkpoint(data);
2437 this.add_checkpoint(data);
2427 this.events.trigger('checkpoint_created.Notebook', data);
2438 this.events.trigger('checkpoint_created.Notebook', data);
2428 };
2439 };
2429
2440
2430 Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) {
2441 Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) {
2431 var that = this;
2442 var that = this;
2432 checkpoint = checkpoint || this.last_checkpoint;
2443 checkpoint = checkpoint || this.last_checkpoint;
2433 if ( ! checkpoint ) {
2444 if ( ! checkpoint ) {
2434 console.log("restore dialog, but no checkpoint to restore to!");
2445 console.log("restore dialog, but no checkpoint to restore to!");
2435 return;
2446 return;
2436 }
2447 }
2437 var body = $('<div/>').append(
2448 var body = $('<div/>').append(
2438 $('<p/>').addClass("p-space").text(
2449 $('<p/>').addClass("p-space").text(
2439 "Are you sure you want to revert the notebook to " +
2450 "Are you sure you want to revert the notebook to " +
2440 "the latest checkpoint?"
2451 "the latest checkpoint?"
2441 ).append(
2452 ).append(
2442 $("<strong/>").text(
2453 $("<strong/>").text(
2443 " This cannot be undone."
2454 " This cannot be undone."
2444 )
2455 )
2445 )
2456 )
2446 ).append(
2457 ).append(
2447 $('<p/>').addClass("p-space").text("The checkpoint was last updated at:")
2458 $('<p/>').addClass("p-space").text("The checkpoint was last updated at:")
2448 ).append(
2459 ).append(
2449 $('<p/>').addClass("p-space").text(
2460 $('<p/>').addClass("p-space").text(
2450 Date(checkpoint.last_modified)
2461 Date(checkpoint.last_modified)
2451 ).css("text-align", "center")
2462 ).css("text-align", "center")
2452 );
2463 );
2453
2464
2454 dialog.modal({
2465 dialog.modal({
2455 notebook: this,
2466 notebook: this,
2456 keyboard_manager: this.keyboard_manager,
2467 keyboard_manager: this.keyboard_manager,
2457 title : "Revert notebook to checkpoint",
2468 title : "Revert notebook to checkpoint",
2458 body : body,
2469 body : body,
2459 buttons : {
2470 buttons : {
2460 Revert : {
2471 Revert : {
2461 class : "btn-danger",
2472 class : "btn-danger",
2462 click : function () {
2473 click : function () {
2463 that.restore_checkpoint(checkpoint.id);
2474 that.restore_checkpoint(checkpoint.id);
2464 }
2475 }
2465 },
2476 },
2466 Cancel : {}
2477 Cancel : {}
2467 }
2478 }
2468 });
2479 });
2469 };
2480 };
2470
2481
2471 /**
2482 /**
2472 * Restore the notebook to a checkpoint state.
2483 * Restore the notebook to a checkpoint state.
2473 *
2484 *
2474 * @method restore_checkpoint
2485 * @method restore_checkpoint
2475 * @param {String} checkpoint ID
2486 * @param {String} checkpoint ID
2476 */
2487 */
2477 Notebook.prototype.restore_checkpoint = function (checkpoint) {
2488 Notebook.prototype.restore_checkpoint = function (checkpoint) {
2478 this.events.trigger('notebook_restoring.Notebook', checkpoint);
2489 this.events.trigger('notebook_restoring.Notebook', checkpoint);
2479 var that = this;
2490 var that = this;
2480 this.contents.restore_checkpoint(this.notebook_path, checkpoint).then(
2491 this.contents.restore_checkpoint(this.notebook_path, checkpoint).then(
2481 $.proxy(this.restore_checkpoint_success, this),
2492 $.proxy(this.restore_checkpoint_success, this),
2482 function (error) {
2493 function (error) {
2483 that.events.trigger('checkpoint_restore_failed.Notebook', error);
2494 that.events.trigger('checkpoint_restore_failed.Notebook', error);
2484 }
2495 }
2485 );
2496 );
2486 };
2497 };
2487
2498
2488 /**
2499 /**
2489 * Success callback for restoring a notebook to a checkpoint.
2500 * Success callback for restoring a notebook to a checkpoint.
2490 *
2501 *
2491 * @method restore_checkpoint_success
2502 * @method restore_checkpoint_success
2492 */
2503 */
2493 Notebook.prototype.restore_checkpoint_success = function () {
2504 Notebook.prototype.restore_checkpoint_success = function () {
2494 this.events.trigger('checkpoint_restored.Notebook');
2505 this.events.trigger('checkpoint_restored.Notebook');
2495 this.load_notebook(this.notebook_path);
2506 this.load_notebook(this.notebook_path);
2496 };
2507 };
2497
2508
2498 /**
2509 /**
2499 * Delete a notebook checkpoint.
2510 * Delete a notebook checkpoint.
2500 *
2511 *
2501 * @method delete_checkpoint
2512 * @method delete_checkpoint
2502 * @param {String} checkpoint ID
2513 * @param {String} checkpoint ID
2503 */
2514 */
2504 Notebook.prototype.delete_checkpoint = function (checkpoint) {
2515 Notebook.prototype.delete_checkpoint = function (checkpoint) {
2505 this.events.trigger('notebook_restoring.Notebook', checkpoint);
2516 this.events.trigger('notebook_restoring.Notebook', checkpoint);
2506 var that = this;
2517 var that = this;
2507 this.contents.delete_checkpoint(this.notebook_path, checkpoint).then(
2518 this.contents.delete_checkpoint(this.notebook_path, checkpoint).then(
2508 $.proxy(this.delete_checkpoint_success, this),
2519 $.proxy(this.delete_checkpoint_success, this),
2509 function (error) {
2520 function (error) {
2510 that.events.trigger('checkpoint_delete_failed.Notebook', error);
2521 that.events.trigger('checkpoint_delete_failed.Notebook', error);
2511 }
2522 }
2512 );
2523 );
2513 };
2524 };
2514
2525
2515 /**
2526 /**
2516 * Success callback for deleting a notebook checkpoint
2527 * Success callback for deleting a notebook checkpoint
2517 *
2528 *
2518 * @method delete_checkpoint_success
2529 * @method delete_checkpoint_success
2519 */
2530 */
2520 Notebook.prototype.delete_checkpoint_success = function () {
2531 Notebook.prototype.delete_checkpoint_success = function () {
2521 this.events.trigger('checkpoint_deleted.Notebook');
2532 this.events.trigger('checkpoint_deleted.Notebook');
2522 this.load_notebook(this.notebook_path);
2533 this.load_notebook(this.notebook_path);
2523 };
2534 };
2524
2535
2525
2536
2526 // For backwards compatability.
2537 // For backwards compatability.
2527 IPython.Notebook = Notebook;
2538 IPython.Notebook = Notebook;
2528
2539
2529 return {'Notebook': Notebook};
2540 return {'Notebook': Notebook};
2530 });
2541 });
@@ -1,332 +1,333 b''
1 {% extends "page.html" %}
1 {% extends "page.html" %}
2
2
3 {% block stylesheet %}
3 {% block stylesheet %}
4
4
5 {% if mathjax_url %}
5 {% if mathjax_url %}
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
7 {% endif %}
7 {% endif %}
8 <script type="text/javascript">
8 <script type="text/javascript">
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
10 // where it will be undefined, and should prompt a dialog later.
10 // where it will be undefined, and should prompt a dialog later.
11 window.mathjax_url = "{{mathjax_url}}";
11 window.mathjax_url = "{{mathjax_url}}";
12 </script>
12 </script>
13
13
14 <link rel="stylesheet" href="{{ static_url("components/bootstrap-tour/build/css/bootstrap-tour.min.css") }}" type="text/css" />
14 <link rel="stylesheet" href="{{ static_url("components/bootstrap-tour/build/css/bootstrap-tour.min.css") }}" type="text/css" />
15 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
15 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
16
16
17 {{super()}}
17 {{super()}}
18
18
19 <link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
19 <link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
20 <link rel="stylesheet" href="" id='kernel-css' type="text/css" />
20
21
21 {% endblock %}
22 {% endblock %}
22
23
23 {% block params %}
24 {% block params %}
24
25
25 data-project="{{project}}"
26 data-project="{{project}}"
26 data-base-url="{{base_url}}"
27 data-base-url="{{base_url}}"
27 data-ws-url="{{ws_url}}"
28 data-ws-url="{{ws_url}}"
28 data-notebook-name="{{notebook_name}}"
29 data-notebook-name="{{notebook_name}}"
29 data-notebook-path="{{notebook_path}}"
30 data-notebook-path="{{notebook_path}}"
30 class="notebook_app"
31 class="notebook_app"
31
32
32 {% endblock %}
33 {% endblock %}
33
34
34
35
35 {% block headercontainer %}
36 {% block headercontainer %}
36
37
37
38
38 <span id="save_widget" class="pull-left save_widget">
39 <span id="save_widget" class="pull-left save_widget">
39 <span class="filename"></span>
40 <span class="filename"></span>
40 <span class="checkpoint_status"></span>
41 <span class="checkpoint_status"></span>
41 <span class="autosave_status"></span>
42 <span class="autosave_status"></span>
42 </span>
43 </span>
43
44
44 <span id="kernel_selector_widget" class="pull-right dropdown">
45 <span id="kernel_selector_widget" class="pull-right dropdown">
45 <img id="current_kernel_logo"/>
46 <img id="current_kernel_logo"/>
46 <button class="dropdown-toggle btn btn-sm navbar-btn" data-toggle="dropdown" type='button' id="current_kernel_spec">
47 <button class="dropdown-toggle btn btn-sm navbar-btn" data-toggle="dropdown" type='button' id="current_kernel_spec">
47 <span class='kernel_name'>Kernel</span>
48 <span class='kernel_name'>Kernel</span>
48 <span class="caret"></span>
49 <span class="caret"></span>
49 </button>
50 </button>
50 <ul id="kernel_selector" class="dropdown-menu">
51 <ul id="kernel_selector" class="dropdown-menu">
51 </ul>
52 </ul>
52 </span>
53 </span>
53
54
54 {% endblock headercontainer %}
55 {% endblock headercontainer %}
55
56
56 {% block header %}
57 {% block header %}
57 <div id="menubar-container" class="container">
58 <div id="menubar-container" class="container">
58 <div id="menubar">
59 <div id="menubar">
59 <div id="menus" class="navbar navbar-default" role="navigation">
60 <div id="menus" class="navbar navbar-default" role="navigation">
60 <div class="container-fluid">
61 <div class="container-fluid">
61 <button type="button" class="btn btn-default navbar-btn navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
62 <button type="button" class="btn btn-default navbar-btn navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
62 <i class="fa fa-bars"></i>
63 <i class="fa fa-bars"></i>
63 <span class="navbar-text">Menu</span>
64 <span class="navbar-text">Menu</span>
64 </button>
65 </button>
65 <p id="kernel_indicator" class="navbar-text">
66 <p id="kernel_indicator" class="navbar-text">
66 <i id="kernel_indicator_icon"></i>
67 <i id="kernel_indicator_icon"></i>
67 </p>
68 </p>
68 <p id="modal_indicator" class="navbar-text">
69 <p id="modal_indicator" class="navbar-text">
69 <i id="modal_indicator_icon"></i>
70 <i id="modal_indicator_icon"></i>
70 </p>
71 </p>
71 <span id="notification_area"></span>
72 <span id="notification_area"></span>
72 <div class="navbar-collapse collapse">
73 <div class="navbar-collapse collapse">
73 <ul class="nav navbar-nav">
74 <ul class="nav navbar-nav">
74 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
75 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
75 <ul id="file_menu" class="dropdown-menu">
76 <ul id="file_menu" class="dropdown-menu">
76 <li id="new_notebook"
77 <li id="new_notebook"
77 title="Make a new notebook (Opens a new window)">
78 title="Make a new notebook (Opens a new window)">
78 <a href="#">New</a></li>
79 <a href="#">New</a></li>
79 <li id="open_notebook"
80 <li id="open_notebook"
80 title="Opens a new window with the Dashboard view">
81 title="Opens a new window with the Dashboard view">
81 <a href="#">Open...</a></li>
82 <a href="#">Open...</a></li>
82 <!-- <hr/> -->
83 <!-- <hr/> -->
83 <li class="divider"></li>
84 <li class="divider"></li>
84 <li id="copy_notebook"
85 <li id="copy_notebook"
85 title="Open a copy of this notebook's contents and start a new kernel">
86 title="Open a copy of this notebook's contents and start a new kernel">
86 <a href="#">Make a Copy...</a></li>
87 <a href="#">Make a Copy...</a></li>
87 <li id="rename_notebook"><a href="#">Rename...</a></li>
88 <li id="rename_notebook"><a href="#">Rename...</a></li>
88 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
89 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
89 <!-- <hr/> -->
90 <!-- <hr/> -->
90 <li class="divider"></li>
91 <li class="divider"></li>
91 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
92 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
92 <ul class="dropdown-menu">
93 <ul class="dropdown-menu">
93 <li><a href="#"></a></li>
94 <li><a href="#"></a></li>
94 <li><a href="#"></a></li>
95 <li><a href="#"></a></li>
95 <li><a href="#"></a></li>
96 <li><a href="#"></a></li>
96 <li><a href="#"></a></li>
97 <li><a href="#"></a></li>
97 <li><a href="#"></a></li>
98 <li><a href="#"></a></li>
98 </ul>
99 </ul>
99 </li>
100 </li>
100 <li class="divider"></li>
101 <li class="divider"></li>
101 <li id="print_preview"><a href="#">Print Preview</a></li>
102 <li id="print_preview"><a href="#">Print Preview</a></li>
102 <li class="dropdown-submenu"><a href="#">Download as</a>
103 <li class="dropdown-submenu"><a href="#">Download as</a>
103 <ul class="dropdown-menu">
104 <ul class="dropdown-menu">
104 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
105 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
105 <li id="download_script"><a href="#">Script</a></li>
106 <li id="download_script"><a href="#">Script</a></li>
106 <li id="download_html"><a href="#">HTML (.html)</a></li>
107 <li id="download_html"><a href="#">HTML (.html)</a></li>
107 <li id="download_rst"><a href="#">reST (.rst)</a></li>
108 <li id="download_rst"><a href="#">reST (.rst)</a></li>
108 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
109 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
109 </ul>
110 </ul>
110 </li>
111 </li>
111 <li class="divider"></li>
112 <li class="divider"></li>
112 <li id="trust_notebook"
113 <li id="trust_notebook"
113 title="Trust the output of this notebook">
114 title="Trust the output of this notebook">
114 <a href="#" >Trust Notebook</a></li>
115 <a href="#" >Trust Notebook</a></li>
115 <li class="divider"></li>
116 <li class="divider"></li>
116 <li id="kill_and_exit"
117 <li id="kill_and_exit"
117 title="Shutdown this notebook's kernel, and close this window">
118 title="Shutdown this notebook's kernel, and close this window">
118 <a href="#" >Close and halt</a></li>
119 <a href="#" >Close and halt</a></li>
119 </ul>
120 </ul>
120 </li>
121 </li>
121 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
122 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
122 <ul id="edit_menu" class="dropdown-menu">
123 <ul id="edit_menu" class="dropdown-menu">
123 <li id="cut_cell"><a href="#">Cut Cell</a></li>
124 <li id="cut_cell"><a href="#">Cut Cell</a></li>
124 <li id="copy_cell"><a href="#">Copy Cell</a></li>
125 <li id="copy_cell"><a href="#">Copy Cell</a></li>
125 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
126 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
126 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
127 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
127 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
128 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
128 <li id="delete_cell"><a href="#">Delete Cell</a></li>
129 <li id="delete_cell"><a href="#">Delete Cell</a></li>
129 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
130 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
130 <li class="divider"></li>
131 <li class="divider"></li>
131 <li id="split_cell"><a href="#">Split Cell</a></li>
132 <li id="split_cell"><a href="#">Split Cell</a></li>
132 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
133 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
133 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
134 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
134 <li class="divider"></li>
135 <li class="divider"></li>
135 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
136 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
136 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
137 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
137 <li class="divider"></li>
138 <li class="divider"></li>
138 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
139 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
139 </ul>
140 </ul>
140 </li>
141 </li>
141 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
142 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
142 <ul id="view_menu" class="dropdown-menu">
143 <ul id="view_menu" class="dropdown-menu">
143 <li id="toggle_header"
144 <li id="toggle_header"
144 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
145 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
145 <a href="#">Toggle Header</a></li>
146 <a href="#">Toggle Header</a></li>
146 <li id="toggle_toolbar"
147 <li id="toggle_toolbar"
147 title="Show/Hide the action icons (below menu bar)">
148 title="Show/Hide the action icons (below menu bar)">
148 <a href="#">Toggle Toolbar</a></li>
149 <a href="#">Toggle Toolbar</a></li>
149 </ul>
150 </ul>
150 </li>
151 </li>
151 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
152 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
152 <ul id="insert_menu" class="dropdown-menu">
153 <ul id="insert_menu" class="dropdown-menu">
153 <li id="insert_cell_above"
154 <li id="insert_cell_above"
154 title="Insert an empty Code cell above the currently active cell">
155 title="Insert an empty Code cell above the currently active cell">
155 <a href="#">Insert Cell Above</a></li>
156 <a href="#">Insert Cell Above</a></li>
156 <li id="insert_cell_below"
157 <li id="insert_cell_below"
157 title="Insert an empty Code cell below the currently active cell">
158 title="Insert an empty Code cell below the currently active cell">
158 <a href="#">Insert Cell Below</a></li>
159 <a href="#">Insert Cell Below</a></li>
159 </ul>
160 </ul>
160 </li>
161 </li>
161 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
162 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
162 <ul id="cell_menu" class="dropdown-menu">
163 <ul id="cell_menu" class="dropdown-menu">
163 <li id="run_cell" title="Run this cell, and move cursor to the next one">
164 <li id="run_cell" title="Run this cell, and move cursor to the next one">
164 <a href="#">Run</a></li>
165 <a href="#">Run</a></li>
165 <li id="run_cell_select_below" title="Run this cell, select below">
166 <li id="run_cell_select_below" title="Run this cell, select below">
166 <a href="#">Run and Select Below</a></li>
167 <a href="#">Run and Select Below</a></li>
167 <li id="run_cell_insert_below" title="Run this cell, insert below">
168 <li id="run_cell_insert_below" title="Run this cell, insert below">
168 <a href="#">Run and Insert Below</a></li>
169 <a href="#">Run and Insert Below</a></li>
169 <li id="run_all_cells" title="Run all cells in the notebook">
170 <li id="run_all_cells" title="Run all cells in the notebook">
170 <a href="#">Run All</a></li>
171 <a href="#">Run All</a></li>
171 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
172 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
172 <a href="#">Run All Above</a></li>
173 <a href="#">Run All Above</a></li>
173 <li id="run_all_cells_below" title="Run this cell and all cells below it">
174 <li id="run_all_cells_below" title="Run this cell and all cells below it">
174 <a href="#">Run All Below</a></li>
175 <a href="#">Run All Below</a></li>
175 <li class="divider"></li>
176 <li class="divider"></li>
176 <li id="change_cell_type" class="dropdown-submenu"
177 <li id="change_cell_type" class="dropdown-submenu"
177 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
178 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
178 <a href="#">Cell Type</a>
179 <a href="#">Cell Type</a>
179 <ul class="dropdown-menu">
180 <ul class="dropdown-menu">
180 <li id="to_code"
181 <li id="to_code"
181 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
182 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
182 <a href="#">Code</a></li>
183 <a href="#">Code</a></li>
183 <li id="to_markdown"
184 <li id="to_markdown"
184 title="Contents will be rendered as HTML and serve as explanatory text">
185 title="Contents will be rendered as HTML and serve as explanatory text">
185 <a href="#">Markdown</a></li>
186 <a href="#">Markdown</a></li>
186 <li id="to_raw"
187 <li id="to_raw"
187 title="Contents will pass through nbconvert unmodified">
188 title="Contents will pass through nbconvert unmodified">
188 <a href="#">Raw NBConvert</a></li>
189 <a href="#">Raw NBConvert</a></li>
189 </ul>
190 </ul>
190 </li>
191 </li>
191 <li class="divider"></li>
192 <li class="divider"></li>
192 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
193 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
193 <ul class="dropdown-menu">
194 <ul class="dropdown-menu">
194 <li id="toggle_current_output"
195 <li id="toggle_current_output"
195 title="Hide/Show the output of the current cell">
196 title="Hide/Show the output of the current cell">
196 <a href="#">Toggle</a>
197 <a href="#">Toggle</a>
197 </li>
198 </li>
198 <li id="toggle_current_output_scroll"
199 <li id="toggle_current_output_scroll"
199 title="Scroll the output of the current cell">
200 title="Scroll the output of the current cell">
200 <a href="#">Toggle Scrolling</a>
201 <a href="#">Toggle Scrolling</a>
201 </li>
202 </li>
202 <li id="clear_current_output"
203 <li id="clear_current_output"
203 title="Clear the output of the current cell">
204 title="Clear the output of the current cell">
204 <a href="#">Clear</a>
205 <a href="#">Clear</a>
205 </li>
206 </li>
206 </ul>
207 </ul>
207 </li>
208 </li>
208 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
209 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
209 <ul class="dropdown-menu">
210 <ul class="dropdown-menu">
210 <li id="toggle_all_output"
211 <li id="toggle_all_output"
211 title="Hide/Show the output of all cells">
212 title="Hide/Show the output of all cells">
212 <a href="#">Toggle</a>
213 <a href="#">Toggle</a>
213 </li>
214 </li>
214 <li id="toggle_all_output_scroll"
215 <li id="toggle_all_output_scroll"
215 title="Scroll the output of all cells">
216 title="Scroll the output of all cells">
216 <a href="#">Toggle Scrolling</a>
217 <a href="#">Toggle Scrolling</a>
217 </li>
218 </li>
218 <li id="clear_all_output"
219 <li id="clear_all_output"
219 title="Clear the output of all cells">
220 title="Clear the output of all cells">
220 <a href="#">Clear</a>
221 <a href="#">Clear</a>
221 </li>
222 </li>
222 </ul>
223 </ul>
223 </li>
224 </li>
224 </ul>
225 </ul>
225 </li>
226 </li>
226 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
227 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
227 <ul id="kernel_menu" class="dropdown-menu">
228 <ul id="kernel_menu" class="dropdown-menu">
228 <li id="int_kernel"
229 <li id="int_kernel"
229 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
230 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
230 <a href="#">Interrupt</a>
231 <a href="#">Interrupt</a>
231 </li>
232 </li>
232 <li id="restart_kernel"
233 <li id="restart_kernel"
233 title="Restart the Kernel">
234 title="Restart the Kernel">
234 <a href="#">Restart</a>
235 <a href="#">Restart</a>
235 </li>
236 </li>
236 <li id="reconnect_kernel"
237 <li id="reconnect_kernel"
237 title="Reconnect to the Kernel">
238 title="Reconnect to the Kernel">
238 <a href="#">Reconnect</a>
239 <a href="#">Reconnect</a>
239 </li>
240 </li>
240 <li class="divider"></li>
241 <li class="divider"></li>
241 <li id="menu-change-kernel" class="dropdown-submenu">
242 <li id="menu-change-kernel" class="dropdown-submenu">
242 <a href="#">Change kernel</a>
243 <a href="#">Change kernel</a>
243 <ul class="dropdown-menu" id="menu-change-kernel-submenu"></ul>
244 <ul class="dropdown-menu" id="menu-change-kernel-submenu"></ul>
244 </li>
245 </li>
245 </ul>
246 </ul>
246 </li>
247 </li>
247 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
248 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
248 <ul id="help_menu" class="dropdown-menu">
249 <ul id="help_menu" class="dropdown-menu">
249 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
250 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
250 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
251 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
251 <li class="divider"></li>
252 <li class="divider"></li>
252 {% set
253 {% set
253 sections = (
254 sections = (
254 (
255 (
255 ("http://ipython.org/documentation.html","IPython Help",True),
256 ("http://ipython.org/documentation.html","IPython Help",True),
256 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
257 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
257 ),(
258 ),(
258 ("http://docs.python.org","Python",True),
259 ("http://docs.python.org","Python",True),
259 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
260 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
260 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
261 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
261 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
262 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
262 ("http://matplotlib.org/contents.html","Matplotlib",True),
263 ("http://matplotlib.org/contents.html","Matplotlib",True),
263 ("http://docs.sympy.org/latest/index.html","SymPy",True),
264 ("http://docs.sympy.org/latest/index.html","SymPy",True),
264 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
265 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
265 )
266 )
266 )
267 )
267 %}
268 %}
268
269
269 {% for helplinks in sections %}
270 {% for helplinks in sections %}
270 {% for link in helplinks %}
271 {% for link in helplinks %}
271 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
272 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
272 {{'<i class="fa fa-external-link menu-icon pull-right"></i>' if link[2]}}
273 {{'<i class="fa fa-external-link menu-icon pull-right"></i>' if link[2]}}
273 {{link[1]}}
274 {{link[1]}}
274 </a></li>
275 </a></li>
275 {% endfor %}
276 {% endfor %}
276 {% if not loop.last %}
277 {% if not loop.last %}
277 <li class="divider"></li>
278 <li class="divider"></li>
278 {% endif %}
279 {% endif %}
279 {% endfor %}
280 {% endfor %}
280 <li class="divider"></li>
281 <li class="divider"></li>
281 <li title="About IPython Notebook"><a id="notebook_about" href="#">About</a></li>
282 <li title="About IPython Notebook"><a id="notebook_about" href="#">About</a></li>
282 </ul>
283 </ul>
283 </li>
284 </li>
284 </ul>
285 </ul>
285 </div>
286 </div>
286 </div>
287 </div>
287 </div>
288 </div>
288 </div>
289 </div>
289
290
290 <div id="maintoolbar" class="navbar">
291 <div id="maintoolbar" class="navbar">
291 <div class="toolbar-inner navbar-inner navbar-nobg">
292 <div class="toolbar-inner navbar-inner navbar-nobg">
292 <div id="maintoolbar-container" class="container"></div>
293 <div id="maintoolbar-container" class="container"></div>
293 </div>
294 </div>
294 </div>
295 </div>
295 </div>
296 </div>
296
297
297 <div class="lower-header-bar"></div>
298 <div class="lower-header-bar"></div>
298 {% endblock header %}
299 {% endblock header %}
299
300
300 {% block site %}
301 {% block site %}
301
302
302
303
303 <div id="ipython-main-app">
304 <div id="ipython-main-app">
304 <div id="notebook_panel">
305 <div id="notebook_panel">
305 <div id="notebook"></div>
306 <div id="notebook"></div>
306 </div>
307 </div>
307 </div>
308 </div>
308
309
309 <div id="pager">
310 <div id="pager">
310 <div id="pager-contents">
311 <div id="pager-contents">
311 <div id="pager-container" class="container"></div>
312 <div id="pager-container" class="container"></div>
312 </div>
313 </div>
313 <div id='pager-button-area'></div>
314 <div id='pager-button-area'></div>
314 </div>
315 </div>
315
316
316 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
317 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
317
318
318
319
319 {% endblock %}
320 {% endblock %}
320
321
321
322
322 {% block script %}
323 {% block script %}
323 {{super()}}
324 {{super()}}
324 <script type="text/javascript">
325 <script type="text/javascript">
325 sys_info = {{sys_info}};
326 sys_info = {{sys_info}};
326 </script>
327 </script>
327
328
328 <script src="{{ static_url("components/text-encoding/lib/encoding.js") }}" charset="utf-8"></script>
329 <script src="{{ static_url("components/text-encoding/lib/encoding.js") }}" charset="utf-8"></script>
329
330
330 <script src="{{ static_url("notebook/js/main.js") }}" charset="utf-8"></script>
331 <script src="{{ static_url("notebook/js/main.js") }}" charset="utf-8"></script>
331
332
332 {% endblock %}
333 {% endblock %}
@@ -1,119 +1,120 b''
1 <!DOCTYPE HTML>
1 <!DOCTYPE HTML>
2 <html>
2 <html>
3
3
4 <head>
4 <head>
5 <meta charset="utf-8">
5 <meta charset="utf-8">
6
6
7 <title>{% block title %}IPython Notebook{% endblock %}</title>
7 <title>{% block title %}IPython Notebook{% endblock %}</title>
8 {% block favicon %}<link rel="shortcut icon" type="image/x-icon" href="{{static_url("base/images/favicon.ico") }}">{% endblock %}
8 {% block favicon %}<link rel="shortcut icon" type="image/x-icon" href="{{static_url("base/images/favicon.ico") }}">{% endblock %}
9 <meta http-equiv="X-UA-Compatible" content="chrome=1">
9 <meta http-equiv="X-UA-Compatible" content="chrome=1">
10 <link rel="stylesheet" href="{{static_url("components/jquery-ui/themes/smoothness/jquery-ui.min.css") }}" type="text/css" />
10 <link rel="stylesheet" href="{{static_url("components/jquery-ui/themes/smoothness/jquery-ui.min.css") }}" type="text/css" />
11 <meta name="viewport" content="width=device-width, initial-scale=1.0">
11 <meta name="viewport" content="width=device-width, initial-scale=1.0">
12
12
13 {% block stylesheet %}
13 {% block stylesheet %}
14 <link rel="stylesheet" href="{{ static_url("style/style.min.css") }}" type="text/css"/>
14 <link rel="stylesheet" href="{{ static_url("style/style.min.css") }}" type="text/css"/>
15 {% endblock %}
15 {% endblock %}
16 <link rel="stylesheet" href="{{ static_url("custom/custom.css") }}" type="text/css" />
16 <link rel="stylesheet" href="{{ static_url("custom/custom.css") }}" type="text/css" />
17 <script src="{{static_url("components/es6-promise/promise.min.js")}}" type="text/javascript" charset="utf-8"></script>
17 <script src="{{static_url("components/es6-promise/promise.min.js")}}" type="text/javascript" charset="utf-8"></script>
18 <script src="{{static_url("components/requirejs/require.js") }}" type="text/javascript" charset="utf-8"></script>
18 <script src="{{static_url("components/requirejs/require.js") }}" type="text/javascript" charset="utf-8"></script>
19 <script>
19 <script>
20 require.config({
20 require.config({
21 {% if version_hash %}
21 {% if version_hash %}
22 urlArgs: "v={{version_hash}}",
22 urlArgs: "v={{version_hash}}",
23 {% endif %}
23 {% endif %}
24 baseUrl: '{{static_url("", include_version=False)}}',
24 baseUrl: '{{static_url("", include_version=False)}}',
25 paths: {
25 paths: {
26 nbextensions : '{{ base_url }}nbextensions',
26 nbextensions : '{{ base_url }}nbextensions',
27 kernelspecs : '{{ base_url }}kernelspecs',
27 underscore : 'components/underscore/underscore-min',
28 underscore : 'components/underscore/underscore-min',
28 backbone : 'components/backbone/backbone-min',
29 backbone : 'components/backbone/backbone-min',
29 jquery: 'components/jquery/jquery.min',
30 jquery: 'components/jquery/jquery.min',
30 bootstrap: 'components/bootstrap/js/bootstrap.min',
31 bootstrap: 'components/bootstrap/js/bootstrap.min',
31 bootstraptour: 'components/bootstrap-tour/build/js/bootstrap-tour.min',
32 bootstraptour: 'components/bootstrap-tour/build/js/bootstrap-tour.min',
32 jqueryui: 'components/jquery-ui/ui/minified/jquery-ui.min',
33 jqueryui: 'components/jquery-ui/ui/minified/jquery-ui.min',
33 moment: 'components/moment/moment',
34 moment: 'components/moment/moment',
34 codemirror: 'components/codemirror',
35 codemirror: 'components/codemirror',
35 termjs: 'components/term.js/src/term',
36 termjs: 'components/term.js/src/term',
36 },
37 },
37 shim: {
38 shim: {
38 underscore: {
39 underscore: {
39 exports: '_'
40 exports: '_'
40 },
41 },
41 backbone: {
42 backbone: {
42 deps: ["underscore", "jquery"],
43 deps: ["underscore", "jquery"],
43 exports: "Backbone"
44 exports: "Backbone"
44 },
45 },
45 bootstrap: {
46 bootstrap: {
46 deps: ["jquery"],
47 deps: ["jquery"],
47 exports: "bootstrap"
48 exports: "bootstrap"
48 },
49 },
49 bootstraptour: {
50 bootstraptour: {
50 deps: ["bootstrap"],
51 deps: ["bootstrap"],
51 exports: "Tour"
52 exports: "Tour"
52 },
53 },
53 jqueryui: {
54 jqueryui: {
54 deps: ["jquery"],
55 deps: ["jquery"],
55 exports: "$"
56 exports: "$"
56 }
57 }
57 }
58 }
58 });
59 });
59
60
60 require.config({
61 require.config({
61 map: {
62 map: {
62 '*':{
63 '*':{
63 'contents': '{{ contents_js_source }}',
64 'contents': '{{ contents_js_source }}',
64 }
65 }
65 }
66 }
66 });
67 });
67 </script>
68 </script>
68
69
69 {% block meta %}
70 {% block meta %}
70 {% endblock %}
71 {% endblock %}
71
72
72 </head>
73 </head>
73
74
74 <body {% block params %}{% endblock %}>
75 <body {% block params %}{% endblock %}>
75
76
76 <noscript>
77 <noscript>
77 <div id='noscript'>
78 <div id='noscript'>
78 IPython Notebook requires JavaScript.<br>
79 IPython Notebook requires JavaScript.<br>
79 Please enable it to proceed.
80 Please enable it to proceed.
80 </div>
81 </div>
81 </noscript>
82 </noscript>
82
83
83 <div id="header" class="navbar navbar-fixed-top">
84 <div id="header" class="navbar navbar-fixed-top">
84 <div id="header-container" class="container">
85 <div id="header-container" class="container">
85 <div id="ipython_notebook" class="nav navbar-brand pull-left"><a href="{{base_url}}tree" alt='dashboard'>{% block logo %}<img src='{{static_url("base/images/logo.png") }}' alt='Jupyter Notebook'/>{% endblock %}</a></div>
86 <div id="ipython_notebook" class="nav navbar-brand pull-left"><a href="{{base_url}}tree" alt='dashboard'>{% block logo %}<img src='{{static_url("base/images/logo.png") }}' alt='Jupyter Notebook'/>{% endblock %}</a></div>
86
87
87 {% block login_widget %}
88 {% block login_widget %}
88
89
89 <span id="login_widget">
90 <span id="login_widget">
90 {% if logged_in %}
91 {% if logged_in %}
91 <button id="logout" class="btn btn-sm navbar-btn">Logout</button>
92 <button id="logout" class="btn btn-sm navbar-btn">Logout</button>
92 {% elif login_available and not logged_in %}
93 {% elif login_available and not logged_in %}
93 <button id="login" class="btn btn-sm navbar-btn">Login</button>
94 <button id="login" class="btn btn-sm navbar-btn">Login</button>
94 {% endif %}
95 {% endif %}
95 </span>
96 </span>
96
97
97 {% endblock %}
98 {% endblock %}
98
99
99 {% block headercontainer %}
100 {% block headercontainer %}
100 {% endblock %}
101 {% endblock %}
101 </div>
102 </div>
102 <div class="header-bar"></div>
103 <div class="header-bar"></div>
103
104
104 {% block header %}
105 {% block header %}
105 {% endblock %}
106 {% endblock %}
106 </div>
107 </div>
107 <div id="header-spacer"></div>
108 <div id="header-spacer"></div>
108
109
109 <div id="site">
110 <div id="site">
110 {% block site %}
111 {% block site %}
111 {% endblock %}
112 {% endblock %}
112 </div>
113 </div>
113
114
114 {% block script %}
115 {% block script %}
115 {% endblock %}
116 {% endblock %}
116
117
117 </body>
118 </body>
118
119
119 </html>
120 </html>
General Comments 0
You need to be logged in to leave comments. Login now