UNPKG

js-draw

Version:

Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript.

176 lines (175 loc) 7.18 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const isUppercaseLetter = (text) => { return text.toUpperCase() === text && text.toLowerCase() !== text && text.length === 1; }; const isLowercaseLetter = (text) => { return text.toLowerCase() === text && text.toUpperCase() !== text && text.length === 1; }; /** Represents a key combination that can trigger a keyboard shortcut. */ class KeyBinding { constructor(trigger) { this.key = trigger.key; this.shiftKey = trigger.shiftKey; this.ctrlKey = trigger.ctrlKey; this.altKey = trigger.altKey; this.metaKey = trigger.metaKey; this.controlOrMeta = trigger.controlOrMeta; } /** Returns true if and only if `keyEvent` should trigger this shortcut. */ matchesEvent(keyEvent) { const lowercaseKey = keyEvent.key?.toLowerCase(); // Determine whether the input is an upper case letter or not. const isUpperCaseKey = isUppercaseLetter(keyEvent.key ?? ''); const isLowercaseKey = isLowercaseLetter(keyEvent.key ?? ''); const ctrlKey = (keyEvent.ctrlKey ?? false) || lowercaseKey === 'control'; const altKey = (keyEvent.altKey ?? false) || lowercaseKey === 'alt'; const metaKey = (keyEvent.metaKey ?? false) || lowercaseKey === 'meta'; const shiftKey = (keyEvent.shiftKey ?? isUpperCaseKey) || lowercaseKey === 'shift'; const keyEventHasCtrlOrMeta = keyEvent.controlOrMeta || keyEvent.ctrlKey || keyEvent.metaKey || false; // If we're not working with key codes, if (this.key !== keyEvent.code) { // Different keys entirely? They don't match. if (this.key.toLowerCase() !== lowercaseKey) { return false; } // If a case where the ASCII case of the given key might matter, // compare. if ((isUpperCaseKey || isLowercaseKey) && this.key !== keyEvent.key) { // this.shiftKey may be interpreted as allowing this shortcut to be uppercased. // If so, try making this.key uppercase and matching the shortcut. const uppercaseKeyMatches = this.shiftKey === true && this.key.toUpperCase() === keyEvent.key; if (!uppercaseKeyMatches) { return false; } } } const shortcutControlOrMeta = this.controlOrMeta; // Match ctrl/meta if the shortcut doesn't have controlOrMeta specified // (controlOrMeta should match either). const ctrlAndMetaMatches = ctrlKey === this.ctrlKey && metaKey === this.metaKey && !shortcutControlOrMeta; const matches = (ctrlAndMetaMatches || (shortcutControlOrMeta && keyEventHasCtrlOrMeta)) && altKey === this.altKey && (shiftKey === this.shiftKey || this.shiftKey === undefined); return matches; } /** * Returns a string representation of this shortcut in the same format accepted by * {@link fromString}. */ toString() { const result = []; if (this.ctrlKey && this.key !== 'control') { result.push('Ctrl'); } if (this.controlOrMeta) { result.push('CtrlOrMeta'); } if (this.altKey && this.key !== 'alt') { result.push('Alt'); } if (this.metaKey && this.key !== 'meta') { result.push('Meta'); } if (this.shiftKey && this.key !== 'shift') { result.push('Shift'); } result.push(this.key); return result.join('+'); } /** * Accepts a string in the form `modifier1+modifier2+...+key` (e.g. `Ctrl+Shift+a`) * and returns the corresponding `KeyboardShortcut`. */ static fromString(shortcutStr) { const getDefaultModifiers = (key) => { // Unless a letter, as long as the given key matches, it shouldn't matter whether // the shift key is pressed. let shiftKey = undefined; if (isUppercaseLetter(key)) { shiftKey = true; } else if (isLowercaseLetter(key)) { shiftKey = false; } // If not just a single character (e.g. a key code like KeyA), shift must // be specified manually. else if (key.length > 1) { shiftKey = false; } const lowercaseKey = key.toLowerCase(); // shiftKey should always be true if the key is 'shift' if (lowercaseKey === 'shift') { shiftKey = true; } return { shiftKey, ctrlKey: lowercaseKey === 'control' || lowercaseKey === 'ctrl', altKey: lowercaseKey === 'alt', metaKey: lowercaseKey === 'meta', controlOrMeta: lowercaseKey === 'control or meta' || lowercaseKey === 'ctrlormeta', }; }; const hasNoModifiers = shortcutStr.search(/[-+]/) === -1 || shortcutStr.length === 1; if (hasNoModifiers) { const modifiers = getDefaultModifiers(shortcutStr); return new KeyBinding({ key: shortcutStr, ...modifiers, }); } const keyModifiersExp = /^(.*[-+])?(.+)$/g; const match = keyModifiersExp.exec(shortcutStr); if (!match) { throw new Error(`Invalid shortcut expression, ${shortcutStr}!`); } const key = match[2]; const defaultModifiers = getDefaultModifiers(key); const modifierStrings = (match[1] ?? '').split(/[-+]/); let shiftKey = defaultModifiers.shiftKey; let ctrlKey = defaultModifiers.ctrlKey; let altKey = defaultModifiers.altKey; let metaKey = defaultModifiers.metaKey; let controlOrMeta = defaultModifiers.controlOrMeta; for (const modifier of modifierStrings) { if (modifier === '') { continue; } switch (modifier.toLowerCase()) { case 'shift': shiftKey = true; break; case 'anyshift': shiftKey = undefined; break; case 'ctrl': case 'control': ctrlKey = true; break; case 'meta': metaKey = true; break; case 'ctrlormeta': case 'ctrl or meta': case 'controlormeta': controlOrMeta = true; break; case 'alt': altKey = true; break; default: throw new Error(`Unknown modifier: "${modifier}" in shortcut ${shortcutStr}.`); } } const shortcut = new KeyBinding({ key, shiftKey, ctrlKey, altKey, metaKey, controlOrMeta, }); return shortcut; } } exports.default = KeyBinding;