nbsrc/utils/InteractionObserver.js

goog.module('nbsrc.utils.InteractionObserver');

const events      = goog.require('goog.events');
const Event       = goog.require('goog.events.Event');
const EventTarget = goog.require('goog.events.EventTarget');
const EventType   = goog.require('goog.events.EventType');
const classlist   = goog.require('goog.dom.classlist');

/**
 * Checks permanently if a user interacts with a mouse or touch and toggles a class to the html tag.
 * @extends {EventTarget}
 */
class InteractionObserver extends EventTarget
{
    constructor()
    {
        super();

        /**
         * Defines if a mouse interaction was detected.
         * @type {boolean}
         * @private
         */
        this.isMouseDetected_ = false;

        /**
         * Defines if a touch interaction was detected.
         * @type {boolean}
         * @private
         */
        this.isTouchDetected_ = false;

        /**
         * Defines if a keyboard tab interaction was detected.
         * @type {boolean}
         * @private
         */
        this.isTabDetected_ = false;

        /**
         * Defines the current interaction mode: `MOUSE`, `TAB` or `TOUCH`
         * @type {string}
         * @private
         */
        this.currentMode_ = 'TOUCH';

        /**
         * Specifies whether one of the classes `is-mouse-mode`, `is-tab-mode` or `is-touch-mode`
         * should be added to the main document element as a representation of the current interaction mode.
         * @type {boolean}
         * @private
         */
        this.setCssClasses_ = true;

        events.listen(window, EventType.MOUSEMOVE, this.handleMouseMove_, false, this);
        events.listen(window, EventType.TOUCHSTART, this.handleTouchStart_, false, this);
        events.listen(window, EventType.KEYUP, this.handleKeyUp_, false, this);

        if(this.setCssClasses_)
            classlist.enable(document.documentElement, 'is-touch-mode', true);
    }

    /**
     * Monitors the interaction with the mouse
     * @private
     * @param {Event} event
     */
    handleMouseMove_(event)
    {
        if(this.isTabDetected_)
        {
            this.isTabDetected_ = false;

            if(this.setCssClasses_)
                classlist.enable(document.documentElement, 'is-tab-mode', false);
        }

        if(!this.isMouseDetected_)
        {
            if(!this.isTouchDetected_)
            {
                this.isMouseDetected_ = true;
                this.handleModeChange_();
            }

            this.isTouchDetected_ = false;
        }
        else
        {
            events.unlisten(window, EventType.MOUSEMOVE, this.handleMouseMove_, false, this);
        }
    }

    /**
     * Monitors the interaction by touch
     * @private
     * @param {Event} event
     */
    handleTouchStart_(event)
    {
        if(this.isTabDetected_)
        {
            this.isTabDetected_ = false;

            if(this.setCssClasses_)
                classlist.enable(document.documentElement, 'is-tab-mode', false);
        }

        this.isTouchDetected_ = true;
        this.isMouseDetected_ = false;
        this.handleModeChange_();
    }

    /**
     * Monitors the interaction by key
     * @private
     * @param {Event} event
     */
    handleKeyUp_(event)
    {
        if (event.keyCode === 9)
        {
            this.isTabDetected_ = true;
            this.handleModeChange_();
        }
    }

    /**
     * Changes the current interaction mode and fires a EventType.CHANGE event when the mode changes.
     * @private
     */
    handleModeChange_()
    {
        if(this.currentMode_ !== 'TAB' && this.isTabDetected_)
        {
            if(this.setCssClasses_)
                classlist.enable(document.documentElement, 'is-tab-mode', true);

            events.listen(window, EventType.MOUSEMOVE, this.handleMouseMove_, false, this);
            this.currentMode_ = 'TAB';
            this.isMouseDetected_ = false;
            this.isTouchDetected_ = false;

            this.dispatchEvent(new Event(EventType.CHANGE));
        }
        else if(this.currentMode_ !== 'MOUSE' && this.isMouseDetected_)
        {
            if(this.setCssClasses_)
            {
                classlist.enable(document.documentElement, 'is-mouse-mode', true);
                classlist.enable(document.documentElement, 'is-touch-mode', false);
            }

            events.unlisten(window, EventType.MOUSEMOVE, this.handleMouseMove_, false, this);
            this.currentMode_ = 'MOUSE';

            this.dispatchEvent(new Event(EventType.CHANGE));
        }
        else if(this.currentMode_ !== 'TOUCH' && this.isTouchDetected_)
        {
            if(this.setCssClasses_)
            {
                classlist.enable(document.documentElement, 'is-mouse-mode', false);
                classlist.enable(document.documentElement, 'is-touch-mode', true);
            }

            events.listen(window, EventType.MOUSEMOVE, this.handleMouseMove_, false, this);
            this.currentMode_ = 'TOUCH';

            this.dispatchEvent(new Event(EventType.CHANGE));
        }
    }

    /**
     * Returns the current interaction mode
     * @public
     */
    getMode()
    {
        return this.currentMode_;
    }
}

goog.addSingletonGetter(InteractionObserver);

exports = InteractionObserver;