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