@theia/core
Version:
Theia is a cloud & desktop IDE framework implemented in TypeScript.
694 lines (628 loc) • 29.2 kB
text/typescript
// *****************************************************************************
// Copyright (C) 2017-2019 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************
import { isOSX } from './os';
import { isObject } from './types';
export type KeySequence = KeyCode[];
export namespace KeySequence {
export function equals(a: KeySequence, b: KeySequence): boolean {
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (!a[i].equals(b[i])) {
return false;
}
}
return true;
}
export enum CompareResult {
NONE = 0,
PARTIAL,
SHADOW,
FULL
}
/* Compares two KeySequences, returns:
* FULL if the KeySequences are the same.
* PARTIAL if the KeySequence a part of b.
* SHADOW if the KeySequence b part of a.
* NONE if the KeySequences are not the same at all.
*/
export function compare(a: KeySequence, b: KeySequence): CompareResult {
let first = a;
let second = b;
let shadow = false;
if (b.length < a.length) {
first = b;
second = a;
shadow = true;
}
for (let i = 0; i < first.length; i++) {
if (first[i].equals(second[i]) === false) {
return KeySequence.CompareResult.NONE;
}
}
if (first.length < second.length) {
if (shadow === false) {
return KeySequence.CompareResult.PARTIAL;
} else {
return KeySequence.CompareResult.SHADOW;
}
}
return KeySequence.CompareResult.FULL;
}
export function parse(keybinding: string): KeySequence {
const keyCodes = [];
const rawKeyCodes = keybinding.trim().split(/\s+/g);
for (const rawKeyCode of rawKeyCodes) {
const keyCode = KeyCode.parse(rawKeyCode);
if (keyCode !== undefined) {
keyCodes.push(keyCode);
}
}
return keyCodes;
}
}
/**
* The key sequence for this binding. This key sequence should consist of one or more key strokes. Key strokes
* consist of one or more keys held down at the same time. This should be zero or more modifier keys, and zero or one other key.
* Since `M2+M3+<Key>` (Alt+Shift+<Key>) is reserved on MacOS X for writing special characters, such bindings are commonly
* undefined for platform MacOS X and redefined as `M1+M3+<Key>`. The rule applies on the `M3+M2+<Key>` sequence.
*/
export interface Keystroke {
readonly first?: Key;
readonly modifiers?: KeyModifier[];
}
export interface KeyCodeSchema {
key?: Partial<Key>;
ctrl?: boolean;
shift?: boolean;
alt?: boolean;
meta?: boolean;
character?: string;
}
/**
* Representation of a pressed key combined with key modifiers.
*/
export class KeyCode {
public readonly key: Key | undefined;
public readonly ctrl: boolean;
public readonly shift: boolean;
public readonly alt: boolean;
public readonly meta: boolean;
public readonly character: string | undefined;
public constructor(schema: KeyCodeSchema) {
const key = schema.key;
if (key) {
if (key.code && key.keyCode && key.easyString) {
this.key = key as Key;
} else if (key.code) {
this.key = Key.getKey(key.code);
} else if (key.keyCode) {
this.key = Key.getKey(key.keyCode);
}
}
this.ctrl = !!schema.ctrl;
this.shift = !!schema.shift;
this.alt = !!schema.alt;
this.meta = !!schema.meta;
this.character = schema.character;
}
/**
* Return true if this KeyCode only contains modifiers.
*/
public isModifierOnly(): boolean {
return this.key === undefined;
}
/**
* Return true if the given KeyCode is equal to this one.
*/
equals(other: KeyCode): boolean {
if (this.key && (!other.key || this.key.code !== other.key.code) || !this.key && other.key) {
return false;
}
return this.ctrl === other.ctrl && this.alt === other.alt && this.shift === other.shift && this.meta === other.meta;
}
/*
* Return a keybinding string compatible with the `Keybinding.keybinding` property.
*/
toString(): string {
const result = [];
if (this.meta) {
result.push(SpecialCases.META);
}
if (this.shift) {
result.push(Key.SHIFT_LEFT.easyString);
}
if (this.alt) {
result.push(Key.ALT_LEFT.easyString);
}
if (this.ctrl) {
result.push(Key.CONTROL_LEFT.easyString);
}
if (this.key) {
result.push(this.key.easyString);
}
return result.join('+');
}
/**
* Create a KeyCode from one of several input types.
*/
public static createKeyCode(input: KeyboardEvent | Keystroke | KeyCodeSchema | string, eventDispatch: 'code' | 'keyCode' = 'code'): KeyCode {
if (typeof input === 'string') {
const parts = input.split('+');
if (!KeyCode.isModifierString(parts[0])) {
return KeyCode.createKeyCode({
first: Key.getKey(parts[0]),
modifiers: parts.slice(1) as KeyModifier[]
});
}
return KeyCode.createKeyCode({ modifiers: parts as KeyModifier[] });
} else if (KeyCode.isKeyboardEvent(input)) {
const key = KeyCode.toKey(input, eventDispatch);
return new KeyCode({
key: Key.isModifier(key.code) ? undefined : key,
meta: isOSX && input.metaKey,
shift: input.shiftKey,
alt: input.altKey,
ctrl: input.ctrlKey,
character: KeyCode.toCharacter(input)
});
} else if ((input as Keystroke).first || (input as Keystroke).modifiers) {
const keystroke = input as Keystroke;
const schema: KeyCodeSchema = {
key: keystroke.first
};
if (keystroke.modifiers) {
if (isOSX) {
schema.meta = keystroke.modifiers.some(mod => mod === KeyModifier.CtrlCmd);
schema.ctrl = keystroke.modifiers.some(mod => mod === KeyModifier.MacCtrl);
} else {
schema.meta = false;
schema.ctrl = keystroke.modifiers.some(mod => mod === KeyModifier.CtrlCmd);
}
schema.shift = keystroke.modifiers.some(mod => mod === KeyModifier.Shift);
schema.alt = keystroke.modifiers.some(mod => mod === KeyModifier.Alt);
}
return new KeyCode(schema);
} else {
return new KeyCode(input as KeyCodeSchema);
}
}
private static keybindings: { [key: string]: KeyCode } = {};
/* Reset the key hashmap, this is for testing purposes. */
public static resetKeyBindings(): void {
KeyCode.keybindings = {};
}
/**
* Parses a string and returns a KeyCode object.
* @param keybinding String representation of a keybinding
*/
public static parse(keybinding: string): KeyCode {
if (KeyCode.keybindings[keybinding]) {
return KeyCode.keybindings[keybinding];
}
const schema: KeyCodeSchema = {};
const keys = [];
let currentKey = '';
for (const character of keybinding.trim().toLowerCase()) {
if (currentKey && (character === '-' || character === '+')) {
keys.push(currentKey);
currentKey = '';
} else if (character !== '+') {
currentKey += character;
}
}
if (currentKey) {
keys.push(currentKey);
}
/* If duplicates i.e ctrl+ctrl+a or alt+alt+b or b+alt+b it is invalid */
if (keys.length !== new Set(keys).size) {
throw new Error(`Can't parse keybinding ${keybinding} Duplicate modifiers`);
}
for (let keyString of keys) {
if (SPECIAL_ALIASES[keyString] !== undefined) {
keyString = SPECIAL_ALIASES[keyString];
}
const key = EASY_TO_KEY[keyString];
/* meta only works on macOS */
if (keyString === SpecialCases.META) {
if (isOSX) {
schema.meta = true;
} else {
throw new Error(`Can't parse keybinding ${keybinding} meta is for OSX only`);
}
/* ctrlcmd for M1 keybindings that work on both macOS and other platforms */
} else if (keyString === SpecialCases.CTRLCMD) {
if (isOSX) {
schema.meta = true;
} else {
schema.ctrl = true;
}
} else if (Key.isKey(key)) {
if (Key.isModifier(key.code)) {
if (key.code === Key.CONTROL_LEFT.code || key.code === Key.CONTROL_RIGHT.code) {
schema.ctrl = true;
} else if (key.code === Key.SHIFT_LEFT.code || key.code === Key.SHIFT_RIGHT.code) {
schema.shift = true;
} else if (key.code === Key.ALT_LEFT.code || key.code === Key.ALT_RIGHT.code) {
schema.alt = true;
}
} else {
schema.key = key;
}
} else {
throw new Error(`Unrecognized key '${keyString}' in '${keybinding}'`);
}
}
KeyCode.keybindings[keybinding] = new KeyCode(schema);
return KeyCode.keybindings[keybinding];
}
}
export namespace KeyCode {
/**
* Determines a `true` of `false` value for the key code argument.
*/
export type Predicate = (keyCode: KeyCode) => boolean;
/*
* Return true if the string is a modifier M1 to M4.
*/
export function isModifierString(key: string): boolean {
return key === KeyModifier.CtrlCmd
|| key === KeyModifier.Shift
|| key === KeyModifier.Alt
|| key === KeyModifier.MacCtrl;
}
/**
* Different scopes have different execution environments. This means that they have different built-ins
* (different global object, different constructors, etc.). This may result in unexpected results. For instance,
* `[] instanceof window.frames[0].Array` will return `false`, because `Array.prototype !== window.frames[0].Array`
* and arrays inherit from the former.
* See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof
*
* Note: just add another check if the current `event.type` checking is insufficient.
*/
export function isKeyboardEvent(event: object & { readonly type?: string }): event is KeyboardEvent {
if (typeof KeyboardEvent === 'undefined') { // This can happen in tests
return false;
}
if (event instanceof KeyboardEvent) {
return true;
}
const { type } = event;
if (type) {
return type === 'keypress' || type === 'keydown' || type === 'keyup';
}
return false;
}
/**
* Determine the pressed key of a keyboard event. This key should correspond to the physical key according
* to a standard US keyboard layout. International keyboard layouts are handled by `KeyboardLayoutService`.
*
* `keyIdentifier` is used to access this deprecated field:
* https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyIdentifier
*/
export function toKey(event: KeyboardEvent, dispatch: 'code' | 'keyCode' = 'code'): Key {
const code = event.code;
if (code && dispatch === 'code') {
if (isOSX) {
// https://github.com/eclipse-theia/theia/issues/4986
const char = event.key;
if (code === 'IntlBackslash' && (char === '`' || char === '~')) {
return Key.BACKQUOTE;
} else if (code === 'Backquote' && (char === '§' || char === '±')) {
return Key.INTL_BACKSLASH;
}
}
// https://github.com/eclipse-theia/theia/issues/7315
if (code.startsWith('Numpad') && event.key && event.key.length > 1) {
const k = Key.getKey(event.key);
if (k) {
return k;
}
}
const key = Key.getKey(code);
if (key) {
return key;
}
}
// tslint:disable-next-line: deprecation
const keyCode = event.keyCode;
if (keyCode) {
const key = Key.getKey(keyCode);
if (key) {
return key;
}
}
const keyIdentifier = (event as KeyboardEvent & { keyIdentifier?: string }).keyIdentifier;
if (keyIdentifier) {
const key = Key.getKey(keyIdentifier);
if (key) {
return key;
}
}
throw new Error(`Cannot get key code from the keyboard event: ${event}.`);
}
/**
* Determine the actual printable character that is generated from a pressed key.
* If the key does not correspond to a printable character, `undefined` is returned.
* The result may be altered by modifier keys.
*/
export function toCharacter(event: KeyboardEvent): string | undefined {
const key = event.key;
// Use the key property if it contains exactly one unicode character
if (key && Array.from(key).length === 1) {
return key;
}
const charCode = event.charCode;
// Use the charCode property if it does not correspond to a unicode control character
if (charCode && charCode > 0x1f && !(charCode >= 0x80 && charCode <= 0x9f)) {
return String.fromCharCode(charCode);
}
return undefined;
}
}
export enum KeyModifier {
/**
* M1 is the COMMAND key on MacOS X, and the CTRL key on most other platforms.
*/
CtrlCmd = 'M1',
/**
* M2 is the SHIFT key.
*/
Shift = 'M2',
/**
* M3 is the Option key on MacOS X, and the ALT key on most other platforms.
*/
Alt = 'M3',
/**
* M4 is the CTRL key on MacOS X, and is undefined on other platforms.
*/
MacCtrl = 'M4'
}
export namespace KeyModifier {
/**
* The CTRL key, independently of the platform.
* _Note:_ In general `KeyModifier.CtrlCmd` should be preferred over this constant.
*/
export const CTRL: KeyModifier.MacCtrl | KeyModifier.CtrlCmd = isOSX ? KeyModifier.MacCtrl : KeyModifier.CtrlCmd;
/**
* An alias for the SHIFT key (`KeyModifier.Shift`).
*/
export const SHIFT: KeyModifier.Shift = KeyModifier.Shift;
/**
* `true` if the argument represents a modifier. Otherwise, `false`.
*/
export function isModifier(key: string | undefined): boolean {
if (key) {
switch (key) {
case 'M1': // Fall through.
case 'M2': // Fall through.
case 'M3': // Fall through.
case 'M4': return true;
default: return false;
}
}
return false;
}
}
export interface Key {
readonly code: string;
readonly keyCode: number;
readonly easyString: string;
}
const CODE_TO_KEY: { [code: string]: Key } = {};
const KEY_CODE_TO_KEY: { [keyCode: number]: Key } = {};
const EASY_TO_KEY: { [code: string]: Key } = {}; // From 'ctrl' to Key structure
const MODIFIERS: Key[] = [];
const SPECIAL_ALIASES: { [index: string]: string } = {
'option': 'alt',
'command': 'meta',
'cmd': 'meta',
'return': 'enter',
'esc': 'escape',
'mod': 'ctrl',
'ins': 'insert',
'del': 'delete',
'control': 'ctrl',
};
export namespace SpecialCases {
export const META = 'meta';
export const CTRLCMD = 'ctrlcmd';
}
export namespace Key {
export function isKey(arg: unknown): arg is Key {
return isObject(arg) && 'code' in arg && 'keyCode' in arg;
}
export function getKey(arg: string | number): Key | undefined {
if (typeof arg === 'number') {
return KEY_CODE_TO_KEY[arg];
} else {
return CODE_TO_KEY[arg];
}
}
export function isModifier(arg: string | number): boolean {
if (typeof arg === 'number') {
return MODIFIERS.find(key => key.keyCode === arg) !== undefined;
}
return MODIFIERS.find(key => key.code === arg) !== undefined;
}
export function equals(key: Key, keyCode: KeyCode): boolean {
return !!keyCode.key && key.keyCode === keyCode.key.keyCode;
}
export const BACKSPACE: Key = { code: 'Backspace', keyCode: 8, easyString: 'backspace' };
export const TAB: Key = { code: 'Tab', keyCode: 9, easyString: 'tab' };
export const ENTER: Key = { code: 'Enter', keyCode: 13, easyString: 'enter' };
export const ESCAPE: Key = { code: 'Escape', keyCode: 27, easyString: 'escape' };
export const SPACE: Key = { code: 'Space', keyCode: 32, easyString: 'space' };
export const PAGE_UP: Key = { code: 'PageUp', keyCode: 33, easyString: 'pageup' };
export const PAGE_DOWN: Key = { code: 'PageDown', keyCode: 34, easyString: 'pagedown' };
export const END: Key = { code: 'End', keyCode: 35, easyString: 'end' };
export const HOME: Key = { code: 'Home', keyCode: 36, easyString: 'home' };
export const ARROW_LEFT: Key = { code: 'ArrowLeft', keyCode: 37, easyString: 'left' };
export const ARROW_UP: Key = { code: 'ArrowUp', keyCode: 38, easyString: 'up' };
export const ARROW_RIGHT: Key = { code: 'ArrowRight', keyCode: 39, easyString: 'right' };
export const ARROW_DOWN: Key = { code: 'ArrowDown', keyCode: 40, easyString: 'down' };
export const INSERT: Key = { code: 'Insert', keyCode: 45, easyString: 'insert' };
export const DELETE: Key = { code: 'Delete', keyCode: 46, easyString: 'delete' };
export const SHIFT_LEFT: Key = { code: 'ShiftLeft', keyCode: 16, easyString: 'shift' };
export const SHIFT_RIGHT: Key = { code: 'ShiftRight', keyCode: 16, easyString: 'shift' };
export const CONTROL_LEFT: Key = { code: 'ControlLeft', keyCode: 17, easyString: 'ctrl' };
export const CONTROL_RIGHT: Key = { code: 'ControlRight', keyCode: 17, easyString: 'ctrl' };
export const ALT_LEFT: Key = { code: 'AltLeft', keyCode: 18, easyString: 'alt' };
export const ALT_RIGHT: Key = { code: 'AltRight', keyCode: 18, easyString: 'alt' };
export const CAPS_LOCK: Key = { code: 'CapsLock', keyCode: 20, easyString: 'capslock' };
export const OS_LEFT: Key = { code: 'OSLeft', keyCode: 91, easyString: 'super' };
export const OS_RIGHT: Key = { code: 'OSRight', keyCode: 92, easyString: 'super' };
export const DIGIT0: Key = { code: 'Digit0', keyCode: 48, easyString: '0' };
export const DIGIT1: Key = { code: 'Digit1', keyCode: 49, easyString: '1' };
export const DIGIT2: Key = { code: 'Digit2', keyCode: 50, easyString: '2' };
export const DIGIT3: Key = { code: 'Digit3', keyCode: 51, easyString: '3' };
export const DIGIT4: Key = { code: 'Digit4', keyCode: 52, easyString: '4' };
export const DIGIT5: Key = { code: 'Digit5', keyCode: 53, easyString: '5' };
export const DIGIT6: Key = { code: 'Digit6', keyCode: 54, easyString: '6' };
export const DIGIT7: Key = { code: 'Digit7', keyCode: 55, easyString: '7' };
export const DIGIT8: Key = { code: 'Digit8', keyCode: 56, easyString: '8' };
export const DIGIT9: Key = { code: 'Digit9', keyCode: 57, easyString: '9' };
export const KEY_A: Key = { code: 'KeyA', keyCode: 65, easyString: 'a' };
export const KEY_B: Key = { code: 'KeyB', keyCode: 66, easyString: 'b' };
export const KEY_C: Key = { code: 'KeyC', keyCode: 67, easyString: 'c' };
export const KEY_D: Key = { code: 'KeyD', keyCode: 68, easyString: 'd' };
export const KEY_E: Key = { code: 'KeyE', keyCode: 69, easyString: 'e' };
export const KEY_F: Key = { code: 'KeyF', keyCode: 70, easyString: 'f' };
export const KEY_G: Key = { code: 'KeyG', keyCode: 71, easyString: 'g' };
export const KEY_H: Key = { code: 'KeyH', keyCode: 72, easyString: 'h' };
export const KEY_I: Key = { code: 'KeyI', keyCode: 73, easyString: 'i' };
export const KEY_J: Key = { code: 'KeyJ', keyCode: 74, easyString: 'j' };
export const KEY_K: Key = { code: 'KeyK', keyCode: 75, easyString: 'k' };
export const KEY_L: Key = { code: 'KeyL', keyCode: 76, easyString: 'l' };
export const KEY_M: Key = { code: 'KeyM', keyCode: 77, easyString: 'm' };
export const KEY_N: Key = { code: 'KeyN', keyCode: 78, easyString: 'n' };
export const KEY_O: Key = { code: 'KeyO', keyCode: 79, easyString: 'o' };
export const KEY_P: Key = { code: 'KeyP', keyCode: 80, easyString: 'p' };
export const KEY_Q: Key = { code: 'KeyQ', keyCode: 81, easyString: 'q' };
export const KEY_R: Key = { code: 'KeyR', keyCode: 82, easyString: 'r' };
export const KEY_S: Key = { code: 'KeyS', keyCode: 83, easyString: 's' };
export const KEY_T: Key = { code: 'KeyT', keyCode: 84, easyString: 't' };
export const KEY_U: Key = { code: 'KeyU', keyCode: 85, easyString: 'u' };
export const KEY_V: Key = { code: 'KeyV', keyCode: 86, easyString: 'v' };
export const KEY_W: Key = { code: 'KeyW', keyCode: 87, easyString: 'w' };
export const KEY_X: Key = { code: 'KeyX', keyCode: 88, easyString: 'x' };
export const KEY_Y: Key = { code: 'KeyY', keyCode: 89, easyString: 'y' };
export const KEY_Z: Key = { code: 'KeyZ', keyCode: 90, easyString: 'z' };
export const MULTIPLY: Key = { code: 'NumpadMultiply', keyCode: 106, easyString: 'multiply' };
export const ADD: Key = { code: 'NumpadAdd', keyCode: 107, easyString: 'add' };
export const DECIMAL: Key = { code: 'NumpadDecimal', keyCode: 108, easyString: 'decimal' };
export const SUBTRACT: Key = { code: 'NumpadSubtract', keyCode: 109, easyString: 'subtract' };
export const DIVIDE: Key = { code: 'NumpadDivide', keyCode: 111, easyString: 'divide' };
export const F1: Key = { code: 'F1', keyCode: 112, easyString: 'f1' };
export const F2: Key = { code: 'F2', keyCode: 113, easyString: 'f2' };
export const F3: Key = { code: 'F3', keyCode: 114, easyString: 'f3' };
export const F4: Key = { code: 'F4', keyCode: 115, easyString: 'f4' };
export const F5: Key = { code: 'F5', keyCode: 116, easyString: 'f5' };
export const F6: Key = { code: 'F6', keyCode: 117, easyString: 'f6' };
export const F7: Key = { code: 'F7', keyCode: 118, easyString: 'f7' };
export const F8: Key = { code: 'F8', keyCode: 119, easyString: 'f8' };
export const F9: Key = { code: 'F9', keyCode: 120, easyString: 'f9' };
export const F10: Key = { code: 'F10', keyCode: 121, easyString: 'f10' };
export const F11: Key = { code: 'F11', keyCode: 122, easyString: 'f11' };
export const F12: Key = { code: 'F12', keyCode: 123, easyString: 'f12' };
export const F13: Key = { code: 'F13', keyCode: 124, easyString: 'f13' };
export const F14: Key = { code: 'F14', keyCode: 125, easyString: 'f14' };
export const F15: Key = { code: 'F15', keyCode: 126, easyString: 'f15' };
export const F16: Key = { code: 'F16', keyCode: 127, easyString: 'f16' };
export const F17: Key = { code: 'F17', keyCode: 128, easyString: 'f17' };
export const F18: Key = { code: 'F18', keyCode: 129, easyString: 'f18' };
export const F19: Key = { code: 'F19', keyCode: 130, easyString: 'f19' };
export const F20: Key = { code: 'F20', keyCode: 131, easyString: 'f20' };
export const F21: Key = { code: 'F21', keyCode: 132, easyString: 'f21' };
export const F22: Key = { code: 'F22', keyCode: 133, easyString: 'f22' };
export const F23: Key = { code: 'F23', keyCode: 134, easyString: 'f23' };
export const F24: Key = { code: 'F24', keyCode: 135, easyString: 'f24' };
export const NUM_LOCK: Key = { code: 'NumLock', keyCode: 144, easyString: 'numlock' };
export const SEMICOLON: Key = { code: 'Semicolon', keyCode: 186, easyString: ';' };
export const EQUAL: Key = { code: 'Equal', keyCode: 187, easyString: '=' };
export const COMMA: Key = { code: 'Comma', keyCode: 188, easyString: ',' };
export const MINUS: Key = { code: 'Minus', keyCode: 189, easyString: '-' };
export const PERIOD: Key = { code: 'Period', keyCode: 190, easyString: '.' };
export const SLASH: Key = { code: 'Slash', keyCode: 191, easyString: '/' };
export const BACKQUOTE: Key = { code: 'Backquote', keyCode: 192, easyString: '`' };
export const INTL_RO: Key = { code: 'IntlRo', keyCode: 193, easyString: 'intlro' };
export const BRACKET_LEFT: Key = { code: 'BracketLeft', keyCode: 219, easyString: '[' };
export const BACKSLASH: Key = { code: 'Backslash', keyCode: 220, easyString: '\\' };
export const BRACKET_RIGHT: Key = { code: 'BracketRight', keyCode: 221, easyString: ']' };
export const QUOTE: Key = { code: 'Quote', keyCode: 222, easyString: '\'' };
export const INTL_BACKSLASH: Key = { code: 'IntlBackslash', keyCode: 229, easyString: 'intlbackslash' };
export const INTL_YEN: Key = { code: 'IntlYen', keyCode: 255, easyString: 'intlyen' };
export const MAX_KEY_CODE = INTL_YEN.keyCode;
}
/* -------------------- Initialize the static key mappings -------------------- */
(() => {
// Set the default key mappings from the constants in the Key namespace
Object.keys(Key).map(prop => Reflect.get(Key, prop)).filter(key => Key.isKey(key)).forEach(key => {
CODE_TO_KEY[key.code] = key;
KEY_CODE_TO_KEY[key.keyCode] = key;
EASY_TO_KEY[key.easyString] = key;
});
// Set additional key mappings
CODE_TO_KEY['Numpad0'] = Key.DIGIT0;
KEY_CODE_TO_KEY[96] = Key.DIGIT0;
CODE_TO_KEY['Numpad1'] = Key.DIGIT1;
KEY_CODE_TO_KEY[97] = Key.DIGIT1;
CODE_TO_KEY['Numpad2'] = Key.DIGIT2;
KEY_CODE_TO_KEY[98] = Key.DIGIT2;
CODE_TO_KEY['Numpad3'] = Key.DIGIT3;
KEY_CODE_TO_KEY[99] = Key.DIGIT3;
CODE_TO_KEY['Numpad4'] = Key.DIGIT4;
KEY_CODE_TO_KEY[100] = Key.DIGIT4;
CODE_TO_KEY['Numpad5'] = Key.DIGIT5;
KEY_CODE_TO_KEY[101] = Key.DIGIT5;
CODE_TO_KEY['Numpad6'] = Key.DIGIT6;
KEY_CODE_TO_KEY[102] = Key.DIGIT6;
CODE_TO_KEY['Numpad7'] = Key.DIGIT7;
KEY_CODE_TO_KEY[103] = Key.DIGIT7;
CODE_TO_KEY['Numpad8'] = Key.DIGIT8;
KEY_CODE_TO_KEY[104] = Key.DIGIT8;
CODE_TO_KEY['Numpad9'] = Key.DIGIT9;
KEY_CODE_TO_KEY[105] = Key.DIGIT9;
CODE_TO_KEY['NumpadEnter'] = Key.ENTER;
CODE_TO_KEY['NumpadEqual'] = Key.EQUAL;
CODE_TO_KEY['MetaLeft'] = Key.OS_LEFT; // Chrome, Safari
KEY_CODE_TO_KEY[224] = Key.OS_LEFT; // Firefox on Mac
CODE_TO_KEY['MetaRight'] = Key.OS_RIGHT; // Chrome, Safari
KEY_CODE_TO_KEY[93] = Key.OS_RIGHT; // Chrome, Safari, Edge
KEY_CODE_TO_KEY[225] = Key.ALT_RIGHT; // Linux
KEY_CODE_TO_KEY[110] = Key.DECIMAL; // Mac, Windows
KEY_CODE_TO_KEY[59] = Key.SEMICOLON; // Firefox
KEY_CODE_TO_KEY[61] = Key.EQUAL; // Firefox
KEY_CODE_TO_KEY[173] = Key.MINUS; // Firefox
KEY_CODE_TO_KEY[226] = Key.BACKSLASH; // Chrome, Edge on Windows
KEY_CODE_TO_KEY[60] = Key.BACKSLASH; // Firefox on Linux
// Set the modifier keys
MODIFIERS.push(...[Key.ALT_LEFT, Key.ALT_RIGHT, Key.CONTROL_LEFT, Key.CONTROL_RIGHT, Key.OS_LEFT, Key.OS_RIGHT, Key.SHIFT_LEFT, Key.SHIFT_RIGHT]);
})();
export type KeysOrKeyCodes = Key | KeyCode | (Key | KeyCode)[];
export namespace KeysOrKeyCodes {
export const toKeyCode = (keyOrKeyCode: Key | KeyCode) =>
keyOrKeyCode instanceof KeyCode ? keyOrKeyCode : KeyCode.createKeyCode({ first: keyOrKeyCode });
export const toKeyCodes = (keysOrKeyCodes: KeysOrKeyCodes) => {
if (keysOrKeyCodes instanceof KeyCode) {
return [keysOrKeyCodes];
} else if (Array.isArray(keysOrKeyCodes)) {
return keysOrKeyCodes.slice().map(toKeyCode);
}
return [toKeyCode(keysOrKeyCodes)];
};
}