escher-vis
Version:
Escher: A Web Application for Building, Sharing, and Embedding Data-Rich Visualizations of Biological Pathways
166 lines (123 loc) • 4.6 kB
JavaScript
/** KeyManager
Manage key listeners and events.
Arguments
---------
assigned_keys (default: {}): An object defining keys to bind.
input_list (default: []): A list of inputs that will override keyboard
shortcuts when in focus.
selection (default: global): A node to bind the events to.
ctrl_equals_cmd (default: false): If true, then control and command have the
same effect.
*/
var utils = require('./utils');
var Mousetrap = require('mousetrap');
var _ = require('underscore');
var KeyManager = utils.make_class();
// instance methods
KeyManager.prototype = {
init: init,
update: update,
toggle: toggle,
add_escape_listener: add_escape_listener,
add_enter_listener: add_enter_listener,
add_key_listener: add_key_listener
};
module.exports = KeyManager;
// instance methods
function init(assigned_keys, input_list, selection, ctrl_equals_cmd) {
// default Arguments
this.assigned_keys = assigned_keys || {};
this.input_list = input_list || [];
this.mousetrap = selection ? new Mousetrap(selection) : new Mousetrap;
this.ctrl_equals_cmd = (!_.isBoolean(ctrl_equals_cmd)) ? false : ctrl_equals_cmd;
// Fix mousetrap behavior; by default, it ignore shortcuts when inputs are
// in focus.
// TODO NOT WORKING https://craig.is/killing/mice
// consider swithching to https://github.com/PolicyStat/combokeys
this.mousetrap.stopCallback = function() { return false; };
this.enabled = true;
this.update();
}
function _add_cmd(key, ctrl_equals_cmd) {
/** If ctrl_equals_cmd is true and key has ctrl+ in it, return an array with
ctrl+ and meta+ variations.
*/
if (!ctrl_equals_cmd) return key;
var key_ar = _.isArray(key) ? key : [key];
var new_ar = key_ar.reduce(function(c, k) {
var n = k.replace('ctrl+', 'meta+');
if (n !== k) c.push(n);
return c;
}, key_ar.slice());
return new_ar.length === key_ar.length ? key : new_ar;
}
/**
* Updated key bindings if attributes have changed.
*/
function update() {
this.mousetrap.reset();
if (!this.enabled) return;
// loop through keys
for (var key_id in this.assigned_keys) {
var assigned_key = this.assigned_keys[key_id];
// OK if this is missing
if (!assigned_key.key) continue;
var key_to_bind = _add_cmd(assigned_key.key, this.ctrl_equals_cmd);
// remember the input_list
assigned_key.input_list = this.input_list;
this.mousetrap.bind(key_to_bind, function(e) {
// check inputs
var input_blocking = false;
if (this.ignore_with_input) {
for (var i = 0, l = this.input_list.length; i < l; i++) {
if (this.input_list[i].is_visible()) {
input_blocking = true;
break;
}
}
}
if (!input_blocking) {
if (this.fn) this.fn.call(this.target);
else console.warn('No function for key: ' + this.key);
e.preventDefault();
}
}.bind(assigned_key), 'keydown');
}
}
function toggle(on_off) {
/** Turn the key manager on or off.
*/
if (_.isUndefined(on_off)) on_off = !this.enabled;
this.enabled = on_off;
this.update();
}
function add_enter_listener(callback, one_time) {
/** Call the callback when the enter key is pressed, then
unregisters the listener.
*/
return this.add_key_listener('enter', callback, one_time);
}
function add_escape_listener(callback, one_time) {
/** Call the callback when the escape key is pressed, then
unregisters the listener.
*/
return this.add_key_listener('escape', callback, one_time);
}
function add_key_listener(key_name, callback, one_time) {
/** Call the callback when the key is pressed, then unregisters the
listener. Returns a function that will unbind the event.
callback: The callback function with no arguments.
key_name: A key name, or list of key names, as defined by the mousetrap
library: https://craig.is/killing/mice
one_time: If True, then cancel the listener after the first execution.
*/
if (_.isUndefined(one_time)) one_time = false;
// unbind function ready to go
var unbind = this.mousetrap.unbind.bind(this.mousetrap, key_name);
this.mousetrap.bind(_add_cmd(key_name, this.ctrl_equals_cmd), function(e) {
e.preventDefault();
callback();
if (one_time) unbind();
});
return unbind;
}