import * as I from 'immutable'; import {compose} from '@avocado/core'; import {EventEmitter} from '@avocado/mixins'; const decorate = compose( EventEmitter, ); export class ActionRegistry extends decorate(class {}) { static normalizeKey(key) { return key.toLowerCase(); } constructor() { super(); // Track events. this.target = undefined; this.mapActionToKey = new Map(); this.mapKeyToAction = {}; // Track actions. this._state = I.Map(); // Handle lame OS input event behavior. See: https://mzl.la/2Ob0WQE this.keysDown = {}; this.keyUpDelays = {}; // Bind event handlers. this.onBlur = this.onBlur.bind(this); this.onKeyDown = this.onKeyDown.bind(this); this.onKeyUp = this.onKeyUp.bind(this); } actionForKey(key) { return this.mapKeyToAction[key]; } listen(target = window.document) { // Only listen once. if (this.target) { return; } this.target = target; this.target.addEventListener('blur', this.onBlur); this.target.addEventListener('keydown', this.onKeyDown); this.target.addEventListener('keyup', this.onKeyUp); } keyForAction(action) { return this.mapActionToKey.get(action); } mapKeysToActions(map) { for (const key in map) { const action = map[key]; this.mapKeyToAction[key] = action; this.mapActionToKey.set(action, key); } } onBlur(event) { event = event || window.event; this.setAllKeysUp(); } onKeyDown(event) { event = event || window.event; const key = this.constructor.normalizeKey(event.key); if (this.keysDown[key]) { if (this.keyUpDelays[key]) { clearTimeout(this.keyUpDelays[key]); delete this.keyUpDelays[key]; } return; } this.keysDown[key] = true; if (this.mapKeyToAction[key]) { const action = this.mapKeyToAction[key]; this._state = this._state.set(action, true); } } onKeyUp(event) { event = event || window.event; const key = this.constructor.normalizeKey(event.key); this.keyUpDelays[key] = setTimeout(() => { delete this.keyUpDelays[key]; delete this.keysDown[key]; if (this.mapKeyToAction[key]) { const action = this.mapKeyToAction[key]; this._state = this._state.delete(action); } }, 20); } setAllKeysUp() { this.keysDown = {}; for (const key in this.keyUpDelays) { const handle = this.keyUpDelays[key]; clearTimeout(handle); } this.keyUpDelays = {}; this._state = I.Map(); } get state() { return this._state; } set state(state) { this._state = state; } stopListening() { this.setAllKeysUp(); if (!this.target) { return; } this.target.removeEventListener('blur', this.onBlur); this.target.removeEventListener('keydown', this.onKeyDown); this.target.removeEventListener('keyup', this.onKeyUp); this.target = undefined; } } export {InputPacket} from './packet/input.packet';