##// 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 });
@@ -1,288 +1,454 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 keyboard
6 * @module keyboard
7 * @namespace keyboard
7 * @namespace keyboard
8 * @class ShortcutManager
8 * @class ShortcutManager
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 ], 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,
30 's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, 'z': 90,
34 's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, 'z': 90,
31 '1 !': 49, '2 @': 50, '3 #': 51, '4 $': 52, '5 %': 53, '6 ^': 54,
35 '1 !': 49, '2 @': 50, '3 #': 51, '4 $': 52, '5 %': 53, '6 ^': 54,
32 '7 &': 55, '8 *': 56, '9 (': 57, '0 )': 48,
36 '7 &': 55, '8 *': 56, '9 (': 57, '0 )': 48,
33 '[ {': 219, '] }': 221, '` ~': 192, ', <': 188, '. >': 190, '/ ?': 191,
37 '[ {': 219, '] }': 221, '` ~': 192, ', <': 188, '. >': 190, '/ ?': 191,
34 '\\ |': 220, '\' "': 222,
38 '\\ |': 220, '\' "': 222,
35 'numpad0': 96, 'numpad1': 97, 'numpad2': 98, 'numpad3': 99, 'numpad4': 100,
39 'numpad0': 96, 'numpad1': 97, 'numpad2': 98, 'numpad3': 99, 'numpad4': 100,
36 'numpad5': 101, 'numpad6': 102, 'numpad7': 103, 'numpad8': 104, 'numpad9': 105,
40 'numpad5': 101, 'numpad6': 102, 'numpad7': 103, 'numpad8': 104, 'numpad9': 105,
37 'multiply': 106, 'add': 107, 'subtract': 109, 'decimal': 110, 'divide': 111,
41 'multiply': 106, 'add': 107, 'subtract': 109, 'decimal': 110, 'divide': 111,
38 'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118,
42 'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118,
39 'f8': 119, 'f9': 120, 'f11': 122, 'f12': 123, 'f13': 124, 'f14': 125, 'f15': 126,
43 'f8': 119, 'f9': 120, 'f11': 122, 'f12': 123, 'f13': 124, 'f14': 125, 'f15': 126,
40 'backspace': 8, 'tab': 9, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18,
44 'backspace': 8, 'tab': 9, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18,
41 'meta': 91, 'capslock': 20, 'esc': 27, 'space': 32, 'pageup': 33, 'pagedown': 34,
45 'meta': 91, 'capslock': 20, 'esc': 27, 'space': 32, 'pageup': 33, 'pagedown': 34,
42 'end': 35, 'home': 36, 'left': 37, 'up': 38, 'right': 39, 'down': 40,
46 'end': 35, 'home': 36, 'left': 37, 'up': 38, 'right': 39, 'down': 40,
43 'insert': 45, 'delete': 46, 'numlock': 144,
47 'insert': 45, 'delete': 46, 'numlock': 144,
44 };
48 };
45
49
46 // These apply to Firefox and Opera
50 // These apply to Firefox and Opera
47 var _mozilla_keycodes = {
51 var _mozilla_keycodes = {
48 '; :': 59, '= +': 61, '- _': 173, 'meta': 224
52 '; :': 59, '= +': 61, '- _': 173, 'meta': 224
49 };
53 };
50
54
51 // This apply to Webkit and IE
55 // This apply to Webkit and IE
52 var _ie_keycodes = {
56 var _ie_keycodes = {
53 '; :': 186, '= +': 187, '- _': 189
57 '; :': 186, '= +': 187, '- _': 189
54 };
58 };
55
59
56 var browser = utils.browser[0];
60 var browser = utils.browser[0];
57 var platform = utils.platform;
61 var platform = utils.platform;
58
62
59 if (browser === 'Firefox' || browser === 'Opera' || browser === 'Netscape') {
63 if (browser === 'Firefox' || browser === 'Opera' || browser === 'Netscape') {
60 $.extend(_keycodes, _mozilla_keycodes);
64 $.extend(_keycodes, _mozilla_keycodes);
61 } else if (browser === 'Safari' || browser === 'Chrome' || browser === 'MSIE') {
65 } else if (browser === 'Safari' || browser === 'Chrome' || browser === 'MSIE') {
62 $.extend(_keycodes, _ie_keycodes);
66 $.extend(_keycodes, _ie_keycodes);
63 }
67 }
64
68
65 var keycodes = {};
69 var keycodes = {};
66 var inv_keycodes = {};
70 var inv_keycodes = {};
67 for (var name in _keycodes) {
71 for (var name in _keycodes) {
68 var names = name.split(' ');
72 var names = name.split(' ');
69 if (names.length === 1) {
73 if (names.length === 1) {
70 var n = names[0];
74 var n = names[0];
71 keycodes[n] = _keycodes[n];
75 keycodes[n] = _keycodes[n];
72 inv_keycodes[_keycodes[n]] = n;
76 inv_keycodes[_keycodes[n]] = n;
73 } else {
77 } else {
74 var primary = names[0];
78 var primary = names[0];
75 var secondary = names[1];
79 var secondary = names[1];
76 keycodes[primary] = _keycodes[name];
80 keycodes[primary] = _keycodes[name];
77 keycodes[secondary] = _keycodes[name];
81 keycodes[secondary] = _keycodes[name];
78 inv_keycodes[_keycodes[name]] = primary;
82 inv_keycodes[_keycodes[name]] = primary;
79 }
83 }
80 }
84 }
81
85
82 var normalize_key = function (key) {
86 var normalize_key = function (key) {
83 return inv_keycodes[keycodes[key]];
87 return inv_keycodes[keycodes[key]];
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]);
97 } else {
120 } else {
98 var modifiers = values.slice(0,-1);
121 var modifiers = values.slice(0,-1);
99 var key = normalize_key(values[values.length-1]);
122 var key = normalize_key(values[values.length-1]);
100 modifiers.sort();
123 modifiers.sort();
101 return modifiers.join('-') + '-' + key;
124 return modifiers.join('-') + '-' + key;
102 }
125 }
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
110 var values = shortcut.split("-");
135 var values = shortcut.split("-");
111 var modifiers = values.slice(0,-1);
136 var modifiers = values.slice(0,-1);
112 var key = values[values.length-1];
137 var key = values[values.length-1];
113 var opts = {which: keycodes[key]};
138 var opts = {which: keycodes[key]};
114 if (modifiers.indexOf('alt') !== -1) {opts.altKey = true;}
139 if (modifiers.indexOf('alt') !== -1) {opts.altKey = true;}
115 if (modifiers.indexOf('ctrl') !== -1) {opts.ctrlKey = true;}
140 if (modifiers.indexOf('ctrl') !== -1) {opts.ctrlKey = true;}
116 if (modifiers.indexOf('meta') !== -1) {opts.metaKey = true;}
141 if (modifiers.indexOf('meta') !== -1) {opts.metaKey = true;}
117 if (modifiers.indexOf('shift') !== -1) {opts.shiftKey = true;}
142 if (modifiers.indexOf('shift') !== -1) {opts.shiftKey = true;}
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-';}
126 if (event.ctrlKey && key !== 'ctrl') {shortcut += 'ctrl-';}
164 if (event.ctrlKey && key !== 'ctrl') {shortcut += 'ctrl-';}
127 if (event.metaKey && key !== 'meta') {shortcut += 'meta-';}
165 if (event.metaKey && key !== 'meta') {shortcut += 'meta-';}
128 if (event.shiftKey && key !== 'shift') {shortcut += 'shift-';}
166 if (event.shiftKey && key !== 'shift') {shortcut += 'shift-';}
129 shortcut += key;
167 shortcut += key;
130 return shortcut;
168 return shortcut;
131 };
169 };
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 *
139 * @class ShortcutManager
177 * @class ShortcutManager
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;
173 };
256 };
174
257
175 ShortcutManager.prototype.clear_shortcuts = function () {
258 ShortcutManager.prototype.clear_shortcuts = function () {
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');
195 }
364 }
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 }
202 // update the keyboard shortcuts notebook help
376 // update the keyboard shortcuts notebook help
203 this.events.trigger('rebuild.QuickHelp');
377 this.events.trigger('rebuild.QuickHelp');
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 /**
248 * Call the corresponding shortcut handler for a keyboard event
400 * Call the corresponding shortcut handler for a keyboard event
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 = {
275 keycodes : keycodes,
441 keycodes : keycodes,
276 inv_keycodes : inv_keycodes,
442 inv_keycodes : inv_keycodes,
277 ShortcutManager : ShortcutManager,
443 ShortcutManager : ShortcutManager,
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.
285 IPython.keyboard = keyboard;
451 IPython.keyboard = keyboard;
286
452
287 return keyboard;
453 return keyboard;
288 });
454 });
This diff has been collapsed as it changes many lines, (533 lines changed) Show them Hide them
@@ -1,602 +1,241 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 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
25 var KeyboardManager = function (options) {
22 var KeyboardManager = function (options) {
26 /**
23 /**
27 * A class to deal with keyboard event and shortcut
24 * A class to deal with keyboard event and shortcut
28 *
25 *
29 * @class KeyboardManager
26 * @class KeyboardManager
30 * @constructor
27 * @constructor
31 * @param options {dict} Dictionary of keyword arguments :
28 * @param options {dict} Dictionary of keyword arguments :
32 * @param options.events {$(Events)} instance
29 * @param options.events {$(Events)} instance
33 * @param options.pager: {Pager} pager instance
30 * @param options.pager: {Pager} pager instance
34 */
31 */
35 this.mode = 'command';
32 this.mode = 'command';
36 this.enabled = true;
33 this.enabled = true;
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 };
507
135
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
524 // websocket connection with firefox
163 // websocket connection with firefox
525 event.preventDefault();
164 event.preventDefault();
526 }
165 }
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;
535 }
173 }
536
174
537 if (this.mode === 'edit') {
175 if (this.mode === 'edit') {
538 return this.edit_shortcuts.call_handler(event);
176 return this.edit_shortcuts.call_handler(event);
539 } else if (this.mode === 'command') {
177 } else if (this.mode === 'command') {
540 return this.command_shortcuts.call_handler(event);
178 return this.command_shortcuts.call_handler(event);
541 }
179 }
542 return true;
180 return true;
543 };
181 };
544
182
545 KeyboardManager.prototype.edit_mode = function () {
183 KeyboardManager.prototype.edit_mode = function () {
546 this.last_mode = this.mode;
184 this.last_mode = this.mode;
547 this.mode = 'edit';
185 this.mode = 'edit';
548 };
186 };
549
187
550 KeyboardManager.prototype.command_mode = function () {
188 KeyboardManager.prototype.command_mode = function () {
551 this.last_mode = this.mode;
189 this.last_mode = this.mode;
552 this.mode = 'command';
190 this.mode = 'command';
553 };
191 };
554
192
555 KeyboardManager.prototype.enable = function () {
193 KeyboardManager.prototype.enable = function () {
556 this.enabled = true;
194 this.enabled = true;
557 };
195 };
558
196
559 KeyboardManager.prototype.disable = function () {
197 KeyboardManager.prototype.disable = function () {
560 this.enabled = false;
198 this.enabled = false;
561 };
199 };
562
200
563 KeyboardManager.prototype.register_events = function (e) {
201 KeyboardManager.prototype.register_events = function (e) {
564 var that = this;
202 var that = this;
565 var handle_focus = function () {
203 var handle_focus = function () {
566 that.disable();
204 that.disable();
567 };
205 };
568 var handle_blur = function () {
206 var handle_blur = function () {
569 that.enable();
207 that.enable();
570 };
208 };
571 e.on('focusin', handle_focus);
209 e.on('focusin', handle_focus);
572 e.on('focusout', handle_blur);
210 e.on('focusout', handle_blur);
573 // TODO: Very strange. The focusout event does not seem fire for the
211 // TODO: Very strange. The focusout event does not seem fire for the
574 // bootstrap textboxes on FF25&26... This works around that by
212 // bootstrap textboxes on FF25&26... This works around that by
575 // registering focus and blur events recursively on all inputs within
213 // registering focus and blur events recursively on all inputs within
576 // registered element.
214 // registered element.
577 e.find('input').blur(handle_blur);
215 e.find('input').blur(handle_blur);
578 e.on('DOMNodeInserted', function (event) {
216 e.on('DOMNodeInserted', function (event) {
579 var target = $(event.target);
217 var target = $(event.target);
580 if (target.is('input')) {
218 if (target.is('input')) {
581 target.blur(handle_blur);
219 target.blur(handle_blur);
582 } else {
220 } else {
583 target.find('input').blur(handle_blur);
221 target.find('input').blur(handle_blur);
584 }
222 }
585 });
223 });
586 // There are times (raw_input) where we remove the element from the DOM before
224 // There are times (raw_input) where we remove the element from the DOM before
587 // focusout is called. In this case we bind to the remove event of jQueryUI,
225 // focusout is called. In this case we bind to the remove event of jQueryUI,
588 // which gets triggered upon removal, iff it is focused at the time.
226 // which gets triggered upon removal, iff it is focused at the time.
589 // is_focused must be used to check for the case where an element within
227 // is_focused must be used to check for the case where an element within
590 // the element being removed is focused.
228 // the element being removed is focused.
591 e.on('remove', function () {
229 e.on('remove', function () {
592 if (utils.is_focused(e[0])) {
230 if (utils.is_focused(e[0])) {
593 that.enable();
231 that.enable();
594 }
232 }
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};
602 });
241 });
@@ -1,146 +1,153 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 require([
4 require([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'notebook/js/notebook',
7 'notebook/js/notebook',
8 'base/js/utils',
8 'base/js/utils',
9 'base/js/page',
9 'base/js/page',
10 'notebook/js/layoutmanager',
10 'notebook/js/layoutmanager',
11 'base/js/events',
11 'base/js/events',
12 'auth/js/loginwidget',
12 'auth/js/loginwidget',
13 'notebook/js/maintoolbar',
13 'notebook/js/maintoolbar',
14 'notebook/js/pager',
14 'notebook/js/pager',
15 'notebook/js/quickhelp',
15 'notebook/js/quickhelp',
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',
22 'codemirror/lib/codemirror',
23 'codemirror/lib/codemirror',
23 // only loaded, not used, please keep sure this is loaded last
24 // only loaded, not used, please keep sure this is loaded last
24 'custom/custom'
25 'custom/custom'
25 ], function(
26 ], function(
26 IPython,
27 IPython,
27 $,
28 $,
28 notebook,
29 notebook,
29 utils,
30 utils,
30 page,
31 page,
31 layoutmanager,
32 layoutmanager,
32 events,
33 events,
33 loginwidget,
34 loginwidget,
34 maintoolbar,
35 maintoolbar,
35 pager,
36 pager,
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,
43 CodeMirror,
45 CodeMirror,
44 // please keep sure that even if not used, this is loaded last
46 // please keep sure that even if not used, this is loaded last
45 custom
47 custom
46 ) {
48 ) {
47 "use strict";
49 "use strict";
48
50
49 // compat with old IPython, remove for IPython > 3.0
51 // compat with old IPython, remove for IPython > 3.0
50 window.CodeMirror = CodeMirror;
52 window.CodeMirror = CodeMirror;
51
53
52 var common_options = {
54 var common_options = {
53 ws_url : utils.get_body_data("wsUrl"),
55 ws_url : utils.get_body_data("wsUrl"),
54 base_url : utils.get_body_data("baseUrl"),
56 base_url : utils.get_body_data("baseUrl"),
55 notebook_path : utils.get_body_data("notebookPath"),
57 notebook_path : utils.get_body_data("notebookPath"),
56 notebook_name : utils.get_body_data('notebookName')
58 notebook_name : utils.get_body_data('notebookName')
57 };
59 };
58
60
59 var user_config = $.extend({}, config.default_config);
61 var user_config = $.extend({}, config.default_config);
60 var page = new page.Page();
62 var page = new page.Page();
61 var layout_manager = new layoutmanager.LayoutManager();
63 var layout_manager = new layoutmanager.LayoutManager();
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});
71 var notebook = new notebook.Notebook('div#notebook', $.extend({
75 var notebook = new notebook.Notebook('div#notebook', $.extend({
72 events: events,
76 events: events,
73 keyboard_manager: keyboard_manager,
77 keyboard_manager: keyboard_manager,
74 save_widget: save_widget,
78 save_widget: save_widget,
75 config: user_config},
79 config: user_config},
76 common_options));
80 common_options));
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,
88 events: events,
95 events: events,
89 save_widget: save_widget,
96 save_widget: save_widget,
90 quick_help: quick_help},
97 quick_help: quick_help},
91 common_options));
98 common_options));
92 var notification_area = new notificationarea.NotificationArea(
99 var notification_area = new notificationarea.NotificationArea(
93 '#notification_area', {
100 '#notification_area', {
94 events: events,
101 events: events,
95 save_widget: save_widget,
102 save_widget: save_widget,
96 notebook: notebook,
103 notebook: notebook,
97 keyboard_manager: keyboard_manager});
104 keyboard_manager: keyboard_manager});
98 notification_area.init_notification_widgets();
105 notification_area.init_notification_widgets();
99 var kernel_selector = new kernelselector.KernelSelector(
106 var kernel_selector = new kernelselector.KernelSelector(
100 '#kernel_selector_widget', notebook);
107 '#kernel_selector_widget', notebook);
101
108
102 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
109 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
103 '<span id="test2" style="font-weight: bold;">x</span>'+
110 '<span id="test2" style="font-weight: bold;">x</span>'+
104 '<span id="test3" style="font-style: italic;">x</span></pre></div>');
111 '<span id="test3" style="font-style: italic;">x</span></pre></div>');
105 var nh = $('#test1').innerHeight();
112 var nh = $('#test1').innerHeight();
106 var bh = $('#test2').innerHeight();
113 var bh = $('#test2').innerHeight();
107 var ih = $('#test3').innerHeight();
114 var ih = $('#test3').innerHeight();
108 if(nh != bh || nh != ih) {
115 if(nh != bh || nh != ih) {
109 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
116 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
110 }
117 }
111 $('#fonttest').remove();
118 $('#fonttest').remove();
112
119
113 page.show();
120 page.show();
114
121
115 layout_manager.do_resize();
122 layout_manager.do_resize();
116 var first_load = function () {
123 var first_load = function () {
117 layout_manager.do_resize();
124 layout_manager.do_resize();
118 var hash = document.location.hash;
125 var hash = document.location.hash;
119 if (hash) {
126 if (hash) {
120 document.location.hash = '';
127 document.location.hash = '';
121 document.location.hash = hash;
128 document.location.hash = hash;
122 }
129 }
123 notebook.set_autosave_interval(notebook.minimum_autosave_interval);
130 notebook.set_autosave_interval(notebook.minimum_autosave_interval);
124 // only do this once
131 // only do this once
125 events.off('notebook_loaded.Notebook', first_load);
132 events.off('notebook_loaded.Notebook', first_load);
126 };
133 };
127 events.on('notebook_loaded.Notebook', first_load);
134 events.on('notebook_loaded.Notebook', first_load);
128
135
129 IPython.page = page;
136 IPython.page = page;
130 IPython.layout_manager = layout_manager;
137 IPython.layout_manager = layout_manager;
131 IPython.notebook = notebook;
138 IPython.notebook = notebook;
132 IPython.pager = pager;
139 IPython.pager = pager;
133 IPython.quick_help = quick_help;
140 IPython.quick_help = quick_help;
134 IPython.login_widget = login_widget;
141 IPython.login_widget = login_widget;
135 IPython.menubar = menubar;
142 IPython.menubar = menubar;
136 IPython.toolbar = toolbar;
143 IPython.toolbar = toolbar;
137 IPython.notification_area = notification_area;
144 IPython.notification_area = notification_area;
138 IPython.keyboard_manager = keyboard_manager;
145 IPython.keyboard_manager = keyboard_manager;
139 IPython.save_widget = save_widget;
146 IPython.save_widget = save_widget;
140 IPython.config = user_config;
147 IPython.config = user_config;
141 IPython.tooltip = notebook.tooltip;
148 IPython.tooltip = notebook.tooltip;
142
149
143 events.trigger('app_initialized.NotebookApp');
150 events.trigger('app_initialized.NotebookApp');
144 notebook.load_notebook(common_options.notebook_name, common_options.notebook_path);
151 notebook.load_notebook(common_options.notebook_name, common_options.notebook_path);
145
152
146 });
153 });
@@ -1,184 +1,187 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 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'base/js/utils',
7 'base/js/utils',
8 'base/js/dialog',
8 'base/js/dialog',
9 ], function(IPython, $, utils, dialog) {
9 ], function(IPython, $, utils, dialog) {
10 "use strict";
10 "use strict";
11 var platform = utils.platform;
11 var platform = utils.platform;
12
12
13 var QuickHelp = function (options) {
13 var QuickHelp = function (options) {
14 // Constructor
14 // Constructor
15 //
15 //
16 // Parameters:
16 // Parameters:
17 // options: dictionary
17 // options: dictionary
18 // Dictionary of keyword arguments.
18 // Dictionary of keyword arguments.
19 // events: $(Events) instance
19 // events: $(Events) instance
20 // keyboard_manager: KeyboardManager instance
20 // keyboard_manager: KeyboardManager instance
21 // notebook: Notebook instance
21 // notebook: Notebook instance
22 this.keyboard_manager = options.keyboard_manager;
22 this.keyboard_manager = options.keyboard_manager;
23 this.notebook = options.notebook;
23 this.notebook = options.notebook;
24 this.keyboard_manager.quick_help = this;
24 this.keyboard_manager.quick_help = this;
25 this.events = options.events;
25 this.events = options.events;
26 };
26 };
27
27
28 var cmd_ctrl = 'Ctrl-';
28 var cmd_ctrl = 'Ctrl-';
29 var platform_specific;
29 var platform_specific;
30
30
31 if (platform === 'MacOS') {
31 if (platform === 'MacOS') {
32 // Mac OS X specific
32 // Mac OS X specific
33 cmd_ctrl = 'Cmd-';
33 cmd_ctrl = 'Cmd-';
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
44 platform_specific = [
44 platform_specific = [
45 { shortcut: "Ctrl-Home", help:"go to cell start" },
45 { shortcut: "Ctrl-Home", help:"go to cell start" },
46 { shortcut: "Ctrl-Up", help:"go to cell start" },
46 { shortcut: "Ctrl-Up", help:"go to cell start" },
47 { shortcut: "Ctrl-End", help:"go to cell end" },
47 { shortcut: "Ctrl-End", help:"go to cell end" },
48 { shortcut: "Ctrl-Down", help:"go to cell end" },
48 { shortcut: "Ctrl-Down", help:"go to cell end" },
49 { shortcut: "Ctrl-Left", help:"go one word left" },
49 { shortcut: "Ctrl-Left", help:"go one word left" },
50 { shortcut: "Ctrl-Right", help:"go one word right" },
50 { shortcut: "Ctrl-Right", help:"go one word right" },
51 { shortcut: "Ctrl-Backspace", help:"del word before" },
51 { shortcut: "Ctrl-Backspace", help:"del word before" },
52 { shortcut: "Ctrl-Delete", help:"del word after" },
52 { shortcut: "Ctrl-Delete", help:"del word after" },
53 ];
53 ];
54 }
54 }
55
55
56 var cm_shortcuts = [
56 var cm_shortcuts = [
57 { shortcut:"Tab", help:"code completion or indent" },
57 { shortcut:"Tab", help:"code completion or indent" },
58 { shortcut:"Shift-Tab", help:"tooltip" },
58 { shortcut:"Shift-Tab", help:"tooltip" },
59 { shortcut: cmd_ctrl + "]", help:"indent" },
59 { shortcut: cmd_ctrl + "]", help:"indent" },
60 { shortcut: cmd_ctrl + "[", help:"dedent" },
60 { shortcut: cmd_ctrl + "[", help:"dedent" },
61 { shortcut: cmd_ctrl + "a", help:"select all" },
61 { shortcut: cmd_ctrl + "a", help:"select all" },
62 { shortcut: cmd_ctrl + "z", help:"undo" },
62 { shortcut: cmd_ctrl + "z", help:"undo" },
63 { shortcut: cmd_ctrl + "Shift-z", help:"redo" },
63 { shortcut: cmd_ctrl + "Shift-z", help:"redo" },
64 { shortcut: cmd_ctrl + "y", help:"redo" },
64 { shortcut: cmd_ctrl + "y", help:"redo" },
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;
75 if ( this.force_rebuild ) {
71 if ( this.force_rebuild ) {
76 this.shortcut_dialog.remove();
72 this.shortcut_dialog.remove();
77 delete(this.shortcut_dialog);
73 delete(this.shortcut_dialog);
78 this.force_rebuild = false;
74 this.force_rebuild = false;
79 }
75 }
80 if ( this.shortcut_dialog ){
76 if ( this.shortcut_dialog ){
81 // if dialog is already shown, close it
77 // if dialog is already shown, close it
82 $(this.shortcut_dialog).modal("toggle");
78 $(this.shortcut_dialog).modal("toggle");
83 return;
79 return;
84 }
80 }
85 var command_shortcuts = this.keyboard_manager.command_shortcuts.help();
81 var command_shortcuts = this.keyboard_manager.command_shortcuts.help();
86 var edit_shortcuts = this.keyboard_manager.edit_shortcuts.help();
82 var edit_shortcuts = this.keyboard_manager.edit_shortcuts.help();
87 var help, shortcut;
83 var help, shortcut;
88 var i, half, n;
84 var i, half, n;
89 var element = $('<div/>');
85 var element = $('<div/>');
90
86
91 // The documentation
87 // The documentation
92 var doc = $('<div/>').addClass('alert alert-warning');
88 var doc = $('<div/>').addClass('alert alert-warning');
93 doc.append(
89 doc.append(
94 $('<button/>').addClass('close').attr('data-dismiss','alert').html('&times;')
90 $('<button/>').addClass('close').attr('data-dismiss','alert').html('&times;')
95 ).append(
91 ).append(
96 'The IPython Notebook has two different keyboard input modes. <b>Edit mode</b> '+
92 'The IPython Notebook has two different keyboard input modes. <b>Edit mode</b> '+
97 'allows you to type code/text into a cell and is indicated by a green cell '+
93 'allows you to type code/text into a cell and is indicated by a green cell '+
98 'border. <b>Command mode</b> binds the keyboard to notebook level actions '+
94 'border. <b>Command mode</b> binds the keyboard to notebook level actions '+
99 'and is indicated by a grey cell border.'
95 'and is indicated by a grey cell border.'
100 );
96 );
101 element.append(doc);
97 element.append(doc);
102
98
103 // Command mode
99 // Command mode
104 var cmd_div = this.build_command_help();
100 var cmd_div = this.build_command_help();
105 element.append(cmd_div);
101 element.append(cmd_div);
106
102
107 // Edit mode
103 // Edit mode
108 var edit_div = this.build_edit_help(cm_shortcuts);
104 var edit_div = this.build_edit_help(cm_shortcuts);
109 element.append(edit_div);
105 element.append(edit_div);
110
106
111 this.shortcut_dialog = dialog.modal({
107 this.shortcut_dialog = dialog.modal({
112 title : "Keyboard shortcuts",
108 title : "Keyboard shortcuts",
113 body : element,
109 body : element,
114 destroy : false,
110 destroy : false,
115 buttons : {
111 buttons : {
116 Close : {}
112 Close : {}
117 },
113 },
118 notebook: this.notebook,
114 notebook: this.notebook,
119 keyboard_manager: this.keyboard_manager,
115 keyboard_manager: this.keyboard_manager,
120 });
116 });
121 this.shortcut_dialog.addClass("modal_stretch");
117 this.shortcut_dialog.addClass("modal_stretch");
122
118
123 this.events.on('rebuild.QuickHelp', function() { that.force_rebuild = true;});
119 this.events.on('rebuild.QuickHelp', function() { that.force_rebuild = true;});
124 };
120 };
125
121
126 QuickHelp.prototype.build_command_help = function () {
122 QuickHelp.prototype.build_command_help = function () {
127 var command_shortcuts = this.keyboard_manager.command_shortcuts.help();
123 var command_shortcuts = this.keyboard_manager.command_shortcuts.help();
128 return build_div('<h4>Command Mode (press <code>Esc</code> to enable)</h4>', command_shortcuts);
124 return build_div('<h4>Command Mode (press <code>Esc</code> to enable)</h4>', command_shortcuts);
129 };
125 };
130
126
131 var special_case = { pageup: "PageUp", pagedown: "Page Down", 'minus': '-' };
127 var special_case = { pageup: "PageUp", pagedown: "Page Down", 'minus': '-' };
132 var prettify = function (s) {
128 var prettify = function (s) {
133 s = s.replace(/-$/, 'minus'); // catch shortcuts using '-' key
129 s = s.replace(/-$/, 'minus'); // catch shortcuts using '-' key
134 var keys = s.split('-');
130 var keys = s.split('-');
135 var k, i;
131 var k, i;
136 for (i=0; i < keys.length; i++) {
132 for (i=0; i < keys.length; i++) {
137 k = keys[i];
133 k = keys[i];
138 if ( k.length == 1 ) {
134 if ( k.length == 1 ) {
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('-');
146
144
147
145
148 };
146 };
149
147
150 QuickHelp.prototype.build_edit_help = function (cm_shortcuts) {
148 QuickHelp.prototype.build_edit_help = function (cm_shortcuts) {
151 var edit_shortcuts = this.keyboard_manager.edit_shortcuts.help();
149 var edit_shortcuts = this.keyboard_manager.edit_shortcuts.help();
152 jQuery.merge(cm_shortcuts, edit_shortcuts);
150 jQuery.merge(cm_shortcuts, edit_shortcuts);
153 return build_div('<h4>Edit Mode (press <code>Enter</code> to enable)</h4>', cm_shortcuts);
151 return build_div('<h4>Edit Mode (press <code>Enter</code> to enable)</h4>', cm_shortcuts);
154 };
152 };
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));
162
165
163 };
166 };
164
167
165 var build_div = function (title, shortcuts) {
168 var build_div = function (title, shortcuts) {
166 var i, half, n;
169 var i, half, n;
167 var div = $('<div/>').append($(title));
170 var div = $('<div/>').append($(title));
168 var sub_div = $('<div/>').addClass('hbox');
171 var sub_div = $('<div/>').addClass('hbox');
169 var col1 = $('<div/>').addClass('box-flex1');
172 var col1 = $('<div/>').addClass('box-flex1');
170 var col2 = $('<div/>').addClass('box-flex1');
173 var col2 = $('<div/>').addClass('box-flex1');
171 n = shortcuts.length;
174 n = shortcuts.length;
172 half = ~~(n/2); // Truncate :)
175 half = ~~(n/2); // Truncate :)
173 for (i=0; i<half; i++) { col1.append( build_one(shortcuts[i]) ); }
176 for (i=0; i<half; i++) { col1.append( build_one(shortcuts[i]) ); }
174 for (i=half; i<n; i++) { col2.append( build_one(shortcuts[i]) ); }
177 for (i=half; i<n; i++) { col2.append( build_one(shortcuts[i]) ); }
175 sub_div.append(col1).append(col2);
178 sub_div.append(col1).append(col2);
176 div.append(sub_div);
179 div.append(sub_div);
177 return div;
180 return div;
178 };
181 };
179
182
180 // Backwards compatability.
183 // Backwards compatability.
181 IPython.QuickHelp = QuickHelp;
184 IPython.QuickHelp = QuickHelp;
182
185
183 return {'QuickHelp': QuickHelp};
186 return {'QuickHelp': QuickHelp};
184 });
187 });
@@ -1,100 +1,100 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 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 ], function(IPython, $) {
7 ], function(IPython, $) {
8 "use strict";
8 "use strict";
9
9
10 /**
10 /**
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;
18 this.layout_manager = layout_manager;
18 this.layout_manager = layout_manager;
19 if (this.selector !== undefined) {
19 if (this.selector !== undefined) {
20 this.element = $(selector);
20 this.element = $(selector);
21 this.style();
21 this.style();
22 }
22 }
23 };
23 };
24
24
25 /**
25 /**
26 * add a group of button into the current toolbar.
26 * add a group of button into the current toolbar.
27 *
27 *
28 *
28 *
29 * @example
29 * @example
30 *
30 *
31 * IPython.toolbar.add_buttons_group([
31 * IPython.toolbar.add_buttons_group([
32 * {
32 * {
33 * label:'my button',
33 * label:'my button',
34 * icon:'icon-hdd',
34 * icon:'icon-hdd',
35 * callback:function(){alert('hoho')},
35 * callback:function(){alert('hoho')},
36 * id : 'my_button_id', // this is optional
36 * id : 'my_button_id', // this is optional
37 * },
37 * },
38 * {
38 * {
39 * label:'my second button',
39 * label:'my second button',
40 * icon:'icon-play',
40 * icon:'icon-play',
41 * callback:function(){alert('be carefull I cut')}
41 * callback:function(){alert('be carefull I cut')}
42 * }
42 * }
43 * ],
43 * ],
44 * "my_button_group_id"
44 * "my_button_group_id"
45 * )
45 * )
46 *
46 *
47 * @method add_buttons_group
47 * @method add_buttons_group
48 * @param list {List}
48 * @param list {List}
49 * List of button of the group, with the following paramter for each :
49 * List of button of the group, with the following paramter for each :
50 * @param list.label {string} text to show on button hover
50 * @param list.label {string} text to show on button hover
51 * @param list.icon {string} icon to choose from [Font Awesome](http://fortawesome.github.io/Font-Awesome)
51 * @param list.icon {string} icon to choose from [Font Awesome](http://fortawesome.github.io/Font-Awesome)
52 * @param list.callback {function} function to be called on button click
52 * @param list.callback {function} function to be called on button click
53 * @param [list.id] {String} id to give to the button
53 * @param [list.id] {String} id to give to the button
54 * @param [group_id] {String} optionnal id to give to the group
54 * @param [group_id] {String} optionnal id to give to the group
55 *
55 *
56 */
56 */
57 ToolBar.prototype.add_buttons_group = function (list, group_id) {
57 ToolBar.prototype.add_buttons_group = function (list, group_id) {
58 var btn_group = $('<div/>').addClass("btn-group");
58 var btn_group = $('<div/>').addClass("btn-group");
59 if( group_id !== undefined ) {
59 if( group_id !== undefined ) {
60 btn_group.attr('id',group_id);
60 btn_group.attr('id',group_id);
61 }
61 }
62 var el;
62 var el;
63 for(var i=0; i < list.length; i++) {
63 for(var i=0; i < list.length; i++) {
64 el = list[i];
64 el = list[i];
65 var button = $('<button/>')
65 var button = $('<button/>')
66 .addClass('btn btn-default')
66 .addClass('btn btn-default')
67 .attr("title", el.label)
67 .attr("title", el.label)
68 .append(
68 .append(
69 $("<i/>").addClass(el.icon).addClass('fa')
69 $("<i/>").addClass(el.icon).addClass('fa')
70 );
70 );
71 var id = el.id;
71 var id = el.id;
72 if( id !== undefined )
72 if( id !== undefined )
73 button.attr('id',id);
73 button.attr('id',id);
74 var fun = el.callback;
74 var fun = el.callback;
75 button.click(fun);
75 button.click(fun);
76 btn_group.append(button);
76 btn_group.append(button);
77 }
77 }
78 $(this.selector).append(btn_group);
78 $(this.selector).append(btn_group);
79 };
79 };
80
80
81 ToolBar.prototype.style = function () {
81 ToolBar.prototype.style = function () {
82 this.element.addClass('toolbar');
82 this.element.addClass('toolbar');
83 };
83 };
84
84
85 /**
85 /**
86 * Show and hide toolbar
86 * Show and hide toolbar
87 * @method toggle
87 * @method toggle
88 */
88 */
89 ToolBar.prototype.toggle = function () {
89 ToolBar.prototype.toggle = function () {
90 this.element.toggle();
90 this.element.toggle();
91 if (this.layout_manager !== undefined) {
91 if (this.layout_manager !== undefined) {
92 this.layout_manager.do_resize();
92 this.layout_manager.do_resize();
93 }
93 }
94 };
94 };
95
95
96 // Backwards compatibility.
96 // Backwards compatibility.
97 IPython.ToolBar = ToolBar;
97 IPython.ToolBar = ToolBar;
98
98
99 return {'ToolBar': ToolBar};
99 return {'ToolBar': ToolBar};
100 });
100 });
General Comments 0
You need to be logged in to leave comments. Login now