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