Source: control2/Store.js

(function() {
    /** Central store for all control2 controls */
    Help4.control2.Store = class {
        /**
         * @private
         * @type {Object}
         */
        static #STORE = {};

        /**
         * adds a control to the control2 store
         * @param {Help4.control2.Control} control
         * @returns {Help4.control2.Store}
         */
        static add(control) {
            this.#STORE[control.id] = control;
            return Help4.control2.Store;
        }

        /**
         * removes a control from the control2 store
         * @param {Help4.control2.Control} control
         * @returns {Help4.control2.Store}
         */
        static remove(control) {
            delete this.#STORE[control.id];
            return Help4.control2.Store;
        }

        /**
         * empties the control2 store and destroys all currently contained controls
         * @returns {Help4.control2.Store}
         */
        static clean() {
            for (const [id, control] of Object.entries(this.#STORE)) {
                control.destroy();
                delete this.#STORE[id];
            }
            return Help4.control2.Store;
        }

        /**
         * gets a control or all of them
         * @param {string} [controlId]
         * @returns {Help4.control2.Control|Object|null}
         */
        static get(controlId) {
            return controlId ? (this.#STORE[controlId] || null) : this.#STORE;
        }

        /**
         * find a control based on automatic DOM id and control id mapping
         * @param {HTMLElement} elem
         * @returns {?Help4.control2.Control}
         */
        static find(elem) {
            let ctl;

            while (elem?.nodeType === Node.ELEMENT_NODE) {
                if (ctl = elem.id && this.get(elem.id)) return ctl;
                elem = elem.parentElement;
            }

            return null;
        }

        /**
         * top level controls are not hosted by another control
         * @param {boolean} [onlyVisible]
         * @returns {Help4.control2.Control[]}
         */
        static getTopLevelControls(onlyVisible = false) {
            const ids = Object.keys(this.#STORE);

            const hosted = [];
            for (const id of ids) {
                if (hosted.indexOf(id) >= 0) continue;

                const hostedIds = this.get(id)
                .getHostedControls(onlyVisible)
                .map(control => control.id);

                hosted.push(...hostedIds);
            }

            return ids
            .filter(id => hosted.indexOf(id) < 0)
            .map(id => this.get(id));
        }

        /**
         * @param {boolean} [onlyVisible]
         * @returns {Object|null}
         */
        static getTexts(onlyVisible = false) {
            const controls = this.getTopLevelControls();
            const textMap = {};

            for (const control of controls) {
                Help4.extendObject(textMap, control.getTexts(onlyVisible));
            }

            return Object.keys(textMap).length ? textMap : null;
        }

        /**
         * @param {Object} textMap
         * @returns {Help4.control2.Store}
         */
        static setTexts(textMap) {
            const controlTexts = {};
            for (const [combinedId, text] of Object.entries(textMap)) {
                const [controlId, textId] = combinedId.split('.');
                controlTexts[controlId] ||= {};
                controlTexts[controlId][textId] = text;
            }

            for (const [controlId, texts] of Object.entries(controlTexts)) {
                const control = this.get(controlId);
                if (control) control.setControlTexts(texts);
            }

            return this;
        }
    };
})();