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