UNPKG

plotboilerplate

Version:

A simple javascript plotting boilerplate for 2d stuff.

430 lines 12 kB
/** * @author Ikaros Kappler * @date 2018-11-11 (Alaaf) * @modified 2020-03-28 Ported this class from vanilla-JS to Typescript. * @modified 2020-07-28 Changed the `delete` key code from 8 to 46. * @modified 2020-10-04 Changed `window` to `globalThis`. * @modified 2020-10-04 Added extended JSDoc. * @modified 2022-02-02 Added the `destroy` method. * @version 1.1.0 * * @file KeyHandler * @public **/ /** * @classdesc A generic key handler. * * Example * ======= * @example * // Javascript * new KeyHandler( { trackAll : true } ) * .down('enter',function() { console.log('ENTER was hit.'); } ) * .press('enter',function() { console.log('ENTER was pressed.'); } ) * .up('enter',function() { console.log('ENTER was released.'); } ) * * .down('e',function() { console.log('e was hit. shift is pressed?',keyHandler.isDown('shift')); } ) * * .up('windows',function() { console.log('windows was released.'); } ) * ; */ export class KeyHandler { /** * The constructor. * * @constructor * @instance * @memberof KeyHandler * @param {HTMLElement} options.element (optional) The HTML element to listen on; if null then 'window' will be used. * @param {boolean} options.trackAll (optional) Set to true if you want to keep track of _all_ keys (keyStatus). **/ constructor(options) { this.downListeners = []; this.pressListeners = []; this.upListeners = []; this.keyStates = {}; options = options || {}; this.element = options.element ? options.element : globalThis; this.downListeners = []; this.pressListeners = []; this.upListeners = []; this.keyStates = []; // This could be made configurable in a later version. It allows to // keep track of the key status no matter if there are any listeners // on the key or not. this.trackAllKeys = options.trackAll || false; // Install the listeners this.installListeners(); } /** * A helper function to fire key events from this KeyHandler. * * @param {KeyboardEvent} event - The key event to fire. * @param {Array<XKeyListener>} listener - The listeners to fire to. */ fireEvent(event, listeners) { let hasListener = false; for (var i in listeners) { var lis = listeners[i]; if (lis.keyCode != event.keyCode) continue; lis.listener(event); hasListener = true; } return hasListener; } /** * Internal function to fire a new keydown event to all listeners. * You should not call this function on your own unless you know what you do. * * @name fireDownEvent * @memberof KeyHandler * @instance * @private * @param {KeyboardEvent} e * @param {KeyHandler} handler * @return {void} */ fireDownEvent(e, handler) { if (handler.fireEvent(e, handler.downListeners) || handler.trackAllKeys) { // Down event has listeners. Update key state. handler.keyStates[e.keyCode] = "down"; } } /** * Internal function to fire a new keypress event to all listeners. * You should not call this function on your own unless you know what you do. * * @name firePressEvent * @memberof KeyHandler * @instance * @private * @param {KeyboardEvent} e * @param {KeyHandler} handler * @return void */ firePressEvent(e, handler) { handler.fireEvent(e, handler.pressListeners); } /** * Internal function to fire a new keyup event to all listeners. * You should not call this function on your own unless you know what you do. * * @name fireUpEvent * @memberof KeyHandler * @instance * @private * @param {KeyboardEvent} e * @param {KeyHandler} handler * @return {void} */ fireUpEvent(e, handler) { if (handler.fireEvent(e, handler.upListeners) || handler.trackAllKeys) { // Up event has listeners. Clear key state. delete handler.keyStates[e.keyCode]; } } /** * Resolve the key/name code. */ static key2code(key) { if (typeof key == "number") return key; if (typeof key != "string") throw "Unknown key name or key type (should be a string or integer): " + key; if (KeyHandler.KEY_CODES[key]) return KeyHandler.KEY_CODES[key]; throw "Unknown key (cannot resolve key code): " + key; } /** * Install the required listeners into the initially passed element. * * By default the listeners are installed into the root element specified on * construction (or 'window'). */ installListeners() { var _self = this; this.element.addEventListener("keydown", (this._keyDownListener = (e) => { _self.fireDownEvent(e, _self); })); this.element.addEventListener("keypress", (this._keyPressListener = (e) => { _self.firePressEvent(e, _self); })); this.element.addEventListener("keyup", (this._keyUpListener = (e) => { _self.fireUpEvent(e, _self); })); } /** * Remove all installed event listeners from the underlying element. */ releaseListeners() { this.element.removeEventListener("keydown", this._keyDownListener); this.element.removeEventListener("keypress", this._keyPressListener); this.element.removeEventListener("keyup", this._keyUpListener); } /** * Listen for key down. This function allows chaining. * * Example: new KeyHandler().down('enter',function() {console.log('Enter hit.')}); * * @name down * @memberof KeyHandler * @instance * @param {string|number} key - Any key identifier, key code or one from the KEY_CODES list. * @param {(e:KeyboardEvent)=>void} e - The callback to be triggered. * @return {KeyHandler} this */ down(key, listener) { this.downListeners.push({ key: key, keyCode: KeyHandler.key2code(key), listener: listener }); return this; } /** * Listen for key press. * * Example: new KeyHandler().press('enter',function() {console.log('Enter pressed.')}); * * @name press * @memberof KeyHandler * @instance * @param {string|number} key - Any key identifier, key code or one from the KEY_CODES list. * @param {(e:KeyboardEvent)=>void} listener - The callback to be triggered. * @return {KeyHandler} this */ press(key, listener) { this.pressListeners.push({ key: key, keyCode: KeyHandler.key2code(key), listener: listener }); return this; } /** * Listen for key up. * * Example: new KeyHandler().up('enter',function() {console.log('Enter released.')}); * * @name up * @memberof KeyHandler * @instance * @param {string} key - Any key identifier, key code or one from the KEY_CODES list. * @param {(e:KeyboardEvent)=>void)} e - The callback to be triggered. * @return {KeyHandler} this */ up(key, listener) { this.upListeners.push({ key: key, keyCode: KeyHandler.key2code(key), listener: listener }); return this; } /** * Check if a specific key is currently held pressed. * * @param {string|number} key - Any key identifier, key code or one from the KEY_CODES list. */ isDown(key) { if (typeof key == "number") return this.keyStates[key] ? true : false; else return this.keyStates[KeyHandler.key2code(key)] ? true : false; } /** * This function should invalidate any installed listeners and invalidate this object. * After calling this function the object might not hold valid data any more and * should not be used any more. */ destroy() { this.releaseListeners(); } } /** * Source: * https://keycode.info/ */ // prettier-ignore KeyHandler.KEY_CODES = { 'break': 3, // alternate: 19 'backspace': 8, // 'delete' : 8, // alternate: 46 'tab': 9, 'clear': 12, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18, 'pause': 19, // 'break' : 19, 'capslock': 20, 'hangul': 21, 'hanja': 25, 'escape': 27, 'conversion': 28, 'non-conversion': 29, // alternate: 235? 'spacebar': 32, 'pageup': 33, 'pagedown': 34, 'end': 35, 'home': 36, // alternate: 172? 'leftarrow': 37, 'uparrow': 38, 'rightarrow': 39, 'downarrow': 40, 'select': 41, 'print': 42, 'execute': 43, 'printscreen': 44, 'insert': 45, 'delete': 46, // alternate: 8 'help': 47, '0': 48, '1': 49, '2': 50, '3': 51, '4': 52, '5': 53, '6': 54, '7': 55, '8': 56, '9': 57, ':': 58, 'semicolon (firefox)': 59, 'equals': 59, '<': 60, 'equals (firefox)': 61, 'ß': 63, '@ (firefox)': 64, 'a': 65, 'b': 66, 'c': 67, 'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73, 'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81, 'r': 82, 's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, 'z': 90, 'windows': 91, 'leftcommand': 91, // left ⌘ 'chromebooksearch': 91, 'rightwindowkey': 92, 'windowsmenu': 93, 'rightcommant': 93, // right ⌘ 'sleep': 95, 'numpad0': 96, 'numpad1': 97, 'numpad2': 98, 'numpad3': 99, 'numpad4': 100, 'numpad5': 101, 'numpad6': 102, 'numpad7': 103, 'numpad8': 104, 'numpad9': 105, 'multiply': 106, 'add': 107, 'numpadperiod': 108, // firefox, 194 on chrome 'subtract': 109, 'decimalpoint': 110, 'divide': 111, 'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118, 'f8': 119, 'f9': 120, 'f10': 121, 'f11': 122, 'f12': 123, 'f13': 124, 'f14': 125, 'f15': 126, 'f16': 127, 'f17': 128, 'f18': 129, 'f19': 130, 'f20': 131, 'f21': 132, 'f22': 133, 'f23': 134, 'f24': 135, 'numlock': 144, 'scrolllock': 145, '^': 160, '!': 161, // '؛' : 162 // (arabic semicolon) '#': 163, '$': 164, 'ù': 165, 'pagebackward': 166, 'pageforward': 167, 'refresh': 168, 'closingparen': 169, // (AZERTY) '*': 170, '~+*': 171, // 'home' : 172, 'minus': 173, // firefox // 'mute' : 173, // 'unmute' : 173, 'decreasevolumelevel': 174, 'increasevolumelevel': 175, 'next': 176, 'previous': 177, 'stop': 178, 'play/pause': 179, 'email': 180, 'mute': 181, // firefox, alternate: 173 'unmute': 181, // alternate: 173? //'decreasevolumelevel' 182 // firefox //'increasevolumelevel' 183 // firefox 'semicolon': 186, 'ñ': 186, 'equal': 187, 'comma': 188, 'dash': 189, 'period': 190, 'forwardslash': 191, 'ç': 191, // 231 alternate? 'grave accent': 192, //'ñ' 192, 'æ': 192, 'ö': 192, '?': 193, '/': 193, '°': 193, // 'numpadperiod' : 194, // chrome 'openbracket': 219, 'backslash': 220, 'closebracket': 221, 'å': 221, 'singlequote': 222, 'ø': 222, 'ä': 222, '`': 223, // 'left or right ⌘ key (firefox)' 224 'altgr': 225, // '< /git >, left back slash' 226 'GNOME Compose Key': 230, 'XF86Forward': 233, 'XF86Back': 234, 'alphanumeric': 240, 'hiragana': 242, 'katakana': 242, 'half-width': 243, 'full-width': 243, 'kanji': 244, 'unlocktrackpad': 251, // Chrome/Edge 'toggletouchpad': 255 }; //# sourceMappingURL=KeyHandler.js.map