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