##// END OF EJS Templates
Remove Ctrl-J & Ctrl-K shortcuts...
Thomas Kluyver -
Show More
@@ -1,232 +1,230 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3 /**
3 /**
4 *
4 *
5 *
5 *
6 * @module keyboardmanager
6 * @module keyboardmanager
7 * @namespace keyboardmanager
7 * @namespace keyboardmanager
8 * @class KeyboardManager
8 * @class KeyboardManager
9 */
9 */
10
10
11 define([
11 define([
12 'base/js/namespace',
12 'base/js/namespace',
13 'jquery',
13 'jquery',
14 'base/js/utils',
14 'base/js/utils',
15 'base/js/keyboard',
15 'base/js/keyboard',
16 ], function(IPython, $, utils, keyboard) {
16 ], function(IPython, $, utils, keyboard) {
17 "use strict";
17 "use strict";
18
18
19 // Main keyboard manager for the notebook
19 // Main keyboard manager for the notebook
20 var keycodes = keyboard.keycodes;
20 var keycodes = keyboard.keycodes;
21
21
22 var KeyboardManager = function (options) {
22 var KeyboardManager = function (options) {
23 /**
23 /**
24 * A class to deal with keyboard event and shortcut
24 * A class to deal with keyboard event and shortcut
25 *
25 *
26 * @class KeyboardManager
26 * @class KeyboardManager
27 * @constructor
27 * @constructor
28 * @param options {dict} Dictionary of keyword arguments :
28 * @param options {dict} Dictionary of keyword arguments :
29 * @param options.events {$(Events)} instance
29 * @param options.events {$(Events)} instance
30 * @param options.pager: {Pager} pager instance
30 * @param options.pager: {Pager} pager instance
31 */
31 */
32 this.mode = 'command';
32 this.mode = 'command';
33 this.enabled = true;
33 this.enabled = true;
34 this.pager = options.pager;
34 this.pager = options.pager;
35 this.quick_help = undefined;
35 this.quick_help = undefined;
36 this.notebook = undefined;
36 this.notebook = undefined;
37 this.last_mode = undefined;
37 this.last_mode = undefined;
38 this.bind_events();
38 this.bind_events();
39 this.env = {pager:this.pager};
39 this.env = {pager:this.pager};
40 this.actions = options.actions;
40 this.actions = options.actions;
41 this.command_shortcuts = new keyboard.ShortcutManager(undefined, options.events, this.actions, this.env );
41 this.command_shortcuts = new keyboard.ShortcutManager(undefined, options.events, this.actions, this.env );
42 this.command_shortcuts.add_shortcuts(this.get_default_common_shortcuts());
42 this.command_shortcuts.add_shortcuts(this.get_default_common_shortcuts());
43 this.command_shortcuts.add_shortcuts(this.get_default_command_shortcuts());
43 this.command_shortcuts.add_shortcuts(this.get_default_command_shortcuts());
44 this.edit_shortcuts = new keyboard.ShortcutManager(undefined, options.events, this.actions, this.env);
44 this.edit_shortcuts = new keyboard.ShortcutManager(undefined, options.events, this.actions, this.env);
45 this.edit_shortcuts.add_shortcuts(this.get_default_common_shortcuts());
45 this.edit_shortcuts.add_shortcuts(this.get_default_common_shortcuts());
46 this.edit_shortcuts.add_shortcuts(this.get_default_edit_shortcuts());
46 this.edit_shortcuts.add_shortcuts(this.get_default_edit_shortcuts());
47 Object.seal(this);
47 Object.seal(this);
48 };
48 };
49
49
50
50
51
51
52
52
53 /**
53 /**
54 * Return a dict of common shortcut
54 * Return a dict of common shortcut
55 * @method get_default_common_shortcuts
55 * @method get_default_common_shortcuts
56 *
56 *
57 * @example Example of returned shortcut
57 * @example Example of returned shortcut
58 * ```
58 * ```
59 * 'shortcut-key': 'action-name'
59 * 'shortcut-key': 'action-name'
60 * // a string representing the shortcut as dash separated value.
60 * // a string representing the shortcut as dash separated value.
61 * // e.g. 'shift' , 'shift-enter', 'cmd-t'
61 * // e.g. 'shift' , 'shift-enter', 'cmd-t'
62 *```
62 *```
63 */
63 */
64 KeyboardManager.prototype.get_default_common_shortcuts = function() {
64 KeyboardManager.prototype.get_default_common_shortcuts = function() {
65 return {
65 return {
66 'shift' : 'ipython.ignore',
66 'shift' : 'ipython.ignore',
67 'shift-enter' : 'ipython.run-select-next',
67 'shift-enter' : 'ipython.run-select-next',
68 'ctrl-enter' : 'ipython.execute-in-place',
68 'ctrl-enter' : 'ipython.execute-in-place',
69 'alt-enter' : 'ipython.execute-and-insert-after',
69 'alt-enter' : 'ipython.execute-and-insert-after',
70 // cmd on mac, ctrl otherwise
70 // cmd on mac, ctrl otherwise
71 'cmdtrl-s' : 'ipython.save-notebook',
71 'cmdtrl-s' : 'ipython.save-notebook',
72 };
72 };
73 };
73 };
74
74
75 KeyboardManager.prototype.get_default_edit_shortcuts = function() {
75 KeyboardManager.prototype.get_default_edit_shortcuts = function() {
76 return {
76 return {
77 'esc' : 'ipython.go-to-command-mode',
77 'esc' : 'ipython.go-to-command-mode',
78 'ctrl-m' : 'ipython.go-to-command-mode',
78 'ctrl-m' : 'ipython.go-to-command-mode',
79 'up' : 'ipython.move-cursor-up-or-previous-cell',
79 'up' : 'ipython.move-cursor-up-or-previous-cell',
80 'down' : 'ipython.move-cursor-down-or-next-cell',
80 'down' : 'ipython.move-cursor-down-or-next-cell',
81 'ctrl-shift--' : 'ipython.split-cell-at-cursor',
81 'ctrl-shift--' : 'ipython.split-cell-at-cursor',
82 'ctrl-shift-subtract' : 'ipython.split-cell-at-cursor'
82 'ctrl-shift-subtract' : 'ipython.split-cell-at-cursor'
83 };
83 };
84 };
84 };
85
85
86 KeyboardManager.prototype.get_default_command_shortcuts = function() {
86 KeyboardManager.prototype.get_default_command_shortcuts = function() {
87 return {
87 return {
88 'shift-space': 'ipython.scroll-up',
88 'shift-space': 'ipython.scroll-up',
89 'shift-v' : 'ipython.paste-cell-before',
89 'shift-v' : 'ipython.paste-cell-before',
90 'shift-m' : 'ipython.merge-selected-cell-with-cell-after',
90 'shift-m' : 'ipython.merge-selected-cell-with-cell-after',
91 'shift-o' : 'ipython.toggle-output-scrolling-selected-cell',
91 'shift-o' : 'ipython.toggle-output-scrolling-selected-cell',
92 'ctrl-j' : 'ipython.move-selected-cell-down',
93 'ctrl-k' : 'ipython.move-selected-cell-up',
94 'enter' : 'ipython.enter-edit-mode',
92 'enter' : 'ipython.enter-edit-mode',
95 'space' : 'ipython.scroll-down',
93 'space' : 'ipython.scroll-down',
96 'down' : 'ipython.select-next-cell',
94 'down' : 'ipython.select-next-cell',
97 'i,i' : 'ipython.interrupt-kernel',
95 'i,i' : 'ipython.interrupt-kernel',
98 '0,0' : 'ipython.restart-kernel',
96 '0,0' : 'ipython.restart-kernel',
99 'd,d' : 'ipython.delete-cell',
97 'd,d' : 'ipython.delete-cell',
100 'esc': 'ipython.close-pager',
98 'esc': 'ipython.close-pager',
101 'up' : 'ipython.select-previous-cell',
99 'up' : 'ipython.select-previous-cell',
102 'k' : 'ipython.select-previous-cell',
100 'k' : 'ipython.select-previous-cell',
103 'j' : 'ipython.select-next-cell',
101 'j' : 'ipython.select-next-cell',
104 'x' : 'ipython.cut-selected-cell',
102 'x' : 'ipython.cut-selected-cell',
105 'c' : 'ipython.copy-selected-cell',
103 'c' : 'ipython.copy-selected-cell',
106 'v' : 'ipython.paste-cell-after',
104 'v' : 'ipython.paste-cell-after',
107 'a' : 'ipython.insert-cell-before',
105 'a' : 'ipython.insert-cell-before',
108 'b' : 'ipython.insert-cell-after',
106 'b' : 'ipython.insert-cell-after',
109 'y' : 'ipython.change-selected-cell-to-code-cell',
107 'y' : 'ipython.change-selected-cell-to-code-cell',
110 'm' : 'ipython.change-selected-cell-to-markdown-cell',
108 'm' : 'ipython.change-selected-cell-to-markdown-cell',
111 'r' : 'ipython.change-selected-cell-to-raw-cell',
109 'r' : 'ipython.change-selected-cell-to-raw-cell',
112 '1' : 'ipython.change-selected-cell-to-heading-1',
110 '1' : 'ipython.change-selected-cell-to-heading-1',
113 '2' : 'ipython.change-selected-cell-to-heading-2',
111 '2' : 'ipython.change-selected-cell-to-heading-2',
114 '3' : 'ipython.change-selected-cell-to-heading-3',
112 '3' : 'ipython.change-selected-cell-to-heading-3',
115 '4' : 'ipython.change-selected-cell-to-heading-4',
113 '4' : 'ipython.change-selected-cell-to-heading-4',
116 '5' : 'ipython.change-selected-cell-to-heading-5',
114 '5' : 'ipython.change-selected-cell-to-heading-5',
117 '6' : 'ipython.change-selected-cell-to-heading-6',
115 '6' : 'ipython.change-selected-cell-to-heading-6',
118 'o' : 'ipython.toggle-output-visibility-selected-cell',
116 'o' : 'ipython.toggle-output-visibility-selected-cell',
119 's' : 'ipython.save-notebook',
117 's' : 'ipython.save-notebook',
120 'l' : 'ipython.toggle-line-number-selected-cell',
118 'l' : 'ipython.toggle-line-number-selected-cell',
121 'h' : 'ipython.show-keyboard-shortcut-help-dialog',
119 'h' : 'ipython.show-keyboard-shortcut-help-dialog',
122 'z' : 'ipython.undo-last-cell-deletion',
120 'z' : 'ipython.undo-last-cell-deletion',
123 'q' : 'ipython.close-pager',
121 'q' : 'ipython.close-pager',
124 };
122 };
125 };
123 };
126
124
127 KeyboardManager.prototype.bind_events = function () {
125 KeyboardManager.prototype.bind_events = function () {
128 var that = this;
126 var that = this;
129 $(document).keydown(function (event) {
127 $(document).keydown(function (event) {
130 if(event._ipkmIgnore===true||(event.originalEvent||{})._ipkmIgnore===true){
128 if(event._ipkmIgnore===true||(event.originalEvent||{})._ipkmIgnore===true){
131 return false;
129 return false;
132 }
130 }
133 return that.handle_keydown(event);
131 return that.handle_keydown(event);
134 });
132 });
135 };
133 };
136
134
137 KeyboardManager.prototype.set_notebook = function (notebook) {
135 KeyboardManager.prototype.set_notebook = function (notebook) {
138 this.notebook = notebook;
136 this.notebook = notebook;
139 this.actions.extend_env({notebook:notebook});
137 this.actions.extend_env({notebook:notebook});
140 };
138 };
141
139
142 KeyboardManager.prototype.set_quickhelp = function (notebook) {
140 KeyboardManager.prototype.set_quickhelp = function (notebook) {
143 this.actions.extend_env({quick_help:notebook});
141 this.actions.extend_env({quick_help:notebook});
144 };
142 };
145
143
146
144
147 KeyboardManager.prototype.handle_keydown = function (event) {
145 KeyboardManager.prototype.handle_keydown = function (event) {
148 /**
146 /**
149 * returning false from this will stop event propagation
147 * returning false from this will stop event propagation
150 **/
148 **/
151
149
152 if (event.which === keycodes.esc) {
150 if (event.which === keycodes.esc) {
153 // Intercept escape at highest level to avoid closing
151 // Intercept escape at highest level to avoid closing
154 // websocket connection with firefox
152 // websocket connection with firefox
155 event.preventDefault();
153 event.preventDefault();
156 }
154 }
157
155
158 if (!this.enabled) {
156 if (!this.enabled) {
159 if (event.which === keycodes.esc) {
157 if (event.which === keycodes.esc) {
160 this.notebook.command_mode();
158 this.notebook.command_mode();
161 return false;
159 return false;
162 }
160 }
163 return true;
161 return true;
164 }
162 }
165
163
166 if (this.mode === 'edit') {
164 if (this.mode === 'edit') {
167 return this.edit_shortcuts.call_handler(event);
165 return this.edit_shortcuts.call_handler(event);
168 } else if (this.mode === 'command') {
166 } else if (this.mode === 'command') {
169 return this.command_shortcuts.call_handler(event);
167 return this.command_shortcuts.call_handler(event);
170 }
168 }
171 return true;
169 return true;
172 };
170 };
173
171
174 KeyboardManager.prototype.edit_mode = function () {
172 KeyboardManager.prototype.edit_mode = function () {
175 this.last_mode = this.mode;
173 this.last_mode = this.mode;
176 this.mode = 'edit';
174 this.mode = 'edit';
177 };
175 };
178
176
179 KeyboardManager.prototype.command_mode = function () {
177 KeyboardManager.prototype.command_mode = function () {
180 this.last_mode = this.mode;
178 this.last_mode = this.mode;
181 this.mode = 'command';
179 this.mode = 'command';
182 };
180 };
183
181
184 KeyboardManager.prototype.enable = function () {
182 KeyboardManager.prototype.enable = function () {
185 this.enabled = true;
183 this.enabled = true;
186 };
184 };
187
185
188 KeyboardManager.prototype.disable = function () {
186 KeyboardManager.prototype.disable = function () {
189 this.enabled = false;
187 this.enabled = false;
190 };
188 };
191
189
192 KeyboardManager.prototype.register_events = function (e) {
190 KeyboardManager.prototype.register_events = function (e) {
193 var that = this;
191 var that = this;
194 var handle_focus = function () {
192 var handle_focus = function () {
195 that.disable();
193 that.disable();
196 };
194 };
197 var handle_blur = function () {
195 var handle_blur = function () {
198 that.enable();
196 that.enable();
199 };
197 };
200 e.on('focusin', handle_focus);
198 e.on('focusin', handle_focus);
201 e.on('focusout', handle_blur);
199 e.on('focusout', handle_blur);
202 // TODO: Very strange. The focusout event does not seem fire for the
200 // TODO: Very strange. The focusout event does not seem fire for the
203 // bootstrap textboxes on FF25&26... This works around that by
201 // bootstrap textboxes on FF25&26... This works around that by
204 // registering focus and blur events recursively on all inputs within
202 // registering focus and blur events recursively on all inputs within
205 // registered element.
203 // registered element.
206 e.find('input').blur(handle_blur);
204 e.find('input').blur(handle_blur);
207 e.on('DOMNodeInserted', function (event) {
205 e.on('DOMNodeInserted', function (event) {
208 var target = $(event.target);
206 var target = $(event.target);
209 if (target.is('input')) {
207 if (target.is('input')) {
210 target.blur(handle_blur);
208 target.blur(handle_blur);
211 } else {
209 } else {
212 target.find('input').blur(handle_blur);
210 target.find('input').blur(handle_blur);
213 }
211 }
214 });
212 });
215 // There are times (raw_input) where we remove the element from the DOM before
213 // There are times (raw_input) where we remove the element from the DOM before
216 // focusout is called. In this case we bind to the remove event of jQueryUI,
214 // focusout is called. In this case we bind to the remove event of jQueryUI,
217 // which gets triggered upon removal, iff it is focused at the time.
215 // which gets triggered upon removal, iff it is focused at the time.
218 // is_focused must be used to check for the case where an element within
216 // is_focused must be used to check for the case where an element within
219 // the element being removed is focused.
217 // the element being removed is focused.
220 e.on('remove', function () {
218 e.on('remove', function () {
221 if (utils.is_focused(e[0])) {
219 if (utils.is_focused(e[0])) {
222 that.enable();
220 that.enable();
223 }
221 }
224 });
222 });
225 };
223 };
226
224
227
225
228 // For backwards compatibility.
226 // For backwards compatibility.
229 IPython.KeyboardManager = KeyboardManager;
227 IPython.KeyboardManager = KeyboardManager;
230
228
231 return {'KeyboardManager': KeyboardManager};
229 return {'KeyboardManager': KeyboardManager};
232 });
230 });
General Comments 0
You need to be logged in to leave comments. Login now