##// END OF EJS Templates
Fixing delete_count logic.
Brian E. Granger -
Show More
@@ -1,623 +1,621 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Keyboard management
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13 "use strict";
14 14
15 15 // Setup global keycodes and inverse keycodes.
16 16
17 17 // See http://unixpapa.com/js/key.html for a complete description. The short of
18 18 // it is that there are different keycode sets. Firefox uses the "Mozilla keycodes"
19 19 // and Webkit/IE use the "IE keycodes". These keycode sets are mostly the same
20 20 // but have minor differences.
21 21
22 22 // These apply to Firefox, (Webkit and IE)
23 23 var _keycodes = {
24 24 'a': 65, 'b': 66, 'c': 67, 'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73,
25 25 'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81, 'r': 82,
26 26 's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, 'z': 90,
27 27 '1 !': 49, '2 @': 50, '3 #': 51, '4 $': 52, '5 %': 53, '6 ^': 54,
28 28 '7 &': 55, '8 *': 56, '9 (': 57, '0 )': 48,
29 29 '[ {': 219, '] }': 221, '` ~': 192, ', <': 188, '. >': 190, '/ ?': 191,
30 30 '\\ |': 220, '\' "': 222,
31 31 'numpad0': 96, 'numpad1': 97, 'numpad2': 98, 'numpad3': 99, 'numpad4': 100,
32 32 'numpad5': 101, 'numpad6': 102, 'numpad7': 103, 'numpad8': 104, 'numpad9': 105,
33 33 'multiply': 106, 'add': 107, 'subtract': 109, 'decimal': 110, 'divide': 111,
34 34 'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118,
35 35 'f8': 119, 'f9': 120, 'f11': 122, 'f12': 123, 'f13': 124, 'f14': 125, 'f15': 126,
36 36 'backspace': 8, 'tab': 9, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18,
37 37 'meta': 91, 'capslock': 20, 'esc': 27, 'space': 32, 'pageup': 33, 'pagedown': 34,
38 38 'end': 35, 'home': 36, 'left': 37, 'up': 38, 'right': 39, 'down': 40,
39 39 'insert': 45, 'delete': 46, 'numlock': 144,
40 40 };
41 41
42 42 // These apply to Firefox and Opera
43 43 var _mozilla_keycodes = {
44 44 '; :': 59, '= +': 61, '- _': 173, 'meta': 224
45 45 }
46 46
47 47 // This apply to Webkit and IE
48 48 var _ie_keycodes = {
49 49 '; :': 186, '= +': 187, '- _': 189,
50 50 }
51 51
52 52 var browser = IPython.utils.browser[0];
53 53
54 54 if (browser === 'Firefox' || browser === 'Opera') {
55 55 $.extend(_keycodes, _mozilla_keycodes);
56 56 } else if (browser === 'Safari' || browser === 'Chrome' || browser === 'MSIE') {
57 57 $.extend(_keycodes, _ie_keycodes);
58 58 }
59 59
60 60 var keycodes = {};
61 61 var inv_keycodes = {};
62 62 for (var name in _keycodes) {
63 63 var names = name.split(' ');
64 64 if (names.length === 1) {
65 65 var n = names[0]
66 66 keycodes[n] = _keycodes[n]
67 67 inv_keycodes[_keycodes[n]] = n
68 68 } else {
69 69 var primary = names[0];
70 70 var secondary = names[1];
71 71 keycodes[primary] = _keycodes[name]
72 72 keycodes[secondary] = _keycodes[name]
73 73 inv_keycodes[_keycodes[name]] = primary
74 74 }
75 75 }
76 76
77 77
78 78 // Default keyboard shortcuts
79 79
80 80 var default_common_shortcuts = {
81 81 'meta+s' : {
82 82 help : 'save notebook',
83 83 handler : function (event) {
84 84 IPython.notebook.save_checkpoint();
85 85 event.preventDefault();
86 86 return false;
87 87 }
88 88 },
89 89 'ctrl+s' : {
90 90 help : 'save notebook',
91 91 handler : function (event) {
92 92 IPython.notebook.save_checkpoint();
93 93 event.preventDefault();
94 94 return false;
95 95 }
96 96 },
97 97 'shift' : {
98 98 help : '',
99 99 handler : function (event) {
100 100 // ignore shift keydown
101 101 return true;
102 102 }
103 103 },
104 104 'shift+enter' : {
105 105 help : 'run cell',
106 106 handler : function (event) {
107 107 IPython.notebook.execute_cell();
108 108 return false;
109 109 }
110 110 },
111 111 'alt+enter' : {
112 112 help : 'run cell, insert below',
113 113 handler : function (event) {
114 114 IPython.notebook.execute_cell_and_insert_below();
115 115 return false;
116 116 }
117 117 },
118 118 'ctrl+enter' : {
119 119 help : 'run cell, select below',
120 120 handler : function (event) {
121 121 IPython.notebook.execute_cell_and_select_below();
122 122 return false;
123 123 }
124 124 }
125 125 }
126 126
127 127 // Edit mode defaults
128 128
129 129 var default_edit_shortcuts = {
130 130 'esc' : {
131 131 help : 'command mode',
132 132 handler : function (event) {
133 133 IPython.notebook.command_mode();
134 134 IPython.notebook.focus_cell();
135 135 return false;
136 136 }
137 137 },
138 138 'ctrl+m' : {
139 139 help : 'command mode',
140 140 handler : function (event) {
141 141 IPython.notebook.command_mode();
142 142 IPython.notebook.focus_cell();
143 143 return false;
144 144 }
145 145 },
146 146 'up' : {
147 147 help : 'select previous cell',
148 148 handler : function (event) {
149 149 var cell = IPython.notebook.get_selected_cell();
150 150 if (cell && cell.at_top()) {
151 151 event.preventDefault();
152 152 IPython.notebook.command_mode()
153 153 IPython.notebook.select_prev();
154 154 IPython.notebook.edit_mode();
155 155 return false;
156 156 };
157 157 }
158 158 },
159 159 'down' : {
160 160 help : 'select next cell',
161 161 handler : function (event) {
162 162 var cell = IPython.notebook.get_selected_cell();
163 163 if (cell && cell.at_bottom()) {
164 164 event.preventDefault();
165 165 IPython.notebook.command_mode()
166 166 IPython.notebook.select_next();
167 167 IPython.notebook.edit_mode();
168 168 return false;
169 169 };
170 170 }
171 171 },
172 172 'alt+-' : {
173 173 help : 'split cell',
174 174 handler : function (event) {
175 175 IPython.notebook.split_cell();
176 176 return false;
177 177 }
178 178 },
179 179 'alt+subtract' : {
180 180 help : 'split cell',
181 181 handler : function (event) {
182 182 IPython.notebook.split_cell();
183 183 return false;
184 184 }
185 185 },
186 186 }
187 187
188 188 // Command mode defaults
189 189
190 190 var default_command_shortcuts = {
191 191 'enter' : {
192 192 help : 'edit mode',
193 193 handler : function (event) {
194 194 IPython.notebook.edit_mode();
195 195 return false;
196 196 }
197 197 },
198 198 'up' : {
199 199 help : 'select previous cell',
200 200 handler : function (event) {
201 201 var index = IPython.notebook.get_selected_index();
202 202 if (index !== 0 && index !== null) {
203 203 IPython.notebook.select_prev();
204 204 var cell = IPython.notebook.get_selected_cell();
205 205 cell.focus_cell();
206 206 };
207 207 return false;
208 208 }
209 209 },
210 210 'down' : {
211 211 help : 'select next cell',
212 212 handler : function (event) {
213 213 var index = IPython.notebook.get_selected_index();
214 214 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
215 215 IPython.notebook.select_next();
216 216 var cell = IPython.notebook.get_selected_cell();
217 217 cell.focus_cell();
218 218 };
219 219 return false;
220 220 }
221 221 },
222 222 'k' : {
223 223 help : 'select previous cell',
224 224 handler : function (event) {
225 225 var index = IPython.notebook.get_selected_index();
226 226 if (index !== 0 && index !== null) {
227 227 IPython.notebook.select_prev();
228 228 var cell = IPython.notebook.get_selected_cell();
229 229 cell.focus_cell();
230 230 };
231 231 return false;
232 232 }
233 233 },
234 234 'j' : {
235 235 help : 'select next cell',
236 236 handler : function (event) {
237 237 var index = IPython.notebook.get_selected_index();
238 238 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
239 239 IPython.notebook.select_next();
240 240 var cell = IPython.notebook.get_selected_cell();
241 241 cell.focus_cell();
242 242 };
243 243 return false;
244 244 }
245 245 },
246 246 'x' : {
247 247 help : 'cut cell',
248 248 handler : function (event) {
249 249 IPython.notebook.cut_cell();
250 250 return false;
251 251 }
252 252 },
253 253 'c' : {
254 254 help : 'copy cell',
255 255 handler : function (event) {
256 256 IPython.notebook.copy_cell();
257 257 return false;
258 258 }
259 259 },
260 260 'v' : {
261 261 help : 'paste cell below',
262 262 handler : function (event) {
263 263 IPython.notebook.paste_cell_below();
264 264 return false;
265 265 }
266 266 },
267 267 'd' : {
268 268 help : 'delete cell (press twice)',
269 269 handler : function (event) {
270 var dc = IPython.delete_count;
271 if (dc === undefined) {
272 IPython.delete_count = 1;
273 } else if (dc === 0) {
274 IPython.delete_count = 1;
270 var dc = IPython.keyboard_manager._delete_count;
271 if (dc === 0) {
272 IPython.keyboard_manager._delete_count = 1;
275 273 setTimeout(function () {
276 IPython.delete_count = 0;
274 IPython.keyboard_manager._delete_count = 0;
277 275 }, 800);
278 276 } else if (dc === 1) {
279 277 IPython.notebook.delete_cell();
280 IPython.delete_count = 0;
278 IPython.keyboard_manager._delete_count = 0;
281 279 }
282 280 return false;
283 281 }
284 282 },
285 283 'a' : {
286 284 help : 'insert cell above',
287 285 handler : function (event) {
288 286 IPython.notebook.insert_cell_above('code');
289 287 IPython.notebook.select_prev();
290 288 IPython.notebook.focus_cell();
291 289 return false;
292 290 }
293 291 },
294 292 'b' : {
295 293 help : 'insert cell below',
296 294 handler : function (event) {
297 295 IPython.notebook.insert_cell_below('code');
298 296 IPython.notebook.select_next();
299 297 IPython.notebook.focus_cell();
300 298 return false;
301 299 }
302 300 },
303 301 'y' : {
304 302 help : 'to code',
305 303 handler : function (event) {
306 304 IPython.notebook.to_code();
307 305 return false;
308 306 }
309 307 },
310 308 'm' : {
311 309 help : 'to markdown',
312 310 handler : function (event) {
313 311 IPython.notebook.to_markdown();
314 312 return false;
315 313 }
316 314 },
317 315 't' : {
318 316 help : 'to raw',
319 317 handler : function (event) {
320 318 IPython.notebook.to_raw();
321 319 return false;
322 320 }
323 321 },
324 322 '1' : {
325 323 help : 'to heading 1',
326 324 handler : function (event) {
327 325 IPython.notebook.to_heading(undefined, 1);
328 326 return false;
329 327 }
330 328 },
331 329 '2' : {
332 330 help : 'to heading 2',
333 331 handler : function (event) {
334 332 IPython.notebook.to_heading(undefined, 2);
335 333 return false;
336 334 }
337 335 },
338 336 '3' : {
339 337 help : 'to heading 3',
340 338 handler : function (event) {
341 339 IPython.notebook.to_heading(undefined, 3);
342 340 return false;
343 341 }
344 342 },
345 343 '4' : {
346 344 help : 'to heading 4',
347 345 handler : function (event) {
348 346 IPython.notebook.to_heading(undefined, 4);
349 347 return false;
350 348 }
351 349 },
352 350 '5' : {
353 351 help : 'to heading 5',
354 352 handler : function (event) {
355 353 IPython.notebook.to_heading(undefined, 5);
356 354 return false;
357 355 }
358 356 },
359 357 '6' : {
360 358 help : 'to heading 6',
361 359 handler : function (event) {
362 360 IPython.notebook.to_heading(undefined, 6);
363 361 return false;
364 362 }
365 363 },
366 364 'o' : {
367 365 help : 'toggle output',
368 366 handler : function (event) {
369 367 IPython.notebook.toggle_output();
370 368 return false;
371 369 }
372 370 },
373 371 'shift+o' : {
374 372 help : 'toggle output',
375 373 handler : function (event) {
376 374 IPython.notebook.toggle_output_scroll();
377 375 return false;
378 376 }
379 377 },
380 378 's' : {
381 379 help : 'save notebook',
382 380 handler : function (event) {
383 381 IPython.notebook.save_checkpoint();
384 382 return false;
385 383 }
386 384 },
387 385 'ctrl+j' : {
388 386 help : 'move cell down',
389 387 handler : function (event) {
390 388 IPython.notebook.move_cell_down();
391 389 return false;
392 390 }
393 391 },
394 392 'ctrl+k' : {
395 393 help : 'move cell up',
396 394 handler : function (event) {
397 395 IPython.notebook.move_cell_up();
398 396 return false;
399 397 }
400 398 },
401 399 'l' : {
402 400 help : 'toggle line numbers',
403 401 handler : function (event) {
404 402 IPython.notebook.cell_toggle_line_numbers();
405 403 return false;
406 404 }
407 405 },
408 406 'i' : {
409 407 help : 'interrupt kernel',
410 408 handler : function (event) {
411 409 IPython.notebook.kernel.interrupt();
412 410 return false;
413 411 }
414 412 },
415 413 '.' : {
416 414 help : 'restart kernel',
417 415 handler : function (event) {
418 416 IPython.notebook.restart_kernel();
419 417 return false;
420 418 }
421 419 },
422 420 'h' : {
423 421 help : 'keyboard shortcuts',
424 422 handler : function (event) {
425 423 IPython.quick_help.show_keyboard_shortcuts();
426 424 return false;
427 425 }
428 426 },
429 427 'z' : {
430 428 help : 'undo last delete',
431 429 handler : function (event) {
432 430 IPython.notebook.undelete_cell();
433 431 return false;
434 432 }
435 433 },
436 434 'shift+=' : {
437 435 help : 'merge cell below',
438 436 handler : function (event) {
439 437 IPython.notebook.merge_cell_below();
440 438 return false;
441 439 }
442 440 },
443 441 }
444 442
445 443
446 444 // Shortcut manager class
447 445
448 446 var ShortcutManager = function () {
449 447 this._shortcuts = {}
450 448 }
451 449
452 450 ShortcutManager.prototype.help = function () {
453 451 var help = [];
454 452 for (var shortcut in this._shortcuts) {
455 453 help.push({shortcut: shortcut, help: this._shortcuts[shortcut]['help']});
456 454 }
457 455 return help;
458 456 }
459 457
460 458 ShortcutManager.prototype.canonicalize_key = function (key) {
461 459 return inv_keycodes[keycodes[key]];
462 460 }
463 461
464 462 ShortcutManager.prototype.canonicalize_shortcut = function (shortcut) {
465 463 // Sort a sequence of + separated modifiers into the order alt+ctrl+meta+shift
466 464 var values = shortcut.split("+");
467 465 if (values.length === 1) {
468 466 return this.canonicalize_key(values[0])
469 467 } else {
470 468 var modifiers = values.slice(0,-1);
471 469 var key = this.canonicalize_key(values[values.length-1]);
472 470 modifiers.sort();
473 471 return modifiers.join('+') + '+' + key;
474 472 }
475 473 }
476 474
477 475 ShortcutManager.prototype.event_to_shortcut = function (event) {
478 476 // Convert a jQuery keyboard event to a strong based keyboard shortcut
479 477 var shortcut = '';
480 478 var key = inv_keycodes[event.which]
481 479 if (event.altKey && key !== 'alt') {shortcut += 'alt+';}
482 480 if (event.ctrlKey && key !== 'ctrl') {shortcut += 'ctrl+';}
483 481 if (event.metaKey && key !== 'meta') {shortcut += 'meta+';}
484 482 if (event.shiftKey && key !== 'shift') {shortcut += 'shift+';}
485 483 shortcut += key;
486 484 return shortcut
487 485 }
488 486
489 487 ShortcutManager.prototype.clear_shortcuts = function () {
490 488 this._shortcuts = {};
491 489 }
492 490
493 491 ShortcutManager.prototype.add_shortcut = function (shortcut, data) {
494 492 shortcut = this.canonicalize_shortcut(shortcut);
495 493 this._shortcuts[shortcut] = data;
496 494 }
497 495
498 496 ShortcutManager.prototype.add_shortcuts = function (data) {
499 497 for (var shortcut in data) {
500 498 this.add_shortcut(shortcut, data[shortcut]);
501 499 }
502 500 }
503 501
504 502 ShortcutManager.prototype.remove_shortcut = function (shortcut) {
505 503 shortcut = this.canonicalize_shortcut(shortcut);
506 504 delete this._shortcuts[shortcut];
507 505 }
508 506
509 507 ShortcutManager.prototype.call_handler = function (event) {
510 508 var shortcut = this.event_to_shortcut(event);
511 509 var data = this._shortcuts[shortcut];
512 510 if (data !== undefined) {
513 511 var handler = data['handler'];
514 512 if (handler !== undefined) {
515 513 return handler(event);
516 514 }
517 515 }
518 516 return true;
519 517 }
520 518
521 519
522 520
523 521 // Main keyboard manager for the notebook
524 522
525 523 var KeyboardManager = function () {
526 524 this.mode = 'command';
527 525 this.enabled = true;
528 this.delete_count = 0;
526 this._delete_count = 0;
529 527 this.bind_events();
530 528 this.command_shortcuts = new ShortcutManager();
531 529 this.command_shortcuts.add_shortcuts(default_common_shortcuts);
532 530 this.command_shortcuts.add_shortcuts(default_command_shortcuts);
533 531 this.edit_shortcuts = new ShortcutManager();
534 532 this.edit_shortcuts.add_shortcuts(default_common_shortcuts);
535 533 this.edit_shortcuts.add_shortcuts(default_edit_shortcuts);
536 534 };
537 535
538 536 KeyboardManager.prototype.bind_events = function () {
539 537 var that = this;
540 538 $(document).keydown(function (event) {
541 539 return that.handle_keydown(event);
542 540 });
543 541 };
544 542
545 543 KeyboardManager.prototype.handle_keydown = function (event) {
546 544 var notebook = IPython.notebook;
547 545
548 546 console.log('keyboard_manager', this.mode, event.keyCode);
549 547
550 548 if (event.which === keycodes['esc']) {
551 549 // Intercept escape at highest level to avoid closing
552 550 // websocket connection with firefox
553 551 event.preventDefault();
554 552 }
555 553
556 554 if (!this.enabled) {
557 555 if (event.which === keycodes['esc']) {
558 556 // ESC
559 557 notebook.command_mode();
560 558 return false;
561 559 }
562 560 return true;
563 561 }
564 562
565 563 if (this.mode === 'edit') {
566 564 return this.edit_shortcuts.call_handler(event);
567 565 } else if (this.mode === 'command') {
568 566 return this.command_shortcuts.call_handler(event);
569 567 }
570 568 return true;
571 569 }
572 570
573 571 KeyboardManager.prototype.edit_mode = function () {
574 572 console.log('KeyboardManager', 'changing to edit mode');
575 573 this.last_mode = this.mode;
576 574 this.mode = 'edit';
577 575 }
578 576
579 577 KeyboardManager.prototype.command_mode = function () {
580 578 console.log('KeyboardManager', 'changing to command mode');
581 579 this.last_mode = this.mode;
582 580 this.mode = 'command';
583 581 }
584 582
585 583 KeyboardManager.prototype.enable = function () {
586 584 this.enabled = true;
587 585 }
588 586
589 587 KeyboardManager.prototype.disable = function () {
590 588 this.enabled = false;
591 589 }
592 590
593 591 KeyboardManager.prototype.register_events = function (e) {
594 592 var that = this;
595 593 e.on('focusin', function () {
596 594 that.command_mode();
597 595 that.disable();
598 596 });
599 597 e.on('focusout', function () {
600 598 that.command_mode();
601 599 that.enable();
602 600 });
603 601 // There are times (raw_input) where we remove the element from the DOM before
604 602 // focusout is called. In this case we bind to the remove event of jQueryUI,
605 603 // which gets triggered upon removal.
606 604 e.on('remove', function () {
607 605 that.command_mode();
608 606 that.enable();
609 607 });
610 608 }
611 609
612 610
613 611 IPython.keycodes = keycodes;
614 612 IPython.inv_keycodes = inv_keycodes;
615 613 IPython.default_common_shortcuts = default_common_shortcuts;
616 614 IPython.default_edit_shortcuts = default_edit_shortcuts;
617 615 IPython.default_command_shortcuts = default_command_shortcuts;
618 616 IPython.ShortcutManager = ShortcutManager;
619 617 IPython.KeyboardManager = KeyboardManager;
620 618
621 619 return IPython;
622 620
623 621 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now