Source: input/index.js

const System = require('engine/system');
const keyboard = require('./keyboard');
const { removeItems } = require('engine/utils/array');

/**
 * Input system which provides key bindings.
 * @class SystemInput
 */
class SystemInput extends System {
  /**
   * @constructor
   */
  constructor() {
    super();

    this.name = 'Input';

    this.bindings = {};
    this.keyList = [];
    this.actions = {};
    this.lastPressed = {};
    this.lastReleased = {};
  }

  /**
   * Bind a key to a specific action.
   * @memberof SystemInput#
   * @method bind
   * @param  {String} key    Key to bind
   * @param  {String} action Action name
   * @return {SystemInput}   Self for chaining
   */
  bind(key, action) {
    if (Array.isArray(this.bindings[key])) {
      this.bindings[key].push(action);
    }
    else {
      this.bindings[key] = [action];
    }

    if (this.keyList.indexOf(key) < 0) {
      this.keyList.push(key);
    }

    this.lastPressed[action] = false;
    this.lastReleased[action] = false;

    return this;
  }
  /**
   * Unbind an action from a key.
   * @memberof SystemInput#
   * @method unbind
   * @param  {String} key    Key to unbind
   * @param  {String} action Action to unbind
   * @return {SystemInput}   Self for chaining
   */
  unbind(key, action) {
    if (Array.isArray(this.bindings[key])) {
      let idx = this.bindings[key].indexOf(action);
      if (idx >= 0) {
        removeItems(this.bindings[key], idx, 1);
      }

      delete this.lastPressed[action];
      delete this.lastReleased[action];
    }

    return this;
  }
  /**
   * Unbind all the actions.
   * @memberof SystemInput#
   * @method unbindAll
   */
  unbindAll() {
    for (let k in this.bindings) {
      if (Array.isArray(this.bindings[k])) {
        this.bindings[k].length = 0;
      }
    }

    this.lastPressed = {};
    this.lastReleased = {};
  }

  /**
   * Whether an action is currently pressed.
   * @memberof SystemInput#
   * @method state
   * @param  {String} action Action name
   * @return {Boolean}       Pressed or not
   */
  state(action) {
    return !!this.actions[action];
  }
  /**
   * Whether an action is just pressed.
   * @memberof SystemInput#
   * @method pressed
   * @param  {String} action Action name
   * @return {Boolean}       Pressed or not
   */
  pressed(action) {
    return !!this.lastPressed[action];
  }
  /**
   * Whether an action is just released.
   * @memberof SystemInput#
   * @method released
   * @param  {String} action Action name
   * @return {Boolean}       Released or not
   */
  released(action) {
    return !!this.lastReleased[action];
  }

  /**
   * Awake callback.
   * @memberof SystemInput#
   * @method awake
   * @private
   */
  awake() {
    keyboard.on('keydown', this.keydown, this);
    keyboard.on('keyup', this.keyup, this);

    this.resetFlags();
  }
  /**
   * Fixed update callback.
   * @memberof SystemInput#
   * @method fixedUpdate
   * @private
   */
  fixedUpdate() {
    // Mark press/release action as false
    this.resetFlags();
  }
  /**
   * Freeze callback.
   * @memberof SystemInput#
   * @method freeze
   * @private
   */
  freeze() {
    keyboard.off('keydown', this.keydown, this);
    keyboard.off('keyup', this.keyup, this);

    this.resetFlags();
  }

  /**
   * Key down listener
   * @memberof SystemInput#
   * @method keydown
   * @param {String} k Name of the key
   * @private
   */
  keydown(k) {
    if (this.keyList.indexOf(k) !== -1) {
      let i, list = this.bindings[k];
      for (i = 0; i < list.length; i++) {
        this.actions[list[i]] = true;
        this.lastPressed[list[i]] = true;
      }
    }
  }
  /**
   * Key up listener
   * @memberof SystemInput#
   * @method keyup
   * @param {String} k Name of the key
   * @private
   */
  keyup(k) {
    if (this.keyList.indexOf(k) !== -1) {
      let i, list = this.bindings[k];
      for (i = 0; i < list.length; i++) {
        this.actions[list[i]] = false;
        this.lastReleased[list[i]] = true;
      }
    }
  }
  /**
   * Reset press and release flags.
   * @memberof SystemInput#
   * @method resetFlags
   * @private
   */
  resetFlags() {
    let k;
    for (k in this.lastPressed) {
      this.lastPressed[k] = false;
    }
    for (k in this.lastReleased) {
      this.lastReleased[k] = false;
    }
  }
}

module.exports = SystemInput;