(function() {
const WAIT_TIME = 100;
/**
* @typedef {Object} Help4.widget.Requirement.Requirements
* @property {string[]} [namespaces] - all required namespaces
* @property {string[]} [instances] - names of all required widget instances
*/
/**
* Requirement tests for Help4.widget
*/
Help4.widget.Requirement = class {
/**
* @param {Help4.widget.Requirement.Requirements} requirements
* @param {Help4.widget.Widget.Context} context
* @returns {Promise<boolean>}
*/
static awaitRequirements(requirements, context) {
/** @type {Array<Promise<boolean>>} */ const promises = [];
const {
/** @type {string[]} */
namespaces = [],
/** @type {string[]} */
instances = []
} = requirements;
for (const namespace of namespaces) {
promises.push(awaitNamespaceExists.call(this, namespace));
}
for (const instance of instances) {
promises.push(awaitWidgetStarted.call(this, instance, context));
}
/**
* @param {boolean[]} results
* @returns {boolean}
*/
const success = results => results.every(result => result);
return Help4.Promise
.all(promises)
.then(success);
}
/**
* @param {string} name - widget instance name
* @returns {boolean}
*/
static isWidgetStarted(name) {
return Help4.widget.getInstance(name)?.isStarted() || false;
}
/**
* @param {Help4.widget.Requirement.Requirements} requirements
* @returns {string[]}
*/
static allRequiredWidgetsStarted(requirements) {
/** @type {string[]} */ const missing = [];
const {/** @type {string[]} */ instances = []} = requirements;
for (const instance of instances) {
// a required widget instance has been shut down
if (!this.isWidgetStarted(instance)) missing.push(instance);
}
return missing; // all fulfilled or no requirements
}
}
/**
* Waits until the namespace exists. Will never resolve in case namespace is not created.
* @memberof Help4.widget.Requirement#
* @private
* @param {string} name - namespace name
* @returns {Promise<true>}
*/
function awaitNamespaceExists(name) {
return new Help4.Promise(resolve => {
const parts = name.split('.');
let obj = window;
const test = () => {
while (parts.length && obj[parts[0]]) {
obj = obj[parts.shift()];
}
parts.length
? setTimeout(test, WAIT_TIME)
: resolve(true);
}
test();
});
}
/**
* Waits until the widget instance is started.
* Will resolve with false on system error.
* Will never resolve in case widget is not started.
* @memberof Help4.widget.Requirement#
* @private
* @param {string} name - widget instance name
* @param {Help4.widget.Widget.Context} context
* @returns {Promise<true>}
*/
function awaitWidgetStarted(name, context) {
return new Help4.Promise(resolve => {
// already started
if (this.isWidgetStarted(name)) return resolve(true);
// observe all widget starts
const {eventBus} = context;
const type = Help4.EventBus.TYPES.widgetStart;
const ebo = new Help4.observer.EventBusObserver(({engine}) => {
if (engine.getName() === name) { // required widget has been started
ebo.destroy();
resolve(true);
}
})
.observe(eventBus, {type});
});
}
})();