##// END OF EJS Templates
Don't allow edit mode up arrow to continue past index == 0
Jonathan Frederic -
Show More
@@ -1,786 +1,787
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, select below',
91 help : 'run cell, select below',
92 help_index : 'ba',
92 help_index : 'ba',
93 handler : function (event) {
93 handler : function (event) {
94 IPython.notebook.execute_cell_and_select_below();
94 IPython.notebook.execute_cell_and_select_below();
95 return false;
95 return false;
96 }
96 }
97 },
97 },
98 'ctrl+enter' : {
98 'ctrl+enter' : {
99 help : 'run cell',
99 help : 'run cell',
100 help_index : 'bb',
100 help_index : 'bb',
101 handler : function (event) {
101 handler : function (event) {
102 IPython.notebook.execute_cell();
102 IPython.notebook.execute_cell();
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 index = IPython.notebook.get_selected_index();
165 var cell = IPython.notebook.get_selected_cell();
166 var cell = IPython.notebook.get_selected_cell();
166 if (cell && cell.at_top()) {
167 if (index !== 0 && cell && cell.at_top()) {
167 event.preventDefault();
168 event.preventDefault();
168 IPython.notebook.command_mode();
169 IPython.notebook.command_mode();
169 IPython.notebook.select_prev();
170 IPython.notebook.select_prev();
170 IPython.notebook.edit_mode();
171 IPython.notebook.edit_mode();
171 return false;
172 return false;
172 }
173 }
173 }
174 }
174 },
175 },
175 'down' : {
176 'down' : {
176 help : '',
177 help : '',
177 help_index : '',
178 help_index : '',
178 handler : function (event) {
179 handler : function (event) {
179 var cell = IPython.notebook.get_selected_cell();
180 var cell = IPython.notebook.get_selected_cell();
180 if (cell && cell.at_bottom()) {
181 if (cell && cell.at_bottom()) {
181 event.preventDefault();
182 event.preventDefault();
182 IPython.notebook.command_mode();
183 IPython.notebook.command_mode();
183 IPython.notebook.select_next();
184 IPython.notebook.select_next();
184 IPython.notebook.edit_mode();
185 IPython.notebook.edit_mode();
185 return false;
186 return false;
186 }
187 }
187 }
188 }
188 },
189 },
189 'alt+-' : {
190 'alt+-' : {
190 help : 'split cell',
191 help : 'split cell',
191 help_index : 'ea',
192 help_index : 'ea',
192 handler : function (event) {
193 handler : function (event) {
193 IPython.notebook.split_cell();
194 IPython.notebook.split_cell();
194 return false;
195 return false;
195 }
196 }
196 },
197 },
197 'alt+subtract' : {
198 'alt+subtract' : {
198 help : '',
199 help : '',
199 help_index : 'eb',
200 help_index : 'eb',
200 handler : function (event) {
201 handler : function (event) {
201 IPython.notebook.split_cell();
202 IPython.notebook.split_cell();
202 return false;
203 return false;
203 }
204 }
204 },
205 },
205 'tab' : {
206 'tab' : {
206 help : 'indent or complete',
207 help : 'indent or complete',
207 help_index : 'ec',
208 help_index : 'ec',
208 },
209 },
209 'shift+tab' : {
210 'shift+tab' : {
210 help : 'tooltip',
211 help : 'tooltip',
211 help_index : 'ed',
212 help_index : 'ed',
212 },
213 },
213 };
214 };
214
215
215 if (platform === 'MacOS') {
216 if (platform === 'MacOS') {
216 default_edit_shortcuts['cmd+/'] =
217 default_edit_shortcuts['cmd+/'] =
217 {
218 {
218 help : 'toggle comment',
219 help : 'toggle comment',
219 help_index : 'ee'
220 help_index : 'ee'
220 };
221 };
221 default_edit_shortcuts['cmd+]'] =
222 default_edit_shortcuts['cmd+]'] =
222 {
223 {
223 help : 'indent',
224 help : 'indent',
224 help_index : 'ef'
225 help_index : 'ef'
225 };
226 };
226 default_edit_shortcuts['cmd+['] =
227 default_edit_shortcuts['cmd+['] =
227 {
228 {
228 help : 'dedent',
229 help : 'dedent',
229 help_index : 'eg'
230 help_index : 'eg'
230 };
231 };
231 } else {
232 } else {
232 default_edit_shortcuts['ctrl+/'] =
233 default_edit_shortcuts['ctrl+/'] =
233 {
234 {
234 help : 'toggle comment',
235 help : 'toggle comment',
235 help_index : 'ee'
236 help_index : 'ee'
236 };
237 };
237 default_edit_shortcuts['ctrl+]'] =
238 default_edit_shortcuts['ctrl+]'] =
238 {
239 {
239 help : 'indent',
240 help : 'indent',
240 help_index : 'ef'
241 help_index : 'ef'
241 };
242 };
242 default_edit_shortcuts['ctrl+['] =
243 default_edit_shortcuts['ctrl+['] =
243 {
244 {
244 help : 'dedent',
245 help : 'dedent',
245 help_index : 'eg'
246 help_index : 'eg'
246 };
247 };
247 }
248 }
248
249
249 // Command mode defaults
250 // Command mode defaults
250
251
251 var default_command_shortcuts = {
252 var default_command_shortcuts = {
252 'enter' : {
253 'enter' : {
253 help : 'edit mode',
254 help : 'edit mode',
254 help_index : 'aa',
255 help_index : 'aa',
255 handler : function (event) {
256 handler : function (event) {
256 IPython.notebook.edit_mode();
257 IPython.notebook.edit_mode();
257 return false;
258 return false;
258 }
259 }
259 },
260 },
260 'up' : {
261 'up' : {
261 help : 'select previous cell',
262 help : 'select previous cell',
262 help_index : 'da',
263 help_index : 'da',
263 handler : function (event) {
264 handler : function (event) {
264 var index = IPython.notebook.get_selected_index();
265 var index = IPython.notebook.get_selected_index();
265 if (index !== 0 && index !== null) {
266 if (index !== 0 && index !== null) {
266 IPython.notebook.select_prev();
267 IPython.notebook.select_prev();
267 IPython.notebook.focus_cell();
268 IPython.notebook.focus_cell();
268 }
269 }
269 return false;
270 return false;
270 }
271 }
271 },
272 },
272 'down' : {
273 'down' : {
273 help : 'select next cell',
274 help : 'select next cell',
274 help_index : 'db',
275 help_index : 'db',
275 handler : function (event) {
276 handler : function (event) {
276 var index = IPython.notebook.get_selected_index();
277 var index = IPython.notebook.get_selected_index();
277 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
278 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
278 IPython.notebook.select_next();
279 IPython.notebook.select_next();
279 IPython.notebook.focus_cell();
280 IPython.notebook.focus_cell();
280 }
281 }
281 return false;
282 return false;
282 }
283 }
283 },
284 },
284 'k' : {
285 'k' : {
285 help : 'select previous cell',
286 help : 'select previous cell',
286 help_index : 'dc',
287 help_index : 'dc',
287 handler : function (event) {
288 handler : function (event) {
288 var index = IPython.notebook.get_selected_index();
289 var index = IPython.notebook.get_selected_index();
289 if (index !== 0 && index !== null) {
290 if (index !== 0 && index !== null) {
290 IPython.notebook.select_prev();
291 IPython.notebook.select_prev();
291 IPython.notebook.focus_cell();
292 IPython.notebook.focus_cell();
292 }
293 }
293 return false;
294 return false;
294 }
295 }
295 },
296 },
296 'j' : {
297 'j' : {
297 help : 'select next cell',
298 help : 'select next cell',
298 help_index : 'dd',
299 help_index : 'dd',
299 handler : function (event) {
300 handler : function (event) {
300 var index = IPython.notebook.get_selected_index();
301 var index = IPython.notebook.get_selected_index();
301 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
302 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
302 IPython.notebook.select_next();
303 IPython.notebook.select_next();
303 IPython.notebook.focus_cell();
304 IPython.notebook.focus_cell();
304 }
305 }
305 return false;
306 return false;
306 }
307 }
307 },
308 },
308 'x' : {
309 'x' : {
309 help : 'cut cell',
310 help : 'cut cell',
310 help_index : 'ee',
311 help_index : 'ee',
311 handler : function (event) {
312 handler : function (event) {
312 IPython.notebook.cut_cell();
313 IPython.notebook.cut_cell();
313 return false;
314 return false;
314 }
315 }
315 },
316 },
316 'c' : {
317 'c' : {
317 help : 'copy cell',
318 help : 'copy cell',
318 help_index : 'ef',
319 help_index : 'ef',
319 handler : function (event) {
320 handler : function (event) {
320 IPython.notebook.copy_cell();
321 IPython.notebook.copy_cell();
321 return false;
322 return false;
322 }
323 }
323 },
324 },
324 'shift+v' : {
325 'shift+v' : {
325 help : 'paste cell above',
326 help : 'paste cell above',
326 help_index : 'eg',
327 help_index : 'eg',
327 handler : function (event) {
328 handler : function (event) {
328 IPython.notebook.paste_cell_above();
329 IPython.notebook.paste_cell_above();
329 return false;
330 return false;
330 }
331 }
331 },
332 },
332 'v' : {
333 'v' : {
333 help : 'paste cell below',
334 help : 'paste cell below',
334 help_index : 'eh',
335 help_index : 'eh',
335 handler : function (event) {
336 handler : function (event) {
336 IPython.notebook.paste_cell_below();
337 IPython.notebook.paste_cell_below();
337 return false;
338 return false;
338 }
339 }
339 },
340 },
340 'd' : {
341 'd' : {
341 help : 'delete cell (press twice)',
342 help : 'delete cell (press twice)',
342 help_index : 'ej',
343 help_index : 'ej',
343 count: 2,
344 count: 2,
344 handler : function (event) {
345 handler : function (event) {
345 IPython.notebook.delete_cell();
346 IPython.notebook.delete_cell();
346 return false;
347 return false;
347 }
348 }
348 },
349 },
349 'a' : {
350 'a' : {
350 help : 'insert cell above',
351 help : 'insert cell above',
351 help_index : 'ec',
352 help_index : 'ec',
352 handler : function (event) {
353 handler : function (event) {
353 IPython.notebook.insert_cell_above('code');
354 IPython.notebook.insert_cell_above('code');
354 IPython.notebook.select_prev();
355 IPython.notebook.select_prev();
355 IPython.notebook.focus_cell();
356 IPython.notebook.focus_cell();
356 return false;
357 return false;
357 }
358 }
358 },
359 },
359 'b' : {
360 'b' : {
360 help : 'insert cell below',
361 help : 'insert cell below',
361 help_index : 'ed',
362 help_index : 'ed',
362 handler : function (event) {
363 handler : function (event) {
363 IPython.notebook.insert_cell_below('code');
364 IPython.notebook.insert_cell_below('code');
364 IPython.notebook.select_next();
365 IPython.notebook.select_next();
365 IPython.notebook.focus_cell();
366 IPython.notebook.focus_cell();
366 return false;
367 return false;
367 }
368 }
368 },
369 },
369 'y' : {
370 'y' : {
370 help : 'to code',
371 help : 'to code',
371 help_index : 'ca',
372 help_index : 'ca',
372 handler : function (event) {
373 handler : function (event) {
373 IPython.notebook.to_code();
374 IPython.notebook.to_code();
374 return false;
375 return false;
375 }
376 }
376 },
377 },
377 'm' : {
378 'm' : {
378 help : 'to markdown',
379 help : 'to markdown',
379 help_index : 'cb',
380 help_index : 'cb',
380 handler : function (event) {
381 handler : function (event) {
381 IPython.notebook.to_markdown();
382 IPython.notebook.to_markdown();
382 return false;
383 return false;
383 }
384 }
384 },
385 },
385 'r' : {
386 'r' : {
386 help : 'to raw',
387 help : 'to raw',
387 help_index : 'cc',
388 help_index : 'cc',
388 handler : function (event) {
389 handler : function (event) {
389 IPython.notebook.to_raw();
390 IPython.notebook.to_raw();
390 return false;
391 return false;
391 }
392 }
392 },
393 },
393 '1' : {
394 '1' : {
394 help : 'to heading 1',
395 help : 'to heading 1',
395 help_index : 'cd',
396 help_index : 'cd',
396 handler : function (event) {
397 handler : function (event) {
397 IPython.notebook.to_heading(undefined, 1);
398 IPython.notebook.to_heading(undefined, 1);
398 return false;
399 return false;
399 }
400 }
400 },
401 },
401 '2' : {
402 '2' : {
402 help : 'to heading 2',
403 help : 'to heading 2',
403 help_index : 'ce',
404 help_index : 'ce',
404 handler : function (event) {
405 handler : function (event) {
405 IPython.notebook.to_heading(undefined, 2);
406 IPython.notebook.to_heading(undefined, 2);
406 return false;
407 return false;
407 }
408 }
408 },
409 },
409 '3' : {
410 '3' : {
410 help : 'to heading 3',
411 help : 'to heading 3',
411 help_index : 'cf',
412 help_index : 'cf',
412 handler : function (event) {
413 handler : function (event) {
413 IPython.notebook.to_heading(undefined, 3);
414 IPython.notebook.to_heading(undefined, 3);
414 return false;
415 return false;
415 }
416 }
416 },
417 },
417 '4' : {
418 '4' : {
418 help : 'to heading 4',
419 help : 'to heading 4',
419 help_index : 'cg',
420 help_index : 'cg',
420 handler : function (event) {
421 handler : function (event) {
421 IPython.notebook.to_heading(undefined, 4);
422 IPython.notebook.to_heading(undefined, 4);
422 return false;
423 return false;
423 }
424 }
424 },
425 },
425 '5' : {
426 '5' : {
426 help : 'to heading 5',
427 help : 'to heading 5',
427 help_index : 'ch',
428 help_index : 'ch',
428 handler : function (event) {
429 handler : function (event) {
429 IPython.notebook.to_heading(undefined, 5);
430 IPython.notebook.to_heading(undefined, 5);
430 return false;
431 return false;
431 }
432 }
432 },
433 },
433 '6' : {
434 '6' : {
434 help : 'to heading 6',
435 help : 'to heading 6',
435 help_index : 'ci',
436 help_index : 'ci',
436 handler : function (event) {
437 handler : function (event) {
437 IPython.notebook.to_heading(undefined, 6);
438 IPython.notebook.to_heading(undefined, 6);
438 return false;
439 return false;
439 }
440 }
440 },
441 },
441 'o' : {
442 'o' : {
442 help : 'toggle output',
443 help : 'toggle output',
443 help_index : 'gb',
444 help_index : 'gb',
444 handler : function (event) {
445 handler : function (event) {
445 IPython.notebook.toggle_output();
446 IPython.notebook.toggle_output();
446 return false;
447 return false;
447 }
448 }
448 },
449 },
449 'shift+o' : {
450 'shift+o' : {
450 help : 'toggle output scrolling',
451 help : 'toggle output scrolling',
451 help_index : 'gc',
452 help_index : 'gc',
452 handler : function (event) {
453 handler : function (event) {
453 IPython.notebook.toggle_output_scroll();
454 IPython.notebook.toggle_output_scroll();
454 return false;
455 return false;
455 }
456 }
456 },
457 },
457 's' : {
458 's' : {
458 help : 'save notebook',
459 help : 'save notebook',
459 help_index : 'fa',
460 help_index : 'fa',
460 handler : function (event) {
461 handler : function (event) {
461 IPython.notebook.save_checkpoint();
462 IPython.notebook.save_checkpoint();
462 return false;
463 return false;
463 }
464 }
464 },
465 },
465 'ctrl+j' : {
466 'ctrl+j' : {
466 help : 'move cell down',
467 help : 'move cell down',
467 help_index : 'eb',
468 help_index : 'eb',
468 handler : function (event) {
469 handler : function (event) {
469 IPython.notebook.move_cell_down();
470 IPython.notebook.move_cell_down();
470 return false;
471 return false;
471 }
472 }
472 },
473 },
473 'ctrl+k' : {
474 'ctrl+k' : {
474 help : 'move cell up',
475 help : 'move cell up',
475 help_index : 'ea',
476 help_index : 'ea',
476 handler : function (event) {
477 handler : function (event) {
477 IPython.notebook.move_cell_up();
478 IPython.notebook.move_cell_up();
478 return false;
479 return false;
479 }
480 }
480 },
481 },
481 'l' : {
482 'l' : {
482 help : 'toggle line numbers',
483 help : 'toggle line numbers',
483 help_index : 'ga',
484 help_index : 'ga',
484 handler : function (event) {
485 handler : function (event) {
485 IPython.notebook.cell_toggle_line_numbers();
486 IPython.notebook.cell_toggle_line_numbers();
486 return false;
487 return false;
487 }
488 }
488 },
489 },
489 'i' : {
490 'i' : {
490 help : 'interrupt kernel (press twice)',
491 help : 'interrupt kernel (press twice)',
491 help_index : 'ha',
492 help_index : 'ha',
492 count: 2,
493 count: 2,
493 handler : function (event) {
494 handler : function (event) {
494 IPython.notebook.kernel.interrupt();
495 IPython.notebook.kernel.interrupt();
495 return false;
496 return false;
496 }
497 }
497 },
498 },
498 '0' : {
499 '0' : {
499 help : 'restart kernel (press twice)',
500 help : 'restart kernel (press twice)',
500 help_index : 'hb',
501 help_index : 'hb',
501 count: 2,
502 count: 2,
502 handler : function (event) {
503 handler : function (event) {
503 IPython.notebook.restart_kernel();
504 IPython.notebook.restart_kernel();
504 return false;
505 return false;
505 }
506 }
506 },
507 },
507 'h' : {
508 'h' : {
508 help : 'keyboard shortcuts',
509 help : 'keyboard shortcuts',
509 help_index : 'ge',
510 help_index : 'ge',
510 handler : function (event) {
511 handler : function (event) {
511 IPython.quick_help.show_keyboard_shortcuts();
512 IPython.quick_help.show_keyboard_shortcuts();
512 return false;
513 return false;
513 }
514 }
514 },
515 },
515 'z' : {
516 'z' : {
516 help : 'undo last delete',
517 help : 'undo last delete',
517 help_index : 'ei',
518 help_index : 'ei',
518 handler : function (event) {
519 handler : function (event) {
519 IPython.notebook.undelete_cell();
520 IPython.notebook.undelete_cell();
520 return false;
521 return false;
521 }
522 }
522 },
523 },
523 'shift+m' : {
524 'shift+m' : {
524 help : 'merge cell below',
525 help : 'merge cell below',
525 help_index : 'ek',
526 help_index : 'ek',
526 handler : function (event) {
527 handler : function (event) {
527 IPython.notebook.merge_cell_below();
528 IPython.notebook.merge_cell_below();
528 return false;
529 return false;
529 }
530 }
530 },
531 },
531 'q' : {
532 'q' : {
532 help : 'close pager',
533 help : 'close pager',
533 help_index : 'gd',
534 help_index : 'gd',
534 handler : function (event) {
535 handler : function (event) {
535 IPython.pager.collapse();
536 IPython.pager.collapse();
536 return false;
537 return false;
537 }
538 }
538 },
539 },
539 };
540 };
540
541
541
542
542 // Shortcut manager class
543 // Shortcut manager class
543
544
544 var ShortcutManager = function (delay) {
545 var ShortcutManager = function (delay) {
545 this._shortcuts = {};
546 this._shortcuts = {};
546 this._counts = {};
547 this._counts = {};
547 this._timers = {};
548 this._timers = {};
548 this.delay = delay || 800; // delay in milliseconds
549 this.delay = delay || 800; // delay in milliseconds
549 };
550 };
550
551
551 ShortcutManager.prototype.help = function () {
552 ShortcutManager.prototype.help = function () {
552 var help = [];
553 var help = [];
553 for (var shortcut in this._shortcuts) {
554 for (var shortcut in this._shortcuts) {
554 var help_string = this._shortcuts[shortcut].help;
555 var help_string = this._shortcuts[shortcut].help;
555 var help_index = this._shortcuts[shortcut].help_index;
556 var help_index = this._shortcuts[shortcut].help_index;
556 if (help_string) {
557 if (help_string) {
557 if (platform === 'MacOS') {
558 if (platform === 'MacOS') {
558 shortcut = shortcut.replace('meta', 'cmd');
559 shortcut = shortcut.replace('meta', 'cmd');
559 }
560 }
560 help.push({
561 help.push({
561 shortcut: shortcut,
562 shortcut: shortcut,
562 help: help_string,
563 help: help_string,
563 help_index: help_index}
564 help_index: help_index}
564 );
565 );
565 }
566 }
566 }
567 }
567 help.sort(function (a, b) {
568 help.sort(function (a, b) {
568 if (a.help_index > b.help_index)
569 if (a.help_index > b.help_index)
569 return 1;
570 return 1;
570 if (a.help_index < b.help_index)
571 if (a.help_index < b.help_index)
571 return -1;
572 return -1;
572 return 0;
573 return 0;
573 });
574 });
574 return help;
575 return help;
575 };
576 };
576
577
577 ShortcutManager.prototype.normalize_key = function (key) {
578 ShortcutManager.prototype.normalize_key = function (key) {
578 return inv_keycodes[keycodes[key]];
579 return inv_keycodes[keycodes[key]];
579 };
580 };
580
581
581 ShortcutManager.prototype.normalize_shortcut = function (shortcut) {
582 ShortcutManager.prototype.normalize_shortcut = function (shortcut) {
582 // Sort a sequence of + separated modifiers into the order alt+ctrl+meta+shift
583 // Sort a sequence of + separated modifiers into the order alt+ctrl+meta+shift
583 shortcut = shortcut.replace('cmd', 'meta').toLowerCase();
584 shortcut = shortcut.replace('cmd', 'meta').toLowerCase();
584 var values = shortcut.split("+");
585 var values = shortcut.split("+");
585 if (values.length === 1) {
586 if (values.length === 1) {
586 return this.normalize_key(values[0]);
587 return this.normalize_key(values[0]);
587 } else {
588 } else {
588 var modifiers = values.slice(0,-1);
589 var modifiers = values.slice(0,-1);
589 var key = this.normalize_key(values[values.length-1]);
590 var key = this.normalize_key(values[values.length-1]);
590 modifiers.sort();
591 modifiers.sort();
591 return modifiers.join('+') + '+' + key;
592 return modifiers.join('+') + '+' + key;
592 }
593 }
593 };
594 };
594
595
595 ShortcutManager.prototype.event_to_shortcut = function (event) {
596 ShortcutManager.prototype.event_to_shortcut = function (event) {
596 // Convert a jQuery keyboard event to a strong based keyboard shortcut
597 // Convert a jQuery keyboard event to a strong based keyboard shortcut
597 var shortcut = '';
598 var shortcut = '';
598 var key = inv_keycodes[event.which];
599 var key = inv_keycodes[event.which];
599 if (event.altKey && key !== 'alt') {shortcut += 'alt+';}
600 if (event.altKey && key !== 'alt') {shortcut += 'alt+';}
600 if (event.ctrlKey && key !== 'ctrl') {shortcut += 'ctrl+';}
601 if (event.ctrlKey && key !== 'ctrl') {shortcut += 'ctrl+';}
601 if (event.metaKey && key !== 'meta') {shortcut += 'meta+';}
602 if (event.metaKey && key !== 'meta') {shortcut += 'meta+';}
602 if (event.shiftKey && key !== 'shift') {shortcut += 'shift+';}
603 if (event.shiftKey && key !== 'shift') {shortcut += 'shift+';}
603 shortcut += key;
604 shortcut += key;
604 return shortcut;
605 return shortcut;
605 };
606 };
606
607
607 ShortcutManager.prototype.clear_shortcuts = function () {
608 ShortcutManager.prototype.clear_shortcuts = function () {
608 this._shortcuts = {};
609 this._shortcuts = {};
609 };
610 };
610
611
611 ShortcutManager.prototype.add_shortcut = function (shortcut, data) {
612 ShortcutManager.prototype.add_shortcut = function (shortcut, data) {
612 if (typeof(data) === 'function') {
613 if (typeof(data) === 'function') {
613 data = {help: '', help_index: '', handler: data};
614 data = {help: '', help_index: '', handler: data};
614 }
615 }
615 data.help_index = data.help_index || '';
616 data.help_index = data.help_index || '';
616 data.help = data.help || '';
617 data.help = data.help || '';
617 data.count = data.count || 1;
618 data.count = data.count || 1;
618 if (data.help_index === '') {
619 if (data.help_index === '') {
619 data.help_index = 'zz';
620 data.help_index = 'zz';
620 }
621 }
621 shortcut = this.normalize_shortcut(shortcut);
622 shortcut = this.normalize_shortcut(shortcut);
622 this._counts[shortcut] = 0;
623 this._counts[shortcut] = 0;
623 this._shortcuts[shortcut] = data;
624 this._shortcuts[shortcut] = data;
624 };
625 };
625
626
626 ShortcutManager.prototype.add_shortcuts = function (data) {
627 ShortcutManager.prototype.add_shortcuts = function (data) {
627 for (var shortcut in data) {
628 for (var shortcut in data) {
628 this.add_shortcut(shortcut, data[shortcut]);
629 this.add_shortcut(shortcut, data[shortcut]);
629 }
630 }
630 };
631 };
631
632
632 ShortcutManager.prototype.remove_shortcut = function (shortcut) {
633 ShortcutManager.prototype.remove_shortcut = function (shortcut) {
633 shortcut = this.normalize_shortcut(shortcut);
634 shortcut = this.normalize_shortcut(shortcut);
634 delete this._counts[shortcut];
635 delete this._counts[shortcut];
635 delete this._shortcuts[shortcut];
636 delete this._shortcuts[shortcut];
636 };
637 };
637
638
638 ShortcutManager.prototype.count_handler = function (shortcut, event, data) {
639 ShortcutManager.prototype.count_handler = function (shortcut, event, data) {
639 var that = this;
640 var that = this;
640 var c = this._counts;
641 var c = this._counts;
641 var t = this._timers;
642 var t = this._timers;
642 var timer = null;
643 var timer = null;
643 if (c[shortcut] === data.count-1) {
644 if (c[shortcut] === data.count-1) {
644 c[shortcut] = 0;
645 c[shortcut] = 0;
645 timer = t[shortcut];
646 timer = t[shortcut];
646 if (timer) {clearTimeout(timer); delete t[shortcut];}
647 if (timer) {clearTimeout(timer); delete t[shortcut];}
647 return data.handler(event);
648 return data.handler(event);
648 } else {
649 } else {
649 c[shortcut] = c[shortcut] + 1;
650 c[shortcut] = c[shortcut] + 1;
650 timer = setTimeout(function () {
651 timer = setTimeout(function () {
651 c[shortcut] = 0;
652 c[shortcut] = 0;
652 }, that.delay);
653 }, that.delay);
653 t[shortcut] = timer;
654 t[shortcut] = timer;
654 }
655 }
655 return false;
656 return false;
656 };
657 };
657
658
658 ShortcutManager.prototype.call_handler = function (event) {
659 ShortcutManager.prototype.call_handler = function (event) {
659 var shortcut = this.event_to_shortcut(event);
660 var shortcut = this.event_to_shortcut(event);
660 var data = this._shortcuts[shortcut];
661 var data = this._shortcuts[shortcut];
661 if (data) {
662 if (data) {
662 var handler = data.handler;
663 var handler = data.handler;
663 if (handler) {
664 if (handler) {
664 if (data.count === 1) {
665 if (data.count === 1) {
665 return handler(event);
666 return handler(event);
666 } else if (data.count > 1) {
667 } else if (data.count > 1) {
667 return this.count_handler(shortcut, event, data);
668 return this.count_handler(shortcut, event, data);
668 }
669 }
669 }
670 }
670 }
671 }
671 return true;
672 return true;
672 };
673 };
673
674
674
675
675 // Main keyboard manager for the notebook
676 // Main keyboard manager for the notebook
676
677
677 var KeyboardManager = function () {
678 var KeyboardManager = function () {
678 this.mode = 'command';
679 this.mode = 'command';
679 this.enabled = true;
680 this.enabled = true;
680 this.bind_events();
681 this.bind_events();
681 this.command_shortcuts = new ShortcutManager();
682 this.command_shortcuts = new ShortcutManager();
682 this.command_shortcuts.add_shortcuts(default_common_shortcuts);
683 this.command_shortcuts.add_shortcuts(default_common_shortcuts);
683 this.command_shortcuts.add_shortcuts(default_command_shortcuts);
684 this.command_shortcuts.add_shortcuts(default_command_shortcuts);
684 this.edit_shortcuts = new ShortcutManager();
685 this.edit_shortcuts = new ShortcutManager();
685 this.edit_shortcuts.add_shortcuts(default_common_shortcuts);
686 this.edit_shortcuts.add_shortcuts(default_common_shortcuts);
686 this.edit_shortcuts.add_shortcuts(default_edit_shortcuts);
687 this.edit_shortcuts.add_shortcuts(default_edit_shortcuts);
687 };
688 };
688
689
689 KeyboardManager.prototype.bind_events = function () {
690 KeyboardManager.prototype.bind_events = function () {
690 var that = this;
691 var that = this;
691 $(document).keydown(function (event) {
692 $(document).keydown(function (event) {
692 return that.handle_keydown(event);
693 return that.handle_keydown(event);
693 });
694 });
694 };
695 };
695
696
696 KeyboardManager.prototype.handle_keydown = function (event) {
697 KeyboardManager.prototype.handle_keydown = function (event) {
697 var notebook = IPython.notebook;
698 var notebook = IPython.notebook;
698
699
699 if (event.which === keycodes.esc) {
700 if (event.which === keycodes.esc) {
700 // Intercept escape at highest level to avoid closing
701 // Intercept escape at highest level to avoid closing
701 // websocket connection with firefox
702 // websocket connection with firefox
702 event.preventDefault();
703 event.preventDefault();
703 }
704 }
704
705
705 if (!this.enabled) {
706 if (!this.enabled) {
706 if (event.which === keycodes.esc) {
707 if (event.which === keycodes.esc) {
707 // ESC
708 // ESC
708 notebook.command_mode();
709 notebook.command_mode();
709 return false;
710 return false;
710 }
711 }
711 return true;
712 return true;
712 }
713 }
713
714
714 if (this.mode === 'edit') {
715 if (this.mode === 'edit') {
715 return this.edit_shortcuts.call_handler(event);
716 return this.edit_shortcuts.call_handler(event);
716 } else if (this.mode === 'command') {
717 } else if (this.mode === 'command') {
717 return this.command_shortcuts.call_handler(event);
718 return this.command_shortcuts.call_handler(event);
718 }
719 }
719 return true;
720 return true;
720 };
721 };
721
722
722 KeyboardManager.prototype.edit_mode = function () {
723 KeyboardManager.prototype.edit_mode = function () {
723 this.last_mode = this.mode;
724 this.last_mode = this.mode;
724 this.mode = 'edit';
725 this.mode = 'edit';
725 };
726 };
726
727
727 KeyboardManager.prototype.command_mode = function () {
728 KeyboardManager.prototype.command_mode = function () {
728 this.last_mode = this.mode;
729 this.last_mode = this.mode;
729 this.mode = 'command';
730 this.mode = 'command';
730 };
731 };
731
732
732 KeyboardManager.prototype.enable = function () {
733 KeyboardManager.prototype.enable = function () {
733 this.enabled = true;
734 this.enabled = true;
734 };
735 };
735
736
736 KeyboardManager.prototype.disable = function () {
737 KeyboardManager.prototype.disable = function () {
737 this.enabled = false;
738 this.enabled = false;
738 };
739 };
739
740
740 KeyboardManager.prototype.register_events = function (e) {
741 KeyboardManager.prototype.register_events = function (e) {
741 var that = this;
742 var that = this;
742 var handle_focus = function () {
743 var handle_focus = function () {
743 that.disable();
744 that.disable();
744 };
745 };
745 var handle_blur = function () {
746 var handle_blur = function () {
746 that.enable();
747 that.enable();
747 };
748 };
748 e.on('focusin', handle_focus);
749 e.on('focusin', handle_focus);
749 e.on('focusout', handle_blur);
750 e.on('focusout', handle_blur);
750 // TODO: Very strange. The focusout event does not seem fire for the
751 // TODO: Very strange. The focusout event does not seem fire for the
751 // bootstrap textboxes on FF25&26... This works around that by
752 // bootstrap textboxes on FF25&26... This works around that by
752 // registering focus and blur events recursively on all inputs within
753 // registering focus and blur events recursively on all inputs within
753 // registered element.
754 // registered element.
754 e.find('input').blur(handle_blur);
755 e.find('input').blur(handle_blur);
755 e.on('DOMNodeInserted', function (event) {
756 e.on('DOMNodeInserted', function (event) {
756 var target = $(event.target);
757 var target = $(event.target);
757 if (target.is('input')) {
758 if (target.is('input')) {
758 target.blur(handle_blur);
759 target.blur(handle_blur);
759 } else {
760 } else {
760 target.find('input').blur(handle_blur);
761 target.find('input').blur(handle_blur);
761 }
762 }
762 });
763 });
763 // There are times (raw_input) where we remove the element from the DOM before
764 // There are times (raw_input) where we remove the element from the DOM before
764 // focusout is called. In this case we bind to the remove event of jQueryUI,
765 // focusout is called. In this case we bind to the remove event of jQueryUI,
765 // which gets triggered upon removal, iff it is focused at the time.
766 // which gets triggered upon removal, iff it is focused at the time.
766 // is_focused must be used to check for the case where an element within
767 // is_focused must be used to check for the case where an element within
767 // the element being removed is focused.
768 // the element being removed is focused.
768 e.on('remove', function () {
769 e.on('remove', function () {
769 if (IPython.utils.is_focused(e[0])) {
770 if (IPython.utils.is_focused(e[0])) {
770 that.enable();
771 that.enable();
771 }
772 }
772 });
773 });
773 };
774 };
774
775
775
776
776 IPython.keycodes = keycodes;
777 IPython.keycodes = keycodes;
777 IPython.inv_keycodes = inv_keycodes;
778 IPython.inv_keycodes = inv_keycodes;
778 IPython.default_common_shortcuts = default_common_shortcuts;
779 IPython.default_common_shortcuts = default_common_shortcuts;
779 IPython.default_edit_shortcuts = default_edit_shortcuts;
780 IPython.default_edit_shortcuts = default_edit_shortcuts;
780 IPython.default_command_shortcuts = default_command_shortcuts;
781 IPython.default_command_shortcuts = default_command_shortcuts;
781 IPython.ShortcutManager = ShortcutManager;
782 IPython.ShortcutManager = ShortcutManager;
782 IPython.KeyboardManager = KeyboardManager;
783 IPython.KeyboardManager = KeyboardManager;
783
784
784 return IPython;
785 return IPython;
785
786
786 }(IPython));
787 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now