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