(function() {
/**
* @typedef {Object} Help4.widget.help.view2.Tile.Params
* @property {Help4.widget.help.Widget} widget
* @property {Help4.widget.help.view.View} view
* @property {Help4.widget.help.view2.TileContainer} container
* @property {Help4.control2.container.Container} contentView
* @property {Help4.control2.container.Container} tileView
* @property {Help4.widget.help.ProjectTile} projectTile
* @property {boolean} whatsnew
* @property {number} [tilePosition = 0]
* @property {boolean} [widgetActive = true]
* @property {?Object} hotspotStatus
* @property {boolean} [stealth = false]
*/
/**
* <catalogueKey>:<catalogueType>:<projectId>:<tileId>
* @typedef {string} Help4.widget.help.view2.Tile.Descriptor
*/
/**
* This can be seen as a Collection: <TileData, ?TileControl, ?HotspotControl, ?BubbleControl, ?LightboxControl>
*
* @augments Help4.jscore.ControlBase
* @property {Help4.widget.help.Widget} __widget
* @property {Help4.widget.help.view.View} __view
* @property {Help4.widget.help.view2.TileContainer} __container
* @property {Help4.control2.container.Container} __contentView
* @property {Help4.control2.container.Container} __tileView
* @property {Help4.widget.help.ProjectTile} projectTile
* @property {Help4.widget.help.view2.Tile.Descriptor} descriptor
* @property {boolean} whatsnew
* @property {number} tilePosition
* @property {boolean} widgetActive
* @property {?Object} hotspotStatus
* @property {boolean} stealth
* @property {?string} tileControlId
* @property {?string} hotspotControlId
* @property {?string} bubbleControlId
* @property {?string} lightboxControlId
* @property {boolean} _blockUpdate
* @property {boolean} _modified
* @property {boolean} _selected
* @property {Function} _onPanelDock
*/
Help4.widget.help.view2.Tile = class extends Help4.jscore.ControlBase {
/**
* @constructor
* @param {Help4.widget.help.view2.Tile.Params} params
*/
constructor(params) {
const onPanelDock = () => _align.call(this);
const {TYPES: T} = Help4.jscore.ControlBase;
super(params, {
params: {
widget: {type: T.instance, mandatory: true, readonly: true, private: true},
view: {type: T.instance, mandatory: true, readonly: true, private: true},
container: {type: T.instance, mandatory: true, readonly: true, private: true},
contentView: {type: T.instance, mandatory: true, readonly: true, private: true},
tileView: {type: T.instance, mandatory: true, readonly: true, private: true},
projectTile: {type: T.object, mandatory: true},
descriptor: {type: T.string, readonly: true}, // will be set below to avoid override by params
whatsnew: {type: T.boolean, readonly: true},
tilePosition: {type: T.number},
widgetActive: {type: T.boolean},
hotspotStatus: {type: T.object_null},
stealth: {type: T.boolean},
// internal
tileControlId: {type: T.string_null},
hotspotControlId: {type: T.string_null},
bubbleControlId: {type: T.string_null},
lightboxControlId: {type: T.string_null}
},
statics: {
_blockUpdate: {init: false, destroy: false},
_modified: {init: false, destroy: false},
_selected: {init: false, destroy: false},
_hovered: {init: false, destroy: false},
_onPanelDock: {init: onPanelDock, destroy: false}
},
config: {
onPropertyChange: ({name, value}) => {
switch (name) {
case 'projectTile':
case 'hotspotStatus':
case 'widgetActive':
case 'stealth':
this._blockUpdate
? this._modified = true
: _update.call(this);
break;
case 'tilePosition':
const {TileTile} = Help4.widget.help.view2;
TileTile.updateTileControlPosition(this);
break;
case 'lightboxControlId':
case 'bubbleControlId':
if (!value) {
// lightbox or bubble have been closed; deselect
const {descriptor} = this;
let type = name.replace(/ControlId/, '');
type = `close${type.charAt(0).toUpperCase()}${type.substring(1)}`;
this._fireEvent({type, descriptor});
}
break;
}
}
}
});
const {view2} = Help4.widget.help;
const descriptor = view2.extractTileDescriptor(params.projectTile);
this.dataFunctions.set({descriptor}, {allowReadonlyOverride: true}); // allow readonly override
_update.call(this);
}
/** @override */
destroy() {
const {__tileView, __contentView, tileControlId, hotspotControlId, lightboxControlId, bubbleControlId} = this;
if (!__tileView.isDestroyed()) {
tileControlId && __tileView.remove(tileControlId);
}
if (!__contentView.isDestroyed()) {
hotspotControlId && __contentView.remove(hotspotControlId);
lightboxControlId && __contentView.remove(lightboxControlId);
bubbleControlId && __contentView.remove(bubbleControlId);
}
super.destroy();
}
/**
* @param {string} controlId
* @returns {Help4.control2.Control}
*/
getControl(controlId) {
return this.__contentView.get(controlId);
}
/**
* @param {Help4.widget.help.ProjectTile} projectTile
* @returns {boolean}
*/
equals(projectTile) {
const {view2} = Help4.widget.help;
return view2.equalTileDescriptors(this.descriptor, view2.extractTileDescriptor(projectTile));
}
/**
* @param {boolean} select
* @returns {boolean}
*/
select(select) {
const {TileTile, TileHotspot} = Help4.widget.help.view2;
this._selected = select;
if (select) {
TileTile.scrollTileIntoView(this);
TileHotspot.scrollHotspotIntoView(this);
}
return _select.call(this);
}
/** @param {boolean} hovered */
wmHover(hovered) {
const {TileHotspot} = Help4.widget.help.view2;
hovered ? TileHotspot.wmHover(this) : TileHotspot.wmLeave(this);
}
/**
* @param {boolean} hovered
* @param {number} [delay = 0]
* @returns {Promise<boolean>|void}
*/
hover(hovered, delay = 0) {
if (hovered || delay === 0) {
// immediate action
const {TileTile} = Help4.widget.help.view2;
const {_selected} = this;
this._hovered = hovered;
hovered && !_selected && TileTile.scrollTileIntoView(this);
_hover.call(this);
} else {
// delayed action
return new Help4.Promise(resolve => {
setTimeout(() => {
if (this.isDestroyed()) return;
const {__contentView, hotspotControlId, bubbleControlId, descriptor} = this;
const hotspot = hotspotControlId && __contentView.get(hotspotControlId);
const bubble = bubbleControlId && __contentView.get(bubbleControlId);
hotspot && bubble && !hotspot.isMouseOver() && !bubble.isMouseOver()
? resolve({descriptor, success: true})
: resolve({descriptor, success: false});
}, delay);
});
}
}
/** @returns {boolean} */
stopHotspotAnimation() {
const {TileHotspot} = Help4.widget.help.view2;
return TileHotspot.stopHotspotAnimation(this);
}
/** causes deselection after bubble close */
closeBubble() {
const {descriptor} = this;
this._fireEvent({type: 'closeBubble', descriptor});
}
/**
* @param {Object} [params = {}] - see {@link Help4.widget.help.view2.Tile.Params}
* @returns {boolean}
*/
update(params = {}) {
// this is a bulk change operation;
// therefore block all control adoptions while the data is just updated
this._blockUpdate = true;
this._modified = false;
/**
* setting this values will cause a sync call to {@link onPropertyChange}
* this._modified will be set to true there, if an update is required
*/
for (const [key, value] of Object.entries(params)) {
if (this.hasOwnProperty(key)) this[key] = value;
}
// if this._modified is still false, no data was changed
const {_modified} = this;
this._blockUpdate = false;
_modified && _update.call(this);
// align controls
_align.call(this);
return _modified;
}
/** @returns {boolean} */
hasHotspot() {
const {view2} = Help4.widget.help;
const {__widget, projectTile} = this;
return view2.hasHotspot(__widget, projectTile);
}
}
/**
* @memberof Help4.widget.help.view2.Tile#
* @private
*/
function _update() {
const {TileHotspot, TileTile, TileBubble} = Help4.widget.help.view2;
if (this.stealth) {
TileHotspot.updateHotspotControl(this);
TileBubble.handleBubble(this);
} else {
const visible = TileHotspot.updateHotspotControl(this); // either no hotspot or it is visible
TileTile.updateTileControl(this, visible);
// notify container about hotspot visibility changes
const {descriptor} = this;
const type = visible ? 'hotspotVisible' : 'hotspotHidden';
this._fireEvent({type, descriptor});
}
}
/**
* @memberof Help4.widget.help.view2.Tile#
* @private
*/
function _align() {
const {TileLightbox, TileBubble} = Help4.widget.help.view2;
TileLightbox.alignLightbox(this);
TileBubble.alignBubble(this);
}
/**
* @memberof Help4.widget.help.view2.Tile#
* @private
* @returns {boolean}
*/
function _select() {
const {TileTile, TileHotspot, TileLightbox, TileBubble} = Help4.widget.help.view2;
// in case widget is inactive: there is no visible tile view
// not need to select any tile as tiles are not available anyway
const {__widget, stealth} = this;
const isActive = __widget.isActive();
const selectionSuccess = isActive ? TileTile.selectTile(this) : true;
if (!stealth) {
// not hotspots and bubbles in stealth mode
TileHotspot.selectHotspot(this);
TileBubble.handleBubble(this);
}
TileLightbox.handleLightbox(this);
return selectionSuccess;
}
/**
* @memberof Help4.widget.help.view2.Tile#
* @private
*/
function _hover() {
const {TileTile, TileHotspot, TileBubble} = Help4.widget.help.view2;
TileTile.selectTile(this);
TileHotspot.selectHotspot(this);
TileBubble.handleBubble(this);
}
})();