##// END OF EJS Templates
add test warning shadow
Matthias Bussonnier -
Show More
@@ -1,241 +1,231
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',
92 'ctrl-j' : 'ipython.move-selected-cell-down',
93 'ctrl-k' : 'ipython.move-selected-cell-up',
93 'ctrl-k' : 'ipython.move-selected-cell-up',
94 'enter' : 'ipython.enter-edit-mode',
94 'enter' : 'ipython.enter-edit-mode',
95 'space' : 'ipython.scroll-down',
95 'space' : 'ipython.scroll-down',
96 'down' : 'ipython.select-next-cell',
96 'down' : 'ipython.select-next-cell',
97 'i,i' : 'ipython.interrupt-kernel',
97 'i,i' : 'ipython.interrupt-kernel',
98 '0,0' : 'ipython.restart-kernel',
98 '0,0' : 'ipython.restart-kernel',
99 'd,d' : 'ipython.delete-cell',
99 'd,d' : 'ipython.delete-cell',
100 'up' : 'ipython.select-previous-cell',
100 'up' : 'ipython.select-previous-cell',
101 'k' : 'ipython.select-previous-cell',
101 'k' : 'ipython.select-previous-cell',
102 'j' : 'ipython.select-next-cell',
102 'j' : 'ipython.select-next-cell',
103 'x' : 'ipython.cut-selected-cell',
103 'x' : 'ipython.cut-selected-cell',
104 'c' : 'ipython.copy-selected-cell',
104 'c' : 'ipython.copy-selected-cell',
105 'v' : 'ipython.paste-cell-after',
105 'v' : 'ipython.paste-cell-after',
106 'a' : 'ipython.insert-cell-before',
106 'a' : 'ipython.insert-cell-before',
107 'b' : 'ipython.insert-cell-after',
107 'b' : 'ipython.insert-cell-after',
108 'y' : 'ipython.change-selected-cell-to-code-cell',
108 'y' : 'ipython.change-selected-cell-to-code-cell',
109 'm' : 'ipython.change-selected-cell-to-markdown-cell',
109 'm' : 'ipython.change-selected-cell-to-markdown-cell',
110 'r' : 'ipython.change-selected-cell-to-raw-cell',
110 'r' : 'ipython.change-selected-cell-to-raw-cell',
111 '1' : 'ipython.change-selected-cell-to-heading-1',
111 '1' : 'ipython.change-selected-cell-to-heading-1',
112 '2' : 'ipython.change-selected-cell-to-heading-2',
112 '2' : 'ipython.change-selected-cell-to-heading-2',
113 '3' : 'ipython.change-selected-cell-to-heading-3',
113 '3' : 'ipython.change-selected-cell-to-heading-3',
114 '4' : 'ipython.change-selected-cell-to-heading-4',
114 '4' : 'ipython.change-selected-cell-to-heading-4',
115 '5' : 'ipython.change-selected-cell-to-heading-5',
115 '5' : 'ipython.change-selected-cell-to-heading-5',
116 '6' : 'ipython.change-selected-cell-to-heading-6',
116 '6' : 'ipython.change-selected-cell-to-heading-6',
117 'o' : 'ipython.toggle-output-visibility-selected-cell',
117 'o' : 'ipython.toggle-output-visibility-selected-cell',
118 's' : 'ipython.save-notebook',
118 's' : 'ipython.save-notebook',
119 'l' : 'ipython.toggle-line-number-selected-cell',
119 'l' : 'ipython.toggle-line-number-selected-cell',
120 'h' : 'ipython.show-keyboard-shortcut-help-dialog',
120 'h' : 'ipython.show-keyboard-shortcut-help-dialog',
121 'z' : 'ipython.undo-last-cell-deletion',
121 'z' : 'ipython.undo-last-cell-deletion',
122 'q' : 'ipython.close-pager',
122 'q' : 'ipython.close-pager',
123 'i,e,e,e,e,e' : function(){console.log('[[===>>> 5E <<<===]]');},
124 'i,d,d,q,d' : function(){console.log('[[===>>> Trigger god mode <<<===]]');},
125 'i,d,d' : function(){console.log('[[===>>> should warn at registration <<<===]]');},
126 'i,d,k' : function(){console.log('[[===>>> Trigger shadow mode <<<===]]');},
127 'i,d,k,r,q' : function(){console.log('[[===>>> Trigger invisibility mode <<<===]]');},
128 ';,up,down,up,down,left,right,left,right,b,a' : function(){console.log('[[===>>> Konami <<<===]]');},
129 'ctrl-x,meta-c,meta-b,u,t,t,e,r,f,l,y' : function(){
130 console.log('[[Are you a real Programmer ?]]');
131 window.open('http://xkcd.com/378/','_blank');
132 },
133 };
123 };
134 };
124 };
135
125
136 KeyboardManager.prototype.bind_events = function () {
126 KeyboardManager.prototype.bind_events = function () {
137 var that = this;
127 var that = this;
138 $(document).keydown(function (event) {
128 $(document).keydown(function (event) {
139 if(event._ipkmIgnore===true||(event.originalEvent||{})._ipkmIgnore===true){
129 if(event._ipkmIgnore===true||(event.originalEvent||{})._ipkmIgnore===true){
140 return false;
130 return false;
141 }
131 }
142 return that.handle_keydown(event);
132 return that.handle_keydown(event);
143 });
133 });
144 };
134 };
145
135
146 KeyboardManager.prototype.set_notebook = function (notebook) {
136 KeyboardManager.prototype.set_notebook = function (notebook) {
147 this.notebook = notebook;
137 this.notebook = notebook;
148 this.actions.extend_env({notebook:notebook});
138 this.actions.extend_env({notebook:notebook});
149 };
139 };
150
140
151 KeyboardManager.prototype.set_quickhelp = function (notebook) {
141 KeyboardManager.prototype.set_quickhelp = function (notebook) {
152 this.actions.extend_env({quick_help:notebook});
142 this.actions.extend_env({quick_help:notebook});
153 };
143 };
154
144
155
145
156 KeyboardManager.prototype.handle_keydown = function (event) {
146 KeyboardManager.prototype.handle_keydown = function (event) {
157 /**
147 /**
158 * returning false from this will stop event propagation
148 * returning false from this will stop event propagation
159 **/
149 **/
160
150
161 if (event.which === keycodes.esc) {
151 if (event.which === keycodes.esc) {
162 // Intercept escape at highest level to avoid closing
152 // Intercept escape at highest level to avoid closing
163 // websocket connection with firefox
153 // websocket connection with firefox
164 event.preventDefault();
154 event.preventDefault();
165 }
155 }
166
156
167 if (!this.enabled) {
157 if (!this.enabled) {
168 if (event.which === keycodes.esc) {
158 if (event.which === keycodes.esc) {
169 this.notebook.command_mode();
159 this.notebook.command_mode();
170 return false;
160 return false;
171 }
161 }
172 return true;
162 return true;
173 }
163 }
174
164
175 if (this.mode === 'edit') {
165 if (this.mode === 'edit') {
176 return this.edit_shortcuts.call_handler(event);
166 return this.edit_shortcuts.call_handler(event);
177 } else if (this.mode === 'command') {
167 } else if (this.mode === 'command') {
178 return this.command_shortcuts.call_handler(event);
168 return this.command_shortcuts.call_handler(event);
179 }
169 }
180 return true;
170 return true;
181 };
171 };
182
172
183 KeyboardManager.prototype.edit_mode = function () {
173 KeyboardManager.prototype.edit_mode = function () {
184 this.last_mode = this.mode;
174 this.last_mode = this.mode;
185 this.mode = 'edit';
175 this.mode = 'edit';
186 };
176 };
187
177
188 KeyboardManager.prototype.command_mode = function () {
178 KeyboardManager.prototype.command_mode = function () {
189 this.last_mode = this.mode;
179 this.last_mode = this.mode;
190 this.mode = 'command';
180 this.mode = 'command';
191 };
181 };
192
182
193 KeyboardManager.prototype.enable = function () {
183 KeyboardManager.prototype.enable = function () {
194 this.enabled = true;
184 this.enabled = true;
195 };
185 };
196
186
197 KeyboardManager.prototype.disable = function () {
187 KeyboardManager.prototype.disable = function () {
198 this.enabled = false;
188 this.enabled = false;
199 };
189 };
200
190
201 KeyboardManager.prototype.register_events = function (e) {
191 KeyboardManager.prototype.register_events = function (e) {
202 var that = this;
192 var that = this;
203 var handle_focus = function () {
193 var handle_focus = function () {
204 that.disable();
194 that.disable();
205 };
195 };
206 var handle_blur = function () {
196 var handle_blur = function () {
207 that.enable();
197 that.enable();
208 };
198 };
209 e.on('focusin', handle_focus);
199 e.on('focusin', handle_focus);
210 e.on('focusout', handle_blur);
200 e.on('focusout', handle_blur);
211 // TODO: Very strange. The focusout event does not seem fire for the
201 // TODO: Very strange. The focusout event does not seem fire for the
212 // bootstrap textboxes on FF25&26... This works around that by
202 // bootstrap textboxes on FF25&26... This works around that by
213 // registering focus and blur events recursively on all inputs within
203 // registering focus and blur events recursively on all inputs within
214 // registered element.
204 // registered element.
215 e.find('input').blur(handle_blur);
205 e.find('input').blur(handle_blur);
216 e.on('DOMNodeInserted', function (event) {
206 e.on('DOMNodeInserted', function (event) {
217 var target = $(event.target);
207 var target = $(event.target);
218 if (target.is('input')) {
208 if (target.is('input')) {
219 target.blur(handle_blur);
209 target.blur(handle_blur);
220 } else {
210 } else {
221 target.find('input').blur(handle_blur);
211 target.find('input').blur(handle_blur);
222 }
212 }
223 });
213 });
224 // There are times (raw_input) where we remove the element from the DOM before
214 // There are times (raw_input) where we remove the element from the DOM before
225 // focusout is called. In this case we bind to the remove event of jQueryUI,
215 // focusout is called. In this case we bind to the remove event of jQueryUI,
226 // which gets triggered upon removal, iff it is focused at the time.
216 // which gets triggered upon removal, iff it is focused at the time.
227 // is_focused must be used to check for the case where an element within
217 // is_focused must be used to check for the case where an element within
228 // the element being removed is focused.
218 // the element being removed is focused.
229 e.on('remove', function () {
219 e.on('remove', function () {
230 if (utils.is_focused(e[0])) {
220 if (utils.is_focused(e[0])) {
231 that.enable();
221 that.enable();
232 }
222 }
233 });
223 });
234 };
224 };
235
225
236
226
237 // For backwards compatibility.
227 // For backwards compatibility.
238 IPython.KeyboardManager = KeyboardManager;
228 IPython.KeyboardManager = KeyboardManager;
239
229
240 return {'KeyboardManager': KeyboardManager};
230 return {'KeyboardManager': KeyboardManager};
241 });
231 });
@@ -1,49 +1,84
1
1
2
2
3 var normalized_shortcuts = [
3 var normalized_shortcuts = [
4 'ctrl-shift-m',
4 'ctrl-shift-m',
5 'alt-meta-p',
5 'alt-meta-p',
6 ];
6 ];
7
7
8 var to_normalize = [
8 var to_normalize = [
9 ['shift-%', 'shift-5'],
9 ['shift-%', 'shift-5'],
10 ['ShiFT-MeTa-CtRl-AlT-m', 'alt-ctrl-meta-shift-m'],
10 ['ShiFT-MeTa-CtRl-AlT-m', 'alt-ctrl-meta-shift-m'],
11 ];
11 ];
12
12
13 var unshifted = "` 1 2 3 4 5 6 7 8 9 0 - = q w e r t y u i o p [ ] \\ a s d f g h j k l ; ' z x c v b n m , . /";
13 var unshifted = "` 1 2 3 4 5 6 7 8 9 0 - = q w e r t y u i o p [ ] \\ a s d f g h j k l ; ' z x c v b n m , . /";
14 // shifted = '~ ! @ # $ % ^ & * ( ) _ + Q W E R T Y U I O P { } | A S D F G H J K L : " Z X C V B N M < > ?';
14 // shifted = '~ ! @ # $ % ^ & * ( ) _ + Q W E R T Y U I O P { } | A S D F G H J K L : " Z X C V B N M < > ?';
15
15
16
16 casper.notebook_test(function () {
17 casper.notebook_test(function () {
18 var that = this;
17
19
18 this.then(function () {
20 this.then(function () {
19 this.each(unshifted.split(' '), function (self, item) {
21 this.each(unshifted.split(' '), function (self, item) {
20 var result = this.evaluate(function (sc) {
22 var result = this.evaluate(function (sc) {
21 var e = IPython.keyboard.shortcut_to_event(sc);
23 var e = IPython.keyboard.shortcut_to_event(sc);
22 var sc2 = IPython.keyboard.event_to_shortcut(e);
24 var sc2 = IPython.keyboard.event_to_shortcut(e);
23 return sc2;
25 return sc2;
24 }, item);
26 }, item);
25 this.test.assertEquals(result, item, 'Shortcut to event roundtrip: '+item);
27 this.test.assertEquals(result, item, 'Shortcut to event roundtrip: '+item);
26 });
28 });
27 });
29 });
28
30
29 this.then(function () {
31 this.then(function () {
30 this.each(to_normalize, function (self, item) {
32 this.each(to_normalize, function (self, item) {
31 var result = this.evaluate(function (pair) {
33 var result = this.evaluate(function (pair) {
32 return IPython.keyboard.normalize_shortcut(pair[0]);
34 return IPython.keyboard.normalize_shortcut(pair[0]);
33 }, item);
35 }, item);
34 this.test.assertEquals(result, item[1], 'Normalize shortcut: '+item[0]);
36 this.test.assertEquals(result, item[1], 'Normalize shortcut: '+item[0]);
35 });
37 });
36 });
38 });
37
39
38 this.then(function () {
40 this.then(function () {
39 this.each(normalized_shortcuts, function (self, item) {
41 this.each(normalized_shortcuts, function (self, item) {
40 var result = this.evaluate(function (sc) {
42 var result = this.evaluate(function (sc) {
41 var e = IPython.keyboard.shortcut_to_event(sc);
43 var e = IPython.keyboard.shortcut_to_event(sc);
42 var sc2 = IPython.keyboard.event_to_shortcut(e);
44 var sc2 = IPython.keyboard.event_to_shortcut(e);
43 return sc2;
45 return sc2;
44 }, item);
46 }, item);
45 this.test.assertEquals(result, item, 'Shortcut to event roundtrip: '+item);
47 this.test.assertEquals(result, item, 'Shortcut to event roundtrip: '+item);
46 });
48 });
47 });
49 });
48
50
51 this.then(function(){
52
53 var shortcuts_test = {
54 'i,e,e,e,e,e' : '[[5E]]',
55 'i,d,d,q,d' : '[[TEST1]]',
56 'i,d,d' : '[[TEST1 WILL BE SHADOWED]]',
57 'i,d,k' : '[[SHOULD SHADOW TEST2]]',
58 'i,d,k,r,q' : '[[TEST2 NOT SHADOWED]]',
59 ';,up,down,up,down,left,right,left,right,b,a' : '[[KONAMI]]',
60 'ctrl-x,meta-c,meta-b,u,t,t,e,r,f,l,y' : '[[XKCD]]'
61
62 };
63
64 that.msgs = [];
65 that.on('remote.message', function(msg) {
66 that.msgs.push(msg);
67 })
68 that.evaluate(function (obj) {
69 for(var k in obj){
70 IPython.keyboard_manager.command_shortcuts.add_shortcut(k, function(){console.log(obj[k])});
71 }
72 }, shortcuts_test);
73
74 var longer_first = false;
75 var longer_last = false;
76 for(var m in that.msgs){
77 longer_first = longer_first||(that.msgs[m].match(/you are overriting/)!= null);
78 longer_last = longer_last ||(that.msgs[m].match(/will be shadowed/) != null);
79 }
80 this.test.assert(longer_first, 'no warnign if registering shorter shortut');
81 this.test.assert(longer_last , 'no warnign if registering longer shortut');
82 })
83
49 });
84 });
General Comments 0
You need to be logged in to leave comments. Login now