(function() {
/**
* @namespace tourlist
* @memberof Help4.widget
*/
Help4.widget.tourlist = {};
const NAME = 'tourlist';
const HELP_NAME = 'help';
const TOUR_NAME = 'tour';
/**
* @typedef {Help4.widget.Widget.Context} Help4.widget.tourlist.Widget.Context
* @property {Object} widget.tourlist
* @property {Help4.widget.tourlist.Data} widget.tourlist.data
* @property {Object} widget.help
* @property {Help4.widget.help.Data} widget.help.data
*/
/**
* Guided Tours list functionality widget
* @augments Help4.widget.Widget
* @property {?Help4.widget.tourlist.Data} _data
* @property {?Help4.widget.tourlist.View} _view
* @property {Function} _onUpdateCatalogue
* @property {Function} _domRefreshExecutor
*/
Help4.widget.tourlist.Widget = class extends Help4.widget.Widget {
/** @override */
constructor() {
const onUpdateCatalogue = async (event) => {
if (event.name === 'catalogues') {
const {_data, _view} = this;
_data.updateCatalogue();
await _setVisible.call(this);
if (this.isDestroyed()) return;
_view?.update();
}
}
const domRefreshExecutor = () => this.isStarted() && _setVisible.call(this);
super({
statics: {
_data: {},
_view: {},
_onUpdateCatalogue: {init: onUpdateCatalogue, destroy: false},
_domRefreshExecutor: {init: domRefreshExecutor, destroy: false}
}
});
}
/**
* will observe DOM changes to re-calculate tourlist visibility<br>
* requirements:<br>
* 1. widget is initialized; see {@link Help4.widget.tourlist.Widget#_onAfterInit}<br>
* 2. controller is open; see {@link Help4.widget.tourlist.Widget#_onControllerOpen}<br>
* otherwise observation is disconnected to minimize footprint
*
* @param {Help4.widget.tourlist.Widget|Help4.widget.whatsnew.Widget} widget
* @param {boolean} [stop = false]
*/
static observeDomRefresh(widget, stop = false) {
const {_domRefreshExecutor} = widget;
if (!_domRefreshExecutor) return;
const {Core} = Help4.widget.companionCore;
const context = widget.getContext();
if (stop) {
Core.disconnectDomRefresh(_domRefreshExecutor, context);
} else {
context.controller?.isOpen()
? Core.observeDomRefresh(_domRefreshExecutor, context, this) // observe DOM changes to adjust tour tile visibility
: Core.disconnectDomRefresh(_domRefreshExecutor, context); // disconnect
}
}
/**
* @param {Help4.typedef.SystemConfiguration} configuration
* @param {Help4.widget.tour.Widget.StartStatus} tour
* @returns {Promise<void>}
*/
static async startTour({help: {serviceLayer}}, {projectId, catalogueKey, catalogueType, dataType, whatsnew}) {
// EXT: automatically change catalogueKey to "pub" for RO model projects
const isEXT = serviceLayer === Help4.SERVICE_LAYER.ext;
if (isEXT && (catalogueType === 'UACP' || catalogueType === 'SEN')) catalogueKey = 'pub';
const status = /** @type {Help4.widget.tour.Widget.StartStatus} */ {projectId, catalogueKey, catalogueType, dataType, whatsnew};
const {[NAME]: tourlistWidget, [TOUR_NAME]: tourWidget} = /** @type {Help4.widget.tour.Widget} */ Help4.widget.getInstance();
await tourlistWidget.deactivate({tour: true});
await tourWidget?.startTour(status);
}
/** @override */
getName() {
return NAME;
}
/**
* @override
* @returns {Help4.widget.tourlist.Widget.Context}
*/
getContext() {
const helpWidget = /** @type {Help4.widget.help.Widget} */ Help4.widget.getInstance(HELP_NAME);
/** @type {Help4.widget.help.Widget.Context} */
const helpContext = helpWidget?.getContext() || {};
/** @type {Help4.widget.Widget.Context} */
const context = super.getContext();
context.widget = {
tourlist: {
data: this._data
},
help: {
data: helpContext?.widget?.help?.data
}
};
return context;
}
/**
* @override
* @returns {Promise<Help4.widget.Widget.Descriptor>}
*/
async _onGetDescriptor() {
const {
Localization,
control2: {ICONS},
widget: {COLOR}
} = Help4;
const {WM} = this.getContext().configuration;
const text = Localization.getText('button.widget.tours');
return {
id: NAME,
enabled: WM < 2,
showPanel: true,
requires: {
namespaces: [
'Help4.widget.companionCore.Core',
'Help4.widget.companionCore.SEN',
'Help4.widget.companionCore.UACP'
],
instances: [HELP_NAME]
},
tile: {
text,
title: text,
icon: ICONS.tour,
color: COLOR.color2,
position: 2
}
};
}
/** @override */
focus() {
this._view?.focus();
}
/**
* @override
* @param {string} direction
*/
focusListItem(direction) {
this._view?.focusListItem(direction);
}
/** @override */
async _onBeforeDestroy() {
const {/** @type {Help4.widget.help.Data} */ data} = this.getContext().widget.help;
data?.removeListener('dataChange', this._onUpdateCatalogue);
this.constructor.observeDomRefresh(this, true);
}
/** @override */
async _onBeforeInit() {
this._data = new Help4.widget.tourlist.Data({widget: this});
}
/** @override */
async _onAfterInit() {
const {/** @type {Help4.widget.help.Data} */ data} = this.getContext().widget.help;
/**
* will update the tour list based on the updated catalogue information
* @param {Help4.jscore.ControlBase.PropertyChangeEvent} event
*/
const {_onUpdateCatalogue} = this;
// catalogue is managed by help widget; subscribe to changes
// from now on we receive every catalogue update
data.addListener('dataChange', _onUpdateCatalogue);
// get data that might have been loaded already
await _onUpdateCatalogue({name: 'catalogues'});
if (this.isDestroyed()) return;
// monitor DOM changes to decide visibility
this.constructor.observeDomRefresh(this);
}
/** @override */
async _onAfterActivate() {
// initialize tour list view
this._view = new Help4.widget.tourlist.View({widget: this})
.addListener('startTour', ({tour}) => {
const {tourOpen} = Help4.EventBus.TYPES;
const controller = Help4.getController();
controller.getService('eventBus').fire({type: tourOpen});
this.startTour(tour);
});
}
/** @override */
async _onBeforeDeactivate() {
this._destroyControl('_view');
}
/** @override */
async redraw() {
await super.redraw();
if (this.isDestroyed()) return;
await this._view?.update();
}
/**
* @param {Help4.widget.tour.Widget.StartStatus} tour
* @returns {Promise<void>}
*/
async startTour(tour) {
const config = this.getContext().configuration;
await this.constructor.startTour(config, tour);
}
/** @override */
async _onControllerOpen() {
this.constructor.observeDomRefresh(this);
}
/** @override */
async _onControllerClose() {
this.constructor.observeDomRefresh(this);
}
/**
* @override
* @param {Help4.widget.Widget.SearchFilter} search
* @returns {Promise<Help4.widget.Widget.SearchResult[]>}
*/
async filter({fulltext}) {
const {Placeholder, widget: {companionCore: {data: {Tour}}}} = Help4;
const projects = await Tour.getFilteredTourProjects();
if (this.isDestroyed()) return [];
const {configuration} = this.getContext();
const catalogueKey = Help4.widget.companionCore.Core.getCatalogueKey({configuration});
const results = [];
for (const project of projects) {
if (this._fulltextMatchesText(fulltext, Placeholder.resolve(project.title))) results.push({
caption: project.title,
projectId: project.id,
contentLanguage: project.language,
catalogueKey,
catalogueType: project._catalogueType,
dataType: project._dataType,
});
}
return results;
}
/** @override */
async getTexts() {
if (this.isActive()) {
const {panel} = this.getContext();
return panel.getTexts();
}
}
/**
* @override
* @param {Object} texts
*/
async setTexts(texts) {
if (this.isActive()) {
const {panel} = this.getContext();
panel.setTexts(texts);
}
}
}
/**
* @memberof Help4.widget.tourlist.Widget#
* @private
* @returns {Promise<void>}
*/
async function _setVisible() {
const {Tour} = Help4.widget.companionCore.data;
const projects = await Tour.getFilteredTourProjects();
if (this.isDestroyed()) return;
this.__visible = projects.length > 0;
}
})();