import {compose, EventEmitter} from '@avocado/core'; const decorate = compose( EventEmitter, ); class PointerEvent { constructor(event) { this.nativeEvent = event; if (window.PointerEvent && event instanceof window.PointerEvent) { this.position = [ event.clientX, event.clientY, ]; } else if (window.TouchEvent && event instanceof window.TouchEvent) { const touches = event.changedTouches; const touch = touches[0]; this.position = [ touch.clientX, touch.clientY, ]; } } } export class InputNormalizer extends decorate(class{}) { constructor() { super(); // Track events. this.target = undefined; this.targetForKeyUp = undefined; // 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); this.onPointerDown = this.onPointerDown.bind(this); this.onPointerMove = this.onPointerMove.bind(this); this.onPointerUp = this.onPointerUp.bind(this); this.onTouchStart = this.onTouchStart.bind(this); this.onTouchMove = this.onTouchMove.bind(this); this.onTouchEnd = this.onTouchEnd.bind(this); this.onWheel = this.onWheel.bind(this); } actionForKey(key) { return this.mapKeyToAction[key]; } destroy() { this.stopListening(); } listen(target = window.document, targetForKeyUp = window.document) { // Only listen once. if (this.target) { return; } this.target = target; this.targetForKeyUp = targetForKeyUp; this.target.addEventListener('blur', this.onBlur); this.target.addEventListener('keydown', this.onKeyDown); this.targetForKeyUp.addEventListener('keyup', this.onKeyUp); // Listen for pointer events if they exist. if ('undefined' !== typeof window.PointerEvent) { this.target.addEventListener('pointerdown', this.onPointerDown); this.target.addEventListener('pointermove', this.onPointerMove); window.addEventListener('pointerup', this.onPointerUp); } // Otherwise, use touch events. (lol Apple) else { this.target.addEventListener('touchstart', this.onTouchStart); this.target.addEventListener('touchmove', this.onTouchMove); window.addEventListener('touchend', this.onTouchEnd); } this.target.addEventListener('wheel', this.onWheel); } onBlur(event) { this.setAllKeysUp(); } onKeyDown(event) { const {key} = event; if (this.keysDown[key]) { if (this.keyUpDelays[key]) { clearTimeout(this.keyUpDelays[key]); delete this.keyUpDelays[key]; } return; } this.keysDown[key] = true; this.emit('keyDown', key); } onKeyUp(event) { const {key} = event; this.keyUpDelays[key] = setTimeout(() => { delete this.keyUpDelays[key]; delete this.keysDown[key]; this.emit('keyUp', key); }, 20); } onPointerDown(event) { this.emit('pointerDown', new PointerEvent(event)); } onPointerMove(event) { this.emit('pointerMove', new PointerEvent(event)); } onPointerUp(event) { this.emit('pointerUp', new PointerEvent(event)); } onTouchStart(event) { this.emit('pointerDown', new PointerEvent(event)); } onTouchMove(event) { this.emit('pointerMove', new PointerEvent(event)); } onTouchEnd(event) { this.emit('pointerUp', new PointerEvent(event)); } onWheel(event) { this.emit('wheel', event); } setAllKeysUp() { this.keysDown = {}; for (const key in this.keyUpDelays) { const handle = this.keyUpDelays[key]; clearTimeout(handle); this.emit('keyUp', key); } this.keyUpDelays = {}; } stopListening() { this.setAllKeysUp(); if (!this.target) { return; } this.target.removeEventListener('blur', this.onBlur); this.target.removeEventListener('keydown', this.onKeyDown); this.targetForKeyUp.removeEventListener('keyup', this.onKeyUp); // Listen for pointer events if they exist. if ('undefined' !== typeof window.PointerEvent) { this.target.removeEventListener('pointerdown', this.onPointerDown); this.target.removeEventListener('pointermove', this.onPointerMove); window.removeEventListener('pointerup', this.onPointerUp); } // Otherwise, use touch events. (lol Apple) else { this.target.removeEventListener('touchstart', this.onTouchStart); this.target.removeEventListener('touchmove', this.onTouchMove); window.removeEventListener('touchend', this.onTouchEnd); } this.target.removeEventListener('wheel', this.onWheel); this.target = undefined; this.targetForKeyUp = undefined; } }