##// END OF EJS Templates
Add comment re: quickhelp key.
David Warde-Farley -
Show More
@@ -1,1362 +1,1363
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 selected cell = v
145 // Paste selected cell = v
146 that.paste_cell();
146 that.paste_cell();
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 that.undelete();
265 that.undelete();
265 that.control_key_active = false;
266 that.control_key_active = false;
266 return false;
267 return false;
267 } else if (that.control_key_active) {
268 } else if (that.control_key_active) {
268 that.control_key_active = false;
269 that.control_key_active = false;
269 return true;
270 return true;
270 };
271 };
271 return true;
272 return true;
272 });
273 });
273
274
274 var collapse_time = function(time){
275 var collapse_time = function(time){
275 var app_height = $('div#main_app').height(); // content height
276 var app_height = $('div#main_app').height(); // content height
276 var splitter_height = $('div#pager_splitter').outerHeight(true);
277 var splitter_height = $('div#pager_splitter').outerHeight(true);
277 var new_height = app_height - splitter_height;
278 var new_height = app_height - splitter_height;
278 that.element.animate({height : new_height + 'px'}, time);
279 that.element.animate({height : new_height + 'px'}, time);
279 }
280 }
280
281
281 this.element.bind('collapse_pager', function (event,extrap) {
282 this.element.bind('collapse_pager', function (event,extrap) {
282 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
283 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
283 collapse_time(time);
284 collapse_time(time);
284 });
285 });
285
286
286 var expand_time = function(time) {
287 var expand_time = function(time) {
287 var app_height = $('div#main_app').height(); // content height
288 var app_height = $('div#main_app').height(); // content height
288 var splitter_height = $('div#pager_splitter').outerHeight(true);
289 var splitter_height = $('div#pager_splitter').outerHeight(true);
289 var pager_height = $('div#pager').outerHeight(true);
290 var pager_height = $('div#pager').outerHeight(true);
290 var new_height = app_height - pager_height - splitter_height;
291 var new_height = app_height - pager_height - splitter_height;
291 that.element.animate({height : new_height + 'px'}, time);
292 that.element.animate({height : new_height + 'px'}, time);
292 }
293 }
293
294
294 this.element.bind('expand_pager', function (event, extrap) {
295 this.element.bind('expand_pager', function (event, extrap) {
295 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
296 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
296 expand_time(time);
297 expand_time(time);
297 });
298 });
298
299
299 $(window).bind('beforeunload', function () {
300 $(window).bind('beforeunload', function () {
300 // TODO: Make killing the kernel configurable.
301 // TODO: Make killing the kernel configurable.
301 var kill_kernel = false;
302 var kill_kernel = false;
302 if (kill_kernel) {
303 if (kill_kernel) {
303 that.kernel.kill();
304 that.kernel.kill();
304 }
305 }
305 if (that.dirty && ! that.read_only) {
306 if (that.dirty && ! that.read_only) {
306 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.";
307 };
308 };
308 // 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
309 // pop up the "don't leave" dialog.
310 // pop up the "don't leave" dialog.
310 return null;
311 return null;
311 });
312 });
312 };
313 };
313
314
314 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
315 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
315 var cells = this.get_cells();
316 var cells = this.get_cells();
316 var time = time || 0;
317 var time = time || 0;
317 cell_number = Math.min(cells.length-1,cell_number);
318 cell_number = Math.min(cells.length-1,cell_number);
318 cell_number = Math.max(0 ,cell_number);
319 cell_number = Math.max(0 ,cell_number);
319 scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
320 scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
320 this.element.animate({scrollTop:scroll_value}, time);
321 this.element.animate({scrollTop:scroll_value}, time);
321 return scroll_value;
322 return scroll_value;
322 };
323 };
323
324
324
325
325 Notebook.prototype.scroll_to_bottom = function () {
326 Notebook.prototype.scroll_to_bottom = function () {
326 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
327 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
327 };
328 };
328
329
329
330
330 Notebook.prototype.scroll_to_top = function () {
331 Notebook.prototype.scroll_to_top = function () {
331 this.element.animate({scrollTop:0}, 0);
332 this.element.animate({scrollTop:0}, 0);
332 };
333 };
333
334
334
335
335 // Cell indexing, retrieval, etc.
336 // Cell indexing, retrieval, etc.
336
337
337 Notebook.prototype.get_cell_elements = function () {
338 Notebook.prototype.get_cell_elements = function () {
338 return this.element.children("div.cell");
339 return this.element.children("div.cell");
339 };
340 };
340
341
341
342
342 Notebook.prototype.get_cell_element = function (index) {
343 Notebook.prototype.get_cell_element = function (index) {
343 var result = null;
344 var result = null;
344 var e = this.get_cell_elements().eq(index);
345 var e = this.get_cell_elements().eq(index);
345 if (e.length !== 0) {
346 if (e.length !== 0) {
346 result = e;
347 result = e;
347 }
348 }
348 return result;
349 return result;
349 };
350 };
350
351
351
352
352 Notebook.prototype.ncells = function (cell) {
353 Notebook.prototype.ncells = function (cell) {
353 return this.get_cell_elements().length;
354 return this.get_cell_elements().length;
354 };
355 };
355
356
356
357
357 // 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
358 // to cells(i) or a new method.
359 // to cells(i) or a new method.
359 Notebook.prototype.get_cells = function () {
360 Notebook.prototype.get_cells = function () {
360 return this.get_cell_elements().toArray().map(function (e) {
361 return this.get_cell_elements().toArray().map(function (e) {
361 return $(e).data("cell");
362 return $(e).data("cell");
362 });
363 });
363 };
364 };
364
365
365
366
366 Notebook.prototype.get_cell = function (index) {
367 Notebook.prototype.get_cell = function (index) {
367 var result = null;
368 var result = null;
368 var ce = this.get_cell_element(index);
369 var ce = this.get_cell_element(index);
369 if (ce !== null) {
370 if (ce !== null) {
370 result = ce.data('cell');
371 result = ce.data('cell');
371 }
372 }
372 return result;
373 return result;
373 }
374 }
374
375
375
376
376 Notebook.prototype.get_next_cell = function (cell) {
377 Notebook.prototype.get_next_cell = function (cell) {
377 var result = null;
378 var result = null;
378 var index = this.find_cell_index(cell);
379 var index = this.find_cell_index(cell);
379 if (index !== null && index < this.ncells()) {
380 if (index !== null && index < this.ncells()) {
380 result = this.get_cell(index+1);
381 result = this.get_cell(index+1);
381 }
382 }
382 return result;
383 return result;
383 }
384 }
384
385
385
386
386 Notebook.prototype.get_prev_cell = function (cell) {
387 Notebook.prototype.get_prev_cell = function (cell) {
387 var result = null;
388 var result = null;
388 var index = this.find_cell_index(cell);
389 var index = this.find_cell_index(cell);
389 if (index !== null && index > 1) {
390 if (index !== null && index > 1) {
390 result = this.get_cell(index-1);
391 result = this.get_cell(index-1);
391 }
392 }
392 return result;
393 return result;
393 }
394 }
394
395
395 Notebook.prototype.find_cell_index = function (cell) {
396 Notebook.prototype.find_cell_index = function (cell) {
396 var result = null;
397 var result = null;
397 this.get_cell_elements().filter(function (index) {
398 this.get_cell_elements().filter(function (index) {
398 if ($(this).data("cell") === cell) {
399 if ($(this).data("cell") === cell) {
399 result = index;
400 result = index;
400 };
401 };
401 });
402 });
402 return result;
403 return result;
403 };
404 };
404
405
405
406
406 Notebook.prototype.index_or_selected = function (index) {
407 Notebook.prototype.index_or_selected = function (index) {
407 var i;
408 var i;
408 if (index === undefined || index === null) {
409 if (index === undefined || index === null) {
409 i = this.get_selected_index();
410 i = this.get_selected_index();
410 if (i === null) {
411 if (i === null) {
411 i = 0;
412 i = 0;
412 }
413 }
413 } else {
414 } else {
414 i = index;
415 i = index;
415 }
416 }
416 return i;
417 return i;
417 };
418 };
418
419
419
420
420 Notebook.prototype.get_selected_cell = function () {
421 Notebook.prototype.get_selected_cell = function () {
421 var index = this.get_selected_index();
422 var index = this.get_selected_index();
422 return this.get_cell(index);
423 return this.get_cell(index);
423 };
424 };
424
425
425
426
426 Notebook.prototype.is_valid_cell_index = function (index) {
427 Notebook.prototype.is_valid_cell_index = function (index) {
427 if (index !== null && index >= 0 && index < this.ncells()) {
428 if (index !== null && index >= 0 && index < this.ncells()) {
428 return true;
429 return true;
429 } else {
430 } else {
430 return false;
431 return false;
431 };
432 };
432 }
433 }
433
434
434 Notebook.prototype.get_selected_index = function () {
435 Notebook.prototype.get_selected_index = function () {
435 var result = null;
436 var result = null;
436 this.get_cell_elements().filter(function (index) {
437 this.get_cell_elements().filter(function (index) {
437 if ($(this).data("cell").selected === true) {
438 if ($(this).data("cell").selected === true) {
438 result = index;
439 result = index;
439 };
440 };
440 });
441 });
441 return result;
442 return result;
442 };
443 };
443
444
444
445
445 // Cell selection.
446 // Cell selection.
446
447
447 Notebook.prototype.select = function (index) {
448 Notebook.prototype.select = function (index) {
448 if (index !== undefined && index >= 0 && index < this.ncells()) {
449 if (index !== undefined && index >= 0 && index < this.ncells()) {
449 sindex = this.get_selected_index()
450 sindex = this.get_selected_index()
450 if (sindex !== null && index !== sindex) {
451 if (sindex !== null && index !== sindex) {
451 this.get_cell(sindex).unselect();
452 this.get_cell(sindex).unselect();
452 };
453 };
453 var cell = this.get_cell(index)
454 var cell = this.get_cell(index)
454 cell.select();
455 cell.select();
455 if (cell.cell_type === 'heading') {
456 if (cell.cell_type === 'heading') {
456 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
457 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
457 {'cell_type':cell.cell_type,level:cell.level}
458 {'cell_type':cell.cell_type,level:cell.level}
458 );
459 );
459 } else {
460 } else {
460 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
461 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
461 {'cell_type':cell.cell_type}
462 {'cell_type':cell.cell_type}
462 );
463 );
463 };
464 };
464 };
465 };
465 return this;
466 return this;
466 };
467 };
467
468
468
469
469 Notebook.prototype.select_next = function () {
470 Notebook.prototype.select_next = function () {
470 var index = this.get_selected_index();
471 var index = this.get_selected_index();
471 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
472 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
472 this.select(index+1);
473 this.select(index+1);
473 };
474 };
474 return this;
475 return this;
475 };
476 };
476
477
477
478
478 Notebook.prototype.select_prev = function () {
479 Notebook.prototype.select_prev = function () {
479 var index = this.get_selected_index();
480 var index = this.get_selected_index();
480 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
481 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
481 this.select(index-1);
482 this.select(index-1);
482 };
483 };
483 return this;
484 return this;
484 };
485 };
485
486
486
487
487 // Cell movement
488 // Cell movement
488
489
489 Notebook.prototype.move_cell_up = function (index) {
490 Notebook.prototype.move_cell_up = function (index) {
490 var i = this.index_or_selected();
491 var i = this.index_or_selected();
491 if (i !== null && i < this.ncells() && i > 0) {
492 if (i !== null && i < this.ncells() && i > 0) {
492 var pivot = this.get_cell_element(i-1);
493 var pivot = this.get_cell_element(i-1);
493 var tomove = this.get_cell_element(i);
494 var tomove = this.get_cell_element(i);
494 if (pivot !== null && tomove !== null) {
495 if (pivot !== null && tomove !== null) {
495 tomove.detach();
496 tomove.detach();
496 pivot.before(tomove);
497 pivot.before(tomove);
497 this.select(i-1);
498 this.select(i-1);
498 };
499 };
499 };
500 };
500 this.dirty = true;
501 this.dirty = true;
501 return this;
502 return this;
502 };
503 };
503
504
504
505
505 Notebook.prototype.move_cell_down = function (index) {
506 Notebook.prototype.move_cell_down = function (index) {
506 var i = this.index_or_selected();
507 var i = this.index_or_selected();
507 if (i !== null && i < (this.ncells()-1) && i >= 0) {
508 if (i !== null && i < (this.ncells()-1) && i >= 0) {
508 var pivot = this.get_cell_element(i+1);
509 var pivot = this.get_cell_element(i+1);
509 var tomove = this.get_cell_element(i);
510 var tomove = this.get_cell_element(i);
510 if (pivot !== null && tomove !== null) {
511 if (pivot !== null && tomove !== null) {
511 tomove.detach();
512 tomove.detach();
512 pivot.after(tomove);
513 pivot.after(tomove);
513 this.select(i+1);
514 this.select(i+1);
514 };
515 };
515 };
516 };
516 this.dirty = true;
517 this.dirty = true;
517 return this;
518 return this;
518 };
519 };
519
520
520
521
521 Notebook.prototype.sort_cells = function () {
522 Notebook.prototype.sort_cells = function () {
522 // This is not working right now. Calling this will actually crash
523 // This is not working right now. Calling this will actually crash
523 // the browser. I think there is an infinite loop in here...
524 // the browser. I think there is an infinite loop in here...
524 var ncells = this.ncells();
525 var ncells = this.ncells();
525 var sindex = this.get_selected_index();
526 var sindex = this.get_selected_index();
526 var swapped;
527 var swapped;
527 do {
528 do {
528 swapped = false;
529 swapped = false;
529 for (var i=1; i<ncells; i++) {
530 for (var i=1; i<ncells; i++) {
530 current = this.get_cell(i);
531 current = this.get_cell(i);
531 previous = this.get_cell(i-1);
532 previous = this.get_cell(i-1);
532 if (previous.input_prompt_number > current.input_prompt_number) {
533 if (previous.input_prompt_number > current.input_prompt_number) {
533 this.move_cell_up(i);
534 this.move_cell_up(i);
534 swapped = true;
535 swapped = true;
535 };
536 };
536 };
537 };
537 } while (swapped);
538 } while (swapped);
538 this.select(sindex);
539 this.select(sindex);
539 return this;
540 return this;
540 };
541 };
541
542
542 // Insertion, deletion.
543 // Insertion, deletion.
543
544
544 Notebook.prototype.delete_cell = function (index) {
545 Notebook.prototype.delete_cell = function (index) {
545 var i = this.index_or_selected(index);
546 var i = this.index_or_selected(index);
546 var cell = this.get_selected_cell();
547 var cell = this.get_selected_cell();
547 this.undelete_backup = cell.toJSON();
548 this.undelete_backup = cell.toJSON();
548 if (this.is_valid_cell_index(i)) {
549 if (this.is_valid_cell_index(i)) {
549 var ce = this.get_cell_element(i);
550 var ce = this.get_cell_element(i);
550 ce.remove();
551 ce.remove();
551 if (i === (this.ncells())) {
552 if (i === (this.ncells())) {
552 this.select(i-1);
553 this.select(i-1);
553 this.undelete_index = i - 1;
554 this.undelete_index = i - 1;
554 this.undelete_below = true;
555 this.undelete_below = true;
555 } else {
556 } else {
556 this.select(i);
557 this.select(i);
557 this.undelete_index = i;
558 this.undelete_index = i;
558 this.undelete_below = false;
559 this.undelete_below = false;
559 };
560 };
560 this.dirty = true;
561 this.dirty = true;
561 };
562 };
562 return this;
563 return this;
563 };
564 };
564
565
565
566
566 Notebook.prototype.insert_cell_at_bottom = function (type){
567 Notebook.prototype.insert_cell_at_bottom = function (type){
567 var len = this.ncells();
568 var len = this.ncells();
568 return this.insert_cell_below(type,len-1);
569 return this.insert_cell_below(type,len-1);
569 }
570 }
570
571
571 Notebook.prototype.insert_cell_below = function (type, index) {
572 Notebook.prototype.insert_cell_below = function (type, index) {
572 // type = ('code','html','markdown')
573 // type = ('code','html','markdown')
573 // index = cell index or undefined to insert below selected
574 // index = cell index or undefined to insert below selected
574 index = this.index_or_selected(index);
575 index = this.index_or_selected(index);
575 var cell = null;
576 var cell = null;
576 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
577 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
577 if (type === 'code') {
578 if (type === 'code') {
578 cell = new IPython.CodeCell(this.kernel);
579 cell = new IPython.CodeCell(this.kernel);
579 cell.set_input_prompt();
580 cell.set_input_prompt();
580 } else if (type === 'markdown') {
581 } else if (type === 'markdown') {
581 cell = new IPython.MarkdownCell();
582 cell = new IPython.MarkdownCell();
582 } else if (type === 'html') {
583 } else if (type === 'html') {
583 cell = new IPython.HTMLCell();
584 cell = new IPython.HTMLCell();
584 } else if (type === 'raw') {
585 } else if (type === 'raw') {
585 cell = new IPython.RawCell();
586 cell = new IPython.RawCell();
586 } else if (type === 'heading') {
587 } else if (type === 'heading') {
587 cell = new IPython.HeadingCell();
588 cell = new IPython.HeadingCell();
588 };
589 };
589 if (cell !== null) {
590 if (cell !== null) {
590 if (this.ncells() === 0) {
591 if (this.ncells() === 0) {
591 this.element.find('div.end_space').before(cell.element);
592 this.element.find('div.end_space').before(cell.element);
592 } else if (this.is_valid_cell_index(index)) {
593 } else if (this.is_valid_cell_index(index)) {
593 this.get_cell_element(index).after(cell.element);
594 this.get_cell_element(index).after(cell.element);
594 };
595 };
595 cell.render();
596 cell.render();
596 this.select(this.find_cell_index(cell));
597 this.select(this.find_cell_index(cell));
597 this.dirty = true;
598 this.dirty = true;
598 return cell;
599 return cell;
599 };
600 };
600 };
601 };
601 return cell;
602 return cell;
602 };
603 };
603
604
604
605
605 Notebook.prototype.insert_cell_above = function (type, index) {
606 Notebook.prototype.insert_cell_above = function (type, index) {
606 // type = ('code','html','markdown')
607 // type = ('code','html','markdown')
607 // index = cell index or undefined to insert above selected
608 // index = cell index or undefined to insert above selected
608 index = this.index_or_selected(index);
609 index = this.index_or_selected(index);
609 var cell = null;
610 var cell = null;
610 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
611 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
611 if (type === 'code') {
612 if (type === 'code') {
612 cell = new IPython.CodeCell(this.kernel);
613 cell = new IPython.CodeCell(this.kernel);
613 cell.set_input_prompt();
614 cell.set_input_prompt();
614 } else if (type === 'markdown') {
615 } else if (type === 'markdown') {
615 cell = new IPython.MarkdownCell();
616 cell = new IPython.MarkdownCell();
616 } else if (type === 'html') {
617 } else if (type === 'html') {
617 cell = new IPython.HTMLCell();
618 cell = new IPython.HTMLCell();
618 } else if (type === 'raw') {
619 } else if (type === 'raw') {
619 cell = new IPython.RawCell();
620 cell = new IPython.RawCell();
620 } else if (type === 'heading') {
621 } else if (type === 'heading') {
621 cell = new IPython.HeadingCell();
622 cell = new IPython.HeadingCell();
622 };
623 };
623 if (cell !== null) {
624 if (cell !== null) {
624 if (this.ncells() === 0) {
625 if (this.ncells() === 0) {
625 this.element.find('div.end_space').before(cell.element);
626 this.element.find('div.end_space').before(cell.element);
626 } else if (this.is_valid_cell_index(index)) {
627 } else if (this.is_valid_cell_index(index)) {
627 this.get_cell_element(index).before(cell.element);
628 this.get_cell_element(index).before(cell.element);
628 };
629 };
629 cell.render();
630 cell.render();
630 this.select(this.find_cell_index(cell));
631 this.select(this.find_cell_index(cell));
631 this.dirty = true;
632 this.dirty = true;
632 return cell;
633 return cell;
633 };
634 };
634 };
635 };
635 return cell;
636 return cell;
636 };
637 };
637
638
638
639
639 Notebook.prototype.to_code = function (index) {
640 Notebook.prototype.to_code = function (index) {
640 var i = this.index_or_selected(index);
641 var i = this.index_or_selected(index);
641 if (this.is_valid_cell_index(i)) {
642 if (this.is_valid_cell_index(i)) {
642 var source_element = this.get_cell_element(i);
643 var source_element = this.get_cell_element(i);
643 var source_cell = source_element.data("cell");
644 var source_cell = source_element.data("cell");
644 if (!(source_cell instanceof IPython.CodeCell)) {
645 if (!(source_cell instanceof IPython.CodeCell)) {
645 target_cell = this.insert_cell_below('code',i);
646 target_cell = this.insert_cell_below('code',i);
646 var text = source_cell.get_text();
647 var text = source_cell.get_text();
647 if (text === source_cell.placeholder) {
648 if (text === source_cell.placeholder) {
648 text = '';
649 text = '';
649 }
650 }
650 target_cell.set_text(text);
651 target_cell.set_text(text);
651 // make this value the starting point, so that we can only undo
652 // make this value the starting point, so that we can only undo
652 // to this state, instead of a blank cell
653 // to this state, instead of a blank cell
653 target_cell.code_mirror.clearHistory();
654 target_cell.code_mirror.clearHistory();
654 source_element.remove();
655 source_element.remove();
655 this.dirty = true;
656 this.dirty = true;
656 };
657 };
657 };
658 };
658 };
659 };
659
660
660
661
661 Notebook.prototype.to_markdown = function (index) {
662 Notebook.prototype.to_markdown = function (index) {
662 var i = this.index_or_selected(index);
663 var i = this.index_or_selected(index);
663 if (this.is_valid_cell_index(i)) {
664 if (this.is_valid_cell_index(i)) {
664 var source_element = this.get_cell_element(i);
665 var source_element = this.get_cell_element(i);
665 var source_cell = source_element.data("cell");
666 var source_cell = source_element.data("cell");
666 if (!(source_cell instanceof IPython.MarkdownCell)) {
667 if (!(source_cell instanceof IPython.MarkdownCell)) {
667 target_cell = this.insert_cell_below('markdown',i);
668 target_cell = this.insert_cell_below('markdown',i);
668 var text = source_cell.get_text();
669 var text = source_cell.get_text();
669 if (text === source_cell.placeholder) {
670 if (text === source_cell.placeholder) {
670 text = '';
671 text = '';
671 };
672 };
672 // The edit must come before the set_text.
673 // The edit must come before the set_text.
673 target_cell.edit();
674 target_cell.edit();
674 target_cell.set_text(text);
675 target_cell.set_text(text);
675 // make this value the starting point, so that we can only undo
676 // make this value the starting point, so that we can only undo
676 // to this state, instead of a blank cell
677 // to this state, instead of a blank cell
677 target_cell.code_mirror.clearHistory();
678 target_cell.code_mirror.clearHistory();
678 source_element.remove();
679 source_element.remove();
679 this.dirty = true;
680 this.dirty = true;
680 };
681 };
681 };
682 };
682 };
683 };
683
684
684
685
685 Notebook.prototype.to_html = function (index) {
686 Notebook.prototype.to_html = function (index) {
686 var i = this.index_or_selected(index);
687 var i = this.index_or_selected(index);
687 if (this.is_valid_cell_index(i)) {
688 if (this.is_valid_cell_index(i)) {
688 var source_element = this.get_cell_element(i);
689 var source_element = this.get_cell_element(i);
689 var source_cell = source_element.data("cell");
690 var source_cell = source_element.data("cell");
690 var target_cell = null;
691 var target_cell = null;
691 if (!(source_cell instanceof IPython.HTMLCell)) {
692 if (!(source_cell instanceof IPython.HTMLCell)) {
692 target_cell = this.insert_cell_below('html',i);
693 target_cell = this.insert_cell_below('html',i);
693 var text = source_cell.get_text();
694 var text = source_cell.get_text();
694 if (text === source_cell.placeholder) {
695 if (text === source_cell.placeholder) {
695 text = '';
696 text = '';
696 };
697 };
697 // The edit must come before the set_text.
698 // The edit must come before the set_text.
698 target_cell.edit();
699 target_cell.edit();
699 target_cell.set_text(text);
700 target_cell.set_text(text);
700 // make this value the starting point, so that we can only undo
701 // make this value the starting point, so that we can only undo
701 // to this state, instead of a blank cell
702 // to this state, instead of a blank cell
702 target_cell.code_mirror.clearHistory();
703 target_cell.code_mirror.clearHistory();
703 source_element.remove();
704 source_element.remove();
704 this.dirty = true;
705 this.dirty = true;
705 };
706 };
706 };
707 };
707 };
708 };
708
709
709
710
710 Notebook.prototype.to_raw = function (index) {
711 Notebook.prototype.to_raw = function (index) {
711 var i = this.index_or_selected(index);
712 var i = this.index_or_selected(index);
712 if (this.is_valid_cell_index(i)) {
713 if (this.is_valid_cell_index(i)) {
713 var source_element = this.get_cell_element(i);
714 var source_element = this.get_cell_element(i);
714 var source_cell = source_element.data("cell");
715 var source_cell = source_element.data("cell");
715 var target_cell = null;
716 var target_cell = null;
716 if (!(source_cell instanceof IPython.RawCell)) {
717 if (!(source_cell instanceof IPython.RawCell)) {
717 target_cell = this.insert_cell_below('raw',i);
718 target_cell = this.insert_cell_below('raw',i);
718 var text = source_cell.get_text();
719 var text = source_cell.get_text();
719 if (text === source_cell.placeholder) {
720 if (text === source_cell.placeholder) {
720 text = '';
721 text = '';
721 };
722 };
722 // The edit must come before the set_text.
723 // The edit must come before the set_text.
723 target_cell.edit();
724 target_cell.edit();
724 target_cell.set_text(text);
725 target_cell.set_text(text);
725 // make this value the starting point, so that we can only undo
726 // make this value the starting point, so that we can only undo
726 // to this state, instead of a blank cell
727 // to this state, instead of a blank cell
727 target_cell.code_mirror.clearHistory();
728 target_cell.code_mirror.clearHistory();
728 source_element.remove();
729 source_element.remove();
729 this.dirty = true;
730 this.dirty = true;
730 };
731 };
731 };
732 };
732 };
733 };
733
734
734
735
735 Notebook.prototype.to_heading = function (index, level) {
736 Notebook.prototype.to_heading = function (index, level) {
736 level = level || 1;
737 level = level || 1;
737 var i = this.index_or_selected(index);
738 var i = this.index_or_selected(index);
738 if (this.is_valid_cell_index(i)) {
739 if (this.is_valid_cell_index(i)) {
739 var source_element = this.get_cell_element(i);
740 var source_element = this.get_cell_element(i);
740 var source_cell = source_element.data("cell");
741 var source_cell = source_element.data("cell");
741 var target_cell = null;
742 var target_cell = null;
742 if (source_cell instanceof IPython.HeadingCell) {
743 if (source_cell instanceof IPython.HeadingCell) {
743 source_cell.set_level(level);
744 source_cell.set_level(level);
744 } else {
745 } else {
745 target_cell = this.insert_cell_below('heading',i);
746 target_cell = this.insert_cell_below('heading',i);
746 var text = source_cell.get_text();
747 var text = source_cell.get_text();
747 if (text === source_cell.placeholder) {
748 if (text === source_cell.placeholder) {
748 text = '';
749 text = '';
749 };
750 };
750 // The edit must come before the set_text.
751 // The edit must come before the set_text.
751 target_cell.set_level(level);
752 target_cell.set_level(level);
752 target_cell.edit();
753 target_cell.edit();
753 target_cell.set_text(text);
754 target_cell.set_text(text);
754 // make this value the starting point, so that we can only undo
755 // make this value the starting point, so that we can only undo
755 // to this state, instead of a blank cell
756 // to this state, instead of a blank cell
756 target_cell.code_mirror.clearHistory();
757 target_cell.code_mirror.clearHistory();
757 source_element.remove();
758 source_element.remove();
758 this.dirty = true;
759 this.dirty = true;
759 };
760 };
760 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
761 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
761 {'cell_type':'heading',level:level}
762 {'cell_type':'heading',level:level}
762 );
763 );
763 };
764 };
764 };
765 };
765
766
766
767
767 // Cut/Copy/Paste
768 // Cut/Copy/Paste
768
769
769 Notebook.prototype.enable_paste = function () {
770 Notebook.prototype.enable_paste = function () {
770 var that = this;
771 var that = this;
771 if (!this.paste_enabled) {
772 if (!this.paste_enabled) {
772 $('#paste_cell').removeClass('ui-state-disabled')
773 $('#paste_cell').removeClass('ui-state-disabled')
773 .on('click', function () {that.paste_cell();});
774 .on('click', function () {that.paste_cell();});
774 $('#paste_cell_above').removeClass('ui-state-disabled')
775 $('#paste_cell_above').removeClass('ui-state-disabled')
775 .on('click', function () {that.paste_cell_above();});
776 .on('click', function () {that.paste_cell_above();});
776 $('#paste_cell_below').removeClass('ui-state-disabled')
777 $('#paste_cell_below').removeClass('ui-state-disabled')
777 .on('click', function () {that.paste_cell_below();});
778 .on('click', function () {that.paste_cell_below();});
778 this.paste_enabled = true;
779 this.paste_enabled = true;
779 };
780 };
780 };
781 };
781
782
782
783
783 Notebook.prototype.disable_paste = function () {
784 Notebook.prototype.disable_paste = function () {
784 if (this.paste_enabled) {
785 if (this.paste_enabled) {
785 $('#paste_cell').addClass('ui-state-disabled').off('click');
786 $('#paste_cell').addClass('ui-state-disabled').off('click');
786 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
787 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
787 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
788 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
788 this.paste_enabled = false;
789 this.paste_enabled = false;
789 };
790 };
790 };
791 };
791
792
792
793
793 Notebook.prototype.cut_cell = function () {
794 Notebook.prototype.cut_cell = function () {
794 this.copy_cell();
795 this.copy_cell();
795 this.delete_cell();
796 this.delete_cell();
796 }
797 }
797
798
798 Notebook.prototype.copy_cell = function () {
799 Notebook.prototype.copy_cell = function () {
799 var cell = this.get_selected_cell();
800 var cell = this.get_selected_cell();
800 this.clipboard = cell.toJSON();
801 this.clipboard = cell.toJSON();
801 this.enable_paste();
802 this.enable_paste();
802 };
803 };
803
804
804
805
805 Notebook.prototype.paste_cell = function () {
806 Notebook.prototype.paste_cell = function () {
806 if (this.clipboard !== null && this.paste_enabled) {
807 if (this.clipboard !== null && this.paste_enabled) {
807 var cell_data = this.clipboard;
808 var cell_data = this.clipboard;
808 var new_cell = this.insert_cell_above(cell_data.cell_type);
809 var new_cell = this.insert_cell_above(cell_data.cell_type);
809 new_cell.fromJSON(cell_data);
810 new_cell.fromJSON(cell_data);
810 old_cell = this.get_next_cell(new_cell);
811 old_cell = this.get_next_cell(new_cell);
811 this.delete_cell(this.find_cell_index(old_cell));
812 this.delete_cell(this.find_cell_index(old_cell));
812 this.select(this.find_cell_index(new_cell));
813 this.select(this.find_cell_index(new_cell));
813 };
814 };
814 };
815 };
815
816
816
817
817 Notebook.prototype.paste_cell_above = function () {
818 Notebook.prototype.paste_cell_above = function () {
818 if (this.clipboard !== null && this.paste_enabled) {
819 if (this.clipboard !== null && this.paste_enabled) {
819 var cell_data = this.clipboard;
820 var cell_data = this.clipboard;
820 var new_cell = this.insert_cell_above(cell_data.cell_type);
821 var new_cell = this.insert_cell_above(cell_data.cell_type);
821 new_cell.fromJSON(cell_data);
822 new_cell.fromJSON(cell_data);
822 };
823 };
823 };
824 };
824
825
825
826
826 Notebook.prototype.paste_cell_below = function () {
827 Notebook.prototype.paste_cell_below = function () {
827 if (this.clipboard !== null && this.paste_enabled) {
828 if (this.clipboard !== null && this.paste_enabled) {
828 var cell_data = this.clipboard;
829 var cell_data = this.clipboard;
829 var new_cell = this.insert_cell_below(cell_data.cell_type);
830 var new_cell = this.insert_cell_below(cell_data.cell_type);
830 new_cell.fromJSON(cell_data);
831 new_cell.fromJSON(cell_data);
831 };
832 };
832 };
833 };
833
834
834 // Cell undelete
835 // Cell undelete
835
836
836 Notebook.prototype.undelete = function() {
837 Notebook.prototype.undelete = function() {
837 if (this.undelete_backup !== null && this.undelete_index !== null) {
838 if (this.undelete_backup !== null && this.undelete_index !== null) {
838 var current_index = this.get_selected_index();
839 var current_index = this.get_selected_index();
839 if (this.undelete_index < current_index) {
840 if (this.undelete_index < current_index) {
840 current_index = current_index + 1;
841 current_index = current_index + 1;
841 }
842 }
842 if (this.undelete_index >= this.ncells()) {
843 if (this.undelete_index >= this.ncells()) {
843 this.select(this.ncells() - 1);
844 this.select(this.ncells() - 1);
844 }
845 }
845 else {
846 else {
846 this.select(this.undelete_index);
847 this.select(this.undelete_index);
847 }
848 }
848 var cell_data = this.undelete_backup;
849 var cell_data = this.undelete_backup;
849 var new_cell = null;
850 var new_cell = null;
850 if (this.undelete_below) {
851 if (this.undelete_below) {
851 new_cell = this.insert_cell_below(cell_data.cell_type);
852 new_cell = this.insert_cell_below(cell_data.cell_type);
852 } else {
853 } else {
853 new_cell = this.insert_cell_above(cell_data.cell_type);
854 new_cell = this.insert_cell_above(cell_data.cell_type);
854 }
855 }
855 new_cell.fromJSON(cell_data);
856 new_cell.fromJSON(cell_data);
856 this.select(current_index);
857 this.select(current_index);
857 this.undelete_backup = null;
858 this.undelete_backup = null;
858 this.undelete_index = null;
859 this.undelete_index = null;
859 }
860 }
860 }
861 }
861
862
862 // Split/merge
863 // Split/merge
863
864
864 Notebook.prototype.split_cell = function () {
865 Notebook.prototype.split_cell = function () {
865 // Todo: implement spliting for other cell types.
866 // Todo: implement spliting for other cell types.
866 var cell = this.get_selected_cell();
867 var cell = this.get_selected_cell();
867 if (cell.is_splittable()) {
868 if (cell.is_splittable()) {
868 texta = cell.get_pre_cursor();
869 texta = cell.get_pre_cursor();
869 textb = cell.get_post_cursor();
870 textb = cell.get_post_cursor();
870 if (cell instanceof IPython.CodeCell) {
871 if (cell instanceof IPython.CodeCell) {
871 cell.set_text(texta);
872 cell.set_text(texta);
872 var new_cell = this.insert_cell_below('code');
873 var new_cell = this.insert_cell_below('code');
873 new_cell.set_text(textb);
874 new_cell.set_text(textb);
874 } else if (cell instanceof IPython.MarkdownCell) {
875 } else if (cell instanceof IPython.MarkdownCell) {
875 cell.set_text(texta);
876 cell.set_text(texta);
876 cell.render();
877 cell.render();
877 var new_cell = this.insert_cell_below('markdown');
878 var new_cell = this.insert_cell_below('markdown');
878 new_cell.edit(); // editor must be visible to call set_text
879 new_cell.edit(); // editor must be visible to call set_text
879 new_cell.set_text(textb);
880 new_cell.set_text(textb);
880 new_cell.render();
881 new_cell.render();
881 } else if (cell instanceof IPython.HTMLCell) {
882 } else if (cell instanceof IPython.HTMLCell) {
882 cell.set_text(texta);
883 cell.set_text(texta);
883 cell.render();
884 cell.render();
884 var new_cell = this.insert_cell_below('html');
885 var new_cell = this.insert_cell_below('html');
885 new_cell.edit(); // editor must be visible to call set_text
886 new_cell.edit(); // editor must be visible to call set_text
886 new_cell.set_text(textb);
887 new_cell.set_text(textb);
887 new_cell.render();
888 new_cell.render();
888 };
889 };
889 };
890 };
890 };
891 };
891
892
892
893
893 Notebook.prototype.merge_cell_above = function () {
894 Notebook.prototype.merge_cell_above = function () {
894 var index = this.get_selected_index();
895 var index = this.get_selected_index();
895 var cell = this.get_cell(index);
896 var cell = this.get_cell(index);
896 if (index > 0) {
897 if (index > 0) {
897 upper_cell = this.get_cell(index-1);
898 upper_cell = this.get_cell(index-1);
898 upper_text = upper_cell.get_text();
899 upper_text = upper_cell.get_text();
899 text = cell.get_text();
900 text = cell.get_text();
900 if (cell instanceof IPython.CodeCell) {
901 if (cell instanceof IPython.CodeCell) {
901 cell.set_text(upper_text+'\n'+text);
902 cell.set_text(upper_text+'\n'+text);
902 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
903 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
903 cell.edit();
904 cell.edit();
904 cell.set_text(upper_text+'\n'+text);
905 cell.set_text(upper_text+'\n'+text);
905 cell.render();
906 cell.render();
906 };
907 };
907 this.delete_cell(index-1);
908 this.delete_cell(index-1);
908 this.select(this.find_cell_index(cell));
909 this.select(this.find_cell_index(cell));
909 };
910 };
910 };
911 };
911
912
912
913
913 Notebook.prototype.merge_cell_below = function () {
914 Notebook.prototype.merge_cell_below = function () {
914 var index = this.get_selected_index();
915 var index = this.get_selected_index();
915 var cell = this.get_cell(index);
916 var cell = this.get_cell(index);
916 if (index < this.ncells()-1) {
917 if (index < this.ncells()-1) {
917 lower_cell = this.get_cell(index+1);
918 lower_cell = this.get_cell(index+1);
918 lower_text = lower_cell.get_text();
919 lower_text = lower_cell.get_text();
919 text = cell.get_text();
920 text = cell.get_text();
920 if (cell instanceof IPython.CodeCell) {
921 if (cell instanceof IPython.CodeCell) {
921 cell.set_text(text+'\n'+lower_text);
922 cell.set_text(text+'\n'+lower_text);
922 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
923 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
923 cell.edit();
924 cell.edit();
924 cell.set_text(text+'\n'+lower_text);
925 cell.set_text(text+'\n'+lower_text);
925 cell.render();
926 cell.render();
926 };
927 };
927 this.delete_cell(index+1);
928 this.delete_cell(index+1);
928 this.select(this.find_cell_index(cell));
929 this.select(this.find_cell_index(cell));
929 };
930 };
930 };
931 };
931
932
932
933
933 // Cell collapsing and output clearing
934 // Cell collapsing and output clearing
934
935
935 Notebook.prototype.collapse = function (index) {
936 Notebook.prototype.collapse = function (index) {
936 var i = this.index_or_selected(index);
937 var i = this.index_or_selected(index);
937 this.get_cell(i).collapse();
938 this.get_cell(i).collapse();
938 this.dirty = true;
939 this.dirty = true;
939 };
940 };
940
941
941
942
942 Notebook.prototype.expand = function (index) {
943 Notebook.prototype.expand = function (index) {
943 var i = this.index_or_selected(index);
944 var i = this.index_or_selected(index);
944 this.get_cell(i).expand();
945 this.get_cell(i).expand();
945 this.dirty = true;
946 this.dirty = true;
946 };
947 };
947
948
948
949
949 Notebook.prototype.toggle_output = function (index) {
950 Notebook.prototype.toggle_output = function (index) {
950 var i = this.index_or_selected(index);
951 var i = this.index_or_selected(index);
951 this.get_cell(i).toggle_output();
952 this.get_cell(i).toggle_output();
952 this.dirty = true;
953 this.dirty = true;
953 };
954 };
954
955
955
956
956 Notebook.prototype.toggle_output_scroll = function (index) {
957 Notebook.prototype.toggle_output_scroll = function (index) {
957 var i = this.index_or_selected(index);
958 var i = this.index_or_selected(index);
958 this.get_cell(i).toggle_output_scroll();
959 this.get_cell(i).toggle_output_scroll();
959 };
960 };
960
961
961
962
962 Notebook.prototype.collapse_all_output = function () {
963 Notebook.prototype.collapse_all_output = function () {
963 var ncells = this.ncells();
964 var ncells = this.ncells();
964 var cells = this.get_cells();
965 var cells = this.get_cells();
965 for (var i=0; i<ncells; i++) {
966 for (var i=0; i<ncells; i++) {
966 if (cells[i] instanceof IPython.CodeCell) {
967 if (cells[i] instanceof IPython.CodeCell) {
967 cells[i].output_area.collapse();
968 cells[i].output_area.collapse();
968 }
969 }
969 };
970 };
970 // this should not be set if the `collapse` key is removed from nbformat
971 // this should not be set if the `collapse` key is removed from nbformat
971 this.dirty = true;
972 this.dirty = true;
972 };
973 };
973
974
974
975
975 Notebook.prototype.scroll_all_output = function () {
976 Notebook.prototype.scroll_all_output = function () {
976 var ncells = this.ncells();
977 var ncells = this.ncells();
977 var cells = this.get_cells();
978 var cells = this.get_cells();
978 for (var i=0; i<ncells; i++) {
979 for (var i=0; i<ncells; i++) {
979 if (cells[i] instanceof IPython.CodeCell) {
980 if (cells[i] instanceof IPython.CodeCell) {
980 cells[i].output_area.expand();
981 cells[i].output_area.expand();
981 cells[i].output_area.scroll_if_long(20);
982 cells[i].output_area.scroll_if_long(20);
982 }
983 }
983 };
984 };
984 // this should not be set if the `collapse` key is removed from nbformat
985 // this should not be set if the `collapse` key is removed from nbformat
985 this.dirty = true;
986 this.dirty = true;
986 };
987 };
987
988
988
989
989 Notebook.prototype.expand_all_output = function () {
990 Notebook.prototype.expand_all_output = function () {
990 var ncells = this.ncells();
991 var ncells = this.ncells();
991 var cells = this.get_cells();
992 var cells = this.get_cells();
992 for (var i=0; i<ncells; i++) {
993 for (var i=0; i<ncells; i++) {
993 if (cells[i] instanceof IPython.CodeCell) {
994 if (cells[i] instanceof IPython.CodeCell) {
994 cells[i].output_area.expand();
995 cells[i].output_area.expand();
995 cells[i].output_area.unscroll_area();
996 cells[i].output_area.unscroll_area();
996 }
997 }
997 };
998 };
998 // this should not be set if the `collapse` key is removed from nbformat
999 // this should not be set if the `collapse` key is removed from nbformat
999 this.dirty = true;
1000 this.dirty = true;
1000 };
1001 };
1001
1002
1002
1003
1003 Notebook.prototype.clear_all_output = function () {
1004 Notebook.prototype.clear_all_output = function () {
1004 var ncells = this.ncells();
1005 var ncells = this.ncells();
1005 var cells = this.get_cells();
1006 var cells = this.get_cells();
1006 for (var i=0; i<ncells; i++) {
1007 for (var i=0; i<ncells; i++) {
1007 if (cells[i] instanceof IPython.CodeCell) {
1008 if (cells[i] instanceof IPython.CodeCell) {
1008 cells[i].clear_output(true,true,true);
1009 cells[i].clear_output(true,true,true);
1009 // Make all In[] prompts blank, as well
1010 // Make all In[] prompts blank, as well
1010 // TODO: make this configurable (via checkbox?)
1011 // TODO: make this configurable (via checkbox?)
1011 cells[i].set_input_prompt();
1012 cells[i].set_input_prompt();
1012 }
1013 }
1013 };
1014 };
1014 this.dirty = true;
1015 this.dirty = true;
1015 };
1016 };
1016
1017
1017
1018
1018 // Other cell functions: line numbers, ...
1019 // Other cell functions: line numbers, ...
1019
1020
1020 Notebook.prototype.cell_toggle_line_numbers = function() {
1021 Notebook.prototype.cell_toggle_line_numbers = function() {
1021 this.get_selected_cell().toggle_line_numbers();
1022 this.get_selected_cell().toggle_line_numbers();
1022 };
1023 };
1023
1024
1024 // Kernel related things
1025 // Kernel related things
1025
1026
1026 Notebook.prototype.start_kernel = function () {
1027 Notebook.prototype.start_kernel = function () {
1027 var base_url = $('body').data('baseKernelUrl') + "kernels";
1028 var base_url = $('body').data('baseKernelUrl') + "kernels";
1028 this.kernel = new IPython.Kernel(base_url);
1029 this.kernel = new IPython.Kernel(base_url);
1029 this.kernel.start(this.notebook_id);
1030 this.kernel.start(this.notebook_id);
1030 // Now that the kernel has been created, tell the CodeCells about it.
1031 // Now that the kernel has been created, tell the CodeCells about it.
1031 var ncells = this.ncells();
1032 var ncells = this.ncells();
1032 for (var i=0; i<ncells; i++) {
1033 for (var i=0; i<ncells; i++) {
1033 var cell = this.get_cell(i);
1034 var cell = this.get_cell(i);
1034 if (cell instanceof IPython.CodeCell) {
1035 if (cell instanceof IPython.CodeCell) {
1035 cell.set_kernel(this.kernel)
1036 cell.set_kernel(this.kernel)
1036 };
1037 };
1037 };
1038 };
1038 };
1039 };
1039
1040
1040
1041
1041 Notebook.prototype.restart_kernel = function () {
1042 Notebook.prototype.restart_kernel = function () {
1042 var that = this;
1043 var that = this;
1043 var dialog = $('<div/>');
1044 var dialog = $('<div/>');
1044 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
1045 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
1045 $(document).append(dialog);
1046 $(document).append(dialog);
1046 dialog.dialog({
1047 dialog.dialog({
1047 resizable: false,
1048 resizable: false,
1048 modal: true,
1049 modal: true,
1049 title: "Restart kernel or continue running?",
1050 title: "Restart kernel or continue running?",
1050 closeText: '',
1051 closeText: '',
1051 buttons : {
1052 buttons : {
1052 "Restart": function () {
1053 "Restart": function () {
1053 that.kernel.restart();
1054 that.kernel.restart();
1054 $(this).dialog('close');
1055 $(this).dialog('close');
1055 },
1056 },
1056 "Continue running": function () {
1057 "Continue running": function () {
1057 $(this).dialog('close');
1058 $(this).dialog('close');
1058 }
1059 }
1059 }
1060 }
1060 });
1061 });
1061 };
1062 };
1062
1063
1063
1064
1064 Notebook.prototype.execute_selected_cell = function (options) {
1065 Notebook.prototype.execute_selected_cell = function (options) {
1065 // add_new: should a new cell be added if we are at the end of the nb
1066 // add_new: should a new cell be added if we are at the end of the nb
1066 // terminal: execute in terminal mode, which stays in the current cell
1067 // terminal: execute in terminal mode, which stays in the current cell
1067 default_options = {terminal: false, add_new: true};
1068 default_options = {terminal: false, add_new: true};
1068 $.extend(default_options, options);
1069 $.extend(default_options, options);
1069 var that = this;
1070 var that = this;
1070 var cell = that.get_selected_cell();
1071 var cell = that.get_selected_cell();
1071 var cell_index = that.find_cell_index(cell);
1072 var cell_index = that.find_cell_index(cell);
1072 if (cell instanceof IPython.CodeCell) {
1073 if (cell instanceof IPython.CodeCell) {
1073 cell.execute();
1074 cell.execute();
1074 } else if (cell instanceof IPython.HTMLCell) {
1075 } else if (cell instanceof IPython.HTMLCell) {
1075 cell.render();
1076 cell.render();
1076 }
1077 }
1077 if (default_options.terminal) {
1078 if (default_options.terminal) {
1078 cell.select_all();
1079 cell.select_all();
1079 } else {
1080 } else {
1080 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1081 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1081 that.insert_cell_below('code');
1082 that.insert_cell_below('code');
1082 // If we are adding a new cell at the end, scroll down to show it.
1083 // If we are adding a new cell at the end, scroll down to show it.
1083 that.scroll_to_bottom();
1084 that.scroll_to_bottom();
1084 } else {
1085 } else {
1085 that.select(cell_index+1);
1086 that.select(cell_index+1);
1086 };
1087 };
1087 };
1088 };
1088 this.dirty = true;
1089 this.dirty = true;
1089 };
1090 };
1090
1091
1091
1092
1092 Notebook.prototype.execute_cells_below = function () {
1093 Notebook.prototype.execute_cells_below = function () {
1093 this.execute_cell_range(this.get_selected_index(), this.ncells());
1094 this.execute_cell_range(this.get_selected_index(), this.ncells());
1094 that.scroll_to_bottom();
1095 that.scroll_to_bottom();
1095 };
1096 };
1096
1097
1097 Notebook.prototype.execute_cells_above = function () {
1098 Notebook.prototype.execute_cells_above = function () {
1098 this.execute_cell_range(0, this.get_selected_index());
1099 this.execute_cell_range(0, this.get_selected_index());
1099 };
1100 };
1100
1101
1101 Notebook.prototype.execute_all_cells = function () {
1102 Notebook.prototype.execute_all_cells = function () {
1102 this.execute_cell_range(0, this.ncells());
1103 this.execute_cell_range(0, this.ncells());
1103 that.scroll_to_bottom();
1104 that.scroll_to_bottom();
1104 };
1105 };
1105
1106
1106 Notebook.prototype.execute_cell_range = function (start, end) {
1107 Notebook.prototype.execute_cell_range = function (start, end) {
1107 for (var i=start; i<end; i++) {
1108 for (var i=start; i<end; i++) {
1108 this.select(i);
1109 this.select(i);
1109 this.execute_selected_cell({add_new:false});
1110 this.execute_selected_cell({add_new:false});
1110 };
1111 };
1111 };
1112 };
1112
1113
1113 // Persistance and loading
1114 // Persistance and loading
1114
1115
1115 Notebook.prototype.get_notebook_id = function () {
1116 Notebook.prototype.get_notebook_id = function () {
1116 return this.notebook_id;
1117 return this.notebook_id;
1117 };
1118 };
1118
1119
1119
1120
1120 Notebook.prototype.get_notebook_name = function () {
1121 Notebook.prototype.get_notebook_name = function () {
1121 return this.notebook_name;
1122 return this.notebook_name;
1122 };
1123 };
1123
1124
1124
1125
1125 Notebook.prototype.set_notebook_name = function (name) {
1126 Notebook.prototype.set_notebook_name = function (name) {
1126 this.notebook_name = name;
1127 this.notebook_name = name;
1127 };
1128 };
1128
1129
1129
1130
1130 Notebook.prototype.test_notebook_name = function (nbname) {
1131 Notebook.prototype.test_notebook_name = function (nbname) {
1131 nbname = nbname || '';
1132 nbname = nbname || '';
1132 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1133 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1133 return true;
1134 return true;
1134 } else {
1135 } else {
1135 return false;
1136 return false;
1136 };
1137 };
1137 };
1138 };
1138
1139
1139
1140
1140 Notebook.prototype.fromJSON = function (data) {
1141 Notebook.prototype.fromJSON = function (data) {
1141 var ncells = this.ncells();
1142 var ncells = this.ncells();
1142 var i;
1143 var i;
1143 for (i=0; i<ncells; i++) {
1144 for (i=0; i<ncells; i++) {
1144 // Always delete cell 0 as they get renumbered as they are deleted.
1145 // Always delete cell 0 as they get renumbered as they are deleted.
1145 this.delete_cell(0);
1146 this.delete_cell(0);
1146 };
1147 };
1147 // Save the metadata and name.
1148 // Save the metadata and name.
1148 this.metadata = data.metadata;
1149 this.metadata = data.metadata;
1149 this.notebook_name = data.metadata.name;
1150 this.notebook_name = data.metadata.name;
1150 // Only handle 1 worksheet for now.
1151 // Only handle 1 worksheet for now.
1151 var worksheet = data.worksheets[0];
1152 var worksheet = data.worksheets[0];
1152 if (worksheet !== undefined) {
1153 if (worksheet !== undefined) {
1153 if (worksheet.metadata) {
1154 if (worksheet.metadata) {
1154 this.worksheet_metadata = worksheet.metadata;
1155 this.worksheet_metadata = worksheet.metadata;
1155 }
1156 }
1156 var new_cells = worksheet.cells;
1157 var new_cells = worksheet.cells;
1157 ncells = new_cells.length;
1158 ncells = new_cells.length;
1158 var cell_data = null;
1159 var cell_data = null;
1159 var new_cell = null;
1160 var new_cell = null;
1160 for (i=0; i<ncells; i++) {
1161 for (i=0; i<ncells; i++) {
1161 cell_data = new_cells[i];
1162 cell_data = new_cells[i];
1162 // VERSIONHACK: plaintext -> raw
1163 // VERSIONHACK: plaintext -> raw
1163 // handle never-released plaintext name for raw cells
1164 // handle never-released plaintext name for raw cells
1164 if (cell_data.cell_type === 'plaintext'){
1165 if (cell_data.cell_type === 'plaintext'){
1165 cell_data.cell_type = 'raw';
1166 cell_data.cell_type = 'raw';
1166 }
1167 }
1167
1168
1168 new_cell = this.insert_cell_below(cell_data.cell_type);
1169 new_cell = this.insert_cell_below(cell_data.cell_type);
1169 new_cell.fromJSON(cell_data);
1170 new_cell.fromJSON(cell_data);
1170 };
1171 };
1171 };
1172 };
1172 if (data.worksheets.length > 1) {
1173 if (data.worksheets.length > 1) {
1173 var dialog = $('<div/>');
1174 var dialog = $('<div/>');
1174 dialog.html("This notebook has " + data.worksheets.length + " worksheets, " +
1175 dialog.html("This notebook has " + data.worksheets.length + " worksheets, " +
1175 "but this version of IPython can only handle the first. " +
1176 "but this version of IPython can only handle the first. " +
1176 "If you save this notebook, worksheets after the first will be lost."
1177 "If you save this notebook, worksheets after the first will be lost."
1177 );
1178 );
1178 this.element.append(dialog);
1179 this.element.append(dialog);
1179 dialog.dialog({
1180 dialog.dialog({
1180 resizable: false,
1181 resizable: false,
1181 modal: true,
1182 modal: true,
1182 title: "Multiple worksheets",
1183 title: "Multiple worksheets",
1183 closeText: "",
1184 closeText: "",
1184 close: function(event, ui) {$(this).dialog('destroy').remove();},
1185 close: function(event, ui) {$(this).dialog('destroy').remove();},
1185 buttons : {
1186 buttons : {
1186 "OK": function () {
1187 "OK": function () {
1187 $(this).dialog('close');
1188 $(this).dialog('close');
1188 }
1189 }
1189 },
1190 },
1190 width: 400
1191 width: 400
1191 });
1192 });
1192 }
1193 }
1193 };
1194 };
1194
1195
1195
1196
1196 Notebook.prototype.toJSON = function () {
1197 Notebook.prototype.toJSON = function () {
1197 var cells = this.get_cells();
1198 var cells = this.get_cells();
1198 var ncells = cells.length;
1199 var ncells = cells.length;
1199 var cell_array = new Array(ncells);
1200 var cell_array = new Array(ncells);
1200 for (var i=0; i<ncells; i++) {
1201 for (var i=0; i<ncells; i++) {
1201 cell_array[i] = cells[i].toJSON();
1202 cell_array[i] = cells[i].toJSON();
1202 };
1203 };
1203 var data = {
1204 var data = {
1204 // Only handle 1 worksheet for now.
1205 // Only handle 1 worksheet for now.
1205 worksheets : [{
1206 worksheets : [{
1206 cells: cell_array,
1207 cells: cell_array,
1207 metadata: this.worksheet_metadata
1208 metadata: this.worksheet_metadata
1208 }],
1209 }],
1209 metadata : this.metadata
1210 metadata : this.metadata
1210 };
1211 };
1211 return data;
1212 return data;
1212 };
1213 };
1213
1214
1214 Notebook.prototype.save_notebook = function () {
1215 Notebook.prototype.save_notebook = function () {
1215 // We may want to move the name/id/nbformat logic inside toJSON?
1216 // We may want to move the name/id/nbformat logic inside toJSON?
1216 var data = this.toJSON();
1217 var data = this.toJSON();
1217 data.metadata.name = this.notebook_name;
1218 data.metadata.name = this.notebook_name;
1218 data.nbformat = this.nbformat;
1219 data.nbformat = this.nbformat;
1219 data.nbformat_minor = this.nbformat_minor;
1220 data.nbformat_minor = this.nbformat_minor;
1220 // We do the call with settings so we can set cache to false.
1221 // We do the call with settings so we can set cache to false.
1221 var settings = {
1222 var settings = {
1222 processData : false,
1223 processData : false,
1223 cache : false,
1224 cache : false,
1224 type : "PUT",
1225 type : "PUT",
1225 data : JSON.stringify(data),
1226 data : JSON.stringify(data),
1226 headers : {'Content-Type': 'application/json'},
1227 headers : {'Content-Type': 'application/json'},
1227 success : $.proxy(this.save_notebook_success,this),
1228 success : $.proxy(this.save_notebook_success,this),
1228 error : $.proxy(this.save_notebook_error,this)
1229 error : $.proxy(this.save_notebook_error,this)
1229 };
1230 };
1230 $([IPython.events]).trigger('notebook_saving.Notebook');
1231 $([IPython.events]).trigger('notebook_saving.Notebook');
1231 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1232 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1232 $.ajax(url, settings);
1233 $.ajax(url, settings);
1233 };
1234 };
1234
1235
1235
1236
1236 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1237 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1237 this.dirty = false;
1238 this.dirty = false;
1238 $([IPython.events]).trigger('notebook_saved.Notebook');
1239 $([IPython.events]).trigger('notebook_saved.Notebook');
1239 };
1240 };
1240
1241
1241
1242
1242 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1243 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1243 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1244 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1244 };
1245 };
1245
1246
1246
1247
1247 Notebook.prototype.load_notebook = function (notebook_id) {
1248 Notebook.prototype.load_notebook = function (notebook_id) {
1248 var that = this;
1249 var that = this;
1249 this.notebook_id = notebook_id;
1250 this.notebook_id = notebook_id;
1250 // We do the call with settings so we can set cache to false.
1251 // We do the call with settings so we can set cache to false.
1251 var settings = {
1252 var settings = {
1252 processData : false,
1253 processData : false,
1253 cache : false,
1254 cache : false,
1254 type : "GET",
1255 type : "GET",
1255 dataType : "json",
1256 dataType : "json",
1256 success : $.proxy(this.load_notebook_success,this),
1257 success : $.proxy(this.load_notebook_success,this),
1257 error : $.proxy(this.load_notebook_error,this),
1258 error : $.proxy(this.load_notebook_error,this),
1258 };
1259 };
1259 $([IPython.events]).trigger('notebook_loading.Notebook');
1260 $([IPython.events]).trigger('notebook_loading.Notebook');
1260 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1261 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1261 $.ajax(url, settings);
1262 $.ajax(url, settings);
1262 };
1263 };
1263
1264
1264
1265
1265 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1266 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1266 this.fromJSON(data);
1267 this.fromJSON(data);
1267 if (this.ncells() === 0) {
1268 if (this.ncells() === 0) {
1268 this.insert_cell_below('code');
1269 this.insert_cell_below('code');
1269 };
1270 };
1270 this.dirty = false;
1271 this.dirty = false;
1271 this.select(0);
1272 this.select(0);
1272 this.scroll_to_top();
1273 this.scroll_to_top();
1273 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1274 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1274 msg = "This notebook has been converted from an older " +
1275 msg = "This notebook has been converted from an older " +
1275 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1276 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1276 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1277 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1277 "newer notebook format will be used and older verions of IPython " +
1278 "newer notebook format will be used and older verions of IPython " +
1278 "may not be able to read it. To keep the older version, close the " +
1279 "may not be able to read it. To keep the older version, close the " +
1279 "notebook without saving it.";
1280 "notebook without saving it.";
1280 var dialog = $('<div/>');
1281 var dialog = $('<div/>');
1281 dialog.html(msg);
1282 dialog.html(msg);
1282 this.element.append(dialog);
1283 this.element.append(dialog);
1283 dialog.dialog({
1284 dialog.dialog({
1284 resizable: false,
1285 resizable: false,
1285 modal: true,
1286 modal: true,
1286 title: "Notebook converted",
1287 title: "Notebook converted",
1287 closeText: "",
1288 closeText: "",
1288 close: function(event, ui) {$(this).dialog('destroy').remove();},
1289 close: function(event, ui) {$(this).dialog('destroy').remove();},
1289 buttons : {
1290 buttons : {
1290 "OK": function () {
1291 "OK": function () {
1291 $(this).dialog('close');
1292 $(this).dialog('close');
1292 }
1293 }
1293 },
1294 },
1294 width: 400
1295 width: 400
1295 });
1296 });
1296 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1297 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1297 var that = this;
1298 var that = this;
1298 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1299 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1299 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1300 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1300 msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1301 msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1301 this_vs + ". You can still work with this notebook, but some features " +
1302 this_vs + ". You can still work with this notebook, but some features " +
1302 "introduced in later notebook versions may not be available."
1303 "introduced in later notebook versions may not be available."
1303
1304
1304 var dialog = $('<div/>');
1305 var dialog = $('<div/>');
1305 dialog.html(msg);
1306 dialog.html(msg);
1306 this.element.append(dialog);
1307 this.element.append(dialog);
1307 dialog.dialog({
1308 dialog.dialog({
1308 resizable: false,
1309 resizable: false,
1309 modal: true,
1310 modal: true,
1310 title: "Newer Notebook",
1311 title: "Newer Notebook",
1311 closeText: "",
1312 closeText: "",
1312 close: function(event, ui) {$(this).dialog('destroy').remove();},
1313 close: function(event, ui) {$(this).dialog('destroy').remove();},
1313 buttons : {
1314 buttons : {
1314 "OK": function () {
1315 "OK": function () {
1315 $(this).dialog('close');
1316 $(this).dialog('close');
1316 }
1317 }
1317 },
1318 },
1318 width: 400
1319 width: 400
1319 });
1320 });
1320
1321
1321 }
1322 }
1322 // Create the kernel after the notebook is completely loaded to prevent
1323 // Create the kernel after the notebook is completely loaded to prevent
1323 // code execution upon loading, which is a security risk.
1324 // code execution upon loading, which is a security risk.
1324 if (! this.read_only) {
1325 if (! this.read_only) {
1325 this.start_kernel();
1326 this.start_kernel();
1326 }
1327 }
1327 $([IPython.events]).trigger('notebook_loaded.Notebook');
1328 $([IPython.events]).trigger('notebook_loaded.Notebook');
1328 };
1329 };
1329
1330
1330
1331
1331 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1332 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1332 if (xhr.status === 500) {
1333 if (xhr.status === 500) {
1333 msg = "An error occurred while loading this notebook. Most likely " +
1334 msg = "An error occurred while loading this notebook. Most likely " +
1334 "this notebook is in a newer format than is supported by this " +
1335 "this notebook is in a newer format than is supported by this " +
1335 "version of IPython. This version can load notebook formats " +
1336 "version of IPython. This version can load notebook formats " +
1336 "v"+this.nbformat+" or earlier.";
1337 "v"+this.nbformat+" or earlier.";
1337 var dialog = $('<div/>');
1338 var dialog = $('<div/>');
1338 dialog.html(msg);
1339 dialog.html(msg);
1339 this.element.append(dialog);
1340 this.element.append(dialog);
1340 dialog.dialog({
1341 dialog.dialog({
1341 resizable: false,
1342 resizable: false,
1342 modal: true,
1343 modal: true,
1343 title: "Error loading notebook",
1344 title: "Error loading notebook",
1344 closeText: "",
1345 closeText: "",
1345 close: function(event, ui) {$(this).dialog('destroy').remove();},
1346 close: function(event, ui) {$(this).dialog('destroy').remove();},
1346 buttons : {
1347 buttons : {
1347 "OK": function () {
1348 "OK": function () {
1348 $(this).dialog('close');
1349 $(this).dialog('close');
1349 }
1350 }
1350 },
1351 },
1351 width: 400
1352 width: 400
1352 });
1353 });
1353 }
1354 }
1354 }
1355 }
1355
1356
1356 IPython.Notebook = Notebook;
1357 IPython.Notebook = Notebook;
1357
1358
1358
1359
1359 return IPython;
1360 return IPython;
1360
1361
1361 }(IPython));
1362 }(IPython));
1362
1363
General Comments 0
You need to be logged in to leave comments. Login now