keyboardmanager.js
578 lines
| 19.5 KiB
| application/javascript
|
JavascriptLexer
|
r17866 | // Copyright (c) IPython Development Team. | ||
// Distributed under the terms of the Modified BSD License. | ||||
|
r17864 | |||
|
r17869 | define([ | ||
'base/js/namespace', | ||||
'jquery', | ||||
'base/js/utils', | ||||
'base/js/keyboard', | ||||
], function(IPython, $, utils, keyboard) { | ||||
|
r14020 | "use strict"; | ||
|
r17871 | |||
|
r17198 | var browser = utils.browser[0]; | ||
var platform = utils.platform; | ||||
|
r14020 | |||
|
r17198 | // Main keyboard manager for the notebook | ||
var keycodes = keyboard.keycodes; | ||||
|
r14036 | |||
|
r17210 | var KeyboardManager = function (options) { | ||
|
r17211 | // Constructor | ||
// | ||||
// Parameters: | ||||
// options: dictionary | ||||
// Dictionary of keyword arguments. | ||||
// events: $(Events) instance | ||||
// pager: Pager instance | ||||
|
r17198 | this.mode = 'command'; | ||
this.enabled = true; | ||||
|
r17210 | this.pager = options.pager; | ||
|
r17198 | this.quick_help = undefined; | ||
this.notebook = undefined; | ||||
this.bind_events(); | ||||
|
r17210 | this.command_shortcuts = new keyboard.ShortcutManager(undefined, options.events); | ||
|
r17198 | this.command_shortcuts.add_shortcuts(this.get_default_common_shortcuts()); | ||
this.command_shortcuts.add_shortcuts(this.get_default_command_shortcuts()); | ||||
|
r17210 | this.edit_shortcuts = new keyboard.ShortcutManager(undefined, options.events); | ||
|
r17198 | this.edit_shortcuts.add_shortcuts(this.get_default_common_shortcuts()); | ||
this.edit_shortcuts.add_shortcuts(this.get_default_edit_shortcuts()); | ||||
}; | ||||
|
r14036 | |||
|
r17198 | KeyboardManager.prototype.get_default_common_shortcuts = function() { | ||
var that = this; | ||||
|
r17200 | var shortcuts = { | ||
|
r17871 | 'shift' : { | ||
help : '', | ||||
help_index : '', | ||||
handler : function (event) { | ||||
// ignore shift keydown | ||||
return true; | ||||
} | ||||
}, | ||||
'shift-enter' : { | ||||
help : 'run cell, select below', | ||||
help_index : 'ba', | ||||
handler : function (event) { | ||||
|
r17198 | that.notebook.execute_cell_and_select_below(); | ||
|
r17871 | return false; | ||
} | ||||
}, | ||||
'ctrl-enter' : { | ||||
help : 'run cell', | ||||
help_index : 'bb', | ||||
handler : function (event) { | ||||
|
r17198 | that.notebook.execute_cell(); | ||
|
r17871 | return false; | ||
} | ||||
}, | ||||
'alt-enter' : { | ||||
help : 'run cell, insert below', | ||||
help_index : 'bc', | ||||
handler : function (event) { | ||||
|
r17198 | that.notebook.execute_cell_and_insert_below(); | ||
|
r17871 | return false; | ||
} | ||||
|
r14036 | } | ||
|
r17871 | }; | ||
|
r17198 | |||
|
r17871 | if (platform === 'MacOS') { | ||
|
r17198 | shortcuts['cmd-s'] = | ||
|
r17871 | { | ||
help : 'save notebook', | ||||
help_index : 'fb', | ||||
handler : function (event) { | ||||
|
r17198 | that.notebook.save_checkpoint(); | ||
|
r17871 | event.preventDefault(); | ||
return false; | ||||
} | ||||
}; | ||||
} else { | ||||
|
r17198 | shortcuts['ctrl-s'] = | ||
|
r17871 | { | ||
help : 'save notebook', | ||||
help_index : 'fb', | ||||
handler : function (event) { | ||||
|
r17198 | that.notebook.save_checkpoint(); | ||
|
r17871 | event.preventDefault(); | ||
return false; | ||||
} | ||||
}; | ||||
} | ||||
|
r17198 | return shortcuts; | ||
|
r15491 | }; | ||
|
r14036 | |||
|
r17198 | KeyboardManager.prototype.get_default_edit_shortcuts = function() { | ||
var that = this; | ||||
return { | ||||
|
r17869 | 'esc' : { | ||
help : 'command mode', | ||||
help_index : 'aa', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.command_mode(); | ||
|
r17864 | return false; | ||
|
r17198 | } | ||
|
r17869 | }, | ||
'ctrl-m' : { | ||||
help : 'command mode', | ||||
help_index : 'ab', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.command_mode(); | ||
|
r15837 | return false; | ||
|
r17869 | } | ||
}, | ||||
'up' : { | ||||
help : '', | ||||
help_index : '', | ||||
handler : function (event) { | ||||
|
r17871 | var index = that.notebook.get_selected_index(); | ||
var cell = that.notebook.get_cell(index); | ||||
|
r17869 | if (cell && cell.at_top() && index !== 0) { | ||
event.preventDefault(); | ||||
|
r17871 | that.notebook.command_mode(); | ||
that.notebook.select_prev(); | ||||
that.notebook.edit_mode(); | ||||
var cm = that.notebook.get_selected_cell().code_mirror; | ||||
|
r17869 | cm.setCursor(cm.lastLine(), 0); | ||
return false; | ||||
} else if (cell) { | ||||
var cm = cell.code_mirror; | ||||
cm.execCommand('goLineUp'); | ||||
return false; | ||||
} | ||||
} | ||||
}, | ||||
'down' : { | ||||
help : '', | ||||
help_index : '', | ||||
handler : function (event) { | ||||
|
r17871 | var index = that.notebook.get_selected_index(); | ||
var cell = that.notebook.get_cell(index); | ||||
if (cell.at_bottom() && index !== (that.notebook.ncells()-1)) { | ||||
|
r17869 | event.preventDefault(); | ||
|
r17871 | that.notebook.command_mode(); | ||
that.notebook.select_next(); | ||||
that.notebook.edit_mode(); | ||||
var cm = that.notebook.get_selected_cell().code_mirror; | ||||
|
r17869 | cm.setCursor(0, 0); | ||
return false; | ||||
} else { | ||||
var cm = cell.code_mirror; | ||||
cm.execCommand('goLineDown'); | ||||
return false; | ||||
} | ||||
} | ||||
}, | ||||
'ctrl-shift--' : { | ||||
help : 'split cell', | ||||
help_index : 'ea', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.split_cell(); | ||
|
r15837 | return false; | ||
|
r15491 | } | ||
|
r17869 | }, | ||
'ctrl-shift-subtract' : { | ||||
help : '', | ||||
help_index : 'eb', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.split_cell(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
}; | ||||
|
r15491 | }; | ||
|
r14816 | |||
|
r17198 | KeyboardManager.prototype.get_default_command_shortcuts = function() { | ||
var that = this; | ||||
return { | ||||
|
r17869 | 'space': { | ||
|
r17871 | help: "Scroll down", | ||
|
r17869 | handler: function(event) { | ||
|
r17876 | return that.notebook.scroll_manager.scroll(1); | ||
|
r17869 | }, | ||
|
r17864 | }, | ||
|
r17869 | 'shift-space': { | ||
|
r17871 | help: "Scroll up", | ||
|
r17869 | handler: function(event) { | ||
|
r17876 | return that.notebook.scroll_manager.scroll(-1); | ||
|
r17869 | }, | ||
|
r17864 | }, | ||
|
r17869 | 'enter' : { | ||
help : 'edit mode', | ||||
help_index : 'aa', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.edit_mode(); | ||
|
r17869 | return false; | ||
|
r15491 | } | ||
|
r17869 | }, | ||
'up' : { | ||||
help : 'select previous cell', | ||||
help_index : 'da', | ||||
handler : function (event) { | ||||
|
r17871 | var index = that.notebook.get_selected_index(); | ||
|
r17869 | if (index !== 0 && index !== null) { | ||
|
r17871 | that.notebook.select_prev(); | ||
that.notebook.focus_cell(); | ||||
|
r17869 | } | ||
return false; | ||||
|
r15491 | } | ||
|
r17869 | }, | ||
'down' : { | ||||
help : 'select next cell', | ||||
help_index : 'db', | ||||
handler : function (event) { | ||||
|
r17871 | var index = that.notebook.get_selected_index(); | ||
if (index !== (that.notebook.ncells()-1) && index !== null) { | ||||
that.notebook.select_next(); | ||||
that.notebook.focus_cell(); | ||||
|
r17869 | } | ||
return false; | ||||
} | ||||
}, | ||||
'k' : { | ||||
help : 'select previous cell', | ||||
help_index : 'dc', | ||||
handler : function (event) { | ||||
|
r17871 | var index = that.notebook.get_selected_index(); | ||
|
r17869 | if (index !== 0 && index !== null) { | ||
|
r17871 | that.notebook.select_prev(); | ||
that.notebook.focus_cell(); | ||||
|
r17869 | } | ||
return false; | ||||
} | ||||
}, | ||||
'j' : { | ||||
help : 'select next cell', | ||||
help_index : 'dd', | ||||
handler : function (event) { | ||||
|
r17871 | var index = that.notebook.get_selected_index(); | ||
if (index !== (that.notebook.ncells()-1) && index !== null) { | ||||
that.notebook.select_next(); | ||||
that.notebook.focus_cell(); | ||||
|
r17869 | } | ||
return false; | ||||
} | ||||
}, | ||||
'x' : { | ||||
help : 'cut cell', | ||||
help_index : 'ee', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.cut_cell(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'c' : { | ||||
help : 'copy cell', | ||||
help_index : 'ef', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.copy_cell(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'shift-v' : { | ||||
help : 'paste cell above', | ||||
help_index : 'eg', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.paste_cell_above(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'v' : { | ||||
help : 'paste cell below', | ||||
help_index : 'eh', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.paste_cell_below(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'd' : { | ||||
help : 'delete cell (press twice)', | ||||
help_index : 'ej', | ||||
count: 2, | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.delete_cell(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'a' : { | ||||
help : 'insert cell above', | ||||
help_index : 'ec', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.insert_cell_above(); | ||
that.notebook.select_prev(); | ||||
that.notebook.focus_cell(); | ||||
|
r17869 | return false; | ||
|
r15491 | } | ||
|
r17869 | }, | ||
'b' : { | ||||
help : 'insert cell below', | ||||
help_index : 'ed', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.insert_cell_below(); | ||
that.notebook.select_next(); | ||||
that.notebook.focus_cell(); | ||||
|
r17869 | return false; | ||
|
r17198 | } | ||
|
r17869 | }, | ||
'y' : { | ||||
help : 'to code', | ||||
help_index : 'ca', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.to_code(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'm' : { | ||||
help : 'to markdown', | ||||
help_index : 'cb', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.to_markdown(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'r' : { | ||||
help : 'to raw', | ||||
help_index : 'cc', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.to_raw(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'1' : { | ||||
help : 'to heading 1', | ||||
help_index : 'cd', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.to_heading(undefined, 1); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'2' : { | ||||
help : 'to heading 2', | ||||
help_index : 'ce', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.to_heading(undefined, 2); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'3' : { | ||||
help : 'to heading 3', | ||||
help_index : 'cf', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.to_heading(undefined, 3); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'4' : { | ||||
help : 'to heading 4', | ||||
help_index : 'cg', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.to_heading(undefined, 4); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'5' : { | ||||
help : 'to heading 5', | ||||
help_index : 'ch', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.to_heading(undefined, 5); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'6' : { | ||||
help : 'to heading 6', | ||||
help_index : 'ci', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.to_heading(undefined, 6); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'o' : { | ||||
help : 'toggle output', | ||||
help_index : 'gb', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.toggle_output(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'shift-o' : { | ||||
help : 'toggle output scrolling', | ||||
help_index : 'gc', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.toggle_output_scroll(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
's' : { | ||||
help : 'save notebook', | ||||
help_index : 'fa', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.save_checkpoint(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'ctrl-j' : { | ||||
help : 'move cell down', | ||||
help_index : 'eb', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.move_cell_down(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'ctrl-k' : { | ||||
help : 'move cell up', | ||||
help_index : 'ea', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.move_cell_up(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'l' : { | ||||
help : 'toggle line numbers', | ||||
help_index : 'ga', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.cell_toggle_line_numbers(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'i' : { | ||||
help : 'interrupt kernel (press twice)', | ||||
help_index : 'ha', | ||||
count: 2, | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.kernel.interrupt(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'0' : { | ||||
help : 'restart kernel (press twice)', | ||||
help_index : 'hb', | ||||
count: 2, | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.restart_kernel(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'h' : { | ||||
help : 'keyboard shortcuts', | ||||
help_index : 'ge', | ||||
handler : function (event) { | ||||
|
r17871 | that.quick_help.show_keyboard_shortcuts(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'z' : { | ||||
help : 'undo last delete', | ||||
help_index : 'ei', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.undelete_cell(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'shift-m' : { | ||||
help : 'merge cell below', | ||||
help_index : 'ek', | ||||
handler : function (event) { | ||||
|
r17871 | that.notebook.merge_cell_below(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
'q' : { | ||||
help : 'close pager', | ||||
help_index : 'gd', | ||||
handler : function (event) { | ||||
|
r17871 | that.pager.collapse(); | ||
|
r17869 | return false; | ||
} | ||||
}, | ||||
}; | ||||
|
r14020 | }; | ||
KeyboardManager.prototype.bind_events = function () { | ||||
var that = this; | ||||
$(document).keydown(function (event) { | ||||
return that.handle_keydown(event); | ||||
}); | ||||
}; | ||||
KeyboardManager.prototype.handle_keydown = function (event) { | ||||
|
r17198 | var notebook = this.notebook; | ||
|
r14089 | |||
|
r15491 | if (event.which === keycodes.esc) { | ||
|
r14020 | // Intercept escape at highest level to avoid closing | ||
// websocket connection with firefox | ||||
event.preventDefault(); | ||||
} | ||||
|
r14023 | if (!this.enabled) { | ||
|
r15491 | if (event.which === keycodes.esc) { | ||
|
r14036 | // ESC | ||
notebook.command_mode(); | ||||
return false; | ||||
} | ||||
|
r14023 | return true; | ||
|
r14020 | } | ||
if (this.mode === 'edit') { | ||||
|
r14036 | return this.edit_shortcuts.call_handler(event); | ||
} else if (this.mode === 'command') { | ||||
return this.command_shortcuts.call_handler(event); | ||||
|
r14020 | } | ||
return true; | ||||
|
r15491 | }; | ||
|
r14020 | |||
KeyboardManager.prototype.edit_mode = function () { | ||||
this.last_mode = this.mode; | ||||
this.mode = 'edit'; | ||||
|
r15491 | }; | ||
|
r14020 | |||
KeyboardManager.prototype.command_mode = function () { | ||||
this.last_mode = this.mode; | ||||
this.mode = 'command'; | ||||
|
r15491 | }; | ||
|
r14020 | |||
|
r14024 | KeyboardManager.prototype.enable = function () { | ||
|
r14030 | this.enabled = true; | ||
|
r15491 | }; | ||
|
r14024 | |||
KeyboardManager.prototype.disable = function () { | ||||
|
r14030 | this.enabled = false; | ||
|
r15491 | }; | ||
|
r14024 | |||
|
r14036 | KeyboardManager.prototype.register_events = function (e) { | ||
var that = this; | ||||
|
r15496 | var handle_focus = function () { | ||
|
r14036 | that.disable(); | ||
|
r15496 | }; | ||
var handle_blur = function () { | ||||
|
r14036 | that.enable(); | ||
|
r15496 | }; | ||
e.on('focusin', handle_focus); | ||||
e.on('focusout', handle_blur); | ||||
|
r15507 | // TODO: Very strange. The focusout event does not seem fire for the | ||
|
r15539 | // bootstrap textboxes on FF25&26... This works around that by | ||
// registering focus and blur events recursively on all inputs within | ||||
// registered element. | ||||
|
r15507 | e.find('input').blur(handle_blur); | ||
e.on('DOMNodeInserted', function (event) { | ||||
var target = $(event.target); | ||||
if (target.is('input')) { | ||||
target.blur(handle_blur); | ||||
} else { | ||||
target.find('input').blur(handle_blur); | ||||
} | ||||
}); | ||||
|
r14046 | // There are times (raw_input) where we remove the element from the DOM before | ||
// focusout is called. In this case we bind to the remove event of jQueryUI, | ||||
|
r15328 | // which gets triggered upon removal, iff it is focused at the time. | ||
|
r15539 | // is_focused must be used to check for the case where an element within | ||
// the element being removed is focused. | ||||
|
r14046 | e.on('remove', function () { | ||
|
r17198 | if (utils.is_focused(e[0])) { | ||
|
r15328 | that.enable(); | ||
} | ||||
|
r14046 | }); | ||
|
r15491 | }; | ||
|
r14036 | |||
|
r17198 | // For backwards compatability. | ||
|
r14020 | IPython.KeyboardManager = KeyboardManager; | ||
|
r17201 | return {'KeyboardManager': KeyboardManager}; | ||
|
r17198 | }); | ||