##// END OF EJS Templates
Add notion of action that differs from shortcuts....
Matthias Bussonnier -
Show More
This diff has been collapsed as it changes many lines, (503 lines changed) Show them Hide them
@@ -0,0 +1,503 b''
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
3
4 define(['require'
5 ], function(require) {
6 "use strict";
7
8 var ActionHandler = function (env) {
9 this.env = env || {};
10 Object.seal(this);
11 };
12
13 /**
14 * A bunch of predefined `Simple Actions` used by IPython.
15 * `Simple Actions` have the following keys:
16 * help (optional): a short string the describe the action.
17 * will be used in various context, like as menu name, tool tips on buttons,
18 * and short description in help menu.
19 * help_index (optional): a string used to sort action in help menu.
20 * icon (optional): a short string that represent the icon that have to be used with this
21 * action. this should mainly correspond to a Font_awesome class.
22 * handler : a function which is called when the action is activated. It will receive at first parameter
23 * a dictionary containing various handle to element of the notebook.
24 *
25 * action need to be registered with a **name** that can be use to refer to this action.
26 *
27 *
28 * if `help` is not provided it will be derived by replacing any dash by space
29 * in the **name** of the action. It is advised to provide a prefix to action name to
30 * avoid conflict the prefix should be all lowercase and end with a dot `.`
31 * in the absence of a prefix the behavior of the action is undefined.
32 *
33 * All action provided by IPython are prefixed with `ipython.`.
34 *
35 * One can register extra actions or replace an existing action with another one is possible
36 * but is considered undefined behavior.
37 *
38 **/
39 var _action = {
40 'run-select-next': {
41 icon: 'fa-play',
42 help : 'run cell, select below',
43 help_index : 'ba',
44 handler : function (env) {
45 env.notebook.execute_cell_and_select_below();
46 }
47 },
48 'execute-in-place':{
49 help : 'run cell',
50 help_index : 'bb',
51 handler : function (env) {
52 env.notebook.execute_cell();
53 }
54 },
55 'execute-and-insert-after':{
56 help : 'run cell, insert below',
57 help_index : 'bc',
58 handler : function (env) {
59 env.notebook.execute_cell_and_insert_below();
60 }
61 },
62 'go-to-command-mode': {
63 help : 'command mode',
64 help_index : 'aa',
65 handler : function (env) {
66 env.notebook.command_mode();
67 }
68 },
69 'split-cell-at-cursor': {
70 help : 'split cell',
71 help_index : 'ea',
72 handler : function (env) {
73 env.notebook.split_cell();
74 }
75 },
76 'enter-edit-mode' : {
77 help_index : 'aa',
78 handler : function (env) {
79 env.notebook.edit_mode();
80 }
81 },
82 'select-previous-cell' : {
83 help_index : 'da',
84 handler : function (env) {
85 var index = env.notebook.get_selected_index();
86 if (index !== 0 && index !== null) {
87 env.notebook.select_prev();
88 env.notebook.focus_cell();
89 }
90 }
91 },
92 'select-next-cell' : {
93 help_index : 'db',
94 handler : function (env) {
95 var index = env.notebook.get_selected_index();
96 if (index !== (env.notebook.ncells()-1) && index !== null) {
97 env.notebook.select_next();
98 env.notebook.focus_cell();
99 }
100 }
101 },
102 'cut-selected-cell' : {
103 icon: 'fa-cut',
104 help_index : 'ee',
105 handler : function (env) {
106 env.notebook.cut_cell();
107 }
108 },
109 'copy-selected-cell' : {
110 icon: 'fa-copy',
111 help_index : 'ef',
112 handler : function (env) {
113 env.notebook.copy_cell();
114 }
115 },
116 'paste-cell-before' : {
117 help_index : 'eg',
118 handler : function (env) {
119 env.notebook.paste_cell_above();
120 }
121 },
122 'paste-cell-after' : {
123 icon: 'fa-paste',
124 help_index : 'eh',
125 handler : function (env) {
126 env.notebook.paste_cell_below();
127 }
128 },
129 'insert-cell-before' : {
130 help_index : 'ec',
131 handler : function (env) {
132 env.notebook.insert_cell_above();
133 env.notebook.select_prev();
134 env.notebook.focus_cell();
135 }
136 },
137 'insert-cell-after' : {
138 icon : 'fa-plus',
139 help_index : 'ed',
140 handler : function (env) {
141 env.notebook.insert_cell_below();
142 env.notebook.select_next();
143 env.notebook.focus_cell();
144 }
145 },
146 'change-selected-cell-to-code-cell' : {
147 help : 'to code',
148 help_index : 'ca',
149 handler : function (env) {
150 env.notebook.to_code();
151 }
152 },
153 'change-selected-cell-to-markdown-cell' : {
154 help : 'to markdown',
155 help_index : 'cb',
156 handler : function (env) {
157 env.notebook.to_markdown();
158 }
159 },
160 'change-selected-cell-to-raw-cell' : {
161 help : 'to raw',
162 help_index : 'cc',
163 handler : function (env) {
164 env.notebook.to_raw();
165 }
166 },
167 'change-selected-cell-to-heading-1' : {
168 help : 'to heading 1',
169 help_index : 'cd',
170 handler : function (env) {
171 env.notebook.to_heading(undefined, 1);
172 }
173 },
174 'change-selected-cell-to-heading-2' : {
175 help : 'to heading 2',
176 help_index : 'ce',
177 handler : function (env) {
178 env.notebook.to_heading(undefined, 2);
179 }
180 },
181 'change-selected-cell-to-heading-3' : {
182 help : 'to heading 3',
183 help_index : 'cf',
184 handler : function (env) {
185 env.notebook.to_heading(undefined, 3);
186 }
187 },
188 'change-selected-cell-to-heading-4' : {
189 help : 'to heading 4',
190 help_index : 'cg',
191 handler : function (env) {
192 env.notebook.to_heading(undefined, 4);
193 }
194 },
195 'change-selected-cell-to-heading-5' : {
196 help : 'to heading 5',
197 help_index : 'ch',
198 handler : function (env) {
199 env.notebook.to_heading(undefined, 5);
200 }
201 },
202 'change-selected-cell-to-heading-6' : {
203 help : 'to heading 6',
204 help_index : 'ci',
205 handler : function (env) {
206 env.notebook.to_heading(undefined, 6);
207 }
208 },
209 'toggle-output-visibility-selected-cell' : {
210 help : 'toggle output',
211 help_index : 'gb',
212 handler : function (env) {
213 env.notebook.toggle_output();
214 }
215 },
216 'toggle-output-scrolling-selected-cell' : {
217 help : 'toggle output scrolling',
218 help_index : 'gc',
219 handler : function (env) {
220 env.notebook.toggle_output_scroll();
221 }
222 },
223 'move-selected-cell-down' : {
224 icon: 'fa-arrow-down',
225 help_index : 'eb',
226 handler : function (env) {
227 env.notebook.move_cell_down();
228 }
229 },
230 'move-selected-cell-up' : {
231 icon: 'fa-arrow-up',
232 help_index : 'ea',
233 handler : function (env) {
234 env.notebook.move_cell_up();
235 }
236 },
237 'toggle-line-number-selected-cell' : {
238 help : 'toggle line numbers',
239 help_index : 'ga',
240 handler : function (env) {
241 env.notebook.cell_toggle_line_numbers();
242 }
243 },
244 'show-keyboard-shortcut-help-dialog' : {
245 help_index : 'ge',
246 handler : function (env) {
247 env.quick_help.show_keyboard_shortcuts();
248 }
249 },
250 'delete-cell': {
251 help_index : 'ej',
252 handler : function (env) {
253 env.notebook.delete_cell();
254 }
255 },
256 'interrupt-kernel':{
257 icon: 'fa-stop',
258 help_index : 'ha',
259 handler : function (env) {
260 env.notebook.kernel.interrupt();
261 }
262 },
263 'restart-kernel':{
264 icon: 'fa-repeat',
265 help_index : 'hb',
266 handler : function (env) {
267 env.notebook.restart_kernel();
268 }
269 },
270 'undo-last-cell-deletion' : {
271 help_index : 'ei',
272 handler : function (env) {
273 env.notebook.undelete_cell();
274 }
275 },
276 'merge-selected-cell-with-cell-after' : {
277 help : 'merge cell below',
278 help_index : 'ek',
279 handler : function (env) {
280 env.notebook.merge_cell_below();
281 }
282 },
283 'close-pager' : {
284 help_index : 'gd',
285 handler : function (env) {
286 env.pager.collapse();
287 }
288 }
289
290 };
291
292 /**
293 * A bunch of `Advance actions` for IPython.
294 * Cf `Simple Action` plus the following properties.
295 *
296 * handler: first argument of the handler is the event that triggerd the action
297 * (typically keypress). The handler is responsible for any modification of the
298 * event and event propagation.
299 * Is also responsible for returning false if the event have to be further ignored,
300 * true, to tell keyboard manager that it ignored the event.
301 *
302 * the second parameter of the handler is the environemnt passed to Simple Actions
303 *
304 **/
305 var custom_ignore = {
306 'ignore':{
307 handler : function () {
308 return true;
309 }
310 },
311 'move-cursor-up-or-previous-cell':{
312 handler : function (env, event) {
313 var index = env.notebook.get_selected_index();
314 var cell = env.notebook.get_cell(index);
315 var cm = env.notebook.get_selected_cell().code_mirror;
316 var cur = cm.getCursor();
317 if (cell && cell.at_top() && index !== 0 && cur.ch === 0) {
318 if(event){
319 event.preventDefault();
320 }
321 env.notebook.command_mode();
322 env.notebook.select_prev();
323 env.notebook.edit_mode();
324 cm = env.notebook.get_selected_cell().code_mirror;
325 cm.setCursor(cm.lastLine(), 0);
326 }
327 return false;
328 }
329 },
330 'move-cursor-down-or-next-cell':{
331 handler : function (env, event) {
332 var index = env.notebook.get_selected_index();
333 var cell = env.notebook.get_cell(index);
334 if (cell.at_bottom() && index !== (env.notebook.ncells()-1)) {
335 if(event){
336 event.preventDefault();
337 }
338 env.notebook.command_mode();
339 env.notebook.select_next();
340 env.notebook.edit_mode();
341 var cm = env.notebook.get_selected_cell().code_mirror;
342 cm.setCursor(0, 0);
343 }
344 return false;
345 }
346 },
347 'scroll-down': {
348 handler: function(env, event) {
349 if(event){
350 event.preventDefault();
351 }
352 return env.notebook.scroll_manager.scroll(1);
353 },
354 },
355 'scroll-up': {
356 handler: function(env, event) {
357 if(event){
358 event.preventDefault();
359 }
360 return env.notebook.scroll_manager.scroll(-1);
361 },
362 },
363 'save-notebook':{
364 help: "Save and Checkpoint",
365 help_index : 'fb',
366 icon: 'fa-save',
367 handler : function (env, event) {
368 env.notebook.save_checkpoint();
369 if(event){
370 event.preventDefault();
371 }
372 return false;
373 }
374 },
375 };
376
377 // private stuff that prepend `.ipython` to actions names
378 // and uniformize/fill in missing pieces in of an action.
379 var _prepare_handler = function(registry, subkey, source){
380 registry['ipython.'+subkey] = {};
381 registry['ipython.'+subkey].help = source[subkey].help||subkey.replace(/-/g,' ');
382 registry['ipython.'+subkey].help_index = source[subkey].help_index;
383 registry['ipython.'+subkey].icon = source[subkey].icon;
384 return source[subkey].handler;
385 };
386
387 // Will actually generate/register all the IPython actions
388 var fun = function(){
389 var final_actions = {};
390 for(var k in _action){
391 // Js closure are function level not block level need to wrap in a IIFE
392 // and append ipython to event name these things do intercept event so are wrapped
393 // in a function that return false.
394 var handler = _prepare_handler(final_actions, k, _action);
395 (function(key, handler){
396 final_actions['ipython.'+key].handler = function(env, event){
397 handler(env);
398 if(event){
399 event.preventDefault();
400 }
401 return false;
402 };
403 })(k, handler);
404 }
405
406 for(var k in custom_ignore){
407 // Js closure are function level not block level need to wrap in a IIFE
408 // same as above, but decide for themselves wether or not they intercept events.
409 var handler = _prepare_handler(final_actions, k, custom_ignore);
410 (function(key, handler){
411 final_actions['ipython.'+key].handler = function(env, event){
412 return handler(env, event);
413 };
414 })(k, handler);
415 }
416
417 return final_actions;
418 };
419 ActionHandler.prototype._actions = fun();
420
421
422 /**
423 * extend the environment variable that will be pass to handlers
424 **/
425 ActionHandler.prototype.extend_env = function(env){
426 for(var k in env){
427 this.env[k] = env[k];
428 }
429 };
430
431 ActionHandler.prototype.register = function(action, name, prefix){
432 /**
433 * Register an `action` with an optional name and prefix.
434 *
435 * if name and prefix are not given they will be determined automatically.
436 * if action if just a `function` it will be wrapped in an anonymous action.
437 *
438 * @return the full name to access this action .
439 **/
440 action = this.normalise(action);
441 if( !name ){
442 name = 'autogenerated-'+String(action.handler);
443 }
444 prefix = prefix || 'auto';
445 var full_name = prefix+'.'+name;
446 this._actions[full_name] = action;
447 return full_name;
448
449 };
450
451
452 ActionHandler.prototype.normalise = function(data){
453 /**
454 * given an `action` or `function`, return a normalised `action`
455 * by setting all known attributes and removing unknown attributes;
456 **/
457 if(typeof(data) === 'function'){
458 data = {handler:data};
459 }
460 if(typeof(data.handler) !== 'function'){
461 throw('unknown datatype, cannot register');
462 }
463 var _data = data;
464 data = {};
465 data.handler = _data.handler;
466 data.help = data.help || '';
467 data.icon = data.icon || '';
468 data.help_index = data.help_index || '';
469 return data;
470 };
471
472 ActionHandler.prototype.get_name = function(name_or_data){
473 /**
474 * given an `action` or `name` of a action, return the name attached to this action.
475 * if given the name of and corresponding actions does not exist in registry, return `null`.
476 **/
477
478 if(typeof(name_or_data) === 'string'){
479 if(this.exists(name_or_data)){
480 return name_or_data;
481 } else {
482 return null;
483 }
484 } else {
485 return this.register(name_or_data);
486 }
487 };
488
489 ActionHandler.prototype.get = function(name){
490 return this._actions[name];
491 };
492
493 ActionHandler.prototype.call = function(name, event, env){
494 return this._actions[name].handler(env|| this.env, event);
495 };
496
497 ActionHandler.prototype.exists = function(name){
498 return (typeof(this._actions[name]) !== 'undefined');
499 };
500
501 return {init:ActionHandler};
502
503 });
@@ -12,18 +12,22 b' define(['
12 'base/js/namespace',
12 'base/js/namespace',
13 'jquery',
13 'jquery',
14 'base/js/utils',
14 'base/js/utils',
15 ], function(IPython, $, utils) {
15 'underscore',
16 ], function(IPython, $, utils, _) {
16 "use strict";
17 "use strict";
17
18
18
19
19 // Setup global keycodes and inverse keycodes.
20 /**
21 * Setup global keycodes and inverse keycodes.
22 *
23 * See http://unixpapa.com/js/key.html for a complete description. The short of
24 * it is that there are different keycode sets. Firefox uses the "Mozilla keycodes"
25 * and Webkit/IE use the "IE keycodes". These keycode sets are mostly the same
26 * but have minor differences.
27 **/
20
28
21 // See http://unixpapa.com/js/key.html for a complete description. The short of
29 // These apply to Firefox, (Webkit and IE)
22 // it is that there are different keycode sets. Firefox uses the "Mozilla keycodes"
30 // This does work **only** on US keyboard.
23 // and Webkit/IE use the "IE keycodes". These keycode sets are mostly the same
24 // but have minor differences.
25
26 // These apply to Firefox, (Webkit and IE)
27 var _keycodes = {
31 var _keycodes = {
28 'a': 65, 'b': 66, 'c': 67, 'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73,
32 'a': 65, 'b': 66, 'c': 67, 'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73,
29 'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81, 'r': 82,
33 'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81, 'r': 82,
@@ -84,13 +88,32 b' define(['
84 };
88 };
85
89
86 var normalize_shortcut = function (shortcut) {
90 var normalize_shortcut = function (shortcut) {
87 // Put a shortcut into normalized form:
91 /**
88 // 1. Make lowercase
92 * @function _normalize_shortcut
89 // 2. Replace cmd by meta
93 * @private
90 // 3. Sort '-' separated modifiers into the order alt-ctrl-meta-shift
94 * return a dict containing the normalized shortcut and the number of time it should be pressed:
91 // 4. Normalize keys
95 *
96 * Put a shortcut into normalized form:
97 * 1. Make lowercase
98 * 2. Replace cmd by meta
99 * 3. Sort '-' separated modifiers into the order alt-ctrl-meta-shift
100 * 4. Normalize keys
101 **/
102 if (platform === 'MacOS') {
103 shortcut = shortcut.toLowerCase().replace('cmdtrl-', 'cmd-');
104 } else {
105 shortcut = shortcut.toLowerCase().replace('cmdtrl-', 'ctrl-');
106 }
107
92 shortcut = shortcut.toLowerCase().replace('cmd', 'meta');
108 shortcut = shortcut.toLowerCase().replace('cmd', 'meta');
93 shortcut = shortcut.replace(/-$/, '_'); // catch shortcuts using '-' key
109 shortcut = shortcut.replace(/-$/, '_'); // catch shortcuts using '-' key
110 shortcut = shortcut.replace(/,$/, 'comma'); // catch shortcuts using '-' key
111 if(shortcut.indexOf(',') !== -1){
112 var sht = shortcut.split(',');
113 sht = _.map(sht, normalize_shortcut);
114 return shortcut;
115 }
116 shortcut = shortcut.replace(/comma/g, ','); // catch shortcuts using '-' key
94 var values = shortcut.split("-");
117 var values = shortcut.split("-");
95 if (values.length === 1) {
118 if (values.length === 1) {
96 return normalize_key(values[0]);
119 return normalize_key(values[0]);
@@ -103,7 +126,9 b' define(['
103 };
126 };
104
127
105 var shortcut_to_event = function (shortcut, type) {
128 var shortcut_to_event = function (shortcut, type) {
106 // Convert a shortcut (shift-r) to a jQuery Event object
129 /**
130 * Convert a shortcut (shift-r) to a jQuery Event object
131 **/
107 type = type || 'keydown';
132 type = type || 'keydown';
108 shortcut = normalize_shortcut(shortcut);
133 shortcut = normalize_shortcut(shortcut);
109 shortcut = shortcut.replace(/-$/, '_'); // catch shortcuts using '-' key
134 shortcut = shortcut.replace(/-$/, '_'); // catch shortcuts using '-' key
@@ -118,8 +143,21 b' define(['
118 return $.Event(type, opts);
143 return $.Event(type, opts);
119 };
144 };
120
145
146 var only_modifier_event = function(event){
147 /**
148 * Return `true` if the event only contains modifiers keys.
149 * false otherwise
150 **/
151 var key = inv_keycodes[event.which];
152 return ((event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) &&
153 (key === 'alt'|| key === 'ctrl'|| key === 'meta'|| key === 'shift'));
154
155 };
156
121 var event_to_shortcut = function (event) {
157 var event_to_shortcut = function (event) {
122 // Convert a jQuery Event object to a shortcut (shift-r)
158 /**
159 * Convert a jQuery Event object to a normalized shortcut string (shift-r)
160 **/
123 var shortcut = '';
161 var shortcut = '';
124 var key = inv_keycodes[event.which];
162 var key = inv_keycodes[event.which];
125 if (event.altKey && key !== 'alt') {shortcut += 'alt-';}
163 if (event.altKey && key !== 'alt') {shortcut += 'alt-';}
@@ -132,7 +170,7 b' define(['
132
170
133 // Shortcut manager class
171 // Shortcut manager class
134
172
135 var ShortcutManager = function (delay, events) {
173 var ShortcutManager = function (delay, events, actions, env) {
136 /**
174 /**
137 * A class to deal with keyboard event and shortcut
175 * A class to deal with keyboard event and shortcut
138 *
176 *
@@ -140,33 +178,78 b' define(['
140 * @constructor
178 * @constructor
141 */
179 */
142 this._shortcuts = {};
180 this._shortcuts = {};
143 this._counts = {};
144 this._timers = {};
145 this.delay = delay || 800; // delay in milliseconds
181 this.delay = delay || 800; // delay in milliseconds
146 this.events = events;
182 this.events = events;
183 this.actions = actions;
184 this.actions.extend_env(env);
185 this._queue = [];
186 this._cleartimeout = null;
187 Object.seal(this);
188 };
189
190 ShortcutManager.prototype.clearsoon = function(){
191 /**
192 * Clear the pending shortcut soon, and cancel previous clearing
193 * that might be registered.
194 **/
195 var that = this;
196 clearTimeout(this._cleartimeout);
197 this._cleartimeout = setTimeout(function(){that.clearqueue();}, this.delay);
198 };
199
200
201 ShortcutManager.prototype.clearqueue = function(){
202 /**
203 * clear the pending shortcut sequence now.
204 **/
205 this._queue = [];
206 clearTimeout(this._cleartimeout);
207 };
208
209
210 var flatten_shorttree = function(tree){
211 /**
212 * Flatten a tree of shortcut sequences.
213 * use full to iterate over all the key/values of available shortcuts.
214 **/
215 var dct = {};
216 for(var key in tree){
217 var value = tree[key];
218 if(typeof(value) === 'string'){
219 dct[key] = value;
220 } else {
221 var ftree=flatten_shorttree(value);
222 for(var subkey in ftree){
223 dct[key+','+subkey] = ftree[subkey];
224 }
225 }
226 }
227 return dct;
147 };
228 };
148
229
149 ShortcutManager.prototype.help = function () {
230 ShortcutManager.prototype.help = function () {
150 var help = [];
231 var help = [];
151 for (var shortcut in this._shortcuts) {
232 var ftree = flatten_shorttree(this._shortcuts);
152 var help_string = this._shortcuts[shortcut].help;
233 for (var shortcut in ftree) {
153 var help_index = this._shortcuts[shortcut].help_index;
234 var action = this.actions.get(ftree[shortcut]);
235 var help_string = action.help||'== no help ==';
236 var help_index = action.help_index;
154 if (help_string) {
237 if (help_string) {
155 if (platform === 'MacOS') {
238 var shortstring = (action.shortstring||shortcut);
156 shortcut = shortcut.replace('meta', 'cmd');
157 }
158 help.push({
239 help.push({
159 shortcut: shortcut,
240 shortcut: shortstring,
160 help: help_string,
241 help: help_string,
161 help_index: help_index}
242 help_index: help_index}
162 );
243 );
163 }
244 }
164 }
245 }
165 help.sort(function (a, b) {
246 help.sort(function (a, b) {
166 if (a.help_index > b.help_index)
247 if (a.help_index > b.help_index){
167 return 1;
248 return 1;
168 if (a.help_index < b.help_index)
249 }
250 if (a.help_index < b.help_index){
169 return -1;
251 return -1;
252 }
170 return 0;
253 return 0;
171 });
254 });
172 return help;
255 return help;
@@ -176,19 +259,105 b' define(['
176 this._shortcuts = {};
259 this._shortcuts = {};
177 };
260 };
178
261
179 ShortcutManager.prototype.add_shortcut = function (shortcut, data, suppress_help_update) {
262 ShortcutManager.prototype.get_shortcut = function (shortcut){
180 if (typeof(data) === 'function') {
263 /**
181 data = {help: '', help_index: '', handler: data};
264 * return a node of the shortcut tree which an action name (string) if leaf,
265 * and an object with `object.subtree===true`
266 **/
267 if(typeof(shortcut) === 'string'){
268 shortcut = shortcut.split(',');
269 }
270
271 return this._get_leaf(shortcut, this._shortcuts);
272 };
273
274
275 ShortcutManager.prototype._get_leaf = function(shortcut_array, tree){
276 /**
277 * @private
278 * find a leaf/node in a subtree of the keyboard shortcut
279 *
280 **/
281 if(shortcut_array.length === 1){
282 return tree[shortcut_array[0]];
283 } else if( typeof(tree[shortcut_array[0]]) !== 'string'){
284 return this._get_leaf(shortcut_array.slice(1), tree[shortcut_array[0]]);
285 }
286 return null;
287 };
288
289 ShortcutManager.prototype.set_shortcut = function( shortcut, action_name){
290 if( typeof(action_name) !== 'string'){ throw('action is not a string', action_name);}
291 if( typeof(shortcut) === 'string'){
292 shortcut = shortcut.split(',');
293 }
294 return this._set_leaf(shortcut, action_name, this._shortcuts);
295 };
296
297 ShortcutManager.prototype._is_leaf = function(shortcut_array, tree){
298 if(shortcut_array.length === 1){
299 return(typeof(tree[shortcut_array[0]]) === 'string');
300 } else {
301 var subtree = tree[shortcut_array[0]];
302 return this._is_leaf(shortcut_array.slice(1), subtree );
303 }
304 };
305
306 ShortcutManager.prototype._remove_leaf = function(shortcut_array, tree, allow_node){
307 if(shortcut_array.length === 1){
308 var current_node = tree[shortcut_array[0]];
309 if(typeof(current_node) === 'string'){
310 delete tree[shortcut_array[0]];
311 } else {
312 throw('try to delete non-leaf');
313 }
314 } else {
315 this._remove_leaf(shortcut_array.slice(1), tree[shortcut_array[0]], allow_node);
316 if(_.keys(tree[shortcut_array[0]]).length === 0){
317 delete tree[shortcut_array[0]];
318 }
319 }
320 };
321
322 ShortcutManager.prototype._set_leaf = function(shortcut_array, action_name, tree){
323 var current_node = tree[shortcut_array[0]];
324 if(shortcut_array.length === 1){
325 if(current_node !== undefined && typeof(current_node) !== 'string'){
326 console.warn('[warning], you are overriting a long shortcut with a shorter one');
327 }
328 tree[shortcut_array[0]] = action_name;
329 return true;
330 } else {
331 if(typeof(current_node) === 'string'){
332 console.warn('you are trying to set a shortcut that will be shadowed'+
333 'by a more specific one. Aborting for :', action_name, 'the follwing '+
334 'will take precedence', current_node);
335 return false;
336 } else {
337 tree[shortcut_array[0]] = tree[shortcut_array[0]]||{};
338 }
339 this._set_leaf(shortcut_array.slice(1), action_name, tree[shortcut_array[0]]);
340 return true;
182 }
341 }
183 data.help_index = data.help_index || '';
342 };
184 data.help = data.help || '';
343
185 data.count = data.count || 1;
344 ShortcutManager.prototype.add_shortcut = function (shortcut, data, suppress_help_update) {
186 if (data.help_index === '') {
345 /**
187 data.help_index = 'zz';
346 * Add a action to be handled by shortcut manager.
347 *
348 * - `shortcut` should be a `Shortcut Sequence` of the for `Ctrl-Alt-C,Meta-X`...
349 * - `data` could be an `action name`, an `action` or a `function`.
350 * if a `function` is passed it will be converted to an anonymous `action`.
351 *
352 **/
353 var action_name = this.actions.get_name(data);
354 if (! action_name){
355 throw('does nto know how to deal with ', data);
188 }
356 }
357
189 shortcut = normalize_shortcut(shortcut);
358 shortcut = normalize_shortcut(shortcut);
190 this._counts[shortcut] = 0;
359 this.set_shortcut(shortcut, action_name);
191 this._shortcuts[shortcut] = data;
360
192 if (!suppress_help_update) {
361 if (!suppress_help_update) {
193 // update the keyboard shortcuts notebook help
362 // update the keyboard shortcuts notebook help
194 this.events.trigger('rebuild.QuickHelp');
363 this.events.trigger('rebuild.QuickHelp');
@@ -196,6 +365,11 b' define(['
196 };
365 };
197
366
198 ShortcutManager.prototype.add_shortcuts = function (data) {
367 ShortcutManager.prototype.add_shortcuts = function (data) {
368 /**
369 * Convenient methods to call `add_shortcut(key, value)` on several items
370 *
371 * data : Dict of the form {key:value, ...}
372 **/
199 for (var shortcut in data) {
373 for (var shortcut in data) {
200 this.add_shortcut(shortcut, data[shortcut], true);
374 this.add_shortcut(shortcut, data[shortcut], true);
201 }
375 }
@@ -204,44 +378,22 b' define(['
204 };
378 };
205
379
206 ShortcutManager.prototype.remove_shortcut = function (shortcut, suppress_help_update) {
380 ShortcutManager.prototype.remove_shortcut = function (shortcut, suppress_help_update) {
381 /**
382 * Remove the binding of shortcut `sortcut` with its action.
383 * throw an error if trying to remove a non-exiting shortcut
384 **/
207 shortcut = normalize_shortcut(shortcut);
385 shortcut = normalize_shortcut(shortcut);
208 delete this._counts[shortcut];
386 if( typeof(shortcut) === 'string'){
209 delete this._shortcuts[shortcut];
387 shortcut = shortcut.split(',');
388 }
389 this._remove_leaf(shortcut, this._shortcuts);
210 if (!suppress_help_update) {
390 if (!suppress_help_update) {
211 // update the keyboard shortcuts notebook help
391 // update the keyboard shortcuts notebook help
212 this.events.trigger('rebuild.QuickHelp');
392 this.events.trigger('rebuild.QuickHelp');
213 }
393 }
214 };
394 };
215
395
216 ShortcutManager.prototype.count_handler = function (shortcut, event, data) {
396
217 /**
218 * Seem to allow to call an handler only after several key press.
219 * like, I suppose `dd` that delete the current cell only after
220 * `d` has been pressed twice..
221 * @method count_handler
222 * @return {Boolean} `true|false`, whether or not the event has been handled.
223 * @param shortcut {shortcut}
224 * @param event {event}
225 * @param data {data}
226 */
227 var that = this;
228 var c = this._counts;
229 var t = this._timers;
230 var timer = null;
231 if (c[shortcut] === data.count-1) {
232 c[shortcut] = 0;
233 timer = t[shortcut];
234 if (timer) {clearTimeout(timer); delete t[shortcut];}
235 return data.handler(event);
236 } else {
237 c[shortcut] = c[shortcut] + 1;
238 timer = setTimeout(function () {
239 c[shortcut] = 0;
240 }, that.delay);
241 t[shortcut] = timer;
242 }
243 return false;
244 };
245
397
246 ShortcutManager.prototype.call_handler = function (event) {
398 ShortcutManager.prototype.call_handler = function (event) {
247 /**
399 /**
@@ -249,26 +401,40 b' define(['
249 * @method call_handler
401 * @method call_handler
250 * @return {Boolean} `true|false`, `false` if no handler was found, otherwise the value return by the handler.
402 * @return {Boolean} `true|false`, `false` if no handler was found, otherwise the value return by the handler.
251 * @param event {event}
403 * @param event {event}
252 */
404 *
405 * given an event, call the corresponding shortcut.
406 * return false is event wan handled, true otherwise
407 * in any case returning false stop event propagation
408 **/
409
410
411 this.clearsoon();
412 if(only_modifier_event(event)){
413 return true;
414 }
253 var shortcut = event_to_shortcut(event);
415 var shortcut = event_to_shortcut(event);
254 var data = this._shortcuts[shortcut];
416 this._queue.push(shortcut);
255 if (data) {
417 var action_name = this.get_shortcut(this._queue);
256 var handler = data.handler;
418
257 if (handler) {
419 if (typeof(action_name) === 'undefined'|| action_name === null){
258 if (data.count === 1) {
420 this.clearqueue();
259 return handler(event);
421 return true;
260 } else if (data.count > 1) {
422 }
261 return this.count_handler(shortcut, event, data);
423
262 }
424 if (this.actions.exists(action_name)) {
263 }
425 event.preventDefault();
426 this.clearqueue();
427 return this.actions.call(action_name, event);
264 }
428 }
265 return true;
429
430 return false;
266 };
431 };
267
432
433
268 ShortcutManager.prototype.handles = function (event) {
434 ShortcutManager.prototype.handles = function (event) {
269 var shortcut = event_to_shortcut(event);
435 var shortcut = event_to_shortcut(event);
270 var data = this._shortcuts[shortcut];
436 var action_name = this.get_shortcut(this._queue.concat(shortcut));
271 return !( data === undefined || data.handler === undefined );
437 return (typeof(action_name) !== 'undefined');
272 };
438 };
273
439
274 var keyboard = {
440 var keyboard = {
@@ -278,7 +444,7 b' define(['
278 normalize_key : normalize_key,
444 normalize_key : normalize_key,
279 normalize_shortcut : normalize_shortcut,
445 normalize_shortcut : normalize_shortcut,
280 shortcut_to_event : shortcut_to_event,
446 shortcut_to_event : shortcut_to_event,
281 event_to_shortcut : event_to_shortcut
447 event_to_shortcut : event_to_shortcut,
282 };
448 };
283
449
284 // For backwards compatibility.
450 // For backwards compatibility.
This diff has been collapsed as it changes many lines, (533 lines changed) Show them Hide them
@@ -16,9 +16,6 b' define(['
16 ], function(IPython, $, utils, keyboard) {
16 ], function(IPython, $, utils, keyboard) {
17 "use strict";
17 "use strict";
18
18
19 var browser = utils.browser[0];
20 var platform = utils.platform;
21
22 // Main keyboard manager for the notebook
19 // Main keyboard manager for the notebook
23 var keycodes = keyboard.keycodes;
20 var keycodes = keyboard.keycodes;
24
21
@@ -37,470 +34,101 b' define(['
37 this.pager = options.pager;
34 this.pager = options.pager;
38 this.quick_help = undefined;
35 this.quick_help = undefined;
39 this.notebook = undefined;
36 this.notebook = undefined;
37 this.last_mode = undefined;
40 this.bind_events();
38 this.bind_events();
41 this.command_shortcuts = new keyboard.ShortcutManager(undefined, options.events);
39 this.env = {pager:this.pager};
40 this.actions = options.actions;
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);
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 };
48 };
48
49
50
51
52
49 /**
53 /**
50 * Return a dict of common shortcut
54 * Return a dict of common shortcut
51 * @method get_default_common_shortcuts
55 * @method get_default_common_shortcuts
52 *
56 *
53 * @example Example of returned shortcut
57 * @example Example of returned shortcut
54 * ```
58 * ```
55 * 'shortcut-key': // a string representing the shortcut as dash separated value.
59 * 'shortcut-key': 'action-name'
56 * // e.g. 'shift' , 'shift-enter', 'cmd-t'
60 * // a string representing the shortcut as dash separated value.
57 * {
61 * // e.g. 'shift' , 'shift-enter', 'cmd-t'
58 * help: String // user facing help string
59 * help_index: String // string used internally to order the shortcut on the quickhelp
60 * handler: function(event){return true|false} // function that takes an even as first and only parameter
61 * // and return a boolean indicating whether or not the event should been handled further.
62 * }
63 *```
62 *```
64 */
63 */
65 KeyboardManager.prototype.get_default_common_shortcuts = function() {
64 KeyboardManager.prototype.get_default_common_shortcuts = function() {
66 var that = this;
65 return {
67 var shortcuts = {
66 'shift' : 'ipython.ignore',
68 'shift' : {
67 'shift-enter' : 'ipython.run-select-next',
69 help : '',
68 'ctrl-enter' : 'ipython.execute-in-place',
70 help_index : '',
69 'alt-enter' : 'ipython.execute-and-insert-after',
71 handler : function (event) {
70 // cmd on mac, ctrl otherwise
72 // ignore shift keydown
71 'cmdtrl-s' : 'ipython.save-notebook',
73 return true;
74 }
75 },
76 'shift-enter' : {
77 help : 'run cell, select below',
78 help_index : 'ba',
79 handler : function (event) {
80 that.notebook.execute_cell_and_select_below();
81 return false;
82 }
83 },
84 'ctrl-enter' : {
85 help : 'run cell',
86 help_index : 'bb',
87 handler : function (event) {
88 that.notebook.execute_cell();
89 return false;
90 }
91 },
92 'alt-enter' : {
93 help : 'run cell, insert below',
94 help_index : 'bc',
95 handler : function (event) {
96 that.notebook.execute_cell_and_insert_below();
97 return false;
98 }
99 }
100 };
72 };
101
102 if (platform === 'MacOS') {
103 shortcuts['cmd-s'] =
104 {
105 help : 'save notebook',
106 help_index : 'fb',
107 handler : function (event) {
108 that.notebook.save_checkpoint();
109 event.preventDefault();
110 return false;
111 }
112 };
113 } else {
114 shortcuts['ctrl-s'] =
115 {
116 help : 'save notebook',
117 help_index : 'fb',
118 handler : function (event) {
119 that.notebook.save_checkpoint();
120 event.preventDefault();
121 return false;
122 }
123 };
124 }
125 return shortcuts;
126 };
73 };
127
74
128 KeyboardManager.prototype.get_default_edit_shortcuts = function() {
75 KeyboardManager.prototype.get_default_edit_shortcuts = function() {
129 var that = this;
130 return {
76 return {
131 'esc' : {
77 'esc' : 'ipython.go-to-command-mode',
132 help : 'command mode',
78 'ctrl-m' : 'ipython.go-to-command-mode',
133 help_index : 'aa',
79 'up' : 'ipython.move-cursor-up-or-previous-cell',
134 handler : function (event) {
80 'down' : 'ipython.move-cursor-down-or-next-cell',
135 that.notebook.command_mode();
81 'ctrl-shift--' : 'ipython.split-cell-at-cursor',
136 return false;
82 'ctrl-shift-subtract' : 'ipython.split-cell-at-cursor'
137 }
138 },
139 'ctrl-m' : {
140 help : 'command mode',
141 help_index : 'ab',
142 handler : function (event) {
143 that.notebook.command_mode();
144 return false;
145 }
146 },
147 'up' : {
148 help : '',
149 help_index : '',
150 handler : function (event) {
151 var index = that.notebook.get_selected_index();
152 var cell = that.notebook.get_cell(index);
153 var cm = that.notebook.get_selected_cell().code_mirror;
154 var cur = cm.getCursor()
155 if (cell && cell.at_top() && index !== 0 && cur.ch === 0) {
156 event.preventDefault();
157 that.notebook.command_mode();
158 that.notebook.select_prev();
159 that.notebook.edit_mode();
160 var cm = that.notebook.get_selected_cell().code_mirror;
161 cm.setCursor(cm.lastLine(), 0);
162 }
163 return false;
164 }
165 },
166 'down' : {
167 help : '',
168 help_index : '',
169 handler : function (event) {
170 var index = that.notebook.get_selected_index();
171 var cell = that.notebook.get_cell(index);
172 if (cell.at_bottom() && index !== (that.notebook.ncells()-1)) {
173 event.preventDefault();
174 that.notebook.command_mode();
175 that.notebook.select_next();
176 that.notebook.edit_mode();
177 var cm = that.notebook.get_selected_cell().code_mirror;
178 cm.setCursor(0, 0);
179 return false;
180 }
181 return false;
182 }
183 },
184 'ctrl-shift--' : {
185 help : 'split cell',
186 help_index : 'ea',
187 handler : function (event) {
188 that.notebook.split_cell();
189 return false;
190 }
191 },
192 'ctrl-shift-subtract' : {
193 help : '',
194 help_index : 'eb',
195 handler : function (event) {
196 that.notebook.split_cell();
197 return false;
198 }
199 },
200 };
83 };
201 };
84 };
202
85
203 KeyboardManager.prototype.get_default_command_shortcuts = function() {
86 KeyboardManager.prototype.get_default_command_shortcuts = function() {
204 var that = this;
205 return {
87 return {
206 'space': {
88 'shift-space': 'ipython.scroll-up',
207 help: "Scroll down",
89 'shift-v' : 'ipython.paste-cell-before',
208 handler: function(event) {
90 'shift-m' : 'ipython.merge-selected-cell-with-cell-after',
209 return that.notebook.scroll_manager.scroll(1);
91 'shift-o' : 'ipython.toggle-output-scrolling-selected-cell',
210 },
92 'ctrl-j' : 'ipython.move-selected-cell-down',
211 },
93 'ctrl-k' : 'ipython.move-selected-cell-up',
212 'shift-space': {
94 'enter' : 'ipython.enter-edit-mode',
213 help: "Scroll up",
95 'space' : 'ipython.scroll-down',
214 handler: function(event) {
96 'down' : 'ipython.select-next-cell',
215 return that.notebook.scroll_manager.scroll(-1);
97 'i,i' : 'ipython.interrupt-kernel',
216 },
98 '0,0' : 'ipython.restart-kernel',
217 },
99 'd,d' : 'ipython.delete-cell',
218 'enter' : {
100 'up' : 'ipython.select-previous-cell',
219 help : 'edit mode',
101 'k' : 'ipython.select-previous-cell',
220 help_index : 'aa',
102 'j' : 'ipython.select-next-cell',
221 handler : function (event) {
103 'x' : 'ipython.cut-selected-cell',
222 that.notebook.edit_mode();
104 'c' : 'ipython.copy-selected-cell',
223 return false;
105 'v' : 'ipython.paste-cell-after',
224 }
106 'a' : 'ipython.insert-cell-before',
225 },
107 'b' : 'ipython.insert-cell-after',
226 'up' : {
108 'y' : 'ipython.change-selected-cell-to-code-cell',
227 help : 'select previous cell',
109 'm' : 'ipython.change-selected-cell-to-markdown-cell',
228 help_index : 'da',
110 'r' : 'ipython.change-selected-cell-to-raw-cell',
229 handler : function (event) {
111 '1' : 'ipython.change-selected-cell-to-heading-1',
230 var index = that.notebook.get_selected_index();
112 '2' : 'ipython.change-selected-cell-to-heading-2',
231 if (index !== 0 && index !== null) {
113 '3' : 'ipython.change-selected-cell-to-heading-3',
232 that.notebook.select_prev();
114 '4' : 'ipython.change-selected-cell-to-heading-4',
233 that.notebook.focus_cell();
115 '5' : 'ipython.change-selected-cell-to-heading-5',
234 }
116 '6' : 'ipython.change-selected-cell-to-heading-6',
235 return false;
117 'o' : 'ipython.toggle-output-visibility-selected-cell',
236 }
118 's' : 'ipython.save-notebook',
237 },
119 'l' : 'ipython.toggle-line-number-selected-cell',
238 'down' : {
120 'h' : 'ipython.show-keyboard-shortcut-help-dialog',
239 help : 'select next cell',
121 'z' : 'ipython.undo-last-cell-deletion',
240 help_index : 'db',
122 'q' : 'ipython.close-pager',
241 handler : function (event) {
123 'i,e,e,e,e,e' : function(){console.log('[[===>>> 5E <<<===]]');},
242 var index = that.notebook.get_selected_index();
124 'i,d,d,q,d' : function(){console.log('[[===>>> Trigger god mode <<<===]]');},
243 if (index !== (that.notebook.ncells()-1) && index !== null) {
125 'i,d,d' : function(){console.log('[[===>>> should warn at registration <<<===]]');},
244 that.notebook.select_next();
126 'i,d,k' : function(){console.log('[[===>>> Trigger shadow mode <<<===]]');},
245 that.notebook.focus_cell();
127 'i,d,k,r,q' : function(){console.log('[[===>>> Trigger invisibility mode <<<===]]');},
246 }
128 ';,up,down,up,down,left,right,left,right,b,a' : function(){console.log('[[===>>> Konami <<<===]]');},
247 return false;
129 'ctrl-x,meta-c,meta-b,u,t,t,e,r,f,l,y' : function(){
248 }
130 console.log('[[Are you a real Programmer ?]]');
249 },
131 window.open('http://xkcd.com/378/','_blank');
250 'k' : {
251 help : 'select previous cell',
252 help_index : 'dc',
253 handler : function (event) {
254 var index = that.notebook.get_selected_index();
255 if (index !== 0 && index !== null) {
256 that.notebook.select_prev();
257 that.notebook.focus_cell();
258 }
259 return false;
260 }
261 },
262 'j' : {
263 help : 'select next cell',
264 help_index : 'dd',
265 handler : function (event) {
266 var index = that.notebook.get_selected_index();
267 if (index !== (that.notebook.ncells()-1) && index !== null) {
268 that.notebook.select_next();
269 that.notebook.focus_cell();
270 }
271 return false;
272 }
273 },
274 'x' : {
275 help : 'cut cell',
276 help_index : 'ee',
277 handler : function (event) {
278 that.notebook.cut_cell();
279 return false;
280 }
281 },
282 'c' : {
283 help : 'copy cell',
284 help_index : 'ef',
285 handler : function (event) {
286 that.notebook.copy_cell();
287 return false;
288 }
289 },
290 'shift-v' : {
291 help : 'paste cell above',
292 help_index : 'eg',
293 handler : function (event) {
294 that.notebook.paste_cell_above();
295 return false;
296 }
297 },
298 'v' : {
299 help : 'paste cell below',
300 help_index : 'eh',
301 handler : function (event) {
302 that.notebook.paste_cell_below();
303 return false;
304 }
305 },
306 'd' : {
307 help : 'delete cell (press twice)',
308 help_index : 'ej',
309 count: 2,
310 handler : function (event) {
311 that.notebook.delete_cell();
312 return false;
313 }
314 },
315 'a' : {
316 help : 'insert cell above',
317 help_index : 'ec',
318 handler : function (event) {
319 that.notebook.insert_cell_above();
320 that.notebook.select_prev();
321 that.notebook.focus_cell();
322 return false;
323 }
324 },
325 'b' : {
326 help : 'insert cell below',
327 help_index : 'ed',
328 handler : function (event) {
329 that.notebook.insert_cell_below();
330 that.notebook.select_next();
331 that.notebook.focus_cell();
332 return false;
333 }
334 },
335 'y' : {
336 help : 'to code',
337 help_index : 'ca',
338 handler : function (event) {
339 that.notebook.to_code();
340 return false;
341 }
342 },
343 'm' : {
344 help : 'to markdown',
345 help_index : 'cb',
346 handler : function (event) {
347 that.notebook.to_markdown();
348 return false;
349 }
350 },
351 'r' : {
352 help : 'to raw',
353 help_index : 'cc',
354 handler : function (event) {
355 that.notebook.to_raw();
356 return false;
357 }
358 },
359 '1' : {
360 help : 'to heading 1',
361 help_index : 'cd',
362 handler : function (event) {
363 that.notebook.to_heading(undefined, 1);
364 return false;
365 }
366 },
367 '2' : {
368 help : 'to heading 2',
369 help_index : 'ce',
370 handler : function (event) {
371 that.notebook.to_heading(undefined, 2);
372 return false;
373 }
374 },
375 '3' : {
376 help : 'to heading 3',
377 help_index : 'cf',
378 handler : function (event) {
379 that.notebook.to_heading(undefined, 3);
380 return false;
381 }
382 },
383 '4' : {
384 help : 'to heading 4',
385 help_index : 'cg',
386 handler : function (event) {
387 that.notebook.to_heading(undefined, 4);
388 return false;
389 }
390 },
391 '5' : {
392 help : 'to heading 5',
393 help_index : 'ch',
394 handler : function (event) {
395 that.notebook.to_heading(undefined, 5);
396 return false;
397 }
398 },
399 '6' : {
400 help : 'to heading 6',
401 help_index : 'ci',
402 handler : function (event) {
403 that.notebook.to_heading(undefined, 6);
404 return false;
405 }
406 },
407 'o' : {
408 help : 'toggle output',
409 help_index : 'gb',
410 handler : function (event) {
411 that.notebook.toggle_output();
412 return false;
413 }
414 },
415 'shift-o' : {
416 help : 'toggle output scrolling',
417 help_index : 'gc',
418 handler : function (event) {
419 that.notebook.toggle_output_scroll();
420 return false;
421 }
422 },
423 's' : {
424 help : 'save notebook',
425 help_index : 'fa',
426 handler : function (event) {
427 that.notebook.save_checkpoint();
428 return false;
429 }
430 },
431 'ctrl-j' : {
432 help : 'move cell down',
433 help_index : 'eb',
434 handler : function (event) {
435 that.notebook.move_cell_down();
436 return false;
437 }
438 },
439 'ctrl-k' : {
440 help : 'move cell up',
441 help_index : 'ea',
442 handler : function (event) {
443 that.notebook.move_cell_up();
444 return false;
445 }
446 },
447 'l' : {
448 help : 'toggle line numbers',
449 help_index : 'ga',
450 handler : function (event) {
451 that.notebook.cell_toggle_line_numbers();
452 return false;
453 }
454 },
455 'i' : {
456 help : 'interrupt kernel (press twice)',
457 help_index : 'ha',
458 count: 2,
459 handler : function (event) {
460 that.notebook.kernel.interrupt();
461 return false;
462 }
463 },
464 '0' : {
465 help : 'restart kernel (press twice)',
466 help_index : 'hb',
467 count: 2,
468 handler : function (event) {
469 that.notebook.restart_kernel();
470 return false;
471 }
472 },
473 'h' : {
474 help : 'keyboard shortcuts',
475 help_index : 'ge',
476 handler : function (event) {
477 that.quick_help.show_keyboard_shortcuts();
478 return false;
479 }
480 },
481 'z' : {
482 help : 'undo last delete',
483 help_index : 'ei',
484 handler : function (event) {
485 that.notebook.undelete_cell();
486 return false;
487 }
488 },
489 'shift-m' : {
490 help : 'merge cell below',
491 help_index : 'ek',
492 handler : function (event) {
493 that.notebook.merge_cell_below();
494 return false;
495 }
496 },
497 'q' : {
498 help : 'close pager',
499 help_index : 'gd',
500 handler : function (event) {
501 that.pager.collapse();
502 return false;
503 }
504 },
132 },
505 };
133 };
506 };
134 };
@@ -508,16 +136,27 b' define(['
508 KeyboardManager.prototype.bind_events = function () {
136 KeyboardManager.prototype.bind_events = function () {
509 var that = this;
137 var that = this;
510 $(document).keydown(function (event) {
138 $(document).keydown(function (event) {
511
139 if(event._ipkmIgnore===true||(event.originalEvent||{})._ipkmIgnore===true){
512 if(event._ipkmIgnore==true||(event.originalEvent||{})._ipkmIgnore==true){
513 return false;
140 return false;
514 }
141 }
515 return that.handle_keydown(event);
142 return that.handle_keydown(event);
516 });
143 });
517 };
144 };
518
145
146 KeyboardManager.prototype.set_notebook = function (notebook) {
147 this.notebook = notebook;
148 this.actions.extend_env({notebook:notebook});
149 };
150
151 KeyboardManager.prototype.set_quickhelp = function (notebook) {
152 this.actions.extend_env({quick_help:notebook});
153 };
154
155
519 KeyboardManager.prototype.handle_keydown = function (event) {
156 KeyboardManager.prototype.handle_keydown = function (event) {
520 var notebook = this.notebook;
157 /**
158 * returning false from this will stop event propagation
159 **/
521
160
522 if (event.which === keycodes.esc) {
161 if (event.which === keycodes.esc) {
523 // Intercept escape at highest level to avoid closing
162 // Intercept escape at highest level to avoid closing
@@ -527,8 +166,7 b' define(['
527
166
528 if (!this.enabled) {
167 if (!this.enabled) {
529 if (event.which === keycodes.esc) {
168 if (event.which === keycodes.esc) {
530 // ESC
169 this.notebook.command_mode();
531 notebook.command_mode();
532 return false;
170 return false;
533 }
171 }
534 return true;
172 return true;
@@ -595,7 +233,8 b' define(['
595 });
233 });
596 };
234 };
597
235
598 // For backwards compatability.
236
237 // For backwards compatibility.
599 IPython.KeyboardManager = KeyboardManager;
238 IPython.KeyboardManager = KeyboardManager;
600
239
601 return {'KeyboardManager': KeyboardManager};
240 return {'KeyboardManager': KeyboardManager};
@@ -16,6 +16,7 b' require(['
16 'notebook/js/menubar',
16 'notebook/js/menubar',
17 'notebook/js/notificationarea',
17 'notebook/js/notificationarea',
18 'notebook/js/savewidget',
18 'notebook/js/savewidget',
19 'notebook/js/actions',
19 'notebook/js/keyboardmanager',
20 'notebook/js/keyboardmanager',
20 'notebook/js/config',
21 'notebook/js/config',
21 'notebook/js/kernelselector',
22 'notebook/js/kernelselector',
@@ -36,7 +37,8 b' require(['
36 quickhelp,
37 quickhelp,
37 menubar,
38 menubar,
38 notificationarea,
39 notificationarea,
39 savewidget,
40 savewidget,
41 actions,
40 keyboardmanager,
42 keyboardmanager,
41 config,
43 config,
42 kernelselector,
44 kernelselector,
@@ -62,9 +64,11 b' require(['
62 var pager = new pager.Pager('div#pager', 'div#pager_splitter', {
64 var pager = new pager.Pager('div#pager', 'div#pager_splitter', {
63 layout_manager: layout_manager,
65 layout_manager: layout_manager,
64 events: events});
66 events: events});
67 var acts = new actions.init();
65 var keyboard_manager = new keyboardmanager.KeyboardManager({
68 var keyboard_manager = new keyboardmanager.KeyboardManager({
66 pager: pager,
69 pager: pager,
67 events: events});
70 events: events,
71 actions: acts });
68 var save_widget = new savewidget.SaveWidget('span#save_widget', {
72 var save_widget = new savewidget.SaveWidget('span#save_widget', {
69 events: events,
73 events: events,
70 keyboard_manager: keyboard_manager});
74 keyboard_manager: keyboard_manager});
@@ -77,11 +81,14 b' require(['
77 var login_widget = new loginwidget.LoginWidget('span#login_widget', common_options);
81 var login_widget = new loginwidget.LoginWidget('span#login_widget', common_options);
78 var toolbar = new maintoolbar.MainToolBar('#maintoolbar-container', {
82 var toolbar = new maintoolbar.MainToolBar('#maintoolbar-container', {
79 notebook: notebook,
83 notebook: notebook,
80 events: events});
84 events: events,
85 actions: acts});
81 var quick_help = new quickhelp.QuickHelp({
86 var quick_help = new quickhelp.QuickHelp({
82 keyboard_manager: keyboard_manager,
87 keyboard_manager: keyboard_manager,
83 events: events,
88 events: events,
84 notebook: notebook});
89 notebook: notebook});
90 keyboard_manager.set_notebook(notebook);
91 keyboard_manager.set_quickhelp(quick_help);
85 var menubar = new menubar.MenuBar('#menubar', $.extend({
92 var menubar = new menubar.MenuBar('#menubar', $.extend({
86 notebook: notebook,
93 notebook: notebook,
87 layout_manager: layout_manager,
94 layout_manager: layout_manager,
@@ -34,10 +34,10 b' define(['
34 platform_specific = [
34 platform_specific = [
35 { shortcut: "Cmd-Up", help:"go to cell start" },
35 { shortcut: "Cmd-Up", help:"go to cell start" },
36 { shortcut: "Cmd-Down", help:"go to cell end" },
36 { shortcut: "Cmd-Down", help:"go to cell end" },
37 { shortcut: "Opt-Left", help:"go one word left" },
37 { shortcut: "Alt-Left", help:"go one word left" },
38 { shortcut: "Opt-Right", help:"go one word right" },
38 { shortcut: "Alt-Right", help:"go one word right" },
39 { shortcut: "Opt-Backspace", help:"del word before" },
39 { shortcut: "Alt-Backspace", help:"del word before" },
40 { shortcut: "Opt-Delete", help:"del word after" },
40 { shortcut: "Alt-Delete", help:"del word after" },
41 ];
41 ];
42 } else {
42 } else {
43 // PC specific
43 // PC specific
@@ -65,10 +65,6 b' define(['
65 ].concat( platform_specific );
65 ].concat( platform_specific );
66
66
67
67
68
69
70
71
72 QuickHelp.prototype.show_keyboard_shortcuts = function () {
68 QuickHelp.prototype.show_keyboard_shortcuts = function () {
73 // toggles display of keyboard shortcut dialog
69 // toggles display of keyboard shortcut dialog
74 var that = this;
70 var that = this;
@@ -139,7 +135,9 b' define(['
139 keys[i] = "<code><strong>" + k + "</strong></code>";
135 keys[i] = "<code><strong>" + k + "</strong></code>";
140 continue; // leave individual keys lower-cased
136 continue; // leave individual keys lower-cased
141 }
137 }
142 keys[i] = ( special_case[k] ? special_case[k] : k.charAt(0).toUpperCase() + k.slice(1) );
138 if (k.indexOf(',') === -1){
139 keys[i] = ( special_case[k] ? special_case[k] : k.charAt(0).toUpperCase() + k.slice(1) );
140 }
143 keys[i] = "<code><strong>" + keys[i] + "</strong></code>";
141 keys[i] = "<code><strong>" + keys[i] + "</strong></code>";
144 }
142 }
145 return keys.join('-');
143 return keys.join('-');
@@ -155,7 +153,12 b' define(['
155
153
156 var build_one = function (s) {
154 var build_one = function (s) {
157 var help = s.help;
155 var help = s.help;
158 var shortcut = prettify(s.shortcut);
156 var shortcut = '';
157 if(s.shortcut){
158 shortcut = prettify(s.shortcut);
159 } else {
160 console.error('[debug] - nothing for', s)
161 }
159 return $('<div>').addClass('quickhelp').
162 return $('<div>').addClass('quickhelp').
160 append($('<span/>').addClass('shortcut_key').append($(shortcut))).
163 append($('<span/>').addClass('shortcut_key').append($(shortcut))).
161 append($('<span/>').addClass('shortcut_descr').text(' : ' + help));
164 append($('<span/>').addClass('shortcut_descr').text(' : ' + help));
@@ -11,7 +11,7 b' define(['
11 * A generic toolbar on which one can add button
11 * A generic toolbar on which one can add button
12 * @class ToolBar
12 * @class ToolBar
13 * @constructor
13 * @constructor
14 * @param {Dom_object} selector
14 * @param {Dom object} selector
15 */
15 */
16 var ToolBar = function (selector, layout_manager) {
16 var ToolBar = function (selector, layout_manager) {
17 this.selector = selector;
17 this.selector = selector;
General Comments 0
You need to be logged in to leave comments. Login now