##// END OF EJS Templates
simplify logic
Matthias BUSSONNIER -
Show More
@@ -1,1418 +1,1417 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Notebook
9 // Notebook
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15 var key = IPython.utils.keycodes;
15 var key = IPython.utils.keycodes;
16
16
17 var Notebook = function (selector) {
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
563
564
564
565 /**
565 /**
566 * 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.
567 *
567 *
568 * Similar to insert_above, but index parameter is mandatory
568 * Similar to insert_above, but index parameter is mandatory
569 *
569 *
570 * Index will be brought back into the accissible range [0,n]
570 * Index will be brought back into the accissible range [0,n]
571 *
571 *
572 * @param type {string} in ['code','html','markdown','heading']
572 * @param type {string} in ['code','html','markdown','heading']
573 * @param [index] {int} a valid index where to inser cell
573 * @param [index] {int} a valid index where to inser cell
574 *
574 *
575 * @return cell {cell|null} created cell or null
575 * @return cell {cell|null} created cell or null
576 **/
576 **/
577 Notebook.prototype.insert_cell_at_index = function(type, index){
577 Notebook.prototype.insert_cell_at_index = function(type, index){
578
578
579 var ncells = this.ncells();
579 var ncells = this.ncells();
580 var index = Math.min(index,ncells);
580 var index = Math.min(index,ncells);
581 index = Math.max(index,0);
581 index = Math.max(index,0);
582 var cell = null;
582 var cell = null;
583
583
584 if (ncells === 0 || this.is_valid_cell_index(index) || index== ncells) {
584 if (ncells === 0 || this.is_valid_cell_index(index) || index== ncells) {
585 if (type === 'code') {
585 if (type === 'code') {
586 cell = new IPython.CodeCell(this.kernel);
586 cell = new IPython.CodeCell(this.kernel);
587 cell.set_input_prompt();
587 cell.set_input_prompt();
588 } else if (type === 'markdown') {
588 } else if (type === 'markdown') {
589 cell = new IPython.MarkdownCell();
589 cell = new IPython.MarkdownCell();
590 } else if (type === 'html') {
590 } else if (type === 'html') {
591 cell = new IPython.HTMLCell();
591 cell = new IPython.HTMLCell();
592 } else if (type === 'raw') {
592 } else if (type === 'raw') {
593 cell = new IPython.RawCell();
593 cell = new IPython.RawCell();
594 } else if (type === 'heading') {
594 } else if (type === 'heading') {
595 cell = new IPython.HeadingCell();
595 cell = new IPython.HeadingCell();
596 }
596 }
597
597
598 if(this._insert_element_at_index(cell.element,index)){
598 if(this._insert_element_at_index(cell.element,index)){
599 cell.render();
599 cell.render();
600 this.select(this.find_cell_index(cell));
600 this.select(this.find_cell_index(cell));
601 this.dirty = true;
601 this.dirty = true;
602 }
602 }
603 }
603 }
604 return cell;
604 return cell;
605
605
606 };
606 };
607
607
608 /**
608 /**
609 * Insert an element at given cell index.
609 * Insert an element at given cell index.
610 *
610 *
611 * @param element {dom element} a cell element
611 * @param element {dom element} a cell element
612 * @param [index] {int} a valid index where to inser cell
612 * @param [index] {int} a valid index where to inser cell
613 * @private
613 * @private
614 *
614 *
615 * return true if everything whent fine.
615 * return true if everything whent fine.
616 **/
616 **/
617 Notebook.prototype._insert_element_at_index = function(element, index){
617 Notebook.prototype._insert_element_at_index = function(element, index){
618 if (element == undefined){
619 return false;
620 }
621
618 var ncells = this.ncells();
622 var ncells = this.ncells();
619
623
620 /// this use to be index < this.undelete_index in some case
624 if (ncells === 0) {
625 // special case append if empty
626 this.element.find('div.end_space').before(element);
627 } else if ( ncells == index ) {
628 // special case append it the end, but not empty
629 this.get_cell_element(index-1).after(element);
630 } else if (this.is_valid_cell_index(index)) {
631 // otherwise always somewhere to append to
632 this.get_cell_element(index).before(element);
633 } else {
634 return false;
635 }
636
621 if (this.undelete_index !== null && index <= this.undelete_index) {
637 if (this.undelete_index !== null && index <= this.undelete_index) {
622 this.undelete_index = this.undelete_index + 1;
638 this.undelete_index = this.undelete_index + 1;
623 this.dirty = true;
639 this.dirty = true;
624 }
640 }
625
641 return true;
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;
643 };
642 };
644
643
645 /**
644 /**
646 * Insert a cell of given type above given index, or at top
645 * Insert a cell of given type above given index, or at top
647 * of notebook if index smaller than 0.
646 * of notebook if index smaller than 0.
648 *
647 *
649 * default index value is the one of currently selected cell
648 * default index value is the one of currently selected cell
650 *
649 *
651 * @param type {string} cell type
650 * @param type {string} cell type
652 * @param [index] {integer}
651 * @param [index] {integer}
653 *
652 *
654 * @return handle to created cell or null
653 * @return handle to created cell or null
655 **/
654 **/
656 Notebook.prototype.insert_cell_above = function (type, index) {
655 Notebook.prototype.insert_cell_above = function (type, index) {
657 index = this.index_or_selected(index);
656 index = this.index_or_selected(index);
658 return this.insert_cell_at_index(type, index);
657 return this.insert_cell_at_index(type, index);
659 };
658 };
660
659
661 /**
660 /**
662 * Insert a cell of given type below given index, or at bottom
661 * Insert a cell of given type below given index, or at bottom
663 * of notebook if index greater thatn number of cell
662 * of notebook if index greater thatn number of cell
664 *
663 *
665 * default index value is the one of currently selected cell
664 * default index value is the one of currently selected cell
666 *
665 *
667 * @method insert_cell_below
666 * @method insert_cell_below
668 * @param type {string} cell type
667 * @param type {string} cell type
669 * @param [index] {integer}
668 * @param [index] {integer}
670 *
669 *
671 * @return handle to created cell or null
670 * @return handle to created cell or null
672 *
671 *
673 **/
672 **/
674 Notebook.prototype.insert_cell_below = function (type, index) {
673 Notebook.prototype.insert_cell_below = function (type, index) {
675 index = this.index_or_selected(index);
674 index = this.index_or_selected(index);
676 return this.insert_cell_at_index(type, index+1);
675 return this.insert_cell_at_index(type, index+1);
677 };
676 };
678
677
679
678
680 /**
679 /**
681 * Insert cell at end of notebook
680 * Insert cell at end of notebook
682 *
681 *
683 * @method insert_cell_at_bottom
682 * @method insert_cell_at_bottom
684 * @param type {String} cell type
683 * @param type {String} cell type
685 *
684 *
686 * @return the added cell; or null
685 * @return the added cell; or null
687 **/
686 **/
688 Notebook.prototype.insert_cell_at_bottom = function (type){
687 Notebook.prototype.insert_cell_at_bottom = function (type){
689 var len = this.ncells();
688 var len = this.ncells();
690 return this.insert_cell_below(type,len-1);
689 return this.insert_cell_below(type,len-1);
691 };
690 };
692
691
693
692
694
693
695 Notebook.prototype.to_code = function (index) {
694 Notebook.prototype.to_code = function (index) {
696 var i = this.index_or_selected(index);
695 var i = this.index_or_selected(index);
697 if (this.is_valid_cell_index(i)) {
696 if (this.is_valid_cell_index(i)) {
698 var source_element = this.get_cell_element(i);
697 var source_element = this.get_cell_element(i);
699 var source_cell = source_element.data("cell");
698 var source_cell = source_element.data("cell");
700 if (!(source_cell instanceof IPython.CodeCell)) {
699 if (!(source_cell instanceof IPython.CodeCell)) {
701 var target_cell = this.insert_cell_below('code',i);
700 var target_cell = this.insert_cell_below('code',i);
702 var text = source_cell.get_text();
701 var text = source_cell.get_text();
703 if (text === source_cell.placeholder) {
702 if (text === source_cell.placeholder) {
704 text = '';
703 text = '';
705 }
704 }
706 target_cell.set_text(text);
705 target_cell.set_text(text);
707 // make this value the starting point, so that we can only undo
706 // make this value the starting point, so that we can only undo
708 // to this state, instead of a blank cell
707 // to this state, instead of a blank cell
709 target_cell.code_mirror.clearHistory();
708 target_cell.code_mirror.clearHistory();
710 source_element.remove();
709 source_element.remove();
711 this.dirty = true;
710 this.dirty = true;
712 };
711 };
713 };
712 };
714 };
713 };
715
714
716
715
717 Notebook.prototype.to_markdown = function (index) {
716 Notebook.prototype.to_markdown = function (index) {
718 var i = this.index_or_selected(index);
717 var i = this.index_or_selected(index);
719 if (this.is_valid_cell_index(i)) {
718 if (this.is_valid_cell_index(i)) {
720 var source_element = this.get_cell_element(i);
719 var source_element = this.get_cell_element(i);
721 var source_cell = source_element.data("cell");
720 var source_cell = source_element.data("cell");
722 if (!(source_cell instanceof IPython.MarkdownCell)) {
721 if (!(source_cell instanceof IPython.MarkdownCell)) {
723 var target_cell = this.insert_cell_below('markdown',i);
722 var target_cell = this.insert_cell_below('markdown',i);
724 var text = source_cell.get_text();
723 var text = source_cell.get_text();
725 if (text === source_cell.placeholder) {
724 if (text === source_cell.placeholder) {
726 text = '';
725 text = '';
727 };
726 };
728 // The edit must come before the set_text.
727 // The edit must come before the set_text.
729 target_cell.edit();
728 target_cell.edit();
730 target_cell.set_text(text);
729 target_cell.set_text(text);
731 // make this value the starting point, so that we can only undo
730 // make this value the starting point, so that we can only undo
732 // to this state, instead of a blank cell
731 // to this state, instead of a blank cell
733 target_cell.code_mirror.clearHistory();
732 target_cell.code_mirror.clearHistory();
734 source_element.remove();
733 source_element.remove();
735 this.dirty = true;
734 this.dirty = true;
736 };
735 };
737 };
736 };
738 };
737 };
739
738
740
739
741 Notebook.prototype.to_html = function (index) {
740 Notebook.prototype.to_html = function (index) {
742 var i = this.index_or_selected(index);
741 var i = this.index_or_selected(index);
743 if (this.is_valid_cell_index(i)) {
742 if (this.is_valid_cell_index(i)) {
744 var source_element = this.get_cell_element(i);
743 var source_element = this.get_cell_element(i);
745 var source_cell = source_element.data("cell");
744 var source_cell = source_element.data("cell");
746 var target_cell = null;
745 var target_cell = null;
747 if (!(source_cell instanceof IPython.HTMLCell)) {
746 if (!(source_cell instanceof IPython.HTMLCell)) {
748 target_cell = this.insert_cell_below('html',i);
747 target_cell = this.insert_cell_below('html',i);
749 var text = source_cell.get_text();
748 var text = source_cell.get_text();
750 if (text === source_cell.placeholder) {
749 if (text === source_cell.placeholder) {
751 text = '';
750 text = '';
752 };
751 };
753 // The edit must come before the set_text.
752 // The edit must come before the set_text.
754 target_cell.edit();
753 target_cell.edit();
755 target_cell.set_text(text);
754 target_cell.set_text(text);
756 // 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
757 // to this state, instead of a blank cell
756 // to this state, instead of a blank cell
758 target_cell.code_mirror.clearHistory();
757 target_cell.code_mirror.clearHistory();
759 source_element.remove();
758 source_element.remove();
760 this.dirty = true;
759 this.dirty = true;
761 };
760 };
762 };
761 };
763 };
762 };
764
763
765
764
766 Notebook.prototype.to_raw = function (index) {
765 Notebook.prototype.to_raw = function (index) {
767 var i = this.index_or_selected(index);
766 var i = this.index_or_selected(index);
768 if (this.is_valid_cell_index(i)) {
767 if (this.is_valid_cell_index(i)) {
769 var source_element = this.get_cell_element(i);
768 var source_element = this.get_cell_element(i);
770 var source_cell = source_element.data("cell");
769 var source_cell = source_element.data("cell");
771 var target_cell = null;
770 var target_cell = null;
772 if (!(source_cell instanceof IPython.RawCell)) {
771 if (!(source_cell instanceof IPython.RawCell)) {
773 target_cell = this.insert_cell_below('raw',i);
772 target_cell = this.insert_cell_below('raw',i);
774 var text = source_cell.get_text();
773 var text = source_cell.get_text();
775 if (text === source_cell.placeholder) {
774 if (text === source_cell.placeholder) {
776 text = '';
775 text = '';
777 };
776 };
778 // The edit must come before the set_text.
777 // The edit must come before the set_text.
779 target_cell.edit();
778 target_cell.edit();
780 target_cell.set_text(text);
779 target_cell.set_text(text);
781 // make this value the starting point, so that we can only undo
780 // make this value the starting point, so that we can only undo
782 // to this state, instead of a blank cell
781 // to this state, instead of a blank cell
783 target_cell.code_mirror.clearHistory();
782 target_cell.code_mirror.clearHistory();
784 source_element.remove();
783 source_element.remove();
785 this.dirty = true;
784 this.dirty = true;
786 };
785 };
787 };
786 };
788 };
787 };
789
788
790
789
791 Notebook.prototype.to_heading = function (index, level) {
790 Notebook.prototype.to_heading = function (index, level) {
792 level = level || 1;
791 level = level || 1;
793 var i = this.index_or_selected(index);
792 var i = this.index_or_selected(index);
794 if (this.is_valid_cell_index(i)) {
793 if (this.is_valid_cell_index(i)) {
795 var source_element = this.get_cell_element(i);
794 var source_element = this.get_cell_element(i);
796 var source_cell = source_element.data("cell");
795 var source_cell = source_element.data("cell");
797 var target_cell = null;
796 var target_cell = null;
798 if (source_cell instanceof IPython.HeadingCell) {
797 if (source_cell instanceof IPython.HeadingCell) {
799 source_cell.set_level(level);
798 source_cell.set_level(level);
800 } else {
799 } else {
801 target_cell = this.insert_cell_below('heading',i);
800 target_cell = this.insert_cell_below('heading',i);
802 var text = source_cell.get_text();
801 var text = source_cell.get_text();
803 if (text === source_cell.placeholder) {
802 if (text === source_cell.placeholder) {
804 text = '';
803 text = '';
805 };
804 };
806 // The edit must come before the set_text.
805 // The edit must come before the set_text.
807 target_cell.set_level(level);
806 target_cell.set_level(level);
808 target_cell.edit();
807 target_cell.edit();
809 target_cell.set_text(text);
808 target_cell.set_text(text);
810 // make this value the starting point, so that we can only undo
809 // make this value the starting point, so that we can only undo
811 // to this state, instead of a blank cell
810 // to this state, instead of a blank cell
812 target_cell.code_mirror.clearHistory();
811 target_cell.code_mirror.clearHistory();
813 source_element.remove();
812 source_element.remove();
814 this.dirty = true;
813 this.dirty = true;
815 };
814 };
816 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
815 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
817 {'cell_type':'heading',level:level}
816 {'cell_type':'heading',level:level}
818 );
817 );
819 };
818 };
820 };
819 };
821
820
822
821
823 // Cut/Copy/Paste
822 // Cut/Copy/Paste
824
823
825 Notebook.prototype.enable_paste = function () {
824 Notebook.prototype.enable_paste = function () {
826 var that = this;
825 var that = this;
827 if (!this.paste_enabled) {
826 if (!this.paste_enabled) {
828 $('#paste_cell_replace').removeClass('ui-state-disabled')
827 $('#paste_cell_replace').removeClass('ui-state-disabled')
829 .on('click', function () {that.paste_cell_replace();});
828 .on('click', function () {that.paste_cell_replace();});
830 $('#paste_cell_above').removeClass('ui-state-disabled')
829 $('#paste_cell_above').removeClass('ui-state-disabled')
831 .on('click', function () {that.paste_cell_above();});
830 .on('click', function () {that.paste_cell_above();});
832 $('#paste_cell_below').removeClass('ui-state-disabled')
831 $('#paste_cell_below').removeClass('ui-state-disabled')
833 .on('click', function () {that.paste_cell_below();});
832 .on('click', function () {that.paste_cell_below();});
834 this.paste_enabled = true;
833 this.paste_enabled = true;
835 };
834 };
836 };
835 };
837
836
838
837
839 Notebook.prototype.disable_paste = function () {
838 Notebook.prototype.disable_paste = function () {
840 if (this.paste_enabled) {
839 if (this.paste_enabled) {
841 $('#paste_cell_replace').addClass('ui-state-disabled').off('click');
840 $('#paste_cell_replace').addClass('ui-state-disabled').off('click');
842 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
841 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
843 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
842 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
844 this.paste_enabled = false;
843 this.paste_enabled = false;
845 };
844 };
846 };
845 };
847
846
848
847
849 Notebook.prototype.cut_cell = function () {
848 Notebook.prototype.cut_cell = function () {
850 this.copy_cell();
849 this.copy_cell();
851 this.delete_cell();
850 this.delete_cell();
852 }
851 }
853
852
854 Notebook.prototype.copy_cell = function () {
853 Notebook.prototype.copy_cell = function () {
855 var cell = this.get_selected_cell();
854 var cell = this.get_selected_cell();
856 this.clipboard = cell.toJSON();
855 this.clipboard = cell.toJSON();
857 this.enable_paste();
856 this.enable_paste();
858 };
857 };
859
858
860
859
861 Notebook.prototype.paste_cell_replace = function () {
860 Notebook.prototype.paste_cell_replace = function () {
862 if (this.clipboard !== null && this.paste_enabled) {
861 if (this.clipboard !== null && this.paste_enabled) {
863 var cell_data = this.clipboard;
862 var cell_data = this.clipboard;
864 var new_cell = this.insert_cell_above(cell_data.cell_type);
863 var new_cell = this.insert_cell_above(cell_data.cell_type);
865 new_cell.fromJSON(cell_data);
864 new_cell.fromJSON(cell_data);
866 var old_cell = this.get_next_cell(new_cell);
865 var old_cell = this.get_next_cell(new_cell);
867 this.delete_cell(this.find_cell_index(old_cell));
866 this.delete_cell(this.find_cell_index(old_cell));
868 this.select(this.find_cell_index(new_cell));
867 this.select(this.find_cell_index(new_cell));
869 };
868 };
870 };
869 };
871
870
872
871
873 Notebook.prototype.paste_cell_above = function () {
872 Notebook.prototype.paste_cell_above = function () {
874 if (this.clipboard !== null && this.paste_enabled) {
873 if (this.clipboard !== null && this.paste_enabled) {
875 var cell_data = this.clipboard;
874 var cell_data = this.clipboard;
876 var new_cell = this.insert_cell_above(cell_data.cell_type);
875 var new_cell = this.insert_cell_above(cell_data.cell_type);
877 new_cell.fromJSON(cell_data);
876 new_cell.fromJSON(cell_data);
878 };
877 };
879 };
878 };
880
879
881
880
882 Notebook.prototype.paste_cell_below = function () {
881 Notebook.prototype.paste_cell_below = function () {
883 if (this.clipboard !== null && this.paste_enabled) {
882 if (this.clipboard !== null && this.paste_enabled) {
884 var cell_data = this.clipboard;
883 var cell_data = this.clipboard;
885 var new_cell = this.insert_cell_below(cell_data.cell_type);
884 var new_cell = this.insert_cell_below(cell_data.cell_type);
886 new_cell.fromJSON(cell_data);
885 new_cell.fromJSON(cell_data);
887 };
886 };
888 };
887 };
889
888
890 // Cell undelete
889 // Cell undelete
891
890
892 Notebook.prototype.undelete = function() {
891 Notebook.prototype.undelete = function() {
893 if (this.undelete_backup !== null && this.undelete_index !== null) {
892 if (this.undelete_backup !== null && this.undelete_index !== null) {
894 var current_index = this.get_selected_index();
893 var current_index = this.get_selected_index();
895 if (this.undelete_index < current_index) {
894 if (this.undelete_index < current_index) {
896 current_index = current_index + 1;
895 current_index = current_index + 1;
897 }
896 }
898 if (this.undelete_index >= this.ncells()) {
897 if (this.undelete_index >= this.ncells()) {
899 this.select(this.ncells() - 1);
898 this.select(this.ncells() - 1);
900 }
899 }
901 else {
900 else {
902 this.select(this.undelete_index);
901 this.select(this.undelete_index);
903 }
902 }
904 var cell_data = this.undelete_backup;
903 var cell_data = this.undelete_backup;
905 var new_cell = null;
904 var new_cell = null;
906 if (this.undelete_below) {
905 if (this.undelete_below) {
907 new_cell = this.insert_cell_below(cell_data.cell_type);
906 new_cell = this.insert_cell_below(cell_data.cell_type);
908 } else {
907 } else {
909 new_cell = this.insert_cell_above(cell_data.cell_type);
908 new_cell = this.insert_cell_above(cell_data.cell_type);
910 }
909 }
911 new_cell.fromJSON(cell_data);
910 new_cell.fromJSON(cell_data);
912 this.select(current_index);
911 this.select(current_index);
913 this.undelete_backup = null;
912 this.undelete_backup = null;
914 this.undelete_index = null;
913 this.undelete_index = null;
915 }
914 }
916 }
915 }
917
916
918 // Split/merge
917 // Split/merge
919
918
920 Notebook.prototype.split_cell = function () {
919 Notebook.prototype.split_cell = function () {
921 // Todo: implement spliting for other cell types.
920 // Todo: implement spliting for other cell types.
922 var cell = this.get_selected_cell();
921 var cell = this.get_selected_cell();
923 if (cell.is_splittable()) {
922 if (cell.is_splittable()) {
924 var texta = cell.get_pre_cursor();
923 var texta = cell.get_pre_cursor();
925 var textb = cell.get_post_cursor();
924 var textb = cell.get_post_cursor();
926 if (cell instanceof IPython.CodeCell) {
925 if (cell instanceof IPython.CodeCell) {
927 cell.set_text(texta);
926 cell.set_text(texta);
928 var new_cell = this.insert_cell_below('code');
927 var new_cell = this.insert_cell_below('code');
929 new_cell.set_text(textb);
928 new_cell.set_text(textb);
930 } else if (cell instanceof IPython.MarkdownCell) {
929 } else if (cell instanceof IPython.MarkdownCell) {
931 cell.set_text(texta);
930 cell.set_text(texta);
932 cell.render();
931 cell.render();
933 var new_cell = this.insert_cell_below('markdown');
932 var new_cell = this.insert_cell_below('markdown');
934 new_cell.edit(); // editor must be visible to call set_text
933 new_cell.edit(); // editor must be visible to call set_text
935 new_cell.set_text(textb);
934 new_cell.set_text(textb);
936 new_cell.render();
935 new_cell.render();
937 } else if (cell instanceof IPython.HTMLCell) {
936 } else if (cell instanceof IPython.HTMLCell) {
938 cell.set_text(texta);
937 cell.set_text(texta);
939 cell.render();
938 cell.render();
940 var new_cell = this.insert_cell_below('html');
939 var new_cell = this.insert_cell_below('html');
941 new_cell.edit(); // editor must be visible to call set_text
940 new_cell.edit(); // editor must be visible to call set_text
942 new_cell.set_text(textb);
941 new_cell.set_text(textb);
943 new_cell.render();
942 new_cell.render();
944 };
943 };
945 };
944 };
946 };
945 };
947
946
948
947
949 Notebook.prototype.merge_cell_above = function () {
948 Notebook.prototype.merge_cell_above = function () {
950 var index = this.get_selected_index();
949 var index = this.get_selected_index();
951 var cell = this.get_cell(index);
950 var cell = this.get_cell(index);
952 if (index > 0) {
951 if (index > 0) {
953 var upper_cell = this.get_cell(index-1);
952 var upper_cell = this.get_cell(index-1);
954 var upper_text = upper_cell.get_text();
953 var upper_text = upper_cell.get_text();
955 var text = cell.get_text();
954 var text = cell.get_text();
956 if (cell instanceof IPython.CodeCell) {
955 if (cell instanceof IPython.CodeCell) {
957 cell.set_text(upper_text+'\n'+text);
956 cell.set_text(upper_text+'\n'+text);
958 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
957 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
959 cell.edit();
958 cell.edit();
960 cell.set_text(upper_text+'\n'+text);
959 cell.set_text(upper_text+'\n'+text);
961 cell.render();
960 cell.render();
962 };
961 };
963 this.delete_cell(index-1);
962 this.delete_cell(index-1);
964 this.select(this.find_cell_index(cell));
963 this.select(this.find_cell_index(cell));
965 };
964 };
966 };
965 };
967
966
968
967
969 Notebook.prototype.merge_cell_below = function () {
968 Notebook.prototype.merge_cell_below = function () {
970 var index = this.get_selected_index();
969 var index = this.get_selected_index();
971 var cell = this.get_cell(index);
970 var cell = this.get_cell(index);
972 if (index < this.ncells()-1) {
971 if (index < this.ncells()-1) {
973 var lower_cell = this.get_cell(index+1);
972 var lower_cell = this.get_cell(index+1);
974 var lower_text = lower_cell.get_text();
973 var lower_text = lower_cell.get_text();
975 var text = cell.get_text();
974 var text = cell.get_text();
976 if (cell instanceof IPython.CodeCell) {
975 if (cell instanceof IPython.CodeCell) {
977 cell.set_text(text+'\n'+lower_text);
976 cell.set_text(text+'\n'+lower_text);
978 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
977 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
979 cell.edit();
978 cell.edit();
980 cell.set_text(text+'\n'+lower_text);
979 cell.set_text(text+'\n'+lower_text);
981 cell.render();
980 cell.render();
982 };
981 };
983 this.delete_cell(index+1);
982 this.delete_cell(index+1);
984 this.select(this.find_cell_index(cell));
983 this.select(this.find_cell_index(cell));
985 };
984 };
986 };
985 };
987
986
988
987
989 // Cell collapsing and output clearing
988 // Cell collapsing and output clearing
990
989
991 Notebook.prototype.collapse = function (index) {
990 Notebook.prototype.collapse = function (index) {
992 var i = this.index_or_selected(index);
991 var i = this.index_or_selected(index);
993 this.get_cell(i).collapse();
992 this.get_cell(i).collapse();
994 this.dirty = true;
993 this.dirty = true;
995 };
994 };
996
995
997
996
998 Notebook.prototype.expand = function (index) {
997 Notebook.prototype.expand = function (index) {
999 var i = this.index_or_selected(index);
998 var i = this.index_or_selected(index);
1000 this.get_cell(i).expand();
999 this.get_cell(i).expand();
1001 this.dirty = true;
1000 this.dirty = true;
1002 };
1001 };
1003
1002
1004
1003
1005 Notebook.prototype.toggle_output = function (index) {
1004 Notebook.prototype.toggle_output = function (index) {
1006 var i = this.index_or_selected(index);
1005 var i = this.index_or_selected(index);
1007 this.get_cell(i).toggle_output();
1006 this.get_cell(i).toggle_output();
1008 this.dirty = true;
1007 this.dirty = true;
1009 };
1008 };
1010
1009
1011
1010
1012 Notebook.prototype.toggle_output_scroll = function (index) {
1011 Notebook.prototype.toggle_output_scroll = function (index) {
1013 var i = this.index_or_selected(index);
1012 var i = this.index_or_selected(index);
1014 this.get_cell(i).toggle_output_scroll();
1013 this.get_cell(i).toggle_output_scroll();
1015 };
1014 };
1016
1015
1017
1016
1018 Notebook.prototype.collapse_all_output = function () {
1017 Notebook.prototype.collapse_all_output = function () {
1019 var ncells = this.ncells();
1018 var ncells = this.ncells();
1020 var cells = this.get_cells();
1019 var cells = this.get_cells();
1021 for (var i=0; i<ncells; i++) {
1020 for (var i=0; i<ncells; i++) {
1022 if (cells[i] instanceof IPython.CodeCell) {
1021 if (cells[i] instanceof IPython.CodeCell) {
1023 cells[i].output_area.collapse();
1022 cells[i].output_area.collapse();
1024 }
1023 }
1025 };
1024 };
1026 // this should not be set if the `collapse` key is removed from nbformat
1025 // this should not be set if the `collapse` key is removed from nbformat
1027 this.dirty = true;
1026 this.dirty = true;
1028 };
1027 };
1029
1028
1030
1029
1031 Notebook.prototype.scroll_all_output = function () {
1030 Notebook.prototype.scroll_all_output = function () {
1032 var ncells = this.ncells();
1031 var ncells = this.ncells();
1033 var cells = this.get_cells();
1032 var cells = this.get_cells();
1034 for (var i=0; i<ncells; i++) {
1033 for (var i=0; i<ncells; i++) {
1035 if (cells[i] instanceof IPython.CodeCell) {
1034 if (cells[i] instanceof IPython.CodeCell) {
1036 cells[i].output_area.expand();
1035 cells[i].output_area.expand();
1037 cells[i].output_area.scroll_if_long(20);
1036 cells[i].output_area.scroll_if_long(20);
1038 }
1037 }
1039 };
1038 };
1040 // this should not be set if the `collapse` key is removed from nbformat
1039 // this should not be set if the `collapse` key is removed from nbformat
1041 this.dirty = true;
1040 this.dirty = true;
1042 };
1041 };
1043
1042
1044
1043
1045 Notebook.prototype.expand_all_output = function () {
1044 Notebook.prototype.expand_all_output = function () {
1046 var ncells = this.ncells();
1045 var ncells = this.ncells();
1047 var cells = this.get_cells();
1046 var cells = this.get_cells();
1048 for (var i=0; i<ncells; i++) {
1047 for (var i=0; i<ncells; i++) {
1049 if (cells[i] instanceof IPython.CodeCell) {
1048 if (cells[i] instanceof IPython.CodeCell) {
1050 cells[i].output_area.expand();
1049 cells[i].output_area.expand();
1051 cells[i].output_area.unscroll_area();
1050 cells[i].output_area.unscroll_area();
1052 }
1051 }
1053 };
1052 };
1054 // this should not be set if the `collapse` key is removed from nbformat
1053 // this should not be set if the `collapse` key is removed from nbformat
1055 this.dirty = true;
1054 this.dirty = true;
1056 };
1055 };
1057
1056
1058
1057
1059 Notebook.prototype.clear_all_output = function () {
1058 Notebook.prototype.clear_all_output = function () {
1060 var ncells = this.ncells();
1059 var ncells = this.ncells();
1061 var cells = this.get_cells();
1060 var cells = this.get_cells();
1062 for (var i=0; i<ncells; i++) {
1061 for (var i=0; i<ncells; i++) {
1063 if (cells[i] instanceof IPython.CodeCell) {
1062 if (cells[i] instanceof IPython.CodeCell) {
1064 cells[i].clear_output(true,true,true);
1063 cells[i].clear_output(true,true,true);
1065 // Make all In[] prompts blank, as well
1064 // Make all In[] prompts blank, as well
1066 // TODO: make this configurable (via checkbox?)
1065 // TODO: make this configurable (via checkbox?)
1067 cells[i].set_input_prompt();
1066 cells[i].set_input_prompt();
1068 }
1067 }
1069 };
1068 };
1070 this.dirty = true;
1069 this.dirty = true;
1071 };
1070 };
1072
1071
1073
1072
1074 // Other cell functions: line numbers, ...
1073 // Other cell functions: line numbers, ...
1075
1074
1076 Notebook.prototype.cell_toggle_line_numbers = function() {
1075 Notebook.prototype.cell_toggle_line_numbers = function() {
1077 this.get_selected_cell().toggle_line_numbers();
1076 this.get_selected_cell().toggle_line_numbers();
1078 };
1077 };
1079
1078
1080 // Kernel related things
1079 // Kernel related things
1081
1080
1082 Notebook.prototype.start_kernel = function () {
1081 Notebook.prototype.start_kernel = function () {
1083 var base_url = $('body').data('baseKernelUrl') + "kernels";
1082 var base_url = $('body').data('baseKernelUrl') + "kernels";
1084 this.kernel = new IPython.Kernel(base_url);
1083 this.kernel = new IPython.Kernel(base_url);
1085 this.kernel.start(this.notebook_id);
1084 this.kernel.start(this.notebook_id);
1086 // Now that the kernel has been created, tell the CodeCells about it.
1085 // Now that the kernel has been created, tell the CodeCells about it.
1087 var ncells = this.ncells();
1086 var ncells = this.ncells();
1088 for (var i=0; i<ncells; i++) {
1087 for (var i=0; i<ncells; i++) {
1089 var cell = this.get_cell(i);
1088 var cell = this.get_cell(i);
1090 if (cell instanceof IPython.CodeCell) {
1089 if (cell instanceof IPython.CodeCell) {
1091 cell.set_kernel(this.kernel)
1090 cell.set_kernel(this.kernel)
1092 };
1091 };
1093 };
1092 };
1094 };
1093 };
1095
1094
1096
1095
1097 Notebook.prototype.restart_kernel = function () {
1096 Notebook.prototype.restart_kernel = function () {
1098 var that = this;
1097 var that = this;
1099 var dialog = $('<div/>');
1098 var dialog = $('<div/>');
1100 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
1099 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
1101 $(document).append(dialog);
1100 $(document).append(dialog);
1102 dialog.dialog({
1101 dialog.dialog({
1103 resizable: false,
1102 resizable: false,
1104 modal: true,
1103 modal: true,
1105 title: "Restart kernel or continue running?",
1104 title: "Restart kernel or continue running?",
1106 closeText: '',
1105 closeText: '',
1107 buttons : {
1106 buttons : {
1108 "Restart": function () {
1107 "Restart": function () {
1109 that.kernel.restart();
1108 that.kernel.restart();
1110 $(this).dialog('close');
1109 $(this).dialog('close');
1111 },
1110 },
1112 "Continue running": function () {
1111 "Continue running": function () {
1113 $(this).dialog('close');
1112 $(this).dialog('close');
1114 }
1113 }
1115 }
1114 }
1116 });
1115 });
1117 };
1116 };
1118
1117
1119
1118
1120 Notebook.prototype.execute_selected_cell = function (options) {
1119 Notebook.prototype.execute_selected_cell = function (options) {
1121 // add_new: should a new cell be added if we are at the end of the nb
1120 // add_new: should a new cell be added if we are at the end of the nb
1122 // terminal: execute in terminal mode, which stays in the current cell
1121 // terminal: execute in terminal mode, which stays in the current cell
1123 var default_options = {terminal: false, add_new: true};
1122 var default_options = {terminal: false, add_new: true};
1124 $.extend(default_options, options);
1123 $.extend(default_options, options);
1125 var that = this;
1124 var that = this;
1126 var cell = that.get_selected_cell();
1125 var cell = that.get_selected_cell();
1127 var cell_index = that.find_cell_index(cell);
1126 var cell_index = that.find_cell_index(cell);
1128 if (cell instanceof IPython.CodeCell) {
1127 if (cell instanceof IPython.CodeCell) {
1129 cell.execute();
1128 cell.execute();
1130 } else if (cell instanceof IPython.HTMLCell) {
1129 } else if (cell instanceof IPython.HTMLCell) {
1131 cell.render();
1130 cell.render();
1132 }
1131 }
1133 if (default_options.terminal) {
1132 if (default_options.terminal) {
1134 cell.select_all();
1133 cell.select_all();
1135 } else {
1134 } else {
1136 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1135 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1137 that.insert_cell_below('code');
1136 that.insert_cell_below('code');
1138 // If we are adding a new cell at the end, scroll down to show it.
1137 // If we are adding a new cell at the end, scroll down to show it.
1139 that.scroll_to_bottom();
1138 that.scroll_to_bottom();
1140 } else {
1139 } else {
1141 that.select(cell_index+1);
1140 that.select(cell_index+1);
1142 };
1141 };
1143 };
1142 };
1144 this.dirty = true;
1143 this.dirty = true;
1145 };
1144 };
1146
1145
1147
1146
1148 Notebook.prototype.execute_cells_below = function () {
1147 Notebook.prototype.execute_cells_below = function () {
1149 this.execute_cell_range(this.get_selected_index(), this.ncells());
1148 this.execute_cell_range(this.get_selected_index(), this.ncells());
1150 this.scroll_to_bottom();
1149 this.scroll_to_bottom();
1151 };
1150 };
1152
1151
1153 Notebook.prototype.execute_cells_above = function () {
1152 Notebook.prototype.execute_cells_above = function () {
1154 this.execute_cell_range(0, this.get_selected_index());
1153 this.execute_cell_range(0, this.get_selected_index());
1155 };
1154 };
1156
1155
1157 Notebook.prototype.execute_all_cells = function () {
1156 Notebook.prototype.execute_all_cells = function () {
1158 this.execute_cell_range(0, this.ncells());
1157 this.execute_cell_range(0, this.ncells());
1159 that.scroll_to_bottom();
1158 that.scroll_to_bottom();
1160 };
1159 };
1161
1160
1162 Notebook.prototype.execute_cell_range = function (start, end) {
1161 Notebook.prototype.execute_cell_range = function (start, end) {
1163 for (var i=start; i<end; i++) {
1162 for (var i=start; i<end; i++) {
1164 this.select(i);
1163 this.select(i);
1165 this.execute_selected_cell({add_new:false});
1164 this.execute_selected_cell({add_new:false});
1166 };
1165 };
1167 };
1166 };
1168
1167
1169 // Persistance and loading
1168 // Persistance and loading
1170
1169
1171 Notebook.prototype.get_notebook_id = function () {
1170 Notebook.prototype.get_notebook_id = function () {
1172 return this.notebook_id;
1171 return this.notebook_id;
1173 };
1172 };
1174
1173
1175
1174
1176 Notebook.prototype.get_notebook_name = function () {
1175 Notebook.prototype.get_notebook_name = function () {
1177 return this.notebook_name;
1176 return this.notebook_name;
1178 };
1177 };
1179
1178
1180
1179
1181 Notebook.prototype.set_notebook_name = function (name) {
1180 Notebook.prototype.set_notebook_name = function (name) {
1182 this.notebook_name = name;
1181 this.notebook_name = name;
1183 };
1182 };
1184
1183
1185
1184
1186 Notebook.prototype.test_notebook_name = function (nbname) {
1185 Notebook.prototype.test_notebook_name = function (nbname) {
1187 nbname = nbname || '';
1186 nbname = nbname || '';
1188 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1187 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1189 return true;
1188 return true;
1190 } else {
1189 } else {
1191 return false;
1190 return false;
1192 };
1191 };
1193 };
1192 };
1194
1193
1195
1194
1196 Notebook.prototype.fromJSON = function (data) {
1195 Notebook.prototype.fromJSON = function (data) {
1197 var ncells = this.ncells();
1196 var ncells = this.ncells();
1198 var i;
1197 var i;
1199 for (i=0; i<ncells; i++) {
1198 for (i=0; i<ncells; i++) {
1200 // Always delete cell 0 as they get renumbered as they are deleted.
1199 // Always delete cell 0 as they get renumbered as they are deleted.
1201 this.delete_cell(0);
1200 this.delete_cell(0);
1202 };
1201 };
1203 // Save the metadata and name.
1202 // Save the metadata and name.
1204 this.metadata = data.metadata;
1203 this.metadata = data.metadata;
1205 this.notebook_name = data.metadata.name;
1204 this.notebook_name = data.metadata.name;
1206 // Only handle 1 worksheet for now.
1205 // Only handle 1 worksheet for now.
1207 var worksheet = data.worksheets[0];
1206 var worksheet = data.worksheets[0];
1208 if (worksheet !== undefined) {
1207 if (worksheet !== undefined) {
1209 if (worksheet.metadata) {
1208 if (worksheet.metadata) {
1210 this.worksheet_metadata = worksheet.metadata;
1209 this.worksheet_metadata = worksheet.metadata;
1211 }
1210 }
1212 var new_cells = worksheet.cells;
1211 var new_cells = worksheet.cells;
1213 ncells = new_cells.length;
1212 ncells = new_cells.length;
1214 var cell_data = null;
1213 var cell_data = null;
1215 var new_cell = null;
1214 var new_cell = null;
1216 for (i=0; i<ncells; i++) {
1215 for (i=0; i<ncells; i++) {
1217 cell_data = new_cells[i];
1216 cell_data = new_cells[i];
1218 // VERSIONHACK: plaintext -> raw
1217 // VERSIONHACK: plaintext -> raw
1219 // handle never-released plaintext name for raw cells
1218 // handle never-released plaintext name for raw cells
1220 if (cell_data.cell_type === 'plaintext'){
1219 if (cell_data.cell_type === 'plaintext'){
1221 cell_data.cell_type = 'raw';
1220 cell_data.cell_type = 'raw';
1222 }
1221 }
1223
1222
1224 new_cell = this.insert_cell_below(cell_data.cell_type);
1223 new_cell = this.insert_cell_below(cell_data.cell_type);
1225 new_cell.fromJSON(cell_data);
1224 new_cell.fromJSON(cell_data);
1226 };
1225 };
1227 };
1226 };
1228 if (data.worksheets.length > 1) {
1227 if (data.worksheets.length > 1) {
1229 var dialog = $('<div/>');
1228 var dialog = $('<div/>');
1230 dialog.html("This notebook has " + data.worksheets.length + " worksheets, " +
1229 dialog.html("This notebook has " + data.worksheets.length + " worksheets, " +
1231 "but this version of IPython can only handle the first. " +
1230 "but this version of IPython can only handle the first. " +
1232 "If you save this notebook, worksheets after the first will be lost."
1231 "If you save this notebook, worksheets after the first will be lost."
1233 );
1232 );
1234 this.element.append(dialog);
1233 this.element.append(dialog);
1235 dialog.dialog({
1234 dialog.dialog({
1236 resizable: false,
1235 resizable: false,
1237 modal: true,
1236 modal: true,
1238 title: "Multiple worksheets",
1237 title: "Multiple worksheets",
1239 closeText: "",
1238 closeText: "",
1240 close: function(event, ui) {$(this).dialog('destroy').remove();},
1239 close: function(event, ui) {$(this).dialog('destroy').remove();},
1241 buttons : {
1240 buttons : {
1242 "OK": function () {
1241 "OK": function () {
1243 $(this).dialog('close');
1242 $(this).dialog('close');
1244 }
1243 }
1245 },
1244 },
1246 width: 400
1245 width: 400
1247 });
1246 });
1248 }
1247 }
1249 };
1248 };
1250
1249
1251
1250
1252 Notebook.prototype.toJSON = function () {
1251 Notebook.prototype.toJSON = function () {
1253 var cells = this.get_cells();
1252 var cells = this.get_cells();
1254 var ncells = cells.length;
1253 var ncells = cells.length;
1255 var cell_array = new Array(ncells);
1254 var cell_array = new Array(ncells);
1256 for (var i=0; i<ncells; i++) {
1255 for (var i=0; i<ncells; i++) {
1257 cell_array[i] = cells[i].toJSON();
1256 cell_array[i] = cells[i].toJSON();
1258 };
1257 };
1259 var data = {
1258 var data = {
1260 // Only handle 1 worksheet for now.
1259 // Only handle 1 worksheet for now.
1261 worksheets : [{
1260 worksheets : [{
1262 cells: cell_array,
1261 cells: cell_array,
1263 metadata: this.worksheet_metadata
1262 metadata: this.worksheet_metadata
1264 }],
1263 }],
1265 metadata : this.metadata
1264 metadata : this.metadata
1266 };
1265 };
1267 return data;
1266 return data;
1268 };
1267 };
1269
1268
1270 Notebook.prototype.save_notebook = function () {
1269 Notebook.prototype.save_notebook = function () {
1271 // We may want to move the name/id/nbformat logic inside toJSON?
1270 // We may want to move the name/id/nbformat logic inside toJSON?
1272 var data = this.toJSON();
1271 var data = this.toJSON();
1273 data.metadata.name = this.notebook_name;
1272 data.metadata.name = this.notebook_name;
1274 data.nbformat = this.nbformat;
1273 data.nbformat = this.nbformat;
1275 data.nbformat_minor = this.nbformat_minor;
1274 data.nbformat_minor = this.nbformat_minor;
1276 // We do the call with settings so we can set cache to false.
1275 // We do the call with settings so we can set cache to false.
1277 var settings = {
1276 var settings = {
1278 processData : false,
1277 processData : false,
1279 cache : false,
1278 cache : false,
1280 type : "PUT",
1279 type : "PUT",
1281 data : JSON.stringify(data),
1280 data : JSON.stringify(data),
1282 headers : {'Content-Type': 'application/json'},
1281 headers : {'Content-Type': 'application/json'},
1283 success : $.proxy(this.save_notebook_success,this),
1282 success : $.proxy(this.save_notebook_success,this),
1284 error : $.proxy(this.save_notebook_error,this)
1283 error : $.proxy(this.save_notebook_error,this)
1285 };
1284 };
1286 $([IPython.events]).trigger('notebook_saving.Notebook');
1285 $([IPython.events]).trigger('notebook_saving.Notebook');
1287 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1286 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1288 $.ajax(url, settings);
1287 $.ajax(url, settings);
1289 };
1288 };
1290
1289
1291
1290
1292 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1291 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1293 this.dirty = false;
1292 this.dirty = false;
1294 $([IPython.events]).trigger('notebook_saved.Notebook');
1293 $([IPython.events]).trigger('notebook_saved.Notebook');
1295 };
1294 };
1296
1295
1297
1296
1298 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1297 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1299 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1298 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1300 };
1299 };
1301
1300
1302
1301
1303 Notebook.prototype.load_notebook = function (notebook_id) {
1302 Notebook.prototype.load_notebook = function (notebook_id) {
1304 var that = this;
1303 var that = this;
1305 this.notebook_id = notebook_id;
1304 this.notebook_id = notebook_id;
1306 // We do the call with settings so we can set cache to false.
1305 // We do the call with settings so we can set cache to false.
1307 var settings = {
1306 var settings = {
1308 processData : false,
1307 processData : false,
1309 cache : false,
1308 cache : false,
1310 type : "GET",
1309 type : "GET",
1311 dataType : "json",
1310 dataType : "json",
1312 success : $.proxy(this.load_notebook_success,this),
1311 success : $.proxy(this.load_notebook_success,this),
1313 error : $.proxy(this.load_notebook_error,this),
1312 error : $.proxy(this.load_notebook_error,this),
1314 };
1313 };
1315 $([IPython.events]).trigger('notebook_loading.Notebook');
1314 $([IPython.events]).trigger('notebook_loading.Notebook');
1316 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1315 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1317 $.ajax(url, settings);
1316 $.ajax(url, settings);
1318 };
1317 };
1319
1318
1320
1319
1321 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1320 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1322 this.fromJSON(data);
1321 this.fromJSON(data);
1323 if (this.ncells() === 0) {
1322 if (this.ncells() === 0) {
1324 this.insert_cell_below('code');
1323 this.insert_cell_below('code');
1325 };
1324 };
1326 this.dirty = false;
1325 this.dirty = false;
1327 this.select(0);
1326 this.select(0);
1328 this.scroll_to_top();
1327 this.scroll_to_top();
1329 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1328 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1330 msg = "This notebook has been converted from an older " +
1329 msg = "This notebook has been converted from an older " +
1331 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1330 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1332 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1331 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1333 "newer notebook format will be used and older verions of IPython " +
1332 "newer notebook format will be used and older verions of IPython " +
1334 "may not be able to read it. To keep the older version, close the " +
1333 "may not be able to read it. To keep the older version, close the " +
1335 "notebook without saving it.";
1334 "notebook without saving it.";
1336 var dialog = $('<div/>');
1335 var dialog = $('<div/>');
1337 dialog.html(msg);
1336 dialog.html(msg);
1338 this.element.append(dialog);
1337 this.element.append(dialog);
1339 dialog.dialog({
1338 dialog.dialog({
1340 resizable: false,
1339 resizable: false,
1341 modal: true,
1340 modal: true,
1342 title: "Notebook converted",
1341 title: "Notebook converted",
1343 closeText: "",
1342 closeText: "",
1344 close: function(event, ui) {$(this).dialog('destroy').remove();},
1343 close: function(event, ui) {$(this).dialog('destroy').remove();},
1345 buttons : {
1344 buttons : {
1346 "OK": function () {
1345 "OK": function () {
1347 $(this).dialog('close');
1346 $(this).dialog('close');
1348 }
1347 }
1349 },
1348 },
1350 width: 400
1349 width: 400
1351 });
1350 });
1352 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1351 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1353 var that = this;
1352 var that = this;
1354 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1353 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1355 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1354 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1356 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1355 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1357 this_vs + ". You can still work with this notebook, but some features " +
1356 this_vs + ". You can still work with this notebook, but some features " +
1358 "introduced in later notebook versions may not be available."
1357 "introduced in later notebook versions may not be available."
1359
1358
1360 var dialog = $('<div/>');
1359 var dialog = $('<div/>');
1361 dialog.html(msg);
1360 dialog.html(msg);
1362 this.element.append(dialog);
1361 this.element.append(dialog);
1363 dialog.dialog({
1362 dialog.dialog({
1364 resizable: false,
1363 resizable: false,
1365 modal: true,
1364 modal: true,
1366 title: "Newer Notebook",
1365 title: "Newer Notebook",
1367 closeText: "",
1366 closeText: "",
1368 close: function(event, ui) {$(this).dialog('destroy').remove();},
1367 close: function(event, ui) {$(this).dialog('destroy').remove();},
1369 buttons : {
1368 buttons : {
1370 "OK": function () {
1369 "OK": function () {
1371 $(this).dialog('close');
1370 $(this).dialog('close');
1372 }
1371 }
1373 },
1372 },
1374 width: 400
1373 width: 400
1375 });
1374 });
1376
1375
1377 }
1376 }
1378 // Create the kernel after the notebook is completely loaded to prevent
1377 // Create the kernel after the notebook is completely loaded to prevent
1379 // code execution upon loading, which is a security risk.
1378 // code execution upon loading, which is a security risk.
1380 if (! this.read_only) {
1379 if (! this.read_only) {
1381 this.start_kernel();
1380 this.start_kernel();
1382 }
1381 }
1383 $([IPython.events]).trigger('notebook_loaded.Notebook');
1382 $([IPython.events]).trigger('notebook_loaded.Notebook');
1384 };
1383 };
1385
1384
1386
1385
1387 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1386 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1388 if (xhr.status === 500) {
1387 if (xhr.status === 500) {
1389 var msg = "An error occurred while loading this notebook. Most likely " +
1388 var msg = "An error occurred while loading this notebook. Most likely " +
1390 "this notebook is in a newer format than is supported by this " +
1389 "this notebook is in a newer format than is supported by this " +
1391 "version of IPython. This version can load notebook formats " +
1390 "version of IPython. This version can load notebook formats " +
1392 "v"+this.nbformat+" or earlier.";
1391 "v"+this.nbformat+" or earlier.";
1393 var dialog = $('<div/>');
1392 var dialog = $('<div/>');
1394 dialog.html(msg);
1393 dialog.html(msg);
1395 this.element.append(dialog);
1394 this.element.append(dialog);
1396 dialog.dialog({
1395 dialog.dialog({
1397 resizable: false,
1396 resizable: false,
1398 modal: true,
1397 modal: true,
1399 title: "Error loading notebook",
1398 title: "Error loading notebook",
1400 closeText: "",
1399 closeText: "",
1401 close: function(event, ui) {$(this).dialog('destroy').remove();},
1400 close: function(event, ui) {$(this).dialog('destroy').remove();},
1402 buttons : {
1401 buttons : {
1403 "OK": function () {
1402 "OK": function () {
1404 $(this).dialog('close');
1403 $(this).dialog('close');
1405 }
1404 }
1406 },
1405 },
1407 width: 400
1406 width: 400
1408 });
1407 });
1409 }
1408 }
1410 }
1409 }
1411
1410
1412 IPython.Notebook = Notebook;
1411 IPython.Notebook = Notebook;
1413
1412
1414
1413
1415 return IPython;
1414 return IPython;
1416
1415
1417 }(IPython));
1416 }(IPython));
1418
1417
General Comments 0
You need to be logged in to leave comments. Login now