diff --git a/IPython/html/static/base/js/keyboard.js b/IPython/html/static/base/js/keyboard.js
new file mode 100644
index 0000000..20e38ba
--- /dev/null
+++ b/IPython/html/static/base/js/keyboard.js
@@ -0,0 +1,251 @@
+//----------------------------------------------------------------------------
+// Copyright (C) 2011 The IPython Development Team
+//
+// Distributed under the terms of the BSD License. The full license is in
+// the file COPYING, distributed as part of this software.
+//----------------------------------------------------------------------------
+
+//============================================================================
+// Keyboard management
+//============================================================================
+
+IPython.namespace('IPython.keyboard');
+
+IPython.keyboard = (function (IPython) {
+ "use strict";
+
+ // Setup global keycodes and inverse keycodes.
+
+ // See http://unixpapa.com/js/key.html for a complete description. The short of
+ // it is that there are different keycode sets. Firefox uses the "Mozilla keycodes"
+ // and Webkit/IE use the "IE keycodes". These keycode sets are mostly the same
+ // but have minor differences.
+
+ // These apply to Firefox, (Webkit and IE)
+ var _keycodes = {
+ 'a': 65, 'b': 66, 'c': 67, 'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73,
+ 'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81, 'r': 82,
+ 's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, 'z': 90,
+ '1 !': 49, '2 @': 50, '3 #': 51, '4 $': 52, '5 %': 53, '6 ^': 54,
+ '7 &': 55, '8 *': 56, '9 (': 57, '0 )': 48,
+ '[ {': 219, '] }': 221, '` ~': 192, ', <': 188, '. >': 190, '/ ?': 191,
+ '\\ |': 220, '\' "': 222,
+ 'numpad0': 96, 'numpad1': 97, 'numpad2': 98, 'numpad3': 99, 'numpad4': 100,
+ 'numpad5': 101, 'numpad6': 102, 'numpad7': 103, 'numpad8': 104, 'numpad9': 105,
+ 'multiply': 106, 'add': 107, 'subtract': 109, 'decimal': 110, 'divide': 111,
+ 'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118,
+ 'f8': 119, 'f9': 120, 'f11': 122, 'f12': 123, 'f13': 124, 'f14': 125, 'f15': 126,
+ 'backspace': 8, 'tab': 9, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18,
+ 'meta': 91, 'capslock': 20, 'esc': 27, 'space': 32, 'pageup': 33, 'pagedown': 34,
+ 'end': 35, 'home': 36, 'left': 37, 'up': 38, 'right': 39, 'down': 40,
+ 'insert': 45, 'delete': 46, 'numlock': 144,
+ };
+
+ // These apply to Firefox and Opera
+ var _mozilla_keycodes = {
+ '; :': 59, '= +': 61, '- _': 173, 'meta': 224
+ }
+
+ // This apply to Webkit and IE
+ var _ie_keycodes = {
+ '; :': 186, '= +': 187, '- _': 189,
+ }
+
+ var browser = IPython.utils.browser[0];
+ var platform = IPython.utils.platform;
+
+ if (browser === 'Firefox' || browser === 'Opera') {
+ $.extend(_keycodes, _mozilla_keycodes);
+ } else if (browser === 'Safari' || browser === 'Chrome' || browser === 'MSIE') {
+ $.extend(_keycodes, _ie_keycodes);
+ }
+
+ var keycodes = {};
+ var inv_keycodes = {};
+ for (var name in _keycodes) {
+ var names = name.split(' ');
+ if (names.length === 1) {
+ var n = names[0]
+ keycodes[n] = _keycodes[n]
+ inv_keycodes[_keycodes[n]] = n
+ } else {
+ var primary = names[0];
+ var secondary = names[1];
+ keycodes[primary] = _keycodes[name]
+ keycodes[secondary] = _keycodes[name]
+ inv_keycodes[_keycodes[name]] = primary
+ }
+ }
+
+ var normalize_key = function (key) {
+ return inv_keycodes[keycodes[key]];
+ }
+
+ var normalize_shortcut = function (shortcut) {
+ // Put a shortcut into normalized form:
+ // 1. Make lowercase
+ // 2. Replace cmd by meta
+ // 3. Sort '+' separated modifiers into the order alt+ctrl+meta+shift
+ // 4. Normalize keys
+ shortcut = shortcut.toLowerCase().replace('cmd', 'meta');
+ var values = shortcut.split("+");
+ if (values.length === 1) {
+ return normalize_key(values[0])
+ } else {
+ var modifiers = values.slice(0,-1);
+ var key = normalize_key(values[values.length-1]);
+ modifiers.sort();
+ return modifiers.join('+') + '+' + key;
+ }
+ }
+
+ var shortcut_to_event = function (shortcut, type) {
+ // Convert a shortcut (shift+r) to a jQuery Event object
+ type = type || 'keydown';
+ shortcut = normalize_shortcut(shortcut);
+ var values = shortcut.split("+");
+ var modifiers = values.slice(0,-1);
+ var key = values[values.length-1];
+ var opts = {which: keycodes[key]};
+ if (modifiers.indexOf('alt') !== -1) {opts.altKey = true;}
+ if (modifiers.indexOf('ctrl') !== -1) {opts.ctrlKey = true;}
+ if (modifiers.indexOf('meta') !== -1) {opts.metaKey = true;}
+ if (modifiers.indexOf('shift') !== -1) {opts.shiftKey = true;}
+ return $.Event(type, opts);
+ }
+
+ var event_to_shortcut = function (event) {
+ // Convert a jQuery Event object to a shortcut (shift+r)
+ var shortcut = '';
+ var key = inv_keycodes[event.which]
+ if (event.altKey && key !== 'alt') {shortcut += 'alt+';}
+ if (event.ctrlKey && key !== 'ctrl') {shortcut += 'ctrl+';}
+ if (event.metaKey && key !== 'meta') {shortcut += 'meta+';}
+ if (event.shiftKey && key !== 'shift') {shortcut += 'shift+';}
+ shortcut += key;
+ return shortcut
+ }
+
+ var trigger_keydown = function (shortcut, element) {
+ // Trigger shortcut keydown on an element
+ element = element || document;
+ element = $(element);
+ var event = shortcut_to_event(shortcut, 'keydown');
+ element.trigger(event);
+ }
+
+
+ // Shortcut manager class
+
+ var ShortcutManager = function (delay) {
+ this._shortcuts = {}
+ this._counts = {}
+ this._timers = {}
+ this.delay = delay || 800; // delay in milliseconds
+ }
+
+ ShortcutManager.prototype.help = function () {
+ var help = [];
+ for (var shortcut in this._shortcuts) {
+ var help_string = this._shortcuts[shortcut]['help'];
+ var help_index = this._shortcuts[shortcut]['help_index'];
+ if (help_string) {
+ if (platform === 'MacOS') {
+ shortcut = shortcut.replace('meta', 'cmd');
+ }
+ help.push({
+ shortcut: shortcut,
+ help: help_string,
+ help_index: help_index}
+ );
+ }
+ }
+ help.sort(function (a, b) {
+ if (a.help_index > b.help_index)
+ return 1;
+ if (a.help_index < b.help_index)
+ return -1;
+ return 0;
+ });
+ return help;
+ }
+
+ ShortcutManager.prototype.clear_shortcuts = function () {
+ this._shortcuts = {};
+ }
+
+ ShortcutManager.prototype.add_shortcut = function (shortcut, data) {
+ if (typeof(data) === 'function') {
+ data = {help: '', help_index: '', handler: data}
+ }
+ data.help_index = data.help_index || '';
+ data.help = data.help || '';
+ data.count = data.count || 1;
+ if (data.help_index === '') {
+ data.help_index = 'zz';
+ }
+ shortcut = normalize_shortcut(shortcut);
+ this._counts[shortcut] = 0;
+ this._shortcuts[shortcut] = data;
+ }
+
+ ShortcutManager.prototype.add_shortcuts = function (data) {
+ for (var shortcut in data) {
+ this.add_shortcut(shortcut, data[shortcut]);
+ }
+ }
+
+ ShortcutManager.prototype.remove_shortcut = function (shortcut) {
+ shortcut = normalize_shortcut(shortcut);
+ delete this._counts[shortcut];
+ delete this._shortcuts[shortcut];
+ }
+
+ ShortcutManager.prototype.count_handler = function (shortcut, event, data) {
+ var that = this;
+ var c = this._counts;
+ var t = this._timers;
+ var timer = null;
+ if (c[shortcut] === data.count-1) {
+ c[shortcut] = 0;
+ var timer = t[shortcut];
+ if (timer) {clearTimeout(timer); delete t[shortcut];}
+ return data.handler(event);
+ } else {
+ c[shortcut] = c[shortcut] + 1;
+ timer = setTimeout(function () {
+ c[shortcut] = 0;
+ }, that.delay);
+ t[shortcut] = timer;
+ }
+ return false;
+ }
+
+ ShortcutManager.prototype.call_handler = function (event) {
+ var shortcut = event_to_shortcut(event);
+ var data = this._shortcuts[shortcut];
+ if (data) {
+ var handler = data['handler'];
+ if (handler) {
+ if (data.count === 1) {
+ return handler(event);
+ } else if (data.count > 1) {
+ return this.count_handler(shortcut, event, data);
+ }
+ }
+ }
+ return true;
+ }
+
+ return {
+ keycodes : keycodes,
+ inv_keycodes : inv_keycodes,
+ ShortcutManager : ShortcutManager,
+ normalize_key : normalize_key,
+ normalize_shortcut : normalize_shortcut,
+ shortcut_to_event : shortcut_to_event,
+ event_to_shortcut : event_to_shortcut,
+ trigger_keydown : trigger_keydown
+ }
+
+}(IPython));
diff --git a/IPython/html/static/base/js/utils.js b/IPython/html/static/base/js/utils.js
index 4be262c..cfd138b 100644
--- a/IPython/html/static/base/js/utils.js
+++ b/IPython/html/static/base/js/utils.js
@@ -350,66 +350,6 @@ IPython.utils = (function (IPython) {
"$1$2$3");
}
- // some keycodes that seem to be platform/browser independent
- var keycodes = {
- BACKSPACE: 8,
- TAB : 9,
- ENTER : 13,
- SHIFT : 16,
- CTRL : 17,
- CONTROL : 17,
- ALT : 18,
- CAPS_LOCK: 20,
- ESC : 27,
- SPACE : 32,
- PGUP : 33,
- PGDOWN : 34,
- END : 35,
- HOME : 36,
- LEFT_ARROW: 37,
- LEFTARROW: 37,
- LEFT : 37,
- UP_ARROW : 38,
- UPARROW : 38,
- UP : 38,
- RIGHT_ARROW:39,
- RIGHTARROW:39,
- RIGHT : 39,
- DOWN_ARROW: 40,
- DOWNARROW: 40,
- DOWN : 40,
- I : 73,
- M : 77,
- // all three of these keys may be COMMAND on OS X:
- LEFT_SUPER : 91,
- RIGHT_SUPER : 92,
- COMMAND : 93,
- };
-
- // trigger a key press event
- var press = function (key) {
- var key_press = $.Event('keydown', {which: key});
- $(document).trigger(key_press);
- }
-
- var press_up = function() { press(keycodes.UP); };
- var press_down = function() { press(keycodes.DOWN); };
-
- var press_ctrl_enter = function() {
- $(document).trigger($.Event('keydown', {which: keycodes.ENTER, ctrlKey: true}));
- };
-
- var press_shift_enter = function() {
- $(document).trigger($.Event('keydown', {which: keycodes.ENTER, shiftKey: true}));
- };
-
- // trigger the ctrl-m shortcut followed by one of our keys
- var press_ghetto = function(key) {
- $(document).trigger($.Event('keydown', {which: keycodes.M, ctrlKey: true}));
- press(key);
- };
-
-
var points_to_pixels = function (points) {
// A reasonably good way of converting between points and pixels.
var test = $('
');
@@ -431,7 +371,6 @@ IPython.utils = (function (IPython) {
};
};
-
var url_path_join = function () {
// join a sequence of url components with '/'
var url = '';
@@ -554,13 +493,6 @@ IPython.utils = (function (IPython) {
regex_split : regex_split,
uuid : uuid,
fixConsole : fixConsole,
- keycodes : keycodes,
- press : press,
- press_up : press_up,
- press_down : press_down,
- press_ctrl_enter : press_ctrl_enter,
- press_shift_enter : press_shift_enter,
- press_ghetto : press_ghetto,
fixCarriageReturn : fixCarriageReturn,
autoLinkUrls : autoLinkUrls,
points_to_pixels : points_to_pixels,
diff --git a/IPython/html/static/notebook/js/celltoolbarpresets/rawcell.js b/IPython/html/static/notebook/js/celltoolbarpresets/rawcell.js
index 5560684..6f16cf3 100644
--- a/IPython/html/static/notebook/js/celltoolbarpresets/rawcell.js
+++ b/IPython/html/static/notebook/js/celltoolbarpresets/rawcell.js
@@ -14,7 +14,6 @@
var CellToolbar = IPython.CellToolbar;
var raw_cell_preset = [];
- var utils = IPython.utils;
var select_type = CellToolbar.utils.select_ui_generator([
["None", "-"],
@@ -58,7 +57,7 @@
var that = $(this);
// Upon ENTER, click the OK button.
that.find('input[type="text"]').keydown(function (event, ui) {
- if (event.which === utils.keycodes.ENTER) {
+ if (event.which === IPython.keyboard.keycodes.enter) {
that.find('.btn-primary').first().click();
return false;
}
diff --git a/IPython/html/static/notebook/js/codecell.js b/IPython/html/static/notebook/js/codecell.js
index 5a07968..3ef98ff 100644
--- a/IPython/html/static/notebook/js/codecell.js
+++ b/IPython/html/static/notebook/js/codecell.js
@@ -45,7 +45,7 @@ var IPython = (function (IPython) {
"use strict";
var utils = IPython.utils;
- var key = IPython.utils.keycodes;
+ var keycodes = IPython.keyboard.keycodes;
/**
* A Cell conceived to write code.
@@ -195,16 +195,16 @@ var IPython = (function (IPython) {
// whatever key is pressed, first, cancel the tooltip request before
// they are sent, and remove tooltip if any, except for tab again
var tooltip_closed = null;
- if (event.type === 'keydown' && event.which != key.TAB ) {
+ if (event.type === 'keydown' && event.which != keycodes.tab ) {
tooltip_closed = IPython.tooltip.remove_and_cancel_tooltip();
}
var cur = editor.getCursor();
- if (event.keyCode === key.ENTER){
+ if (event.keyCode === keycodes.enter){
this.auto_highlight();
}
- if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey || event.altKey)) {
+ if (event.keyCode === keycodes.enter && (event.shiftKey || event.ctrlKey || event.altKey)) {
// Always ignore shift-enter in CodeMirror as we handle it.
return true;
} else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
@@ -214,7 +214,7 @@ var IPython = (function (IPython) {
// The second argument says to hide the tooltip if the docstring
// is actually empty
IPython.tooltip.pending(that, true);
- } else if (event.which === key.UPARROW && event.type === 'keydown') {
+ } else if (event.which === keycodes.up && event.type === 'keydown') {
// If we are not at the top, let CM handle the up arrow and
// prevent the global keydown handler from handling it.
if (!that.at_top()) {
@@ -223,7 +223,7 @@ var IPython = (function (IPython) {
} else {
return true;
}
- } else if (event.which === key.ESC && event.type === 'keydown') {
+ } else if (event.which === keycodes.esc && event.type === 'keydown') {
// First see if the tooltip is active and if so cancel it.
if (tooltip_closed) {
// The call to remove_and_cancel_tooltip above in L177 doesn't pass
@@ -249,7 +249,7 @@ var IPython = (function (IPython) {
// Don't let CM handle the event, defer to global handling.
return true;
}
- } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
+ } else if (event.which === keycodes.down && event.type === 'keydown') {
// If we are not at the bottom, let CM handle the down arrow and
// prevent the global keydown handler from handling it.
if (!that.at_bottom()) {
@@ -258,7 +258,7 @@ var IPython = (function (IPython) {
} else {
return true;
}
- } else if (event.keyCode === key.TAB && event.type === 'keydown' && event.shiftKey) {
+ } else if (event.keyCode === keycodes.tab && event.type === 'keydown' && event.shiftKey) {
if (editor.somethingSelected()){
var anchor = editor.getCursor("anchor");
var head = editor.getCursor("head");
@@ -269,7 +269,7 @@ var IPython = (function (IPython) {
IPython.tooltip.request(that);
event.stop();
return true;
- } else if (event.keyCode === key.TAB && event.type == 'keydown') {
+ } else if (event.keyCode === keycodes.tab && event.type == 'keydown') {
// Tab completion.
IPython.tooltip.remove_and_cancel_tooltip();
if (editor.somethingSelected()) {
diff --git a/IPython/html/static/notebook/js/completer.js b/IPython/html/static/notebook/js/completer.js
index 5b8c518..3fd7245 100644
--- a/IPython/html/static/notebook/js/completer.js
+++ b/IPython/html/static/notebook/js/completer.js
@@ -6,7 +6,7 @@ var IPython = (function (IPython) {
"use strict";
// easier key mapping
- var key = IPython.utils.keycodes;
+ var keycodes = IPython.keyboard.keycodes;
function prepend_n_prc(str, n) {
for( var i =0 ; i< n ; i++){
@@ -307,29 +307,22 @@ var IPython = (function (IPython) {
Completer.prototype.keydown = function (event) {
var code = event.keyCode;
var that = this;
- var special_key = false;
-
- // detect special keys like SHIFT,PGUP,...
- for( var _key in key ) {
- if (code == key[_key] ) {
- special_key = true;
- }
- }
// Enter
- if (code == key.ENTER) {
+ if (code == keycodes.enter) {
CodeMirror.e_stop(event);
this.pick();
}
// Escape or backspace
- else if (code == key.ESC) {
+ else if (code == keycodes.esc) {
CodeMirror.e_stop(event);
this.close();
this.editor.focus();
- } else if (code == key.BACKSPACE) {
+
+ } else if (code == keycodes.backspace) {
this.close();
this.editor.focus();
- } else if (code == key.TAB) {
+ } else if (code == keycodes.tab) {
//all the fastforwarding operation,
//Check that shared start is not null which can append with prefixed completion
// like %pylab , pylab have no shred start, and ff will result in py
@@ -345,7 +338,7 @@ var IPython = (function (IPython) {
setTimeout(function () {
that.carry_on_completion();
}, 50);
- } else if (code == key.UPARROW || code == key.DOWNARROW) {
+ } else if (code == keycodes.up || code == keycodes.down) {
// need to do that to be able to move the arrow
// when on the first or last line ofo a code cell
event.stopPropagation();
@@ -365,8 +358,8 @@ var IPython = (function (IPython) {
// don't handle keypress if it's not a character (arrows on FF)
// or ENTER/TAB
if (event.charCode === 0 ||
- code == key.ENTER ||
- code == key.TAB
+ code == keycodes.enter ||
+ code == keycodes.tab
) return;
var cur = this.editor.getCursor();
diff --git a/IPython/html/static/notebook/js/keyboardmanager.js b/IPython/html/static/notebook/js/keyboardmanager.js
index c28bfb2..b82980c 100644
--- a/IPython/html/static/notebook/js/keyboardmanager.js
+++ b/IPython/html/static/notebook/js/keyboardmanager.js
@@ -12,69 +12,8 @@
var IPython = (function (IPython) {
"use strict";
- // Setup global keycodes and inverse keycodes.
-
- // See http://unixpapa.com/js/key.html for a complete description. The short of
- // it is that there are different keycode sets. Firefox uses the "Mozilla keycodes"
- // and Webkit/IE use the "IE keycodes". These keycode sets are mostly the same
- // but have minor differences.
-
- // These apply to Firefox, (Webkit and IE)
- var _keycodes = {
- 'a': 65, 'b': 66, 'c': 67, 'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73,
- 'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81, 'r': 82,
- 's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, 'z': 90,
- '1 !': 49, '2 @': 50, '3 #': 51, '4 $': 52, '5 %': 53, '6 ^': 54,
- '7 &': 55, '8 *': 56, '9 (': 57, '0 )': 48,
- '[ {': 219, '] }': 221, '` ~': 192, ', <': 188, '. >': 190, '/ ?': 191,
- '\\ |': 220, '\' "': 222,
- 'numpad0': 96, 'numpad1': 97, 'numpad2': 98, 'numpad3': 99, 'numpad4': 100,
- 'numpad5': 101, 'numpad6': 102, 'numpad7': 103, 'numpad8': 104, 'numpad9': 105,
- 'multiply': 106, 'add': 107, 'subtract': 109, 'decimal': 110, 'divide': 111,
- 'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118,
- 'f8': 119, 'f9': 120, 'f11': 122, 'f12': 123, 'f13': 124, 'f14': 125, 'f15': 126,
- 'backspace': 8, 'tab': 9, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18,
- 'meta': 91, 'capslock': 20, 'esc': 27, 'space': 32, 'pageup': 33, 'pagedown': 34,
- 'end': 35, 'home': 36, 'left': 37, 'up': 38, 'right': 39, 'down': 40,
- 'insert': 45, 'delete': 46, 'numlock': 144,
- };
-
- // These apply to Firefox and Opera
- var _mozilla_keycodes = {
- '; :': 59, '= +': 61, '- _': 173, 'meta': 224
- };
-
- // This apply to Webkit and IE
- var _ie_keycodes = {
- '; :': 186, '= +': 187, '- _': 189,
- };
-
var browser = IPython.utils.browser[0];
var platform = IPython.utils.platform;
-
- if (browser === 'Firefox' || browser === 'Opera') {
- $.extend(_keycodes, _mozilla_keycodes);
- } else if (browser === 'Safari' || browser === 'Chrome' || browser === 'MSIE') {
- $.extend(_keycodes, _ie_keycodes);
- }
-
- var keycodes = {};
- var inv_keycodes = {};
- for (var name in _keycodes) {
- var names = name.split(' ');
- if (names.length === 1) {
- var n = names[0];
- keycodes[n] = _keycodes[n];
- inv_keycodes[_keycodes[n]] = n;
- } else {
- var primary = names[0];
- var secondary = names[1];
- keycodes[primary] = _keycodes[name];
- keycodes[secondary] = _keycodes[name];
- inv_keycodes[_keycodes[name]] = primary;
- }
- }
-
// Default keyboard shortcuts
@@ -545,141 +484,11 @@ var IPython = (function (IPython) {
};
- // Shortcut manager class
-
- var ShortcutManager = function (delay) {
- this._shortcuts = {};
- this._counts = {};
- this._timers = {};
- this.delay = delay || 800; // delay in milliseconds
- };
-
- ShortcutManager.prototype.help = function () {
- var help = [];
- for (var shortcut in this._shortcuts) {
- var help_string = this._shortcuts[shortcut].help;
- var help_index = this._shortcuts[shortcut].help_index;
- if (help_string) {
- if (platform === 'MacOS') {
- shortcut = shortcut.replace('meta', 'cmd');
- }
- help.push({
- shortcut: shortcut,
- help: help_string,
- help_index: help_index}
- );
- }
- }
- help.sort(function (a, b) {
- if (a.help_index > b.help_index)
- return 1;
- if (a.help_index < b.help_index)
- return -1;
- return 0;
- });
- return help;
- };
-
- ShortcutManager.prototype.normalize_key = function (key) {
- return inv_keycodes[keycodes[key]];
- };
-
- ShortcutManager.prototype.normalize_shortcut = function (shortcut) {
- // Sort a sequence of + separated modifiers into the order alt+ctrl+meta+shift
- shortcut = shortcut.replace('cmd', 'meta').toLowerCase();
- var values = shortcut.split("+");
- if (values.length === 1) {
- return this.normalize_key(values[0]);
- } else {
- var modifiers = values.slice(0,-1);
- var key = this.normalize_key(values[values.length-1]);
- modifiers.sort();
- return modifiers.join('+') + '+' + key;
- }
- };
-
- ShortcutManager.prototype.event_to_shortcut = function (event) {
- // Convert a jQuery keyboard event to a strong based keyboard shortcut
- var shortcut = '';
- var key = inv_keycodes[event.which];
- if (event.altKey && key !== 'alt') {shortcut += 'alt+';}
- if (event.ctrlKey && key !== 'ctrl') {shortcut += 'ctrl+';}
- if (event.metaKey && key !== 'meta') {shortcut += 'meta+';}
- if (event.shiftKey && key !== 'shift') {shortcut += 'shift+';}
- shortcut += key;
- return shortcut;
- };
-
- ShortcutManager.prototype.clear_shortcuts = function () {
- this._shortcuts = {};
- };
-
- ShortcutManager.prototype.add_shortcut = function (shortcut, data) {
- if (typeof(data) === 'function') {
- data = {help: '', help_index: '', handler: data};
- }
- data.help_index = data.help_index || '';
- data.help = data.help || '';
- data.count = data.count || 1;
- if (data.help_index === '') {
- data.help_index = 'zz';
- }
- shortcut = this.normalize_shortcut(shortcut);
- this._counts[shortcut] = 0;
- this._shortcuts[shortcut] = data;
- };
-
- ShortcutManager.prototype.add_shortcuts = function (data) {
- for (var shortcut in data) {
- this.add_shortcut(shortcut, data[shortcut]);
- }
- };
-
- ShortcutManager.prototype.remove_shortcut = function (shortcut) {
- shortcut = this.normalize_shortcut(shortcut);
- delete this._counts[shortcut];
- delete this._shortcuts[shortcut];
- };
-
- ShortcutManager.prototype.count_handler = function (shortcut, event, data) {
- var that = this;
- var c = this._counts;
- var t = this._timers;
- var timer = null;
- if (c[shortcut] === data.count-1) {
- c[shortcut] = 0;
- timer = t[shortcut];
- if (timer) {clearTimeout(timer); delete t[shortcut];}
- return data.handler(event);
- } else {
- c[shortcut] = c[shortcut] + 1;
- timer = setTimeout(function () {
- c[shortcut] = 0;
- }, that.delay);
- t[shortcut] = timer;
- }
- return false;
- };
-
- ShortcutManager.prototype.call_handler = function (event) {
- var shortcut = this.event_to_shortcut(event);
- var data = this._shortcuts[shortcut];
- if (data) {
- var handler = data.handler;
- if (handler) {
- if (data.count === 1) {
- return handler(event);
- } else if (data.count > 1) {
- return this.count_handler(shortcut, event, data);
- }
- }
- }
- return true;
- };
+ // Main keyboard manager for the notebook
+ var ShortcutManager = IPython.keyboard.ShortcutManager;
+ var keycodes = IPython.keyboard.keycodes;
- // Main keyboard manager for the notebook
-
var KeyboardManager = function () {
this.mode = 'command';
this.enabled = true;
@@ -779,12 +588,9 @@ var IPython = (function (IPython) {
};
- IPython.keycodes = keycodes;
- IPython.inv_keycodes = inv_keycodes;
IPython.default_common_shortcuts = default_common_shortcuts;
IPython.default_edit_shortcuts = default_edit_shortcuts;
IPython.default_command_shortcuts = default_command_shortcuts;
- IPython.ShortcutManager = ShortcutManager;
IPython.KeyboardManager = KeyboardManager;
return IPython;
diff --git a/IPython/html/static/notebook/js/notebook.js b/IPython/html/static/notebook/js/notebook.js
index 97b2edb..d49fd49 100644
--- a/IPython/html/static/notebook/js/notebook.js
+++ b/IPython/html/static/notebook/js/notebook.js
@@ -1917,7 +1917,7 @@ var IPython = (function (IPython) {
var that = $(this);
// Upon ENTER, click the OK button.
that.find('input[type="text"]').keydown(function (event, ui) {
- if (event.which === utils.keycodes.ENTER) {
+ if (event.which === IPython.keyboard.keycodes.enter) {
that.find('.btn-primary').first().click();
}
});
diff --git a/IPython/html/static/notebook/js/outputarea.js b/IPython/html/static/notebook/js/outputarea.js
index c67e2b3..d4e1456 100644
--- a/IPython/html/static/notebook/js/outputarea.js
+++ b/IPython/html/static/notebook/js/outputarea.js
@@ -669,7 +669,7 @@ var IPython = (function (IPython) {
.keydown(function (event, ui) {
// make sure we submit on enter,
// and don't re-execute the *cell* on shift-enter
- if (event.which === utils.keycodes.ENTER) {
+ if (event.which === IPython.keyboard.keycodes.enter) {
that._submit_raw_input();
return false;
}
diff --git a/IPython/html/static/notebook/js/savewidget.js b/IPython/html/static/notebook/js/savewidget.js
index 046e35d..f645571 100644
--- a/IPython/html/static/notebook/js/savewidget.js
+++ b/IPython/html/static/notebook/js/savewidget.js
@@ -103,7 +103,7 @@ var IPython = (function (IPython) {
var that = $(this);
// Upon ENTER, click the OK button.
that.find('input[type="text"]').keydown(function (event, ui) {
- if (event.which === utils.keycodes.ENTER) {
+ if (event.which === IPython.keyboard.keycodes.enter) {
that.find('.btn-primary').first().click();
return false;
}
diff --git a/IPython/html/static/notebook/js/textcell.js b/IPython/html/static/notebook/js/textcell.js
index 4db5e8a..65d8e3a 100644
--- a/IPython/html/static/notebook/js/textcell.js
+++ b/IPython/html/static/notebook/js/textcell.js
@@ -20,7 +20,7 @@ var IPython = (function (IPython) {
"use strict";
// TextCell base class
- var key = IPython.utils.keycodes;
+ var keycodes = IPython.keyboard.keycodes;
/**
* Construct a new TextCell, codemirror mode is by default 'htmlmixed', and cell type is 'text'
@@ -140,7 +140,7 @@ var IPython = (function (IPython) {
if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey || event.altKey)) {
// Always ignore shift-enter in CodeMirror as we handle it.
return true;
- } else if (event.which === key.UPARROW && event.type === 'keydown') {
+ } else if (event.which === keycodes.up && event.type === 'keydown') {
// If we are not at the top, let CM handle the up arrow and
// prevent the global keydown handler from handling it.
if (!that.at_top()) {
@@ -148,8 +148,8 @@ var IPython = (function (IPython) {
return false;
} else {
return true;
- }
- } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
+ };
+ } else if (event.which === keycodes.down && event.type === 'keydown') {
// If we are not at the bottom, let CM handle the down arrow and
// prevent the global keydown handler from handling it.
if (!that.at_bottom()) {
@@ -157,8 +157,8 @@ var IPython = (function (IPython) {
return false;
} else {
return true;
- }
- } else if (event.which === key.ESC && event.type === 'keydown') {
+ };
+ } else if (event.which === keycodes.esc && event.type === 'keydown') {
if (that.code_mirror.options.keyMap === "vim-insert") {
// vim keyMap is active and in insert mode. In this case we leave vim
// insert mode, but remain in notebook edit mode.
diff --git a/IPython/html/templates/notebook.html b/IPython/html/templates/notebook.html
index b5e1361..104d4e3 100644
--- a/IPython/html/templates/notebook.html
+++ b/IPython/html/templates/notebook.html
@@ -317,6 +317,7 @@ class="notebook_app"
+
diff --git a/IPython/html/tests/base/keyboard.js b/IPython/html/tests/base/keyboard.js
new file mode 100644
index 0000000..1fc1edc
--- /dev/null
+++ b/IPython/html/tests/base/keyboard.js
@@ -0,0 +1,49 @@
+
+
+var normalized_shortcuts = [
+ 'ctrl+shift+m',
+ 'alt+meta+p',
+];
+
+var to_normalize = [
+ ['shift+%', 'shift+5'],
+ ['ShiFT+MeTa+CtRl+AlT+m', 'alt+ctrl+meta+shift+m'],
+];
+
+var unshifted = "` 1 2 3 4 5 6 7 8 9 0 - = q w e r t y u i o p [ ] \\ a s d f g h j k l ; ' z x c v b n m , . /";
+// shifted = '~ ! @ # $ % ^ & * ( ) _ + Q W E R T Y U I O P { } | A S D F G H J K L : " Z X C V B N M < > ?';
+
+casper.notebook_test(function () {
+
+ this.then(function () {
+ this.each(unshifted.split(' '), function (self, item) {
+ var result = this.evaluate(function (sc) {
+ var e = IPython.keyboard.shortcut_to_event(sc);
+ var sc2 = IPython.keyboard.event_to_shortcut(e);
+ return sc2;
+ }, item);
+ this.test.assertEquals(result, item, 'Shortcut to event roundtrip: '+item);
+ });
+ });
+
+ this.then(function () {
+ this.each(to_normalize, function (self, item) {
+ var result = this.evaluate(function (pair) {
+ return IPython.keyboard.normalize_shortcut(pair[0]);
+ }, item);
+ this.test.assertEquals(result, item[1], 'Normalize shortcut: '+item[0]);
+ });
+ })
+
+ this.then(function () {
+ this.each(normalized_shortcuts, function (self, item) {
+ var result = this.evaluate(function (sc) {
+ var e = IPython.keyboard.shortcut_to_event(sc);
+ var sc2 = IPython.keyboard.event_to_shortcut(e);
+ return sc2;
+ }, item);
+ this.test.assertEquals(result, item, 'Shortcut to event roundtrip: '+item);
+ });
+ });
+
+});
\ No newline at end of file
diff --git a/IPython/html/tests/notebook/arrow_keys.js b/IPython/html/tests/notebook/arrow_keys.js
index 0fa9dff..6f089ba 100644
--- a/IPython/html/tests/notebook/arrow_keys.js
+++ b/IPython/html/tests/notebook/arrow_keys.js
@@ -5,14 +5,14 @@ casper.notebook_test(function () {
var result = this.evaluate(function() {
IPython.notebook.command_mode();
pos0 = IPython.notebook.get_selected_index();
- IPython.utils.press(IPython.keycodes.b)
+ IPython.keyboard.trigger_keydown('b');
pos1 = IPython.notebook.get_selected_index();
- IPython.utils.press(IPython.keycodes.b)
+ IPython.keyboard.trigger_keydown('b');
pos2 = IPython.notebook.get_selected_index();
// Simulate the "up arrow" and "down arrow" keys.
- IPython.utils.press_up();
+ IPython.keyboard.trigger_keydown('up');
pos3 = IPython.notebook.get_selected_index();
- IPython.utils.press_down();
+ IPython.keyboard.trigger_keydown('down');
pos4 = IPython.notebook.get_selected_index();
return pos0 == 0 &&
pos1 == 1 &&
diff --git a/IPython/html/tests/notebook/empty_arrow_keys.js b/IPython/html/tests/notebook/empty_arrow_keys.js
index ca26557..6abed3a 100644
--- a/IPython/html/tests/notebook/empty_arrow_keys.js
+++ b/IPython/html/tests/notebook/empty_arrow_keys.js
@@ -13,8 +13,8 @@ casper.notebook_test(function () {
// Simulate the "up arrow" and "down arrow" keys.
//
- IPython.utils.press_up();
- IPython.utils.press_down();
+ IPython.keyboard.trigger_keydown('up');
+ IPython.keyboard.trigger_keydown('down');
return true;
});
this.test.assertTrue(result, 'Up/down arrow okay in empty notebook.');
diff --git a/IPython/html/tests/notebook/execute_code.js b/IPython/html/tests/notebook/execute_code.js
index 14db357..fc399e4 100644
--- a/IPython/html/tests/notebook/execute_code.js
+++ b/IPython/html/tests/notebook/execute_code.js
@@ -22,7 +22,7 @@ casper.notebook_test(function () {
var cell = IPython.notebook.get_cell(0);
cell.set_text('a=11; print(a)');
cell.clear_output();
- IPython.utils.press_shift_enter();
+ IPython.keyboard.trigger_keydown('shift+enter');
});
this.wait_for_output(0);
@@ -41,7 +41,7 @@ casper.notebook_test(function () {
var cell = IPython.notebook.get_cell(0);
cell.set_text('a=12; print(a)');
cell.clear_output();
- IPython.utils.press_ctrl_enter();
+ IPython.keyboard.trigger_keydown('ctrl+enter');
});
this.wait_for_output(0);
diff --git a/IPython/html/tests/notebook/interrupt.js b/IPython/html/tests/notebook/interrupt.js
index 5e4b07c..2bb87f8 100644
--- a/IPython/html/tests/notebook/interrupt.js
+++ b/IPython/html/tests/notebook/interrupt.js
@@ -32,7 +32,7 @@ casper.notebook_test(function () {
// interrupt using Ctrl-M I keyboard shortcut
this.thenEvaluate( function() {
- IPython.utils.press_ghetto(IPython.utils.keycodes.I)
+ IPython.keyboard.trigger_keydown('i');
});
this.wait_for_output(0);
diff --git a/IPython/html/tests/notebook/merge_cells.js b/IPython/html/tests/notebook/merge_cells.js
index 1ae939c..2d85614 100644
--- a/IPython/html/tests/notebook/merge_cells.js
+++ b/IPython/html/tests/notebook/merge_cells.js
@@ -9,7 +9,7 @@ casper.notebook_test(function() {
var cell_one = IPython.notebook.get_selected_cell();
cell_one.set_text('a = 5');
- IPython.utils.press(IPython.keycodes.b)
+ IPython.keyboard.trigger_keydown('b');
var cell_two = IPython.notebook.get_selected_cell();
cell_two.set_text('print(a)');
};