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