Home Reference Source

src/keymatcher.js

export const ctrl = 'CTRL';
export const alt = 'ALT';
export const shift = 'SHIFT';

export default class KeyMatcher {

    constructor() {
        this._keys = [];
    }

    /**
     * @desc Sets a {String} of keys this instance of {@link KeyMatcher} should match for calls to {@link KeyMatcher.withEvent}.
     * @param {String} keyString - A {@link String} of the form "keyX+keyY+...+KeyN".
     * @returns {KeyMatcher}
     */
    match(keyString) {
        const keys = keyString.split('+');
        this._setKeysToMatch(keys);
        return this;
    }

    /**
     * @desc Matches the keys passed to {@link KeyMatcher.match} with the passed {@link KeyBoardEvent}.
     * @param {KeyBoardEvent} event - A {@link KeyBoardEvent} the keys passed to {@link KeyMatcher.match} will be matched with.
     * @returns {boolean}
     */
    withEvent(event) {
        const EVENT_TYPE_ERROR = "KeyMatcher.with(event) expects a KeyBoardEvent. This isn't one.";
        if (event instanceof KeyboardEvent) {
            return !!this._matchesKeys(event);
        }
        else {
            throw new TypeError(EVENT_TYPE_ERROR)
        }
    }

    _setKeysToMatch(keys) {
        keys.forEach((key) => {
            switch (key) {
                case ctrl:
                    this._verifyCtrl();
                    break;
                case alt:
                    this._verifyAlt();
                    break;
                case shift:
                    this._verifyShift();
                    break;
                default:
                    this._verifyKey(key);
            }
        });
    }

    _verifyCtrl() {
        this._checkCtrl = true;
    }

    _verifyAlt() {
        this._checkAlt = true;
    }

    _verifyShift() {
        this._checkShift = true;
    }

    _verifyKey(key) {
        this._keys.push(key);
    }

    /**
     * @param {KeyBoardEvent} event - A {@link KeyBoardEvent} this instance of {@link KeyMatcher} matches its keys to.
     * @returns {boolean|*}
     * @private
     */
    _matchesKeys(event) {
        let matchesAllKeys = false;
        for (let i = 0; i < this._keys.length; i++) {
            const key = this._keys[i];
            const matches = (key === event.key);
            matchesAllKeys = matches;
            if (!matches) {
                break
            }
        }
        return matchesAllKeys && this._shouldMatchFunctionKeys(event);
    }

    _shouldMatchFunctionKeys(event) {
        this._shouldMatchCtrl(event.ctrlKey) && this._shouldMatchAlt(event.altKey) && this._shouldMatchShift(event.shiftKey);
    }

    _shouldMatchShift(pressedShift) {
        return KeyMatcher._shouldMatchFunctionKey(pressedShift, this._checkShift)
    }

    _shouldMatchCtrl(pressedCtrl) {
        return KeyMatcher._shouldMatchFunctionKey(pressedCtrl, this._checkCtrl)
    }

    _shouldMatchAlt(pressedAlt) {
        return KeyMatcher._shouldMatchFunctionKey(pressedAlt, this._checkAlt)
    }

    static _shouldMatchFunctionKey(pressedFunctionKey, shouldMatch) {
        if (shouldMatch) {
            return pressedFunctionKey === shouldMatch;
        }
        return true;
    }
}