API Docs for: 1.0.1

File: js\grape\game\input.js

define(['../class', '../env', '../utils'], function (Class, Env, Utils) {
    //TODOv2 node environment
    var KEYS = {
        any: 'any',
        none: 'none',
        mouse1: 'mouseLeft',
        mouse2: 'mouseMiddle',
        mouse3: 'mouseRight',
        8: 'backspace',
        9: 'tab',
        12: 'clear',
        13: 'enter',
        16: 'lshift',
        17: 'ctrl',
        18: 'alt',
        19: 'pause',
        20: 'rshift',
        27: 'esc',
        32: 'space',
        33: 'pagegup',
        34: 'pagedown',
        35: 'end',
        36: 'home',
        37: 'left',
        38: 'up',
        39: 'right',
        40: 'down',
        45: 'insert',
        46: 'delete',
        91: 'windows',
        93: 'contextmenu',
        106: '*',
        107: '+',
        109: '-',
        110: '.',
        111: '/',
        144: 'numlock',
        186: 'é',
        187: 'ó',
        188: ',',
        189: '/',
        190: '.',
        191: 'ü',
        192: 'ö',
        219: '\u0151',
        220: '\u0171',
        221: 'ú',
        222: 'á',
        226: 'í'
    };
    var REVERSED_KEYS = {};
    var i;
    /**
     * An object which stores the mouse position relative to the page. In the most cases you want to use the instance
     * level mouse property instead.
     *
     * @property mouse
     * @static
     * @type {Object}
     */
    var mouseScreen = {
        /**
         * Mouse x relative to document.
         *
         * @property mouse.x
         * @static
         * @type Number
         */
        x: 0,
        /**
         * Mouse y relative to document.
         *
         * @property mouse.y
         * @static
         * @type Number
         */
        y: 0
    };

    /*register letters*/
    for (i = 65; i <= 90; ++i) {
        KEYS[i] = String.fromCharCode(i).toLowerCase();
    }

    /*register digits*/
    for (i = 0; i <= 9; ++i) {
        KEYS[i + 48] = i;
    }

    /*register numpad digits*/
    for (i = 0; i <= 9; ++i) {
        KEYS[i + 96] = 'num' + i;
    }

    /*register function keys*/
    for (i = 1; i <= 12; ++i) {
        KEYS[i + 111] = 'f' + i;
    }

    /*reverse*/
    for (i in KEYS) {
        REVERSED_KEYS[KEYS[i]] = i;
    }


    function isKeyIn(key, keySet) {
        var i;
        if (key === "any" || key === "none") {
            for (i in keySet) {
                return key === "any";
            }
            return key === "none";
        }
        return keySet[REVERSED_KEYS[key]] === true;
    }

    function dispatchKeys(target, keys, prefix) {
        var keyNum = 0;
        for (var i in keys) {
            if (keyNum === 0) { //first key
                target.emit(prefix + '.any', i);
            }
            ++keyNum;
            target.emit(prefix + '.' + KEYS[i]);
        }
        if (keyNum === 0) {
            target.emit(prefix + '.none');
        }
    }

    if (Env.browser) {
        //TODOv2 have to be initialized: https://forum.jquery.com/topic/is-it-possible-to-get-mouse-position-when-the-page-has-loaded-without-moving-the-mouse
        Utils.addEventListener(document, 'mousemove', function (event) {
            mouseScreen.x = event.clientX;
            mouseScreen.y = event.clientY;
        });
    }
    /**
     * Handles which key is down, just pressed or just released in a game. A Game  Also handles mouse. The following
     * keys are available:
     *  <ul>
     *      <li><code>any</code> (matches any key)</li>
     *      <li><code>none</code></li>
     *      <li><code>mouseLeft</code></li>
     *      <li><code>mouseMiddle</code></li>
     *      <li><code>mouseRight</code></li>
     *      <li><code>a</code> ... <code>z</code> (letter keys)</li>
     *      <li><code>0</code> ... <code>9</code> (digit keys)</li>
     *      <li><code>num0</code> ... <code>num9</code> (numpad keys)</li>
     *      <li><code>f1</code> ... <code>f12</code> (function keys)</li>
     *      <li><code>backspace</code></li>
     *      <li><code>tab</code></li>
     *      <li><code>enter</code></li>
     *      <li><code>lshift</code> (left shift)</li>
     *      <li><code>rshift</code> (right shift)</li>
     *      <li><code>ctrl</code></li>
     *      <li><code>alt</code></li>
     *      <li><code>pause</code></li>
     *      <li><code>clear</code></li>
     *      <li><code>esc</code></li>
     *      <li><code>space</code></li>
     *      <li><code>pageup</code></li>
     *      <li><code>pagedown</code></li>
     *      <li><code>end</code></li>
     *      <li><code>home</code></li>
     *      <li><code>left</code></li>
     *      <li><code>right</code></li>
     *      <li><code>up</code></li>
     *      <li><code>down<</code>/li>
     *      <li><code>insert</code></li>
     *      <li><code>delete</code></li>
     *      <li><code>windows</code></li>
     *      <li><code>contextmenu</code></li>
     *      <li><code>+</code></li>
     *      <li><code>-</code></li>
     *      <li><code>*</code></li>
     *      <li><code>.</code></li>
     *      <li><code>/</code></li>
     *      <li><code>numlock</code></li>
     *  </ul>
     *
     * @class Grape.Input
     * @constructor
     * @param {Object} [opts] Options
     * @param {Array} [opts.reservedKeys] keys for which browser's default action will be prevented. The Game class
     * passes this property when instantiating the input.
     */
    return Class('Input', {
        'static mouse': mouseScreen,
        /**
         * Sets the keys which would prevent the browser's default action to be triggered.
         *
         * @method setReservedKeys
         * @param {Array} keys Key ids
         */
        setReservedKeys: function (keys) {
            var result = {},
                i, key;
            for (i = 0; i < keys.length; i++) {
                key = REVERSED_KEYS[keys[i]];
                if (!key) {
                    throw new Error('Key ' + keys[i] + ' does not exist.');
                }
                result[key] = true;
            }
            this._reservedKeys = result;
        },
        init: function (opts) {
            opts = opts || {};
            this.downKeys = {};
            this.pressedKeys = {};
            this.releasedKeys = {};
            /**
             * The x and y coordinates of the mouse relative to the game screen.
             *
             * @property mouse
             * @type {Object}
             */
            this.mouse = {
                x: mouseScreen.x,
                y: mouseScreen.y,
                prevX: mouseScreen.x,
                prevY: mouseScreen.y,
                screen: mouseScreen
            };
            this.setReservedKeys(opts.reservedKeys || []);
        },
        _calculateMouse: function () {
            var rect = this._screen.getBoundingClientRect();
            this.mouse.x = mouseScreen.x - rect.left;
            this.mouse.y = mouseScreen.y - rect.top;
            this.mouse.view = null;
        },
        _start: function (screen) {
            var that = this;
            this._screen = screen;

            function down(key, event) {
                if (!that.downKeys[key]) {
                    that.pressedKeys[key] = true;
                }
                that.downKeys[key] = true;

                if (that._reservedKeys[key]) {
                    event.preventDefault();
                }
            }

            function up(key) {
                if (that.downKeys[key]) {
                    delete that.downKeys[key];
                    that.releasedKeys[key] = true;
                }
            }

            this.onKeyDown = function (event) {
                down(event.which, event);
            };
            this.onKeyUp = function (event) {
                up(event.which);
            };
            this.onContextMenu = function (event) {
                if (Utils.domContains(screen, event.target)) {
                    event.preventDefault();
                }
            };
            this.onMouseDown = function (event) {
                if (screen === event.target || Utils.domContains(screen, event.target)) {
                    down('mouse' + event.which, event);
                    event.preventDefault();
                }
            };
            this.onMouseUp = function (event) {
                up('mouse' + event.which);
            };
            this._calculateMouse();
            Utils.addEventListener(document, 'keydown', this.onKeyDown); //TODOv2 to loop
            Utils.addEventListener(document, 'keyup', this.onKeyUp); //TODOv2 handle all of these globally
            Utils.addEventListener(document, 'contextmenu', this.onContextMenu);
            Utils.addEventListener(document, 'mousedown', this.onMouseDown);
            Utils.addEventListener(document, 'mouseup', this.onMouseUp);
        },
        _stop: function () {
            Utils.removeEventListener(document, 'keydown', this.onKeyDown);
            Utils.removeEventListener(document, 'keyup', this.onKeyUp);
            Utils.removeEventListener(document, 'contextmenu', this.onContextMenu);
            Utils.removeEventListener(document, 'mousedown', this.onMouseDown);
            Utils.removeEventListener(document, 'mouseup', this.onMouseUp);
        },
        _emitEvents: function (target) {
            /**
             * Fires when the <key> was pressed since the last frame.
             *
             * @event keyPress.<key>
             */
            dispatchKeys(target, this.pressedKeys, 'keyPress');
            /**
             * Fires when the <key> is held in the current frame.
             *
             * @event keyDown.<key>
             */
            dispatchKeys(target, this.downKeys, 'keyDown');
            /**
             * Fires when the <key> was released since the last frame.
             *
             * @event keyRelease.<key>
             */
            dispatchKeys(target, this.releasedKeys, 'keyRelease');
            this._calculateMouse();
            if (this.mouse.prevX !== this.mouse.x || this.mouse.prevY !== this.mouse.y) {
                /**
                 * When the mouse moves, this is the first event emitted. The parameter is the mouse property of the
                 * input instance.
                 *
                 * @event beforeMouseMove
                 */
                target.emit('beforeMouseMove', this.mouse);
                /**
                 * When the mouse moves, this is the second event emitted. The parameter is the mouse property of the
                 * input instance.
                 *
                 * @event mouseMove
                 */
                target.emit('mouseMove', this.mouse);
                /**
                 * When the mouse moves, this is the third event emitted. The parameter is the mouse property of the
                 * input instance.
                 *
                 * @event afterMouseMove
                 */
                target.emit('afterMouseMove', this.mouse);
            }
            this.mouse.prevX = this.mouse.x;
            this.mouse.prevY = this.mouse.y;
            this.pressedKeys = {};
            this.releasedKeys = {};
        },
        /**
         * Resets the status of the input system. Since this point all key is considered as it wasn't held. When a key
         * is released when the document is not in focus (like during an alert call), it can be used.
         *
         * @method resetKeys
         */
        resetKeys: function () {
            this.pressedKeys = {};
            this.releasedKeys = {};
            this.downKeys = {};
        },
        /**
         * Tells whether the given key was pressed since the previous frame.
         *
         * @method isPressed
         * @param {String key Key id
         * @return {Boolean} true, if the key wasn't held in the last frame but now is.
         */
        isPressed: function (key) {
            return isKeyIn(key, this.pressedKeys);
        },
        /**
         * Tells whether the given key was released since the last frame.
         *
         * @method isReleased
         * @param {String} key Key id
         * @return {Boolean} true, if the key was held in the last frame and now isn't.
         */
        isReleased: function (key) {
            return isKeyIn(key, this.releasedKeys);
        },
        /**
         * Tells whether the user is holding a key.
         *
         * @method isDown
         * @param {String} key Key id
         * @return {Boolean} true, if held.
         */
        isDown: function (key) {
            return isKeyIn(key, this.downKeys);
        }
    });
});