##// END OF EJS Templates
Simplified Cell menu items related to output.
Brian E. Granger -
Show More
@@ -1,770 +1,770 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2011 The IPython Development Team
2 // Copyright (C) 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 // Keyboard management
9 // Keyboard management
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13 "use strict";
13 "use strict";
14
14
15 // Setup global keycodes and inverse keycodes.
15 // Setup global keycodes and inverse keycodes.
16
16
17 // See http://unixpapa.com/js/key.html for a complete description. The short of
17 // See http://unixpapa.com/js/key.html for a complete description. The short of
18 // it is that there are different keycode sets. Firefox uses the "Mozilla keycodes"
18 // it is that there are different keycode sets. Firefox uses the "Mozilla keycodes"
19 // and Webkit/IE use the "IE keycodes". These keycode sets are mostly the same
19 // and Webkit/IE use the "IE keycodes". These keycode sets are mostly the same
20 // but have minor differences.
20 // but have minor differences.
21
21
22 // These apply to Firefox, (Webkit and IE)
22 // These apply to Firefox, (Webkit and IE)
23 var _keycodes = {
23 var _keycodes = {
24 'a': 65, 'b': 66, 'c': 67, 'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73,
24 'a': 65, 'b': 66, 'c': 67, 'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73,
25 'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81, 'r': 82,
25 'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81, 'r': 82,
26 's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, 'z': 90,
26 's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, 'z': 90,
27 '1 !': 49, '2 @': 50, '3 #': 51, '4 $': 52, '5 %': 53, '6 ^': 54,
27 '1 !': 49, '2 @': 50, '3 #': 51, '4 $': 52, '5 %': 53, '6 ^': 54,
28 '7 &': 55, '8 *': 56, '9 (': 57, '0 )': 48,
28 '7 &': 55, '8 *': 56, '9 (': 57, '0 )': 48,
29 '[ {': 219, '] }': 221, '` ~': 192, ', <': 188, '. >': 190, '/ ?': 191,
29 '[ {': 219, '] }': 221, '` ~': 192, ', <': 188, '. >': 190, '/ ?': 191,
30 '\\ |': 220, '\' "': 222,
30 '\\ |': 220, '\' "': 222,
31 'numpad0': 96, 'numpad1': 97, 'numpad2': 98, 'numpad3': 99, 'numpad4': 100,
31 'numpad0': 96, 'numpad1': 97, 'numpad2': 98, 'numpad3': 99, 'numpad4': 100,
32 'numpad5': 101, 'numpad6': 102, 'numpad7': 103, 'numpad8': 104, 'numpad9': 105,
32 'numpad5': 101, 'numpad6': 102, 'numpad7': 103, 'numpad8': 104, 'numpad9': 105,
33 'multiply': 106, 'add': 107, 'subtract': 109, 'decimal': 110, 'divide': 111,
33 'multiply': 106, 'add': 107, 'subtract': 109, 'decimal': 110, 'divide': 111,
34 'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118,
34 'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118,
35 'f8': 119, 'f9': 120, 'f11': 122, 'f12': 123, 'f13': 124, 'f14': 125, 'f15': 126,
35 'f8': 119, 'f9': 120, 'f11': 122, 'f12': 123, 'f13': 124, 'f14': 125, 'f15': 126,
36 'backspace': 8, 'tab': 9, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18,
36 'backspace': 8, 'tab': 9, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18,
37 'meta': 91, 'capslock': 20, 'esc': 27, 'space': 32, 'pageup': 33, 'pagedown': 34,
37 'meta': 91, 'capslock': 20, 'esc': 27, 'space': 32, 'pageup': 33, 'pagedown': 34,
38 'end': 35, 'home': 36, 'left': 37, 'up': 38, 'right': 39, 'down': 40,
38 'end': 35, 'home': 36, 'left': 37, 'up': 38, 'right': 39, 'down': 40,
39 'insert': 45, 'delete': 46, 'numlock': 144,
39 'insert': 45, 'delete': 46, 'numlock': 144,
40 };
40 };
41
41
42 // These apply to Firefox and Opera
42 // These apply to Firefox and Opera
43 var _mozilla_keycodes = {
43 var _mozilla_keycodes = {
44 '; :': 59, '= +': 61, '- _': 173, 'meta': 224
44 '; :': 59, '= +': 61, '- _': 173, 'meta': 224
45 }
45 }
46
46
47 // This apply to Webkit and IE
47 // This apply to Webkit and IE
48 var _ie_keycodes = {
48 var _ie_keycodes = {
49 '; :': 186, '= +': 187, '- _': 189,
49 '; :': 186, '= +': 187, '- _': 189,
50 }
50 }
51
51
52 var browser = IPython.utils.browser[0];
52 var browser = IPython.utils.browser[0];
53 var platform = IPython.utils.platform;
53 var platform = IPython.utils.platform;
54
54
55 if (browser === 'Firefox' || browser === 'Opera') {
55 if (browser === 'Firefox' || browser === 'Opera') {
56 $.extend(_keycodes, _mozilla_keycodes);
56 $.extend(_keycodes, _mozilla_keycodes);
57 } else if (browser === 'Safari' || browser === 'Chrome' || browser === 'MSIE') {
57 } else if (browser === 'Safari' || browser === 'Chrome' || browser === 'MSIE') {
58 $.extend(_keycodes, _ie_keycodes);
58 $.extend(_keycodes, _ie_keycodes);
59 }
59 }
60
60
61 var keycodes = {};
61 var keycodes = {};
62 var inv_keycodes = {};
62 var inv_keycodes = {};
63 for (var name in _keycodes) {
63 for (var name in _keycodes) {
64 var names = name.split(' ');
64 var names = name.split(' ');
65 if (names.length === 1) {
65 if (names.length === 1) {
66 var n = names[0]
66 var n = names[0]
67 keycodes[n] = _keycodes[n]
67 keycodes[n] = _keycodes[n]
68 inv_keycodes[_keycodes[n]] = n
68 inv_keycodes[_keycodes[n]] = n
69 } else {
69 } else {
70 var primary = names[0];
70 var primary = names[0];
71 var secondary = names[1];
71 var secondary = names[1];
72 keycodes[primary] = _keycodes[name]
72 keycodes[primary] = _keycodes[name]
73 keycodes[secondary] = _keycodes[name]
73 keycodes[secondary] = _keycodes[name]
74 inv_keycodes[_keycodes[name]] = primary
74 inv_keycodes[_keycodes[name]] = primary
75 }
75 }
76 }
76 }
77
77
78
78
79 // Default keyboard shortcuts
79 // Default keyboard shortcuts
80
80
81 var default_common_shortcuts = {
81 var default_common_shortcuts = {
82 'shift' : {
82 'shift' : {
83 help : '',
83 help : '',
84 help_index : '',
84 help_index : '',
85 handler : function (event) {
85 handler : function (event) {
86 // ignore shift keydown
86 // ignore shift keydown
87 return true;
87 return true;
88 }
88 }
89 },
89 },
90 'shift+enter' : {
90 'shift+enter' : {
91 help : 'run cell',
91 help : 'run cell',
92 help_index : 'ba',
92 help_index : 'ba',
93 handler : function (event) {
93 handler : function (event) {
94 IPython.notebook.execute_cell();
94 IPython.notebook.execute_cell();
95 return false;
95 return false;
96 }
96 }
97 },
97 },
98 'ctrl+enter' : {
98 'ctrl+enter' : {
99 help : 'run cell, select below',
99 help : 'run cell, select below',
100 help_index : 'bb',
100 help_index : 'bb',
101 handler : function (event) {
101 handler : function (event) {
102 IPython.notebook.execute_cell_and_select_below();
102 IPython.notebook.execute_cell_and_select_below();
103 return false;
103 return false;
104 }
104 }
105 },
105 },
106 'alt+enter' : {
106 'alt+enter' : {
107 help : 'run cell, insert below',
107 help : 'run cell, insert below',
108 help_index : 'bc',
108 help_index : 'bc',
109 handler : function (event) {
109 handler : function (event) {
110 IPython.notebook.execute_cell_and_insert_below();
110 IPython.notebook.execute_cell_and_insert_below();
111 return false;
111 return false;
112 }
112 }
113 }
113 }
114 }
114 }
115
115
116 if (platform === 'MacOS') {
116 if (platform === 'MacOS') {
117 default_common_shortcuts['cmd+s'] =
117 default_common_shortcuts['cmd+s'] =
118 {
118 {
119 help : 'save notebook',
119 help : 'save notebook',
120 help_index : 'fb',
120 help_index : 'fb',
121 handler : function (event) {
121 handler : function (event) {
122 IPython.notebook.save_checkpoint();
122 IPython.notebook.save_checkpoint();
123 event.preventDefault();
123 event.preventDefault();
124 return false;
124 return false;
125 }
125 }
126 };
126 };
127 } else {
127 } else {
128 default_common_shortcuts['ctrl+s'] =
128 default_common_shortcuts['ctrl+s'] =
129 {
129 {
130 help : 'save notebook',
130 help : 'save notebook',
131 help_index : 'fb',
131 help_index : 'fb',
132 handler : function (event) {
132 handler : function (event) {
133 IPython.notebook.save_checkpoint();
133 IPython.notebook.save_checkpoint();
134 event.preventDefault();
134 event.preventDefault();
135 return false;
135 return false;
136 }
136 }
137 };
137 };
138 }
138 }
139
139
140 // Edit mode defaults
140 // Edit mode defaults
141
141
142 var default_edit_shortcuts = {
142 var default_edit_shortcuts = {
143 'esc' : {
143 'esc' : {
144 help : 'command mode',
144 help : 'command mode',
145 help_index : 'aa',
145 help_index : 'aa',
146 handler : function (event) {
146 handler : function (event) {
147 IPython.notebook.command_mode();
147 IPython.notebook.command_mode();
148 IPython.notebook.focus_cell();
148 IPython.notebook.focus_cell();
149 return false;
149 return false;
150 }
150 }
151 },
151 },
152 'ctrl+m' : {
152 'ctrl+m' : {
153 help : 'command mode',
153 help : 'command mode',
154 help_index : 'ab',
154 help_index : 'ab',
155 handler : function (event) {
155 handler : function (event) {
156 IPython.notebook.command_mode();
156 IPython.notebook.command_mode();
157 IPython.notebook.focus_cell();
157 IPython.notebook.focus_cell();
158 return false;
158 return false;
159 }
159 }
160 },
160 },
161 'up' : {
161 'up' : {
162 help : '',
162 help : '',
163 help_index : '',
163 help_index : '',
164 handler : function (event) {
164 handler : function (event) {
165 var cell = IPython.notebook.get_selected_cell();
165 var cell = IPython.notebook.get_selected_cell();
166 if (cell && cell.at_top()) {
166 if (cell && cell.at_top()) {
167 event.preventDefault();
167 event.preventDefault();
168 IPython.notebook.command_mode()
168 IPython.notebook.command_mode()
169 IPython.notebook.select_prev();
169 IPython.notebook.select_prev();
170 IPython.notebook.edit_mode();
170 IPython.notebook.edit_mode();
171 return false;
171 return false;
172 };
172 };
173 }
173 }
174 },
174 },
175 'down' : {
175 'down' : {
176 help : '',
176 help : '',
177 help_index : '',
177 help_index : '',
178 handler : function (event) {
178 handler : function (event) {
179 var cell = IPython.notebook.get_selected_cell();
179 var cell = IPython.notebook.get_selected_cell();
180 if (cell && cell.at_bottom()) {
180 if (cell && cell.at_bottom()) {
181 event.preventDefault();
181 event.preventDefault();
182 IPython.notebook.command_mode()
182 IPython.notebook.command_mode()
183 IPython.notebook.select_next();
183 IPython.notebook.select_next();
184 IPython.notebook.edit_mode();
184 IPython.notebook.edit_mode();
185 return false;
185 return false;
186 };
186 };
187 }
187 }
188 },
188 },
189 'alt+-' : {
189 'alt+-' : {
190 help : 'split cell',
190 help : 'split cell',
191 help_index : 'ea',
191 help_index : 'ea',
192 handler : function (event) {
192 handler : function (event) {
193 IPython.notebook.split_cell();
193 IPython.notebook.split_cell();
194 return false;
194 return false;
195 }
195 }
196 },
196 },
197 'alt+subtract' : {
197 'alt+subtract' : {
198 help : '',
198 help : '',
199 help_index : 'eb',
199 help_index : 'eb',
200 handler : function (event) {
200 handler : function (event) {
201 IPython.notebook.split_cell();
201 IPython.notebook.split_cell();
202 return false;
202 return false;
203 }
203 }
204 },
204 },
205 'tab' : {
205 'tab' : {
206 help : 'indent or complete',
206 help : 'indent or complete',
207 help_index : 'ec',
207 help_index : 'ec',
208 },
208 },
209 'shift+tab' : {
209 'shift+tab' : {
210 help : 'tooltip',
210 help : 'tooltip',
211 help_index : 'ed',
211 help_index : 'ed',
212 },
212 },
213 }
213 }
214
214
215 if (platform === 'MacOS') {
215 if (platform === 'MacOS') {
216 default_edit_shortcuts['cmd+/'] =
216 default_edit_shortcuts['cmd+/'] =
217 {
217 {
218 help : 'toggle comment',
218 help : 'toggle comment',
219 help_index : 'ee'
219 help_index : 'ee'
220 };
220 };
221 default_edit_shortcuts['cmd+]'] =
221 default_edit_shortcuts['cmd+]'] =
222 {
222 {
223 help : 'indent',
223 help : 'indent',
224 help_index : 'ef'
224 help_index : 'ef'
225 };
225 };
226 default_edit_shortcuts['cmd+['] =
226 default_edit_shortcuts['cmd+['] =
227 {
227 {
228 help : 'dedent',
228 help : 'dedent',
229 help_index : 'eg'
229 help_index : 'eg'
230 };
230 };
231 } else {
231 } else {
232 default_edit_shortcuts['ctrl+/'] =
232 default_edit_shortcuts['ctrl+/'] =
233 {
233 {
234 help : 'toggle comment',
234 help : 'toggle comment',
235 help_index : 'ee'
235 help_index : 'ee'
236 };
236 };
237 default_edit_shortcuts['ctrl+]'] =
237 default_edit_shortcuts['ctrl+]'] =
238 {
238 {
239 help : 'indent',
239 help : 'indent',
240 help_index : 'ef'
240 help_index : 'ef'
241 };
241 };
242 default_edit_shortcuts['ctrl+['] =
242 default_edit_shortcuts['ctrl+['] =
243 {
243 {
244 help : 'dedent',
244 help : 'dedent',
245 help_index : 'eg'
245 help_index : 'eg'
246 };
246 };
247 }
247 }
248
248
249 // Command mode defaults
249 // Command mode defaults
250
250
251 var default_command_shortcuts = {
251 var default_command_shortcuts = {
252 'enter' : {
252 'enter' : {
253 help : 'edit mode',
253 help : 'edit mode',
254 help_index : 'aa',
254 help_index : 'aa',
255 handler : function (event) {
255 handler : function (event) {
256 IPython.notebook.edit_mode();
256 IPython.notebook.edit_mode();
257 return false;
257 return false;
258 }
258 }
259 },
259 },
260 'up' : {
260 'up' : {
261 help : 'select previous cell',
261 help : 'select previous cell',
262 help_index : 'da',
262 help_index : 'da',
263 handler : function (event) {
263 handler : function (event) {
264 var index = IPython.notebook.get_selected_index();
264 var index = IPython.notebook.get_selected_index();
265 if (index !== 0 && index !== null) {
265 if (index !== 0 && index !== null) {
266 IPython.notebook.select_prev();
266 IPython.notebook.select_prev();
267 var cell = IPython.notebook.get_selected_cell();
267 var cell = IPython.notebook.get_selected_cell();
268 cell.focus_cell();
268 cell.focus_cell();
269 };
269 };
270 return false;
270 return false;
271 }
271 }
272 },
272 },
273 'down' : {
273 'down' : {
274 help : 'select next cell',
274 help : 'select next cell',
275 help_index : 'db',
275 help_index : 'db',
276 handler : function (event) {
276 handler : function (event) {
277 var index = IPython.notebook.get_selected_index();
277 var index = IPython.notebook.get_selected_index();
278 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
278 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
279 IPython.notebook.select_next();
279 IPython.notebook.select_next();
280 var cell = IPython.notebook.get_selected_cell();
280 var cell = IPython.notebook.get_selected_cell();
281 cell.focus_cell();
281 cell.focus_cell();
282 };
282 };
283 return false;
283 return false;
284 }
284 }
285 },
285 },
286 'k' : {
286 'k' : {
287 help : 'select previous cell',
287 help : 'select previous cell',
288 help_index : 'dc',
288 help_index : 'dc',
289 handler : function (event) {
289 handler : function (event) {
290 var index = IPython.notebook.get_selected_index();
290 var index = IPython.notebook.get_selected_index();
291 if (index !== 0 && index !== null) {
291 if (index !== 0 && index !== null) {
292 IPython.notebook.select_prev();
292 IPython.notebook.select_prev();
293 var cell = IPython.notebook.get_selected_cell();
293 var cell = IPython.notebook.get_selected_cell();
294 cell.focus_cell();
294 cell.focus_cell();
295 };
295 };
296 return false;
296 return false;
297 }
297 }
298 },
298 },
299 'j' : {
299 'j' : {
300 help : 'select next cell',
300 help : 'select next cell',
301 help_index : 'dd',
301 help_index : 'dd',
302 handler : function (event) {
302 handler : function (event) {
303 var index = IPython.notebook.get_selected_index();
303 var index = IPython.notebook.get_selected_index();
304 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
304 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
305 IPython.notebook.select_next();
305 IPython.notebook.select_next();
306 var cell = IPython.notebook.get_selected_cell();
306 var cell = IPython.notebook.get_selected_cell();
307 cell.focus_cell();
307 cell.focus_cell();
308 };
308 };
309 return false;
309 return false;
310 }
310 }
311 },
311 },
312 'x' : {
312 'x' : {
313 help : 'cut cell',
313 help : 'cut cell',
314 help_index : 'ee',
314 help_index : 'ee',
315 handler : function (event) {
315 handler : function (event) {
316 IPython.notebook.cut_cell();
316 IPython.notebook.cut_cell();
317 return false;
317 return false;
318 }
318 }
319 },
319 },
320 'c' : {
320 'c' : {
321 help : 'copy cell',
321 help : 'copy cell',
322 help_index : 'ef',
322 help_index : 'ef',
323 handler : function (event) {
323 handler : function (event) {
324 IPython.notebook.copy_cell();
324 IPython.notebook.copy_cell();
325 return false;
325 return false;
326 }
326 }
327 },
327 },
328 'shift+v' : {
328 'shift+v' : {
329 help : 'paste cell above',
329 help : 'paste cell above',
330 help_index : 'eg',
330 help_index : 'eg',
331 handler : function (event) {
331 handler : function (event) {
332 IPython.notebook.paste_cell_above();
332 IPython.notebook.paste_cell_above();
333 return false;
333 return false;
334 }
334 }
335 },
335 },
336 'v' : {
336 'v' : {
337 help : 'paste cell below',
337 help : 'paste cell below',
338 help_index : 'eh',
338 help_index : 'eh',
339 handler : function (event) {
339 handler : function (event) {
340 IPython.notebook.paste_cell_below();
340 IPython.notebook.paste_cell_below();
341 return false;
341 return false;
342 }
342 }
343 },
343 },
344 'd' : {
344 'd' : {
345 help : 'delete cell (press twice)',
345 help : 'delete cell (press twice)',
346 help_index : 'ej',
346 help_index : 'ej',
347 count: 2,
347 count: 2,
348 handler : function (event) {
348 handler : function (event) {
349 IPython.notebook.delete_cell();
349 IPython.notebook.delete_cell();
350 return false;
350 return false;
351 }
351 }
352 },
352 },
353 'a' : {
353 'a' : {
354 help : 'insert cell above',
354 help : 'insert cell above',
355 help_index : 'ec',
355 help_index : 'ec',
356 handler : function (event) {
356 handler : function (event) {
357 IPython.notebook.insert_cell_above('code');
357 IPython.notebook.insert_cell_above('code');
358 IPython.notebook.select_prev();
358 IPython.notebook.select_prev();
359 IPython.notebook.focus_cell();
359 IPython.notebook.focus_cell();
360 return false;
360 return false;
361 }
361 }
362 },
362 },
363 'b' : {
363 'b' : {
364 help : 'insert cell below',
364 help : 'insert cell below',
365 help_index : 'ed',
365 help_index : 'ed',
366 handler : function (event) {
366 handler : function (event) {
367 IPython.notebook.insert_cell_below('code');
367 IPython.notebook.insert_cell_below('code');
368 IPython.notebook.select_next();
368 IPython.notebook.select_next();
369 IPython.notebook.focus_cell();
369 IPython.notebook.focus_cell();
370 return false;
370 return false;
371 }
371 }
372 },
372 },
373 'y' : {
373 'y' : {
374 help : 'to code',
374 help : 'to code',
375 help_index : 'ca',
375 help_index : 'ca',
376 handler : function (event) {
376 handler : function (event) {
377 IPython.notebook.to_code();
377 IPython.notebook.to_code();
378 return false;
378 return false;
379 }
379 }
380 },
380 },
381 'm' : {
381 'm' : {
382 help : 'to markdown',
382 help : 'to markdown',
383 help_index : 'cb',
383 help_index : 'cb',
384 handler : function (event) {
384 handler : function (event) {
385 IPython.notebook.to_markdown();
385 IPython.notebook.to_markdown();
386 return false;
386 return false;
387 }
387 }
388 },
388 },
389 'r' : {
389 'r' : {
390 help : 'to raw',
390 help : 'to raw',
391 help_index : 'cc',
391 help_index : 'cc',
392 handler : function (event) {
392 handler : function (event) {
393 IPython.notebook.to_raw();
393 IPython.notebook.to_raw();
394 return false;
394 return false;
395 }
395 }
396 },
396 },
397 '1' : {
397 '1' : {
398 help : 'to heading 1',
398 help : 'to heading 1',
399 help_index : 'cd',
399 help_index : 'cd',
400 handler : function (event) {
400 handler : function (event) {
401 IPython.notebook.to_heading(undefined, 1);
401 IPython.notebook.to_heading(undefined, 1);
402 return false;
402 return false;
403 }
403 }
404 },
404 },
405 '2' : {
405 '2' : {
406 help : 'to heading 2',
406 help : 'to heading 2',
407 help_index : 'ce',
407 help_index : 'ce',
408 handler : function (event) {
408 handler : function (event) {
409 IPython.notebook.to_heading(undefined, 2);
409 IPython.notebook.to_heading(undefined, 2);
410 return false;
410 return false;
411 }
411 }
412 },
412 },
413 '3' : {
413 '3' : {
414 help : 'to heading 3',
414 help : 'to heading 3',
415 help_index : 'cf',
415 help_index : 'cf',
416 handler : function (event) {
416 handler : function (event) {
417 IPython.notebook.to_heading(undefined, 3);
417 IPython.notebook.to_heading(undefined, 3);
418 return false;
418 return false;
419 }
419 }
420 },
420 },
421 '4' : {
421 '4' : {
422 help : 'to heading 4',
422 help : 'to heading 4',
423 help_index : 'cg',
423 help_index : 'cg',
424 handler : function (event) {
424 handler : function (event) {
425 IPython.notebook.to_heading(undefined, 4);
425 IPython.notebook.to_heading(undefined, 4);
426 return false;
426 return false;
427 }
427 }
428 },
428 },
429 '5' : {
429 '5' : {
430 help : 'to heading 5',
430 help : 'to heading 5',
431 help_index : 'ch',
431 help_index : 'ch',
432 handler : function (event) {
432 handler : function (event) {
433 IPython.notebook.to_heading(undefined, 5);
433 IPython.notebook.to_heading(undefined, 5);
434 return false;
434 return false;
435 }
435 }
436 },
436 },
437 '6' : {
437 '6' : {
438 help : 'to heading 6',
438 help : 'to heading 6',
439 help_index : 'ci',
439 help_index : 'ci',
440 handler : function (event) {
440 handler : function (event) {
441 IPython.notebook.to_heading(undefined, 6);
441 IPython.notebook.to_heading(undefined, 6);
442 return false;
442 return false;
443 }
443 }
444 },
444 },
445 'o' : {
445 'o' : {
446 help : 'toggle output',
446 help : 'toggle output',
447 help_index : 'gb',
447 help_index : 'gb',
448 handler : function (event) {
448 handler : function (event) {
449 IPython.notebook.toggle_output();
449 IPython.notebook.toggle_output();
450 return false;
450 return false;
451 }
451 }
452 },
452 },
453 'shift+o' : {
453 'shift+o' : {
454 help : 'toggle output scroll',
454 help : 'toggle output scrolling',
455 help_index : 'gc',
455 help_index : 'gc',
456 handler : function (event) {
456 handler : function (event) {
457 IPython.notebook.toggle_output_scroll();
457 IPython.notebook.toggle_output_scroll();
458 return false;
458 return false;
459 }
459 }
460 },
460 },
461 's' : {
461 's' : {
462 help : 'save notebook',
462 help : 'save notebook',
463 help_index : 'fa',
463 help_index : 'fa',
464 handler : function (event) {
464 handler : function (event) {
465 IPython.notebook.save_checkpoint();
465 IPython.notebook.save_checkpoint();
466 return false;
466 return false;
467 }
467 }
468 },
468 },
469 'ctrl+j' : {
469 'ctrl+j' : {
470 help : 'move cell down',
470 help : 'move cell down',
471 help_index : 'eb',
471 help_index : 'eb',
472 handler : function (event) {
472 handler : function (event) {
473 IPython.notebook.move_cell_down();
473 IPython.notebook.move_cell_down();
474 return false;
474 return false;
475 }
475 }
476 },
476 },
477 'ctrl+k' : {
477 'ctrl+k' : {
478 help : 'move cell up',
478 help : 'move cell up',
479 help_index : 'ea',
479 help_index : 'ea',
480 handler : function (event) {
480 handler : function (event) {
481 IPython.notebook.move_cell_up();
481 IPython.notebook.move_cell_up();
482 return false;
482 return false;
483 }
483 }
484 },
484 },
485 'l' : {
485 'l' : {
486 help : 'toggle line numbers',
486 help : 'toggle line numbers',
487 help_index : 'ga',
487 help_index : 'ga',
488 handler : function (event) {
488 handler : function (event) {
489 IPython.notebook.cell_toggle_line_numbers();
489 IPython.notebook.cell_toggle_line_numbers();
490 return false;
490 return false;
491 }
491 }
492 },
492 },
493 'i' : {
493 'i' : {
494 help : 'interrupt kernel (press twice)',
494 help : 'interrupt kernel (press twice)',
495 help_index : 'ha',
495 help_index : 'ha',
496 count: 2,
496 count: 2,
497 handler : function (event) {
497 handler : function (event) {
498 IPython.notebook.kernel.interrupt();
498 IPython.notebook.kernel.interrupt();
499 return false;
499 return false;
500 }
500 }
501 },
501 },
502 '0' : {
502 '0' : {
503 help : 'restart kernel (press twice)',
503 help : 'restart kernel (press twice)',
504 help_index : 'hb',
504 help_index : 'hb',
505 count: 2,
505 count: 2,
506 handler : function (event) {
506 handler : function (event) {
507 IPython.notebook.restart_kernel();
507 IPython.notebook.restart_kernel();
508 return false;
508 return false;
509 }
509 }
510 },
510 },
511 'h' : {
511 'h' : {
512 help : 'keyboard shortcuts',
512 help : 'keyboard shortcuts',
513 help_index : 'gd',
513 help_index : 'gd',
514 handler : function (event) {
514 handler : function (event) {
515 IPython.quick_help.show_keyboard_shortcuts();
515 IPython.quick_help.show_keyboard_shortcuts();
516 return false;
516 return false;
517 }
517 }
518 },
518 },
519 'z' : {
519 'z' : {
520 help : 'undo last delete',
520 help : 'undo last delete',
521 help_index : 'ei',
521 help_index : 'ei',
522 handler : function (event) {
522 handler : function (event) {
523 IPython.notebook.undelete_cell();
523 IPython.notebook.undelete_cell();
524 return false;
524 return false;
525 }
525 }
526 },
526 },
527 'shift+=' : {
527 'shift+=' : {
528 help : 'merge cell below',
528 help : 'merge cell below',
529 help_index : 'ek',
529 help_index : 'ek',
530 handler : function (event) {
530 handler : function (event) {
531 IPython.notebook.merge_cell_below();
531 IPython.notebook.merge_cell_below();
532 return false;
532 return false;
533 }
533 }
534 },
534 },
535 'shift+m' : {
535 'shift+m' : {
536 help : 'merge cell below',
536 help : 'merge cell below',
537 help_index : 'ek',
537 help_index : 'ek',
538 handler : function (event) {
538 handler : function (event) {
539 IPython.notebook.merge_cell_below();
539 IPython.notebook.merge_cell_below();
540 return false;
540 return false;
541 }
541 }
542 },
542 },
543 }
543 }
544
544
545
545
546 // Shortcut manager class
546 // Shortcut manager class
547
547
548 var ShortcutManager = function (delay) {
548 var ShortcutManager = function (delay) {
549 this._shortcuts = {}
549 this._shortcuts = {}
550 this._counts = {}
550 this._counts = {}
551 this.delay = delay || 800; // delay in milliseconds
551 this.delay = delay || 800; // delay in milliseconds
552 }
552 }
553
553
554 ShortcutManager.prototype.help = function () {
554 ShortcutManager.prototype.help = function () {
555 var help = [];
555 var help = [];
556 for (var shortcut in this._shortcuts) {
556 for (var shortcut in this._shortcuts) {
557 var help_string = this._shortcuts[shortcut]['help'];
557 var help_string = this._shortcuts[shortcut]['help'];
558 var help_index = this._shortcuts[shortcut]['help_index'];
558 var help_index = this._shortcuts[shortcut]['help_index'];
559 if (help_string) {
559 if (help_string) {
560 if (platform === 'MacOS') {
560 if (platform === 'MacOS') {
561 shortcut = shortcut.replace('meta', 'cmd');
561 shortcut = shortcut.replace('meta', 'cmd');
562 }
562 }
563 help.push({
563 help.push({
564 shortcut: shortcut,
564 shortcut: shortcut,
565 help: help_string,
565 help: help_string,
566 help_index: help_index}
566 help_index: help_index}
567 );
567 );
568 }
568 }
569 }
569 }
570 help.sort(function (a, b) {
570 help.sort(function (a, b) {
571 if (a.help_index > b.help_index)
571 if (a.help_index > b.help_index)
572 return 1;
572 return 1;
573 if (a.help_index < b.help_index)
573 if (a.help_index < b.help_index)
574 return -1;
574 return -1;
575 return 0;
575 return 0;
576 });
576 });
577 return help;
577 return help;
578 }
578 }
579
579
580 ShortcutManager.prototype.normalize_key = function (key) {
580 ShortcutManager.prototype.normalize_key = function (key) {
581 return inv_keycodes[keycodes[key]];
581 return inv_keycodes[keycodes[key]];
582 }
582 }
583
583
584 ShortcutManager.prototype.normalize_shortcut = function (shortcut) {
584 ShortcutManager.prototype.normalize_shortcut = function (shortcut) {
585 // Sort a sequence of + separated modifiers into the order alt+ctrl+meta+shift
585 // Sort a sequence of + separated modifiers into the order alt+ctrl+meta+shift
586 shortcut = shortcut.replace('cmd', 'meta').toLowerCase();
586 shortcut = shortcut.replace('cmd', 'meta').toLowerCase();
587 var values = shortcut.split("+");
587 var values = shortcut.split("+");
588 if (values.length === 1) {
588 if (values.length === 1) {
589 return this.normalize_key(values[0])
589 return this.normalize_key(values[0])
590 } else {
590 } else {
591 var modifiers = values.slice(0,-1);
591 var modifiers = values.slice(0,-1);
592 var key = this.normalize_key(values[values.length-1]);
592 var key = this.normalize_key(values[values.length-1]);
593 modifiers.sort();
593 modifiers.sort();
594 return modifiers.join('+') + '+' + key;
594 return modifiers.join('+') + '+' + key;
595 }
595 }
596 }
596 }
597
597
598 ShortcutManager.prototype.event_to_shortcut = function (event) {
598 ShortcutManager.prototype.event_to_shortcut = function (event) {
599 // Convert a jQuery keyboard event to a strong based keyboard shortcut
599 // Convert a jQuery keyboard event to a strong based keyboard shortcut
600 var shortcut = '';
600 var shortcut = '';
601 var key = inv_keycodes[event.which]
601 var key = inv_keycodes[event.which]
602 if (event.altKey && key !== 'alt') {shortcut += 'alt+';}
602 if (event.altKey && key !== 'alt') {shortcut += 'alt+';}
603 if (event.ctrlKey && key !== 'ctrl') {shortcut += 'ctrl+';}
603 if (event.ctrlKey && key !== 'ctrl') {shortcut += 'ctrl+';}
604 if (event.metaKey && key !== 'meta') {shortcut += 'meta+';}
604 if (event.metaKey && key !== 'meta') {shortcut += 'meta+';}
605 if (event.shiftKey && key !== 'shift') {shortcut += 'shift+';}
605 if (event.shiftKey && key !== 'shift') {shortcut += 'shift+';}
606 shortcut += key;
606 shortcut += key;
607 return shortcut
607 return shortcut
608 }
608 }
609
609
610 ShortcutManager.prototype.clear_shortcuts = function () {
610 ShortcutManager.prototype.clear_shortcuts = function () {
611 this._shortcuts = {};
611 this._shortcuts = {};
612 }
612 }
613
613
614 ShortcutManager.prototype.add_shortcut = function (shortcut, data) {
614 ShortcutManager.prototype.add_shortcut = function (shortcut, data) {
615 if (typeof(data) === 'function') {
615 if (typeof(data) === 'function') {
616 data = {help: '', help_index: '', handler: data}
616 data = {help: '', help_index: '', handler: data}
617 }
617 }
618 data.help_index = data.help_index || '';
618 data.help_index = data.help_index || '';
619 data.help = data.help || '';
619 data.help = data.help || '';
620 data.count = data.count || 1;
620 data.count = data.count || 1;
621 if (data.help_index === '') {
621 if (data.help_index === '') {
622 data.help_index = 'zz';
622 data.help_index = 'zz';
623 }
623 }
624 shortcut = this.normalize_shortcut(shortcut);
624 shortcut = this.normalize_shortcut(shortcut);
625 this._counts[shortcut] = 0;
625 this._counts[shortcut] = 0;
626 this._shortcuts[shortcut] = data;
626 this._shortcuts[shortcut] = data;
627 }
627 }
628
628
629 ShortcutManager.prototype.add_shortcuts = function (data) {
629 ShortcutManager.prototype.add_shortcuts = function (data) {
630 for (var shortcut in data) {
630 for (var shortcut in data) {
631 this.add_shortcut(shortcut, data[shortcut]);
631 this.add_shortcut(shortcut, data[shortcut]);
632 }
632 }
633 }
633 }
634
634
635 ShortcutManager.prototype.remove_shortcut = function (shortcut) {
635 ShortcutManager.prototype.remove_shortcut = function (shortcut) {
636 shortcut = this.normalize_shortcut(shortcut);
636 shortcut = this.normalize_shortcut(shortcut);
637 delete this._counts[shortcut];
637 delete this._counts[shortcut];
638 delete this._shortcuts[shortcut];
638 delete this._shortcuts[shortcut];
639 }
639 }
640
640
641 ShortcutManager.prototype.count_handler = function (shortcut, event, data) {
641 ShortcutManager.prototype.count_handler = function (shortcut, event, data) {
642 var that = this;
642 var that = this;
643 var c = this._counts;
643 var c = this._counts;
644 if (c[shortcut] === data.count-1) {
644 if (c[shortcut] === data.count-1) {
645 c[shortcut] = 0;
645 c[shortcut] = 0;
646 return data.handler(event);
646 return data.handler(event);
647 } else {
647 } else {
648 c[shortcut] = c[shortcut] + 1;
648 c[shortcut] = c[shortcut] + 1;
649 setTimeout(function () {
649 setTimeout(function () {
650 c[shortcut] = 0;
650 c[shortcut] = 0;
651 }, that.delay);
651 }, that.delay);
652 }
652 }
653 return false;
653 return false;
654
654
655 }
655 }
656
656
657 ShortcutManager.prototype.call_handler = function (event) {
657 ShortcutManager.prototype.call_handler = function (event) {
658 var shortcut = this.event_to_shortcut(event);
658 var shortcut = this.event_to_shortcut(event);
659 var data = this._shortcuts[shortcut];
659 var data = this._shortcuts[shortcut];
660 if (data) {
660 if (data) {
661 var handler = data['handler'];
661 var handler = data['handler'];
662 if (handler) {
662 if (handler) {
663 if (data.count === 1) {
663 if (data.count === 1) {
664 return handler(event);
664 return handler(event);
665 } else if (data.count > 1) {
665 } else if (data.count > 1) {
666 return this.count_handler(shortcut, event, data);
666 return this.count_handler(shortcut, event, data);
667 }
667 }
668 }
668 }
669 }
669 }
670 return true;
670 return true;
671 }
671 }
672
672
673
673
674
674
675 // Main keyboard manager for the notebook
675 // Main keyboard manager for the notebook
676
676
677 var KeyboardManager = function () {
677 var KeyboardManager = function () {
678 this.mode = 'command';
678 this.mode = 'command';
679 this.enabled = true;
679 this.enabled = true;
680 this.bind_events();
680 this.bind_events();
681 this.command_shortcuts = new ShortcutManager();
681 this.command_shortcuts = new ShortcutManager();
682 this.command_shortcuts.add_shortcuts(default_common_shortcuts);
682 this.command_shortcuts.add_shortcuts(default_common_shortcuts);
683 this.command_shortcuts.add_shortcuts(default_command_shortcuts);
683 this.command_shortcuts.add_shortcuts(default_command_shortcuts);
684 this.edit_shortcuts = new ShortcutManager();
684 this.edit_shortcuts = new ShortcutManager();
685 this.edit_shortcuts.add_shortcuts(default_common_shortcuts);
685 this.edit_shortcuts.add_shortcuts(default_common_shortcuts);
686 this.edit_shortcuts.add_shortcuts(default_edit_shortcuts);
686 this.edit_shortcuts.add_shortcuts(default_edit_shortcuts);
687 };
687 };
688
688
689 KeyboardManager.prototype.bind_events = function () {
689 KeyboardManager.prototype.bind_events = function () {
690 var that = this;
690 var that = this;
691 $(document).keydown(function (event) {
691 $(document).keydown(function (event) {
692 return that.handle_keydown(event);
692 return that.handle_keydown(event);
693 });
693 });
694 };
694 };
695
695
696 KeyboardManager.prototype.handle_keydown = function (event) {
696 KeyboardManager.prototype.handle_keydown = function (event) {
697 var notebook = IPython.notebook;
697 var notebook = IPython.notebook;
698
698
699 if (event.which === keycodes['esc']) {
699 if (event.which === keycodes['esc']) {
700 // Intercept escape at highest level to avoid closing
700 // Intercept escape at highest level to avoid closing
701 // websocket connection with firefox
701 // websocket connection with firefox
702 event.preventDefault();
702 event.preventDefault();
703 }
703 }
704
704
705 if (!this.enabled) {
705 if (!this.enabled) {
706 if (event.which === keycodes['esc']) {
706 if (event.which === keycodes['esc']) {
707 // ESC
707 // ESC
708 notebook.command_mode();
708 notebook.command_mode();
709 return false;
709 return false;
710 }
710 }
711 return true;
711 return true;
712 }
712 }
713
713
714 if (this.mode === 'edit') {
714 if (this.mode === 'edit') {
715 return this.edit_shortcuts.call_handler(event);
715 return this.edit_shortcuts.call_handler(event);
716 } else if (this.mode === 'command') {
716 } else if (this.mode === 'command') {
717 return this.command_shortcuts.call_handler(event);
717 return this.command_shortcuts.call_handler(event);
718 }
718 }
719 return true;
719 return true;
720 }
720 }
721
721
722 KeyboardManager.prototype.edit_mode = function () {
722 KeyboardManager.prototype.edit_mode = function () {
723 this.last_mode = this.mode;
723 this.last_mode = this.mode;
724 this.mode = 'edit';
724 this.mode = 'edit';
725 }
725 }
726
726
727 KeyboardManager.prototype.command_mode = function () {
727 KeyboardManager.prototype.command_mode = function () {
728 this.last_mode = this.mode;
728 this.last_mode = this.mode;
729 this.mode = 'command';
729 this.mode = 'command';
730 }
730 }
731
731
732 KeyboardManager.prototype.enable = function () {
732 KeyboardManager.prototype.enable = function () {
733 this.enabled = true;
733 this.enabled = true;
734 }
734 }
735
735
736 KeyboardManager.prototype.disable = function () {
736 KeyboardManager.prototype.disable = function () {
737 this.enabled = false;
737 this.enabled = false;
738 }
738 }
739
739
740 KeyboardManager.prototype.register_events = function (e) {
740 KeyboardManager.prototype.register_events = function (e) {
741 var that = this;
741 var that = this;
742 e.on('focusin', function () {
742 e.on('focusin', function () {
743 that.command_mode();
743 that.command_mode();
744 that.disable();
744 that.disable();
745 });
745 });
746 e.on('focusout', function () {
746 e.on('focusout', function () {
747 that.command_mode();
747 that.command_mode();
748 that.enable();
748 that.enable();
749 });
749 });
750 // There are times (raw_input) where we remove the element from the DOM before
750 // There are times (raw_input) where we remove the element from the DOM before
751 // focusout is called. In this case we bind to the remove event of jQueryUI,
751 // focusout is called. In this case we bind to the remove event of jQueryUI,
752 // which gets triggered upon removal.
752 // which gets triggered upon removal.
753 e.on('remove', function () {
753 e.on('remove', function () {
754 that.command_mode();
754 that.command_mode();
755 that.enable();
755 that.enable();
756 });
756 });
757 }
757 }
758
758
759
759
760 IPython.keycodes = keycodes;
760 IPython.keycodes = keycodes;
761 IPython.inv_keycodes = inv_keycodes;
761 IPython.inv_keycodes = inv_keycodes;
762 IPython.default_common_shortcuts = default_common_shortcuts;
762 IPython.default_common_shortcuts = default_common_shortcuts;
763 IPython.default_edit_shortcuts = default_edit_shortcuts;
763 IPython.default_edit_shortcuts = default_edit_shortcuts;
764 IPython.default_command_shortcuts = default_command_shortcuts;
764 IPython.default_command_shortcuts = default_command_shortcuts;
765 IPython.ShortcutManager = ShortcutManager;
765 IPython.ShortcutManager = ShortcutManager;
766 IPython.KeyboardManager = KeyboardManager;
766 IPython.KeyboardManager = KeyboardManager;
767
767
768 return IPython;
768 return IPython;
769
769
770 }(IPython));
770 }(IPython));
@@ -1,330 +1,327 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 // MenuBar
9 // MenuBar
10 //============================================================================
10 //============================================================================
11
11
12 /**
12 /**
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 * @submodule MenuBar
15 * @submodule MenuBar
16 */
16 */
17
17
18
18
19 var IPython = (function (IPython) {
19 var IPython = (function (IPython) {
20 "use strict";
20 "use strict";
21
21
22 var utils = IPython.utils;
22 var utils = IPython.utils;
23
23
24 /**
24 /**
25 * A MenuBar Class to generate the menubar of IPython notebook
25 * A MenuBar Class to generate the menubar of IPython notebook
26 * @Class MenuBar
26 * @Class MenuBar
27 *
27 *
28 * @constructor
28 * @constructor
29 *
29 *
30 *
30 *
31 * @param selector {string} selector for the menubar element in DOM
31 * @param selector {string} selector for the menubar element in DOM
32 * @param {object} [options]
32 * @param {object} [options]
33 * @param [options.baseProjectUrl] {String} String to use for the
33 * @param [options.baseProjectUrl] {String} String to use for the
34 * Base Project url, default would be to inspect
34 * Base Project url, default would be to inspect
35 * $('body').data('baseProjectUrl');
35 * $('body').data('baseProjectUrl');
36 * does not support change for now is set through this option
36 * does not support change for now is set through this option
37 */
37 */
38 var MenuBar = function (selector, options) {
38 var MenuBar = function (selector, options) {
39 options = options || {};
39 options = options || {};
40 if (options.baseProjectUrl !== undefined) {
40 if (options.baseProjectUrl !== undefined) {
41 this._baseProjectUrl = options.baseProjectUrl;
41 this._baseProjectUrl = options.baseProjectUrl;
42 }
42 }
43 this.selector = selector;
43 this.selector = selector;
44 if (this.selector !== undefined) {
44 if (this.selector !== undefined) {
45 this.element = $(selector);
45 this.element = $(selector);
46 this.style();
46 this.style();
47 this.bind_events();
47 this.bind_events();
48 }
48 }
49 };
49 };
50
50
51 MenuBar.prototype.baseProjectUrl = function(){
51 MenuBar.prototype.baseProjectUrl = function(){
52 return this._baseProjectUrl || $('body').data('baseProjectUrl');
52 return this._baseProjectUrl || $('body').data('baseProjectUrl');
53 };
53 };
54
54
55 MenuBar.prototype.notebookPath = function() {
55 MenuBar.prototype.notebookPath = function() {
56 var path = $('body').data('notebookPath');
56 var path = $('body').data('notebookPath');
57 path = decodeURIComponent(path);
57 path = decodeURIComponent(path);
58 return path;
58 return path;
59 };
59 };
60
60
61 MenuBar.prototype.style = function () {
61 MenuBar.prototype.style = function () {
62 this.element.addClass('border-box-sizing');
62 this.element.addClass('border-box-sizing');
63 this.element.find("li").click(function (event, ui) {
63 this.element.find("li").click(function (event, ui) {
64 // The selected cell loses focus when the menu is entered, so we
64 // The selected cell loses focus when the menu is entered, so we
65 // re-select it upon selection.
65 // re-select it upon selection.
66 var i = IPython.notebook.get_selected_index();
66 var i = IPython.notebook.get_selected_index();
67 IPython.notebook.select(i);
67 IPython.notebook.select(i);
68 }
68 }
69 );
69 );
70 };
70 };
71
71
72 MenuBar.prototype._nbconvert = function (format, download) {
72 MenuBar.prototype._nbconvert = function (format, download) {
73 download = download || false;
73 download = download || false;
74 var notebook_name = IPython.notebook.get_notebook_name();
74 var notebook_name = IPython.notebook.get_notebook_name();
75 if (IPython.notebook.dirty) {
75 if (IPython.notebook.dirty) {
76 IPython.notebook.save_notebook({async : false});
76 IPython.notebook.save_notebook({async : false});
77 }
77 }
78 var url = utils.url_path_join(
78 var url = utils.url_path_join(
79 this.baseProjectUrl(),
79 this.baseProjectUrl(),
80 'nbconvert',
80 'nbconvert',
81 format,
81 format,
82 this.notebookPath(),
82 this.notebookPath(),
83 notebook_name + '.ipynb'
83 notebook_name + '.ipynb'
84 ) + "?download=" + download.toString();
84 ) + "?download=" + download.toString();
85
85
86 window.open(url);
86 window.open(url);
87 }
87 }
88
88
89 MenuBar.prototype.bind_events = function () {
89 MenuBar.prototype.bind_events = function () {
90 // File
90 // File
91 var that = this;
91 var that = this;
92 this.element.find('#new_notebook').click(function () {
92 this.element.find('#new_notebook').click(function () {
93 IPython.notebook.new_notebook();
93 IPython.notebook.new_notebook();
94 });
94 });
95 this.element.find('#open_notebook').click(function () {
95 this.element.find('#open_notebook').click(function () {
96 window.open(utils.url_join_encode(
96 window.open(utils.url_join_encode(
97 that.baseProjectUrl(),
97 that.baseProjectUrl(),
98 'tree',
98 'tree',
99 that.notebookPath()
99 that.notebookPath()
100 ));
100 ));
101 });
101 });
102 this.element.find('#copy_notebook').click(function () {
102 this.element.find('#copy_notebook').click(function () {
103 IPython.notebook.copy_notebook();
103 IPython.notebook.copy_notebook();
104 return false;
104 return false;
105 });
105 });
106 this.element.find('#download_ipynb').click(function () {
106 this.element.find('#download_ipynb').click(function () {
107 var notebook_name = IPython.notebook.get_notebook_name();
107 var notebook_name = IPython.notebook.get_notebook_name();
108 if (IPython.notebook.dirty) {
108 if (IPython.notebook.dirty) {
109 IPython.notebook.save_notebook({async : false});
109 IPython.notebook.save_notebook({async : false});
110 }
110 }
111
111
112 var url = utils.url_join_encode(
112 var url = utils.url_join_encode(
113 that.baseProjectUrl(),
113 that.baseProjectUrl(),
114 'files',
114 'files',
115 that.notebookPath(),
115 that.notebookPath(),
116 notebook_name + '.ipynb'
116 notebook_name + '.ipynb'
117 );
117 );
118 window.location.assign(url);
118 window.location.assign(url);
119 });
119 });
120
120
121 this.element.find('#print_preview').click(function () {
121 this.element.find('#print_preview').click(function () {
122 that._nbconvert('html', false);
122 that._nbconvert('html', false);
123 });
123 });
124
124
125 this.element.find('#download_py').click(function () {
125 this.element.find('#download_py').click(function () {
126 that._nbconvert('python', true);
126 that._nbconvert('python', true);
127 });
127 });
128
128
129 this.element.find('#download_html').click(function () {
129 this.element.find('#download_html').click(function () {
130 that._nbconvert('html', true);
130 that._nbconvert('html', true);
131 });
131 });
132
132
133 this.element.find('#download_rst').click(function () {
133 this.element.find('#download_rst').click(function () {
134 that._nbconvert('rst', true);
134 that._nbconvert('rst', true);
135 });
135 });
136
136
137 this.element.find('#rename_notebook').click(function () {
137 this.element.find('#rename_notebook').click(function () {
138 IPython.save_widget.rename_notebook();
138 IPython.save_widget.rename_notebook();
139 });
139 });
140 this.element.find('#save_checkpoint').click(function () {
140 this.element.find('#save_checkpoint').click(function () {
141 IPython.notebook.save_checkpoint();
141 IPython.notebook.save_checkpoint();
142 });
142 });
143 this.element.find('#restore_checkpoint').click(function () {
143 this.element.find('#restore_checkpoint').click(function () {
144 });
144 });
145 this.element.find('#kill_and_exit').click(function () {
145 this.element.find('#kill_and_exit').click(function () {
146 IPython.notebook.session.delete();
146 IPython.notebook.session.delete();
147 setTimeout(function(){
147 setTimeout(function(){
148 // allow closing of new tabs in Chromium, impossible in FF
148 // allow closing of new tabs in Chromium, impossible in FF
149 window.open('', '_self', '');
149 window.open('', '_self', '');
150 window.close();
150 window.close();
151 }, 500);
151 }, 500);
152 });
152 });
153 // Edit
153 // Edit
154 this.element.find('#cut_cell').click(function () {
154 this.element.find('#cut_cell').click(function () {
155 IPython.notebook.cut_cell();
155 IPython.notebook.cut_cell();
156 });
156 });
157 this.element.find('#copy_cell').click(function () {
157 this.element.find('#copy_cell').click(function () {
158 IPython.notebook.copy_cell();
158 IPython.notebook.copy_cell();
159 });
159 });
160 this.element.find('#delete_cell').click(function () {
160 this.element.find('#delete_cell').click(function () {
161 IPython.notebook.delete_cell();
161 IPython.notebook.delete_cell();
162 });
162 });
163 this.element.find('#undelete_cell').click(function () {
163 this.element.find('#undelete_cell').click(function () {
164 IPython.notebook.undelete_cell();
164 IPython.notebook.undelete_cell();
165 });
165 });
166 this.element.find('#split_cell').click(function () {
166 this.element.find('#split_cell').click(function () {
167 IPython.notebook.split_cell();
167 IPython.notebook.split_cell();
168 });
168 });
169 this.element.find('#merge_cell_above').click(function () {
169 this.element.find('#merge_cell_above').click(function () {
170 IPython.notebook.merge_cell_above();
170 IPython.notebook.merge_cell_above();
171 });
171 });
172 this.element.find('#merge_cell_below').click(function () {
172 this.element.find('#merge_cell_below').click(function () {
173 IPython.notebook.merge_cell_below();
173 IPython.notebook.merge_cell_below();
174 });
174 });
175 this.element.find('#move_cell_up').click(function () {
175 this.element.find('#move_cell_up').click(function () {
176 IPython.notebook.move_cell_up();
176 IPython.notebook.move_cell_up();
177 });
177 });
178 this.element.find('#move_cell_down').click(function () {
178 this.element.find('#move_cell_down').click(function () {
179 IPython.notebook.move_cell_down();
179 IPython.notebook.move_cell_down();
180 });
180 });
181 this.element.find('#edit_nb_metadata').click(function () {
181 this.element.find('#edit_nb_metadata').click(function () {
182 IPython.notebook.edit_metadata();
182 IPython.notebook.edit_metadata();
183 });
183 });
184
184
185 // View
185 // View
186 this.element.find('#toggle_header').click(function () {
186 this.element.find('#toggle_header').click(function () {
187 $('div#header').toggle();
187 $('div#header').toggle();
188 IPython.layout_manager.do_resize();
188 IPython.layout_manager.do_resize();
189 });
189 });
190 this.element.find('#toggle_toolbar').click(function () {
190 this.element.find('#toggle_toolbar').click(function () {
191 $('div#maintoolbar').toggle();
191 $('div#maintoolbar').toggle();
192 IPython.layout_manager.do_resize();
192 IPython.layout_manager.do_resize();
193 });
193 });
194 // Insert
194 // Insert
195 this.element.find('#insert_cell_above').click(function () {
195 this.element.find('#insert_cell_above').click(function () {
196 IPython.notebook.insert_cell_above('code');
196 IPython.notebook.insert_cell_above('code');
197 IPython.notebook.select_prev();
197 IPython.notebook.select_prev();
198 });
198 });
199 this.element.find('#insert_cell_below').click(function () {
199 this.element.find('#insert_cell_below').click(function () {
200 IPython.notebook.insert_cell_below('code');
200 IPython.notebook.insert_cell_below('code');
201 IPython.notebook.select_next();
201 IPython.notebook.select_next();
202 });
202 });
203 // Cell
203 // Cell
204 this.element.find('#run_cell').click(function () {
204 this.element.find('#run_cell').click(function () {
205 IPython.notebook.execute_cell();
205 IPython.notebook.execute_cell();
206 });
206 });
207 this.element.find('#run_cell_select_below').click(function () {
207 this.element.find('#run_cell_select_below').click(function () {
208 IPython.notebook.execute_cell_and_select_below();
208 IPython.notebook.execute_cell_and_select_below();
209 });
209 });
210 this.element.find('#run_cell_insert_below').click(function () {
210 this.element.find('#run_cell_insert_below').click(function () {
211 IPython.notebook.execute_cell_and_insert_below();
211 IPython.notebook.execute_cell_and_insert_below();
212 });
212 });
213 this.element.find('#run_all_cells').click(function () {
213 this.element.find('#run_all_cells').click(function () {
214 IPython.notebook.execute_all_cells();
214 IPython.notebook.execute_all_cells();
215 });
215 });
216 this.element.find('#run_all_cells_above').click(function () {
216 this.element.find('#run_all_cells_above').click(function () {
217 IPython.notebook.execute_cells_above();
217 IPython.notebook.execute_cells_above();
218 });
218 });
219 this.element.find('#run_all_cells_below').click(function () {
219 this.element.find('#run_all_cells_below').click(function () {
220 IPython.notebook.execute_cells_below();
220 IPython.notebook.execute_cells_below();
221 });
221 });
222 this.element.find('#to_code').click(function () {
222 this.element.find('#to_code').click(function () {
223 IPython.notebook.to_code();
223 IPython.notebook.to_code();
224 });
224 });
225 this.element.find('#to_markdown').click(function () {
225 this.element.find('#to_markdown').click(function () {
226 IPython.notebook.to_markdown();
226 IPython.notebook.to_markdown();
227 });
227 });
228 this.element.find('#to_raw').click(function () {
228 this.element.find('#to_raw').click(function () {
229 IPython.notebook.to_raw();
229 IPython.notebook.to_raw();
230 });
230 });
231 this.element.find('#to_heading1').click(function () {
231 this.element.find('#to_heading1').click(function () {
232 IPython.notebook.to_heading(undefined, 1);
232 IPython.notebook.to_heading(undefined, 1);
233 });
233 });
234 this.element.find('#to_heading2').click(function () {
234 this.element.find('#to_heading2').click(function () {
235 IPython.notebook.to_heading(undefined, 2);
235 IPython.notebook.to_heading(undefined, 2);
236 });
236 });
237 this.element.find('#to_heading3').click(function () {
237 this.element.find('#to_heading3').click(function () {
238 IPython.notebook.to_heading(undefined, 3);
238 IPython.notebook.to_heading(undefined, 3);
239 });
239 });
240 this.element.find('#to_heading4').click(function () {
240 this.element.find('#to_heading4').click(function () {
241 IPython.notebook.to_heading(undefined, 4);
241 IPython.notebook.to_heading(undefined, 4);
242 });
242 });
243 this.element.find('#to_heading5').click(function () {
243 this.element.find('#to_heading5').click(function () {
244 IPython.notebook.to_heading(undefined, 5);
244 IPython.notebook.to_heading(undefined, 5);
245 });
245 });
246 this.element.find('#to_heading6').click(function () {
246 this.element.find('#to_heading6').click(function () {
247 IPython.notebook.to_heading(undefined, 6);
247 IPython.notebook.to_heading(undefined, 6);
248 });
248 });
249 this.element.find('#collapse_current_output').click(function () {
249
250 IPython.notebook.collapse_output();
250 this.element.find('#toggle_current_output').click(function () {
251 });
251 IPython.notebook.toggle_output();
252 this.element.find('#scroll_current_output').click(function () {
253 IPython.notebook.scroll_output();
254 });
252 });
255 this.element.find('#expand_current_output').click(function () {
253 this.element.find('#toggle_current_output_scroll').click(function () {
256 IPython.notebook.expand_output();
254 IPython.notebook.toggle_output_scroll();
257 });
255 });
258 this.element.find('#clear_current_output').click(function () {
256 this.element.find('#clear_current_output').click(function () {
259 IPython.notebook.clear_output();
257 IPython.notebook.clear_output();
260 });
258 });
261 this.element.find('#collapse_all_output').click(function () {
259
262 IPython.notebook.collapse_all_output();
260 this.element.find('#toggle_all_output').click(function () {
263 });
261 IPython.notebook.toggle_all_output();
264 this.element.find('#scroll_all_output').click(function () {
265 IPython.notebook.scroll_all_output();
266 });
262 });
267 this.element.find('#expand_all_output').click(function () {
263 this.element.find('#toggle_all_output_scroll').click(function () {
268 IPython.notebook.expand_all_output();
264 IPython.notebook.toggle_all_output_scroll();
269 });
265 });
270 this.element.find('#clear_all_output').click(function () {
266 this.element.find('#clear_all_output').click(function () {
271 IPython.notebook.clear_all_output();
267 IPython.notebook.clear_all_output();
272 });
268 });
269
273 // Kernel
270 // Kernel
274 this.element.find('#int_kernel').click(function () {
271 this.element.find('#int_kernel').click(function () {
275 IPython.notebook.session.interrupt_kernel();
272 IPython.notebook.session.interrupt_kernel();
276 });
273 });
277 this.element.find('#restart_kernel').click(function () {
274 this.element.find('#restart_kernel').click(function () {
278 IPython.notebook.restart_kernel();
275 IPython.notebook.restart_kernel();
279 });
276 });
280 // Help
277 // Help
281 this.element.find('#keyboard_shortcuts').click(function () {
278 this.element.find('#keyboard_shortcuts').click(function () {
282 IPython.quick_help.show_keyboard_shortcuts();
279 IPython.quick_help.show_keyboard_shortcuts();
283 });
280 });
284
281
285 this.update_restore_checkpoint(null);
282 this.update_restore_checkpoint(null);
286
283
287 $([IPython.events]).on('checkpoints_listed.Notebook', function (event, data) {
284 $([IPython.events]).on('checkpoints_listed.Notebook', function (event, data) {
288 that.update_restore_checkpoint(IPython.notebook.checkpoints);
285 that.update_restore_checkpoint(IPython.notebook.checkpoints);
289 });
286 });
290
287
291 $([IPython.events]).on('checkpoint_created.Notebook', function (event, data) {
288 $([IPython.events]).on('checkpoint_created.Notebook', function (event, data) {
292 that.update_restore_checkpoint(IPython.notebook.checkpoints);
289 that.update_restore_checkpoint(IPython.notebook.checkpoints);
293 });
290 });
294 };
291 };
295
292
296 MenuBar.prototype.update_restore_checkpoint = function(checkpoints) {
293 MenuBar.prototype.update_restore_checkpoint = function(checkpoints) {
297 var ul = this.element.find("#restore_checkpoint").find("ul");
294 var ul = this.element.find("#restore_checkpoint").find("ul");
298 ul.empty();
295 ul.empty();
299 if (!checkpoints || checkpoints.length === 0) {
296 if (!checkpoints || checkpoints.length === 0) {
300 ul.append(
297 ul.append(
301 $("<li/>")
298 $("<li/>")
302 .addClass("disabled")
299 .addClass("disabled")
303 .append(
300 .append(
304 $("<a/>")
301 $("<a/>")
305 .text("No checkpoints")
302 .text("No checkpoints")
306 )
303 )
307 );
304 );
308 return;
305 return;
309 }
306 }
310
307
311 checkpoints.map(function (checkpoint) {
308 checkpoints.map(function (checkpoint) {
312 var d = new Date(checkpoint.last_modified);
309 var d = new Date(checkpoint.last_modified);
313 ul.append(
310 ul.append(
314 $("<li/>").append(
311 $("<li/>").append(
315 $("<a/>")
312 $("<a/>")
316 .attr("href", "#")
313 .attr("href", "#")
317 .text(d.format("mmm dd HH:MM:ss"))
314 .text(d.format("mmm dd HH:MM:ss"))
318 .click(function () {
315 .click(function () {
319 IPython.notebook.restore_checkpoint_dialog(checkpoint);
316 IPython.notebook.restore_checkpoint_dialog(checkpoint);
320 })
317 })
321 )
318 )
322 );
319 );
323 });
320 });
324 };
321 };
325
322
326 IPython.MenuBar = MenuBar;
323 IPython.MenuBar = MenuBar;
327
324
328 return IPython;
325 return IPython;
329
326
330 }(IPython));
327 }(IPython));
@@ -1,2255 +1,2284 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2011 The IPython Development Team
2 // Copyright (C) 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 "use strict";
13 "use strict";
14
14
15 var utils = IPython.utils;
15 var utils = IPython.utils;
16
16
17 /**
17 /**
18 * A notebook contains and manages cells.
18 * A notebook contains and manages cells.
19 *
19 *
20 * @class Notebook
20 * @class Notebook
21 * @constructor
21 * @constructor
22 * @param {String} selector A jQuery selector for the notebook's DOM element
22 * @param {String} selector A jQuery selector for the notebook's DOM element
23 * @param {Object} [options] A config object
23 * @param {Object} [options] A config object
24 */
24 */
25 var Notebook = function (selector, options) {
25 var Notebook = function (selector, options) {
26 var options = options || {};
26 var options = options || {};
27 this._baseProjectUrl = options.baseProjectUrl;
27 this._baseProjectUrl = options.baseProjectUrl;
28 this.notebook_path = options.notebookPath;
28 this.notebook_path = options.notebookPath;
29 this.notebook_name = options.notebookName;
29 this.notebook_name = options.notebookName;
30 this.element = $(selector);
30 this.element = $(selector);
31 this.element.scroll();
31 this.element.scroll();
32 this.element.data("notebook", this);
32 this.element.data("notebook", this);
33 this.next_prompt_number = 1;
33 this.next_prompt_number = 1;
34 this.session = null;
34 this.session = null;
35 this.kernel = null;
35 this.kernel = null;
36 this.clipboard = null;
36 this.clipboard = null;
37 this.undelete_backup = null;
37 this.undelete_backup = null;
38 this.undelete_index = null;
38 this.undelete_index = null;
39 this.undelete_below = false;
39 this.undelete_below = false;
40 this.paste_enabled = false;
40 this.paste_enabled = false;
41 // It is important to start out in command mode to match the intial mode
41 // It is important to start out in command mode to match the intial mode
42 // of the KeyboardManager.
42 // of the KeyboardManager.
43 this.mode = 'command';
43 this.mode = 'command';
44 this.set_dirty(false);
44 this.set_dirty(false);
45 this.metadata = {};
45 this.metadata = {};
46 this._checkpoint_after_save = false;
46 this._checkpoint_after_save = false;
47 this.last_checkpoint = null;
47 this.last_checkpoint = null;
48 this.checkpoints = [];
48 this.checkpoints = [];
49 this.autosave_interval = 0;
49 this.autosave_interval = 0;
50 this.autosave_timer = null;
50 this.autosave_timer = null;
51 // autosave *at most* every two minutes
51 // autosave *at most* every two minutes
52 this.minimum_autosave_interval = 120000;
52 this.minimum_autosave_interval = 120000;
53 // single worksheet for now
53 // single worksheet for now
54 this.worksheet_metadata = {};
54 this.worksheet_metadata = {};
55 this.notebook_name_blacklist_re = /[\/\\:]/;
55 this.notebook_name_blacklist_re = /[\/\\:]/;
56 this.nbformat = 3 // Increment this when changing the nbformat
56 this.nbformat = 3 // Increment this when changing the nbformat
57 this.nbformat_minor = 0 // Increment this when changing the nbformat
57 this.nbformat_minor = 0 // Increment this when changing the nbformat
58 this.style();
58 this.style();
59 this.create_elements();
59 this.create_elements();
60 this.bind_events();
60 this.bind_events();
61 };
61 };
62
62
63 /**
63 /**
64 * Tweak the notebook's CSS style.
64 * Tweak the notebook's CSS style.
65 *
65 *
66 * @method style
66 * @method style
67 */
67 */
68 Notebook.prototype.style = function () {
68 Notebook.prototype.style = function () {
69 $('div#notebook').addClass('border-box-sizing');
69 $('div#notebook').addClass('border-box-sizing');
70 };
70 };
71
71
72 /**
72 /**
73 * Get the root URL of the notebook server.
73 * Get the root URL of the notebook server.
74 *
74 *
75 * @method baseProjectUrl
75 * @method baseProjectUrl
76 * @return {String} The base project URL
76 * @return {String} The base project URL
77 */
77 */
78 Notebook.prototype.baseProjectUrl = function() {
78 Notebook.prototype.baseProjectUrl = function() {
79 return this._baseProjectUrl || $('body').data('baseProjectUrl');
79 return this._baseProjectUrl || $('body').data('baseProjectUrl');
80 };
80 };
81
81
82 Notebook.prototype.notebookName = function() {
82 Notebook.prototype.notebookName = function() {
83 return $('body').data('notebookName');
83 return $('body').data('notebookName');
84 };
84 };
85
85
86 Notebook.prototype.notebookPath = function() {
86 Notebook.prototype.notebookPath = function() {
87 return $('body').data('notebookPath');
87 return $('body').data('notebookPath');
88 };
88 };
89
89
90 /**
90 /**
91 * Create an HTML and CSS representation of the notebook.
91 * Create an HTML and CSS representation of the notebook.
92 *
92 *
93 * @method create_elements
93 * @method create_elements
94 */
94 */
95 Notebook.prototype.create_elements = function () {
95 Notebook.prototype.create_elements = function () {
96 var that = this;
96 var that = this;
97 this.element.attr('tabindex','-1');
97 this.element.attr('tabindex','-1');
98 this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
98 this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
99 // We add this end_space div to the end of the notebook div to:
99 // We add this end_space div to the end of the notebook div to:
100 // i) provide a margin between the last cell and the end of the notebook
100 // i) provide a margin between the last cell and the end of the notebook
101 // ii) to prevent the div from scrolling up when the last cell is being
101 // ii) to prevent the div from scrolling up when the last cell is being
102 // edited, but is too low on the page, which browsers will do automatically.
102 // edited, but is too low on the page, which browsers will do automatically.
103 var end_space = $('<div/>').addClass('end_space');
103 var end_space = $('<div/>').addClass('end_space');
104 end_space.dblclick(function (e) {
104 end_space.dblclick(function (e) {
105 var ncells = that.ncells();
105 var ncells = that.ncells();
106 that.insert_cell_below('code',ncells-1);
106 that.insert_cell_below('code',ncells-1);
107 });
107 });
108 this.element.append(this.container);
108 this.element.append(this.container);
109 this.container.append(end_space);
109 this.container.append(end_space);
110 };
110 };
111
111
112 /**
112 /**
113 * Bind JavaScript events: key presses and custom IPython events.
113 * Bind JavaScript events: key presses and custom IPython events.
114 *
114 *
115 * @method bind_events
115 * @method bind_events
116 */
116 */
117 Notebook.prototype.bind_events = function () {
117 Notebook.prototype.bind_events = function () {
118 var that = this;
118 var that = this;
119
119
120 $([IPython.events]).on('set_next_input.Notebook', function (event, data) {
120 $([IPython.events]).on('set_next_input.Notebook', function (event, data) {
121 var index = that.find_cell_index(data.cell);
121 var index = that.find_cell_index(data.cell);
122 var new_cell = that.insert_cell_below('code',index);
122 var new_cell = that.insert_cell_below('code',index);
123 new_cell.set_text(data.text);
123 new_cell.set_text(data.text);
124 that.dirty = true;
124 that.dirty = true;
125 });
125 });
126
126
127 $([IPython.events]).on('set_dirty.Notebook', function (event, data) {
127 $([IPython.events]).on('set_dirty.Notebook', function (event, data) {
128 that.dirty = data.value;
128 that.dirty = data.value;
129 });
129 });
130
130
131 $([IPython.events]).on('select.Cell', function (event, data) {
131 $([IPython.events]).on('select.Cell', function (event, data) {
132 var index = that.find_cell_index(data.cell);
132 var index = that.find_cell_index(data.cell);
133 that.select(index);
133 that.select(index);
134 });
134 });
135
135
136 $([IPython.events]).on('edit_mode.Cell', function (event, data) {
136 $([IPython.events]).on('edit_mode.Cell', function (event, data) {
137 var index = that.find_cell_index(data.cell);
137 var index = that.find_cell_index(data.cell);
138 that.select(index);
138 that.select(index);
139 that.edit_mode();
139 that.edit_mode();
140 });
140 });
141
141
142 $([IPython.events]).on('command_mode.Cell', function (event, data) {
142 $([IPython.events]).on('command_mode.Cell', function (event, data) {
143 that.command_mode();
143 that.command_mode();
144 });
144 });
145
145
146 $([IPython.events]).on('status_autorestarting.Kernel', function () {
146 $([IPython.events]).on('status_autorestarting.Kernel', function () {
147 IPython.dialog.modal({
147 IPython.dialog.modal({
148 title: "Kernel Restarting",
148 title: "Kernel Restarting",
149 body: "The kernel appears to have died. It will restart automatically.",
149 body: "The kernel appears to have died. It will restart automatically.",
150 buttons: {
150 buttons: {
151 OK : {
151 OK : {
152 class : "btn-primary"
152 class : "btn-primary"
153 }
153 }
154 }
154 }
155 });
155 });
156 });
156 });
157
157
158 var collapse_time = function (time) {
158 var collapse_time = function (time) {
159 var app_height = $('#ipython-main-app').height(); // content height
159 var app_height = $('#ipython-main-app').height(); // content height
160 var splitter_height = $('div#pager_splitter').outerHeight(true);
160 var splitter_height = $('div#pager_splitter').outerHeight(true);
161 var new_height = app_height - splitter_height;
161 var new_height = app_height - splitter_height;
162 that.element.animate({height : new_height + 'px'}, time);
162 that.element.animate({height : new_height + 'px'}, time);
163 };
163 };
164
164
165 this.element.bind('collapse_pager', function (event, extrap) {
165 this.element.bind('collapse_pager', function (event, extrap) {
166 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
166 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
167 collapse_time(time);
167 collapse_time(time);
168 });
168 });
169
169
170 var expand_time = function (time) {
170 var expand_time = function (time) {
171 var app_height = $('#ipython-main-app').height(); // content height
171 var app_height = $('#ipython-main-app').height(); // content height
172 var splitter_height = $('div#pager_splitter').outerHeight(true);
172 var splitter_height = $('div#pager_splitter').outerHeight(true);
173 var pager_height = $('div#pager').outerHeight(true);
173 var pager_height = $('div#pager').outerHeight(true);
174 var new_height = app_height - pager_height - splitter_height;
174 var new_height = app_height - pager_height - splitter_height;
175 that.element.animate({height : new_height + 'px'}, time);
175 that.element.animate({height : new_height + 'px'}, time);
176 };
176 };
177
177
178 this.element.bind('expand_pager', function (event, extrap) {
178 this.element.bind('expand_pager', function (event, extrap) {
179 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
179 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
180 expand_time(time);
180 expand_time(time);
181 });
181 });
182
182
183 // Firefox 22 broke $(window).on("beforeunload")
183 // Firefox 22 broke $(window).on("beforeunload")
184 // I'm not sure why or how.
184 // I'm not sure why or how.
185 window.onbeforeunload = function (e) {
185 window.onbeforeunload = function (e) {
186 // TODO: Make killing the kernel configurable.
186 // TODO: Make killing the kernel configurable.
187 var kill_kernel = false;
187 var kill_kernel = false;
188 if (kill_kernel) {
188 if (kill_kernel) {
189 that.session.kill_kernel();
189 that.session.kill_kernel();
190 }
190 }
191 // if we are autosaving, trigger an autosave on nav-away.
191 // if we are autosaving, trigger an autosave on nav-away.
192 // still warn, because if we don't the autosave may fail.
192 // still warn, because if we don't the autosave may fail.
193 if (that.dirty) {
193 if (that.dirty) {
194 if ( that.autosave_interval ) {
194 if ( that.autosave_interval ) {
195 // schedule autosave in a timeout
195 // schedule autosave in a timeout
196 // this gives you a chance to forcefully discard changes
196 // this gives you a chance to forcefully discard changes
197 // by reloading the page if you *really* want to.
197 // by reloading the page if you *really* want to.
198 // the timer doesn't start until you *dismiss* the dialog.
198 // the timer doesn't start until you *dismiss* the dialog.
199 setTimeout(function () {
199 setTimeout(function () {
200 if (that.dirty) {
200 if (that.dirty) {
201 that.save_notebook();
201 that.save_notebook();
202 }
202 }
203 }, 1000);
203 }, 1000);
204 return "Autosave in progress, latest changes may be lost.";
204 return "Autosave in progress, latest changes may be lost.";
205 } else {
205 } else {
206 return "Unsaved changes will be lost.";
206 return "Unsaved changes will be lost.";
207 }
207 }
208 };
208 };
209 // Null is the *only* return value that will make the browser not
209 // Null is the *only* return value that will make the browser not
210 // pop up the "don't leave" dialog.
210 // pop up the "don't leave" dialog.
211 return null;
211 return null;
212 };
212 };
213 };
213 };
214
214
215 /**
215 /**
216 * Set the dirty flag, and trigger the set_dirty.Notebook event
216 * Set the dirty flag, and trigger the set_dirty.Notebook event
217 *
217 *
218 * @method set_dirty
218 * @method set_dirty
219 */
219 */
220 Notebook.prototype.set_dirty = function (value) {
220 Notebook.prototype.set_dirty = function (value) {
221 if (value === undefined) {
221 if (value === undefined) {
222 value = true;
222 value = true;
223 }
223 }
224 if (this.dirty == value) {
224 if (this.dirty == value) {
225 return;
225 return;
226 }
226 }
227 $([IPython.events]).trigger('set_dirty.Notebook', {value: value});
227 $([IPython.events]).trigger('set_dirty.Notebook', {value: value});
228 };
228 };
229
229
230 /**
230 /**
231 * Scroll the top of the page to a given cell.
231 * Scroll the top of the page to a given cell.
232 *
232 *
233 * @method scroll_to_cell
233 * @method scroll_to_cell
234 * @param {Number} cell_number An index of the cell to view
234 * @param {Number} cell_number An index of the cell to view
235 * @param {Number} time Animation time in milliseconds
235 * @param {Number} time Animation time in milliseconds
236 * @return {Number} Pixel offset from the top of the container
236 * @return {Number} Pixel offset from the top of the container
237 */
237 */
238 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
238 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
239 var cells = this.get_cells();
239 var cells = this.get_cells();
240 var time = time || 0;
240 var time = time || 0;
241 cell_number = Math.min(cells.length-1,cell_number);
241 cell_number = Math.min(cells.length-1,cell_number);
242 cell_number = Math.max(0 ,cell_number);
242 cell_number = Math.max(0 ,cell_number);
243 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
243 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
244 this.element.animate({scrollTop:scroll_value}, time);
244 this.element.animate({scrollTop:scroll_value}, time);
245 return scroll_value;
245 return scroll_value;
246 };
246 };
247
247
248 /**
248 /**
249 * Scroll to the bottom of the page.
249 * Scroll to the bottom of the page.
250 *
250 *
251 * @method scroll_to_bottom
251 * @method scroll_to_bottom
252 */
252 */
253 Notebook.prototype.scroll_to_bottom = function () {
253 Notebook.prototype.scroll_to_bottom = function () {
254 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
254 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
255 };
255 };
256
256
257 /**
257 /**
258 * Scroll to the top of the page.
258 * Scroll to the top of the page.
259 *
259 *
260 * @method scroll_to_top
260 * @method scroll_to_top
261 */
261 */
262 Notebook.prototype.scroll_to_top = function () {
262 Notebook.prototype.scroll_to_top = function () {
263 this.element.animate({scrollTop:0}, 0);
263 this.element.animate({scrollTop:0}, 0);
264 };
264 };
265
265
266 // Edit Notebook metadata
266 // Edit Notebook metadata
267
267
268 Notebook.prototype.edit_metadata = function () {
268 Notebook.prototype.edit_metadata = function () {
269 var that = this;
269 var that = this;
270 IPython.dialog.edit_metadata(this.metadata, function (md) {
270 IPython.dialog.edit_metadata(this.metadata, function (md) {
271 that.metadata = md;
271 that.metadata = md;
272 }, 'Notebook');
272 }, 'Notebook');
273 };
273 };
274
274
275 // Cell indexing, retrieval, etc.
275 // Cell indexing, retrieval, etc.
276
276
277 /**
277 /**
278 * Get all cell elements in the notebook.
278 * Get all cell elements in the notebook.
279 *
279 *
280 * @method get_cell_elements
280 * @method get_cell_elements
281 * @return {jQuery} A selector of all cell elements
281 * @return {jQuery} A selector of all cell elements
282 */
282 */
283 Notebook.prototype.get_cell_elements = function () {
283 Notebook.prototype.get_cell_elements = function () {
284 return this.container.children("div.cell");
284 return this.container.children("div.cell");
285 };
285 };
286
286
287 /**
287 /**
288 * Get a particular cell element.
288 * Get a particular cell element.
289 *
289 *
290 * @method get_cell_element
290 * @method get_cell_element
291 * @param {Number} index An index of a cell to select
291 * @param {Number} index An index of a cell to select
292 * @return {jQuery} A selector of the given cell.
292 * @return {jQuery} A selector of the given cell.
293 */
293 */
294 Notebook.prototype.get_cell_element = function (index) {
294 Notebook.prototype.get_cell_element = function (index) {
295 var result = null;
295 var result = null;
296 var e = this.get_cell_elements().eq(index);
296 var e = this.get_cell_elements().eq(index);
297 if (e.length !== 0) {
297 if (e.length !== 0) {
298 result = e;
298 result = e;
299 }
299 }
300 return result;
300 return result;
301 };
301 };
302
302
303 /**
303 /**
304 * Try to get a particular cell by msg_id.
304 * Try to get a particular cell by msg_id.
305 *
305 *
306 * @method get_msg_cell
306 * @method get_msg_cell
307 * @param {String} msg_id A message UUID
307 * @param {String} msg_id A message UUID
308 * @return {Cell} Cell or null if no cell was found.
308 * @return {Cell} Cell or null if no cell was found.
309 */
309 */
310 Notebook.prototype.get_msg_cell = function (msg_id) {
310 Notebook.prototype.get_msg_cell = function (msg_id) {
311 return IPython.CodeCell.msg_cells[msg_id] || null;
311 return IPython.CodeCell.msg_cells[msg_id] || null;
312 };
312 };
313
313
314 /**
314 /**
315 * Count the cells in this notebook.
315 * Count the cells in this notebook.
316 *
316 *
317 * @method ncells
317 * @method ncells
318 * @return {Number} The number of cells in this notebook
318 * @return {Number} The number of cells in this notebook
319 */
319 */
320 Notebook.prototype.ncells = function () {
320 Notebook.prototype.ncells = function () {
321 return this.get_cell_elements().length;
321 return this.get_cell_elements().length;
322 };
322 };
323
323
324 /**
324 /**
325 * Get all Cell objects in this notebook.
325 * Get all Cell objects in this notebook.
326 *
326 *
327 * @method get_cells
327 * @method get_cells
328 * @return {Array} This notebook's Cell objects
328 * @return {Array} This notebook's Cell objects
329 */
329 */
330 // TODO: we are often calling cells as cells()[i], which we should optimize
330 // TODO: we are often calling cells as cells()[i], which we should optimize
331 // to cells(i) or a new method.
331 // to cells(i) or a new method.
332 Notebook.prototype.get_cells = function () {
332 Notebook.prototype.get_cells = function () {
333 return this.get_cell_elements().toArray().map(function (e) {
333 return this.get_cell_elements().toArray().map(function (e) {
334 return $(e).data("cell");
334 return $(e).data("cell");
335 });
335 });
336 };
336 };
337
337
338 /**
338 /**
339 * Get a Cell object from this notebook.
339 * Get a Cell object from this notebook.
340 *
340 *
341 * @method get_cell
341 * @method get_cell
342 * @param {Number} index An index of a cell to retrieve
342 * @param {Number} index An index of a cell to retrieve
343 * @return {Cell} A particular cell
343 * @return {Cell} A particular cell
344 */
344 */
345 Notebook.prototype.get_cell = function (index) {
345 Notebook.prototype.get_cell = function (index) {
346 var result = null;
346 var result = null;
347 var ce = this.get_cell_element(index);
347 var ce = this.get_cell_element(index);
348 if (ce !== null) {
348 if (ce !== null) {
349 result = ce.data('cell');
349 result = ce.data('cell');
350 }
350 }
351 return result;
351 return result;
352 }
352 }
353
353
354 /**
354 /**
355 * Get the cell below a given cell.
355 * Get the cell below a given cell.
356 *
356 *
357 * @method get_next_cell
357 * @method get_next_cell
358 * @param {Cell} cell The provided cell
358 * @param {Cell} cell The provided cell
359 * @return {Cell} The next cell
359 * @return {Cell} The next cell
360 */
360 */
361 Notebook.prototype.get_next_cell = function (cell) {
361 Notebook.prototype.get_next_cell = function (cell) {
362 var result = null;
362 var result = null;
363 var index = this.find_cell_index(cell);
363 var index = this.find_cell_index(cell);
364 if (this.is_valid_cell_index(index+1)) {
364 if (this.is_valid_cell_index(index+1)) {
365 result = this.get_cell(index+1);
365 result = this.get_cell(index+1);
366 }
366 }
367 return result;
367 return result;
368 }
368 }
369
369
370 /**
370 /**
371 * Get the cell above a given cell.
371 * Get the cell above a given cell.
372 *
372 *
373 * @method get_prev_cell
373 * @method get_prev_cell
374 * @param {Cell} cell The provided cell
374 * @param {Cell} cell The provided cell
375 * @return {Cell} The previous cell
375 * @return {Cell} The previous cell
376 */
376 */
377 Notebook.prototype.get_prev_cell = function (cell) {
377 Notebook.prototype.get_prev_cell = function (cell) {
378 // TODO: off-by-one
378 // TODO: off-by-one
379 // nb.get_prev_cell(nb.get_cell(1)) is null
379 // nb.get_prev_cell(nb.get_cell(1)) is null
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 /**
388 /**
389 * Get the numeric index of a given cell.
389 * Get the numeric index of a given cell.
390 *
390 *
391 * @method find_cell_index
391 * @method find_cell_index
392 * @param {Cell} cell The provided cell
392 * @param {Cell} cell The provided cell
393 * @return {Number} The cell's numeric index
393 * @return {Number} The cell's numeric index
394 */
394 */
395 Notebook.prototype.find_cell_index = function (cell) {
395 Notebook.prototype.find_cell_index = function (cell) {
396 var result = null;
396 var result = null;
397 this.get_cell_elements().filter(function (index) {
397 this.get_cell_elements().filter(function (index) {
398 if ($(this).data("cell") === cell) {
398 if ($(this).data("cell") === cell) {
399 result = index;
399 result = index;
400 };
400 };
401 });
401 });
402 return result;
402 return result;
403 };
403 };
404
404
405 /**
405 /**
406 * Get a given index , or the selected index if none is provided.
406 * Get a given index , or the selected index if none is provided.
407 *
407 *
408 * @method index_or_selected
408 * @method index_or_selected
409 * @param {Number} index A cell's index
409 * @param {Number} index A cell's index
410 * @return {Number} The given index, or selected index if none is provided.
410 * @return {Number} The given index, or selected index if none is provided.
411 */
411 */
412 Notebook.prototype.index_or_selected = function (index) {
412 Notebook.prototype.index_or_selected = function (index) {
413 var i;
413 var i;
414 if (index === undefined || index === null) {
414 if (index === undefined || index === null) {
415 i = this.get_selected_index();
415 i = this.get_selected_index();
416 if (i === null) {
416 if (i === null) {
417 i = 0;
417 i = 0;
418 }
418 }
419 } else {
419 } else {
420 i = index;
420 i = index;
421 }
421 }
422 return i;
422 return i;
423 };
423 };
424
424
425 /**
425 /**
426 * Get the currently selected cell.
426 * Get the currently selected cell.
427 * @method get_selected_cell
427 * @method get_selected_cell
428 * @return {Cell} The selected cell
428 * @return {Cell} The selected cell
429 */
429 */
430 Notebook.prototype.get_selected_cell = function () {
430 Notebook.prototype.get_selected_cell = function () {
431 var index = this.get_selected_index();
431 var index = this.get_selected_index();
432 return this.get_cell(index);
432 return this.get_cell(index);
433 };
433 };
434
434
435 /**
435 /**
436 * Check whether a cell index is valid.
436 * Check whether a cell index is valid.
437 *
437 *
438 * @method is_valid_cell_index
438 * @method is_valid_cell_index
439 * @param {Number} index A cell index
439 * @param {Number} index A cell index
440 * @return True if the index is valid, false otherwise
440 * @return True if the index is valid, false otherwise
441 */
441 */
442 Notebook.prototype.is_valid_cell_index = function (index) {
442 Notebook.prototype.is_valid_cell_index = function (index) {
443 if (index !== null && index >= 0 && index < this.ncells()) {
443 if (index !== null && index >= 0 && index < this.ncells()) {
444 return true;
444 return true;
445 } else {
445 } else {
446 return false;
446 return false;
447 };
447 };
448 }
448 }
449
449
450 /**
450 /**
451 * Get the index of the currently selected cell.
451 * Get the index of the currently selected cell.
452
452
453 * @method get_selected_index
453 * @method get_selected_index
454 * @return {Number} The selected cell's numeric index
454 * @return {Number} The selected cell's numeric index
455 */
455 */
456 Notebook.prototype.get_selected_index = function () {
456 Notebook.prototype.get_selected_index = function () {
457 var result = null;
457 var result = null;
458 this.get_cell_elements().filter(function (index) {
458 this.get_cell_elements().filter(function (index) {
459 if ($(this).data("cell").selected === true) {
459 if ($(this).data("cell").selected === true) {
460 result = index;
460 result = index;
461 };
461 };
462 });
462 });
463 return result;
463 return result;
464 };
464 };
465
465
466
466
467 // Cell selection.
467 // Cell selection.
468
468
469 /**
469 /**
470 * Programmatically select a cell.
470 * Programmatically select a cell.
471 *
471 *
472 * @method select
472 * @method select
473 * @param {Number} index A cell's index
473 * @param {Number} index A cell's index
474 * @return {Notebook} This notebook
474 * @return {Notebook} This notebook
475 */
475 */
476 Notebook.prototype.select = function (index) {
476 Notebook.prototype.select = function (index) {
477 if (this.is_valid_cell_index(index)) {
477 if (this.is_valid_cell_index(index)) {
478 var sindex = this.get_selected_index()
478 var sindex = this.get_selected_index()
479 if (sindex !== null && index !== sindex) {
479 if (sindex !== null && index !== sindex) {
480 this.command_mode();
480 this.command_mode();
481 this.get_cell(sindex).unselect();
481 this.get_cell(sindex).unselect();
482 };
482 };
483 var cell = this.get_cell(index);
483 var cell = this.get_cell(index);
484 cell.select();
484 cell.select();
485 if (cell.cell_type === 'heading') {
485 if (cell.cell_type === 'heading') {
486 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
486 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
487 {'cell_type':cell.cell_type,level:cell.level}
487 {'cell_type':cell.cell_type,level:cell.level}
488 );
488 );
489 } else {
489 } else {
490 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
490 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
491 {'cell_type':cell.cell_type}
491 {'cell_type':cell.cell_type}
492 );
492 );
493 };
493 };
494 };
494 };
495 return this;
495 return this;
496 };
496 };
497
497
498 /**
498 /**
499 * Programmatically select the next cell.
499 * Programmatically select the next cell.
500 *
500 *
501 * @method select_next
501 * @method select_next
502 * @return {Notebook} This notebook
502 * @return {Notebook} This notebook
503 */
503 */
504 Notebook.prototype.select_next = function () {
504 Notebook.prototype.select_next = function () {
505 var index = this.get_selected_index();
505 var index = this.get_selected_index();
506 this.select(index+1);
506 this.select(index+1);
507 return this;
507 return this;
508 };
508 };
509
509
510 /**
510 /**
511 * Programmatically select the previous cell.
511 * Programmatically select the previous cell.
512 *
512 *
513 * @method select_prev
513 * @method select_prev
514 * @return {Notebook} This notebook
514 * @return {Notebook} This notebook
515 */
515 */
516 Notebook.prototype.select_prev = function () {
516 Notebook.prototype.select_prev = function () {
517 var index = this.get_selected_index();
517 var index = this.get_selected_index();
518 this.select(index-1);
518 this.select(index-1);
519 return this;
519 return this;
520 };
520 };
521
521
522
522
523 // Edit/Command mode
523 // Edit/Command mode
524
524
525 Notebook.prototype.get_edit_index = function () {
525 Notebook.prototype.get_edit_index = function () {
526 var result = null;
526 var result = null;
527 this.get_cell_elements().filter(function (index) {
527 this.get_cell_elements().filter(function (index) {
528 if ($(this).data("cell").mode === 'edit') {
528 if ($(this).data("cell").mode === 'edit') {
529 result = index;
529 result = index;
530 };
530 };
531 });
531 });
532 return result;
532 return result;
533 };
533 };
534
534
535 Notebook.prototype.command_mode = function () {
535 Notebook.prototype.command_mode = function () {
536 if (this.mode !== 'command') {
536 if (this.mode !== 'command') {
537 var index = this.get_edit_index();
537 var index = this.get_edit_index();
538 var cell = this.get_cell(index);
538 var cell = this.get_cell(index);
539 if (cell) {
539 if (cell) {
540 cell.command_mode();
540 cell.command_mode();
541 };
541 };
542 this.mode = 'command';
542 this.mode = 'command';
543 IPython.keyboard_manager.command_mode();
543 IPython.keyboard_manager.command_mode();
544 };
544 };
545 };
545 };
546
546
547 Notebook.prototype.edit_mode = function () {
547 Notebook.prototype.edit_mode = function () {
548 if (this.mode !== 'edit') {
548 if (this.mode !== 'edit') {
549 var cell = this.get_selected_cell();
549 var cell = this.get_selected_cell();
550 if (cell === null) {return;} // No cell is selected
550 if (cell === null) {return;} // No cell is selected
551 // We need to set the mode to edit to prevent reentering this method
551 // We need to set the mode to edit to prevent reentering this method
552 // when cell.edit_mode() is called below.
552 // when cell.edit_mode() is called below.
553 this.mode = 'edit';
553 this.mode = 'edit';
554 IPython.keyboard_manager.edit_mode();
554 IPython.keyboard_manager.edit_mode();
555 cell.edit_mode();
555 cell.edit_mode();
556 };
556 };
557 };
557 };
558
558
559 Notebook.prototype.focus_cell = function () {
559 Notebook.prototype.focus_cell = function () {
560 var cell = this.get_selected_cell();
560 var cell = this.get_selected_cell();
561 if (cell === null) {return;} // No cell is selected
561 if (cell === null) {return;} // No cell is selected
562 cell.focus_cell();
562 cell.focus_cell();
563 };
563 };
564
564
565 // Cell movement
565 // Cell movement
566
566
567 /**
567 /**
568 * Move given (or selected) cell up and select it.
568 * Move given (or selected) cell up and select it.
569 *
569 *
570 * @method move_cell_up
570 * @method move_cell_up
571 * @param [index] {integer} cell index
571 * @param [index] {integer} cell index
572 * @return {Notebook} This notebook
572 * @return {Notebook} This notebook
573 **/
573 **/
574 Notebook.prototype.move_cell_up = function (index) {
574 Notebook.prototype.move_cell_up = function (index) {
575 var i = this.index_or_selected(index);
575 var i = this.index_or_selected(index);
576 if (this.is_valid_cell_index(i) && i > 0) {
576 if (this.is_valid_cell_index(i) && i > 0) {
577 var pivot = this.get_cell_element(i-1);
577 var pivot = this.get_cell_element(i-1);
578 var tomove = this.get_cell_element(i);
578 var tomove = this.get_cell_element(i);
579 if (pivot !== null && tomove !== null) {
579 if (pivot !== null && tomove !== null) {
580 tomove.detach();
580 tomove.detach();
581 pivot.before(tomove);
581 pivot.before(tomove);
582 this.select(i-1);
582 this.select(i-1);
583 var cell = this.get_selected_cell();
583 var cell = this.get_selected_cell();
584 cell.focus_cell();
584 cell.focus_cell();
585 };
585 };
586 this.set_dirty(true);
586 this.set_dirty(true);
587 };
587 };
588 return this;
588 return this;
589 };
589 };
590
590
591
591
592 /**
592 /**
593 * Move given (or selected) cell down and select it
593 * Move given (or selected) cell down and select it
594 *
594 *
595 * @method move_cell_down
595 * @method move_cell_down
596 * @param [index] {integer} cell index
596 * @param [index] {integer} cell index
597 * @return {Notebook} This notebook
597 * @return {Notebook} This notebook
598 **/
598 **/
599 Notebook.prototype.move_cell_down = function (index) {
599 Notebook.prototype.move_cell_down = function (index) {
600 var i = this.index_or_selected(index);
600 var i = this.index_or_selected(index);
601 if (this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
601 if (this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
602 var pivot = this.get_cell_element(i+1);
602 var pivot = this.get_cell_element(i+1);
603 var tomove = this.get_cell_element(i);
603 var tomove = this.get_cell_element(i);
604 if (pivot !== null && tomove !== null) {
604 if (pivot !== null && tomove !== null) {
605 tomove.detach();
605 tomove.detach();
606 pivot.after(tomove);
606 pivot.after(tomove);
607 this.select(i+1);
607 this.select(i+1);
608 var cell = this.get_selected_cell();
608 var cell = this.get_selected_cell();
609 cell.focus_cell();
609 cell.focus_cell();
610 };
610 };
611 };
611 };
612 this.set_dirty();
612 this.set_dirty();
613 return this;
613 return this;
614 };
614 };
615
615
616
616
617 // Insertion, deletion.
617 // Insertion, deletion.
618
618
619 /**
619 /**
620 * Delete a cell from the notebook.
620 * Delete a cell from the notebook.
621 *
621 *
622 * @method delete_cell
622 * @method delete_cell
623 * @param [index] A cell's numeric index
623 * @param [index] A cell's numeric index
624 * @return {Notebook} This notebook
624 * @return {Notebook} This notebook
625 */
625 */
626 Notebook.prototype.delete_cell = function (index) {
626 Notebook.prototype.delete_cell = function (index) {
627 var i = this.index_or_selected(index);
627 var i = this.index_or_selected(index);
628 var cell = this.get_selected_cell();
628 var cell = this.get_selected_cell();
629 this.undelete_backup = cell.toJSON();
629 this.undelete_backup = cell.toJSON();
630 $('#undelete_cell').removeClass('disabled');
630 $('#undelete_cell').removeClass('disabled');
631 if (this.is_valid_cell_index(i)) {
631 if (this.is_valid_cell_index(i)) {
632 var old_ncells = this.ncells();
632 var old_ncells = this.ncells();
633 var ce = this.get_cell_element(i);
633 var ce = this.get_cell_element(i);
634 ce.remove();
634 ce.remove();
635 if (i === 0) {
635 if (i === 0) {
636 // Always make sure we have at least one cell.
636 // Always make sure we have at least one cell.
637 if (old_ncells === 1) {
637 if (old_ncells === 1) {
638 this.insert_cell_below('code');
638 this.insert_cell_below('code');
639 }
639 }
640 this.select(0);
640 this.select(0);
641 this.undelete_index = 0;
641 this.undelete_index = 0;
642 this.undelete_below = false;
642 this.undelete_below = false;
643 } else if (i === old_ncells-1 && i !== 0) {
643 } else if (i === old_ncells-1 && i !== 0) {
644 this.select(i-1);
644 this.select(i-1);
645 this.undelete_index = i - 1;
645 this.undelete_index = i - 1;
646 this.undelete_below = true;
646 this.undelete_below = true;
647 } else {
647 } else {
648 this.select(i);
648 this.select(i);
649 this.undelete_index = i;
649 this.undelete_index = i;
650 this.undelete_below = false;
650 this.undelete_below = false;
651 };
651 };
652 $([IPython.events]).trigger('delete.Cell', {'cell': cell, 'index': i});
652 $([IPython.events]).trigger('delete.Cell', {'cell': cell, 'index': i});
653 this.set_dirty(true);
653 this.set_dirty(true);
654 };
654 };
655 return this;
655 return this;
656 };
656 };
657
657
658 /**
658 /**
659 * Restore the most recently deleted cell.
659 * Restore the most recently deleted cell.
660 *
660 *
661 * @method undelete
661 * @method undelete
662 */
662 */
663 Notebook.prototype.undelete_cell = function() {
663 Notebook.prototype.undelete_cell = function() {
664 if (this.undelete_backup !== null && this.undelete_index !== null) {
664 if (this.undelete_backup !== null && this.undelete_index !== null) {
665 var current_index = this.get_selected_index();
665 var current_index = this.get_selected_index();
666 if (this.undelete_index < current_index) {
666 if (this.undelete_index < current_index) {
667 current_index = current_index + 1;
667 current_index = current_index + 1;
668 }
668 }
669 if (this.undelete_index >= this.ncells()) {
669 if (this.undelete_index >= this.ncells()) {
670 this.select(this.ncells() - 1);
670 this.select(this.ncells() - 1);
671 }
671 }
672 else {
672 else {
673 this.select(this.undelete_index);
673 this.select(this.undelete_index);
674 }
674 }
675 var cell_data = this.undelete_backup;
675 var cell_data = this.undelete_backup;
676 var new_cell = null;
676 var new_cell = null;
677 if (this.undelete_below) {
677 if (this.undelete_below) {
678 new_cell = this.insert_cell_below(cell_data.cell_type);
678 new_cell = this.insert_cell_below(cell_data.cell_type);
679 } else {
679 } else {
680 new_cell = this.insert_cell_above(cell_data.cell_type);
680 new_cell = this.insert_cell_above(cell_data.cell_type);
681 }
681 }
682 new_cell.fromJSON(cell_data);
682 new_cell.fromJSON(cell_data);
683 if (this.undelete_below) {
683 if (this.undelete_below) {
684 this.select(current_index+1);
684 this.select(current_index+1);
685 } else {
685 } else {
686 this.select(current_index);
686 this.select(current_index);
687 }
687 }
688 this.undelete_backup = null;
688 this.undelete_backup = null;
689 this.undelete_index = null;
689 this.undelete_index = null;
690 }
690 }
691 $('#undelete_cell').addClass('disabled');
691 $('#undelete_cell').addClass('disabled');
692 }
692 }
693
693
694 /**
694 /**
695 * Insert a cell so that after insertion the cell is at given index.
695 * Insert a cell so that after insertion the cell is at given index.
696 *
696 *
697 * Similar to insert_above, but index parameter is mandatory
697 * Similar to insert_above, but index parameter is mandatory
698 *
698 *
699 * Index will be brought back into the accissible range [0,n]
699 * Index will be brought back into the accissible range [0,n]
700 *
700 *
701 * @method insert_cell_at_index
701 * @method insert_cell_at_index
702 * @param type {string} in ['code','markdown','heading']
702 * @param type {string} in ['code','markdown','heading']
703 * @param [index] {int} a valid index where to inser cell
703 * @param [index] {int} a valid index where to inser cell
704 *
704 *
705 * @return cell {cell|null} created cell or null
705 * @return cell {cell|null} created cell or null
706 **/
706 **/
707 Notebook.prototype.insert_cell_at_index = function(type, index){
707 Notebook.prototype.insert_cell_at_index = function(type, index){
708
708
709 var ncells = this.ncells();
709 var ncells = this.ncells();
710 var index = Math.min(index,ncells);
710 var index = Math.min(index,ncells);
711 index = Math.max(index,0);
711 index = Math.max(index,0);
712 var cell = null;
712 var cell = null;
713
713
714 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
714 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
715 if (type === 'code') {
715 if (type === 'code') {
716 cell = new IPython.CodeCell(this.kernel);
716 cell = new IPython.CodeCell(this.kernel);
717 cell.set_input_prompt();
717 cell.set_input_prompt();
718 } else if (type === 'markdown') {
718 } else if (type === 'markdown') {
719 cell = new IPython.MarkdownCell();
719 cell = new IPython.MarkdownCell();
720 } else if (type === 'raw') {
720 } else if (type === 'raw') {
721 cell = new IPython.RawCell();
721 cell = new IPython.RawCell();
722 } else if (type === 'heading') {
722 } else if (type === 'heading') {
723 cell = new IPython.HeadingCell();
723 cell = new IPython.HeadingCell();
724 }
724 }
725
725
726 if(this._insert_element_at_index(cell.element,index)) {
726 if(this._insert_element_at_index(cell.element,index)) {
727 cell.render();
727 cell.render();
728 $([IPython.events]).trigger('create.Cell', {'cell': cell, 'index': index});
728 $([IPython.events]).trigger('create.Cell', {'cell': cell, 'index': index});
729 cell.refresh();
729 cell.refresh();
730 // We used to select the cell after we refresh it, but there
730 // We used to select the cell after we refresh it, but there
731 // are now cases were this method is called where select is
731 // are now cases were this method is called where select is
732 // not appropriate. The selection logic should be handled by the
732 // not appropriate. The selection logic should be handled by the
733 // caller of the the top level insert_cell methods.
733 // caller of the the top level insert_cell methods.
734 this.set_dirty(true);
734 this.set_dirty(true);
735 }
735 }
736 }
736 }
737 return cell;
737 return cell;
738
738
739 };
739 };
740
740
741 /**
741 /**
742 * Insert an element at given cell index.
742 * Insert an element at given cell index.
743 *
743 *
744 * @method _insert_element_at_index
744 * @method _insert_element_at_index
745 * @param element {dom element} a cell element
745 * @param element {dom element} a cell element
746 * @param [index] {int} a valid index where to inser cell
746 * @param [index] {int} a valid index where to inser cell
747 * @private
747 * @private
748 *
748 *
749 * return true if everything whent fine.
749 * return true if everything whent fine.
750 **/
750 **/
751 Notebook.prototype._insert_element_at_index = function(element, index){
751 Notebook.prototype._insert_element_at_index = function(element, index){
752 if (element === undefined){
752 if (element === undefined){
753 return false;
753 return false;
754 }
754 }
755
755
756 var ncells = this.ncells();
756 var ncells = this.ncells();
757
757
758 if (ncells === 0) {
758 if (ncells === 0) {
759 // special case append if empty
759 // special case append if empty
760 this.element.find('div.end_space').before(element);
760 this.element.find('div.end_space').before(element);
761 } else if ( ncells === index ) {
761 } else if ( ncells === index ) {
762 // special case append it the end, but not empty
762 // special case append it the end, but not empty
763 this.get_cell_element(index-1).after(element);
763 this.get_cell_element(index-1).after(element);
764 } else if (this.is_valid_cell_index(index)) {
764 } else if (this.is_valid_cell_index(index)) {
765 // otherwise always somewhere to append to
765 // otherwise always somewhere to append to
766 this.get_cell_element(index).before(element);
766 this.get_cell_element(index).before(element);
767 } else {
767 } else {
768 return false;
768 return false;
769 }
769 }
770
770
771 if (this.undelete_index !== null && index <= this.undelete_index) {
771 if (this.undelete_index !== null && index <= this.undelete_index) {
772 this.undelete_index = this.undelete_index + 1;
772 this.undelete_index = this.undelete_index + 1;
773 this.set_dirty(true);
773 this.set_dirty(true);
774 }
774 }
775 return true;
775 return true;
776 };
776 };
777
777
778 /**
778 /**
779 * Insert a cell of given type above given index, or at top
779 * Insert a cell of given type above given index, or at top
780 * of notebook if index smaller than 0.
780 * of notebook if index smaller than 0.
781 *
781 *
782 * default index value is the one of currently selected cell
782 * default index value is the one of currently selected cell
783 *
783 *
784 * @method insert_cell_above
784 * @method insert_cell_above
785 * @param type {string} cell type
785 * @param type {string} cell type
786 * @param [index] {integer}
786 * @param [index] {integer}
787 *
787 *
788 * @return handle to created cell or null
788 * @return handle to created cell or null
789 **/
789 **/
790 Notebook.prototype.insert_cell_above = function (type, index) {
790 Notebook.prototype.insert_cell_above = function (type, index) {
791 index = this.index_or_selected(index);
791 index = this.index_or_selected(index);
792 return this.insert_cell_at_index(type, index);
792 return this.insert_cell_at_index(type, index);
793 };
793 };
794
794
795 /**
795 /**
796 * Insert a cell of given type below given index, or at bottom
796 * Insert a cell of given type below given index, or at bottom
797 * of notebook if index greater thatn number of cell
797 * of notebook if index greater thatn number of cell
798 *
798 *
799 * default index value is the one of currently selected cell
799 * default index value is the one of currently selected cell
800 *
800 *
801 * @method insert_cell_below
801 * @method insert_cell_below
802 * @param type {string} cell type
802 * @param type {string} cell type
803 * @param [index] {integer}
803 * @param [index] {integer}
804 *
804 *
805 * @return handle to created cell or null
805 * @return handle to created cell or null
806 *
806 *
807 **/
807 **/
808 Notebook.prototype.insert_cell_below = function (type, index) {
808 Notebook.prototype.insert_cell_below = function (type, index) {
809 index = this.index_or_selected(index);
809 index = this.index_or_selected(index);
810 return this.insert_cell_at_index(type, index+1);
810 return this.insert_cell_at_index(type, index+1);
811 };
811 };
812
812
813
813
814 /**
814 /**
815 * Insert cell at end of notebook
815 * Insert cell at end of notebook
816 *
816 *
817 * @method insert_cell_at_bottom
817 * @method insert_cell_at_bottom
818 * @param {String} type cell type
818 * @param {String} type cell type
819 *
819 *
820 * @return the added cell; or null
820 * @return the added cell; or null
821 **/
821 **/
822 Notebook.prototype.insert_cell_at_bottom = function (type){
822 Notebook.prototype.insert_cell_at_bottom = function (type){
823 var len = this.ncells();
823 var len = this.ncells();
824 return this.insert_cell_below(type,len-1);
824 return this.insert_cell_below(type,len-1);
825 };
825 };
826
826
827 /**
827 /**
828 * Turn a cell into a code cell.
828 * Turn a cell into a code cell.
829 *
829 *
830 * @method to_code
830 * @method to_code
831 * @param {Number} [index] A cell's index
831 * @param {Number} [index] A cell's index
832 */
832 */
833 Notebook.prototype.to_code = function (index) {
833 Notebook.prototype.to_code = function (index) {
834 var i = this.index_or_selected(index);
834 var i = this.index_or_selected(index);
835 if (this.is_valid_cell_index(i)) {
835 if (this.is_valid_cell_index(i)) {
836 var source_element = this.get_cell_element(i);
836 var source_element = this.get_cell_element(i);
837 var source_cell = source_element.data("cell");
837 var source_cell = source_element.data("cell");
838 if (!(source_cell instanceof IPython.CodeCell)) {
838 if (!(source_cell instanceof IPython.CodeCell)) {
839 var target_cell = this.insert_cell_below('code',i);
839 var target_cell = this.insert_cell_below('code',i);
840 var text = source_cell.get_text();
840 var text = source_cell.get_text();
841 if (text === source_cell.placeholder) {
841 if (text === source_cell.placeholder) {
842 text = '';
842 text = '';
843 }
843 }
844 target_cell.set_text(text);
844 target_cell.set_text(text);
845 // make this value the starting point, so that we can only undo
845 // make this value the starting point, so that we can only undo
846 // to this state, instead of a blank cell
846 // to this state, instead of a blank cell
847 target_cell.code_mirror.clearHistory();
847 target_cell.code_mirror.clearHistory();
848 source_element.remove();
848 source_element.remove();
849 this.select(i);
849 this.select(i);
850 this.edit_mode();
850 this.edit_mode();
851 this.set_dirty(true);
851 this.set_dirty(true);
852 };
852 };
853 };
853 };
854 };
854 };
855
855
856 /**
856 /**
857 * Turn a cell into a Markdown cell.
857 * Turn a cell into a Markdown cell.
858 *
858 *
859 * @method to_markdown
859 * @method to_markdown
860 * @param {Number} [index] A cell's index
860 * @param {Number} [index] A cell's index
861 */
861 */
862 Notebook.prototype.to_markdown = function (index) {
862 Notebook.prototype.to_markdown = function (index) {
863 var i = this.index_or_selected(index);
863 var i = this.index_or_selected(index);
864 if (this.is_valid_cell_index(i)) {
864 if (this.is_valid_cell_index(i)) {
865 var source_element = this.get_cell_element(i);
865 var source_element = this.get_cell_element(i);
866 var source_cell = source_element.data("cell");
866 var source_cell = source_element.data("cell");
867 if (!(source_cell instanceof IPython.MarkdownCell)) {
867 if (!(source_cell instanceof IPython.MarkdownCell)) {
868 var target_cell = this.insert_cell_below('markdown',i);
868 var target_cell = this.insert_cell_below('markdown',i);
869 var text = source_cell.get_text();
869 var text = source_cell.get_text();
870 if (text === source_cell.placeholder) {
870 if (text === source_cell.placeholder) {
871 text = '';
871 text = '';
872 };
872 };
873 // We must show the editor before setting its contents
873 // We must show the editor before setting its contents
874 target_cell.unrender();
874 target_cell.unrender();
875 target_cell.set_text(text);
875 target_cell.set_text(text);
876 // make this value the starting point, so that we can only undo
876 // make this value the starting point, so that we can only undo
877 // to this state, instead of a blank cell
877 // to this state, instead of a blank cell
878 target_cell.code_mirror.clearHistory();
878 target_cell.code_mirror.clearHistory();
879 source_element.remove();
879 source_element.remove();
880 this.select(i);
880 this.select(i);
881 this.edit_mode();
881 this.edit_mode();
882 this.set_dirty(true);
882 this.set_dirty(true);
883 };
883 };
884 };
884 };
885 };
885 };
886
886
887 /**
887 /**
888 * Turn a cell into a raw text cell.
888 * Turn a cell into a raw text cell.
889 *
889 *
890 * @method to_raw
890 * @method to_raw
891 * @param {Number} [index] A cell's index
891 * @param {Number} [index] A cell's index
892 */
892 */
893 Notebook.prototype.to_raw = function (index) {
893 Notebook.prototype.to_raw = function (index) {
894 var i = this.index_or_selected(index);
894 var i = this.index_or_selected(index);
895 if (this.is_valid_cell_index(i)) {
895 if (this.is_valid_cell_index(i)) {
896 var source_element = this.get_cell_element(i);
896 var source_element = this.get_cell_element(i);
897 var source_cell = source_element.data("cell");
897 var source_cell = source_element.data("cell");
898 var target_cell = null;
898 var target_cell = null;
899 if (!(source_cell instanceof IPython.RawCell)) {
899 if (!(source_cell instanceof IPython.RawCell)) {
900 target_cell = this.insert_cell_below('raw',i);
900 target_cell = this.insert_cell_below('raw',i);
901 var text = source_cell.get_text();
901 var text = source_cell.get_text();
902 if (text === source_cell.placeholder) {
902 if (text === source_cell.placeholder) {
903 text = '';
903 text = '';
904 };
904 };
905 // We must show the editor before setting its contents
905 // We must show the editor before setting its contents
906 target_cell.unrender();
906 target_cell.unrender();
907 target_cell.set_text(text);
907 target_cell.set_text(text);
908 // make this value the starting point, so that we can only undo
908 // make this value the starting point, so that we can only undo
909 // to this state, instead of a blank cell
909 // to this state, instead of a blank cell
910 target_cell.code_mirror.clearHistory();
910 target_cell.code_mirror.clearHistory();
911 source_element.remove();
911 source_element.remove();
912 this.select(i);
912 this.select(i);
913 this.edit_mode();
913 this.edit_mode();
914 this.set_dirty(true);
914 this.set_dirty(true);
915 };
915 };
916 };
916 };
917 };
917 };
918
918
919 /**
919 /**
920 * Turn a cell into a heading cell.
920 * Turn a cell into a heading cell.
921 *
921 *
922 * @method to_heading
922 * @method to_heading
923 * @param {Number} [index] A cell's index
923 * @param {Number} [index] A cell's index
924 * @param {Number} [level] A heading level (e.g., 1 becomes &lt;h1&gt;)
924 * @param {Number} [level] A heading level (e.g., 1 becomes &lt;h1&gt;)
925 */
925 */
926 Notebook.prototype.to_heading = function (index, level) {
926 Notebook.prototype.to_heading = function (index, level) {
927 level = level || 1;
927 level = level || 1;
928 var i = this.index_or_selected(index);
928 var i = this.index_or_selected(index);
929 if (this.is_valid_cell_index(i)) {
929 if (this.is_valid_cell_index(i)) {
930 var source_element = this.get_cell_element(i);
930 var source_element = this.get_cell_element(i);
931 var source_cell = source_element.data("cell");
931 var source_cell = source_element.data("cell");
932 var target_cell = null;
932 var target_cell = null;
933 if (source_cell instanceof IPython.HeadingCell) {
933 if (source_cell instanceof IPython.HeadingCell) {
934 source_cell.set_level(level);
934 source_cell.set_level(level);
935 } else {
935 } else {
936 target_cell = this.insert_cell_below('heading',i);
936 target_cell = this.insert_cell_below('heading',i);
937 var text = source_cell.get_text();
937 var text = source_cell.get_text();
938 if (text === source_cell.placeholder) {
938 if (text === source_cell.placeholder) {
939 text = '';
939 text = '';
940 };
940 };
941 // We must show the editor before setting its contents
941 // We must show the editor before setting its contents
942 target_cell.set_level(level);
942 target_cell.set_level(level);
943 target_cell.unrender();
943 target_cell.unrender();
944 target_cell.set_text(text);
944 target_cell.set_text(text);
945 // make this value the starting point, so that we can only undo
945 // make this value the starting point, so that we can only undo
946 // to this state, instead of a blank cell
946 // to this state, instead of a blank cell
947 target_cell.code_mirror.clearHistory();
947 target_cell.code_mirror.clearHistory();
948 source_element.remove();
948 source_element.remove();
949 this.select(i);
949 this.select(i);
950 };
950 };
951 this.edit_mode();
951 this.edit_mode();
952 this.set_dirty(true);
952 this.set_dirty(true);
953 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
953 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
954 {'cell_type':'heading',level:level}
954 {'cell_type':'heading',level:level}
955 );
955 );
956 };
956 };
957 };
957 };
958
958
959
959
960 // Cut/Copy/Paste
960 // Cut/Copy/Paste
961
961
962 /**
962 /**
963 * Enable UI elements for pasting cells.
963 * Enable UI elements for pasting cells.
964 *
964 *
965 * @method enable_paste
965 * @method enable_paste
966 */
966 */
967 Notebook.prototype.enable_paste = function () {
967 Notebook.prototype.enable_paste = function () {
968 var that = this;
968 var that = this;
969 if (!this.paste_enabled) {
969 if (!this.paste_enabled) {
970 $('#paste_cell_replace').removeClass('disabled')
970 $('#paste_cell_replace').removeClass('disabled')
971 .on('click', function () {that.paste_cell_replace();});
971 .on('click', function () {that.paste_cell_replace();});
972 $('#paste_cell_above').removeClass('disabled')
972 $('#paste_cell_above').removeClass('disabled')
973 .on('click', function () {that.paste_cell_above();});
973 .on('click', function () {that.paste_cell_above();});
974 $('#paste_cell_below').removeClass('disabled')
974 $('#paste_cell_below').removeClass('disabled')
975 .on('click', function () {that.paste_cell_below();});
975 .on('click', function () {that.paste_cell_below();});
976 this.paste_enabled = true;
976 this.paste_enabled = true;
977 };
977 };
978 };
978 };
979
979
980 /**
980 /**
981 * Disable UI elements for pasting cells.
981 * Disable UI elements for pasting cells.
982 *
982 *
983 * @method disable_paste
983 * @method disable_paste
984 */
984 */
985 Notebook.prototype.disable_paste = function () {
985 Notebook.prototype.disable_paste = function () {
986 if (this.paste_enabled) {
986 if (this.paste_enabled) {
987 $('#paste_cell_replace').addClass('disabled').off('click');
987 $('#paste_cell_replace').addClass('disabled').off('click');
988 $('#paste_cell_above').addClass('disabled').off('click');
988 $('#paste_cell_above').addClass('disabled').off('click');
989 $('#paste_cell_below').addClass('disabled').off('click');
989 $('#paste_cell_below').addClass('disabled').off('click');
990 this.paste_enabled = false;
990 this.paste_enabled = false;
991 };
991 };
992 };
992 };
993
993
994 /**
994 /**
995 * Cut a cell.
995 * Cut a cell.
996 *
996 *
997 * @method cut_cell
997 * @method cut_cell
998 */
998 */
999 Notebook.prototype.cut_cell = function () {
999 Notebook.prototype.cut_cell = function () {
1000 this.copy_cell();
1000 this.copy_cell();
1001 this.delete_cell();
1001 this.delete_cell();
1002 }
1002 }
1003
1003
1004 /**
1004 /**
1005 * Copy a cell.
1005 * Copy a cell.
1006 *
1006 *
1007 * @method copy_cell
1007 * @method copy_cell
1008 */
1008 */
1009 Notebook.prototype.copy_cell = function () {
1009 Notebook.prototype.copy_cell = function () {
1010 var cell = this.get_selected_cell();
1010 var cell = this.get_selected_cell();
1011 this.clipboard = cell.toJSON();
1011 this.clipboard = cell.toJSON();
1012 this.enable_paste();
1012 this.enable_paste();
1013 };
1013 };
1014
1014
1015 /**
1015 /**
1016 * Replace the selected cell with a cell in the clipboard.
1016 * Replace the selected cell with a cell in the clipboard.
1017 *
1017 *
1018 * @method paste_cell_replace
1018 * @method paste_cell_replace
1019 */
1019 */
1020 Notebook.prototype.paste_cell_replace = function () {
1020 Notebook.prototype.paste_cell_replace = function () {
1021 if (this.clipboard !== null && this.paste_enabled) {
1021 if (this.clipboard !== null && this.paste_enabled) {
1022 var cell_data = this.clipboard;
1022 var cell_data = this.clipboard;
1023 var new_cell = this.insert_cell_above(cell_data.cell_type);
1023 var new_cell = this.insert_cell_above(cell_data.cell_type);
1024 new_cell.fromJSON(cell_data);
1024 new_cell.fromJSON(cell_data);
1025 var old_cell = this.get_next_cell(new_cell);
1025 var old_cell = this.get_next_cell(new_cell);
1026 this.delete_cell(this.find_cell_index(old_cell));
1026 this.delete_cell(this.find_cell_index(old_cell));
1027 this.select(this.find_cell_index(new_cell));
1027 this.select(this.find_cell_index(new_cell));
1028 };
1028 };
1029 };
1029 };
1030
1030
1031 /**
1031 /**
1032 * Paste a cell from the clipboard above the selected cell.
1032 * Paste a cell from the clipboard above the selected cell.
1033 *
1033 *
1034 * @method paste_cell_above
1034 * @method paste_cell_above
1035 */
1035 */
1036 Notebook.prototype.paste_cell_above = function () {
1036 Notebook.prototype.paste_cell_above = function () {
1037 if (this.clipboard !== null && this.paste_enabled) {
1037 if (this.clipboard !== null && this.paste_enabled) {
1038 var cell_data = this.clipboard;
1038 var cell_data = this.clipboard;
1039 var new_cell = this.insert_cell_above(cell_data.cell_type);
1039 var new_cell = this.insert_cell_above(cell_data.cell_type);
1040 new_cell.fromJSON(cell_data);
1040 new_cell.fromJSON(cell_data);
1041 };
1041 };
1042 };
1042 };
1043
1043
1044 /**
1044 /**
1045 * Paste a cell from the clipboard below the selected cell.
1045 * Paste a cell from the clipboard below the selected cell.
1046 *
1046 *
1047 * @method paste_cell_below
1047 * @method paste_cell_below
1048 */
1048 */
1049 Notebook.prototype.paste_cell_below = function () {
1049 Notebook.prototype.paste_cell_below = function () {
1050 if (this.clipboard !== null && this.paste_enabled) {
1050 if (this.clipboard !== null && this.paste_enabled) {
1051 var cell_data = this.clipboard;
1051 var cell_data = this.clipboard;
1052 var new_cell = this.insert_cell_below(cell_data.cell_type);
1052 var new_cell = this.insert_cell_below(cell_data.cell_type);
1053 new_cell.fromJSON(cell_data);
1053 new_cell.fromJSON(cell_data);
1054 };
1054 };
1055 };
1055 };
1056
1056
1057 // Split/merge
1057 // Split/merge
1058
1058
1059 /**
1059 /**
1060 * Split the selected cell into two, at the cursor.
1060 * Split the selected cell into two, at the cursor.
1061 *
1061 *
1062 * @method split_cell
1062 * @method split_cell
1063 */
1063 */
1064 Notebook.prototype.split_cell = function () {
1064 Notebook.prototype.split_cell = function () {
1065 var mdc = IPython.MarkdownCell;
1065 var mdc = IPython.MarkdownCell;
1066 var rc = IPython.RawCell;
1066 var rc = IPython.RawCell;
1067 var cell = this.get_selected_cell();
1067 var cell = this.get_selected_cell();
1068 if (cell.is_splittable()) {
1068 if (cell.is_splittable()) {
1069 var texta = cell.get_pre_cursor();
1069 var texta = cell.get_pre_cursor();
1070 var textb = cell.get_post_cursor();
1070 var textb = cell.get_post_cursor();
1071 if (cell instanceof IPython.CodeCell) {
1071 if (cell instanceof IPython.CodeCell) {
1072 // In this case the operations keep the notebook in its existing mode
1072 // In this case the operations keep the notebook in its existing mode
1073 // so we don't need to do any post-op mode changes.
1073 // so we don't need to do any post-op mode changes.
1074 cell.set_text(textb);
1074 cell.set_text(textb);
1075 var new_cell = this.insert_cell_above('code');
1075 var new_cell = this.insert_cell_above('code');
1076 new_cell.set_text(texta);
1076 new_cell.set_text(texta);
1077 } else if ((cell instanceof mdc && !cell.rendered) || (cell instanceof rc)) {
1077 } else if ((cell instanceof mdc && !cell.rendered) || (cell instanceof rc)) {
1078 // We know cell is !rendered so we can use set_text.
1078 // We know cell is !rendered so we can use set_text.
1079 cell.set_text(textb);
1079 cell.set_text(textb);
1080 var new_cell = this.insert_cell_above(cell.cell_type);
1080 var new_cell = this.insert_cell_above(cell.cell_type);
1081 // Unrender the new cell so we can call set_text.
1081 // Unrender the new cell so we can call set_text.
1082 new_cell.unrender();
1082 new_cell.unrender();
1083 new_cell.set_text(texta);
1083 new_cell.set_text(texta);
1084 }
1084 }
1085 };
1085 };
1086 };
1086 };
1087
1087
1088 /**
1088 /**
1089 * Combine the selected cell into the cell above it.
1089 * Combine the selected cell into the cell above it.
1090 *
1090 *
1091 * @method merge_cell_above
1091 * @method merge_cell_above
1092 */
1092 */
1093 Notebook.prototype.merge_cell_above = function () {
1093 Notebook.prototype.merge_cell_above = function () {
1094 var mdc = IPython.MarkdownCell;
1094 var mdc = IPython.MarkdownCell;
1095 var rc = IPython.RawCell;
1095 var rc = IPython.RawCell;
1096 var index = this.get_selected_index();
1096 var index = this.get_selected_index();
1097 var cell = this.get_cell(index);
1097 var cell = this.get_cell(index);
1098 var render = cell.rendered;
1098 var render = cell.rendered;
1099 if (!cell.is_mergeable()) {
1099 if (!cell.is_mergeable()) {
1100 return;
1100 return;
1101 }
1101 }
1102 if (index > 0) {
1102 if (index > 0) {
1103 var upper_cell = this.get_cell(index-1);
1103 var upper_cell = this.get_cell(index-1);
1104 if (!upper_cell.is_mergeable()) {
1104 if (!upper_cell.is_mergeable()) {
1105 return;
1105 return;
1106 }
1106 }
1107 var upper_text = upper_cell.get_text();
1107 var upper_text = upper_cell.get_text();
1108 var text = cell.get_text();
1108 var text = cell.get_text();
1109 if (cell instanceof IPython.CodeCell) {
1109 if (cell instanceof IPython.CodeCell) {
1110 cell.set_text(upper_text+'\n'+text);
1110 cell.set_text(upper_text+'\n'+text);
1111 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1111 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1112 cell.unrender(); // Must unrender before we set_text.
1112 cell.unrender(); // Must unrender before we set_text.
1113 cell.set_text(upper_text+'\n\n'+text);
1113 cell.set_text(upper_text+'\n\n'+text);
1114 if (render) {
1114 if (render) {
1115 // The rendered state of the final cell should match
1115 // The rendered state of the final cell should match
1116 // that of the original selected cell;
1116 // that of the original selected cell;
1117 cell.render();
1117 cell.render();
1118 }
1118 }
1119 };
1119 };
1120 this.delete_cell(index-1);
1120 this.delete_cell(index-1);
1121 this.select(this.find_cell_index(cell));
1121 this.select(this.find_cell_index(cell));
1122 };
1122 };
1123 };
1123 };
1124
1124
1125 /**
1125 /**
1126 * Combine the selected cell into the cell below it.
1126 * Combine the selected cell into the cell below it.
1127 *
1127 *
1128 * @method merge_cell_below
1128 * @method merge_cell_below
1129 */
1129 */
1130 Notebook.prototype.merge_cell_below = function () {
1130 Notebook.prototype.merge_cell_below = function () {
1131 var mdc = IPython.MarkdownCell;
1131 var mdc = IPython.MarkdownCell;
1132 var rc = IPython.RawCell;
1132 var rc = IPython.RawCell;
1133 var index = this.get_selected_index();
1133 var index = this.get_selected_index();
1134 var cell = this.get_cell(index);
1134 var cell = this.get_cell(index);
1135 var render = cell.rendered;
1135 var render = cell.rendered;
1136 if (!cell.is_mergeable()) {
1136 if (!cell.is_mergeable()) {
1137 return;
1137 return;
1138 }
1138 }
1139 if (index < this.ncells()-1) {
1139 if (index < this.ncells()-1) {
1140 var lower_cell = this.get_cell(index+1);
1140 var lower_cell = this.get_cell(index+1);
1141 if (!lower_cell.is_mergeable()) {
1141 if (!lower_cell.is_mergeable()) {
1142 return;
1142 return;
1143 }
1143 }
1144 var lower_text = lower_cell.get_text();
1144 var lower_text = lower_cell.get_text();
1145 var text = cell.get_text();
1145 var text = cell.get_text();
1146 if (cell instanceof IPython.CodeCell) {
1146 if (cell instanceof IPython.CodeCell) {
1147 cell.set_text(text+'\n'+lower_text);
1147 cell.set_text(text+'\n'+lower_text);
1148 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1148 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1149 cell.unrender(); // Must unrender before we set_text.
1149 cell.unrender(); // Must unrender before we set_text.
1150 cell.set_text(text+'\n\n'+lower_text);
1150 cell.set_text(text+'\n\n'+lower_text);
1151 if (render) {
1151 if (render) {
1152 // The rendered state of the final cell should match
1152 // The rendered state of the final cell should match
1153 // that of the original selected cell;
1153 // that of the original selected cell;
1154 cell.render();
1154 cell.render();
1155 }
1155 }
1156 };
1156 };
1157 this.delete_cell(index+1);
1157 this.delete_cell(index+1);
1158 this.select(this.find_cell_index(cell));
1158 this.select(this.find_cell_index(cell));
1159 };
1159 };
1160 };
1160 };
1161
1161
1162
1162
1163 // Cell collapsing and output clearing
1163 // Cell collapsing and output clearing
1164
1164
1165 /**
1165 /**
1166 * Hide a cell's output.
1166 * Hide a cell's output.
1167 *
1167 *
1168 * @method collapse_output
1168 * @method collapse_output
1169 * @param {Number} index A cell's numeric index
1169 * @param {Number} index A cell's numeric index
1170 */
1170 */
1171 Notebook.prototype.collapse_output = function (index) {
1171 Notebook.prototype.collapse_output = function (index) {
1172 var i = this.index_or_selected(index);
1172 var i = this.index_or_selected(index);
1173 var cell = this.get_cell(i);
1173 var cell = this.get_cell(i);
1174 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1174 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1175 cell.collapse_output();
1175 cell.collapse_output();
1176 this.set_dirty(true);
1176 this.set_dirty(true);
1177 }
1177 }
1178 };
1178 };
1179
1179
1180 /**
1180 /**
1181 * Hide each code cell's output area.
1181 * Hide each code cell's output area.
1182 *
1182 *
1183 * @method collapse_all_output
1183 * @method collapse_all_output
1184 */
1184 */
1185 Notebook.prototype.collapse_all_output = function () {
1185 Notebook.prototype.collapse_all_output = function () {
1186 $.map(this.get_cells(), function (cell, i) {
1186 $.map(this.get_cells(), function (cell, i) {
1187 if (cell instanceof IPython.CodeCell) {
1187 if (cell instanceof IPython.CodeCell) {
1188 cell.collapse_output();
1188 cell.collapse_output();
1189 }
1189 }
1190 });
1190 });
1191 // this should not be set if the `collapse` key is removed from nbformat
1191 // this should not be set if the `collapse` key is removed from nbformat
1192 this.set_dirty(true);
1192 this.set_dirty(true);
1193 };
1193 };
1194
1194
1195 /**
1195 /**
1196 * Show a cell's output.
1196 * Show a cell's output.
1197 *
1197 *
1198 * @method expand_output
1198 * @method expand_output
1199 * @param {Number} index A cell's numeric index
1199 * @param {Number} index A cell's numeric index
1200 */
1200 */
1201 Notebook.prototype.expand_output = function (index) {
1201 Notebook.prototype.expand_output = function (index) {
1202 var i = this.index_or_selected(index);
1202 var i = this.index_or_selected(index);
1203 var cell = this.get_cell(i);
1203 var cell = this.get_cell(i);
1204 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1204 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1205 cell.expand_output();
1205 cell.expand_output();
1206 this.set_dirty(true);
1206 this.set_dirty(true);
1207 }
1207 }
1208 };
1208 };
1209
1209
1210 /**
1210 /**
1211 * Expand each code cell's output area, and remove scrollbars.
1211 * Expand each code cell's output area, and remove scrollbars.
1212 *
1212 *
1213 * @method expand_all_output
1213 * @method expand_all_output
1214 */
1214 */
1215 Notebook.prototype.expand_all_output = function () {
1215 Notebook.prototype.expand_all_output = function () {
1216 $.map(this.get_cells(), function (cell, i) {
1216 $.map(this.get_cells(), function (cell, i) {
1217 if (cell instanceof IPython.CodeCell) {
1217 if (cell instanceof IPython.CodeCell) {
1218 cell.expand_output();
1218 cell.expand_output();
1219 }
1219 }
1220 });
1220 });
1221 // this should not be set if the `collapse` key is removed from nbformat
1221 // this should not be set if the `collapse` key is removed from nbformat
1222 this.set_dirty(true);
1222 this.set_dirty(true);
1223 };
1223 };
1224
1224
1225 /**
1225 /**
1226 * Clear the selected CodeCell's output area.
1226 * Clear the selected CodeCell's output area.
1227 *
1227 *
1228 * @method clear_output
1228 * @method clear_output
1229 * @param {Number} index A cell's numeric index
1229 * @param {Number} index A cell's numeric index
1230 */
1230 */
1231 Notebook.prototype.clear_output = function (index) {
1231 Notebook.prototype.clear_output = function (index) {
1232 var i = this.index_or_selected(index);
1232 var i = this.index_or_selected(index);
1233 var cell = this.get_cell(i);
1233 var cell = this.get_cell(i);
1234 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1234 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1235 cell.clear_output();
1235 cell.clear_output();
1236 this.set_dirty(true);
1236 this.set_dirty(true);
1237 }
1237 }
1238 };
1238 };
1239
1239
1240 /**
1240 /**
1241 * Clear each code cell's output area.
1241 * Clear each code cell's output area.
1242 *
1242 *
1243 * @method clear_all_output
1243 * @method clear_all_output
1244 */
1244 */
1245 Notebook.prototype.clear_all_output = function () {
1245 Notebook.prototype.clear_all_output = function () {
1246 $.map(this.get_cells(), function (cell, i) {
1246 $.map(this.get_cells(), function (cell, i) {
1247 if (cell instanceof IPython.CodeCell) {
1247 if (cell instanceof IPython.CodeCell) {
1248 cell.clear_output();
1248 cell.clear_output();
1249 }
1249 }
1250 });
1250 });
1251 this.set_dirty(true);
1251 this.set_dirty(true);
1252 };
1252 };
1253
1253
1254 /**
1254 /**
1255 * Scroll the selected CodeCell's output area.
1255 * Scroll the selected CodeCell's output area.
1256 *
1256 *
1257 * @method scroll_output
1257 * @method scroll_output
1258 * @param {Number} index A cell's numeric index
1258 * @param {Number} index A cell's numeric index
1259 */
1259 */
1260 Notebook.prototype.scroll_output = function (index) {
1260 Notebook.prototype.scroll_output = function (index) {
1261 var i = this.index_or_selected(index);
1261 var i = this.index_or_selected(index);
1262 var cell = this.get_cell(i);
1262 var cell = this.get_cell(i);
1263 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1263 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1264 cell.scroll_output();
1264 cell.scroll_output();
1265 this.set_dirty(true);
1265 this.set_dirty(true);
1266 }
1266 }
1267 };
1267 };
1268
1268
1269 /**
1269 /**
1270 * Expand each code cell's output area, and add a scrollbar for long output.
1270 * Expand each code cell's output area, and add a scrollbar for long output.
1271 *
1271 *
1272 * @method scroll_all_output
1272 * @method scroll_all_output
1273 */
1273 */
1274 Notebook.prototype.scroll_all_output = function () {
1274 Notebook.prototype.scroll_all_output = function () {
1275 $.map(this.get_cells(), function (cell, i) {
1275 $.map(this.get_cells(), function (cell, i) {
1276 if (cell instanceof IPython.CodeCell) {
1276 if (cell instanceof IPython.CodeCell) {
1277 cell.scroll_output();
1277 cell.scroll_output();
1278 }
1278 }
1279 });
1279 });
1280 // this should not be set if the `collapse` key is removed from nbformat
1280 // this should not be set if the `collapse` key is removed from nbformat
1281 this.set_dirty(true);
1281 this.set_dirty(true);
1282 };
1282 };
1283
1283
1284 /** Toggle whether a cell's output is collapsed or expanded.
1284 /** Toggle whether a cell's output is collapsed or expanded.
1285 *
1285 *
1286 * @method toggle_output
1286 * @method toggle_output
1287 * @param {Number} index A cell's numeric index
1287 * @param {Number} index A cell's numeric index
1288 */
1288 */
1289 Notebook.prototype.toggle_output = function (index) {
1289 Notebook.prototype.toggle_output = function (index) {
1290 var i = this.index_or_selected(index);
1290 var i = this.index_or_selected(index);
1291 var cell = this.get_cell(i);
1291 var cell = this.get_cell(i);
1292 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1292 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1293 cell.toggle_output();
1293 cell.toggle_output();
1294 this.set_dirty(true);
1294 this.set_dirty(true);
1295 }
1295 }
1296 };
1296 };
1297
1297
1298 /**
1298 /**
1299 * Hide/show the output of all cells.
1300 *
1301 * @method toggle_all_output
1302 */
1303 Notebook.prototype.toggle_all_output = function () {
1304 $.map(this.get_cells(), function (cell, i) {
1305 if (cell instanceof IPython.CodeCell) {
1306 cell.toggle_output();
1307 }
1308 });
1309 // this should not be set if the `collapse` key is removed from nbformat
1310 this.set_dirty(true);
1311 };
1312
1313 /**
1299 * Toggle a scrollbar for long cell outputs.
1314 * Toggle a scrollbar for long cell outputs.
1300 *
1315 *
1301 * @method toggle_output_scroll
1316 * @method toggle_output_scroll
1302 * @param {Number} index A cell's numeric index
1317 * @param {Number} index A cell's numeric index
1303 */
1318 */
1304 Notebook.prototype.toggle_output_scroll = function (index) {
1319 Notebook.prototype.toggle_output_scroll = function (index) {
1305 var i = this.index_or_selected(index);
1320 var i = this.index_or_selected(index);
1306 var cell = this.get_cell(i);
1321 var cell = this.get_cell(i);
1307 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1322 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1308 cell.toggle_output_scroll();
1323 cell.toggle_output_scroll();
1309 this.set_dirty(true);
1324 this.set_dirty(true);
1310 }
1325 }
1311 };
1326 };
1312
1327
1328 /**
1329 * Toggle the scrolling of long output on all cells.
1330 *
1331 * @method toggle_all_output_scrolling
1332 */
1333 Notebook.prototype.toggle_all_output_scroll = function () {
1334 $.map(this.get_cells(), function (cell, i) {
1335 if (cell instanceof IPython.CodeCell) {
1336 cell.toggle_output_scroll();
1337 }
1338 });
1339 // this should not be set if the `collapse` key is removed from nbformat
1340 this.set_dirty(true);
1341 };
1313
1342
1314 // Other cell functions: line numbers, ...
1343 // Other cell functions: line numbers, ...
1315
1344
1316 /**
1345 /**
1317 * Toggle line numbers in the selected cell's input area.
1346 * Toggle line numbers in the selected cell's input area.
1318 *
1347 *
1319 * @method cell_toggle_line_numbers
1348 * @method cell_toggle_line_numbers
1320 */
1349 */
1321 Notebook.prototype.cell_toggle_line_numbers = function() {
1350 Notebook.prototype.cell_toggle_line_numbers = function() {
1322 this.get_selected_cell().toggle_line_numbers();
1351 this.get_selected_cell().toggle_line_numbers();
1323 };
1352 };
1324
1353
1325 // Session related things
1354 // Session related things
1326
1355
1327 /**
1356 /**
1328 * Start a new session and set it on each code cell.
1357 * Start a new session and set it on each code cell.
1329 *
1358 *
1330 * @method start_session
1359 * @method start_session
1331 */
1360 */
1332 Notebook.prototype.start_session = function () {
1361 Notebook.prototype.start_session = function () {
1333 this.session = new IPython.Session(this.notebook_name, this.notebook_path, this);
1362 this.session = new IPython.Session(this.notebook_name, this.notebook_path, this);
1334 this.session.start($.proxy(this._session_started, this));
1363 this.session.start($.proxy(this._session_started, this));
1335 };
1364 };
1336
1365
1337
1366
1338 /**
1367 /**
1339 * Once a session is started, link the code cells to the kernel and pass the
1368 * Once a session is started, link the code cells to the kernel and pass the
1340 * comm manager to the widget manager
1369 * comm manager to the widget manager
1341 *
1370 *
1342 */
1371 */
1343 Notebook.prototype._session_started = function(){
1372 Notebook.prototype._session_started = function(){
1344 this.kernel = this.session.kernel;
1373 this.kernel = this.session.kernel;
1345 var ncells = this.ncells();
1374 var ncells = this.ncells();
1346 for (var i=0; i<ncells; i++) {
1375 for (var i=0; i<ncells; i++) {
1347 var cell = this.get_cell(i);
1376 var cell = this.get_cell(i);
1348 if (cell instanceof IPython.CodeCell) {
1377 if (cell instanceof IPython.CodeCell) {
1349 cell.set_kernel(this.session.kernel);
1378 cell.set_kernel(this.session.kernel);
1350 };
1379 };
1351 };
1380 };
1352 };
1381 };
1353
1382
1354 /**
1383 /**
1355 * Prompt the user to restart the IPython kernel.
1384 * Prompt the user to restart the IPython kernel.
1356 *
1385 *
1357 * @method restart_kernel
1386 * @method restart_kernel
1358 */
1387 */
1359 Notebook.prototype.restart_kernel = function () {
1388 Notebook.prototype.restart_kernel = function () {
1360 var that = this;
1389 var that = this;
1361 IPython.dialog.modal({
1390 IPython.dialog.modal({
1362 title : "Restart kernel or continue running?",
1391 title : "Restart kernel or continue running?",
1363 body : $("<p/>").text(
1392 body : $("<p/>").text(
1364 'Do you want to restart the current kernel? You will lose all variables defined in it.'
1393 'Do you want to restart the current kernel? You will lose all variables defined in it.'
1365 ),
1394 ),
1366 buttons : {
1395 buttons : {
1367 "Continue running" : {},
1396 "Continue running" : {},
1368 "Restart" : {
1397 "Restart" : {
1369 "class" : "btn-danger",
1398 "class" : "btn-danger",
1370 "click" : function() {
1399 "click" : function() {
1371 that.session.restart_kernel();
1400 that.session.restart_kernel();
1372 }
1401 }
1373 }
1402 }
1374 }
1403 }
1375 });
1404 });
1376 };
1405 };
1377
1406
1378 /**
1407 /**
1379 * Execute or render cell outputs and go into command mode.
1408 * Execute or render cell outputs and go into command mode.
1380 *
1409 *
1381 * @method execute_cell
1410 * @method execute_cell
1382 */
1411 */
1383 Notebook.prototype.execute_cell = function () {
1412 Notebook.prototype.execute_cell = function () {
1384 // mode = shift, ctrl, alt
1413 // mode = shift, ctrl, alt
1385 var cell = this.get_selected_cell();
1414 var cell = this.get_selected_cell();
1386 var cell_index = this.find_cell_index(cell);
1415 var cell_index = this.find_cell_index(cell);
1387
1416
1388 cell.execute();
1417 cell.execute();
1389 this.command_mode();
1418 this.command_mode();
1390 cell.focus_cell();
1419 cell.focus_cell();
1391 this.set_dirty(true);
1420 this.set_dirty(true);
1392 }
1421 }
1393
1422
1394 /**
1423 /**
1395 * Execute or render cell outputs and insert a new cell below.
1424 * Execute or render cell outputs and insert a new cell below.
1396 *
1425 *
1397 * @method execute_cell_and_insert_below
1426 * @method execute_cell_and_insert_below
1398 */
1427 */
1399 Notebook.prototype.execute_cell_and_insert_below = function () {
1428 Notebook.prototype.execute_cell_and_insert_below = function () {
1400 var cell = this.get_selected_cell();
1429 var cell = this.get_selected_cell();
1401 var cell_index = this.find_cell_index(cell);
1430 var cell_index = this.find_cell_index(cell);
1402
1431
1403 cell.execute();
1432 cell.execute();
1404
1433
1405 // If we are at the end always insert a new cell and return
1434 // If we are at the end always insert a new cell and return
1406 if (cell_index === (this.ncells()-1)) {
1435 if (cell_index === (this.ncells()-1)) {
1407 this.insert_cell_below('code');
1436 this.insert_cell_below('code');
1408 this.select(cell_index+1);
1437 this.select(cell_index+1);
1409 this.edit_mode();
1438 this.edit_mode();
1410 this.scroll_to_bottom();
1439 this.scroll_to_bottom();
1411 this.set_dirty(true);
1440 this.set_dirty(true);
1412 return;
1441 return;
1413 }
1442 }
1414
1443
1415 // Only insert a new cell, if we ended up in an already populated cell
1444 // Only insert a new cell, if we ended up in an already populated cell
1416 var next_text = this.get_cell(cell_index+1).get_text();
1445 var next_text = this.get_cell(cell_index+1).get_text();
1417 if (/\S/.test(next_text) === true) {
1446 if (/\S/.test(next_text) === true) {
1418 this.insert_cell_below('code');
1447 this.insert_cell_below('code');
1419 }
1448 }
1420 this.select(cell_index+1);
1449 this.select(cell_index+1);
1421 this.edit_mode();
1450 this.edit_mode();
1422 this.set_dirty(true);
1451 this.set_dirty(true);
1423 };
1452 };
1424
1453
1425 /**
1454 /**
1426 * Execute or render cell outputs and select the next cell.
1455 * Execute or render cell outputs and select the next cell.
1427 *
1456 *
1428 * @method execute_cell_and_select_below
1457 * @method execute_cell_and_select_below
1429 */
1458 */
1430 Notebook.prototype.execute_cell_and_select_below = function () {
1459 Notebook.prototype.execute_cell_and_select_below = function () {
1431
1460
1432 var cell = this.get_selected_cell();
1461 var cell = this.get_selected_cell();
1433 var cell_index = this.find_cell_index(cell);
1462 var cell_index = this.find_cell_index(cell);
1434
1463
1435 cell.execute();
1464 cell.execute();
1436
1465
1437 // If we are at the end always insert a new cell and return
1466 // If we are at the end always insert a new cell and return
1438 if (cell_index === (this.ncells()-1)) {
1467 if (cell_index === (this.ncells()-1)) {
1439 this.insert_cell_below('code');
1468 this.insert_cell_below('code');
1440 this.select(cell_index+1);
1469 this.select(cell_index+1);
1441 this.edit_mode();
1470 this.edit_mode();
1442 this.scroll_to_bottom();
1471 this.scroll_to_bottom();
1443 this.set_dirty(true);
1472 this.set_dirty(true);
1444 return;
1473 return;
1445 }
1474 }
1446
1475
1447 this.select(cell_index+1);
1476 this.select(cell_index+1);
1448 this.get_cell(cell_index+1).focus_cell();
1477 this.get_cell(cell_index+1).focus_cell();
1449 this.set_dirty(true);
1478 this.set_dirty(true);
1450 };
1479 };
1451
1480
1452 /**
1481 /**
1453 * Execute all cells below the selected cell.
1482 * Execute all cells below the selected cell.
1454 *
1483 *
1455 * @method execute_cells_below
1484 * @method execute_cells_below
1456 */
1485 */
1457 Notebook.prototype.execute_cells_below = function () {
1486 Notebook.prototype.execute_cells_below = function () {
1458 this.execute_cell_range(this.get_selected_index(), this.ncells());
1487 this.execute_cell_range(this.get_selected_index(), this.ncells());
1459 this.scroll_to_bottom();
1488 this.scroll_to_bottom();
1460 };
1489 };
1461
1490
1462 /**
1491 /**
1463 * Execute all cells above the selected cell.
1492 * Execute all cells above the selected cell.
1464 *
1493 *
1465 * @method execute_cells_above
1494 * @method execute_cells_above
1466 */
1495 */
1467 Notebook.prototype.execute_cells_above = function () {
1496 Notebook.prototype.execute_cells_above = function () {
1468 this.execute_cell_range(0, this.get_selected_index());
1497 this.execute_cell_range(0, this.get_selected_index());
1469 };
1498 };
1470
1499
1471 /**
1500 /**
1472 * Execute all cells.
1501 * Execute all cells.
1473 *
1502 *
1474 * @method execute_all_cells
1503 * @method execute_all_cells
1475 */
1504 */
1476 Notebook.prototype.execute_all_cells = function () {
1505 Notebook.prototype.execute_all_cells = function () {
1477 this.execute_cell_range(0, this.ncells());
1506 this.execute_cell_range(0, this.ncells());
1478 this.scroll_to_bottom();
1507 this.scroll_to_bottom();
1479 };
1508 };
1480
1509
1481 /**
1510 /**
1482 * Execute a contiguous range of cells.
1511 * Execute a contiguous range of cells.
1483 *
1512 *
1484 * @method execute_cell_range
1513 * @method execute_cell_range
1485 * @param {Number} start Index of the first cell to execute (inclusive)
1514 * @param {Number} start Index of the first cell to execute (inclusive)
1486 * @param {Number} end Index of the last cell to execute (exclusive)
1515 * @param {Number} end Index of the last cell to execute (exclusive)
1487 */
1516 */
1488 Notebook.prototype.execute_cell_range = function (start, end) {
1517 Notebook.prototype.execute_cell_range = function (start, end) {
1489 for (var i=start; i<end; i++) {
1518 for (var i=start; i<end; i++) {
1490 this.select(i);
1519 this.select(i);
1491 this.execute_cell();
1520 this.execute_cell();
1492 };
1521 };
1493 };
1522 };
1494
1523
1495 // Persistance and loading
1524 // Persistance and loading
1496
1525
1497 /**
1526 /**
1498 * Getter method for this notebook's name.
1527 * Getter method for this notebook's name.
1499 *
1528 *
1500 * @method get_notebook_name
1529 * @method get_notebook_name
1501 * @return {String} This notebook's name
1530 * @return {String} This notebook's name
1502 */
1531 */
1503 Notebook.prototype.get_notebook_name = function () {
1532 Notebook.prototype.get_notebook_name = function () {
1504 var nbname = this.notebook_name.substring(0,this.notebook_name.length-6);
1533 var nbname = this.notebook_name.substring(0,this.notebook_name.length-6);
1505 return nbname;
1534 return nbname;
1506 };
1535 };
1507
1536
1508 /**
1537 /**
1509 * Setter method for this notebook's name.
1538 * Setter method for this notebook's name.
1510 *
1539 *
1511 * @method set_notebook_name
1540 * @method set_notebook_name
1512 * @param {String} name A new name for this notebook
1541 * @param {String} name A new name for this notebook
1513 */
1542 */
1514 Notebook.prototype.set_notebook_name = function (name) {
1543 Notebook.prototype.set_notebook_name = function (name) {
1515 this.notebook_name = name;
1544 this.notebook_name = name;
1516 };
1545 };
1517
1546
1518 /**
1547 /**
1519 * Check that a notebook's name is valid.
1548 * Check that a notebook's name is valid.
1520 *
1549 *
1521 * @method test_notebook_name
1550 * @method test_notebook_name
1522 * @param {String} nbname A name for this notebook
1551 * @param {String} nbname A name for this notebook
1523 * @return {Boolean} True if the name is valid, false if invalid
1552 * @return {Boolean} True if the name is valid, false if invalid
1524 */
1553 */
1525 Notebook.prototype.test_notebook_name = function (nbname) {
1554 Notebook.prototype.test_notebook_name = function (nbname) {
1526 nbname = nbname || '';
1555 nbname = nbname || '';
1527 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1556 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1528 return true;
1557 return true;
1529 } else {
1558 } else {
1530 return false;
1559 return false;
1531 };
1560 };
1532 };
1561 };
1533
1562
1534 /**
1563 /**
1535 * Load a notebook from JSON (.ipynb).
1564 * Load a notebook from JSON (.ipynb).
1536 *
1565 *
1537 * This currently handles one worksheet: others are deleted.
1566 * This currently handles one worksheet: others are deleted.
1538 *
1567 *
1539 * @method fromJSON
1568 * @method fromJSON
1540 * @param {Object} data JSON representation of a notebook
1569 * @param {Object} data JSON representation of a notebook
1541 */
1570 */
1542 Notebook.prototype.fromJSON = function (data) {
1571 Notebook.prototype.fromJSON = function (data) {
1543 var content = data.content;
1572 var content = data.content;
1544 var ncells = this.ncells();
1573 var ncells = this.ncells();
1545 var i;
1574 var i;
1546 for (i=0; i<ncells; i++) {
1575 for (i=0; i<ncells; i++) {
1547 // Always delete cell 0 as they get renumbered as they are deleted.
1576 // Always delete cell 0 as they get renumbered as they are deleted.
1548 this.delete_cell(0);
1577 this.delete_cell(0);
1549 };
1578 };
1550 // Save the metadata and name.
1579 // Save the metadata and name.
1551 this.metadata = content.metadata;
1580 this.metadata = content.metadata;
1552 this.notebook_name = data.name;
1581 this.notebook_name = data.name;
1553 // Only handle 1 worksheet for now.
1582 // Only handle 1 worksheet for now.
1554 var worksheet = content.worksheets[0];
1583 var worksheet = content.worksheets[0];
1555 if (worksheet !== undefined) {
1584 if (worksheet !== undefined) {
1556 if (worksheet.metadata) {
1585 if (worksheet.metadata) {
1557 this.worksheet_metadata = worksheet.metadata;
1586 this.worksheet_metadata = worksheet.metadata;
1558 }
1587 }
1559 var new_cells = worksheet.cells;
1588 var new_cells = worksheet.cells;
1560 ncells = new_cells.length;
1589 ncells = new_cells.length;
1561 var cell_data = null;
1590 var cell_data = null;
1562 var new_cell = null;
1591 var new_cell = null;
1563 for (i=0; i<ncells; i++) {
1592 for (i=0; i<ncells; i++) {
1564 cell_data = new_cells[i];
1593 cell_data = new_cells[i];
1565 // VERSIONHACK: plaintext -> raw
1594 // VERSIONHACK: plaintext -> raw
1566 // handle never-released plaintext name for raw cells
1595 // handle never-released plaintext name for raw cells
1567 if (cell_data.cell_type === 'plaintext'){
1596 if (cell_data.cell_type === 'plaintext'){
1568 cell_data.cell_type = 'raw';
1597 cell_data.cell_type = 'raw';
1569 }
1598 }
1570
1599
1571 new_cell = this.insert_cell_at_index(cell_data.cell_type, i);
1600 new_cell = this.insert_cell_at_index(cell_data.cell_type, i);
1572 new_cell.fromJSON(cell_data);
1601 new_cell.fromJSON(cell_data);
1573 };
1602 };
1574 };
1603 };
1575 if (content.worksheets.length > 1) {
1604 if (content.worksheets.length > 1) {
1576 IPython.dialog.modal({
1605 IPython.dialog.modal({
1577 title : "Multiple worksheets",
1606 title : "Multiple worksheets",
1578 body : "This notebook has " + data.worksheets.length + " worksheets, " +
1607 body : "This notebook has " + data.worksheets.length + " worksheets, " +
1579 "but this version of IPython can only handle the first. " +
1608 "but this version of IPython can only handle the first. " +
1580 "If you save this notebook, worksheets after the first will be lost.",
1609 "If you save this notebook, worksheets after the first will be lost.",
1581 buttons : {
1610 buttons : {
1582 OK : {
1611 OK : {
1583 class : "btn-danger"
1612 class : "btn-danger"
1584 }
1613 }
1585 }
1614 }
1586 });
1615 });
1587 }
1616 }
1588 };
1617 };
1589
1618
1590 /**
1619 /**
1591 * Dump this notebook into a JSON-friendly object.
1620 * Dump this notebook into a JSON-friendly object.
1592 *
1621 *
1593 * @method toJSON
1622 * @method toJSON
1594 * @return {Object} A JSON-friendly representation of this notebook.
1623 * @return {Object} A JSON-friendly representation of this notebook.
1595 */
1624 */
1596 Notebook.prototype.toJSON = function () {
1625 Notebook.prototype.toJSON = function () {
1597 var cells = this.get_cells();
1626 var cells = this.get_cells();
1598 var ncells = cells.length;
1627 var ncells = cells.length;
1599 var cell_array = new Array(ncells);
1628 var cell_array = new Array(ncells);
1600 for (var i=0; i<ncells; i++) {
1629 for (var i=0; i<ncells; i++) {
1601 cell_array[i] = cells[i].toJSON();
1630 cell_array[i] = cells[i].toJSON();
1602 };
1631 };
1603 var data = {
1632 var data = {
1604 // Only handle 1 worksheet for now.
1633 // Only handle 1 worksheet for now.
1605 worksheets : [{
1634 worksheets : [{
1606 cells: cell_array,
1635 cells: cell_array,
1607 metadata: this.worksheet_metadata
1636 metadata: this.worksheet_metadata
1608 }],
1637 }],
1609 metadata : this.metadata
1638 metadata : this.metadata
1610 };
1639 };
1611 return data;
1640 return data;
1612 };
1641 };
1613
1642
1614 /**
1643 /**
1615 * Start an autosave timer, for periodically saving the notebook.
1644 * Start an autosave timer, for periodically saving the notebook.
1616 *
1645 *
1617 * @method set_autosave_interval
1646 * @method set_autosave_interval
1618 * @param {Integer} interval the autosave interval in milliseconds
1647 * @param {Integer} interval the autosave interval in milliseconds
1619 */
1648 */
1620 Notebook.prototype.set_autosave_interval = function (interval) {
1649 Notebook.prototype.set_autosave_interval = function (interval) {
1621 var that = this;
1650 var that = this;
1622 // clear previous interval, so we don't get simultaneous timers
1651 // clear previous interval, so we don't get simultaneous timers
1623 if (this.autosave_timer) {
1652 if (this.autosave_timer) {
1624 clearInterval(this.autosave_timer);
1653 clearInterval(this.autosave_timer);
1625 }
1654 }
1626
1655
1627 this.autosave_interval = this.minimum_autosave_interval = interval;
1656 this.autosave_interval = this.minimum_autosave_interval = interval;
1628 if (interval) {
1657 if (interval) {
1629 this.autosave_timer = setInterval(function() {
1658 this.autosave_timer = setInterval(function() {
1630 if (that.dirty) {
1659 if (that.dirty) {
1631 that.save_notebook();
1660 that.save_notebook();
1632 }
1661 }
1633 }, interval);
1662 }, interval);
1634 $([IPython.events]).trigger("autosave_enabled.Notebook", interval);
1663 $([IPython.events]).trigger("autosave_enabled.Notebook", interval);
1635 } else {
1664 } else {
1636 this.autosave_timer = null;
1665 this.autosave_timer = null;
1637 $([IPython.events]).trigger("autosave_disabled.Notebook");
1666 $([IPython.events]).trigger("autosave_disabled.Notebook");
1638 };
1667 };
1639 };
1668 };
1640
1669
1641 /**
1670 /**
1642 * Save this notebook on the server.
1671 * Save this notebook on the server.
1643 *
1672 *
1644 * @method save_notebook
1673 * @method save_notebook
1645 */
1674 */
1646 Notebook.prototype.save_notebook = function (extra_settings) {
1675 Notebook.prototype.save_notebook = function (extra_settings) {
1647 // Create a JSON model to be sent to the server.
1676 // Create a JSON model to be sent to the server.
1648 var model = {};
1677 var model = {};
1649 model.name = this.notebook_name;
1678 model.name = this.notebook_name;
1650 model.path = this.notebook_path;
1679 model.path = this.notebook_path;
1651 model.content = this.toJSON();
1680 model.content = this.toJSON();
1652 model.content.nbformat = this.nbformat;
1681 model.content.nbformat = this.nbformat;
1653 model.content.nbformat_minor = this.nbformat_minor;
1682 model.content.nbformat_minor = this.nbformat_minor;
1654 // time the ajax call for autosave tuning purposes.
1683 // time the ajax call for autosave tuning purposes.
1655 var start = new Date().getTime();
1684 var start = new Date().getTime();
1656 // We do the call with settings so we can set cache to false.
1685 // We do the call with settings so we can set cache to false.
1657 var settings = {
1686 var settings = {
1658 processData : false,
1687 processData : false,
1659 cache : false,
1688 cache : false,
1660 type : "PUT",
1689 type : "PUT",
1661 data : JSON.stringify(model),
1690 data : JSON.stringify(model),
1662 headers : {'Content-Type': 'application/json'},
1691 headers : {'Content-Type': 'application/json'},
1663 success : $.proxy(this.save_notebook_success, this, start),
1692 success : $.proxy(this.save_notebook_success, this, start),
1664 error : $.proxy(this.save_notebook_error, this)
1693 error : $.proxy(this.save_notebook_error, this)
1665 };
1694 };
1666 if (extra_settings) {
1695 if (extra_settings) {
1667 for (var key in extra_settings) {
1696 for (var key in extra_settings) {
1668 settings[key] = extra_settings[key];
1697 settings[key] = extra_settings[key];
1669 }
1698 }
1670 }
1699 }
1671 $([IPython.events]).trigger('notebook_saving.Notebook');
1700 $([IPython.events]).trigger('notebook_saving.Notebook');
1672 var url = utils.url_join_encode(
1701 var url = utils.url_join_encode(
1673 this._baseProjectUrl,
1702 this._baseProjectUrl,
1674 'api/notebooks',
1703 'api/notebooks',
1675 this.notebook_path,
1704 this.notebook_path,
1676 this.notebook_name
1705 this.notebook_name
1677 );
1706 );
1678 $.ajax(url, settings);
1707 $.ajax(url, settings);
1679 };
1708 };
1680
1709
1681 /**
1710 /**
1682 * Success callback for saving a notebook.
1711 * Success callback for saving a notebook.
1683 *
1712 *
1684 * @method save_notebook_success
1713 * @method save_notebook_success
1685 * @param {Integer} start the time when the save request started
1714 * @param {Integer} start the time when the save request started
1686 * @param {Object} data JSON representation of a notebook
1715 * @param {Object} data JSON representation of a notebook
1687 * @param {String} status Description of response status
1716 * @param {String} status Description of response status
1688 * @param {jqXHR} xhr jQuery Ajax object
1717 * @param {jqXHR} xhr jQuery Ajax object
1689 */
1718 */
1690 Notebook.prototype.save_notebook_success = function (start, data, status, xhr) {
1719 Notebook.prototype.save_notebook_success = function (start, data, status, xhr) {
1691 this.set_dirty(false);
1720 this.set_dirty(false);
1692 $([IPython.events]).trigger('notebook_saved.Notebook');
1721 $([IPython.events]).trigger('notebook_saved.Notebook');
1693 this._update_autosave_interval(start);
1722 this._update_autosave_interval(start);
1694 if (this._checkpoint_after_save) {
1723 if (this._checkpoint_after_save) {
1695 this.create_checkpoint();
1724 this.create_checkpoint();
1696 this._checkpoint_after_save = false;
1725 this._checkpoint_after_save = false;
1697 };
1726 };
1698 };
1727 };
1699
1728
1700 /**
1729 /**
1701 * update the autosave interval based on how long the last save took
1730 * update the autosave interval based on how long the last save took
1702 *
1731 *
1703 * @method _update_autosave_interval
1732 * @method _update_autosave_interval
1704 * @param {Integer} timestamp when the save request started
1733 * @param {Integer} timestamp when the save request started
1705 */
1734 */
1706 Notebook.prototype._update_autosave_interval = function (start) {
1735 Notebook.prototype._update_autosave_interval = function (start) {
1707 var duration = (new Date().getTime() - start);
1736 var duration = (new Date().getTime() - start);
1708 if (this.autosave_interval) {
1737 if (this.autosave_interval) {
1709 // new save interval: higher of 10x save duration or parameter (default 30 seconds)
1738 // new save interval: higher of 10x save duration or parameter (default 30 seconds)
1710 var interval = Math.max(10 * duration, this.minimum_autosave_interval);
1739 var interval = Math.max(10 * duration, this.minimum_autosave_interval);
1711 // round to 10 seconds, otherwise we will be setting a new interval too often
1740 // round to 10 seconds, otherwise we will be setting a new interval too often
1712 interval = 10000 * Math.round(interval / 10000);
1741 interval = 10000 * Math.round(interval / 10000);
1713 // set new interval, if it's changed
1742 // set new interval, if it's changed
1714 if (interval != this.autosave_interval) {
1743 if (interval != this.autosave_interval) {
1715 this.set_autosave_interval(interval);
1744 this.set_autosave_interval(interval);
1716 }
1745 }
1717 }
1746 }
1718 };
1747 };
1719
1748
1720 /**
1749 /**
1721 * Failure callback for saving a notebook.
1750 * Failure callback for saving a notebook.
1722 *
1751 *
1723 * @method save_notebook_error
1752 * @method save_notebook_error
1724 * @param {jqXHR} xhr jQuery Ajax object
1753 * @param {jqXHR} xhr jQuery Ajax object
1725 * @param {String} status Description of response status
1754 * @param {String} status Description of response status
1726 * @param {String} error HTTP error message
1755 * @param {String} error HTTP error message
1727 */
1756 */
1728 Notebook.prototype.save_notebook_error = function (xhr, status, error) {
1757 Notebook.prototype.save_notebook_error = function (xhr, status, error) {
1729 $([IPython.events]).trigger('notebook_save_failed.Notebook', [xhr, status, error]);
1758 $([IPython.events]).trigger('notebook_save_failed.Notebook', [xhr, status, error]);
1730 };
1759 };
1731
1760
1732 Notebook.prototype.new_notebook = function(){
1761 Notebook.prototype.new_notebook = function(){
1733 var path = this.notebook_path;
1762 var path = this.notebook_path;
1734 var base_project_url = this._baseProjectUrl;
1763 var base_project_url = this._baseProjectUrl;
1735 var settings = {
1764 var settings = {
1736 processData : false,
1765 processData : false,
1737 cache : false,
1766 cache : false,
1738 type : "POST",
1767 type : "POST",
1739 dataType : "json",
1768 dataType : "json",
1740 async : false,
1769 async : false,
1741 success : function (data, status, xhr){
1770 success : function (data, status, xhr){
1742 var notebook_name = data.name;
1771 var notebook_name = data.name;
1743 window.open(
1772 window.open(
1744 utils.url_join_encode(
1773 utils.url_join_encode(
1745 base_project_url,
1774 base_project_url,
1746 'notebooks',
1775 'notebooks',
1747 path,
1776 path,
1748 notebook_name
1777 notebook_name
1749 ),
1778 ),
1750 '_blank'
1779 '_blank'
1751 );
1780 );
1752 }
1781 }
1753 };
1782 };
1754 var url = utils.url_join_encode(
1783 var url = utils.url_join_encode(
1755 base_project_url,
1784 base_project_url,
1756 'api/notebooks',
1785 'api/notebooks',
1757 path
1786 path
1758 );
1787 );
1759 $.ajax(url,settings);
1788 $.ajax(url,settings);
1760 };
1789 };
1761
1790
1762
1791
1763 Notebook.prototype.copy_notebook = function(){
1792 Notebook.prototype.copy_notebook = function(){
1764 var path = this.notebook_path;
1793 var path = this.notebook_path;
1765 var base_project_url = this._baseProjectUrl;
1794 var base_project_url = this._baseProjectUrl;
1766 var settings = {
1795 var settings = {
1767 processData : false,
1796 processData : false,
1768 cache : false,
1797 cache : false,
1769 type : "POST",
1798 type : "POST",
1770 dataType : "json",
1799 dataType : "json",
1771 data : JSON.stringify({copy_from : this.notebook_name}),
1800 data : JSON.stringify({copy_from : this.notebook_name}),
1772 async : false,
1801 async : false,
1773 success : function (data, status, xhr) {
1802 success : function (data, status, xhr) {
1774 window.open(utils.url_join_encode(
1803 window.open(utils.url_join_encode(
1775 base_project_url,
1804 base_project_url,
1776 'notebooks',
1805 'notebooks',
1777 data.path,
1806 data.path,
1778 data.name
1807 data.name
1779 ), '_blank');
1808 ), '_blank');
1780 }
1809 }
1781 };
1810 };
1782 var url = utils.url_join_encode(
1811 var url = utils.url_join_encode(
1783 base_project_url,
1812 base_project_url,
1784 'api/notebooks',
1813 'api/notebooks',
1785 path
1814 path
1786 );
1815 );
1787 $.ajax(url,settings);
1816 $.ajax(url,settings);
1788 };
1817 };
1789
1818
1790 Notebook.prototype.rename = function (nbname) {
1819 Notebook.prototype.rename = function (nbname) {
1791 var that = this;
1820 var that = this;
1792 var data = {name: nbname + '.ipynb'};
1821 var data = {name: nbname + '.ipynb'};
1793 var settings = {
1822 var settings = {
1794 processData : false,
1823 processData : false,
1795 cache : false,
1824 cache : false,
1796 type : "PATCH",
1825 type : "PATCH",
1797 data : JSON.stringify(data),
1826 data : JSON.stringify(data),
1798 dataType: "json",
1827 dataType: "json",
1799 headers : {'Content-Type': 'application/json'},
1828 headers : {'Content-Type': 'application/json'},
1800 success : $.proxy(that.rename_success, this),
1829 success : $.proxy(that.rename_success, this),
1801 error : $.proxy(that.rename_error, this)
1830 error : $.proxy(that.rename_error, this)
1802 };
1831 };
1803 $([IPython.events]).trigger('rename_notebook.Notebook', data);
1832 $([IPython.events]).trigger('rename_notebook.Notebook', data);
1804 var url = utils.url_join_encode(
1833 var url = utils.url_join_encode(
1805 this._baseProjectUrl,
1834 this._baseProjectUrl,
1806 'api/notebooks',
1835 'api/notebooks',
1807 this.notebook_path,
1836 this.notebook_path,
1808 this.notebook_name
1837 this.notebook_name
1809 );
1838 );
1810 $.ajax(url, settings);
1839 $.ajax(url, settings);
1811 };
1840 };
1812
1841
1813
1842
1814 Notebook.prototype.rename_success = function (json, status, xhr) {
1843 Notebook.prototype.rename_success = function (json, status, xhr) {
1815 this.notebook_name = json.name;
1844 this.notebook_name = json.name;
1816 var name = this.notebook_name;
1845 var name = this.notebook_name;
1817 var path = json.path;
1846 var path = json.path;
1818 this.session.rename_notebook(name, path);
1847 this.session.rename_notebook(name, path);
1819 $([IPython.events]).trigger('notebook_renamed.Notebook', json);
1848 $([IPython.events]).trigger('notebook_renamed.Notebook', json);
1820 }
1849 }
1821
1850
1822 Notebook.prototype.rename_error = function (xhr, status, error) {
1851 Notebook.prototype.rename_error = function (xhr, status, error) {
1823 var that = this;
1852 var that = this;
1824 var dialog = $('<div/>').append(
1853 var dialog = $('<div/>').append(
1825 $("<p/>").addClass("rename-message")
1854 $("<p/>").addClass("rename-message")
1826 .text('This notebook name already exists.')
1855 .text('This notebook name already exists.')
1827 )
1856 )
1828 $([IPython.events]).trigger('notebook_rename_failed.Notebook', [xhr, status, error]);
1857 $([IPython.events]).trigger('notebook_rename_failed.Notebook', [xhr, status, error]);
1829 IPython.dialog.modal({
1858 IPython.dialog.modal({
1830 title: "Notebook Rename Error!",
1859 title: "Notebook Rename Error!",
1831 body: dialog,
1860 body: dialog,
1832 buttons : {
1861 buttons : {
1833 "Cancel": {},
1862 "Cancel": {},
1834 "OK": {
1863 "OK": {
1835 class: "btn-primary",
1864 class: "btn-primary",
1836 click: function () {
1865 click: function () {
1837 IPython.save_widget.rename_notebook();
1866 IPython.save_widget.rename_notebook();
1838 }}
1867 }}
1839 },
1868 },
1840 open : function (event, ui) {
1869 open : function (event, ui) {
1841 var that = $(this);
1870 var that = $(this);
1842 // Upon ENTER, click the OK button.
1871 // Upon ENTER, click the OK button.
1843 that.find('input[type="text"]').keydown(function (event, ui) {
1872 that.find('input[type="text"]').keydown(function (event, ui) {
1844 if (event.which === utils.keycodes.ENTER) {
1873 if (event.which === utils.keycodes.ENTER) {
1845 that.find('.btn-primary').first().click();
1874 that.find('.btn-primary').first().click();
1846 }
1875 }
1847 });
1876 });
1848 that.find('input[type="text"]').focus();
1877 that.find('input[type="text"]').focus();
1849 }
1878 }
1850 });
1879 });
1851 }
1880 }
1852
1881
1853 /**
1882 /**
1854 * Request a notebook's data from the server.
1883 * Request a notebook's data from the server.
1855 *
1884 *
1856 * @method load_notebook
1885 * @method load_notebook
1857 * @param {String} notebook_name and path A notebook to load
1886 * @param {String} notebook_name and path A notebook to load
1858 */
1887 */
1859 Notebook.prototype.load_notebook = function (notebook_name, notebook_path) {
1888 Notebook.prototype.load_notebook = function (notebook_name, notebook_path) {
1860 var that = this;
1889 var that = this;
1861 this.notebook_name = notebook_name;
1890 this.notebook_name = notebook_name;
1862 this.notebook_path = notebook_path;
1891 this.notebook_path = notebook_path;
1863 // We do the call with settings so we can set cache to false.
1892 // We do the call with settings so we can set cache to false.
1864 var settings = {
1893 var settings = {
1865 processData : false,
1894 processData : false,
1866 cache : false,
1895 cache : false,
1867 type : "GET",
1896 type : "GET",
1868 dataType : "json",
1897 dataType : "json",
1869 success : $.proxy(this.load_notebook_success,this),
1898 success : $.proxy(this.load_notebook_success,this),
1870 error : $.proxy(this.load_notebook_error,this),
1899 error : $.proxy(this.load_notebook_error,this),
1871 };
1900 };
1872 $([IPython.events]).trigger('notebook_loading.Notebook');
1901 $([IPython.events]).trigger('notebook_loading.Notebook');
1873 var url = utils.url_join_encode(
1902 var url = utils.url_join_encode(
1874 this._baseProjectUrl,
1903 this._baseProjectUrl,
1875 'api/notebooks',
1904 'api/notebooks',
1876 this.notebook_path,
1905 this.notebook_path,
1877 this.notebook_name
1906 this.notebook_name
1878 );
1907 );
1879 $.ajax(url, settings);
1908 $.ajax(url, settings);
1880 };
1909 };
1881
1910
1882 /**
1911 /**
1883 * Success callback for loading a notebook from the server.
1912 * Success callback for loading a notebook from the server.
1884 *
1913 *
1885 * Load notebook data from the JSON response.
1914 * Load notebook data from the JSON response.
1886 *
1915 *
1887 * @method load_notebook_success
1916 * @method load_notebook_success
1888 * @param {Object} data JSON representation of a notebook
1917 * @param {Object} data JSON representation of a notebook
1889 * @param {String} status Description of response status
1918 * @param {String} status Description of response status
1890 * @param {jqXHR} xhr jQuery Ajax object
1919 * @param {jqXHR} xhr jQuery Ajax object
1891 */
1920 */
1892 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1921 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1893 this.fromJSON(data);
1922 this.fromJSON(data);
1894 if (this.ncells() === 0) {
1923 if (this.ncells() === 0) {
1895 this.insert_cell_below('code');
1924 this.insert_cell_below('code');
1896 this.select(0);
1925 this.select(0);
1897 this.edit_mode();
1926 this.edit_mode();
1898 } else {
1927 } else {
1899 this.select(0);
1928 this.select(0);
1900 this.command_mode();
1929 this.command_mode();
1901 };
1930 };
1902 this.set_dirty(false);
1931 this.set_dirty(false);
1903 this.scroll_to_top();
1932 this.scroll_to_top();
1904 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1933 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1905 var msg = "This notebook has been converted from an older " +
1934 var msg = "This notebook has been converted from an older " +
1906 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1935 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1907 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1936 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1908 "newer notebook format will be used and older versions of IPython " +
1937 "newer notebook format will be used and older versions of IPython " +
1909 "may not be able to read it. To keep the older version, close the " +
1938 "may not be able to read it. To keep the older version, close the " +
1910 "notebook without saving it.";
1939 "notebook without saving it.";
1911 IPython.dialog.modal({
1940 IPython.dialog.modal({
1912 title : "Notebook converted",
1941 title : "Notebook converted",
1913 body : msg,
1942 body : msg,
1914 buttons : {
1943 buttons : {
1915 OK : {
1944 OK : {
1916 class : "btn-primary"
1945 class : "btn-primary"
1917 }
1946 }
1918 }
1947 }
1919 });
1948 });
1920 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1949 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1921 var that = this;
1950 var that = this;
1922 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1951 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1923 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1952 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1924 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1953 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1925 this_vs + ". You can still work with this notebook, but some features " +
1954 this_vs + ". You can still work with this notebook, but some features " +
1926 "introduced in later notebook versions may not be available."
1955 "introduced in later notebook versions may not be available."
1927
1956
1928 IPython.dialog.modal({
1957 IPython.dialog.modal({
1929 title : "Newer Notebook",
1958 title : "Newer Notebook",
1930 body : msg,
1959 body : msg,
1931 buttons : {
1960 buttons : {
1932 OK : {
1961 OK : {
1933 class : "btn-danger"
1962 class : "btn-danger"
1934 }
1963 }
1935 }
1964 }
1936 });
1965 });
1937
1966
1938 }
1967 }
1939
1968
1940 // Create the session after the notebook is completely loaded to prevent
1969 // Create the session after the notebook is completely loaded to prevent
1941 // code execution upon loading, which is a security risk.
1970 // code execution upon loading, which is a security risk.
1942 if (this.session == null) {
1971 if (this.session == null) {
1943 this.start_session();
1972 this.start_session();
1944 }
1973 }
1945 // load our checkpoint list
1974 // load our checkpoint list
1946 this.list_checkpoints();
1975 this.list_checkpoints();
1947
1976
1948 // load toolbar state
1977 // load toolbar state
1949 if (this.metadata.celltoolbar) {
1978 if (this.metadata.celltoolbar) {
1950 IPython.CellToolbar.global_show();
1979 IPython.CellToolbar.global_show();
1951 IPython.CellToolbar.activate_preset(this.metadata.celltoolbar);
1980 IPython.CellToolbar.activate_preset(this.metadata.celltoolbar);
1952 }
1981 }
1953
1982
1954 $([IPython.events]).trigger('notebook_loaded.Notebook');
1983 $([IPython.events]).trigger('notebook_loaded.Notebook');
1955 };
1984 };
1956
1985
1957 /**
1986 /**
1958 * Failure callback for loading a notebook from the server.
1987 * Failure callback for loading a notebook from the server.
1959 *
1988 *
1960 * @method load_notebook_error
1989 * @method load_notebook_error
1961 * @param {jqXHR} xhr jQuery Ajax object
1990 * @param {jqXHR} xhr jQuery Ajax object
1962 * @param {String} status Description of response status
1991 * @param {String} status Description of response status
1963 * @param {String} error HTTP error message
1992 * @param {String} error HTTP error message
1964 */
1993 */
1965 Notebook.prototype.load_notebook_error = function (xhr, status, error) {
1994 Notebook.prototype.load_notebook_error = function (xhr, status, error) {
1966 $([IPython.events]).trigger('notebook_load_failed.Notebook', [xhr, status, error]);
1995 $([IPython.events]).trigger('notebook_load_failed.Notebook', [xhr, status, error]);
1967 if (xhr.status === 400) {
1996 if (xhr.status === 400) {
1968 var msg = error;
1997 var msg = error;
1969 } else if (xhr.status === 500) {
1998 } else if (xhr.status === 500) {
1970 var msg = "An unknown error occurred while loading this notebook. " +
1999 var msg = "An unknown error occurred while loading this notebook. " +
1971 "This version can load notebook formats " +
2000 "This version can load notebook formats " +
1972 "v" + this.nbformat + " or earlier.";
2001 "v" + this.nbformat + " or earlier.";
1973 }
2002 }
1974 IPython.dialog.modal({
2003 IPython.dialog.modal({
1975 title: "Error loading notebook",
2004 title: "Error loading notebook",
1976 body : msg,
2005 body : msg,
1977 buttons : {
2006 buttons : {
1978 "OK": {}
2007 "OK": {}
1979 }
2008 }
1980 });
2009 });
1981 }
2010 }
1982
2011
1983 /********************* checkpoint-related *********************/
2012 /********************* checkpoint-related *********************/
1984
2013
1985 /**
2014 /**
1986 * Save the notebook then immediately create a checkpoint.
2015 * Save the notebook then immediately create a checkpoint.
1987 *
2016 *
1988 * @method save_checkpoint
2017 * @method save_checkpoint
1989 */
2018 */
1990 Notebook.prototype.save_checkpoint = function () {
2019 Notebook.prototype.save_checkpoint = function () {
1991 this._checkpoint_after_save = true;
2020 this._checkpoint_after_save = true;
1992 this.save_notebook();
2021 this.save_notebook();
1993 };
2022 };
1994
2023
1995 /**
2024 /**
1996 * Add a checkpoint for this notebook.
2025 * Add a checkpoint for this notebook.
1997 * for use as a callback from checkpoint creation.
2026 * for use as a callback from checkpoint creation.
1998 *
2027 *
1999 * @method add_checkpoint
2028 * @method add_checkpoint
2000 */
2029 */
2001 Notebook.prototype.add_checkpoint = function (checkpoint) {
2030 Notebook.prototype.add_checkpoint = function (checkpoint) {
2002 var found = false;
2031 var found = false;
2003 for (var i = 0; i < this.checkpoints.length; i++) {
2032 for (var i = 0; i < this.checkpoints.length; i++) {
2004 var existing = this.checkpoints[i];
2033 var existing = this.checkpoints[i];
2005 if (existing.id == checkpoint.id) {
2034 if (existing.id == checkpoint.id) {
2006 found = true;
2035 found = true;
2007 this.checkpoints[i] = checkpoint;
2036 this.checkpoints[i] = checkpoint;
2008 break;
2037 break;
2009 }
2038 }
2010 }
2039 }
2011 if (!found) {
2040 if (!found) {
2012 this.checkpoints.push(checkpoint);
2041 this.checkpoints.push(checkpoint);
2013 }
2042 }
2014 this.last_checkpoint = this.checkpoints[this.checkpoints.length - 1];
2043 this.last_checkpoint = this.checkpoints[this.checkpoints.length - 1];
2015 };
2044 };
2016
2045
2017 /**
2046 /**
2018 * List checkpoints for this notebook.
2047 * List checkpoints for this notebook.
2019 *
2048 *
2020 * @method list_checkpoints
2049 * @method list_checkpoints
2021 */
2050 */
2022 Notebook.prototype.list_checkpoints = function () {
2051 Notebook.prototype.list_checkpoints = function () {
2023 var url = utils.url_join_encode(
2052 var url = utils.url_join_encode(
2024 this._baseProjectUrl,
2053 this._baseProjectUrl,
2025 'api/notebooks',
2054 'api/notebooks',
2026 this.notebook_path,
2055 this.notebook_path,
2027 this.notebook_name,
2056 this.notebook_name,
2028 'checkpoints'
2057 'checkpoints'
2029 );
2058 );
2030 $.get(url).done(
2059 $.get(url).done(
2031 $.proxy(this.list_checkpoints_success, this)
2060 $.proxy(this.list_checkpoints_success, this)
2032 ).fail(
2061 ).fail(
2033 $.proxy(this.list_checkpoints_error, this)
2062 $.proxy(this.list_checkpoints_error, this)
2034 );
2063 );
2035 };
2064 };
2036
2065
2037 /**
2066 /**
2038 * Success callback for listing checkpoints.
2067 * Success callback for listing checkpoints.
2039 *
2068 *
2040 * @method list_checkpoint_success
2069 * @method list_checkpoint_success
2041 * @param {Object} data JSON representation of a checkpoint
2070 * @param {Object} data JSON representation of a checkpoint
2042 * @param {String} status Description of response status
2071 * @param {String} status Description of response status
2043 * @param {jqXHR} xhr jQuery Ajax object
2072 * @param {jqXHR} xhr jQuery Ajax object
2044 */
2073 */
2045 Notebook.prototype.list_checkpoints_success = function (data, status, xhr) {
2074 Notebook.prototype.list_checkpoints_success = function (data, status, xhr) {
2046 var data = $.parseJSON(data);
2075 var data = $.parseJSON(data);
2047 this.checkpoints = data;
2076 this.checkpoints = data;
2048 if (data.length) {
2077 if (data.length) {
2049 this.last_checkpoint = data[data.length - 1];
2078 this.last_checkpoint = data[data.length - 1];
2050 } else {
2079 } else {
2051 this.last_checkpoint = null;
2080 this.last_checkpoint = null;
2052 }
2081 }
2053 $([IPython.events]).trigger('checkpoints_listed.Notebook', [data]);
2082 $([IPython.events]).trigger('checkpoints_listed.Notebook', [data]);
2054 };
2083 };
2055
2084
2056 /**
2085 /**
2057 * Failure callback for listing a checkpoint.
2086 * Failure callback for listing a checkpoint.
2058 *
2087 *
2059 * @method list_checkpoint_error
2088 * @method list_checkpoint_error
2060 * @param {jqXHR} xhr jQuery Ajax object
2089 * @param {jqXHR} xhr jQuery Ajax object
2061 * @param {String} status Description of response status
2090 * @param {String} status Description of response status
2062 * @param {String} error_msg HTTP error message
2091 * @param {String} error_msg HTTP error message
2063 */
2092 */
2064 Notebook.prototype.list_checkpoints_error = function (xhr, status, error_msg) {
2093 Notebook.prototype.list_checkpoints_error = function (xhr, status, error_msg) {
2065 $([IPython.events]).trigger('list_checkpoints_failed.Notebook');
2094 $([IPython.events]).trigger('list_checkpoints_failed.Notebook');
2066 };
2095 };
2067
2096
2068 /**
2097 /**
2069 * Create a checkpoint of this notebook on the server from the most recent save.
2098 * Create a checkpoint of this notebook on the server from the most recent save.
2070 *
2099 *
2071 * @method create_checkpoint
2100 * @method create_checkpoint
2072 */
2101 */
2073 Notebook.prototype.create_checkpoint = function () {
2102 Notebook.prototype.create_checkpoint = function () {
2074 var url = utils.url_join_encode(
2103 var url = utils.url_join_encode(
2075 this._baseProjectUrl,
2104 this._baseProjectUrl,
2076 'api/notebooks',
2105 'api/notebooks',
2077 this.notebookPath(),
2106 this.notebookPath(),
2078 this.notebook_name,
2107 this.notebook_name,
2079 'checkpoints'
2108 'checkpoints'
2080 );
2109 );
2081 $.post(url).done(
2110 $.post(url).done(
2082 $.proxy(this.create_checkpoint_success, this)
2111 $.proxy(this.create_checkpoint_success, this)
2083 ).fail(
2112 ).fail(
2084 $.proxy(this.create_checkpoint_error, this)
2113 $.proxy(this.create_checkpoint_error, this)
2085 );
2114 );
2086 };
2115 };
2087
2116
2088 /**
2117 /**
2089 * Success callback for creating a checkpoint.
2118 * Success callback for creating a checkpoint.
2090 *
2119 *
2091 * @method create_checkpoint_success
2120 * @method create_checkpoint_success
2092 * @param {Object} data JSON representation of a checkpoint
2121 * @param {Object} data JSON representation of a checkpoint
2093 * @param {String} status Description of response status
2122 * @param {String} status Description of response status
2094 * @param {jqXHR} xhr jQuery Ajax object
2123 * @param {jqXHR} xhr jQuery Ajax object
2095 */
2124 */
2096 Notebook.prototype.create_checkpoint_success = function (data, status, xhr) {
2125 Notebook.prototype.create_checkpoint_success = function (data, status, xhr) {
2097 var data = $.parseJSON(data);
2126 var data = $.parseJSON(data);
2098 this.add_checkpoint(data);
2127 this.add_checkpoint(data);
2099 $([IPython.events]).trigger('checkpoint_created.Notebook', data);
2128 $([IPython.events]).trigger('checkpoint_created.Notebook', data);
2100 };
2129 };
2101
2130
2102 /**
2131 /**
2103 * Failure callback for creating a checkpoint.
2132 * Failure callback for creating a checkpoint.
2104 *
2133 *
2105 * @method create_checkpoint_error
2134 * @method create_checkpoint_error
2106 * @param {jqXHR} xhr jQuery Ajax object
2135 * @param {jqXHR} xhr jQuery Ajax object
2107 * @param {String} status Description of response status
2136 * @param {String} status Description of response status
2108 * @param {String} error_msg HTTP error message
2137 * @param {String} error_msg HTTP error message
2109 */
2138 */
2110 Notebook.prototype.create_checkpoint_error = function (xhr, status, error_msg) {
2139 Notebook.prototype.create_checkpoint_error = function (xhr, status, error_msg) {
2111 $([IPython.events]).trigger('checkpoint_failed.Notebook');
2140 $([IPython.events]).trigger('checkpoint_failed.Notebook');
2112 };
2141 };
2113
2142
2114 Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) {
2143 Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) {
2115 var that = this;
2144 var that = this;
2116 var checkpoint = checkpoint || this.last_checkpoint;
2145 var checkpoint = checkpoint || this.last_checkpoint;
2117 if ( ! checkpoint ) {
2146 if ( ! checkpoint ) {
2118 console.log("restore dialog, but no checkpoint to restore to!");
2147 console.log("restore dialog, but no checkpoint to restore to!");
2119 return;
2148 return;
2120 }
2149 }
2121 var body = $('<div/>').append(
2150 var body = $('<div/>').append(
2122 $('<p/>').addClass("p-space").text(
2151 $('<p/>').addClass("p-space").text(
2123 "Are you sure you want to revert the notebook to " +
2152 "Are you sure you want to revert the notebook to " +
2124 "the latest checkpoint?"
2153 "the latest checkpoint?"
2125 ).append(
2154 ).append(
2126 $("<strong/>").text(
2155 $("<strong/>").text(
2127 " This cannot be undone."
2156 " This cannot be undone."
2128 )
2157 )
2129 )
2158 )
2130 ).append(
2159 ).append(
2131 $('<p/>').addClass("p-space").text("The checkpoint was last updated at:")
2160 $('<p/>').addClass("p-space").text("The checkpoint was last updated at:")
2132 ).append(
2161 ).append(
2133 $('<p/>').addClass("p-space").text(
2162 $('<p/>').addClass("p-space").text(
2134 Date(checkpoint.last_modified)
2163 Date(checkpoint.last_modified)
2135 ).css("text-align", "center")
2164 ).css("text-align", "center")
2136 );
2165 );
2137
2166
2138 IPython.dialog.modal({
2167 IPython.dialog.modal({
2139 title : "Revert notebook to checkpoint",
2168 title : "Revert notebook to checkpoint",
2140 body : body,
2169 body : body,
2141 buttons : {
2170 buttons : {
2142 Revert : {
2171 Revert : {
2143 class : "btn-danger",
2172 class : "btn-danger",
2144 click : function () {
2173 click : function () {
2145 that.restore_checkpoint(checkpoint.id);
2174 that.restore_checkpoint(checkpoint.id);
2146 }
2175 }
2147 },
2176 },
2148 Cancel : {}
2177 Cancel : {}
2149 }
2178 }
2150 });
2179 });
2151 }
2180 }
2152
2181
2153 /**
2182 /**
2154 * Restore the notebook to a checkpoint state.
2183 * Restore the notebook to a checkpoint state.
2155 *
2184 *
2156 * @method restore_checkpoint
2185 * @method restore_checkpoint
2157 * @param {String} checkpoint ID
2186 * @param {String} checkpoint ID
2158 */
2187 */
2159 Notebook.prototype.restore_checkpoint = function (checkpoint) {
2188 Notebook.prototype.restore_checkpoint = function (checkpoint) {
2160 $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint);
2189 $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint);
2161 var url = utils.url_join_encode(
2190 var url = utils.url_join_encode(
2162 this._baseProjectUrl,
2191 this._baseProjectUrl,
2163 'api/notebooks',
2192 'api/notebooks',
2164 this.notebookPath(),
2193 this.notebookPath(),
2165 this.notebook_name,
2194 this.notebook_name,
2166 'checkpoints',
2195 'checkpoints',
2167 checkpoint
2196 checkpoint
2168 );
2197 );
2169 $.post(url).done(
2198 $.post(url).done(
2170 $.proxy(this.restore_checkpoint_success, this)
2199 $.proxy(this.restore_checkpoint_success, this)
2171 ).fail(
2200 ).fail(
2172 $.proxy(this.restore_checkpoint_error, this)
2201 $.proxy(this.restore_checkpoint_error, this)
2173 );
2202 );
2174 };
2203 };
2175
2204
2176 /**
2205 /**
2177 * Success callback for restoring a notebook to a checkpoint.
2206 * Success callback for restoring a notebook to a checkpoint.
2178 *
2207 *
2179 * @method restore_checkpoint_success
2208 * @method restore_checkpoint_success
2180 * @param {Object} data (ignored, should be empty)
2209 * @param {Object} data (ignored, should be empty)
2181 * @param {String} status Description of response status
2210 * @param {String} status Description of response status
2182 * @param {jqXHR} xhr jQuery Ajax object
2211 * @param {jqXHR} xhr jQuery Ajax object
2183 */
2212 */
2184 Notebook.prototype.restore_checkpoint_success = function (data, status, xhr) {
2213 Notebook.prototype.restore_checkpoint_success = function (data, status, xhr) {
2185 $([IPython.events]).trigger('checkpoint_restored.Notebook');
2214 $([IPython.events]).trigger('checkpoint_restored.Notebook');
2186 this.load_notebook(this.notebook_name, this.notebook_path);
2215 this.load_notebook(this.notebook_name, this.notebook_path);
2187 };
2216 };
2188
2217
2189 /**
2218 /**
2190 * Failure callback for restoring a notebook to a checkpoint.
2219 * Failure callback for restoring a notebook to a checkpoint.
2191 *
2220 *
2192 * @method restore_checkpoint_error
2221 * @method restore_checkpoint_error
2193 * @param {jqXHR} xhr jQuery Ajax object
2222 * @param {jqXHR} xhr jQuery Ajax object
2194 * @param {String} status Description of response status
2223 * @param {String} status Description of response status
2195 * @param {String} error_msg HTTP error message
2224 * @param {String} error_msg HTTP error message
2196 */
2225 */
2197 Notebook.prototype.restore_checkpoint_error = function (xhr, status, error_msg) {
2226 Notebook.prototype.restore_checkpoint_error = function (xhr, status, error_msg) {
2198 $([IPython.events]).trigger('checkpoint_restore_failed.Notebook');
2227 $([IPython.events]).trigger('checkpoint_restore_failed.Notebook');
2199 };
2228 };
2200
2229
2201 /**
2230 /**
2202 * Delete a notebook checkpoint.
2231 * Delete a notebook checkpoint.
2203 *
2232 *
2204 * @method delete_checkpoint
2233 * @method delete_checkpoint
2205 * @param {String} checkpoint ID
2234 * @param {String} checkpoint ID
2206 */
2235 */
2207 Notebook.prototype.delete_checkpoint = function (checkpoint) {
2236 Notebook.prototype.delete_checkpoint = function (checkpoint) {
2208 $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint);
2237 $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint);
2209 var url = utils.url_join_encode(
2238 var url = utils.url_join_encode(
2210 this._baseProjectUrl,
2239 this._baseProjectUrl,
2211 'api/notebooks',
2240 'api/notebooks',
2212 this.notebookPath(),
2241 this.notebookPath(),
2213 this.notebook_name,
2242 this.notebook_name,
2214 'checkpoints',
2243 'checkpoints',
2215 checkpoint
2244 checkpoint
2216 );
2245 );
2217 $.ajax(url, {
2246 $.ajax(url, {
2218 type: 'DELETE',
2247 type: 'DELETE',
2219 success: $.proxy(this.delete_checkpoint_success, this),
2248 success: $.proxy(this.delete_checkpoint_success, this),
2220 error: $.proxy(this.delete_notebook_error,this)
2249 error: $.proxy(this.delete_notebook_error,this)
2221 });
2250 });
2222 };
2251 };
2223
2252
2224 /**
2253 /**
2225 * Success callback for deleting a notebook checkpoint
2254 * Success callback for deleting a notebook checkpoint
2226 *
2255 *
2227 * @method delete_checkpoint_success
2256 * @method delete_checkpoint_success
2228 * @param {Object} data (ignored, should be empty)
2257 * @param {Object} data (ignored, should be empty)
2229 * @param {String} status Description of response status
2258 * @param {String} status Description of response status
2230 * @param {jqXHR} xhr jQuery Ajax object
2259 * @param {jqXHR} xhr jQuery Ajax object
2231 */
2260 */
2232 Notebook.prototype.delete_checkpoint_success = function (data, status, xhr) {
2261 Notebook.prototype.delete_checkpoint_success = function (data, status, xhr) {
2233 $([IPython.events]).trigger('checkpoint_deleted.Notebook', data);
2262 $([IPython.events]).trigger('checkpoint_deleted.Notebook', data);
2234 this.load_notebook(this.notebook_name, this.notebook_path);
2263 this.load_notebook(this.notebook_name, this.notebook_path);
2235 };
2264 };
2236
2265
2237 /**
2266 /**
2238 * Failure callback for deleting a notebook checkpoint.
2267 * Failure callback for deleting a notebook checkpoint.
2239 *
2268 *
2240 * @method delete_checkpoint_error
2269 * @method delete_checkpoint_error
2241 * @param {jqXHR} xhr jQuery Ajax object
2270 * @param {jqXHR} xhr jQuery Ajax object
2242 * @param {String} status Description of response status
2271 * @param {String} status Description of response status
2243 * @param {String} error_msg HTTP error message
2272 * @param {String} error_msg HTTP error message
2244 */
2273 */
2245 Notebook.prototype.delete_checkpoint_error = function (xhr, status, error_msg) {
2274 Notebook.prototype.delete_checkpoint_error = function (xhr, status, error_msg) {
2246 $([IPython.events]).trigger('checkpoint_delete_failed.Notebook');
2275 $([IPython.events]).trigger('checkpoint_delete_failed.Notebook');
2247 };
2276 };
2248
2277
2249
2278
2250 IPython.Notebook = Notebook;
2279 IPython.Notebook = Notebook;
2251
2280
2252
2281
2253 return IPython;
2282 return IPython;
2254
2283
2255 }(IPython));
2284 }(IPython));
@@ -1,315 +1,325 b''
1 {% extends "page.html" %}
1 {% extends "page.html" %}
2
2
3 {% block stylesheet %}
3 {% block stylesheet %}
4
4
5 {% if mathjax_url %}
5 {% if mathjax_url %}
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
7 {% endif %}
7 {% endif %}
8 <script type="text/javascript">
8 <script type="text/javascript">
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
10 // where it will be undefined, and should prompt a dialog later.
10 // where it will be undefined, and should prompt a dialog later.
11 window.mathjax_url = "{{mathjax_url}}";
11 window.mathjax_url = "{{mathjax_url}}";
12 </script>
12 </script>
13
13
14 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
14 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
15
15
16 {{super()}}
16 {{super()}}
17
17
18 <link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
18 <link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
19
19
20 {% endblock %}
20 {% endblock %}
21
21
22 {% block params %}
22 {% block params %}
23
23
24 data-project="{{project}}"
24 data-project="{{project}}"
25 data-base-project-url="{{base_project_url}}"
25 data-base-project-url="{{base_project_url}}"
26 data-base-kernel-url="{{base_kernel_url}}"
26 data-base-kernel-url="{{base_kernel_url}}"
27 data-notebook-name="{{notebook_name}}"
27 data-notebook-name="{{notebook_name}}"
28 data-notebook-path="{{notebook_path}}"
28 data-notebook-path="{{notebook_path}}"
29 class="notebook_app"
29 class="notebook_app"
30
30
31 {% endblock %}
31 {% endblock %}
32
32
33
33
34 {% block header %}
34 {% block header %}
35
35
36 <span id="save_widget" class="nav pull-left">
36 <span id="save_widget" class="nav pull-left">
37 <span id="notebook_name"></span>
37 <span id="notebook_name"></span>
38 <span id="checkpoint_status"></span>
38 <span id="checkpoint_status"></span>
39 <span id="autosave_status"></span>
39 <span id="autosave_status"></span>
40 </span>
40 </span>
41
41
42 {% endblock %}
42 {% endblock %}
43
43
44
44
45 {% block site %}
45 {% block site %}
46
46
47 <div id="menubar-container" class="container">
47 <div id="menubar-container" class="container">
48 <div id="menubar">
48 <div id="menubar">
49 <div class="navbar">
49 <div class="navbar">
50 <div class="navbar-inner">
50 <div class="navbar-inner">
51 <div class="container">
51 <div class="container">
52 <ul id="menus" class="nav">
52 <ul id="menus" class="nav">
53 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
53 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
54 <ul class="dropdown-menu">
54 <ul class="dropdown-menu">
55 <li id="new_notebook"
55 <li id="new_notebook"
56 title="Make a new notebook (Opens a new window)">
56 title="Make a new notebook (Opens a new window)">
57 <a href="#">New</a></li>
57 <a href="#">New</a></li>
58 <li id="open_notebook"
58 <li id="open_notebook"
59 title="Opens a new window with the Dashboard view">
59 title="Opens a new window with the Dashboard view">
60 <a href="#">Open...</a></li>
60 <a href="#">Open...</a></li>
61 <!-- <hr/> -->
61 <!-- <hr/> -->
62 <li class="divider"></li>
62 <li class="divider"></li>
63 <li id="copy_notebook"
63 <li id="copy_notebook"
64 title="Open a copy of this notebook's contents and start a new kernel">
64 title="Open a copy of this notebook's contents and start a new kernel">
65 <a href="#">Make a Copy...</a></li>
65 <a href="#">Make a Copy...</a></li>
66 <li id="rename_notebook"><a href="#">Rename...</a></li>
66 <li id="rename_notebook"><a href="#">Rename...</a></li>
67 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
67 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
68 <!-- <hr/> -->
68 <!-- <hr/> -->
69 <li class="divider"></li>
69 <li class="divider"></li>
70 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
70 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
71 <ul class="dropdown-menu">
71 <ul class="dropdown-menu">
72 <li><a href="#"></a></li>
72 <li><a href="#"></a></li>
73 <li><a href="#"></a></li>
73 <li><a href="#"></a></li>
74 <li><a href="#"></a></li>
74 <li><a href="#"></a></li>
75 <li><a href="#"></a></li>
75 <li><a href="#"></a></li>
76 <li><a href="#"></a></li>
76 <li><a href="#"></a></li>
77 </ul>
77 </ul>
78 </li>
78 </li>
79 <li class="divider"></li>
79 <li class="divider"></li>
80 <li id="print_preview"><a href="#">Print Preview</a></li>
80 <li id="print_preview"><a href="#">Print Preview</a></li>
81 <li class="dropdown-submenu"><a href="#">Download as</a>
81 <li class="dropdown-submenu"><a href="#">Download as</a>
82 <ul class="dropdown-menu">
82 <ul class="dropdown-menu">
83 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
83 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
84 <li id="download_py"><a href="#">Python (.py)</a></li>
84 <li id="download_py"><a href="#">Python (.py)</a></li>
85 <li id="download_html"><a href="#">HTML (.html)</a></li>
85 <li id="download_html"><a href="#">HTML (.html)</a></li>
86 <li id="download_rst"><a href="#">reST (.rst)</a></li>
86 <li id="download_rst"><a href="#">reST (.rst)</a></li>
87 </ul>
87 </ul>
88 </li>
88 </li>
89 <li class="divider"></li>
89 <li class="divider"></li>
90
90
91 <li id="kill_and_exit"
91 <li id="kill_and_exit"
92 title="Shutdown this notebook's kernel, and close this window">
92 title="Shutdown this notebook's kernel, and close this window">
93 <a href="#" >Close and halt</a></li>
93 <a href="#" >Close and halt</a></li>
94 </ul>
94 </ul>
95 </li>
95 </li>
96 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
96 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
97 <ul class="dropdown-menu">
97 <ul class="dropdown-menu">
98 <li id="cut_cell"><a href="#">Cut Cell</a></li>
98 <li id="cut_cell"><a href="#">Cut Cell</a></li>
99 <li id="copy_cell"><a href="#">Copy Cell</a></li>
99 <li id="copy_cell"><a href="#">Copy Cell</a></li>
100 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
100 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
101 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
101 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
102 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
102 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
103 <li id="delete_cell"><a href="#">Delete Cell</a></li>
103 <li id="delete_cell"><a href="#">Delete Cell</a></li>
104 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
104 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
105 <li class="divider"></li>
105 <li class="divider"></li>
106 <li id="split_cell"><a href="#">Split Cell</a></li>
106 <li id="split_cell"><a href="#">Split Cell</a></li>
107 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
107 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
108 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
108 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
109 <li class="divider"></li>
109 <li class="divider"></li>
110 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
110 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
111 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
111 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
112 <li class="divider"></li>
112 <li class="divider"></li>
113 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
113 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
114 </ul>
114 </ul>
115 </li>
115 </li>
116 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
116 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
117 <ul class="dropdown-menu">
117 <ul class="dropdown-menu">
118 <li id="toggle_header"
118 <li id="toggle_header"
119 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
119 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
120 <a href="#">Toggle Header</a></li>
120 <a href="#">Toggle Header</a></li>
121 <li id="toggle_toolbar"
121 <li id="toggle_toolbar"
122 title="Show/Hide the action icons (below menu bar)">
122 title="Show/Hide the action icons (below menu bar)">
123 <a href="#">Toggle Toolbar</a></li>
123 <a href="#">Toggle Toolbar</a></li>
124 </ul>
124 </ul>
125 </li>
125 </li>
126 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
126 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
127 <ul class="dropdown-menu">
127 <ul class="dropdown-menu">
128 <li id="insert_cell_above"
128 <li id="insert_cell_above"
129 title="Insert an empty Code cell above the currently active cell">
129 title="Insert an empty Code cell above the currently active cell">
130 <a href="#">Insert Cell Above</a></li>
130 <a href="#">Insert Cell Above</a></li>
131 <li id="insert_cell_below"
131 <li id="insert_cell_below"
132 title="Insert an empty Code cell below the currently active cell">
132 title="Insert an empty Code cell below the currently active cell">
133 <a href="#">Insert Cell Below</a></li>
133 <a href="#">Insert Cell Below</a></li>
134 </ul>
134 </ul>
135 </li>
135 </li>
136 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
136 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
137 <ul class="dropdown-menu">
137 <ul class="dropdown-menu">
138 <li id="run_cell" title="Run this cell, and move cursor to the next one">
138 <li id="run_cell" title="Run this cell, and move cursor to the next one">
139 <a href="#">Run</a></li>
139 <a href="#">Run</a></li>
140 <li id="run_cell_select_below" title="Run this cell, select below">
140 <li id="run_cell_select_below" title="Run this cell, select below">
141 <a href="#">Run and Select Below</a></li>
141 <a href="#">Run and Select Below</a></li>
142 <li id="run_cell_insert_below" title="Run this cell, insert below">
142 <li id="run_cell_insert_below" title="Run this cell, insert below">
143 <a href="#">Run and Insert Below</a></li>
143 <a href="#">Run and Insert Below</a></li>
144 <li id="run_all_cells" title="Run all cells in the notebook">
144 <li id="run_all_cells" title="Run all cells in the notebook">
145 <a href="#">Run All</a></li>
145 <a href="#">Run All</a></li>
146 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
146 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
147 <a href="#">Run All Above</a></li>
147 <a href="#">Run All Above</a></li>
148 <li id="run_all_cells_below" title="Run this cell and all cells below it">
148 <li id="run_all_cells_below" title="Run this cell and all cells below it">
149 <a href="#">Run All Below</a></li>
149 <a href="#">Run All Below</a></li>
150 <li class="divider"></li>
150 <li class="divider"></li>
151 <li id="change_cell_type" class="dropdown-submenu"
151 <li id="change_cell_type" class="dropdown-submenu"
152 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
152 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
153 <a href="#">Cell Type</a>
153 <a href="#">Cell Type</a>
154 <ul class="dropdown-menu">
154 <ul class="dropdown-menu">
155 <li id="to_code"
155 <li id="to_code"
156 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
156 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
157 <a href="#">Code</a></li>
157 <a href="#">Code</a></li>
158 <li id="to_markdown"
158 <li id="to_markdown"
159 title="Contents will be rendered as HTML and serve as explanatory text">
159 title="Contents will be rendered as HTML and serve as explanatory text">
160 <a href="#">Markdown</a></li>
160 <a href="#">Markdown</a></li>
161 <li id="to_raw"
161 <li id="to_raw"
162 title="Contents will pass through nbconvert unmodified">
162 title="Contents will pass through nbconvert unmodified">
163 <a href="#">Raw NBConvert</a></li>
163 <a href="#">Raw NBConvert</a></li>
164 <li id="to_heading1"><a href="#">Heading 1</a></li>
164 <li id="to_heading1"><a href="#">Heading 1</a></li>
165 <li id="to_heading2"><a href="#">Heading 2</a></li>
165 <li id="to_heading2"><a href="#">Heading 2</a></li>
166 <li id="to_heading3"><a href="#">Heading 3</a></li>
166 <li id="to_heading3"><a href="#">Heading 3</a></li>
167 <li id="to_heading4"><a href="#">Heading 4</a></li>
167 <li id="to_heading4"><a href="#">Heading 4</a></li>
168 <li id="to_heading5"><a href="#">Heading 5</a></li>
168 <li id="to_heading5"><a href="#">Heading 5</a></li>
169 <li id="to_heading6"><a href="#">Heading 6</a></li>
169 <li id="to_heading6"><a href="#">Heading 6</a></li>
170 </ul>
170 </ul>
171 </li>
171 </li>
172 <li class="divider"></li>
172 <li class="divider"></li>
173 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
173 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
174 <ul class="dropdown-menu">
174 <ul class="dropdown-menu">
175 <li id="collapse_current_output"><a href="#">Collapse</a></li>
175 <li id="toggle_current_output"
176 <li id="expand_current_output"><a href="#">Expand</a></li>
176 title="Hide/Show the output of the current cell">
177 <a href="#">Toggle</a>
178 </li>
179 <li id="toggle_current_output_scroll"
180 title="Scroll the output of the current cell">
181 <a href="#">Toggle Scrolling</a>
182 </li>
177 <li id="clear_current_output"
183 <li id="clear_current_output"
178 title="Clear the output portion of the current cell">
184 title="Clear the output of the current cell">
179 <a href="#">Clear</a>
185 <a href="#">Clear</a>
180 </li>
186 </li>
181 <li id="scroll_current_output"><a href="#">Scroll Long</a></li>
182 </ul>
187 </ul>
183 </li>
188 </li>
184 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
189 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
185 <ul class="dropdown-menu">
190 <ul class="dropdown-menu">
186 <li id="collapse_all_output"><a href="#">Collapse</a></li>
191 <li id="toggle_all_output"
187 <li id="expand_all_output"><a href="#">Expand</a></li>
192 title="Hide/Show the output of all cells">
193 <a href="#">Toggle</a>
194 </li>
195 <li id="toggle_all_output_scroll"
196 title="Scroll the output of all cells">
197 <a href="#">Toggle Scrolling</a>
198 </li>
188 <li id="clear_all_output"
199 <li id="clear_all_output"
189 title="Clear the output portion of all Code cells">
200 title="Clear the output of all cells">
190 <a href="#">Clear</a>
201 <a href="#">Clear</a>
191 </li>
202 </li>
192 <li id="scroll_all_output"><a href="#">Scroll Long</a></li>
193 </ul>
203 </ul>
194 </li>
204 </li>
195 </ul>
205 </ul>
196 </li>
206 </li>
197 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
207 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
198 <ul class="dropdown-menu">
208 <ul class="dropdown-menu">
199 <li id="int_kernel"
209 <li id="int_kernel"
200 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
210 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
201 <a href="#">Interrupt</a></li>
211 <a href="#">Interrupt</a></li>
202 <li id="restart_kernel"
212 <li id="restart_kernel"
203 title="Restart the Kernel">
213 title="Restart the Kernel">
204 <a href="#">Restart</a></li>
214 <a href="#">Restart</a></li>
205 </ul>
215 </ul>
206 </li>
216 </li>
207 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
217 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
208 <ul class="dropdown-menu" title="Opens in a new window">
218 <ul class="dropdown-menu" title="Opens in a new window">
209 <li><a href="http://ipython.org/documentation.html" target="_blank">IPython Help</a></li>
219 <li><a href="http://ipython.org/documentation.html" target="_blank">IPython Help</a></li>
210 <li><a href="http://ipython.org/ipython-doc/stable/interactive/notebook.html" target="_blank">Notebook Help</a></li>
220 <li><a href="http://ipython.org/ipython-doc/stable/interactive/notebook.html" target="_blank">Notebook Help</a></li>
211 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
221 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
212 <li><a href="http://ipython.org/ipython-doc/dev/interactive/cm_keyboard.html" target="_blank">Editor Shortcuts</a></li>
222 <li><a href="http://ipython.org/ipython-doc/dev/interactive/cm_keyboard.html" target="_blank">Editor Shortcuts</a></li>
213 <li class="divider"></li>
223 <li class="divider"></li>
214 <li><a href="http://docs.python.org" target="_blank">Python</a></li>
224 <li><a href="http://docs.python.org" target="_blank">Python</a></li>
215 <li><a href="http://docs.scipy.org/doc/numpy/reference/" target="_blank">NumPy</a></li>
225 <li><a href="http://docs.scipy.org/doc/numpy/reference/" target="_blank">NumPy</a></li>
216 <li><a href="http://docs.scipy.org/doc/scipy/reference/" target="_blank">SciPy</a></li>
226 <li><a href="http://docs.scipy.org/doc/scipy/reference/" target="_blank">SciPy</a></li>
217 <li><a href="http://matplotlib.org/" target="_blank">Matplotlib</a></li>
227 <li><a href="http://matplotlib.org/" target="_blank">Matplotlib</a></li>
218 <li><a href="http://docs.sympy.org/dev/index.html" target="_blank">SymPy</a></li>
228 <li><a href="http://docs.sympy.org/dev/index.html" target="_blank">SymPy</a></li>
219 <li><a href="http://pandas.pydata.org/pandas-docs/stable/" target="_blank">pandas</a></li>
229 <li><a href="http://pandas.pydata.org/pandas-docs/stable/" target="_blank">pandas</a></li>
220 </ul>
230 </ul>
221 </li>
231 </li>
222 </ul>
232 </ul>
223 <div id="notification_area"></div>
233 <div id="notification_area"></div>
224 </div>
234 </div>
225 </div>
235 </div>
226 </div>
236 </div>
227 </div>
237 </div>
228 <div id="maintoolbar" class="navbar">
238 <div id="maintoolbar" class="navbar">
229 <div class="toolbar-inner navbar-inner navbar-nobg">
239 <div class="toolbar-inner navbar-inner navbar-nobg">
230 <div id="maintoolbar-container" class="container"></div>
240 <div id="maintoolbar-container" class="container"></div>
231 </div>
241 </div>
232 </div>
242 </div>
233 </div>
243 </div>
234
244
235 <div id="ipython-main-app">
245 <div id="ipython-main-app">
236
246
237 <div id="notebook_panel">
247 <div id="notebook_panel">
238 <div id="notebook"></div>
248 <div id="notebook"></div>
239 <div id="pager_splitter"></div>
249 <div id="pager_splitter"></div>
240 <div id="pager">
250 <div id="pager">
241 <div id='pager_button_area'>
251 <div id='pager_button_area'>
242 </div>
252 </div>
243 <div id="pager-container" class="container"></div>
253 <div id="pager-container" class="container"></div>
244 </div>
254 </div>
245 </div>
255 </div>
246
256
247 </div>
257 </div>
248 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
258 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
249
259
250
260
251 {% endblock %}
261 {% endblock %}
252
262
253
263
254 {% block script %}
264 {% block script %}
255
265
256 {{super()}}
266 {{super()}}
257
267
258 <script src="{{ static_url("components/codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
268 <script src="{{ static_url("components/codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
259 <script type="text/javascript">
269 <script type="text/javascript">
260 CodeMirror.modeURL = "{{ static_url("components/codemirror/mode/%N/%N.js") }}";
270 CodeMirror.modeURL = "{{ static_url("components/codemirror/mode/%N/%N.js") }}";
261 </script>
271 </script>
262 <script src="{{ static_url("components/codemirror/addon/mode/loadmode.js") }}" charset="utf-8"></script>
272 <script src="{{ static_url("components/codemirror/addon/mode/loadmode.js") }}" charset="utf-8"></script>
263 <script src="{{ static_url("components/codemirror/addon/mode/multiplex.js") }}" charset="utf-8"></script>
273 <script src="{{ static_url("components/codemirror/addon/mode/multiplex.js") }}" charset="utf-8"></script>
264 <script src="{{ static_url("components/codemirror/addon/mode/overlay.js") }}" charset="utf-8"></script>
274 <script src="{{ static_url("components/codemirror/addon/mode/overlay.js") }}" charset="utf-8"></script>
265 <script src="{{ static_url("components/codemirror/addon/edit/matchbrackets.js") }}" charset="utf-8"></script>
275 <script src="{{ static_url("components/codemirror/addon/edit/matchbrackets.js") }}" charset="utf-8"></script>
266 <script src="{{ static_url("components/codemirror/addon/comment/comment.js") }}" charset="utf-8"></script>
276 <script src="{{ static_url("components/codemirror/addon/comment/comment.js") }}" charset="utf-8"></script>
267 <script src="{{ static_url("components/codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
277 <script src="{{ static_url("components/codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
268 <script src="{{ static_url("components/codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
278 <script src="{{ static_url("components/codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
269 <script src="{{ static_url("components/codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
279 <script src="{{ static_url("components/codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
270 <script src="{{ static_url("components/codemirror/mode/css/css.js") }}" charset="utf-8"></script>
280 <script src="{{ static_url("components/codemirror/mode/css/css.js") }}" charset="utf-8"></script>
271 <script src="{{ static_url("components/codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
281 <script src="{{ static_url("components/codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
272 <script src="{{ static_url("components/codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
282 <script src="{{ static_url("components/codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
273 <script src="{{ static_url("components/codemirror/mode/gfm/gfm.js") }}" charset="utf-8"></script>
283 <script src="{{ static_url("components/codemirror/mode/gfm/gfm.js") }}" charset="utf-8"></script>
274 <script src="{{ static_url("components/codemirror/mode/python/python.js") }}" charset="utf-8"></script>
284 <script src="{{ static_url("components/codemirror/mode/python/python.js") }}" charset="utf-8"></script>
275 <script src="{{ static_url("notebook/js/codemirror-ipython.js") }}" charset="utf-8"></script>
285 <script src="{{ static_url("notebook/js/codemirror-ipython.js") }}" charset="utf-8"></script>
276
286
277 <script src="{{ static_url("components/highlight.js/build/highlight.pack.js") }}" charset="utf-8"></script>
287 <script src="{{ static_url("components/highlight.js/build/highlight.pack.js") }}" charset="utf-8"></script>
278
288
279 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
289 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
280
290
281 <script src="{{ static_url("base/js/events.js") }}" type="text/javascript" charset="utf-8"></script>
291 <script src="{{ static_url("base/js/events.js") }}" type="text/javascript" charset="utf-8"></script>
282 <script src="{{ static_url("base/js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
292 <script src="{{ static_url("base/js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
283 <script src="{{ static_url("base/js/dialog.js") }}" type="text/javascript" charset="utf-8"></script>
293 <script src="{{ static_url("base/js/dialog.js") }}" type="text/javascript" charset="utf-8"></script>
284 <script src="{{ static_url("services/kernels/js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
294 <script src="{{ static_url("services/kernels/js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
285 <script src="{{ static_url("services/kernels/js/comm.js") }}" type="text/javascript" charset="utf-8"></script>
295 <script src="{{ static_url("services/kernels/js/comm.js") }}" type="text/javascript" charset="utf-8"></script>
286 <script src="{{ static_url("services/sessions/js/session.js") }}" type="text/javascript" charset="utf-8"></script>
296 <script src="{{ static_url("services/sessions/js/session.js") }}" type="text/javascript" charset="utf-8"></script>
287 <script src="{{ static_url("notebook/js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
297 <script src="{{ static_url("notebook/js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
288 <script src="{{ static_url("notebook/js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
298 <script src="{{ static_url("notebook/js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
289 <script src="{{ static_url("notebook/js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
299 <script src="{{ static_url("notebook/js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
290 <script src="{{ static_url("notebook/js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
300 <script src="{{ static_url("notebook/js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
291 <script src="{{ static_url("notebook/js/celltoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
301 <script src="{{ static_url("notebook/js/celltoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
292 <script src="{{ static_url("notebook/js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
302 <script src="{{ static_url("notebook/js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
293 <script src="{{ static_url("notebook/js/completer.js") }}" type="text/javascript" charset="utf-8"></script>
303 <script src="{{ static_url("notebook/js/completer.js") }}" type="text/javascript" charset="utf-8"></script>
294 <script src="{{ static_url("notebook/js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
304 <script src="{{ static_url("notebook/js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
295 <script src="{{ static_url("notebook/js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
305 <script src="{{ static_url("notebook/js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
296 <script src="{{ static_url("notebook/js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
306 <script src="{{ static_url("notebook/js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
297 <script src="{{ static_url("notebook/js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
307 <script src="{{ static_url("notebook/js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
298 <script src="{{ static_url("notebook/js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
308 <script src="{{ static_url("notebook/js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
299 <script src="{{ static_url("notebook/js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
309 <script src="{{ static_url("notebook/js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
300 <script src="{{ static_url("notebook/js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
310 <script src="{{ static_url("notebook/js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
301 <script src="{{ static_url("notebook/js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
311 <script src="{{ static_url("notebook/js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
302 <script src="{{ static_url("notebook/js/keyboardmanager.js") }}" type="text/javascript" charset="utf-8"></script>
312 <script src="{{ static_url("notebook/js/keyboardmanager.js") }}" type="text/javascript" charset="utf-8"></script>
303 <script src="{{ static_url("notebook/js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
313 <script src="{{ static_url("notebook/js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
304 <script src="{{ static_url("notebook/js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
314 <script src="{{ static_url("notebook/js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
305 <script src="{{ static_url("notebook/js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>
315 <script src="{{ static_url("notebook/js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>
306 <script src="{{ static_url("notebook/js/config.js") }}" type="text/javascript" charset="utf-8"></script>
316 <script src="{{ static_url("notebook/js/config.js") }}" type="text/javascript" charset="utf-8"></script>
307 <script src="{{ static_url("notebook/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
317 <script src="{{ static_url("notebook/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
308
318
309 <script src="{{ static_url("notebook/js/contexthint.js") }}" charset="utf-8"></script>
319 <script src="{{ static_url("notebook/js/contexthint.js") }}" charset="utf-8"></script>
310
320
311 <script src="{{ static_url("notebook/js/celltoolbarpresets/default.js") }}" type="text/javascript" charset="utf-8"></script>
321 <script src="{{ static_url("notebook/js/celltoolbarpresets/default.js") }}" type="text/javascript" charset="utf-8"></script>
312 <script src="{{ static_url("notebook/js/celltoolbarpresets/rawcell.js") }}" type="text/javascript" charset="utf-8"></script>
322 <script src="{{ static_url("notebook/js/celltoolbarpresets/rawcell.js") }}" type="text/javascript" charset="utf-8"></script>
313 <script src="{{ static_url("notebook/js/celltoolbarpresets/slideshow.js") }}" type="text/javascript" charset="utf-8"></script>
323 <script src="{{ static_url("notebook/js/celltoolbarpresets/slideshow.js") }}" type="text/javascript" charset="utf-8"></script>
314
324
315 {% endblock %}
325 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now