(function() {
/** bubble helper for {@link Help4.widget.help.view2.Tile} */
Help4.widget.help.view2.TileBubble = class {
/** @param {Help4.widget.help.view2.Tile} tile */
static handleBubble(tile) {
const {projectTile: {type, _catalogueType}, _selected, _hovered, __contentView, bubbleControlId, stealth} = tile;
if (type !== 'help') return;
const {CATALOGUE_TYPE: GlobalHelp_CATALOGUE_TYPE} = Help4.widget.help.catalogues.GlobalHelp;
const isAlreadyOpen = !!(bubbleControlId && __contentView.get(bubbleControlId));
const isGlobalHelp = _catalogueType === GlobalHelp_CATALOGUE_TYPE;
(_selected || _hovered) && !stealth
? isAlreadyOpen || (isGlobalHelp ? _openGlobalHelp(tile) : _openBubble(tile))
: isAlreadyOpen && this.closeBubble(tile);
}
/** @param {Help4.widget.help.view2.Tile} tile */
static alignBubble(tile) {
const {__contentView, bubbleControlId, hotspotControlId, projectTile} = tile;
const bubble = bubbleControlId && __contentView.get(bubbleControlId);
if (!bubble || bubble.getMetadata('dragdrop')) return; // do not align a bubble that has been moved by the user
let alignment;
const hotspot = hotspotControlId && __contentView.get(hotspotControlId);
const points = /** @type {?Help4.control2.ConnectionPoints} */ hotspot?.visible ? hotspot.getConnectionPoints() : null;
if (points) { // align to hotspot
const {ORI_MAP} = Help4.control.bubble;
const {bubbleOrientation: orientation, bubbleOffset} = projectTile;
let ori = /** @type {string} */ ORI_MAP[orientation]; // XRAY-1446: bubble offset
if (ori && bubbleOffset) {
const modify = points[ori.charAt(0)];
if (modify) {
modify.x += bubbleOffset.left;
modify.y += bubbleOffset.top;
}
}
alignment = bubble.align(points, {ignoreArea: true, orientation});
}
if (alignment) {
bubble.addCss('hotspot'); // specific class in order to beautify hotspot alignment
} else {
// align centered
bubble.removeCss('hotspot'); // specific class in order to beautify hotspot alignment
bubble.align();
}
}
/** @param {Help4.widget.help.view2.Tile} tile */
static closeBubble(tile) {
const {__contentView, bubbleControlId} = tile;
bubbleControlId && __contentView.remove(bubbleControlId);
tile.bubbleControlId = null;
}
}
/**
* @private
* @param {Help4.widget.help.view2.Tile} tile
*/
function _openBubble(tile) {
const {
control: {input: {HtmlEditor}},
Placeholder
} = Help4;
const {__contentView, __widget, projectTile, descriptor} = tile;
const {controller} = __widget.getContext();
const {
_ctx: ctx,
_mac: mac,
_isUR: isUR,
showTitleBar: showCaption,
bubbleSize: size,
bubbleAnimationType: animationType,
language
} = projectTile;
let {
title: caption,
content,
showArrow
} = projectTile;
caption = Placeholder.resolve(caption);
content = HtmlEditor.transformForPlayback({
text: Placeholder.resolve(content),
ctx,
mac,
controller
});
showArrow ??= true;
const zoomService = controller.getService('zoom');
const mlt = controller.getEngine('MLT');
const bubbleParams = {
_metadata: {descriptor, type: 'bubble', isUR},
controlType: 'Help4.widget.help.view2.BubbleControl',
caption,
content,
showArrow,
showCaption,
size,
animationType,
contentLanguage: language,
enableTranslation: !isUR && mlt.isTranslationActive(),
activeTranslation: !isUR && mlt.isTranslationActive(),
zoomService,
autoFocus: false
};
const bubble = /** @type {Help4.widget.help.view2.BubbleControl} */ __contentView.add(bubbleParams);
tile.bubbleControlId = bubble.id;
mlt.registerBubble(bubble);
const {TileBubble} = Help4.widget.help.view2;
bubble
.addListener('translate', () => mlt.onBubbleClick({bubble, active: !bubble.activeTranslation}))
.addListener('destroy', () => mlt.unregisterBubble(bubble))
.addListener('close', () => {
TileBubble.closeBubble(tile);
// fire event to signal a user click to the close button of the bubble
// this is different from other close reasons that are handled in closeBubble
tile._fireEvent({type: 'userCloseBubble', descriptor});
})
.addListener('dragdrop', () => {
bubble.setMetadata('dragdrop', true);
bubble.showArrow = false;
});
Help4.WM.enableVideoTracking(bubble.getDom());
// make sure bubble is properly aligned
TileBubble.alignBubble(tile);
// align again once images and videos are loaded
_alignAfterImageLoad(tile, bubble);
}
/**
* @private
* @param {Help4.widget.help.view2.Tile} tile
*/
function _openGlobalHelp(tile) {
const {
control: {input: {HtmlEditor}},
Placeholder
} = Help4;
const {__contentView, __widget, projectTile, descriptor} = tile;
const {controller} = __widget.getContext();
const {
_ctx: ctx,
_mac: mac,
language
} = projectTile;
let {
title: caption,
content
} = projectTile;
caption = Placeholder.resolve(caption);
content = HtmlEditor.transformForPlayback({
text: Placeholder.resolve(content),
ctx,
mac,
controller
});
const zoomService = controller.getService('zoom');
const mlt = controller.getEngine('MLT');
const bubbleParams = {
_metadata: {descriptor, type: 'bubble'},
controlType: 'Help4.widget.help.view2.GlobalHelpDialogControl',
caption,
content,
contentLanguage: language,
enableTranslation: mlt.isTranslationActive(),
activeTranslation: mlt.isTranslationActive(),
zoomService
};
const bubble = /** @type {Help4.widget.help.view2.GlobalHelpDialogControl} */ __contentView.add(bubbleParams);
tile.bubbleControlId = bubble.id;
mlt.registerBubble(bubble);
const {TileBubble} = Help4.widget.help.view2;
bubble
.addListener('close', () => TileBubble.closeBubble(tile))
.addListener('translate', () => mlt.onBubbleClick({bubble, active: !bubble.activeTranslation}))
.addListener('destroy', () => mlt.unregisterBubble(bubble))
.addListener('dragdrop', () => bubble.setMetadata('dragdrop', true));
// make sure bubble is properly aligned
TileBubble.alignBubble(tile);
}
/**
* @private
* @param {Help4.widget.help.view2.Tile} tile
* @param {Help4.control2.bubble.Bubble} bubble
*/
function _alignAfterImageLoad(tile, bubble) {
const {MediaWatcher} = Help4.jscore;
const dom = bubble.getDom();
MediaWatcher.observe(dom)
.then(result => {
if (result.length && !bubble.isDestroyed()) {
bubble.resetAlignmentCache();
const {TileBubble} = Help4.widget.help.view2;
TileBubble.alignBubble(tile);
}
});
}
})();