Source: engine/ur/Tour.js

(function() {
    const {v2: M} = Help4.engine.ur.UrHarmonizationEngine.MSG_TYPE;
    /** Guided Tour handling for UR harmonization */
    Help4.engine.ur.Tour = class {
        /**
         * map UR event to tour event types
         * @private
         */
        static #EVENT_MAP = {
            [M.activateElement]: 'click',
            [M.hoverElement]: 'mouseover',
            [M.interactOnElement]: 'keyup',
            [M.leaveElement]: 'blur',
            [M.hotkeyPress]: 'key'
        };

        /**
         * forwarding UR postMessage events
         * @param {Help4.engine.ur.UrHarmonizationEngine} engine
         * @param {string} eventType
         * @param {string} [hotspotId]
         * @param {string|number} [key]
         * @param {boolean} [shift]
         * @param {boolean} [ctrl]
         * @param {boolean} [alt]
         */
        static onEvent(engine, eventType, {hotspotId, key, shift, ctrl, alt}) {
            // keeping the input key parameters for now, but the only values used by the tour observer are
            // event.type and event.params.value (which isn't communicated by UR)
            const {view} = Help4.widget.getActiveInstance()?.getContext().widget.tour || {};    // CMP4
            const handler = !view && engine.controller.getHandler();                            // CMP3
            const tour = handler instanceof Help4.controller.Tour
                ? /** @type {Help4.controller.Tour} */ handler
                : /** @type {?Help4.widget.tour.View} */ view;

            // ignore events when tour is not active, e.g. when they arrive late
            if (!tour) return;

            // ignore unsupported events
            const type = this.#EVENT_MAP[eventType];
            if (!type) return;

            // handle hotkeyPress event without hotspotId
            const tourEvent = {event: {type}};
            if (eventType === M.hotkeyPress) {
                // support for both key codes and key names
                const keyName = typeof key === 'number'
                    ? Help4.service.HotkeyService.getKey({event: {type: 'key', keyCode: key, shiftKey: shift, ctrlKey: ctrl, altKey: alt}})
                    : key;
                // for tours only tab and enter are relevant and get their own event types
                if (keyName === 'tab' || keyName === 'enter') tourEvent.event.type = keyName;
                if (!hotspotId) {
                    tour.onUrTourEvent(tourEvent);
                    return;
                }
            }

            // validate the hotspot event targets the current tour step; ignore events for other hotspots
            const {hotspotAnchor} = (view ? view.getCurrentTile() : handler.getTile(handler._step)) ||  {};
            const {Help} = Help4.widget.companionCore.data;
            const value = Help.decodeUrHotspot(hotspotAnchor)?.ur;
            if (value !== hotspotId) return;

            // forward the event
            tour.onUrTourEvent(tourEvent);
        }

        /**
         * send request to UR app to scroll the hotspot into view
         * @param {Help4.engine.ur.UrHarmonizationEngine} engine
         * @param {string} hotspotId
         */
        static scrollIntoView(engine, hotspotId) {
            const {
                _appFrame,
                _connected,
                _running,
                constructor: {MSG_TYPE: {v2}},
            } = engine;

            if (!hotspotId || !_connected || !_running) return;

            const {Connection} = Help4.engine.ur;
            const service = v2.scrollIntoView;
            engine._log?.('CMP: sending', 'ℹ️ ' + service, ()=>hotspotId);
            _appFrame?.contentWindow?.postMessage(Help4.JSON.stringify({
                type: 'request',
                service,
                body: {hotspotId}
            }), Connection.getTargetOrigin(engine));
        }
    }
})();