/**
* @provides phabricator-keyboard-shortcut-manager
* @requires javelin-install
* javelin-util
* javelin-stratcom
* javelin-dom
* javelin-vector
* @javelin
*/
JX.install('KeyboardShortcutManager', {
construct : function() {
this._shortcuts = [];
JX.Stratcom.listen('keypress', null, JX.bind(this, this._onkeypress));
JX.Stratcom.listen('keydown', null, JX.bind(this, this._onkeydown));
JX.Stratcom.listen('keyup', null, JX.bind(this, this._onkeyup));
var onelement = JX.bind(this, this._onelement);
JX.Stratcom.listen('click', 'has-key-command', onelement);
},
statics : {
_instance : null,
/**
* Some keys don't invoke keypress events in some browsers. We handle these
* on keydown instead of keypress.
*/
_downkeys : {
left: 1,
right: 1,
up: 1,
down: 1
},
/**
* Some keys require Alt to be pressed in order to type them on certain
* keyboard layouts.
*/
_altkeys: {
// "Alt+L" on German layouts.
'@': 1,
// "Alt+Shift+7" on German layouts.
'\\': 1
},
getInstance : function() {
if (!JX.KeyboardShortcutManager._instance) {
JX.KeyboardShortcutManager._instance = new JX.KeyboardShortcutManager();
}
return JX.KeyboardShortcutManager._instance;
}
},
members : {
_shortcuts : null,
/**
* Instead of calling this directly, you should call
* KeyboardShortcut.register().
*/
addKeyboardShortcut : function(s) {
this._shortcuts.push(s);
},
getShortcutDescriptions : function() {
var desc = [];
for (var ii = 0; ii < this._shortcuts.length; ii++) {
var shortcut = this._shortcuts[ii];
desc.push({
keys : shortcut.getKeys(),
group: shortcut.getGroup(),
description : shortcut.getDescription()
});
}
return desc;
},
/**
* Scroll an element into view.
*/
scrollTo : function(node) {
var scroll_distance = JX.Vector.getAggregateScrollForNode(node);
var node_position = JX.$V(node);
JX.DOM.scrollToPosition(0, node_position.y + scroll_distance.y - 60);
},
_onkeypress : function(e) {
if (!(this._getKey(e) in JX.KeyboardShortcutManager._downkeys)) {
this._onkeyhit(e);
}
},
_onkeyhit : function(e) {
var self = JX.KeyboardShortcutManager;
var raw = e.getRawEvent();
if (raw.ctrlKey || raw.metaKey) {
// Never activate keyboard shortcuts if modifier keys are also
// depressed.
return;
}
// For most keystrokes, don't activate keyboard shortcuts if the Alt
// key is depressed. However, we continue if the character requires the
// use of Alt to type it on some keyboard layouts.
var key = this._getKey(e);
if (raw.altKey && !(key in self._altkeys)) {
return;
}
var target = e.getTarget();
var ignore = ['input', 'select', 'textarea', 'object', 'embed'];
if (JX.DOM.isType(target, ignore)) {
// Never activate keyboard shortcuts if the user has some other control
// focused.
return;
}
var key = this._getKey(e);
var handled = this._handleKey(key);
if (handled) {
e.kill();
}
},
_handleKey: function(key) {
var shortcuts = this._shortcuts;
for (var ii = 0; ii < shortcuts.length; ii++) {
var keys = shortcuts[ii].getKeys();
for (var jj = 0; jj < keys.length; jj++) {
if (keys[jj] == key) {
shortcuts[ii].getHandler()(this);
return true;
}
}
}
return false;
},
_onkeydown : function(e) {
this._handleTooltipKeyEvent(e, true);
if (this._getKey(e) in JX.KeyboardShortcutManager._downkeys) {
this._onkeyhit(e);
}
},
_onkeyup : function(e) {
this._handleTooltipKeyEvent(e, false);
},
_getKey : function(e) {
return e.getSpecialKey() || String.fromCharCode(e.getRawEvent().charCode);
},
_onelement: function(e) {
var data = e.getNodeData('has-key-command');
this._handleKey(data.keyCommand);
e.kill();
},
_handleTooltipKeyEvent : function(e, is_keydown) {
if (e.getRawEvent().keyCode != 18) {
// If this isn't the alt/option key, don't do anything.
return;
}
// Fire all the shortcut handlers.
var shortcuts = this._shortcuts;
for (var ii = 0; ii < shortcuts.length; ii++) {
var handler = shortcuts[ii].getTooltipHandler();
handler && handler(this, is_keydown);
}
}
}
});