Source: components/input.js

/*
 * File: input.js
 *  
 * interfaces with HTML5 to to receive keyboard events
 * 
 * For a complete list of key codes, see
 * https://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes
 */
"use strict";

/**
 * Mapping and functions for live user input
 * <p>Found in Chapter 4, page 125 of the textbook </p>
 * 
 * Examples:
 * {@link https://mylesacd.github.io/build-your-own-2d-game-engine-2e-doc/BookSourceCode/chapter4/4.2.keyboard_support/index.html 4.2 Keyboard Support}, 
 * {@link https://mylesacd.github.io/build-your-own-2d-game-engine-2e-doc/BookSourceCode/chapter7/7.5.mouse_input/index.html 7.5 Mouse Input}
 * @module input
 */
// Key code constants

/**
 * Keyboard enums
 * @export input
 * 
 */
const keys = {
    Backspace: 8,
    Enter: 13,

    // arrows
    Left: 37,
    Up: 38,
    Right: 39,
    Down: 40,

    // space bar
    Space: 32,

    // numbers 
    Zero: 48,
    One: 49,
    Two: 50,
    Three: 51,
    Four: 52,
    Five: 53,
    Six: 54,
    Seven: 55,
    Eight: 56,
    Nine: 57,

    // Alphabets
    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,

    LastKeyCode: 222
}

// mouse button enums
/**
 * Mouse button enums
 * @export input
 * @enum
 */
const eMouseButton = Object.freeze({
    eLeft: 0,
    eMiddle: 1,
    eRight: 2
});

// Previous key state
let mKeyPreviousState = []; // a new array
// The pressed keys.
let mIsKeyPressed = [];
// Click events: once an event is set, it will remain there until polled
let mIsKeyClicked = [];

// Event handler functions
function onKeyDown(event) {
    mIsKeyPressed[event.keyCode] = true;
}

function onKeyUp(event) {
    mIsKeyPressed[event.keyCode] = false;
}

// Support mouse
let mCanvas = null;
let mButtonPreviousState = [];
let mIsButtonPressed = [];
let mIsButtonClicked = [];
let mMousePosX = -1;
let mMousePosY = -1;

function onMouseMove(event) {
    let inside = false;
    let bBox = mCanvas.getBoundingClientRect();
    // In Canvas Space now. Convert via ratio from canvas to client.
    let x = Math.round((event.clientX - bBox.left) * (mCanvas.width / bBox.width));
    let y = Math.round((event.clientY - bBox.top) * (mCanvas.height / bBox.height));

    if ((x >= 0) && (x < mCanvas.width) &&
        (y >= 0) && (y < mCanvas.height)) {
        mMousePosX = x;
        mMousePosY = mCanvas.height - 1 - y;
        inside = true;
    }
    return inside;
}

function onMouseDown(event) {
    if (onMouseMove(event)) {
        mIsButtonPressed[event.button] = true;
    }
}

function onMouseUp(event) {
    onMouseMove(event);
    mIsButtonPressed[event.button] = false;
}

function cleanUp() { }  // nothing to do for now

/**
 * Initialize the input manager and instantiate input listeners
 * @export input
 * @param {string} canvasID - the case sensitive html canvas id
 */
function init(canvasID) {
    let i;

    // keyboard support
    for (i = 0; i < keys.LastKeyCode; i++) {
        mIsKeyPressed[i] = false;
        mKeyPreviousState[i] = false;
        mIsKeyClicked[i] = false;
    }

    // register handlers 
    window.addEventListener('keyup', onKeyUp);
    window.addEventListener('keydown', onKeyDown);

    // Mouse support
    for (i = 0; i < 3; i++) {
        mButtonPreviousState[i] = false;
        mIsButtonPressed[i] = false;
        mIsButtonClicked[i] = false;
    }
    window.addEventListener('mousedown', onMouseDown);
    window.addEventListener('mouseup', onMouseUp);
    window.addEventListener('mousemove', onMouseMove);
    mCanvas = document.getElementById(canvasID);
}
/**
 * Update function called from GameLoop
 * @export input
 */
function update() {
    let i;
    // update keyboard input state
    for (i = 0; i < keys.LastKeyCode; i++) {
        mIsKeyClicked[i] = (!mKeyPreviousState[i]) && mIsKeyPressed[i];
        mKeyPreviousState[i] = mIsKeyPressed[i];
    }

    // update mouse input state
    for (i = 0; i < 3; i++) {
        mIsButtonClicked[i] = (!mButtonPreviousState[i]) && mIsButtonPressed[i];
        mButtonPreviousState[i] = mIsButtonPressed[i];
    }
}

// Function for GameEngine programmer to test if a key is pressed down
/**
 * Returns if a specific key is pressed
 * @export input
 * @param {keys} keyCode - key to check for pressed state
 * @returns {boolean} true if the key is pressed
 */
function isKeyPressed(keyCode) {
    return mIsKeyPressed[keyCode];
}

/**
 * Returns if a specific key is clicked
 * @export input
 * @param {keys} keyCode - key to check for clicked state
 * @returns {boolean} true if the key is clicked
 */
function isKeyClicked(keyCode) {
    return mIsKeyClicked[keyCode];
}

// Functions for query mouse button state and position
/**
 * Returns if a specific mouse button is pressed
 * @export input
 * @param {eMouseButton} button - button to check for pressed state
 * @returns {boolean} true if the button is pressed
 */
function isButtonPressed(button) {
    return mIsButtonPressed[button];
}

/**
 * Returns if a specific mouse button is clicked
 * For a button to be clicked it must have been pressed then released
 * @export input
 * @param {eMouseButton} button - button to check for ckicked state
 * @returns {boolean} true if the button is clicked
 */
function isButtonClicked(button) {
    return mIsButtonClicked[button];
}

/**
 * Returns mouse X position
 * @export input
 * @returns {float} X position of mouse
 */
function getMousePosX() { return mMousePosX; }

/**
 * Returns mouse Y position
 * @export input
 * @returns {float} Y position of mouse
 */
function getMousePosY() { return mMousePosY; }

export {
    keys, eMouseButton,

    init, cleanUp, update,

    // keyboard
    isKeyClicked, isKeyPressed,

    // mouse
    isButtonClicked, isButtonPressed, getMousePosX, getMousePosY
}