293 lines
6.4 KiB
JavaScript
293 lines
6.4 KiB
JavaScript
const modifiers = /^(CommandOrControl|CmdOrCtrl|Command|Cmd|Control|Ctrl|AltGr|Option|Alt|Shift|Super)/i;
|
|
const keyCodes = /^(Plus|Space|Tab|Backspace|Delete|Insert|Return|Enter|Up|Down|Left|Right|Home|End|PageUp|PageDown|Escape|Esc|VolumeUp|VolumeDown|VolumeMute|MediaNextTrack|MediaPreviousTrack|MediaStop|MediaPlayPause|PrintScreen|F24|F23|F22|F21|F20|F19|F18|F17|F16|F15|F14|F13|F12|F11|F10|F9|F8|F7|F6|F5|F4|F3|F2|F1|[0-9A-Z)!@#$%^&*(:+<_>?~{|}";=,\-./`[\\\]'])/i;
|
|
const UNSUPPORTED = {};
|
|
|
|
function _command(accelerator, event, modifier) {
|
|
if (process.platform !== 'darwin') {
|
|
return UNSUPPORTED;
|
|
}
|
|
|
|
if (event.metaKey) {
|
|
throw new Error('Double `Command` modifier specified.');
|
|
}
|
|
|
|
return {
|
|
event: Object.assign({}, event, {metaKey: true}),
|
|
accelerator: accelerator.slice(modifier.length)
|
|
};
|
|
}
|
|
|
|
function _super(accelerator, event, modifier) {
|
|
if (event.metaKey) {
|
|
throw new Error('Double `Super` modifier specified.');
|
|
}
|
|
|
|
return {
|
|
event: Object.assign({}, event, {metaKey: true}),
|
|
accelerator: accelerator.slice(modifier.length)
|
|
};
|
|
}
|
|
|
|
function _commandorcontrol(accelerator, event, modifier) {
|
|
if (process.platform === 'darwin') {
|
|
if (event.metaKey) {
|
|
throw new Error('Double `Command` modifier specified.');
|
|
}
|
|
|
|
return {
|
|
event: Object.assign({}, event, {metaKey: true}),
|
|
accelerator: accelerator.slice(modifier.length)
|
|
};
|
|
}
|
|
|
|
if (event.ctrlKey) {
|
|
throw new Error('Double `Control` modifier specified.');
|
|
}
|
|
|
|
return {
|
|
event: Object.assign({}, event, {ctrlKey: true}),
|
|
accelerator: accelerator.slice(modifier.length)
|
|
};
|
|
}
|
|
|
|
function _alt(accelerator, event, modifier) {
|
|
if (modifier === 'option' && process.platform !== 'darwin') {
|
|
return UNSUPPORTED;
|
|
}
|
|
|
|
if (event.altKey) {
|
|
throw new Error('Double `Alt` modifier specified.');
|
|
}
|
|
|
|
return {
|
|
event: Object.assign({}, event, {altKey: true}),
|
|
accelerator: accelerator.slice(modifier.length)
|
|
};
|
|
}
|
|
|
|
function _shift(accelerator, event, modifier) {
|
|
if (event.shiftKey) {
|
|
throw new Error('Double `Shift` modifier specified.');
|
|
}
|
|
|
|
return {
|
|
event: Object.assign({}, event, {shiftKey: true}),
|
|
accelerator: accelerator.slice(modifier.length)
|
|
};
|
|
}
|
|
|
|
function _control(accelerator, event, modifier) {
|
|
if (event.ctrlKey) {
|
|
throw new Error('Double `Control` modifier specified.');
|
|
}
|
|
|
|
return {
|
|
event: Object.assign({}, event, {ctrlKey: true}),
|
|
accelerator: accelerator.slice(modifier.length)
|
|
};
|
|
}
|
|
|
|
function reduceModifier({accelerator, event}, modifier) {
|
|
switch (modifier) {
|
|
case 'command':
|
|
case 'cmd': {
|
|
return _command(accelerator, event, modifier);
|
|
}
|
|
|
|
case 'super': {
|
|
return _super(accelerator, event, modifier);
|
|
}
|
|
|
|
case 'control':
|
|
case 'ctrl': {
|
|
return _control(accelerator, event, modifier);
|
|
}
|
|
|
|
case 'commandorcontrol':
|
|
case 'cmdorctrl': {
|
|
return _commandorcontrol(accelerator, event, modifier);
|
|
}
|
|
|
|
case 'option':
|
|
case 'altgr':
|
|
case 'alt': {
|
|
return _alt(accelerator, event, modifier);
|
|
}
|
|
|
|
case 'shift': {
|
|
return _shift(accelerator, event, modifier);
|
|
}
|
|
|
|
default:
|
|
console.error(modifier);
|
|
}
|
|
}
|
|
|
|
function reducePlus({accelerator, event}) {
|
|
return {
|
|
event,
|
|
accelerator: accelerator.trim().slice(1)
|
|
};
|
|
}
|
|
|
|
const virtualKeyCodes = {
|
|
0: 'Digit0',
|
|
1: 'Digit1',
|
|
2: 'Digit2',
|
|
3: 'Digit3',
|
|
4: 'Digit4',
|
|
5: 'Digit5',
|
|
6: 'Digit6',
|
|
7: 'Digit7',
|
|
8: 'Digit8',
|
|
9: 'Digit9',
|
|
'-': 'Minus',
|
|
'=': 'Equal',
|
|
Q: 'KeyQ',
|
|
W: 'KeyW',
|
|
E: 'KeyE',
|
|
R: 'KeyR',
|
|
T: 'KeyT',
|
|
Y: 'KeyY',
|
|
U: 'KeyU',
|
|
I: 'KeyI',
|
|
O: 'KeyO',
|
|
P: 'KeyP',
|
|
'[': 'BracketLeft',
|
|
']': 'BracketRight',
|
|
A: 'KeyA',
|
|
S: 'KeyS',
|
|
D: 'KeyD',
|
|
F: 'KeyF',
|
|
G: 'KeyG',
|
|
H: 'KeyH',
|
|
J: 'KeyJ',
|
|
K: 'KeyK',
|
|
L: 'KeyL',
|
|
';': 'Semicolon',
|
|
'\'': 'Quote',
|
|
'`': 'Backquote',
|
|
'/': 'Backslash',
|
|
Z: 'KeyZ',
|
|
X: 'KeyX',
|
|
C: 'KeyC',
|
|
V: 'KeyV',
|
|
B: 'KeyB',
|
|
N: 'KeyN',
|
|
M: 'KeyM',
|
|
',': 'Comma',
|
|
'.': 'Period',
|
|
'\\': 'Slash',
|
|
' ': 'Space'
|
|
};
|
|
|
|
function reduceKey({accelerator, event}, key) {
|
|
if (key.length > 1 || event.key) {
|
|
throw new Error(`Unvalid keycode \`${key}\`.`);
|
|
}
|
|
|
|
const code =
|
|
key.toUpperCase() in virtualKeyCodes ?
|
|
virtualKeyCodes[key.toUpperCase()] :
|
|
null;
|
|
|
|
return {
|
|
event: Object.assign({}, event, {key}, code ? {code} : null),
|
|
accelerator: accelerator.trim().slice(key.length)
|
|
};
|
|
}
|
|
|
|
const domKeys = Object.assign(Object.create(null), {
|
|
plus: 'Add',
|
|
space: 'Space',
|
|
tab: 'Tab',
|
|
backspace: 'Backspace',
|
|
delete: 'Delete',
|
|
insert: 'Insert',
|
|
return: 'Return',
|
|
enter: 'Return',
|
|
up: 'ArrowUp',
|
|
down: 'ArrowDown',
|
|
left: 'ArrowLeft',
|
|
right: 'ArrowRight',
|
|
home: 'Home',
|
|
end: 'End',
|
|
pageup: 'PageUp',
|
|
pagedown: 'PageDown',
|
|
escape: 'Escape',
|
|
esc: 'Escape',
|
|
volumeup: 'AudioVolumeUp',
|
|
volumedown: 'AudioVolumeDown',
|
|
volumemute: 'AudioVolumeMute',
|
|
medianexttrack: 'MediaTrackNext',
|
|
mediaprevioustrack: 'MediaTrackPrevious',
|
|
mediastop: 'MediaStop',
|
|
mediaplaypause: 'MediaPlayPause',
|
|
printscreen: 'PrintScreen'
|
|
});
|
|
|
|
// Add function keys
|
|
for (let i = 1; i <= 24; i++) {
|
|
domKeys[`f${i}`] = `F${i}`;
|
|
}
|
|
|
|
function reduceCode({accelerator, event}, {code, key}) {
|
|
if (event.code) {
|
|
throw new Error(`Duplicated keycode \`${key}\`.`);
|
|
}
|
|
|
|
return {
|
|
event: Object.assign({}, event, {key}, code ? {code} : null),
|
|
accelerator: accelerator.trim().slice((key && key.length) || 0)
|
|
};
|
|
}
|
|
|
|
/**
|
|
* This function transform an Electron Accelerator string into
|
|
* a DOM KeyboardEvent object.
|
|
*
|
|
* @param {string} accelerator an Electron Accelerator string, e.g. `Ctrl+C` or `Shift+Space`.
|
|
* @return {object} a DOM KeyboardEvent object derivate from the `accelerator` argument.
|
|
*/
|
|
function toKeyEvent(accelerator) {
|
|
let state = {accelerator, event: {}};
|
|
while (state.accelerator !== '') {
|
|
const modifierMatch = state.accelerator.match(modifiers);
|
|
if (modifierMatch) {
|
|
const modifier = modifierMatch[0].toLowerCase();
|
|
state = reduceModifier(state, modifier);
|
|
if (state === UNSUPPORTED) {
|
|
return {unsupportedKeyForPlatform: true};
|
|
}
|
|
} else if (state.accelerator.trim()[0] === '+') {
|
|
state = reducePlus(state);
|
|
} else {
|
|
const codeMatch = state.accelerator.match(keyCodes);
|
|
if (codeMatch) {
|
|
const code = codeMatch[0].toLowerCase();
|
|
if (code in domKeys) {
|
|
state = reduceCode(state, {
|
|
code: domKeys[code],
|
|
key: code
|
|
});
|
|
} else {
|
|
state = reduceKey(state, code);
|
|
}
|
|
} else {
|
|
throw new Error(`Unvalid accelerator: "${state.accelerator}"`);
|
|
}
|
|
}
|
|
}
|
|
|
|
return state.event;
|
|
}
|
|
|
|
module.exports = {
|
|
UNSUPPORTED,
|
|
reduceModifier,
|
|
reducePlus,
|
|
reduceKey,
|
|
reduceCode,
|
|
toKeyEvent
|
|
};
|