extensions/gyroscope.extension.js

// 'ESLint' configuration
/* global Gyroscope */

import {EVENT_CODES, EVENT_TYPES, EventGyroscopeAnalog, EventGyroscopeDigital, MATHEMATICS} from '../index.js';

/**
 * The ordered list of the rotations event codes of the gyroscope.
 * @type {Array<Array<string>>}
 * @constant
 * @private
 */
const $GYROSCOPE_ROTATIONS = [

    [EVENT_CODES.GYROSCOPE.ROTATION_X_NEGATIVE, EVENT_CODES.GYROSCOPE.ROTATION_X_POSITIVE],
    [EVENT_CODES.GYROSCOPE.ROTATION_Y_NEGATIVE, EVENT_CODES.GYROSCOPE.ROTATION_Y_POSITIVE],
    [EVENT_CODES.GYROSCOPE.ROTATION_Z_NEGATIVE, EVENT_CODES.GYROSCOPE.ROTATION_Z_POSITIVE]
];

/**
 * The threshold of the gyroscope angular velocity (in radians/s).
 * @type {number}
 * @constant
 * @private
 */
const $THRESHOLD_GYROSCOPE_VELOCITY_ANGULAR = 1 * MATHEMATICS.RADIANS;

/**
 * Creates gyroscope extensions.
 *
 * @example
 *
 * ExtensionGyroscope.activate();
 */
class ExtensionGyroscope {

    /**
     * Stores the activated status.
     * @type {boolean}
     * @private
     * @static
     */
    static $activated = false;

    /**
     * Stores the gyroscope.
     * @type {Gyroscope}
     * @private
     */
    $gyroscope;

    /**
     * Stores the gyroscope state.
     * @type {Object<string, boolean>}
     * @private
     */
    $stateGyroscope;

    /**
     * Creates a new gyroscope extension.
     * @protected
     */
    constructor() {

        this.$stateGyroscope = {};

        [...$GYROSCOPE_ROTATIONS.flat()].forEach(($code) => {

            this.$stateGyroscope[$code] = false;
        });

        this.$gyroscope = new Gyroscope({

            'frequency': 60
        });

        window.addEventListener(EVENT_TYPES.NATIVE.BLUR, this.$onBlur.bind(this));

        this.$gyroscope.addEventListener(EVENT_TYPES.NATIVE.READING, this.$onGyroscope.bind(this));

        this.$gyroscope.start();
    }

    /**
     * Called when the focus is lost.
     * @private
     */
    $onBlur() {

        $GYROSCOPE_ROTATIONS.forEach(($pair) => {

            const [rotationMinimum, rotationMaximum] = $pair;

            if (this.$stateGyroscope[rotationMinimum] === true) {

                this.$stateGyroscope[rotationMinimum] = false;
                window.dispatchEvent(new EventGyroscopeDigital(EVENT_TYPES.GYROSCOPE.GYROSCOPE_UP, rotationMinimum));
            }

            if (this.$stateGyroscope[rotationMaximum] === true) {

                this.$stateGyroscope[rotationMaximum] = false;
                window.dispatchEvent(new EventGyroscopeDigital(EVENT_TYPES.GYROSCOPE.GYROSCOPE_UP, rotationMaximum));
            }
        });
    }

    /**
     * Called when the gyroscope is updated.
     * @private
     */
    $onGyroscope() {

        [this.$gyroscope.x, this.$gyroscope.y, this.$gyroscope.z].forEach(($rotation, $index) => {

            const [rotationMinimum, rotationMaximum] = $GYROSCOPE_ROTATIONS[$index];

            if ($rotation <= - $THRESHOLD_GYROSCOPE_VELOCITY_ANGULAR) {

                if (this.$stateGyroscope[rotationMaximum] === true) {

                    this.$stateGyroscope[rotationMaximum] = false;
                    window.dispatchEvent(new EventGyroscopeDigital(EVENT_TYPES.GYROSCOPE.GYROSCOPE_UP, rotationMaximum));
                }

                this.$stateGyroscope[rotationMinimum] = true;
                window.dispatchEvent(new EventGyroscopeDigital(EVENT_TYPES.GYROSCOPE.GYROSCOPE_DOWN, rotationMinimum));
            }

            else if ($rotation >= $THRESHOLD_GYROSCOPE_VELOCITY_ANGULAR) {

                if (this.$stateGyroscope[rotationMinimum] === true) {

                    this.$stateGyroscope[rotationMinimum] = false;
                    window.dispatchEvent(new EventGyroscopeDigital(EVENT_TYPES.GYROSCOPE.GYROSCOPE_UP, rotationMinimum));
                }

                this.$stateGyroscope[rotationMaximum] = true;
                window.dispatchEvent(new EventGyroscopeDigital(EVENT_TYPES.GYROSCOPE.GYROSCOPE_DOWN, rotationMaximum));
            }

            else {

                if (this.$stateGyroscope[rotationMinimum] === true) {

                    this.$stateGyroscope[rotationMinimum] = false;
                    window.dispatchEvent(new EventGyroscopeDigital(EVENT_TYPES.GYROSCOPE.GYROSCOPE_UP, rotationMinimum));
                }

                if (this.$stateGyroscope[rotationMaximum] === true) {

                    this.$stateGyroscope[rotationMaximum] = false;
                    window.dispatchEvent(new EventGyroscopeDigital(EVENT_TYPES.GYROSCOPE.GYROSCOPE_UP, rotationMaximum));
                }
            }

            if (Math.abs($rotation) >= $THRESHOLD_GYROSCOPE_VELOCITY_ANGULAR) {

                window.dispatchEvent(new EventGyroscopeAnalog(EVENT_TYPES.GYROSCOPE.GYROSCOPE_ANALOG, rotationMinimum, - $rotation));
                window.dispatchEvent(new EventGyroscopeAnalog(EVENT_TYPES.GYROSCOPE.GYROSCOPE_ANALOG, rotationMaximum, $rotation));
            }
        });
    }

    /**
     * Activates the extension.
     * @public
     * @static
     */
    static activate() {

        if (typeof window.Gyroscope === 'undefined') {

            return;
        }

        if (ExtensionGyroscope.$activated === true) {

            return;
        }

        new ExtensionGyroscope();

        ExtensionGyroscope.$activated = true;
    }
}

export {

    ExtensionGyroscope
};

export default ExtensionGyroscope;