keyboardmanager.js
241 lines
| 9.1 KiB
| application/javascript
|
JavascriptLexer
Jonathan Frederic
|
r17866 | // Copyright (c) IPython Development Team. | ||
// Distributed under the terms of the Modified BSD License. | ||||
Matthias BUSSONNIER
|
r18280 | /** | ||
* | ||||
* | ||||
* @module keyboardmanager | ||||
* @namespace keyboardmanager | ||||
* @class KeyboardManager | ||||
*/ | ||||
Jonathan Frederic
|
r17864 | |||
Jonathan Frederic
|
r17869 | define([ | ||
'base/js/namespace', | ||||
'jquery', | ||||
'base/js/utils', | ||||
'base/js/keyboard', | ||||
], function(IPython, $, utils, keyboard) { | ||||
Brian E. Granger
|
r14020 | "use strict"; | ||
Jonathan Frederic
|
r17871 | |||
Jonathan Frederic
|
r17198 | // Main keyboard manager for the notebook | ||
var keycodes = keyboard.keycodes; | ||||
Brian E. Granger
|
r14036 | |||
jon
|
r17210 | var KeyboardManager = function (options) { | ||
Matthias BUSSONNIER
|
r18280 | /** | ||
* A class to deal with keyboard event and shortcut | ||||
* | ||||
* @class KeyboardManager | ||||
* @constructor | ||||
* @param options {dict} Dictionary of keyword arguments : | ||||
* @param options.events {$(Events)} instance | ||||
* @param options.pager: {Pager} pager instance | ||||
*/ | ||||
Jonathan Frederic
|
r17198 | this.mode = 'command'; | ||
this.enabled = true; | ||||
jon
|
r17210 | this.pager = options.pager; | ||
Jonathan Frederic
|
r17198 | this.quick_help = undefined; | ||
this.notebook = undefined; | ||||
Matthias Bussonnier
|
r18390 | this.last_mode = undefined; | ||
Jonathan Frederic
|
r17198 | this.bind_events(); | ||
Matthias Bussonnier
|
r18390 | this.env = {pager:this.pager}; | ||
this.actions = options.actions; | ||||
this.command_shortcuts = new keyboard.ShortcutManager(undefined, options.events, this.actions, this.env ); | ||||
Jonathan Frederic
|
r17198 | this.command_shortcuts.add_shortcuts(this.get_default_common_shortcuts()); | ||
this.command_shortcuts.add_shortcuts(this.get_default_command_shortcuts()); | ||||
Matthias Bussonnier
|
r18390 | this.edit_shortcuts = new keyboard.ShortcutManager(undefined, options.events, this.actions, this.env); | ||
Jonathan Frederic
|
r17198 | this.edit_shortcuts.add_shortcuts(this.get_default_common_shortcuts()); | ||
this.edit_shortcuts.add_shortcuts(this.get_default_edit_shortcuts()); | ||||
Matthias Bussonnier
|
r18390 | Object.seal(this); | ||
Jonathan Frederic
|
r17198 | }; | ||
Brian E. Granger
|
r14036 | |||
Matthias Bussonnier
|
r18390 | |||
Matthias BUSSONNIER
|
r18280 | /** | ||
* Return a dict of common shortcut | ||||
* @method get_default_common_shortcuts | ||||
* | ||||
* @example Example of returned shortcut | ||||
* ``` | ||||
Matthias Bussonnier
|
r18390 | * 'shortcut-key': 'action-name' | ||
* // a string representing the shortcut as dash separated value. | ||||
* // e.g. 'shift' , 'shift-enter', 'cmd-t' | ||||
Matthias BUSSONNIER
|
r18280 | *``` | ||
*/ | ||||
Jonathan Frederic
|
r17198 | KeyboardManager.prototype.get_default_common_shortcuts = function() { | ||
Matthias Bussonnier
|
r18390 | return { | ||
'shift' : 'ipython.ignore', | ||||
'shift-enter' : 'ipython.run-select-next', | ||||
'ctrl-enter' : 'ipython.execute-in-place', | ||||
'alt-enter' : 'ipython.execute-and-insert-after', | ||||
// cmd on mac, ctrl otherwise | ||||
'cmdtrl-s' : 'ipython.save-notebook', | ||||
Jonathan Frederic
|
r17871 | }; | ||
Jonathan Frederic
|
r15491 | }; | ||
Brian E. Granger
|
r14036 | |||
Jonathan Frederic
|
r17198 | KeyboardManager.prototype.get_default_edit_shortcuts = function() { | ||
return { | ||||
Matthias Bussonnier
|
r18390 | 'esc' : 'ipython.go-to-command-mode', | ||
'ctrl-m' : 'ipython.go-to-command-mode', | ||||
'up' : 'ipython.move-cursor-up-or-previous-cell', | ||||
'down' : 'ipython.move-cursor-down-or-next-cell', | ||||
'ctrl-shift--' : 'ipython.split-cell-at-cursor', | ||||
'ctrl-shift-subtract' : 'ipython.split-cell-at-cursor' | ||||
Jonathan Frederic
|
r17869 | }; | ||
Jonathan Frederic
|
r15491 | }; | ||
Brian E. Granger
|
r14816 | |||
Jonathan Frederic
|
r17198 | KeyboardManager.prototype.get_default_command_shortcuts = function() { | ||
return { | ||||
Matthias Bussonnier
|
r18390 | 'shift-space': 'ipython.scroll-up', | ||
'shift-v' : 'ipython.paste-cell-before', | ||||
'shift-m' : 'ipython.merge-selected-cell-with-cell-after', | ||||
'shift-o' : 'ipython.toggle-output-scrolling-selected-cell', | ||||
'ctrl-j' : 'ipython.move-selected-cell-down', | ||||
'ctrl-k' : 'ipython.move-selected-cell-up', | ||||
'enter' : 'ipython.enter-edit-mode', | ||||
'space' : 'ipython.scroll-down', | ||||
'down' : 'ipython.select-next-cell', | ||||
'i,i' : 'ipython.interrupt-kernel', | ||||
'0,0' : 'ipython.restart-kernel', | ||||
'd,d' : 'ipython.delete-cell', | ||||
'up' : 'ipython.select-previous-cell', | ||||
'k' : 'ipython.select-previous-cell', | ||||
'j' : 'ipython.select-next-cell', | ||||
'x' : 'ipython.cut-selected-cell', | ||||
'c' : 'ipython.copy-selected-cell', | ||||
'v' : 'ipython.paste-cell-after', | ||||
'a' : 'ipython.insert-cell-before', | ||||
'b' : 'ipython.insert-cell-after', | ||||
'y' : 'ipython.change-selected-cell-to-code-cell', | ||||
'm' : 'ipython.change-selected-cell-to-markdown-cell', | ||||
'r' : 'ipython.change-selected-cell-to-raw-cell', | ||||
'1' : 'ipython.change-selected-cell-to-heading-1', | ||||
'2' : 'ipython.change-selected-cell-to-heading-2', | ||||
'3' : 'ipython.change-selected-cell-to-heading-3', | ||||
'4' : 'ipython.change-selected-cell-to-heading-4', | ||||
'5' : 'ipython.change-selected-cell-to-heading-5', | ||||
'6' : 'ipython.change-selected-cell-to-heading-6', | ||||
'o' : 'ipython.toggle-output-visibility-selected-cell', | ||||
's' : 'ipython.save-notebook', | ||||
'l' : 'ipython.toggle-line-number-selected-cell', | ||||
'h' : 'ipython.show-keyboard-shortcut-help-dialog', | ||||
'z' : 'ipython.undo-last-cell-deletion', | ||||
'q' : 'ipython.close-pager', | ||||
'i,e,e,e,e,e' : function(){console.log('[[===>>> 5E <<<===]]');}, | ||||
'i,d,d,q,d' : function(){console.log('[[===>>> Trigger god mode <<<===]]');}, | ||||
'i,d,d' : function(){console.log('[[===>>> should warn at registration <<<===]]');}, | ||||
'i,d,k' : function(){console.log('[[===>>> Trigger shadow mode <<<===]]');}, | ||||
'i,d,k,r,q' : function(){console.log('[[===>>> Trigger invisibility mode <<<===]]');}, | ||||
';,up,down,up,down,left,right,left,right,b,a' : function(){console.log('[[===>>> Konami <<<===]]');}, | ||||
'ctrl-x,meta-c,meta-b,u,t,t,e,r,f,l,y' : function(){ | ||||
console.log('[[Are you a real Programmer ?]]'); | ||||
window.open('http://xkcd.com/378/','_blank'); | ||||
Jonathan Frederic
|
r17869 | }, | ||
}; | ||||
Brian E. Granger
|
r14020 | }; | ||
KeyboardManager.prototype.bind_events = function () { | ||||
var that = this; | ||||
$(document).keydown(function (event) { | ||||
Matthias Bussonnier
|
r18390 | if(event._ipkmIgnore===true||(event.originalEvent||{})._ipkmIgnore===true){ | ||
Matthias Bussonnier
|
r18282 | return false; | ||
} | ||||
Brian E. Granger
|
r14020 | return that.handle_keydown(event); | ||
}); | ||||
}; | ||||
Matthias Bussonnier
|
r18390 | KeyboardManager.prototype.set_notebook = function (notebook) { | ||
this.notebook = notebook; | ||||
this.actions.extend_env({notebook:notebook}); | ||||
}; | ||||
KeyboardManager.prototype.set_quickhelp = function (notebook) { | ||||
this.actions.extend_env({quick_help:notebook}); | ||||
}; | ||||
Brian E. Granger
|
r14020 | KeyboardManager.prototype.handle_keydown = function (event) { | ||
Matthias Bussonnier
|
r18390 | /** | ||
* returning false from this will stop event propagation | ||||
**/ | ||||
Brian E. Granger
|
r14089 | |||
Jonathan Frederic
|
r15491 | if (event.which === keycodes.esc) { | ||
Brian E. Granger
|
r14020 | // Intercept escape at highest level to avoid closing | ||
// websocket connection with firefox | ||||
event.preventDefault(); | ||||
} | ||||
Brian E. Granger
|
r14023 | if (!this.enabled) { | ||
Jonathan Frederic
|
r15491 | if (event.which === keycodes.esc) { | ||
Matthias Bussonnier
|
r18390 | this.notebook.command_mode(); | ||
Brian E. Granger
|
r14036 | return false; | ||
} | ||||
Brian E. Granger
|
r14023 | return true; | ||
Brian E. Granger
|
r14020 | } | ||
if (this.mode === 'edit') { | ||||
Brian E. Granger
|
r14036 | return this.edit_shortcuts.call_handler(event); | ||
} else if (this.mode === 'command') { | ||||
return this.command_shortcuts.call_handler(event); | ||||
Brian E. Granger
|
r14020 | } | ||
return true; | ||||
Jonathan Frederic
|
r15491 | }; | ||
Brian E. Granger
|
r14020 | |||
KeyboardManager.prototype.edit_mode = function () { | ||||
this.last_mode = this.mode; | ||||
this.mode = 'edit'; | ||||
Jonathan Frederic
|
r15491 | }; | ||
Brian E. Granger
|
r14020 | |||
KeyboardManager.prototype.command_mode = function () { | ||||
this.last_mode = this.mode; | ||||
this.mode = 'command'; | ||||
Jonathan Frederic
|
r15491 | }; | ||
Brian E. Granger
|
r14020 | |||
Brian E. Granger
|
r14024 | KeyboardManager.prototype.enable = function () { | ||
Brian E. Granger
|
r14030 | this.enabled = true; | ||
Jonathan Frederic
|
r15491 | }; | ||
Brian E. Granger
|
r14024 | |||
KeyboardManager.prototype.disable = function () { | ||||
Brian E. Granger
|
r14030 | this.enabled = false; | ||
Jonathan Frederic
|
r15491 | }; | ||
Brian E. Granger
|
r14024 | |||
Brian E. Granger
|
r14036 | KeyboardManager.prototype.register_events = function (e) { | ||
var that = this; | ||||
Jonathan Frederic
|
r15496 | var handle_focus = function () { | ||
Brian E. Granger
|
r14036 | that.disable(); | ||
Jonathan Frederic
|
r15496 | }; | ||
var handle_blur = function () { | ||||
Brian E. Granger
|
r14036 | that.enable(); | ||
Jonathan Frederic
|
r15496 | }; | ||
e.on('focusin', handle_focus); | ||||
e.on('focusout', handle_blur); | ||||
Jonathan Frederic
|
r15507 | // TODO: Very strange. The focusout event does not seem fire for the | ||
Jonathan Frederic
|
r15539 | // bootstrap textboxes on FF25&26... This works around that by | ||
// registering focus and blur events recursively on all inputs within | ||||
// registered element. | ||||
Jonathan Frederic
|
r15507 | e.find('input').blur(handle_blur); | ||
e.on('DOMNodeInserted', function (event) { | ||||
var target = $(event.target); | ||||
if (target.is('input')) { | ||||
target.blur(handle_blur); | ||||
} else { | ||||
target.find('input').blur(handle_blur); | ||||
} | ||||
}); | ||||
Brian E. Granger
|
r14046 | // There are times (raw_input) where we remove the element from the DOM before | ||
// focusout is called. In this case we bind to the remove event of jQueryUI, | ||||
MinRK
|
r15328 | // which gets triggered upon removal, iff it is focused at the time. | ||
Jonathan Frederic
|
r15539 | // is_focused must be used to check for the case where an element within | ||
// the element being removed is focused. | ||||
Brian E. Granger
|
r14046 | e.on('remove', function () { | ||
Jonathan Frederic
|
r17198 | if (utils.is_focused(e[0])) { | ||
MinRK
|
r15328 | that.enable(); | ||
} | ||||
Brian E. Granger
|
r14046 | }); | ||
Jonathan Frederic
|
r15491 | }; | ||
Brian E. Granger
|
r14036 | |||
Matthias Bussonnier
|
r18390 | |||
// For backwards compatibility. | ||||
Brian E. Granger
|
r14020 | IPython.KeyboardManager = KeyboardManager; | ||
Jonathan Frederic
|
r17201 | return {'KeyboardManager': KeyboardManager}; | ||
Jonathan Frederic
|
r17198 | }); | ||