##// END OF EJS Templates
expose notebook checkpoints in html/js...
MinRK -
Show More
@@ -1,206 +1,206
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2011 The IPython Development Team
2 // Copyright (C) 2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // ToolBar
9 // ToolBar
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var MainToolBar = function (selector) {
14 var MainToolBar = function (selector) {
15 IPython.ToolBar.apply(this, arguments);
15 IPython.ToolBar.apply(this, arguments);
16 this.construct();
16 this.construct();
17 this.add_celltype_list();
17 this.add_celltype_list();
18 this.add_celltoolbar_list();
18 this.add_celltoolbar_list();
19 this.bind_events();
19 this.bind_events();
20 };
20 };
21
21
22 MainToolBar.prototype = new IPython.ToolBar();
22 MainToolBar.prototype = new IPython.ToolBar();
23
23
24 MainToolBar.prototype.construct = function () {
24 MainToolBar.prototype.construct = function () {
25 this.add_buttons_group([
25 this.add_buttons_group([
26 {
26 {
27 id : 'save_b',
27 id : 'save_b',
28 label : 'Save',
28 label : 'Save Checkpoint',
29 icon : 'ui-icon-disk',
29 icon : 'ui-icon-disk',
30 callback : function () {
30 callback : function () {
31 IPython.notebook.save_notebook();
31 IPython.notebook.save_checkpoint();
32 }
32 }
33 }
33 }
34 ]);
34 ]);
35 this.add_buttons_group([
35 this.add_buttons_group([
36 {
36 {
37 id : 'cut_b',
37 id : 'cut_b',
38 label : 'Cut Cell',
38 label : 'Cut Cell',
39 icon : 'ui-icon-scissors',
39 icon : 'ui-icon-scissors',
40 callback : function () {
40 callback : function () {
41 IPython.notebook.cut_cell();
41 IPython.notebook.cut_cell();
42 }
42 }
43 },
43 },
44 {
44 {
45 id : 'copy_b',
45 id : 'copy_b',
46 label : 'Copy Cell',
46 label : 'Copy Cell',
47 icon : 'ui-icon-copy',
47 icon : 'ui-icon-copy',
48 callback : function () {
48 callback : function () {
49 IPython.notebook.copy_cell();
49 IPython.notebook.copy_cell();
50 }
50 }
51 },
51 },
52 {
52 {
53 id : 'paste_b',
53 id : 'paste_b',
54 label : 'Paste Cell Below',
54 label : 'Paste Cell Below',
55 icon : 'ui-icon-clipboard',
55 icon : 'ui-icon-clipboard',
56 callback : function () {
56 callback : function () {
57 IPython.notebook.paste_cell_below();
57 IPython.notebook.paste_cell_below();
58 }
58 }
59 }
59 }
60 ],'cut_copy_paste');
60 ],'cut_copy_paste');
61
61
62 this.add_buttons_group([
62 this.add_buttons_group([
63 {
63 {
64 id : 'move_up_b',
64 id : 'move_up_b',
65 label : 'Move Cell Up',
65 label : 'Move Cell Up',
66 icon : 'ui-icon-arrowthick-1-n',
66 icon : 'ui-icon-arrowthick-1-n',
67 callback : function () {
67 callback : function () {
68 IPython.notebook.move_cell_up();
68 IPython.notebook.move_cell_up();
69 }
69 }
70 },
70 },
71 {
71 {
72 id : 'move_down_b',
72 id : 'move_down_b',
73 label : 'Move Cell Down',
73 label : 'Move Cell Down',
74 icon : 'ui-icon-arrowthick-1-s',
74 icon : 'ui-icon-arrowthick-1-s',
75 callback : function () {
75 callback : function () {
76 IPython.notebook.move_cell_down();
76 IPython.notebook.move_cell_down();
77 }
77 }
78 }
78 }
79 ],'move_up_down');
79 ],'move_up_down');
80
80
81 this.add_buttons_group([
81 this.add_buttons_group([
82 {
82 {
83 id : 'insert_above_b',
83 id : 'insert_above_b',
84 label : 'Insert Cell Above',
84 label : 'Insert Cell Above',
85 icon : 'ui-icon-arrowthickstop-1-n',
85 icon : 'ui-icon-arrowthickstop-1-n',
86 callback : function () {
86 callback : function () {
87 IPython.notebook.insert_cell_above('code');
87 IPython.notebook.insert_cell_above('code');
88 }
88 }
89 },
89 },
90 {
90 {
91 id : 'insert_below_b',
91 id : 'insert_below_b',
92 label : 'Insert Cell Below',
92 label : 'Insert Cell Below',
93 icon : 'ui-icon-arrowthickstop-1-s',
93 icon : 'ui-icon-arrowthickstop-1-s',
94 callback : function () {
94 callback : function () {
95 IPython.notebook.insert_cell_below('code');
95 IPython.notebook.insert_cell_below('code');
96 }
96 }
97 }
97 }
98 ],'insert_above_below');
98 ],'insert_above_below');
99
99
100 this.add_buttons_group([
100 this.add_buttons_group([
101 {
101 {
102 id : 'run_b',
102 id : 'run_b',
103 label : 'Run Cell',
103 label : 'Run Cell',
104 icon : 'ui-icon-play',
104 icon : 'ui-icon-play',
105 callback : function () {
105 callback : function () {
106 IPython.notebook.execute_selected_cell();
106 IPython.notebook.execute_selected_cell();
107 }
107 }
108 },
108 },
109 {
109 {
110 id : 'interrupt_b',
110 id : 'interrupt_b',
111 label : 'Interrupt',
111 label : 'Interrupt',
112 icon : 'ui-icon-stop',
112 icon : 'ui-icon-stop',
113 callback : function () {
113 callback : function () {
114 IPython.notebook.kernel.interrupt();
114 IPython.notebook.kernel.interrupt();
115 }
115 }
116 }
116 }
117 ],'run_int');
117 ],'run_int');
118 };
118 };
119
119
120 MainToolBar.prototype.add_celltype_list = function () {
120 MainToolBar.prototype.add_celltype_list = function () {
121 this.element
121 this.element
122 .append($('<select/>')
122 .append($('<select/>')
123 .attr('id','cell_type')
123 .attr('id','cell_type')
124 .addClass('ui-widget-content')
124 .addClass('ui-widget-content')
125 .append($('<option/>').attr('value','code').text('Code'))
125 .append($('<option/>').attr('value','code').text('Code'))
126 .append($('<option/>').attr('value','markdown').text('Markdown'))
126 .append($('<option/>').attr('value','markdown').text('Markdown'))
127 .append($('<option/>').attr('value','raw').text('Raw Text'))
127 .append($('<option/>').attr('value','raw').text('Raw Text'))
128 .append($('<option/>').attr('value','heading1').text('Heading 1'))
128 .append($('<option/>').attr('value','heading1').text('Heading 1'))
129 .append($('<option/>').attr('value','heading2').text('Heading 2'))
129 .append($('<option/>').attr('value','heading2').text('Heading 2'))
130 .append($('<option/>').attr('value','heading3').text('Heading 3'))
130 .append($('<option/>').attr('value','heading3').text('Heading 3'))
131 .append($('<option/>').attr('value','heading4').text('Heading 4'))
131 .append($('<option/>').attr('value','heading4').text('Heading 4'))
132 .append($('<option/>').attr('value','heading5').text('Heading 5'))
132 .append($('<option/>').attr('value','heading5').text('Heading 5'))
133 .append($('<option/>').attr('value','heading6').text('Heading 6'))
133 .append($('<option/>').attr('value','heading6').text('Heading 6'))
134 );
134 );
135 };
135 };
136
136
137
137
138 MainToolBar.prototype.add_celltoolbar_list = function () {
138 MainToolBar.prototype.add_celltoolbar_list = function () {
139 var label = $('<label/>').text('Cell Toolbar:');
139 var label = $('<label/>').text('Cell Toolbar:');
140 var select = $('<select/>')
140 var select = $('<select/>')
141 .addClass('ui-widget-content')
141 .addClass('ui-widget-content')
142 .attr('id', 'ctb_select')
142 .attr('id', 'ctb_select')
143 .append($('<option/>').attr('value', '').text('None'));
143 .append($('<option/>').attr('value', '').text('None'));
144 this.element.append(label).append(select);
144 this.element.append(label).append(select);
145 select.change(function() {
145 select.change(function() {
146 var val = $(this).val()
146 var val = $(this).val()
147 if (val =='') {
147 if (val =='') {
148 IPython.CellToolbar.global_hide();
148 IPython.CellToolbar.global_hide();
149 } else {
149 } else {
150 IPython.CellToolbar.global_show();
150 IPython.CellToolbar.global_show();
151 IPython.CellToolbar.activate_preset(val);
151 IPython.CellToolbar.activate_preset(val);
152 }
152 }
153 });
153 });
154 // Setup the currently registered presets.
154 // Setup the currently registered presets.
155 var presets = IPython.CellToolbar.list_presets();
155 var presets = IPython.CellToolbar.list_presets();
156 for (var i=0; i<presets.length; i++) {
156 for (var i=0; i<presets.length; i++) {
157 var name = presets[i];
157 var name = presets[i];
158 select.append($('<option/>').attr('value', name).text(name));
158 select.append($('<option/>').attr('value', name).text(name));
159 }
159 }
160 // Setup future preset registrations.
160 // Setup future preset registrations.
161 $([IPython.events]).on('preset_added.CellToolbar', function (event, data) {
161 $([IPython.events]).on('preset_added.CellToolbar', function (event, data) {
162 var name = data.name;
162 var name = data.name;
163 select.append($('<option/>').attr('value', name).text(name));
163 select.append($('<option/>').attr('value', name).text(name));
164 });
164 });
165 };
165 };
166
166
167
167
168 MainToolBar.prototype.bind_events = function () {
168 MainToolBar.prototype.bind_events = function () {
169 var that = this;
169 var that = this;
170
170
171 this.element.find('#cell_type').change(function () {
171 this.element.find('#cell_type').change(function () {
172 var cell_type = $(this).val();
172 var cell_type = $(this).val();
173 if (cell_type === 'code') {
173 if (cell_type === 'code') {
174 IPython.notebook.to_code();
174 IPython.notebook.to_code();
175 } else if (cell_type === 'markdown') {
175 } else if (cell_type === 'markdown') {
176 IPython.notebook.to_markdown();
176 IPython.notebook.to_markdown();
177 } else if (cell_type === 'raw') {
177 } else if (cell_type === 'raw') {
178 IPython.notebook.to_raw();
178 IPython.notebook.to_raw();
179 } else if (cell_type === 'heading1') {
179 } else if (cell_type === 'heading1') {
180 IPython.notebook.to_heading(undefined, 1);
180 IPython.notebook.to_heading(undefined, 1);
181 } else if (cell_type === 'heading2') {
181 } else if (cell_type === 'heading2') {
182 IPython.notebook.to_heading(undefined, 2);
182 IPython.notebook.to_heading(undefined, 2);
183 } else if (cell_type === 'heading3') {
183 } else if (cell_type === 'heading3') {
184 IPython.notebook.to_heading(undefined, 3);
184 IPython.notebook.to_heading(undefined, 3);
185 } else if (cell_type === 'heading4') {
185 } else if (cell_type === 'heading4') {
186 IPython.notebook.to_heading(undefined, 4);
186 IPython.notebook.to_heading(undefined, 4);
187 } else if (cell_type === 'heading5') {
187 } else if (cell_type === 'heading5') {
188 IPython.notebook.to_heading(undefined, 5);
188 IPython.notebook.to_heading(undefined, 5);
189 } else if (cell_type === 'heading6') {
189 } else if (cell_type === 'heading6') {
190 IPython.notebook.to_heading(undefined, 6);
190 IPython.notebook.to_heading(undefined, 6);
191 }
191 }
192 });
192 });
193 $([IPython.events]).on('selected_cell_type_changed.Notebook', function (event, data) {
193 $([IPython.events]).on('selected_cell_type_changed.Notebook', function (event, data) {
194 if (data.cell_type === 'heading') {
194 if (data.cell_type === 'heading') {
195 that.element.find('#cell_type').val(data.cell_type+data.level);
195 that.element.find('#cell_type').val(data.cell_type+data.level);
196 } else {
196 } else {
197 that.element.find('#cell_type').val(data.cell_type);
197 that.element.find('#cell_type').val(data.cell_type);
198 }
198 }
199 });
199 });
200 };
200 };
201
201
202 IPython.MainToolBar = MainToolBar;
202 IPython.MainToolBar = MainToolBar;
203
203
204 return IPython;
204 return IPython;
205
205
206 }(IPython));
206 }(IPython));
@@ -1,228 +1,231
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // MenuBar
9 // MenuBar
10 //============================================================================
10 //============================================================================
11
11
12 /**
12 /**
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 * @submodule MenuBar
15 * @submodule MenuBar
16 */
16 */
17
17
18
18
19 var IPython = (function (IPython) {
19 var IPython = (function (IPython) {
20
20
21 /**
21 /**
22 * A MenuBar Class to generate the menubar of IPython noteboko
22 * A MenuBar Class to generate the menubar of IPython noteboko
23 * @Class MenuBar
23 * @Class MenuBar
24 *
24 *
25 * @constructor
25 * @constructor
26 *
26 *
27 *
27 *
28 * @param selector {string} selector for the menubar element in DOM
28 * @param selector {string} selector for the menubar element in DOM
29 * @param {object} [options]
29 * @param {object} [options]
30 * @param [options.baseProjectUrl] {String} String to use for the
30 * @param [options.baseProjectUrl] {String} String to use for the
31 * Base Project url, default would be to inspect
31 * Base Project url, default would be to inspect
32 * $('body').data('baseProjectUrl');
32 * $('body').data('baseProjectUrl');
33 * does not support change for now is set through this option
33 * does not support change for now is set through this option
34 */
34 */
35 var MenuBar = function (selector, options) {
35 var MenuBar = function (selector, options) {
36 var options = options || {};
36 var options = options || {};
37 if(options.baseProjectUrl!= undefined){
37 if(options.baseProjectUrl!= undefined){
38 this._baseProjectUrl = options.baseProjectUrl;
38 this._baseProjectUrl = options.baseProjectUrl;
39 }
39 }
40 this.selector = selector;
40 this.selector = selector;
41 if (this.selector !== undefined) {
41 if (this.selector !== undefined) {
42 this.element = $(selector);
42 this.element = $(selector);
43 this.style();
43 this.style();
44 this.bind_events();
44 this.bind_events();
45 }
45 }
46 };
46 };
47
47
48 MenuBar.prototype.baseProjectUrl = function(){
48 MenuBar.prototype.baseProjectUrl = function(){
49 return this._baseProjectUrl || $('body').data('baseProjectUrl');
49 return this._baseProjectUrl || $('body').data('baseProjectUrl');
50 };
50 };
51
51
52
52
53 MenuBar.prototype.style = function () {
53 MenuBar.prototype.style = function () {
54 this.element.addClass('border-box-sizing');
54 this.element.addClass('border-box-sizing');
55 $('ul#menus').menubar({
55 $('ul#menus').menubar({
56 select : function (event, ui) {
56 select : function (event, ui) {
57 // The selected cell loses focus when the menu is entered, so we
57 // The selected cell loses focus when the menu is entered, so we
58 // re-select it upon selection.
58 // re-select it upon selection.
59 var i = IPython.notebook.get_selected_index();
59 var i = IPython.notebook.get_selected_index();
60 IPython.notebook.select(i);
60 IPython.notebook.select(i);
61 }
61 }
62 });
62 });
63 };
63 };
64
64
65
65
66 MenuBar.prototype.bind_events = function () {
66 MenuBar.prototype.bind_events = function () {
67 // File
67 // File
68 var that = this;
68 var that = this;
69 this.element.find('#new_notebook').click(function () {
69 this.element.find('#new_notebook').click(function () {
70 window.open(that.baseProjectUrl()+'new');
70 window.open(that.baseProjectUrl()+'new');
71 });
71 });
72 this.element.find('#open_notebook').click(function () {
72 this.element.find('#open_notebook').click(function () {
73 window.open(that.baseProjectUrl());
73 window.open(that.baseProjectUrl());
74 });
74 });
75 this.element.find('#rename_notebook').click(function () {
75 this.element.find('#rename_notebook').click(function () {
76 IPython.save_widget.rename_notebook();
76 IPython.save_widget.rename_notebook();
77 });
77 });
78 this.element.find('#copy_notebook').click(function () {
78 this.element.find('#copy_notebook').click(function () {
79 var notebook_id = IPython.notebook.get_notebook_id();
79 var notebook_id = IPython.notebook.get_notebook_id();
80 var url = that.baseProjectUrl() + notebook_id + '/copy';
80 var url = that.baseProjectUrl() + notebook_id + '/copy';
81 window.open(url,'_blank');
81 window.open(url,'_blank');
82 return false;
82 return false;
83 });
83 });
84 this.element.find('#save_notebook').click(function () {
84 this.element.find('#save_notebook').click(function () {
85 IPython.notebook.save_notebook();
85 IPython.notebook.save_notebook();
86 });
86 });
87 this.element.find('#save_checkpoint').click(function () {
88 IPython.notebook.save_checkpoint();
89 });
87 this.element.find('#download_ipynb').click(function () {
90 this.element.find('#download_ipynb').click(function () {
88 var notebook_id = IPython.notebook.get_notebook_id();
91 var notebook_id = IPython.notebook.get_notebook_id();
89 var url = that.baseProjectUrl() + 'notebooks/' +
92 var url = that.baseProjectUrl() + 'notebooks/' +
90 notebook_id + '?format=json';
93 notebook_id + '?format=json';
91 window.location.assign(url);
94 window.location.assign(url);
92 });
95 });
93 this.element.find('#download_py').click(function () {
96 this.element.find('#download_py').click(function () {
94 var notebook_id = IPython.notebook.get_notebook_id();
97 var notebook_id = IPython.notebook.get_notebook_id();
95 var url = that.baseProjectUrl() + 'notebooks/' +
98 var url = that.baseProjectUrl() + 'notebooks/' +
96 notebook_id + '?format=py';
99 notebook_id + '?format=py';
97 window.location.assign(url);
100 window.location.assign(url);
98 });
101 });
99 this.element.find('#kill_and_exit').click(function () {
102 this.element.find('#kill_and_exit').click(function () {
100 IPython.notebook.kernel.kill();
103 IPython.notebook.kernel.kill();
101 setTimeout(function(){window.close();}, 200);
104 setTimeout(function(){window.close();}, 200);
102 });
105 });
103 // Edit
106 // Edit
104 this.element.find('#cut_cell').click(function () {
107 this.element.find('#cut_cell').click(function () {
105 IPython.notebook.cut_cell();
108 IPython.notebook.cut_cell();
106 });
109 });
107 this.element.find('#copy_cell').click(function () {
110 this.element.find('#copy_cell').click(function () {
108 IPython.notebook.copy_cell();
111 IPython.notebook.copy_cell();
109 });
112 });
110 this.element.find('#delete_cell').click(function () {
113 this.element.find('#delete_cell').click(function () {
111 IPython.notebook.delete_cell();
114 IPython.notebook.delete_cell();
112 });
115 });
113 this.element.find('#undelete_cell').click(function () {
116 this.element.find('#undelete_cell').click(function () {
114 IPython.notebook.undelete();
117 IPython.notebook.undelete();
115 });
118 });
116 this.element.find('#split_cell').click(function () {
119 this.element.find('#split_cell').click(function () {
117 IPython.notebook.split_cell();
120 IPython.notebook.split_cell();
118 });
121 });
119 this.element.find('#merge_cell_above').click(function () {
122 this.element.find('#merge_cell_above').click(function () {
120 IPython.notebook.merge_cell_above();
123 IPython.notebook.merge_cell_above();
121 });
124 });
122 this.element.find('#merge_cell_below').click(function () {
125 this.element.find('#merge_cell_below').click(function () {
123 IPython.notebook.merge_cell_below();
126 IPython.notebook.merge_cell_below();
124 });
127 });
125 this.element.find('#move_cell_up').click(function () {
128 this.element.find('#move_cell_up').click(function () {
126 IPython.notebook.move_cell_up();
129 IPython.notebook.move_cell_up();
127 });
130 });
128 this.element.find('#move_cell_down').click(function () {
131 this.element.find('#move_cell_down').click(function () {
129 IPython.notebook.move_cell_down();
132 IPython.notebook.move_cell_down();
130 });
133 });
131 this.element.find('#select_previous').click(function () {
134 this.element.find('#select_previous').click(function () {
132 IPython.notebook.select_prev();
135 IPython.notebook.select_prev();
133 });
136 });
134 this.element.find('#select_next').click(function () {
137 this.element.find('#select_next').click(function () {
135 IPython.notebook.select_next();
138 IPython.notebook.select_next();
136 });
139 });
137 // View
140 // View
138 this.element.find('#toggle_header').click(function () {
141 this.element.find('#toggle_header').click(function () {
139 $('div#header').toggle();
142 $('div#header').toggle();
140 IPython.layout_manager.do_resize();
143 IPython.layout_manager.do_resize();
141 });
144 });
142 this.element.find('#toggle_toolbar').click(function () {
145 this.element.find('#toggle_toolbar').click(function () {
143 IPython.toolbar.toggle();
146 IPython.toolbar.toggle();
144 });
147 });
145 // Insert
148 // Insert
146 this.element.find('#insert_cell_above').click(function () {
149 this.element.find('#insert_cell_above').click(function () {
147 IPython.notebook.insert_cell_above('code');
150 IPython.notebook.insert_cell_above('code');
148 });
151 });
149 this.element.find('#insert_cell_below').click(function () {
152 this.element.find('#insert_cell_below').click(function () {
150 IPython.notebook.insert_cell_below('code');
153 IPython.notebook.insert_cell_below('code');
151 });
154 });
152 // Cell
155 // Cell
153 this.element.find('#run_cell').click(function () {
156 this.element.find('#run_cell').click(function () {
154 IPython.notebook.execute_selected_cell();
157 IPython.notebook.execute_selected_cell();
155 });
158 });
156 this.element.find('#run_cell_in_place').click(function () {
159 this.element.find('#run_cell_in_place').click(function () {
157 IPython.notebook.execute_selected_cell({terminal:true});
160 IPython.notebook.execute_selected_cell({terminal:true});
158 });
161 });
159 this.element.find('#run_all_cells').click(function () {
162 this.element.find('#run_all_cells').click(function () {
160 IPython.notebook.execute_all_cells();
163 IPython.notebook.execute_all_cells();
161 }).attr('title', 'Run all cells in the notebook');
164 }).attr('title', 'Run all cells in the notebook');
162 this.element.find('#run_all_cells_above').click(function () {
165 this.element.find('#run_all_cells_above').click(function () {
163 IPython.notebook.execute_cells_above();
166 IPython.notebook.execute_cells_above();
164 }).attr('title', 'Run all cells above (but not including) this cell');
167 }).attr('title', 'Run all cells above (but not including) this cell');
165 this.element.find('#run_all_cells_below').click(function () {
168 this.element.find('#run_all_cells_below').click(function () {
166 IPython.notebook.execute_cells_below();
169 IPython.notebook.execute_cells_below();
167 }).attr('title', 'Run this cell and all cells below it');
170 }).attr('title', 'Run this cell and all cells below it');
168 this.element.find('#to_code').click(function () {
171 this.element.find('#to_code').click(function () {
169 IPython.notebook.to_code();
172 IPython.notebook.to_code();
170 });
173 });
171 this.element.find('#to_markdown').click(function () {
174 this.element.find('#to_markdown').click(function () {
172 IPython.notebook.to_markdown();
175 IPython.notebook.to_markdown();
173 });
176 });
174 this.element.find('#to_raw').click(function () {
177 this.element.find('#to_raw').click(function () {
175 IPython.notebook.to_raw();
178 IPython.notebook.to_raw();
176 });
179 });
177 this.element.find('#to_heading1').click(function () {
180 this.element.find('#to_heading1').click(function () {
178 IPython.notebook.to_heading(undefined, 1);
181 IPython.notebook.to_heading(undefined, 1);
179 });
182 });
180 this.element.find('#to_heading2').click(function () {
183 this.element.find('#to_heading2').click(function () {
181 IPython.notebook.to_heading(undefined, 2);
184 IPython.notebook.to_heading(undefined, 2);
182 });
185 });
183 this.element.find('#to_heading3').click(function () {
186 this.element.find('#to_heading3').click(function () {
184 IPython.notebook.to_heading(undefined, 3);
187 IPython.notebook.to_heading(undefined, 3);
185 });
188 });
186 this.element.find('#to_heading4').click(function () {
189 this.element.find('#to_heading4').click(function () {
187 IPython.notebook.to_heading(undefined, 4);
190 IPython.notebook.to_heading(undefined, 4);
188 });
191 });
189 this.element.find('#to_heading5').click(function () {
192 this.element.find('#to_heading5').click(function () {
190 IPython.notebook.to_heading(undefined, 5);
193 IPython.notebook.to_heading(undefined, 5);
191 });
194 });
192 this.element.find('#to_heading6').click(function () {
195 this.element.find('#to_heading6').click(function () {
193 IPython.notebook.to_heading(undefined, 6);
196 IPython.notebook.to_heading(undefined, 6);
194 });
197 });
195 this.element.find('#toggle_output').click(function () {
198 this.element.find('#toggle_output').click(function () {
196 IPython.notebook.toggle_output();
199 IPython.notebook.toggle_output();
197 });
200 });
198 this.element.find('#collapse_all_output').click(function () {
201 this.element.find('#collapse_all_output').click(function () {
199 IPython.notebook.collapse_all_output();
202 IPython.notebook.collapse_all_output();
200 });
203 });
201 this.element.find('#scroll_all_output').click(function () {
204 this.element.find('#scroll_all_output').click(function () {
202 IPython.notebook.scroll_all_output();
205 IPython.notebook.scroll_all_output();
203 });
206 });
204 this.element.find('#expand_all_output').click(function () {
207 this.element.find('#expand_all_output').click(function () {
205 IPython.notebook.expand_all_output();
208 IPython.notebook.expand_all_output();
206 });
209 });
207 this.element.find('#clear_all_output').click(function () {
210 this.element.find('#clear_all_output').click(function () {
208 IPython.notebook.clear_all_output();
211 IPython.notebook.clear_all_output();
209 });
212 });
210 // Kernel
213 // Kernel
211 this.element.find('#int_kernel').click(function () {
214 this.element.find('#int_kernel').click(function () {
212 IPython.notebook.kernel.interrupt();
215 IPython.notebook.kernel.interrupt();
213 });
216 });
214 this.element.find('#restart_kernel').click(function () {
217 this.element.find('#restart_kernel').click(function () {
215 IPython.notebook.restart_kernel();
218 IPython.notebook.restart_kernel();
216 });
219 });
217 // Help
220 // Help
218 this.element.find('#keyboard_shortcuts').click(function () {
221 this.element.find('#keyboard_shortcuts').click(function () {
219 IPython.quick_help.show_keyboard_shortcuts();
222 IPython.quick_help.show_keyboard_shortcuts();
220 });
223 });
221 };
224 };
222
225
223
226
224 IPython.MenuBar = MenuBar;
227 IPython.MenuBar = MenuBar;
225
228
226 return IPython;
229 return IPython;
227
230
228 }(IPython));
231 }(IPython));
@@ -1,1740 +1,1966
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Notebook
9 // Notebook
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15 var key = IPython.utils.keycodes;
15 var key = IPython.utils.keycodes;
16
16
17 /**
17 /**
18 * A notebook contains and manages cells.
18 * A notebook contains and manages cells.
19 *
19 *
20 * @class Notebook
20 * @class Notebook
21 * @constructor
21 * @constructor
22 * @param {String} selector A jQuery selector for the notebook's DOM element
22 * @param {String} selector A jQuery selector for the notebook's DOM element
23 * @param {Object} [options] A config object
23 * @param {Object} [options] A config object
24 */
24 */
25 var Notebook = function (selector, options) {
25 var Notebook = function (selector, options) {
26 var options = options || {};
26 var options = options || {};
27 this._baseProjectUrl = options.baseProjectUrl;
27 this._baseProjectUrl = options.baseProjectUrl;
28 this.read_only = options.read_only || IPython.read_only;
28 this.read_only = options.read_only || IPython.read_only;
29
29
30 this.element = $(selector);
30 this.element = $(selector);
31 this.element.scroll();
31 this.element.scroll();
32 this.element.data("notebook", this);
32 this.element.data("notebook", this);
33 this.next_prompt_number = 1;
33 this.next_prompt_number = 1;
34 this.kernel = null;
34 this.kernel = null;
35 this.clipboard = null;
35 this.clipboard = null;
36 this.undelete_backup = null;
36 this.undelete_backup = null;
37 this.undelete_index = null;
37 this.undelete_index = null;
38 this.undelete_below = false;
38 this.undelete_below = false;
39 this.paste_enabled = false;
39 this.paste_enabled = false;
40 this.dirty = false;
40 this.dirty = false;
41 this.metadata = {};
41 this.metadata = {};
42 this._checkpoint_after_save = false;
43 this.last_checkpoint = null;
42 // single worksheet for now
44 // single worksheet for now
43 this.worksheet_metadata = {};
45 this.worksheet_metadata = {};
44 this.control_key_active = false;
46 this.control_key_active = false;
45 this.notebook_id = null;
47 this.notebook_id = null;
46 this.notebook_name = null;
48 this.notebook_name = null;
47 this.notebook_name_blacklist_re = /[\/\\:]/;
49 this.notebook_name_blacklist_re = /[\/\\:]/;
48 this.nbformat = 3 // Increment this when changing the nbformat
50 this.nbformat = 3 // Increment this when changing the nbformat
49 this.nbformat_minor = 0 // Increment this when changing the nbformat
51 this.nbformat_minor = 0 // Increment this when changing the nbformat
50 this.style();
52 this.style();
51 this.create_elements();
53 this.create_elements();
52 this.bind_events();
54 this.bind_events();
53 };
55 };
54
56
55 /**
57 /**
56 * Tweak the notebook's CSS style.
58 * Tweak the notebook's CSS style.
57 *
59 *
58 * @method style
60 * @method style
59 */
61 */
60 Notebook.prototype.style = function () {
62 Notebook.prototype.style = function () {
61 $('div#notebook').addClass('border-box-sizing');
63 $('div#notebook').addClass('border-box-sizing');
62 };
64 };
63
65
64 /**
66 /**
65 * Get the root URL of the notebook server.
67 * Get the root URL of the notebook server.
66 *
68 *
67 * @method baseProjectUrl
69 * @method baseProjectUrl
68 * @return {String} The base project URL
70 * @return {String} The base project URL
69 */
71 */
70 Notebook.prototype.baseProjectUrl = function(){
72 Notebook.prototype.baseProjectUrl = function(){
71 return this._baseProjectUrl || $('body').data('baseProjectUrl');
73 return this._baseProjectUrl || $('body').data('baseProjectUrl');
72 };
74 };
73
75
74 /**
76 /**
75 * Create an HTML and CSS representation of the notebook.
77 * Create an HTML and CSS representation of the notebook.
76 *
78 *
77 * @method create_elements
79 * @method create_elements
78 */
80 */
79 Notebook.prototype.create_elements = function () {
81 Notebook.prototype.create_elements = function () {
80 // We add this end_space div to the end of the notebook div to:
82 // We add this end_space div to the end of the notebook div to:
81 // i) provide a margin between the last cell and the end of the notebook
83 // i) provide a margin between the last cell and the end of the notebook
82 // ii) to prevent the div from scrolling up when the last cell is being
84 // ii) to prevent the div from scrolling up when the last cell is being
83 // edited, but is too low on the page, which browsers will do automatically.
85 // edited, but is too low on the page, which browsers will do automatically.
84 var that = this;
86 var that = this;
85 var end_space = $('<div/>').addClass('end_space').height("30%");
87 var end_space = $('<div/>').addClass('end_space').height("30%");
86 end_space.dblclick(function (e) {
88 end_space.dblclick(function (e) {
87 if (that.read_only) return;
89 if (that.read_only) return;
88 var ncells = that.ncells();
90 var ncells = that.ncells();
89 that.insert_cell_below('code',ncells-1);
91 that.insert_cell_below('code',ncells-1);
90 });
92 });
91 this.element.append(end_space);
93 this.element.append(end_space);
92 $('div#notebook').addClass('border-box-sizing');
94 $('div#notebook').addClass('border-box-sizing');
93 };
95 };
94
96
95 /**
97 /**
96 * Bind JavaScript events: key presses and custom IPython events.
98 * Bind JavaScript events: key presses and custom IPython events.
97 *
99 *
98 * @method bind_events
100 * @method bind_events
99 */
101 */
100 Notebook.prototype.bind_events = function () {
102 Notebook.prototype.bind_events = function () {
101 var that = this;
103 var that = this;
102
104
103 $([IPython.events]).on('set_next_input.Notebook', function (event, data) {
105 $([IPython.events]).on('set_next_input.Notebook', function (event, data) {
104 var index = that.find_cell_index(data.cell);
106 var index = that.find_cell_index(data.cell);
105 var new_cell = that.insert_cell_below('code',index);
107 var new_cell = that.insert_cell_below('code',index);
106 new_cell.set_text(data.text);
108 new_cell.set_text(data.text);
107 that.dirty = true;
109 that.dirty = true;
108 });
110 });
109
111
110 $([IPython.events]).on('set_dirty.Notebook', function (event, data) {
112 $([IPython.events]).on('set_dirty.Notebook', function (event, data) {
111 that.dirty = data.value;
113 that.dirty = data.value;
112 });
114 });
113
115
114 $([IPython.events]).on('select.Cell', function (event, data) {
116 $([IPython.events]).on('select.Cell', function (event, data) {
115 var index = that.find_cell_index(data.cell);
117 var index = that.find_cell_index(data.cell);
116 that.select(index);
118 that.select(index);
117 });
119 });
118
120
119
121
120 $(document).keydown(function (event) {
122 $(document).keydown(function (event) {
121 // console.log(event);
123 // console.log(event);
122 if (that.read_only) return true;
124 if (that.read_only) return true;
123
125
124 // Save (CTRL+S) or (AppleKey+S)
126 // Save (CTRL+S) or (AppleKey+S)
125 //metaKey = applekey on mac
127 //metaKey = applekey on mac
126 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
128 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
127 that.save_notebook();
129 that.save_notebook();
128 event.preventDefault();
130 event.preventDefault();
129 return false;
131 return false;
130 } else if (event.which === key.ESC) {
132 } else if (event.which === key.ESC) {
131 // Intercept escape at highest level to avoid closing
133 // Intercept escape at highest level to avoid closing
132 // websocket connection with firefox
134 // websocket connection with firefox
133 event.preventDefault();
135 event.preventDefault();
134 } else if (event.which === key.SHIFT) {
136 } else if (event.which === key.SHIFT) {
135 // ignore shift keydown
137 // ignore shift keydown
136 return true;
138 return true;
137 }
139 }
138 if (event.which === key.UPARROW && !event.shiftKey) {
140 if (event.which === key.UPARROW && !event.shiftKey) {
139 var cell = that.get_selected_cell();
141 var cell = that.get_selected_cell();
140 if (cell && cell.at_top()) {
142 if (cell && cell.at_top()) {
141 event.preventDefault();
143 event.preventDefault();
142 that.select_prev();
144 that.select_prev();
143 };
145 };
144 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
146 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
145 var cell = that.get_selected_cell();
147 var cell = that.get_selected_cell();
146 if (cell && cell.at_bottom()) {
148 if (cell && cell.at_bottom()) {
147 event.preventDefault();
149 event.preventDefault();
148 that.select_next();
150 that.select_next();
149 };
151 };
150 } else if (event.which === key.ENTER && event.shiftKey) {
152 } else if (event.which === key.ENTER && event.shiftKey) {
151 that.execute_selected_cell();
153 that.execute_selected_cell();
152 return false;
154 return false;
153 } else if (event.which === key.ENTER && event.altKey) {
155 } else if (event.which === key.ENTER && event.altKey) {
154 // Execute code cell, and insert new in place
156 // Execute code cell, and insert new in place
155 that.execute_selected_cell();
157 that.execute_selected_cell();
156 // Only insert a new cell, if we ended up in an already populated cell
158 // Only insert a new cell, if we ended up in an already populated cell
157 if (/\S/.test(that.get_selected_cell().get_text()) == true) {
159 if (/\S/.test(that.get_selected_cell().get_text()) == true) {
158 that.insert_cell_above('code');
160 that.insert_cell_above('code');
159 }
161 }
160 return false;
162 return false;
161 } else if (event.which === key.ENTER && event.ctrlKey) {
163 } else if (event.which === key.ENTER && event.ctrlKey) {
162 that.execute_selected_cell({terminal:true});
164 that.execute_selected_cell({terminal:true});
163 return false;
165 return false;
164 } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
166 } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
165 that.control_key_active = true;
167 that.control_key_active = true;
166 return false;
168 return false;
167 } else if (event.which === 88 && that.control_key_active) {
169 } else if (event.which === 88 && that.control_key_active) {
168 // Cut selected cell = x
170 // Cut selected cell = x
169 that.cut_cell();
171 that.cut_cell();
170 that.control_key_active = false;
172 that.control_key_active = false;
171 return false;
173 return false;
172 } else if (event.which === 67 && that.control_key_active) {
174 } else if (event.which === 67 && that.control_key_active) {
173 // Copy selected cell = c
175 // Copy selected cell = c
174 that.copy_cell();
176 that.copy_cell();
175 that.control_key_active = false;
177 that.control_key_active = false;
176 return false;
178 return false;
177 } else if (event.which === 86 && that.control_key_active) {
179 } else if (event.which === 86 && that.control_key_active) {
178 // Paste below selected cell = v
180 // Paste below selected cell = v
179 that.paste_cell_below();
181 that.paste_cell_below();
180 that.control_key_active = false;
182 that.control_key_active = false;
181 return false;
183 return false;
182 } else if (event.which === 68 && that.control_key_active) {
184 } else if (event.which === 68 && that.control_key_active) {
183 // Delete selected cell = d
185 // Delete selected cell = d
184 that.delete_cell();
186 that.delete_cell();
185 that.control_key_active = false;
187 that.control_key_active = false;
186 return false;
188 return false;
187 } else if (event.which === 65 && that.control_key_active) {
189 } else if (event.which === 65 && that.control_key_active) {
188 // Insert code cell above selected = a
190 // Insert code cell above selected = a
189 that.insert_cell_above('code');
191 that.insert_cell_above('code');
190 that.control_key_active = false;
192 that.control_key_active = false;
191 return false;
193 return false;
192 } else if (event.which === 66 && that.control_key_active) {
194 } else if (event.which === 66 && that.control_key_active) {
193 // Insert code cell below selected = b
195 // Insert code cell below selected = b
194 that.insert_cell_below('code');
196 that.insert_cell_below('code');
195 that.control_key_active = false;
197 that.control_key_active = false;
196 return false;
198 return false;
197 } else if (event.which === 89 && that.control_key_active) {
199 } else if (event.which === 89 && that.control_key_active) {
198 // To code = y
200 // To code = y
199 that.to_code();
201 that.to_code();
200 that.control_key_active = false;
202 that.control_key_active = false;
201 return false;
203 return false;
202 } else if (event.which === 77 && that.control_key_active) {
204 } else if (event.which === 77 && that.control_key_active) {
203 // To markdown = m
205 // To markdown = m
204 that.to_markdown();
206 that.to_markdown();
205 that.control_key_active = false;
207 that.control_key_active = false;
206 return false;
208 return false;
207 } else if (event.which === 84 && that.control_key_active) {
209 } else if (event.which === 84 && that.control_key_active) {
208 // To Raw = t
210 // To Raw = t
209 that.to_raw();
211 that.to_raw();
210 that.control_key_active = false;
212 that.control_key_active = false;
211 return false;
213 return false;
212 } else if (event.which === 49 && that.control_key_active) {
214 } else if (event.which === 49 && that.control_key_active) {
213 // To Heading 1 = 1
215 // To Heading 1 = 1
214 that.to_heading(undefined, 1);
216 that.to_heading(undefined, 1);
215 that.control_key_active = false;
217 that.control_key_active = false;
216 return false;
218 return false;
217 } else if (event.which === 50 && that.control_key_active) {
219 } else if (event.which === 50 && that.control_key_active) {
218 // To Heading 2 = 2
220 // To Heading 2 = 2
219 that.to_heading(undefined, 2);
221 that.to_heading(undefined, 2);
220 that.control_key_active = false;
222 that.control_key_active = false;
221 return false;
223 return false;
222 } else if (event.which === 51 && that.control_key_active) {
224 } else if (event.which === 51 && that.control_key_active) {
223 // To Heading 3 = 3
225 // To Heading 3 = 3
224 that.to_heading(undefined, 3);
226 that.to_heading(undefined, 3);
225 that.control_key_active = false;
227 that.control_key_active = false;
226 return false;
228 return false;
227 } else if (event.which === 52 && that.control_key_active) {
229 } else if (event.which === 52 && that.control_key_active) {
228 // To Heading 4 = 4
230 // To Heading 4 = 4
229 that.to_heading(undefined, 4);
231 that.to_heading(undefined, 4);
230 that.control_key_active = false;
232 that.control_key_active = false;
231 return false;
233 return false;
232 } else if (event.which === 53 && that.control_key_active) {
234 } else if (event.which === 53 && that.control_key_active) {
233 // To Heading 5 = 5
235 // To Heading 5 = 5
234 that.to_heading(undefined, 5);
236 that.to_heading(undefined, 5);
235 that.control_key_active = false;
237 that.control_key_active = false;
236 return false;
238 return false;
237 } else if (event.which === 54 && that.control_key_active) {
239 } else if (event.which === 54 && that.control_key_active) {
238 // To Heading 6 = 6
240 // To Heading 6 = 6
239 that.to_heading(undefined, 6);
241 that.to_heading(undefined, 6);
240 that.control_key_active = false;
242 that.control_key_active = false;
241 return false;
243 return false;
242 } else if (event.which === 79 && that.control_key_active) {
244 } else if (event.which === 79 && that.control_key_active) {
243 // Toggle output = o
245 // Toggle output = o
244 if (event.shiftKey){
246 if (event.shiftKey){
245 that.toggle_output_scroll();
247 that.toggle_output_scroll();
246 } else {
248 } else {
247 that.toggle_output();
249 that.toggle_output();
248 }
250 }
249 that.control_key_active = false;
251 that.control_key_active = false;
250 return false;
252 return false;
251 } else if (event.which === 83 && that.control_key_active) {
253 } else if (event.which === 83 && that.control_key_active) {
252 // Save notebook = s
254 // Save notebook = s
253 that.save_notebook();
255 that.save_checkpoint();
254 that.control_key_active = false;
256 that.control_key_active = false;
255 return false;
257 return false;
256 } else if (event.which === 74 && that.control_key_active) {
258 } else if (event.which === 74 && that.control_key_active) {
257 // Move cell down = j
259 // Move cell down = j
258 that.move_cell_down();
260 that.move_cell_down();
259 that.control_key_active = false;
261 that.control_key_active = false;
260 return false;
262 return false;
261 } else if (event.which === 75 && that.control_key_active) {
263 } else if (event.which === 75 && that.control_key_active) {
262 // Move cell up = k
264 // Move cell up = k
263 that.move_cell_up();
265 that.move_cell_up();
264 that.control_key_active = false;
266 that.control_key_active = false;
265 return false;
267 return false;
266 } else if (event.which === 80 && that.control_key_active) {
268 } else if (event.which === 80 && that.control_key_active) {
267 // Select previous = p
269 // Select previous = p
268 that.select_prev();
270 that.select_prev();
269 that.control_key_active = false;
271 that.control_key_active = false;
270 return false;
272 return false;
271 } else if (event.which === 78 && that.control_key_active) {
273 } else if (event.which === 78 && that.control_key_active) {
272 // Select next = n
274 // Select next = n
273 that.select_next();
275 that.select_next();
274 that.control_key_active = false;
276 that.control_key_active = false;
275 return false;
277 return false;
276 } else if (event.which === 76 && that.control_key_active) {
278 } else if (event.which === 76 && that.control_key_active) {
277 // Toggle line numbers = l
279 // Toggle line numbers = l
278 that.cell_toggle_line_numbers();
280 that.cell_toggle_line_numbers();
279 that.control_key_active = false;
281 that.control_key_active = false;
280 return false;
282 return false;
281 } else if (event.which === 73 && that.control_key_active) {
283 } else if (event.which === 73 && that.control_key_active) {
282 // Interrupt kernel = i
284 // Interrupt kernel = i
283 that.kernel.interrupt();
285 that.kernel.interrupt();
284 that.control_key_active = false;
286 that.control_key_active = false;
285 return false;
287 return false;
286 } else if (event.which === 190 && that.control_key_active) {
288 } else if (event.which === 190 && that.control_key_active) {
287 // Restart kernel = . # matches qt console
289 // Restart kernel = . # matches qt console
288 that.restart_kernel();
290 that.restart_kernel();
289 that.control_key_active = false;
291 that.control_key_active = false;
290 return false;
292 return false;
291 } else if (event.which === 72 && that.control_key_active) {
293 } else if (event.which === 72 && that.control_key_active) {
292 // Show keyboard shortcuts = h
294 // Show keyboard shortcuts = h
293 IPython.quick_help.show_keyboard_shortcuts();
295 IPython.quick_help.show_keyboard_shortcuts();
294 that.control_key_active = false;
296 that.control_key_active = false;
295 return false;
297 return false;
296 } else if (event.which === 90 && that.control_key_active) {
298 } else if (event.which === 90 && that.control_key_active) {
297 // Undo last cell delete = z
299 // Undo last cell delete = z
298 that.undelete();
300 that.undelete();
299 that.control_key_active = false;
301 that.control_key_active = false;
300 return false;
302 return false;
301 } else if (that.control_key_active) {
303 } else if (that.control_key_active) {
302 that.control_key_active = false;
304 that.control_key_active = false;
303 return true;
305 return true;
304 };
306 };
305 return true;
307 return true;
306 });
308 });
307
309
308 var collapse_time = function(time){
310 var collapse_time = function(time){
309 var app_height = $('#ipython-main-app').height(); // content height
311 var app_height = $('#ipython-main-app').height(); // content height
310 var splitter_height = $('div#pager_splitter').outerHeight(true);
312 var splitter_height = $('div#pager_splitter').outerHeight(true);
311 var new_height = app_height - splitter_height;
313 var new_height = app_height - splitter_height;
312 that.element.animate({height : new_height + 'px'}, time);
314 that.element.animate({height : new_height + 'px'}, time);
313 }
315 }
314
316
315 this.element.bind('collapse_pager', function (event,extrap) {
317 this.element.bind('collapse_pager', function (event,extrap) {
316 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
318 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
317 collapse_time(time);
319 collapse_time(time);
318 });
320 });
319
321
320 var expand_time = function(time) {
322 var expand_time = function(time) {
321 var app_height = $('#ipython-main-app').height(); // content height
323 var app_height = $('#ipython-main-app').height(); // content height
322 var splitter_height = $('div#pager_splitter').outerHeight(true);
324 var splitter_height = $('div#pager_splitter').outerHeight(true);
323 var pager_height = $('div#pager').outerHeight(true);
325 var pager_height = $('div#pager').outerHeight(true);
324 var new_height = app_height - pager_height - splitter_height;
326 var new_height = app_height - pager_height - splitter_height;
325 that.element.animate({height : new_height + 'px'}, time);
327 that.element.animate({height : new_height + 'px'}, time);
326 }
328 }
327
329
328 this.element.bind('expand_pager', function (event, extrap) {
330 this.element.bind('expand_pager', function (event, extrap) {
329 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
331 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
330 expand_time(time);
332 expand_time(time);
331 });
333 });
332
334
333 $(window).bind('beforeunload', function () {
335 $(window).bind('beforeunload', function () {
334 // TODO: Make killing the kernel configurable.
336 // TODO: Make killing the kernel configurable.
335 var kill_kernel = false;
337 var kill_kernel = false;
336 if (kill_kernel) {
338 if (kill_kernel) {
337 that.kernel.kill();
339 that.kernel.kill();
338 }
340 }
339 if (that.dirty && ! that.read_only) {
341 if (that.dirty && ! that.read_only) {
340 return "You have unsaved changes that will be lost if you leave this page.";
342 return "You have unsaved changes that will be lost if you leave this page.";
341 };
343 };
342 // Null is the *only* return value that will make the browser not
344 // Null is the *only* return value that will make the browser not
343 // pop up the "don't leave" dialog.
345 // pop up the "don't leave" dialog.
344 return null;
346 return null;
345 });
347 });
346 };
348 };
347
349
348 /**
350 /**
349 * Scroll the top of the page to a given cell.
351 * Scroll the top of the page to a given cell.
350 *
352 *
351 * @method scroll_to_cell
353 * @method scroll_to_cell
352 * @param {Number} cell_number An index of the cell to view
354 * @param {Number} cell_number An index of the cell to view
353 * @param {Number} time Animation time in milliseconds
355 * @param {Number} time Animation time in milliseconds
354 * @return {Number} Pixel offset from the top of the container
356 * @return {Number} Pixel offset from the top of the container
355 */
357 */
356 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
358 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
357 var cells = this.get_cells();
359 var cells = this.get_cells();
358 var time = time || 0;
360 var time = time || 0;
359 cell_number = Math.min(cells.length-1,cell_number);
361 cell_number = Math.min(cells.length-1,cell_number);
360 cell_number = Math.max(0 ,cell_number);
362 cell_number = Math.max(0 ,cell_number);
361 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
363 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
362 this.element.animate({scrollTop:scroll_value}, time);
364 this.element.animate({scrollTop:scroll_value}, time);
363 return scroll_value;
365 return scroll_value;
364 };
366 };
365
367
366 /**
368 /**
367 * Scroll to the bottom of the page.
369 * Scroll to the bottom of the page.
368 *
370 *
369 * @method scroll_to_bottom
371 * @method scroll_to_bottom
370 */
372 */
371 Notebook.prototype.scroll_to_bottom = function () {
373 Notebook.prototype.scroll_to_bottom = function () {
372 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
374 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
373 };
375 };
374
376
375 /**
377 /**
376 * Scroll to the top of the page.
378 * Scroll to the top of the page.
377 *
379 *
378 * @method scroll_to_top
380 * @method scroll_to_top
379 */
381 */
380 Notebook.prototype.scroll_to_top = function () {
382 Notebook.prototype.scroll_to_top = function () {
381 this.element.animate({scrollTop:0}, 0);
383 this.element.animate({scrollTop:0}, 0);
382 };
384 };
383
385
384
386
385 // Cell indexing, retrieval, etc.
387 // Cell indexing, retrieval, etc.
386
388
387 /**
389 /**
388 * Get all cell elements in the notebook.
390 * Get all cell elements in the notebook.
389 *
391 *
390 * @method get_cell_elements
392 * @method get_cell_elements
391 * @return {jQuery} A selector of all cell elements
393 * @return {jQuery} A selector of all cell elements
392 */
394 */
393 Notebook.prototype.get_cell_elements = function () {
395 Notebook.prototype.get_cell_elements = function () {
394 return this.element.children("div.cell");
396 return this.element.children("div.cell");
395 };
397 };
396
398
397 /**
399 /**
398 * Get a particular cell element.
400 * Get a particular cell element.
399 *
401 *
400 * @method get_cell_element
402 * @method get_cell_element
401 * @param {Number} index An index of a cell to select
403 * @param {Number} index An index of a cell to select
402 * @return {jQuery} A selector of the given cell.
404 * @return {jQuery} A selector of the given cell.
403 */
405 */
404 Notebook.prototype.get_cell_element = function (index) {
406 Notebook.prototype.get_cell_element = function (index) {
405 var result = null;
407 var result = null;
406 var e = this.get_cell_elements().eq(index);
408 var e = this.get_cell_elements().eq(index);
407 if (e.length !== 0) {
409 if (e.length !== 0) {
408 result = e;
410 result = e;
409 }
411 }
410 return result;
412 return result;
411 };
413 };
412
414
413 /**
415 /**
414 * Count the cells in this notebook.
416 * Count the cells in this notebook.
415 *
417 *
416 * @method ncells
418 * @method ncells
417 * @return {Number} The number of cells in this notebook
419 * @return {Number} The number of cells in this notebook
418 */
420 */
419 Notebook.prototype.ncells = function () {
421 Notebook.prototype.ncells = function () {
420 return this.get_cell_elements().length;
422 return this.get_cell_elements().length;
421 };
423 };
422
424
423 /**
425 /**
424 * Get all Cell objects in this notebook.
426 * Get all Cell objects in this notebook.
425 *
427 *
426 * @method get_cells
428 * @method get_cells
427 * @return {Array} This notebook's Cell objects
429 * @return {Array} This notebook's Cell objects
428 */
430 */
429 // TODO: we are often calling cells as cells()[i], which we should optimize
431 // TODO: we are often calling cells as cells()[i], which we should optimize
430 // to cells(i) or a new method.
432 // to cells(i) or a new method.
431 Notebook.prototype.get_cells = function () {
433 Notebook.prototype.get_cells = function () {
432 return this.get_cell_elements().toArray().map(function (e) {
434 return this.get_cell_elements().toArray().map(function (e) {
433 return $(e).data("cell");
435 return $(e).data("cell");
434 });
436 });
435 };
437 };
436
438
437 /**
439 /**
438 * Get a Cell object from this notebook.
440 * Get a Cell object from this notebook.
439 *
441 *
440 * @method get_cell
442 * @method get_cell
441 * @param {Number} index An index of a cell to retrieve
443 * @param {Number} index An index of a cell to retrieve
442 * @return {Cell} A particular cell
444 * @return {Cell} A particular cell
443 */
445 */
444 Notebook.prototype.get_cell = function (index) {
446 Notebook.prototype.get_cell = function (index) {
445 var result = null;
447 var result = null;
446 var ce = this.get_cell_element(index);
448 var ce = this.get_cell_element(index);
447 if (ce !== null) {
449 if (ce !== null) {
448 result = ce.data('cell');
450 result = ce.data('cell');
449 }
451 }
450 return result;
452 return result;
451 }
453 }
452
454
453 /**
455 /**
454 * Get the cell below a given cell.
456 * Get the cell below a given cell.
455 *
457 *
456 * @method get_next_cell
458 * @method get_next_cell
457 * @param {Cell} cell The provided cell
459 * @param {Cell} cell The provided cell
458 * @return {Cell} The next cell
460 * @return {Cell} The next cell
459 */
461 */
460 Notebook.prototype.get_next_cell = function (cell) {
462 Notebook.prototype.get_next_cell = function (cell) {
461 var result = null;
463 var result = null;
462 var index = this.find_cell_index(cell);
464 var index = this.find_cell_index(cell);
463 if (this.is_valid_cell_index(index+1)) {
465 if (this.is_valid_cell_index(index+1)) {
464 result = this.get_cell(index+1);
466 result = this.get_cell(index+1);
465 }
467 }
466 return result;
468 return result;
467 }
469 }
468
470
469 /**
471 /**
470 * Get the cell above a given cell.
472 * Get the cell above a given cell.
471 *
473 *
472 * @method get_prev_cell
474 * @method get_prev_cell
473 * @param {Cell} cell The provided cell
475 * @param {Cell} cell The provided cell
474 * @return {Cell} The previous cell
476 * @return {Cell} The previous cell
475 */
477 */
476 Notebook.prototype.get_prev_cell = function (cell) {
478 Notebook.prototype.get_prev_cell = function (cell) {
477 // TODO: off-by-one
479 // TODO: off-by-one
478 // nb.get_prev_cell(nb.get_cell(1)) is null
480 // nb.get_prev_cell(nb.get_cell(1)) is null
479 var result = null;
481 var result = null;
480 var index = this.find_cell_index(cell);
482 var index = this.find_cell_index(cell);
481 if (index !== null && index > 1) {
483 if (index !== null && index > 1) {
482 result = this.get_cell(index-1);
484 result = this.get_cell(index-1);
483 }
485 }
484 return result;
486 return result;
485 }
487 }
486
488
487 /**
489 /**
488 * Get the numeric index of a given cell.
490 * Get the numeric index of a given cell.
489 *
491 *
490 * @method find_cell_index
492 * @method find_cell_index
491 * @param {Cell} cell The provided cell
493 * @param {Cell} cell The provided cell
492 * @return {Number} The cell's numeric index
494 * @return {Number} The cell's numeric index
493 */
495 */
494 Notebook.prototype.find_cell_index = function (cell) {
496 Notebook.prototype.find_cell_index = function (cell) {
495 var result = null;
497 var result = null;
496 this.get_cell_elements().filter(function (index) {
498 this.get_cell_elements().filter(function (index) {
497 if ($(this).data("cell") === cell) {
499 if ($(this).data("cell") === cell) {
498 result = index;
500 result = index;
499 };
501 };
500 });
502 });
501 return result;
503 return result;
502 };
504 };
503
505
504 /**
506 /**
505 * Get a given index , or the selected index if none is provided.
507 * Get a given index , or the selected index if none is provided.
506 *
508 *
507 * @method index_or_selected
509 * @method index_or_selected
508 * @param {Number} index A cell's index
510 * @param {Number} index A cell's index
509 * @return {Number} The given index, or selected index if none is provided.
511 * @return {Number} The given index, or selected index if none is provided.
510 */
512 */
511 Notebook.prototype.index_or_selected = function (index) {
513 Notebook.prototype.index_or_selected = function (index) {
512 var i;
514 var i;
513 if (index === undefined || index === null) {
515 if (index === undefined || index === null) {
514 i = this.get_selected_index();
516 i = this.get_selected_index();
515 if (i === null) {
517 if (i === null) {
516 i = 0;
518 i = 0;
517 }
519 }
518 } else {
520 } else {
519 i = index;
521 i = index;
520 }
522 }
521 return i;
523 return i;
522 };
524 };
523
525
524 /**
526 /**
525 * Get the currently selected cell.
527 * Get the currently selected cell.
526 * @method get_selected_cell
528 * @method get_selected_cell
527 * @return {Cell} The selected cell
529 * @return {Cell} The selected cell
528 */
530 */
529 Notebook.prototype.get_selected_cell = function () {
531 Notebook.prototype.get_selected_cell = function () {
530 var index = this.get_selected_index();
532 var index = this.get_selected_index();
531 return this.get_cell(index);
533 return this.get_cell(index);
532 };
534 };
533
535
534 /**
536 /**
535 * Check whether a cell index is valid.
537 * Check whether a cell index is valid.
536 *
538 *
537 * @method is_valid_cell_index
539 * @method is_valid_cell_index
538 * @param {Number} index A cell index
540 * @param {Number} index A cell index
539 * @return True if the index is valid, false otherwise
541 * @return True if the index is valid, false otherwise
540 */
542 */
541 Notebook.prototype.is_valid_cell_index = function (index) {
543 Notebook.prototype.is_valid_cell_index = function (index) {
542 if (index !== null && index >= 0 && index < this.ncells()) {
544 if (index !== null && index >= 0 && index < this.ncells()) {
543 return true;
545 return true;
544 } else {
546 } else {
545 return false;
547 return false;
546 };
548 };
547 }
549 }
548
550
549 /**
551 /**
550 * Get the index of the currently selected cell.
552 * Get the index of the currently selected cell.
551
553
552 * @method get_selected_index
554 * @method get_selected_index
553 * @return {Number} The selected cell's numeric index
555 * @return {Number} The selected cell's numeric index
554 */
556 */
555 Notebook.prototype.get_selected_index = function () {
557 Notebook.prototype.get_selected_index = function () {
556 var result = null;
558 var result = null;
557 this.get_cell_elements().filter(function (index) {
559 this.get_cell_elements().filter(function (index) {
558 if ($(this).data("cell").selected === true) {
560 if ($(this).data("cell").selected === true) {
559 result = index;
561 result = index;
560 };
562 };
561 });
563 });
562 return result;
564 return result;
563 };
565 };
564
566
565
567
566 // Cell selection.
568 // Cell selection.
567
569
568 /**
570 /**
569 * Programmatically select a cell.
571 * Programmatically select a cell.
570 *
572 *
571 * @method select
573 * @method select
572 * @param {Number} index A cell's index
574 * @param {Number} index A cell's index
573 * @return {Notebook} This notebook
575 * @return {Notebook} This notebook
574 */
576 */
575 Notebook.prototype.select = function (index) {
577 Notebook.prototype.select = function (index) {
576 if (this.is_valid_cell_index(index)) {
578 if (this.is_valid_cell_index(index)) {
577 var sindex = this.get_selected_index()
579 var sindex = this.get_selected_index()
578 if (sindex !== null && index !== sindex) {
580 if (sindex !== null && index !== sindex) {
579 this.get_cell(sindex).unselect();
581 this.get_cell(sindex).unselect();
580 };
582 };
581 var cell = this.get_cell(index);
583 var cell = this.get_cell(index);
582 cell.select();
584 cell.select();
583 if (cell.cell_type === 'heading') {
585 if (cell.cell_type === 'heading') {
584 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
586 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
585 {'cell_type':cell.cell_type,level:cell.level}
587 {'cell_type':cell.cell_type,level:cell.level}
586 );
588 );
587 } else {
589 } else {
588 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
590 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
589 {'cell_type':cell.cell_type}
591 {'cell_type':cell.cell_type}
590 );
592 );
591 };
593 };
592 };
594 };
593 return this;
595 return this;
594 };
596 };
595
597
596 /**
598 /**
597 * Programmatically select the next cell.
599 * Programmatically select the next cell.
598 *
600 *
599 * @method select_next
601 * @method select_next
600 * @return {Notebook} This notebook
602 * @return {Notebook} This notebook
601 */
603 */
602 Notebook.prototype.select_next = function () {
604 Notebook.prototype.select_next = function () {
603 var index = this.get_selected_index();
605 var index = this.get_selected_index();
604 this.select(index+1);
606 this.select(index+1);
605 return this;
607 return this;
606 };
608 };
607
609
608 /**
610 /**
609 * Programmatically select the previous cell.
611 * Programmatically select the previous cell.
610 *
612 *
611 * @method select_prev
613 * @method select_prev
612 * @return {Notebook} This notebook
614 * @return {Notebook} This notebook
613 */
615 */
614 Notebook.prototype.select_prev = function () {
616 Notebook.prototype.select_prev = function () {
615 var index = this.get_selected_index();
617 var index = this.get_selected_index();
616 this.select(index-1);
618 this.select(index-1);
617 return this;
619 return this;
618 };
620 };
619
621
620
622
621 // Cell movement
623 // Cell movement
622
624
623 /**
625 /**
624 * Move given (or selected) cell up and select it.
626 * Move given (or selected) cell up and select it.
625 *
627 *
626 * @method move_cell_up
628 * @method move_cell_up
627 * @param [index] {integer} cell index
629 * @param [index] {integer} cell index
628 * @return {Notebook} This notebook
630 * @return {Notebook} This notebook
629 **/
631 **/
630 Notebook.prototype.move_cell_up = function (index) {
632 Notebook.prototype.move_cell_up = function (index) {
631 var i = this.index_or_selected(index);
633 var i = this.index_or_selected(index);
632 if (this.is_valid_cell_index(i) && i > 0) {
634 if (this.is_valid_cell_index(i) && i > 0) {
633 var pivot = this.get_cell_element(i-1);
635 var pivot = this.get_cell_element(i-1);
634 var tomove = this.get_cell_element(i);
636 var tomove = this.get_cell_element(i);
635 if (pivot !== null && tomove !== null) {
637 if (pivot !== null && tomove !== null) {
636 tomove.detach();
638 tomove.detach();
637 pivot.before(tomove);
639 pivot.before(tomove);
638 this.select(i-1);
640 this.select(i-1);
639 };
641 };
640 this.dirty = true;
642 this.dirty = true;
641 };
643 };
642 return this;
644 return this;
643 };
645 };
644
646
645
647
646 /**
648 /**
647 * Move given (or selected) cell down and select it
649 * Move given (or selected) cell down and select it
648 *
650 *
649 * @method move_cell_down
651 * @method move_cell_down
650 * @param [index] {integer} cell index
652 * @param [index] {integer} cell index
651 * @return {Notebook} This notebook
653 * @return {Notebook} This notebook
652 **/
654 **/
653 Notebook.prototype.move_cell_down = function (index) {
655 Notebook.prototype.move_cell_down = function (index) {
654 var i = this.index_or_selected(index);
656 var i = this.index_or_selected(index);
655 if ( this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
657 if ( this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
656 var pivot = this.get_cell_element(i+1);
658 var pivot = this.get_cell_element(i+1);
657 var tomove = this.get_cell_element(i);
659 var tomove = this.get_cell_element(i);
658 if (pivot !== null && tomove !== null) {
660 if (pivot !== null && tomove !== null) {
659 tomove.detach();
661 tomove.detach();
660 pivot.after(tomove);
662 pivot.after(tomove);
661 this.select(i+1);
663 this.select(i+1);
662 };
664 };
663 };
665 };
664 this.dirty = true;
666 this.dirty = true;
665 return this;
667 return this;
666 };
668 };
667
669
668
670
669 // Insertion, deletion.
671 // Insertion, deletion.
670
672
671 /**
673 /**
672 * Delete a cell from the notebook.
674 * Delete a cell from the notebook.
673 *
675 *
674 * @method delete_cell
676 * @method delete_cell
675 * @param [index] A cell's numeric index
677 * @param [index] A cell's numeric index
676 * @return {Notebook} This notebook
678 * @return {Notebook} This notebook
677 */
679 */
678 Notebook.prototype.delete_cell = function (index) {
680 Notebook.prototype.delete_cell = function (index) {
679 var i = this.index_or_selected(index);
681 var i = this.index_or_selected(index);
680 var cell = this.get_selected_cell();
682 var cell = this.get_selected_cell();
681 this.undelete_backup = cell.toJSON();
683 this.undelete_backup = cell.toJSON();
682 $('#undelete_cell').removeClass('ui-state-disabled');
684 $('#undelete_cell').removeClass('ui-state-disabled');
683 if (this.is_valid_cell_index(i)) {
685 if (this.is_valid_cell_index(i)) {
684 var ce = this.get_cell_element(i);
686 var ce = this.get_cell_element(i);
685 ce.remove();
687 ce.remove();
686 if (i === (this.ncells())) {
688 if (i === (this.ncells())) {
687 this.select(i-1);
689 this.select(i-1);
688 this.undelete_index = i - 1;
690 this.undelete_index = i - 1;
689 this.undelete_below = true;
691 this.undelete_below = true;
690 } else {
692 } else {
691 this.select(i);
693 this.select(i);
692 this.undelete_index = i;
694 this.undelete_index = i;
693 this.undelete_below = false;
695 this.undelete_below = false;
694 };
696 };
695 this.dirty = true;
697 this.dirty = true;
696 };
698 };
697 return this;
699 return this;
698 };
700 };
699
701
700 /**
702 /**
701 * Insert a cell so that after insertion the cell is at given index.
703 * Insert a cell so that after insertion the cell is at given index.
702 *
704 *
703 * Similar to insert_above, but index parameter is mandatory
705 * Similar to insert_above, but index parameter is mandatory
704 *
706 *
705 * Index will be brought back into the accissible range [0,n]
707 * Index will be brought back into the accissible range [0,n]
706 *
708 *
707 * @method insert_cell_at_index
709 * @method insert_cell_at_index
708 * @param type {string} in ['code','markdown','heading']
710 * @param type {string} in ['code','markdown','heading']
709 * @param [index] {int} a valid index where to inser cell
711 * @param [index] {int} a valid index where to inser cell
710 *
712 *
711 * @return cell {cell|null} created cell or null
713 * @return cell {cell|null} created cell or null
712 **/
714 **/
713 Notebook.prototype.insert_cell_at_index = function(type, index){
715 Notebook.prototype.insert_cell_at_index = function(type, index){
714
716
715 var ncells = this.ncells();
717 var ncells = this.ncells();
716 var index = Math.min(index,ncells);
718 var index = Math.min(index,ncells);
717 index = Math.max(index,0);
719 index = Math.max(index,0);
718 var cell = null;
720 var cell = null;
719
721
720 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
722 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
721 if (type === 'code') {
723 if (type === 'code') {
722 cell = new IPython.CodeCell(this.kernel);
724 cell = new IPython.CodeCell(this.kernel);
723 cell.set_input_prompt();
725 cell.set_input_prompt();
724 } else if (type === 'markdown') {
726 } else if (type === 'markdown') {
725 cell = new IPython.MarkdownCell();
727 cell = new IPython.MarkdownCell();
726 } else if (type === 'raw') {
728 } else if (type === 'raw') {
727 cell = new IPython.RawCell();
729 cell = new IPython.RawCell();
728 } else if (type === 'heading') {
730 } else if (type === 'heading') {
729 cell = new IPython.HeadingCell();
731 cell = new IPython.HeadingCell();
730 }
732 }
731
733
732 if(this._insert_element_at_index(cell.element,index)){
734 if(this._insert_element_at_index(cell.element,index)){
733 cell.render();
735 cell.render();
734 this.select(this.find_cell_index(cell));
736 this.select(this.find_cell_index(cell));
735 this.dirty = true;
737 this.dirty = true;
736 }
738 }
737 }
739 }
738 return cell;
740 return cell;
739
741
740 };
742 };
741
743
742 /**
744 /**
743 * Insert an element at given cell index.
745 * Insert an element at given cell index.
744 *
746 *
745 * @method _insert_element_at_index
747 * @method _insert_element_at_index
746 * @param element {dom element} a cell element
748 * @param element {dom element} a cell element
747 * @param [index] {int} a valid index where to inser cell
749 * @param [index] {int} a valid index where to inser cell
748 * @private
750 * @private
749 *
751 *
750 * return true if everything whent fine.
752 * return true if everything whent fine.
751 **/
753 **/
752 Notebook.prototype._insert_element_at_index = function(element, index){
754 Notebook.prototype._insert_element_at_index = function(element, index){
753 if (element === undefined){
755 if (element === undefined){
754 return false;
756 return false;
755 }
757 }
756
758
757 var ncells = this.ncells();
759 var ncells = this.ncells();
758
760
759 if (ncells === 0) {
761 if (ncells === 0) {
760 // special case append if empty
762 // special case append if empty
761 this.element.find('div.end_space').before(element);
763 this.element.find('div.end_space').before(element);
762 } else if ( ncells === index ) {
764 } else if ( ncells === index ) {
763 // special case append it the end, but not empty
765 // special case append it the end, but not empty
764 this.get_cell_element(index-1).after(element);
766 this.get_cell_element(index-1).after(element);
765 } else if (this.is_valid_cell_index(index)) {
767 } else if (this.is_valid_cell_index(index)) {
766 // otherwise always somewhere to append to
768 // otherwise always somewhere to append to
767 this.get_cell_element(index).before(element);
769 this.get_cell_element(index).before(element);
768 } else {
770 } else {
769 return false;
771 return false;
770 }
772 }
771
773
772 if (this.undelete_index !== null && index <= this.undelete_index) {
774 if (this.undelete_index !== null && index <= this.undelete_index) {
773 this.undelete_index = this.undelete_index + 1;
775 this.undelete_index = this.undelete_index + 1;
774 this.dirty = true;
776 this.dirty = true;
775 }
777 }
776 return true;
778 return true;
777 };
779 };
778
780
779 /**
781 /**
780 * Insert a cell of given type above given index, or at top
782 * Insert a cell of given type above given index, or at top
781 * of notebook if index smaller than 0.
783 * of notebook if index smaller than 0.
782 *
784 *
783 * default index value is the one of currently selected cell
785 * default index value is the one of currently selected cell
784 *
786 *
785 * @method insert_cell_above
787 * @method insert_cell_above
786 * @param type {string} cell type
788 * @param type {string} cell type
787 * @param [index] {integer}
789 * @param [index] {integer}
788 *
790 *
789 * @return handle to created cell or null
791 * @return handle to created cell or null
790 **/
792 **/
791 Notebook.prototype.insert_cell_above = function (type, index) {
793 Notebook.prototype.insert_cell_above = function (type, index) {
792 index = this.index_or_selected(index);
794 index = this.index_or_selected(index);
793 return this.insert_cell_at_index(type, index);
795 return this.insert_cell_at_index(type, index);
794 };
796 };
795
797
796 /**
798 /**
797 * Insert a cell of given type below given index, or at bottom
799 * Insert a cell of given type below given index, or at bottom
798 * of notebook if index greater thatn number of cell
800 * of notebook if index greater thatn number of cell
799 *
801 *
800 * default index value is the one of currently selected cell
802 * default index value is the one of currently selected cell
801 *
803 *
802 * @method insert_cell_below
804 * @method insert_cell_below
803 * @param type {string} cell type
805 * @param type {string} cell type
804 * @param [index] {integer}
806 * @param [index] {integer}
805 *
807 *
806 * @return handle to created cell or null
808 * @return handle to created cell or null
807 *
809 *
808 **/
810 **/
809 Notebook.prototype.insert_cell_below = function (type, index) {
811 Notebook.prototype.insert_cell_below = function (type, index) {
810 index = this.index_or_selected(index);
812 index = this.index_or_selected(index);
811 return this.insert_cell_at_index(type, index+1);
813 return this.insert_cell_at_index(type, index+1);
812 };
814 };
813
815
814
816
815 /**
817 /**
816 * Insert cell at end of notebook
818 * Insert cell at end of notebook
817 *
819 *
818 * @method insert_cell_at_bottom
820 * @method insert_cell_at_bottom
819 * @param {String} type cell type
821 * @param {String} type cell type
820 *
822 *
821 * @return the added cell; or null
823 * @return the added cell; or null
822 **/
824 **/
823 Notebook.prototype.insert_cell_at_bottom = function (type){
825 Notebook.prototype.insert_cell_at_bottom = function (type){
824 var len = this.ncells();
826 var len = this.ncells();
825 return this.insert_cell_below(type,len-1);
827 return this.insert_cell_below(type,len-1);
826 };
828 };
827
829
828 /**
830 /**
829 * Turn a cell into a code cell.
831 * Turn a cell into a code cell.
830 *
832 *
831 * @method to_code
833 * @method to_code
832 * @param {Number} [index] A cell's index
834 * @param {Number} [index] A cell's index
833 */
835 */
834 Notebook.prototype.to_code = function (index) {
836 Notebook.prototype.to_code = function (index) {
835 var i = this.index_or_selected(index);
837 var i = this.index_or_selected(index);
836 if (this.is_valid_cell_index(i)) {
838 if (this.is_valid_cell_index(i)) {
837 var source_element = this.get_cell_element(i);
839 var source_element = this.get_cell_element(i);
838 var source_cell = source_element.data("cell");
840 var source_cell = source_element.data("cell");
839 if (!(source_cell instanceof IPython.CodeCell)) {
841 if (!(source_cell instanceof IPython.CodeCell)) {
840 var target_cell = this.insert_cell_below('code',i);
842 var target_cell = this.insert_cell_below('code',i);
841 var text = source_cell.get_text();
843 var text = source_cell.get_text();
842 if (text === source_cell.placeholder) {
844 if (text === source_cell.placeholder) {
843 text = '';
845 text = '';
844 }
846 }
845 target_cell.set_text(text);
847 target_cell.set_text(text);
846 // make this value the starting point, so that we can only undo
848 // make this value the starting point, so that we can only undo
847 // to this state, instead of a blank cell
849 // to this state, instead of a blank cell
848 target_cell.code_mirror.clearHistory();
850 target_cell.code_mirror.clearHistory();
849 source_element.remove();
851 source_element.remove();
850 this.dirty = true;
852 this.dirty = true;
851 };
853 };
852 };
854 };
853 };
855 };
854
856
855 /**
857 /**
856 * Turn a cell into a Markdown cell.
858 * Turn a cell into a Markdown cell.
857 *
859 *
858 * @method to_markdown
860 * @method to_markdown
859 * @param {Number} [index] A cell's index
861 * @param {Number} [index] A cell's index
860 */
862 */
861 Notebook.prototype.to_markdown = function (index) {
863 Notebook.prototype.to_markdown = function (index) {
862 var i = this.index_or_selected(index);
864 var i = this.index_or_selected(index);
863 if (this.is_valid_cell_index(i)) {
865 if (this.is_valid_cell_index(i)) {
864 var source_element = this.get_cell_element(i);
866 var source_element = this.get_cell_element(i);
865 var source_cell = source_element.data("cell");
867 var source_cell = source_element.data("cell");
866 if (!(source_cell instanceof IPython.MarkdownCell)) {
868 if (!(source_cell instanceof IPython.MarkdownCell)) {
867 var target_cell = this.insert_cell_below('markdown',i);
869 var target_cell = this.insert_cell_below('markdown',i);
868 var text = source_cell.get_text();
870 var text = source_cell.get_text();
869 if (text === source_cell.placeholder) {
871 if (text === source_cell.placeholder) {
870 text = '';
872 text = '';
871 };
873 };
872 // The edit must come before the set_text.
874 // The edit must come before the set_text.
873 target_cell.edit();
875 target_cell.edit();
874 target_cell.set_text(text);
876 target_cell.set_text(text);
875 // make this value the starting point, so that we can only undo
877 // make this value the starting point, so that we can only undo
876 // to this state, instead of a blank cell
878 // to this state, instead of a blank cell
877 target_cell.code_mirror.clearHistory();
879 target_cell.code_mirror.clearHistory();
878 source_element.remove();
880 source_element.remove();
879 this.dirty = true;
881 this.dirty = true;
880 };
882 };
881 };
883 };
882 };
884 };
883
885
884 /**
886 /**
885 * Turn a cell into a raw text cell.
887 * Turn a cell into a raw text cell.
886 *
888 *
887 * @method to_raw
889 * @method to_raw
888 * @param {Number} [index] A cell's index
890 * @param {Number} [index] A cell's index
889 */
891 */
890 Notebook.prototype.to_raw = function (index) {
892 Notebook.prototype.to_raw = function (index) {
891 var i = this.index_or_selected(index);
893 var i = this.index_or_selected(index);
892 if (this.is_valid_cell_index(i)) {
894 if (this.is_valid_cell_index(i)) {
893 var source_element = this.get_cell_element(i);
895 var source_element = this.get_cell_element(i);
894 var source_cell = source_element.data("cell");
896 var source_cell = source_element.data("cell");
895 var target_cell = null;
897 var target_cell = null;
896 if (!(source_cell instanceof IPython.RawCell)) {
898 if (!(source_cell instanceof IPython.RawCell)) {
897 target_cell = this.insert_cell_below('raw',i);
899 target_cell = this.insert_cell_below('raw',i);
898 var text = source_cell.get_text();
900 var text = source_cell.get_text();
899 if (text === source_cell.placeholder) {
901 if (text === source_cell.placeholder) {
900 text = '';
902 text = '';
901 };
903 };
902 // The edit must come before the set_text.
904 // The edit must come before the set_text.
903 target_cell.edit();
905 target_cell.edit();
904 target_cell.set_text(text);
906 target_cell.set_text(text);
905 // make this value the starting point, so that we can only undo
907 // make this value the starting point, so that we can only undo
906 // to this state, instead of a blank cell
908 // to this state, instead of a blank cell
907 target_cell.code_mirror.clearHistory();
909 target_cell.code_mirror.clearHistory();
908 source_element.remove();
910 source_element.remove();
909 this.dirty = true;
911 this.dirty = true;
910 };
912 };
911 };
913 };
912 };
914 };
913
915
914 /**
916 /**
915 * Turn a cell into a heading cell.
917 * Turn a cell into a heading cell.
916 *
918 *
917 * @method to_heading
919 * @method to_heading
918 * @param {Number} [index] A cell's index
920 * @param {Number} [index] A cell's index
919 * @param {Number} [level] A heading level (e.g., 1 becomes &lt;h1&gt;)
921 * @param {Number} [level] A heading level (e.g., 1 becomes &lt;h1&gt;)
920 */
922 */
921 Notebook.prototype.to_heading = function (index, level) {
923 Notebook.prototype.to_heading = function (index, level) {
922 level = level || 1;
924 level = level || 1;
923 var i = this.index_or_selected(index);
925 var i = this.index_or_selected(index);
924 if (this.is_valid_cell_index(i)) {
926 if (this.is_valid_cell_index(i)) {
925 var source_element = this.get_cell_element(i);
927 var source_element = this.get_cell_element(i);
926 var source_cell = source_element.data("cell");
928 var source_cell = source_element.data("cell");
927 var target_cell = null;
929 var target_cell = null;
928 if (source_cell instanceof IPython.HeadingCell) {
930 if (source_cell instanceof IPython.HeadingCell) {
929 source_cell.set_level(level);
931 source_cell.set_level(level);
930 } else {
932 } else {
931 target_cell = this.insert_cell_below('heading',i);
933 target_cell = this.insert_cell_below('heading',i);
932 var text = source_cell.get_text();
934 var text = source_cell.get_text();
933 if (text === source_cell.placeholder) {
935 if (text === source_cell.placeholder) {
934 text = '';
936 text = '';
935 };
937 };
936 // The edit must come before the set_text.
938 // The edit must come before the set_text.
937 target_cell.set_level(level);
939 target_cell.set_level(level);
938 target_cell.edit();
940 target_cell.edit();
939 target_cell.set_text(text);
941 target_cell.set_text(text);
940 // make this value the starting point, so that we can only undo
942 // make this value the starting point, so that we can only undo
941 // to this state, instead of a blank cell
943 // to this state, instead of a blank cell
942 target_cell.code_mirror.clearHistory();
944 target_cell.code_mirror.clearHistory();
943 source_element.remove();
945 source_element.remove();
944 this.dirty = true;
946 this.dirty = true;
945 };
947 };
946 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
948 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
947 {'cell_type':'heading',level:level}
949 {'cell_type':'heading',level:level}
948 );
950 );
949 };
951 };
950 };
952 };
951
953
952
954
953 // Cut/Copy/Paste
955 // Cut/Copy/Paste
954
956
955 /**
957 /**
956 * Enable UI elements for pasting cells.
958 * Enable UI elements for pasting cells.
957 *
959 *
958 * @method enable_paste
960 * @method enable_paste
959 */
961 */
960 Notebook.prototype.enable_paste = function () {
962 Notebook.prototype.enable_paste = function () {
961 var that = this;
963 var that = this;
962 if (!this.paste_enabled) {
964 if (!this.paste_enabled) {
963 $('#paste_cell_replace').removeClass('ui-state-disabled')
965 $('#paste_cell_replace').removeClass('ui-state-disabled')
964 .on('click', function () {that.paste_cell_replace();});
966 .on('click', function () {that.paste_cell_replace();});
965 $('#paste_cell_above').removeClass('ui-state-disabled')
967 $('#paste_cell_above').removeClass('ui-state-disabled')
966 .on('click', function () {that.paste_cell_above();});
968 .on('click', function () {that.paste_cell_above();});
967 $('#paste_cell_below').removeClass('ui-state-disabled')
969 $('#paste_cell_below').removeClass('ui-state-disabled')
968 .on('click', function () {that.paste_cell_below();});
970 .on('click', function () {that.paste_cell_below();});
969 this.paste_enabled = true;
971 this.paste_enabled = true;
970 };
972 };
971 };
973 };
972
974
973 /**
975 /**
974 * Disable UI elements for pasting cells.
976 * Disable UI elements for pasting cells.
975 *
977 *
976 * @method disable_paste
978 * @method disable_paste
977 */
979 */
978 Notebook.prototype.disable_paste = function () {
980 Notebook.prototype.disable_paste = function () {
979 if (this.paste_enabled) {
981 if (this.paste_enabled) {
980 $('#paste_cell_replace').addClass('ui-state-disabled').off('click');
982 $('#paste_cell_replace').addClass('ui-state-disabled').off('click');
981 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
983 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
982 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
984 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
983 this.paste_enabled = false;
985 this.paste_enabled = false;
984 };
986 };
985 };
987 };
986
988
987 /**
989 /**
988 * Cut a cell.
990 * Cut a cell.
989 *
991 *
990 * @method cut_cell
992 * @method cut_cell
991 */
993 */
992 Notebook.prototype.cut_cell = function () {
994 Notebook.prototype.cut_cell = function () {
993 this.copy_cell();
995 this.copy_cell();
994 this.delete_cell();
996 this.delete_cell();
995 }
997 }
996
998
997 /**
999 /**
998 * Copy a cell.
1000 * Copy a cell.
999 *
1001 *
1000 * @method copy_cell
1002 * @method copy_cell
1001 */
1003 */
1002 Notebook.prototype.copy_cell = function () {
1004 Notebook.prototype.copy_cell = function () {
1003 var cell = this.get_selected_cell();
1005 var cell = this.get_selected_cell();
1004 this.clipboard = cell.toJSON();
1006 this.clipboard = cell.toJSON();
1005 this.enable_paste();
1007 this.enable_paste();
1006 };
1008 };
1007
1009
1008 /**
1010 /**
1009 * Replace the selected cell with a cell in the clipboard.
1011 * Replace the selected cell with a cell in the clipboard.
1010 *
1012 *
1011 * @method paste_cell_replace
1013 * @method paste_cell_replace
1012 */
1014 */
1013 Notebook.prototype.paste_cell_replace = function () {
1015 Notebook.prototype.paste_cell_replace = function () {
1014 if (this.clipboard !== null && this.paste_enabled) {
1016 if (this.clipboard !== null && this.paste_enabled) {
1015 var cell_data = this.clipboard;
1017 var cell_data = this.clipboard;
1016 var new_cell = this.insert_cell_above(cell_data.cell_type);
1018 var new_cell = this.insert_cell_above(cell_data.cell_type);
1017 new_cell.fromJSON(cell_data);
1019 new_cell.fromJSON(cell_data);
1018 var old_cell = this.get_next_cell(new_cell);
1020 var old_cell = this.get_next_cell(new_cell);
1019 this.delete_cell(this.find_cell_index(old_cell));
1021 this.delete_cell(this.find_cell_index(old_cell));
1020 this.select(this.find_cell_index(new_cell));
1022 this.select(this.find_cell_index(new_cell));
1021 };
1023 };
1022 };
1024 };
1023
1025
1024 /**
1026 /**
1025 * Paste a cell from the clipboard above the selected cell.
1027 * Paste a cell from the clipboard above the selected cell.
1026 *
1028 *
1027 * @method paste_cell_above
1029 * @method paste_cell_above
1028 */
1030 */
1029 Notebook.prototype.paste_cell_above = function () {
1031 Notebook.prototype.paste_cell_above = function () {
1030 if (this.clipboard !== null && this.paste_enabled) {
1032 if (this.clipboard !== null && this.paste_enabled) {
1031 var cell_data = this.clipboard;
1033 var cell_data = this.clipboard;
1032 var new_cell = this.insert_cell_above(cell_data.cell_type);
1034 var new_cell = this.insert_cell_above(cell_data.cell_type);
1033 new_cell.fromJSON(cell_data);
1035 new_cell.fromJSON(cell_data);
1034 };
1036 };
1035 };
1037 };
1036
1038
1037 /**
1039 /**
1038 * Paste a cell from the clipboard below the selected cell.
1040 * Paste a cell from the clipboard below the selected cell.
1039 *
1041 *
1040 * @method paste_cell_below
1042 * @method paste_cell_below
1041 */
1043 */
1042 Notebook.prototype.paste_cell_below = function () {
1044 Notebook.prototype.paste_cell_below = function () {
1043 if (this.clipboard !== null && this.paste_enabled) {
1045 if (this.clipboard !== null && this.paste_enabled) {
1044 var cell_data = this.clipboard;
1046 var cell_data = this.clipboard;
1045 var new_cell = this.insert_cell_below(cell_data.cell_type);
1047 var new_cell = this.insert_cell_below(cell_data.cell_type);
1046 new_cell.fromJSON(cell_data);
1048 new_cell.fromJSON(cell_data);
1047 };
1049 };
1048 };
1050 };
1049
1051
1050 // Cell undelete
1052 // Cell undelete
1051
1053
1052 /**
1054 /**
1053 * Restore the most recently deleted cell.
1055 * Restore the most recently deleted cell.
1054 *
1056 *
1055 * @method undelete
1057 * @method undelete
1056 */
1058 */
1057 Notebook.prototype.undelete = function() {
1059 Notebook.prototype.undelete = function() {
1058 if (this.undelete_backup !== null && this.undelete_index !== null) {
1060 if (this.undelete_backup !== null && this.undelete_index !== null) {
1059 var current_index = this.get_selected_index();
1061 var current_index = this.get_selected_index();
1060 if (this.undelete_index < current_index) {
1062 if (this.undelete_index < current_index) {
1061 current_index = current_index + 1;
1063 current_index = current_index + 1;
1062 }
1064 }
1063 if (this.undelete_index >= this.ncells()) {
1065 if (this.undelete_index >= this.ncells()) {
1064 this.select(this.ncells() - 1);
1066 this.select(this.ncells() - 1);
1065 }
1067 }
1066 else {
1068 else {
1067 this.select(this.undelete_index);
1069 this.select(this.undelete_index);
1068 }
1070 }
1069 var cell_data = this.undelete_backup;
1071 var cell_data = this.undelete_backup;
1070 var new_cell = null;
1072 var new_cell = null;
1071 if (this.undelete_below) {
1073 if (this.undelete_below) {
1072 new_cell = this.insert_cell_below(cell_data.cell_type);
1074 new_cell = this.insert_cell_below(cell_data.cell_type);
1073 } else {
1075 } else {
1074 new_cell = this.insert_cell_above(cell_data.cell_type);
1076 new_cell = this.insert_cell_above(cell_data.cell_type);
1075 }
1077 }
1076 new_cell.fromJSON(cell_data);
1078 new_cell.fromJSON(cell_data);
1077 this.select(current_index);
1079 this.select(current_index);
1078 this.undelete_backup = null;
1080 this.undelete_backup = null;
1079 this.undelete_index = null;
1081 this.undelete_index = null;
1080 }
1082 }
1081 $('#undelete_cell').addClass('ui-state-disabled');
1083 $('#undelete_cell').addClass('ui-state-disabled');
1082 }
1084 }
1083
1085
1084 // Split/merge
1086 // Split/merge
1085
1087
1086 /**
1088 /**
1087 * Split the selected cell into two, at the cursor.
1089 * Split the selected cell into two, at the cursor.
1088 *
1090 *
1089 * @method split_cell
1091 * @method split_cell
1090 */
1092 */
1091 Notebook.prototype.split_cell = function () {
1093 Notebook.prototype.split_cell = function () {
1092 // Todo: implement spliting for other cell types.
1094 // Todo: implement spliting for other cell types.
1093 var cell = this.get_selected_cell();
1095 var cell = this.get_selected_cell();
1094 if (cell.is_splittable()) {
1096 if (cell.is_splittable()) {
1095 var texta = cell.get_pre_cursor();
1097 var texta = cell.get_pre_cursor();
1096 var textb = cell.get_post_cursor();
1098 var textb = cell.get_post_cursor();
1097 if (cell instanceof IPython.CodeCell) {
1099 if (cell instanceof IPython.CodeCell) {
1098 cell.set_text(texta);
1100 cell.set_text(texta);
1099 var new_cell = this.insert_cell_below('code');
1101 var new_cell = this.insert_cell_below('code');
1100 new_cell.set_text(textb);
1102 new_cell.set_text(textb);
1101 } else if (cell instanceof IPython.MarkdownCell) {
1103 } else if (cell instanceof IPython.MarkdownCell) {
1102 cell.set_text(texta);
1104 cell.set_text(texta);
1103 cell.render();
1105 cell.render();
1104 var new_cell = this.insert_cell_below('markdown');
1106 var new_cell = this.insert_cell_below('markdown');
1105 new_cell.edit(); // editor must be visible to call set_text
1107 new_cell.edit(); // editor must be visible to call set_text
1106 new_cell.set_text(textb);
1108 new_cell.set_text(textb);
1107 new_cell.render();
1109 new_cell.render();
1108 }
1110 }
1109 };
1111 };
1110 };
1112 };
1111
1113
1112 /**
1114 /**
1113 * Combine the selected cell into the cell above it.
1115 * Combine the selected cell into the cell above it.
1114 *
1116 *
1115 * @method merge_cell_above
1117 * @method merge_cell_above
1116 */
1118 */
1117 Notebook.prototype.merge_cell_above = function () {
1119 Notebook.prototype.merge_cell_above = function () {
1118 var index = this.get_selected_index();
1120 var index = this.get_selected_index();
1119 var cell = this.get_cell(index);
1121 var cell = this.get_cell(index);
1120 if (index > 0) {
1122 if (index > 0) {
1121 var upper_cell = this.get_cell(index-1);
1123 var upper_cell = this.get_cell(index-1);
1122 var upper_text = upper_cell.get_text();
1124 var upper_text = upper_cell.get_text();
1123 var text = cell.get_text();
1125 var text = cell.get_text();
1124 if (cell instanceof IPython.CodeCell) {
1126 if (cell instanceof IPython.CodeCell) {
1125 cell.set_text(upper_text+'\n'+text);
1127 cell.set_text(upper_text+'\n'+text);
1126 } else if (cell instanceof IPython.MarkdownCell) {
1128 } else if (cell instanceof IPython.MarkdownCell) {
1127 cell.edit();
1129 cell.edit();
1128 cell.set_text(upper_text+'\n'+text);
1130 cell.set_text(upper_text+'\n'+text);
1129 cell.render();
1131 cell.render();
1130 };
1132 };
1131 this.delete_cell(index-1);
1133 this.delete_cell(index-1);
1132 this.select(this.find_cell_index(cell));
1134 this.select(this.find_cell_index(cell));
1133 };
1135 };
1134 };
1136 };
1135
1137
1136 /**
1138 /**
1137 * Combine the selected cell into the cell below it.
1139 * Combine the selected cell into the cell below it.
1138 *
1140 *
1139 * @method merge_cell_below
1141 * @method merge_cell_below
1140 */
1142 */
1141 Notebook.prototype.merge_cell_below = function () {
1143 Notebook.prototype.merge_cell_below = function () {
1142 var index = this.get_selected_index();
1144 var index = this.get_selected_index();
1143 var cell = this.get_cell(index);
1145 var cell = this.get_cell(index);
1144 if (index < this.ncells()-1) {
1146 if (index < this.ncells()-1) {
1145 var lower_cell = this.get_cell(index+1);
1147 var lower_cell = this.get_cell(index+1);
1146 var lower_text = lower_cell.get_text();
1148 var lower_text = lower_cell.get_text();
1147 var text = cell.get_text();
1149 var text = cell.get_text();
1148 if (cell instanceof IPython.CodeCell) {
1150 if (cell instanceof IPython.CodeCell) {
1149 cell.set_text(text+'\n'+lower_text);
1151 cell.set_text(text+'\n'+lower_text);
1150 } else if (cell instanceof IPython.MarkdownCell) {
1152 } else if (cell instanceof IPython.MarkdownCell) {
1151 cell.edit();
1153 cell.edit();
1152 cell.set_text(text+'\n'+lower_text);
1154 cell.set_text(text+'\n'+lower_text);
1153 cell.render();
1155 cell.render();
1154 };
1156 };
1155 this.delete_cell(index+1);
1157 this.delete_cell(index+1);
1156 this.select(this.find_cell_index(cell));
1158 this.select(this.find_cell_index(cell));
1157 };
1159 };
1158 };
1160 };
1159
1161
1160
1162
1161 // Cell collapsing and output clearing
1163 // Cell collapsing and output clearing
1162
1164
1163 /**
1165 /**
1164 * Hide a cell's output.
1166 * Hide a cell's output.
1165 *
1167 *
1166 * @method collapse
1168 * @method collapse
1167 * @param {Number} index A cell's numeric index
1169 * @param {Number} index A cell's numeric index
1168 */
1170 */
1169 Notebook.prototype.collapse = function (index) {
1171 Notebook.prototype.collapse = function (index) {
1170 var i = this.index_or_selected(index);
1172 var i = this.index_or_selected(index);
1171 this.get_cell(i).collapse();
1173 this.get_cell(i).collapse();
1172 this.dirty = true;
1174 this.dirty = true;
1173 };
1175 };
1174
1176
1175 /**
1177 /**
1176 * Show a cell's output.
1178 * Show a cell's output.
1177 *
1179 *
1178 * @method expand
1180 * @method expand
1179 * @param {Number} index A cell's numeric index
1181 * @param {Number} index A cell's numeric index
1180 */
1182 */
1181 Notebook.prototype.expand = function (index) {
1183 Notebook.prototype.expand = function (index) {
1182 var i = this.index_or_selected(index);
1184 var i = this.index_or_selected(index);
1183 this.get_cell(i).expand();
1185 this.get_cell(i).expand();
1184 this.dirty = true;
1186 this.dirty = true;
1185 };
1187 };
1186
1188
1187 /** Toggle whether a cell's output is collapsed or expanded.
1189 /** Toggle whether a cell's output is collapsed or expanded.
1188 *
1190 *
1189 * @method toggle_output
1191 * @method toggle_output
1190 * @param {Number} index A cell's numeric index
1192 * @param {Number} index A cell's numeric index
1191 */
1193 */
1192 Notebook.prototype.toggle_output = function (index) {
1194 Notebook.prototype.toggle_output = function (index) {
1193 var i = this.index_or_selected(index);
1195 var i = this.index_or_selected(index);
1194 this.get_cell(i).toggle_output();
1196 this.get_cell(i).toggle_output();
1195 this.dirty = true;
1197 this.dirty = true;
1196 };
1198 };
1197
1199
1198 /**
1200 /**
1199 * Toggle a scrollbar for long cell outputs.
1201 * Toggle a scrollbar for long cell outputs.
1200 *
1202 *
1201 * @method toggle_output_scroll
1203 * @method toggle_output_scroll
1202 * @param {Number} index A cell's numeric index
1204 * @param {Number} index A cell's numeric index
1203 */
1205 */
1204 Notebook.prototype.toggle_output_scroll = function (index) {
1206 Notebook.prototype.toggle_output_scroll = function (index) {
1205 var i = this.index_or_selected(index);
1207 var i = this.index_or_selected(index);
1206 this.get_cell(i).toggle_output_scroll();
1208 this.get_cell(i).toggle_output_scroll();
1207 };
1209 };
1208
1210
1209 /**
1211 /**
1210 * Hide each code cell's output area.
1212 * Hide each code cell's output area.
1211 *
1213 *
1212 * @method collapse_all_output
1214 * @method collapse_all_output
1213 */
1215 */
1214 Notebook.prototype.collapse_all_output = function () {
1216 Notebook.prototype.collapse_all_output = function () {
1215 var ncells = this.ncells();
1217 var ncells = this.ncells();
1216 var cells = this.get_cells();
1218 var cells = this.get_cells();
1217 for (var i=0; i<ncells; i++) {
1219 for (var i=0; i<ncells; i++) {
1218 if (cells[i] instanceof IPython.CodeCell) {
1220 if (cells[i] instanceof IPython.CodeCell) {
1219 cells[i].output_area.collapse();
1221 cells[i].output_area.collapse();
1220 }
1222 }
1221 };
1223 };
1222 // this should not be set if the `collapse` key is removed from nbformat
1224 // this should not be set if the `collapse` key is removed from nbformat
1223 this.dirty = true;
1225 this.dirty = true;
1224 };
1226 };
1225
1227
1226 /**
1228 /**
1227 * Expand each code cell's output area, and add a scrollbar for long output.
1229 * Expand each code cell's output area, and add a scrollbar for long output.
1228 *
1230 *
1229 * @method scroll_all_output
1231 * @method scroll_all_output
1230 */
1232 */
1231 Notebook.prototype.scroll_all_output = function () {
1233 Notebook.prototype.scroll_all_output = function () {
1232 var ncells = this.ncells();
1234 var ncells = this.ncells();
1233 var cells = this.get_cells();
1235 var cells = this.get_cells();
1234 for (var i=0; i<ncells; i++) {
1236 for (var i=0; i<ncells; i++) {
1235 if (cells[i] instanceof IPython.CodeCell) {
1237 if (cells[i] instanceof IPython.CodeCell) {
1236 cells[i].output_area.expand();
1238 cells[i].output_area.expand();
1237 cells[i].output_area.scroll_if_long(20);
1239 cells[i].output_area.scroll_if_long(20);
1238 }
1240 }
1239 };
1241 };
1240 // this should not be set if the `collapse` key is removed from nbformat
1242 // this should not be set if the `collapse` key is removed from nbformat
1241 this.dirty = true;
1243 this.dirty = true;
1242 };
1244 };
1243
1245
1244 /**
1246 /**
1245 * Expand each code cell's output area, and remove scrollbars.
1247 * Expand each code cell's output area, and remove scrollbars.
1246 *
1248 *
1247 * @method expand_all_output
1249 * @method expand_all_output
1248 */
1250 */
1249 Notebook.prototype.expand_all_output = function () {
1251 Notebook.prototype.expand_all_output = function () {
1250 var ncells = this.ncells();
1252 var ncells = this.ncells();
1251 var cells = this.get_cells();
1253 var cells = this.get_cells();
1252 for (var i=0; i<ncells; i++) {
1254 for (var i=0; i<ncells; i++) {
1253 if (cells[i] instanceof IPython.CodeCell) {
1255 if (cells[i] instanceof IPython.CodeCell) {
1254 cells[i].output_area.expand();
1256 cells[i].output_area.expand();
1255 cells[i].output_area.unscroll_area();
1257 cells[i].output_area.unscroll_area();
1256 }
1258 }
1257 };
1259 };
1258 // this should not be set if the `collapse` key is removed from nbformat
1260 // this should not be set if the `collapse` key is removed from nbformat
1259 this.dirty = true;
1261 this.dirty = true;
1260 };
1262 };
1261
1263
1262 /**
1264 /**
1263 * Clear each code cell's output area.
1265 * Clear each code cell's output area.
1264 *
1266 *
1265 * @method clear_all_output
1267 * @method clear_all_output
1266 */
1268 */
1267 Notebook.prototype.clear_all_output = function () {
1269 Notebook.prototype.clear_all_output = function () {
1268 var ncells = this.ncells();
1270 var ncells = this.ncells();
1269 var cells = this.get_cells();
1271 var cells = this.get_cells();
1270 for (var i=0; i<ncells; i++) {
1272 for (var i=0; i<ncells; i++) {
1271 if (cells[i] instanceof IPython.CodeCell) {
1273 if (cells[i] instanceof IPython.CodeCell) {
1272 cells[i].clear_output(true,true,true);
1274 cells[i].clear_output(true,true,true);
1273 // Make all In[] prompts blank, as well
1275 // Make all In[] prompts blank, as well
1274 // TODO: make this configurable (via checkbox?)
1276 // TODO: make this configurable (via checkbox?)
1275 cells[i].set_input_prompt();
1277 cells[i].set_input_prompt();
1276 }
1278 }
1277 };
1279 };
1278 this.dirty = true;
1280 this.dirty = true;
1279 };
1281 };
1280
1282
1281
1283
1282 // Other cell functions: line numbers, ...
1284 // Other cell functions: line numbers, ...
1283
1285
1284 /**
1286 /**
1285 * Toggle line numbers in the selected cell's input area.
1287 * Toggle line numbers in the selected cell's input area.
1286 *
1288 *
1287 * @method cell_toggle_line_numbers
1289 * @method cell_toggle_line_numbers
1288 */
1290 */
1289 Notebook.prototype.cell_toggle_line_numbers = function() {
1291 Notebook.prototype.cell_toggle_line_numbers = function() {
1290 this.get_selected_cell().toggle_line_numbers();
1292 this.get_selected_cell().toggle_line_numbers();
1291 };
1293 };
1292
1294
1293 // Kernel related things
1295 // Kernel related things
1294
1296
1295 /**
1297 /**
1296 * Start a new kernel and set it on each code cell.
1298 * Start a new kernel and set it on each code cell.
1297 *
1299 *
1298 * @method start_kernel
1300 * @method start_kernel
1299 */
1301 */
1300 Notebook.prototype.start_kernel = function () {
1302 Notebook.prototype.start_kernel = function () {
1301 var base_url = $('body').data('baseKernelUrl') + "kernels";
1303 var base_url = $('body').data('baseKernelUrl') + "kernels";
1302 this.kernel = new IPython.Kernel(base_url);
1304 this.kernel = new IPython.Kernel(base_url);
1303 this.kernel.start(this.notebook_id);
1305 this.kernel.start(this.notebook_id);
1304 // Now that the kernel has been created, tell the CodeCells about it.
1306 // Now that the kernel has been created, tell the CodeCells about it.
1305 var ncells = this.ncells();
1307 var ncells = this.ncells();
1306 for (var i=0; i<ncells; i++) {
1308 for (var i=0; i<ncells; i++) {
1307 var cell = this.get_cell(i);
1309 var cell = this.get_cell(i);
1308 if (cell instanceof IPython.CodeCell) {
1310 if (cell instanceof IPython.CodeCell) {
1309 cell.set_kernel(this.kernel)
1311 cell.set_kernel(this.kernel)
1310 };
1312 };
1311 };
1313 };
1312 };
1314 };
1313
1315
1314 /**
1316 /**
1315 * Prompt the user to restart the IPython kernel.
1317 * Prompt the user to restart the IPython kernel.
1316 *
1318 *
1317 * @method restart_kernel
1319 * @method restart_kernel
1318 */
1320 */
1319 Notebook.prototype.restart_kernel = function () {
1321 Notebook.prototype.restart_kernel = function () {
1320 var that = this;
1322 var that = this;
1321 var dialog = $('<div/>');
1323 var dialog = $('<div/>');
1322 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
1324 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
1323 $(document).append(dialog);
1325 $(document).append(dialog);
1324 dialog.dialog({
1326 dialog.dialog({
1325 resizable: false,
1327 resizable: false,
1326 modal: true,
1328 modal: true,
1327 title: "Restart kernel or continue running?",
1329 title: "Restart kernel or continue running?",
1328 closeText: '',
1330 closeText: '',
1329 buttons : {
1331 buttons : {
1330 "Restart": function () {
1332 "Restart": function () {
1331 that.kernel.restart();
1333 that.kernel.restart();
1332 $(this).dialog('close');
1334 $(this).dialog('close');
1333 },
1335 },
1334 "Continue running": function () {
1336 "Continue running": function () {
1335 $(this).dialog('close');
1337 $(this).dialog('close');
1336 }
1338 }
1337 }
1339 }
1338 });
1340 });
1339 };
1341 };
1340
1342
1341 /**
1343 /**
1342 * Run the selected cell.
1344 * Run the selected cell.
1343 *
1345 *
1344 * Execute or render cell outputs.
1346 * Execute or render cell outputs.
1345 *
1347 *
1346 * @method execute_selected_cell
1348 * @method execute_selected_cell
1347 * @param {Object} options Customize post-execution behavior
1349 * @param {Object} options Customize post-execution behavior
1348 */
1350 */
1349 Notebook.prototype.execute_selected_cell = function (options) {
1351 Notebook.prototype.execute_selected_cell = function (options) {
1350 // add_new: should a new cell be added if we are at the end of the nb
1352 // add_new: should a new cell be added if we are at the end of the nb
1351 // terminal: execute in terminal mode, which stays in the current cell
1353 // terminal: execute in terminal mode, which stays in the current cell
1352 var default_options = {terminal: false, add_new: true};
1354 var default_options = {terminal: false, add_new: true};
1353 $.extend(default_options, options);
1355 $.extend(default_options, options);
1354 var that = this;
1356 var that = this;
1355 var cell = that.get_selected_cell();
1357 var cell = that.get_selected_cell();
1356 var cell_index = that.find_cell_index(cell);
1358 var cell_index = that.find_cell_index(cell);
1357 if (cell instanceof IPython.CodeCell) {
1359 if (cell instanceof IPython.CodeCell) {
1358 cell.execute();
1360 cell.execute();
1359 }
1361 }
1360 if (default_options.terminal) {
1362 if (default_options.terminal) {
1361 cell.select_all();
1363 cell.select_all();
1362 } else {
1364 } else {
1363 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1365 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1364 that.insert_cell_below('code');
1366 that.insert_cell_below('code');
1365 // If we are adding a new cell at the end, scroll down to show it.
1367 // If we are adding a new cell at the end, scroll down to show it.
1366 that.scroll_to_bottom();
1368 that.scroll_to_bottom();
1367 } else {
1369 } else {
1368 that.select(cell_index+1);
1370 that.select(cell_index+1);
1369 };
1371 };
1370 };
1372 };
1371 this.dirty = true;
1373 this.dirty = true;
1372 };
1374 };
1373
1375
1374 /**
1376 /**
1375 * Execute all cells below the selected cell.
1377 * Execute all cells below the selected cell.
1376 *
1378 *
1377 * @method execute_cells_below
1379 * @method execute_cells_below
1378 */
1380 */
1379 Notebook.prototype.execute_cells_below = function () {
1381 Notebook.prototype.execute_cells_below = function () {
1380 this.execute_cell_range(this.get_selected_index(), this.ncells());
1382 this.execute_cell_range(this.get_selected_index(), this.ncells());
1381 this.scroll_to_bottom();
1383 this.scroll_to_bottom();
1382 };
1384 };
1383
1385
1384 /**
1386 /**
1385 * Execute all cells above the selected cell.
1387 * Execute all cells above the selected cell.
1386 *
1388 *
1387 * @method execute_cells_above
1389 * @method execute_cells_above
1388 */
1390 */
1389 Notebook.prototype.execute_cells_above = function () {
1391 Notebook.prototype.execute_cells_above = function () {
1390 this.execute_cell_range(0, this.get_selected_index());
1392 this.execute_cell_range(0, this.get_selected_index());
1391 };
1393 };
1392
1394
1393 /**
1395 /**
1394 * Execute all cells.
1396 * Execute all cells.
1395 *
1397 *
1396 * @method execute_all_cells
1398 * @method execute_all_cells
1397 */
1399 */
1398 Notebook.prototype.execute_all_cells = function () {
1400 Notebook.prototype.execute_all_cells = function () {
1399 this.execute_cell_range(0, this.ncells());
1401 this.execute_cell_range(0, this.ncells());
1400 this.scroll_to_bottom();
1402 this.scroll_to_bottom();
1401 };
1403 };
1402
1404
1403 /**
1405 /**
1404 * Execute a contiguous range of cells.
1406 * Execute a contiguous range of cells.
1405 *
1407 *
1406 * @method execute_cell_range
1408 * @method execute_cell_range
1407 * @param {Number} start Index of the first cell to execute (inclusive)
1409 * @param {Number} start Index of the first cell to execute (inclusive)
1408 * @param {Number} end Index of the last cell to execute (exclusive)
1410 * @param {Number} end Index of the last cell to execute (exclusive)
1409 */
1411 */
1410 Notebook.prototype.execute_cell_range = function (start, end) {
1412 Notebook.prototype.execute_cell_range = function (start, end) {
1411 for (var i=start; i<end; i++) {
1413 for (var i=start; i<end; i++) {
1412 this.select(i);
1414 this.select(i);
1413 this.execute_selected_cell({add_new:false});
1415 this.execute_selected_cell({add_new:false});
1414 };
1416 };
1415 };
1417 };
1416
1418
1417 // Persistance and loading
1419 // Persistance and loading
1418
1420
1419 /**
1421 /**
1420 * Getter method for this notebook's ID.
1422 * Getter method for this notebook's ID.
1421 *
1423 *
1422 * @method get_notebook_id
1424 * @method get_notebook_id
1423 * @return {String} This notebook's ID
1425 * @return {String} This notebook's ID
1424 */
1426 */
1425 Notebook.prototype.get_notebook_id = function () {
1427 Notebook.prototype.get_notebook_id = function () {
1426 return this.notebook_id;
1428 return this.notebook_id;
1427 };
1429 };
1428
1430
1429 /**
1431 /**
1430 * Getter method for this notebook's name.
1432 * Getter method for this notebook's name.
1431 *
1433 *
1432 * @method get_notebook_name
1434 * @method get_notebook_name
1433 * @return {String} This notebook's name
1435 * @return {String} This notebook's name
1434 */
1436 */
1435 Notebook.prototype.get_notebook_name = function () {
1437 Notebook.prototype.get_notebook_name = function () {
1436 return this.notebook_name;
1438 return this.notebook_name;
1437 };
1439 };
1438
1440
1439 /**
1441 /**
1440 * Setter method for this notebook's name.
1442 * Setter method for this notebook's name.
1441 *
1443 *
1442 * @method set_notebook_name
1444 * @method set_notebook_name
1443 * @param {String} name A new name for this notebook
1445 * @param {String} name A new name for this notebook
1444 */
1446 */
1445 Notebook.prototype.set_notebook_name = function (name) {
1447 Notebook.prototype.set_notebook_name = function (name) {
1446 this.notebook_name = name;
1448 this.notebook_name = name;
1447 };
1449 };
1448
1450
1449 /**
1451 /**
1450 * Check that a notebook's name is valid.
1452 * Check that a notebook's name is valid.
1451 *
1453 *
1452 * @method test_notebook_name
1454 * @method test_notebook_name
1453 * @param {String} nbname A name for this notebook
1455 * @param {String} nbname A name for this notebook
1454 * @return {Boolean} True if the name is valid, false if invalid
1456 * @return {Boolean} True if the name is valid, false if invalid
1455 */
1457 */
1456 Notebook.prototype.test_notebook_name = function (nbname) {
1458 Notebook.prototype.test_notebook_name = function (nbname) {
1457 nbname = nbname || '';
1459 nbname = nbname || '';
1458 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1460 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1459 return true;
1461 return true;
1460 } else {
1462 } else {
1461 return false;
1463 return false;
1462 };
1464 };
1463 };
1465 };
1464
1466
1465 /**
1467 /**
1466 * Load a notebook from JSON (.ipynb).
1468 * Load a notebook from JSON (.ipynb).
1467 *
1469 *
1468 * This currently handles one worksheet: others are deleted.
1470 * This currently handles one worksheet: others are deleted.
1469 *
1471 *
1470 * @method fromJSON
1472 * @method fromJSON
1471 * @param {Object} data JSON representation of a notebook
1473 * @param {Object} data JSON representation of a notebook
1472 */
1474 */
1473 Notebook.prototype.fromJSON = function (data) {
1475 Notebook.prototype.fromJSON = function (data) {
1474 var ncells = this.ncells();
1476 var ncells = this.ncells();
1475 var i;
1477 var i;
1476 for (i=0; i<ncells; i++) {
1478 for (i=0; i<ncells; i++) {
1477 // Always delete cell 0 as they get renumbered as they are deleted.
1479 // Always delete cell 0 as they get renumbered as they are deleted.
1478 this.delete_cell(0);
1480 this.delete_cell(0);
1479 };
1481 };
1480 // Save the metadata and name.
1482 // Save the metadata and name.
1481 this.metadata = data.metadata;
1483 this.metadata = data.metadata;
1482 this.notebook_name = data.metadata.name;
1484 this.notebook_name = data.metadata.name;
1483 // Only handle 1 worksheet for now.
1485 // Only handle 1 worksheet for now.
1484 var worksheet = data.worksheets[0];
1486 var worksheet = data.worksheets[0];
1485 if (worksheet !== undefined) {
1487 if (worksheet !== undefined) {
1486 if (worksheet.metadata) {
1488 if (worksheet.metadata) {
1487 this.worksheet_metadata = worksheet.metadata;
1489 this.worksheet_metadata = worksheet.metadata;
1488 }
1490 }
1489 var new_cells = worksheet.cells;
1491 var new_cells = worksheet.cells;
1490 ncells = new_cells.length;
1492 ncells = new_cells.length;
1491 var cell_data = null;
1493 var cell_data = null;
1492 var new_cell = null;
1494 var new_cell = null;
1493 for (i=0; i<ncells; i++) {
1495 for (i=0; i<ncells; i++) {
1494 cell_data = new_cells[i];
1496 cell_data = new_cells[i];
1495 // VERSIONHACK: plaintext -> raw
1497 // VERSIONHACK: plaintext -> raw
1496 // handle never-released plaintext name for raw cells
1498 // handle never-released plaintext name for raw cells
1497 if (cell_data.cell_type === 'plaintext'){
1499 if (cell_data.cell_type === 'plaintext'){
1498 cell_data.cell_type = 'raw';
1500 cell_data.cell_type = 'raw';
1499 }
1501 }
1500
1502
1501 new_cell = this.insert_cell_below(cell_data.cell_type);
1503 new_cell = this.insert_cell_below(cell_data.cell_type);
1502 new_cell.fromJSON(cell_data);
1504 new_cell.fromJSON(cell_data);
1503 };
1505 };
1504 };
1506 };
1505 if (data.worksheets.length > 1) {
1507 if (data.worksheets.length > 1) {
1506 var dialog = $('<div/>');
1508 var dialog = $('<div/>');
1507 dialog.html("This notebook has " + data.worksheets.length + " worksheets, " +
1509 dialog.html("This notebook has " + data.worksheets.length + " worksheets, " +
1508 "but this version of IPython can only handle the first. " +
1510 "but this version of IPython can only handle the first. " +
1509 "If you save this notebook, worksheets after the first will be lost."
1511 "If you save this notebook, worksheets after the first will be lost."
1510 );
1512 );
1511 this.element.append(dialog);
1513 this.element.append(dialog);
1512 dialog.dialog({
1514 dialog.dialog({
1513 resizable: false,
1515 resizable: false,
1514 modal: true,
1516 modal: true,
1515 title: "Multiple worksheets",
1517 title: "Multiple worksheets",
1516 closeText: "",
1518 closeText: "",
1517 close: function(event, ui) {$(this).dialog('destroy').remove();},
1519 close: function(event, ui) {$(this).dialog('destroy').remove();},
1518 buttons : {
1520 buttons : {
1519 "OK": function () {
1521 "OK": function () {
1520 $(this).dialog('close');
1522 $(this).dialog('close');
1521 }
1523 }
1522 },
1524 },
1523 width: 400
1525 width: 400
1524 });
1526 });
1525 }
1527 }
1526 };
1528 };
1527
1529
1528 /**
1530 /**
1529 * Dump this notebook into a JSON-friendly object.
1531 * Dump this notebook into a JSON-friendly object.
1530 *
1532 *
1531 * @method toJSON
1533 * @method toJSON
1532 * @return {Object} A JSON-friendly representation of this notebook.
1534 * @return {Object} A JSON-friendly representation of this notebook.
1533 */
1535 */
1534 Notebook.prototype.toJSON = function () {
1536 Notebook.prototype.toJSON = function () {
1535 var cells = this.get_cells();
1537 var cells = this.get_cells();
1536 var ncells = cells.length;
1538 var ncells = cells.length;
1537 var cell_array = new Array(ncells);
1539 var cell_array = new Array(ncells);
1538 for (var i=0; i<ncells; i++) {
1540 for (var i=0; i<ncells; i++) {
1539 cell_array[i] = cells[i].toJSON();
1541 cell_array[i] = cells[i].toJSON();
1540 };
1542 };
1541 var data = {
1543 var data = {
1542 // Only handle 1 worksheet for now.
1544 // Only handle 1 worksheet for now.
1543 worksheets : [{
1545 worksheets : [{
1544 cells: cell_array,
1546 cells: cell_array,
1545 metadata: this.worksheet_metadata
1547 metadata: this.worksheet_metadata
1546 }],
1548 }],
1547 metadata : this.metadata
1549 metadata : this.metadata
1548 };
1550 };
1549 return data;
1551 return data;
1550 };
1552 };
1551
1553
1552 /**
1554 /**
1553 * Save this notebook on the server.
1555 * Save this notebook on the server.
1554 *
1556 *
1555 * @method save_notebook
1557 * @method save_notebook
1556 */
1558 */
1557 Notebook.prototype.save_notebook = function () {
1559 Notebook.prototype.save_notebook = function () {
1558 // We may want to move the name/id/nbformat logic inside toJSON?
1560 // We may want to move the name/id/nbformat logic inside toJSON?
1559 var data = this.toJSON();
1561 var data = this.toJSON();
1560 data.metadata.name = this.notebook_name;
1562 data.metadata.name = this.notebook_name;
1561 data.nbformat = this.nbformat;
1563 data.nbformat = this.nbformat;
1562 data.nbformat_minor = this.nbformat_minor;
1564 data.nbformat_minor = this.nbformat_minor;
1563 // We do the call with settings so we can set cache to false.
1565 // We do the call with settings so we can set cache to false.
1564 var settings = {
1566 var settings = {
1565 processData : false,
1567 processData : false,
1566 cache : false,
1568 cache : false,
1567 type : "PUT",
1569 type : "PUT",
1568 data : JSON.stringify(data),
1570 data : JSON.stringify(data),
1569 headers : {'Content-Type': 'application/json'},
1571 headers : {'Content-Type': 'application/json'},
1570 success : $.proxy(this.save_notebook_success,this),
1572 success : $.proxy(this.save_notebook_success,this),
1571 error : $.proxy(this.save_notebook_error,this)
1573 error : $.proxy(this.save_notebook_error,this)
1572 };
1574 };
1573 $([IPython.events]).trigger('notebook_saving.Notebook');
1575 $([IPython.events]).trigger('notebook_saving.Notebook');
1574 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id;
1576 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id;
1575 $.ajax(url, settings);
1577 $.ajax(url, settings);
1576 };
1578 };
1577
1579
1578 /**
1580 /**
1579 * Success callback for saving a notebook.
1581 * Success callback for saving a notebook.
1580 *
1582 *
1581 * @method save_notebook_success
1583 * @method save_notebook_success
1582 * @param {Object} data JSON representation of a notebook
1584 * @param {Object} data JSON representation of a notebook
1583 * @param {String} status Description of response status
1585 * @param {String} status Description of response status
1584 * @param {jqXHR} xhr jQuery Ajax object
1586 * @param {jqXHR} xhr jQuery Ajax object
1585 */
1587 */
1586 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1588 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1587 this.dirty = false;
1589 this.dirty = false;
1588 $([IPython.events]).trigger('notebook_saved.Notebook');
1590 $([IPython.events]).trigger('notebook_saved.Notebook');
1591 if (this._checkpoint_after_save) {
1592 this.create_checkpoint();
1593 this._checkpoint_after_save = false;
1594 };
1589 };
1595 };
1590
1596
1591 /**
1597 /**
1592 * Failure callback for saving a notebook.
1598 * Failure callback for saving a notebook.
1593 *
1599 *
1594 * @method save_notebook_error
1600 * @method save_notebook_error
1595 * @param {jqXHR} xhr jQuery Ajax object
1601 * @param {jqXHR} xhr jQuery Ajax object
1596 * @param {String} status Description of response status
1602 * @param {String} status Description of response status
1597 * @param {String} error_msg HTTP error message
1603 * @param {String} error_msg HTTP error message
1598 */
1604 */
1599 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1605 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1600 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1606 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1601 };
1607 };
1602
1608
1603 /**
1609 /**
1604 * Request a notebook's data from the server.
1610 * Request a notebook's data from the server.
1605 *
1611 *
1606 * @method load_notebook
1612 * @method load_notebook
1607 * @param {String} notebook_id A notebook to load
1613 * @param {String} notebook_id A notebook to load
1608 */
1614 */
1609 Notebook.prototype.load_notebook = function (notebook_id) {
1615 Notebook.prototype.load_notebook = function (notebook_id) {
1610 var that = this;
1616 var that = this;
1611 this.notebook_id = notebook_id;
1617 this.notebook_id = notebook_id;
1612 // We do the call with settings so we can set cache to false.
1618 // We do the call with settings so we can set cache to false.
1613 var settings = {
1619 var settings = {
1614 processData : false,
1620 processData : false,
1615 cache : false,
1621 cache : false,
1616 type : "GET",
1622 type : "GET",
1617 dataType : "json",
1623 dataType : "json",
1618 success : $.proxy(this.load_notebook_success,this),
1624 success : $.proxy(this.load_notebook_success,this),
1619 error : $.proxy(this.load_notebook_error,this),
1625 error : $.proxy(this.load_notebook_error,this),
1620 };
1626 };
1621 $([IPython.events]).trigger('notebook_loading.Notebook');
1627 $([IPython.events]).trigger('notebook_loading.Notebook');
1622 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id;
1628 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id;
1623 $.ajax(url, settings);
1629 $.ajax(url, settings);
1624 };
1630 };
1625
1631
1626 /**
1632 /**
1627 * Success callback for loading a notebook from the server.
1633 * Success callback for loading a notebook from the server.
1628 *
1634 *
1629 * Load notebook data from the JSON response.
1635 * Load notebook data from the JSON response.
1630 *
1636 *
1631 * @method load_notebook_success
1637 * @method load_notebook_success
1632 * @param {Object} data JSON representation of a notebook
1638 * @param {Object} data JSON representation of a notebook
1633 * @param {String} status Description of response status
1639 * @param {String} status Description of response status
1634 * @param {jqXHR} xhr jQuery Ajax object
1640 * @param {jqXHR} xhr jQuery Ajax object
1635 */
1641 */
1636 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1642 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1637 this.fromJSON(data);
1643 this.fromJSON(data);
1638 if (this.ncells() === 0) {
1644 if (this.ncells() === 0) {
1639 this.insert_cell_below('code');
1645 this.insert_cell_below('code');
1640 };
1646 };
1641 this.dirty = false;
1647 this.dirty = false;
1642 this.select(0);
1648 this.select(0);
1643 this.scroll_to_top();
1649 this.scroll_to_top();
1644 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1650 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1645 msg = "This notebook has been converted from an older " +
1651 msg = "This notebook has been converted from an older " +
1646 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1652 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1647 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1653 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1648 "newer notebook format will be used and older verions of IPython " +
1654 "newer notebook format will be used and older verions of IPython " +
1649 "may not be able to read it. To keep the older version, close the " +
1655 "may not be able to read it. To keep the older version, close the " +
1650 "notebook without saving it.";
1656 "notebook without saving it.";
1651 var dialog = $('<div/>');
1657 var dialog = $('<div/>');
1652 dialog.html(msg);
1658 dialog.html(msg);
1653 this.element.append(dialog);
1659 this.element.append(dialog);
1654 dialog.dialog({
1660 dialog.dialog({
1655 resizable: false,
1661 resizable: false,
1656 modal: true,
1662 modal: true,
1657 title: "Notebook converted",
1663 title: "Notebook converted",
1658 closeText: "",
1664 closeText: "",
1659 close: function(event, ui) {$(this).dialog('destroy').remove();},
1665 close: function(event, ui) {$(this).dialog('destroy').remove();},
1660 buttons : {
1666 buttons : {
1661 "OK": function () {
1667 "OK": function () {
1662 $(this).dialog('close');
1668 $(this).dialog('close');
1663 }
1669 }
1664 },
1670 },
1665 width: 400
1671 width: 400
1666 });
1672 });
1667 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1673 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1668 var that = this;
1674 var that = this;
1669 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1675 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1670 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1676 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1671 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1677 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1672 this_vs + ". You can still work with this notebook, but some features " +
1678 this_vs + ". You can still work with this notebook, but some features " +
1673 "introduced in later notebook versions may not be available."
1679 "introduced in later notebook versions may not be available."
1674
1680
1675 var dialog = $('<div/>');
1681 var dialog = $('<div/>');
1676 dialog.html(msg);
1682 dialog.html(msg);
1677 this.element.append(dialog);
1683 this.element.append(dialog);
1678 dialog.dialog({
1684 dialog.dialog({
1679 resizable: false,
1685 resizable: false,
1680 modal: true,
1686 modal: true,
1681 title: "Newer Notebook",
1687 title: "Newer Notebook",
1682 closeText: "",
1688 closeText: "",
1683 close: function(event, ui) {$(this).dialog('destroy').remove();},
1689 close: function(event, ui) {$(this).dialog('destroy').remove();},
1684 buttons : {
1690 buttons : {
1685 "OK": function () {
1691 "OK": function () {
1686 $(this).dialog('close');
1692 $(this).dialog('close');
1687 }
1693 }
1688 },
1694 },
1689 width: 400
1695 width: 400
1690 });
1696 });
1691
1697
1692 }
1698 }
1693 // Create the kernel after the notebook is completely loaded to prevent
1699 // Create the kernel after the notebook is completely loaded to prevent
1694 // code execution upon loading, which is a security risk.
1700 // code execution upon loading, which is a security risk.
1695 if (! this.read_only) {
1701 if (! this.read_only) {
1696 this.start_kernel();
1702 this.start_kernel();
1697 }
1703 }
1698 $([IPython.events]).trigger('notebook_loaded.Notebook');
1704 $([IPython.events]).trigger('notebook_loaded.Notebook');
1699 };
1705 };
1700
1706
1701 /**
1707 /**
1702 * Failure callback for loading a notebook from the server.
1708 * Failure callback for loading a notebook from the server.
1703 *
1709 *
1704 * @method load_notebook_error
1710 * @method load_notebook_error
1705 * @param {jqXHR} xhr jQuery Ajax object
1711 * @param {jqXHR} xhr jQuery Ajax object
1706 * @param {String} textStatus Description of response status
1712 * @param {String} textStatus Description of response status
1707 * @param {String} errorThrow HTTP error message
1713 * @param {String} errorThrow HTTP error message
1708 */
1714 */
1709 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1715 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1710 if (xhr.status === 500) {
1716 if (xhr.status === 500) {
1711 var msg = "An error occurred while loading this notebook. Most likely " +
1717 var msg = "An error occurred while loading this notebook. Most likely " +
1712 "this notebook is in a newer format than is supported by this " +
1718 "this notebook is in a newer format than is supported by this " +
1713 "version of IPython. This version can load notebook formats " +
1719 "version of IPython. This version can load notebook formats " +
1714 "v"+this.nbformat+" or earlier.";
1720 "v"+this.nbformat+" or earlier.";
1715 var dialog = $('<div/>');
1721 var dialog = $('<div/>');
1716 dialog.html(msg);
1722 dialog.html(msg);
1717 this.element.append(dialog);
1723 this.element.append(dialog);
1718 dialog.dialog({
1724 dialog.dialog({
1719 resizable: false,
1725 resizable: false,
1720 modal: true,
1726 modal: true,
1721 title: "Error loading notebook",
1727 title: "Error loading notebook",
1722 closeText: "",
1728 closeText: "",
1723 close: function(event, ui) {$(this).dialog('destroy').remove();},
1729 close: function(event, ui) {$(this).dialog('destroy').remove();},
1724 buttons : {
1730 buttons : {
1725 "OK": function () {
1731 "OK": function () {
1726 $(this).dialog('close');
1732 $(this).dialog('close');
1727 }
1733 }
1728 },
1734 },
1729 width: 400
1735 width: 400
1730 });
1736 });
1731 }
1737 }
1732 }
1738 }
1733
1739
1740 /********************* checkpoint-related *********************/
1741
1742 /**
1743 * Save the notebook then immediately create a checkpoint.
1744 *
1745 * @method save_checkpoint
1746 */
1747 Notebook.prototype.save_checkpoint = function () {
1748 this._checkpoint_after_save = true;
1749 this.save_notebook();
1750 };
1751
1752 /**
1753 * List checkpoints for this notebook.
1754 *
1755 * @method list_checkpoint
1756 */
1757 Notebook.prototype.list_checkpoints = function () {
1758 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints';
1759 $.get(url).done(
1760 $.proxy(this.list_checkpoints_success, this)
1761 ).fail(
1762 $.proxy(this.list_checkpoints_error, this)
1763 );
1764 };
1765
1766 /**
1767 * Success callback for listing checkpoints.
1768 *
1769 * @method list_checkpoint_success
1770 * @param {Object} data JSON representation of a checkpoint
1771 * @param {String} status Description of response status
1772 * @param {jqXHR} xhr jQuery Ajax object
1773 */
1774 Notebook.prototype.list_checkpoints_success = function (data, status, xhr) {
1775 var data = $.parseJSON(data);
1776 if (data.length) {
1777 this.last_checkpoint = data[0];
1778 } else {
1779 this.last_checkpoint = null;
1780 }
1781 $([IPython.events]).trigger('checkpoints_listed.Notebook', data);
1782 };
1783
1784 /**
1785 * Failure callback for listing a checkpoint.
1786 *
1787 * @method list_checkpoint_error
1788 * @param {jqXHR} xhr jQuery Ajax object
1789 * @param {String} status Description of response status
1790 * @param {String} error_msg HTTP error message
1791 */
1792 Notebook.prototype.list_checkpoints_error = function (xhr, status, error_msg) {
1793 $([IPython.events]).trigger('list_checkpoints_failed.Notebook');
1794 };
1795
1796 /**
1797 * Create a checkpoint of this notebook on the server from the most recent save.
1798 *
1799 * @method create_checkpoint
1800 */
1801 Notebook.prototype.create_checkpoint = function () {
1802 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints';
1803 $.post(url).done(
1804 $.proxy(this.create_checkpoint_success, this)
1805 ).fail(
1806 $.proxy(this.create_checkpoint_error, this)
1807 );
1808 };
1809
1810 /**
1811 * Success callback for creating a checkpoint.
1812 *
1813 * @method create_checkpoint_success
1814 * @param {Object} data JSON representation of a checkpoint
1815 * @param {String} status Description of response status
1816 * @param {jqXHR} xhr jQuery Ajax object
1817 */
1818 Notebook.prototype.create_checkpoint_success = function (data, status, xhr) {
1819 var data = $.parseJSON(data);
1820 this.last_checkpoint = data;
1821 $([IPython.events]).trigger('checkpoint_created.Notebook', data);
1822 };
1823
1824 /**
1825 * Failure callback for creating a checkpoint.
1826 *
1827 * @method create_checkpoint_error
1828 * @param {jqXHR} xhr jQuery Ajax object
1829 * @param {String} status Description of response status
1830 * @param {String} error_msg HTTP error message
1831 */
1832 Notebook.prototype.create_checkpoint_error = function (xhr, status, error_msg) {
1833 $([IPython.events]).trigger('checkpoint_failed.Notebook');
1834 };
1835
1836 Notebook.prototype.restore_checkpoint_dialog = function () {
1837 var that = this;
1838 var checkpoint = this.last_checkpoint;
1839 if ( ! checkpoint ) {
1840 console.log("restore dialog, but no checkpoint to restore to!");
1841 return;
1842 }
1843 var dialog = $('<div/>').append(
1844 $('<p/>').text("Are you sure you want to revert the notebook to " +
1845 "the latest checkpoint?"
1846 ).append(
1847 $("<strong/>").text(
1848 " This cannot be undone."
1849 )
1850 )
1851 ).append(
1852 $('<p/>').text("The checkpoint was last updated at")
1853 ).append(
1854 $('<p/>').text(Date(checkpoint.last_modified))
1855 );
1856
1857 $(document).append(dialog);
1858
1859 dialog.dialog({
1860 resizable: false,
1861 modal: true,
1862 title: "Revert notebook to checkpoint",
1863 closeText: '',
1864 buttons : {
1865 "Revert": function () {
1866 that.restore_checkpoint(checkpoint.checkpoint_id);
1867 $(this).dialog('close');
1868 },
1869 "Cancel": function () {
1870 $(this).dialog('close');
1871 }
1872 },
1873 width: 400
1874 });
1875 }
1876
1877 /**
1878 * Restore the notebook to a checkpoint state.
1879 *
1880 * @method restore_checkpoint
1881 * @param {String} checkpoint ID
1882 */
1883 Notebook.prototype.restore_checkpoint = function (checkpoint) {
1884 $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint);
1885 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints/' + checkpoint;
1886 $.post(url).done(
1887 $.proxy(this.restore_checkpoint_success, this)
1888 ).fail(
1889 $.proxy(this.restore_checkpoint_error, this)
1890 );
1891 };
1892
1893 /**
1894 * Success callback for restoring a notebook to a checkpoint.
1895 *
1896 * @method restore_checkpoint_success
1897 * @param {Object} data (ignored, should be empty)
1898 * @param {String} status Description of response status
1899 * @param {jqXHR} xhr jQuery Ajax object
1900 */
1901 Notebook.prototype.restore_checkpoint_success = function (data, status, xhr) {
1902 $([IPython.events]).trigger('checkpoint_restored.Notebook');
1903 this.load_notebook(this.notebook_id);
1904 };
1905
1906 /**
1907 * Failure callback for restoring a notebook to a checkpoint.
1908 *
1909 * @method restore_checkpoint_error
1910 * @param {jqXHR} xhr jQuery Ajax object
1911 * @param {String} status Description of response status
1912 * @param {String} error_msg HTTP error message
1913 */
1914 Notebook.prototype.restore_checkpoint_error = function (xhr, status, error_msg) {
1915 $([IPython.events]).trigger('checkpoint_restore_failed.Notebook');
1916 };
1917
1918 /**
1919 * Delete a notebook checkpoint.
1920 *
1921 * @method delete_checkpoint
1922 * @param {String} checkpoint ID
1923 */
1924 Notebook.prototype.delete_checkpoint = function (checkpoint) {
1925 $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint);
1926 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints/' + checkpoint;
1927 $.ajax(url, {
1928 type: 'DELETE',
1929 success: $.proxy(this.delete_checkpoint_success, this),
1930 error: $.proxy(this.delete_notebook_error,this)
1931 });
1932 };
1933
1934 /**
1935 * Success callback for deleting a notebook checkpoint
1936 *
1937 * @method delete_checkpoint_success
1938 * @param {Object} data (ignored, should be empty)
1939 * @param {String} status Description of response status
1940 * @param {jqXHR} xhr jQuery Ajax object
1941 */
1942 Notebook.prototype.delete_checkpoint_success = function (data, status, xhr) {
1943 $([IPython.events]).trigger('checkpoint_deleted.Notebook', data);
1944 this.load_notebook(this.notebook_id);
1945 };
1946
1947 /**
1948 * Failure callback for deleting a notebook checkpoint.
1949 *
1950 * @method delete_checkpoint_error
1951 * @param {jqXHR} xhr jQuery Ajax object
1952 * @param {String} status Description of response status
1953 * @param {String} error_msg HTTP error message
1954 */
1955 Notebook.prototype.delete_checkpoint_error = function (xhr, status, error_msg) {
1956 $([IPython.events]).trigger('checkpoint_delete_failed.Notebook');
1957 };
1958
1959
1734 IPython.Notebook = Notebook;
1960 IPython.Notebook = Notebook;
1735
1961
1736
1962
1737 return IPython;
1963 return IPython;
1738
1964
1739 }(IPython));
1965 }(IPython));
1740
1966
@@ -1,139 +1,139
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // SaveWidget
9 // SaveWidget
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15
15
16 var SaveWidget = function (selector) {
16 var SaveWidget = function (selector) {
17 this.selector = selector;
17 this.selector = selector;
18 if (this.selector !== undefined) {
18 if (this.selector !== undefined) {
19 this.element = $(selector);
19 this.element = $(selector);
20 this.style();
20 this.style();
21 this.bind_events();
21 this.bind_events();
22 }
22 }
23 };
23 };
24
24
25
25
26 SaveWidget.prototype.style = function () {
26 SaveWidget.prototype.style = function () {
27 this.element.find('span#save_widget').addClass('ui-widget');
27 this.element.find('span#save_widget').addClass('ui-widget');
28 this.element.find('span#notebook_name').addClass('ui-widget');
28 this.element.find('span#notebook_name').addClass('ui-widget');
29 this.element.find('span#save_status').addClass('ui-widget')
29 this.element.find('span#save_status').addClass('ui-widget')
30 .css({border: 'none', 'margin-left': '20px'});
30 .css({border: 'none', 'margin-left': '20px'});
31 };
31 };
32
32
33
33
34 SaveWidget.prototype.bind_events = function () {
34 SaveWidget.prototype.bind_events = function () {
35 var that = this;
35 var that = this;
36 this.element.find('span#notebook_name').click(function () {
36 this.element.find('span#notebook_name').click(function () {
37 that.rename_notebook();
37 that.rename_notebook();
38 });
38 });
39 this.element.find('span#notebook_name').hover(function () {
39 this.element.find('span#notebook_name').hover(function () {
40 $(this).addClass("ui-state-hover");
40 $(this).addClass("ui-state-hover");
41 }, function () {
41 }, function () {
42 $(this).removeClass("ui-state-hover");
42 $(this).removeClass("ui-state-hover");
43 });
43 });
44 $([IPython.events]).on('notebook_loaded.Notebook', function () {
44 $([IPython.events]).on('notebook_loaded.Notebook', function () {
45 that.set_last_saved();
45 that.set_last_saved();
46 that.update_notebook_name();
46 that.update_notebook_name();
47 that.update_document_title();
47 that.update_document_title();
48 });
48 });
49 $([IPython.events]).on('notebook_saved.Notebook', function () {
49 $([IPython.events]).on('notebook_saved.Notebook', function () {
50 that.set_last_saved();
50 that.set_last_saved();
51 that.update_notebook_name();
51 that.update_notebook_name();
52 that.update_document_title();
52 that.update_document_title();
53 });
53 });
54 $([IPython.events]).on('notebook_save_failed.Notebook', function () {
54 $([IPython.events]).on('notebook_save_failed.Notebook', function () {
55 that.set_save_status('Last Save Failed!');
55 that.set_save_status('Last Save Failed!');
56 });
56 });
57 };
57 };
58
58
59
59
60 SaveWidget.prototype.rename_notebook = function () {
60 SaveWidget.prototype.rename_notebook = function () {
61 var that = this;
61 var that = this;
62 var dialog = $('<div/>');
62 var dialog = $('<div/>');
63 dialog.append(
63 dialog.append(
64 $('<p/>').html('Enter a new notebook name:')
64 $('<p/>').html('Enter a new notebook name:')
65 .css({'margin-bottom': '10px'})
65 .css({'margin-bottom': '10px'})
66 );
66 );
67 dialog.append(
67 dialog.append(
68 $('<input/>').attr('type','text').attr('size','25')
68 $('<input/>').attr('type','text').attr('size','25')
69 .addClass('ui-widget ui-widget-content')
69 .addClass('ui-widget ui-widget-content')
70 .attr('value',IPython.notebook.get_notebook_name())
70 .attr('value',IPython.notebook.get_notebook_name())
71 );
71 );
72 // $(document).append(dialog);
72 // $(document).append(dialog);
73 dialog.dialog({
73 dialog.dialog({
74 resizable: false,
74 resizable: false,
75 modal: true,
75 modal: true,
76 title: "Rename Notebook",
76 title: "Rename Notebook",
77 closeText: "",
77 closeText: "",
78 close: function(event, ui) {$(this).dialog('destroy').remove();},
78 close: function(event, ui) {$(this).dialog('destroy').remove();},
79 buttons : {
79 buttons : {
80 "OK": function () {
80 "OK": function () {
81 var new_name = $(this).find('input').attr('value');
81 var new_name = $(this).find('input').attr('value');
82 if (!IPython.notebook.test_notebook_name(new_name)) {
82 if (!IPython.notebook.test_notebook_name(new_name)) {
83 $(this).find('h3').html(
83 $(this).find('h3').html(
84 "Invalid notebook name. Notebook names must "+
84 "Invalid notebook name. Notebook names must "+
85 "have 1 or more characters and can contain any characters " +
85 "have 1 or more characters and can contain any characters " +
86 "except :/\\. Please enter a new notebook name:"
86 "except :/\\. Please enter a new notebook name:"
87 );
87 );
88 } else {
88 } else {
89 IPython.notebook.set_notebook_name(new_name);
89 IPython.notebook.set_notebook_name(new_name);
90 IPython.notebook.save_notebook();
90 IPython.notebook.save_checkpoint();
91 $(this).dialog('close');
91 $(this).dialog('close');
92 }
92 }
93 },
93 },
94 "Cancel": function () {
94 "Cancel": function () {
95 $(this).dialog('close');
95 $(this).dialog('close');
96 }
96 }
97 },
97 },
98 open : function (event, ui) {
98 open : function (event, ui) {
99 var that = $(this);
99 var that = $(this);
100 // Upon ENTER, click the OK button.
100 // Upon ENTER, click the OK button.
101 that.find('input[type="text"]').keydown(function (event, ui) {
101 that.find('input[type="text"]').keydown(function (event, ui) {
102 if (event.which === utils.keycodes.ENTER) {
102 if (event.which === utils.keycodes.ENTER) {
103 that.parent().find('button').first().click();
103 that.parent().find('button').first().click();
104 }
104 }
105 });
105 });
106 }
106 }
107 });
107 });
108 }
108 }
109
109
110
110
111 SaveWidget.prototype.update_notebook_name = function () {
111 SaveWidget.prototype.update_notebook_name = function () {
112 var nbname = IPython.notebook.get_notebook_name();
112 var nbname = IPython.notebook.get_notebook_name();
113 this.element.find('span#notebook_name').html(nbname);
113 this.element.find('span#notebook_name').html(nbname);
114 };
114 };
115
115
116
116
117 SaveWidget.prototype.update_document_title = function () {
117 SaveWidget.prototype.update_document_title = function () {
118 var nbname = IPython.notebook.get_notebook_name();
118 var nbname = IPython.notebook.get_notebook_name();
119 document.title = nbname;
119 document.title = nbname;
120 };
120 };
121
121
122
122
123 SaveWidget.prototype.set_save_status = function (msg) {
123 SaveWidget.prototype.set_save_status = function (msg) {
124 this.element.find('span#save_status').html(msg);
124 this.element.find('span#save_status').html(msg);
125 }
125 }
126
126
127
127
128 SaveWidget.prototype.set_last_saved = function () {
128 SaveWidget.prototype.set_last_saved = function () {
129 var d = new Date();
129 var d = new Date();
130 this.set_save_status('Last saved: '+d.format('mmm dd HH:MM'));
130 this.set_save_status('Last saved: '+d.format('mmm dd HH:MM'));
131 };
131 };
132
132
133
133
134 IPython.SaveWidget = SaveWidget;
134 IPython.SaveWidget = SaveWidget;
135
135
136 return IPython;
136 return IPython;
137
137
138 }(IPython));
138 }(IPython));
139
139
@@ -1,232 +1,233
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/codemirror/lib/codemirror.css") }}">
14 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
15 <link rel="stylesheet" href="{{ static_url("css/codemirror-ipython.css") }}">
15 <link rel="stylesheet" href="{{ static_url("css/codemirror-ipython.css") }}">
16
16
17 <link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
17 <link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
18
18
19 <link rel="stylesheet" href="{{ static_url("css/celltoolbar.css") }}" type="text/css" />
19 <link rel="stylesheet" href="{{ static_url("css/celltoolbar.css") }}" type="text/css" />
20
20
21 {{super()}}
21 {{super()}}
22
22
23 {% endblock %}
23 {% endblock %}
24
24
25 {% block params %}
25 {% block params %}
26
26
27 data-project={{project}}
27 data-project={{project}}
28 data-base-project-url={{base_project_url}}
28 data-base-project-url={{base_project_url}}
29 data-base-kernel-url={{base_kernel_url}}
29 data-base-kernel-url={{base_kernel_url}}
30 data-read-only={{read_only and not logged_in}}
30 data-read-only={{read_only and not logged_in}}
31 data-notebook-id={{notebook_id}}
31 data-notebook-id={{notebook_id}}
32 class="notebook_app"
32 class="notebook_app"
33
33
34 {% endblock %}
34 {% endblock %}
35
35
36
36
37 {% block header %}
37 {% block header %}
38
38
39 <span id="save_widget">
39 <span id="save_widget">
40 <span id="notebook_name"></span>
40 <span id="notebook_name"></span>
41 <span id="save_status"></span>
41 <span id="save_status"></span>
42 </span>
42 </span>
43
43
44 {% endblock %}
44 {% endblock %}
45
45
46
46
47 {% block site %}
47 {% block site %}
48
48
49 <div id="menubar_container">
49 <div id="menubar_container">
50 <div id="menubar">
50 <div id="menubar">
51 <ul id="menus">
51 <ul id="menus">
52 <li><a href="#">File</a>
52 <li><a href="#">File</a>
53 <ul>
53 <ul>
54 <li id="new_notebook"><a href="#">New</a></li>
54 <li id="new_notebook"><a href="#">New</a></li>
55 <li id="open_notebook"><a href="#">Open...</a></li>
55 <li id="open_notebook"><a href="#">Open...</a></li>
56 <hr/>
56 <hr/>
57 <li id="copy_notebook"><a href="#">Make a Copy...</a></li>
57 <li id="copy_notebook"><a href="#">Make a Copy...</a></li>
58 <li id="rename_notebook"><a href="#">Rename...</a></li>
58 <li id="rename_notebook"><a href="#">Rename...</a></li>
59 <li id="save_notebook"><a href="#">Save</a></li>
59 <li id="save_notebook"><a href="#">Save</a></li>
60 <li id="save_checkpoint"><a href="#">Save Checkpoint</a></li>
60 <hr/>
61 <hr/>
61 <li><a href="#">Download as</a>
62 <li><a href="#">Download as</a>
62 <ul>
63 <ul>
63 <li id="download_ipynb"><a href="#">IPython (.ipynb)</a></li>
64 <li id="download_ipynb"><a href="#">IPython (.ipynb)</a></li>
64 <li id="download_py"><a href="#">Python (.py)</a></li>
65 <li id="download_py"><a href="#">Python (.py)</a></li>
65 </ul>
66 </ul>
66 </li>
67 </li>
67 <!--<hr/>
68 <!--<hr/>
68 <li id="print_notebook"><a href="/{{notebook_id}}/print" target="_blank">Print View</a></li>-->
69 <li id="print_notebook"><a href="/{{notebook_id}}/print" target="_blank">Print View</a></li>-->
69 <hr/>
70 <hr/>
70 <li id="kill_and_exit"><a href="#" >Close and halt</a></li>
71 <li id="kill_and_exit"><a href="#" >Close and halt</a></li>
71 </ul>
72 </ul>
72 </li>
73 </li>
73 <li><a href="#">Edit</a>
74 <li><a href="#">Edit</a>
74 <ul>
75 <ul>
75 <li id="cut_cell"><a href="#">Cut Cell</a></li>
76 <li id="cut_cell"><a href="#">Cut Cell</a></li>
76 <li id="copy_cell"><a href="#">Copy Cell</a></li>
77 <li id="copy_cell"><a href="#">Copy Cell</a></li>
77 <li id="paste_cell_above" class="ui-state-disabled"><a href="#">Paste Cell Above</a></li>
78 <li id="paste_cell_above" class="ui-state-disabled"><a href="#">Paste Cell Above</a></li>
78 <li id="paste_cell_below" class="ui-state-disabled"><a href="#">Paste Cell Below</a></li>
79 <li id="paste_cell_below" class="ui-state-disabled"><a href="#">Paste Cell Below</a></li>
79 <li id="paste_cell_replace" class="ui-state-disabled"><a href="#">Paste Cell &amp; Replace</a></li>
80 <li id="paste_cell_replace" class="ui-state-disabled"><a href="#">Paste Cell &amp; Replace</a></li>
80 <li id="delete_cell"><a href="#">Delete Cell</a></li>
81 <li id="delete_cell"><a href="#">Delete Cell</a></li>
81 <li id="undelete_cell" class="ui-state-disabled"><a href="#">Undo Delete Cell</a></li>
82 <li id="undelete_cell" class="ui-state-disabled"><a href="#">Undo Delete Cell</a></li>
82 <hr/>
83 <hr/>
83 <li id="split_cell"><a href="#">Split Cell</a></li>
84 <li id="split_cell"><a href="#">Split Cell</a></li>
84 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
85 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
85 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
86 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
86 <hr/>
87 <hr/>
87 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
88 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
88 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
89 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
89 <hr/>
90 <hr/>
90 <li id="select_previous"><a href="#">Select Previous Cell</a></li>
91 <li id="select_previous"><a href="#">Select Previous Cell</a></li>
91 <li id="select_next"><a href="#">Select Next Cell</a></li>
92 <li id="select_next"><a href="#">Select Next Cell</a></li>
92 </ul>
93 </ul>
93 </li>
94 </li>
94 <li><a href="#">View</a>
95 <li><a href="#">View</a>
95 <ul>
96 <ul>
96 <li id="toggle_header"><a href="#">Toggle Header</a></li>
97 <li id="toggle_header"><a href="#">Toggle Header</a></li>
97 <li id="toggle_toolbar"><a href="#">Toggle Toolbar</a></li>
98 <li id="toggle_toolbar"><a href="#">Toggle Toolbar</a></li>
98 </ul>
99 </ul>
99 </li>
100 </li>
100 <li><a href="#">Insert</a>
101 <li><a href="#">Insert</a>
101 <ul>
102 <ul>
102 <li id="insert_cell_above"><a href="#">Insert Cell Above</a></li>
103 <li id="insert_cell_above"><a href="#">Insert Cell Above</a></li>
103 <li id="insert_cell_below"><a href="#">Insert Cell Below</a></li>
104 <li id="insert_cell_below"><a href="#">Insert Cell Below</a></li>
104 </ul>
105 </ul>
105 </li>
106 </li>
106 <li><a href="#">Cell</a>
107 <li><a href="#">Cell</a>
107 <ul>
108 <ul>
108 <li id="run_cell"><a href="#">Run</a></li>
109 <li id="run_cell"><a href="#">Run</a></li>
109 <li id="run_cell_in_place"><a href="#">Run in Place</a></li>
110 <li id="run_cell_in_place"><a href="#">Run in Place</a></li>
110 <li id="run_all_cells"><a href="#">Run All</a></li>
111 <li id="run_all_cells"><a href="#">Run All</a></li>
111 <li id="run_all_cells_above"><a href="#">Run All Above</a></li>
112 <li id="run_all_cells_above"><a href="#">Run All Above</a></li>
112 <li id="run_all_cells_below"><a href="#">Run All Below</a></li>
113 <li id="run_all_cells_below"><a href="#">Run All Below</a></li>
113 <hr/>
114 <hr/>
114 <li id="to_code"><a href="#">Code</a></li>
115 <li id="to_code"><a href="#">Code</a></li>
115 <li id="to_markdown"><a href="#">Markdown </a></li>
116 <li id="to_markdown"><a href="#">Markdown </a></li>
116 <li id="to_raw"><a href="#">Raw Text</a></li>
117 <li id="to_raw"><a href="#">Raw Text</a></li>
117 <li id="to_heading1"><a href="#">Heading 1</a></li>
118 <li id="to_heading1"><a href="#">Heading 1</a></li>
118 <li id="to_heading2"><a href="#">Heading 2</a></li>
119 <li id="to_heading2"><a href="#">Heading 2</a></li>
119 <li id="to_heading3"><a href="#">Heading 3</a></li>
120 <li id="to_heading3"><a href="#">Heading 3</a></li>
120 <li id="to_heading4"><a href="#">Heading 4</a></li>
121 <li id="to_heading4"><a href="#">Heading 4</a></li>
121 <li id="to_heading5"><a href="#">Heading 5</a></li>
122 <li id="to_heading5"><a href="#">Heading 5</a></li>
122 <li id="to_heading6"><a href="#">Heading 6</a></li>
123 <li id="to_heading6"><a href="#">Heading 6</a></li>
123 <hr/>
124 <hr/>
124 <li id="toggle_output"><a href="#">Toggle Current Output</a></li>
125 <li id="toggle_output"><a href="#">Toggle Current Output</a></li>
125 <li id="all_outputs"><a href="#">All Output</a>
126 <li id="all_outputs"><a href="#">All Output</a>
126 <ul>
127 <ul>
127 <li id="expand_all_output"><a href="#">Expand</a></li>
128 <li id="expand_all_output"><a href="#">Expand</a></li>
128 <li id="scroll_all_output"><a href="#">Scroll Long</a></li>
129 <li id="scroll_all_output"><a href="#">Scroll Long</a></li>
129 <li id="collapse_all_output"><a href="#">Collapse</a></li>
130 <li id="collapse_all_output"><a href="#">Collapse</a></li>
130 <li id="clear_all_output"><a href="#">Clear</a></li>
131 <li id="clear_all_output"><a href="#">Clear</a></li>
131 </ul>
132 </ul>
132 </li>
133 </li>
133 </ul>
134 </ul>
134 </li>
135 </li>
135 <li><a href="#">Kernel</a>
136 <li><a href="#">Kernel</a>
136 <ul>
137 <ul>
137 <li id="int_kernel"><a href="#">Interrupt</a></li>
138 <li id="int_kernel"><a href="#">Interrupt</a></li>
138 <li id="restart_kernel"><a href="#">Restart</a></li>
139 <li id="restart_kernel"><a href="#">Restart</a></li>
139 </ul>
140 </ul>
140 </li>
141 </li>
141 <li><a href="#">Help</a>
142 <li><a href="#">Help</a>
142 <ul>
143 <ul>
143 <li><a href="http://ipython.org/documentation.html" target="_blank">IPython Help</a></li>
144 <li><a href="http://ipython.org/documentation.html" target="_blank">IPython Help</a></li>
144 <li><a href="http://ipython.org/ipython-doc/stable/interactive/htmlnotebook.html" target="_blank">Notebook Help</a></li>
145 <li><a href="http://ipython.org/ipython-doc/stable/interactive/htmlnotebook.html" target="_blank">Notebook Help</a></li>
145 <li id="keyboard_shortcuts"><a href="#">Keyboard Shortcuts</a></li>
146 <li id="keyboard_shortcuts"><a href="#">Keyboard Shortcuts</a></li>
146 <hr/>
147 <hr/>
147 <li><a href="http://docs.python.org" target="_blank">Python</a></li>
148 <li><a href="http://docs.python.org" target="_blank">Python</a></li>
148 <li><a href="http://docs.scipy.org/doc/numpy/reference/" target="_blank">NumPy</a></li>
149 <li><a href="http://docs.scipy.org/doc/numpy/reference/" target="_blank">NumPy</a></li>
149 <li><a href="http://docs.scipy.org/doc/scipy/reference/" target="_blank">SciPy</a></li>
150 <li><a href="http://docs.scipy.org/doc/scipy/reference/" target="_blank">SciPy</a></li>
150 <li><a href="http://docs.sympy.org/dev/index.html" target="_blank">SymPy</a></li>
151 <li><a href="http://docs.sympy.org/dev/index.html" target="_blank">SymPy</a></li>
151 <li><a href="http://matplotlib.sourceforge.net/" target="_blank">Matplotlib</a></li>
152 <li><a href="http://matplotlib.sourceforge.net/" target="_blank">Matplotlib</a></li>
152 </ul>
153 </ul>
153 </li>
154 </li>
154 </ul>
155 </ul>
155
156
156 </div>
157 </div>
157 <div id="notification_area">
158 <div id="notification_area">
158 </div>
159 </div>
159 </div>
160 </div>
160
161
161
162
162 <div id="maintoolbar"></div>
163 <div id="maintoolbar"></div>
163
164
164 <div id="ipython-main-app">
165 <div id="ipython-main-app">
165
166
166 <div id="notebook_panel">
167 <div id="notebook_panel">
167 <div id="notebook"></div>
168 <div id="notebook"></div>
168 <div id="pager_splitter"></div>
169 <div id="pager_splitter"></div>
169 <div id="pager_container">
170 <div id="pager_container">
170 <div id='pager_button_area'>
171 <div id='pager_button_area'>
171 </div>
172 </div>
172 <div id="pager"></div>
173 <div id="pager"></div>
173 </div>
174 </div>
174 </div>
175 </div>
175
176
176 </div>
177 </div>
177 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
178 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
178
179
179
180
180 {% endblock %}
181 {% endblock %}
181
182
182
183
183 {% block script %}
184 {% block script %}
184
185
185 {{super()}}
186 {{super()}}
186
187
187 <script src="{{ static_url("components/codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
188 <script src="{{ static_url("components/codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
188 <script src="{{ static_url("components/codemirror/addon/mode/loadmode.js") }}" charset="utf-8"></script>
189 <script src="{{ static_url("components/codemirror/addon/mode/loadmode.js") }}" charset="utf-8"></script>
189 <script src="{{ static_url("components/codemirror/addon/mode/multiplex.js") }}" charset="utf-8"></script>
190 <script src="{{ static_url("components/codemirror/addon/mode/multiplex.js") }}" charset="utf-8"></script>
190 <script src="{{ static_url("js/codemirror-ipython.js") }}" charset="utf-8"></script>
191 <script src="{{ static_url("js/codemirror-ipython.js") }}" charset="utf-8"></script>
191 <script src="{{ static_url("components/codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
192 <script src="{{ static_url("components/codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
192 <script src="{{ static_url("components/codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
193 <script src="{{ static_url("components/codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
193 <script src="{{ static_url("components/codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
194 <script src="{{ static_url("components/codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
194 <script src="{{ static_url("components/codemirror/mode/css/css.js") }}" charset="utf-8"></script>
195 <script src="{{ static_url("components/codemirror/mode/css/css.js") }}" charset="utf-8"></script>
195 <script src="{{ static_url("components/codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
196 <script src="{{ static_url("components/codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
196 <script src="{{ static_url("components/codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
197 <script src="{{ static_url("components/codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
197
198
198 <script src="{{ static_url("pagedown/Markdown.Converter.js") }}" charset="utf-8"></script>
199 <script src="{{ static_url("pagedown/Markdown.Converter.js") }}" charset="utf-8"></script>
199
200
200 <script src="{{ static_url("prettify/prettify.js") }}" charset="utf-8"></script>
201 <script src="{{ static_url("prettify/prettify.js") }}" charset="utf-8"></script>
201 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
202 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
202
203
203 <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script>
204 <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script>
204 <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
205 <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
205 <script src="{{ static_url("js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
206 <script src="{{ static_url("js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
206 <script src="{{ static_url("js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
207 <script src="{{ static_url("js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
207 <script src="{{ static_url("js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
208 <script src="{{ static_url("js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
208 <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
209 <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
209 <script src="{{ static_url("js/celltoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
210 <script src="{{ static_url("js/celltoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
210 <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
211 <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
211 <script src="{{ static_url("js/completer.js") }}" type="text/javascript" charset="utf-8"></script>
212 <script src="{{ static_url("js/completer.js") }}" type="text/javascript" charset="utf-8"></script>
212 <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
213 <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
213 <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
214 <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
214 <script src="{{ static_url("js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
215 <script src="{{ static_url("js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
215 <script src="{{ static_url("js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
216 <script src="{{ static_url("js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
216 <script src="{{ static_url("js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
217 <script src="{{ static_url("js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
217 <script src="{{ static_url("js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
218 <script src="{{ static_url("js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
218 <script src="{{ static_url("js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
219 <script src="{{ static_url("js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
219 <script src="{{ static_url("js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
220 <script src="{{ static_url("js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
220 <script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
221 <script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
221 <script src="{{ static_url("js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
222 <script src="{{ static_url("js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
222 <script src="{{ static_url("js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
223 <script src="{{ static_url("js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
223 <script src="{{ static_url("js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>
224 <script src="{{ static_url("js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>
224 <script src="{{ static_url("js/config.js") }}" type="text/javascript" charset="utf-8"></script>
225 <script src="{{ static_url("js/config.js") }}" type="text/javascript" charset="utf-8"></script>
225 <script src="{{ static_url("js/notebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
226 <script src="{{ static_url("js/notebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
226
227
227 <script src="{{ static_url("js/contexthint.js") }}" charset="utf-8"></script>
228 <script src="{{ static_url("js/contexthint.js") }}" charset="utf-8"></script>
228
229
229 <script src="{{ static_url("js/celltoolbarpresets/default.js") }}" type="text/javascript" charset="utf-8"></script>
230 <script src="{{ static_url("js/celltoolbarpresets/default.js") }}" type="text/javascript" charset="utf-8"></script>
230 <script src="{{ static_url("js/celltoolbarpresets/slideshow.js") }}" type="text/javascript" charset="utf-8"></script>
231 <script src="{{ static_url("js/celltoolbarpresets/slideshow.js") }}" type="text/javascript" charset="utf-8"></script>
231
232
232 {% endblock %}
233 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now