(function() {
/**
* @typedef {Help4.control2.Control.Params} Help4.control2.hotspot.Connected.Params
* @property {string} hotspotType - hotspot type, such as "circle" or "rectangle"
*/
/**
* Combines a hotspot with one or more connector lines ("laser beams").
* @augments Help4.control2.Control
* @property {string} hotspotType - hotspot type, such as "circle" or "rectangle"
*/
Help4.control2.hotspot.Connected = class extends Help4.control2.Control {
/**
* @override
* @param {Help4.control2.hotspot.Connected.Params} [params]
* @param {Help4.jscore.ControlBase.Params} [derived]
*/
constructor(params, derived) {
const dfSet = (json, params, dfSet) => {
if (json === this) return this; // is not a JSON but myself
// is not a JSON but another instance of my class
if (json instanceof this.constructor) {
json = json.dataFunctions.toObject();
}
const jsonKeys = Object.keys(json);
const myKeys = this.dataFunctions.getKeys();
const myProperties = {};
const hotspot = this._hotspot;
const hotspotKeys = hotspot ? hotspot.dataFunctions.getKeys() : [];
const hotspotProperties = {};
for (const key of jsonKeys) {
if (myKeys.indexOf(key) >= 0) {
myProperties[key] = json[key];
} else if (hotspotKeys.indexOf(key) >= 0) {
hotspotProperties[key] = json[key];
}
}
dfSet(myProperties, params);
hotspot.dataFunctions.set(hotspotProperties, params);
return this;
}
const T = Help4.jscore.ControlBase.TYPES;
super(params, {
params: {
role: {init: 'button'}, // needs to be in sync with Hotspot.js
tabIndex: {init: 1}, // needs to be in sync with Hotspot.js
hotspotType: {type: T.string, readonly: true, mandatory: true}
},
statics: {
_hotspot: {},
_connections: {},
_hotspotParams: {destroy: false}
},
dataFunctions: {
set: dfSet
},
config: {
css: 'control-connectedhotspot'
},
derived
});
}
/** @returns {Help4.control2.hotspot.Connected} */
cleanConnections() {
this._destroyControl('_connections');
this._connections = null;
return this;
}
/**
* @param {Object} data - parameters for line
* @returns {Help4.control2.Line}
*/
addConnection(data) {
this._connections ||= this._createControl(Help4.control2.container.Container, {
id: this.id + '-c',
type: 'Line',
dom: this.getDom()
});
const {_connections, _hotspot} = this;
const {id, point: point1, ending: ending1, thickness, endingSize, visible = false} = data;
const point2 = _hotspot.calcMeetingPoint(data.point);
return _connections.add({id, point1, point2, ending1, thickness, endingSize, visible});
}
/**
* @param {string} connId
* @returns {Help4.control2.hotspot.Connected}
*/
removeConnection(connId) {
const {_connections} = this;
if (_connections) { // connections will be null if no connection exists
_connections.remove(connId);
if (!_connections.count()) this.cleanConnections();
}
return this;
}
/**
* return position of the connected hotspot control
* @override
*/
getPosition() {
return this._hotspot?.getPosition();
}
/**
* return position of the connected hotspot control
* @override
*/
getDragStartPosition() {
return this._hotspot?.getDragStartPosition();
}
/**
* @override
* @returns {Help4.control2.hotspot.Connected}
*/
focus() {
!this.mobile && this._hotspot?.focus();
return this;
}
/** @override */
getConnectionPoints() {
const {_hotspot} = this;
return _hotspot?.getConnectionPoints.apply(_hotspot, arguments);
}
/**
* @override
* @param {Help4.control2.hotspot.Connected.Params} params - same params as provided to the constructor
*/
_onAfterInit(params) {
super._onAfterInit(params);
this._hotspotParams = params;
this._hotspot = null;
this._connections = null;
}
/** @override */
_onBeforeDestroy() {
this.cleanConnections();
super._onBeforeDestroy();
}
/**
* @override
* @param {HTMLElement} dom - control DOM
*/
_onDomAttached(dom) {
// create hotspot control params
Help4.extendObject(this._hotspotParams, {
_metadata: {connectedHotspotId: this.id},
hotspotType: null,
container: null,
css: null,
id: this.id + '-h',
active: this.active,
rtl: this.rtl,
language: this.language,
mobile: this.mobile,
dom: dom,
// small hack: min-width is misused to signal the width that is consumed by all hotspot borders from CSS
// see this._hotspot.calcMeetingPoint
borderSize: parseInt(getComputedStyle(dom).minWidth) || 0
});
const t = Help4.toFirstUpperCase(this.hotspotType);
const h = this._hotspot = this._createControl(Help4.control2.hotspot[t], this._hotspotParams);
h.addListener('*', (event) => {
this._fireEvent(event);
});
this._hotspotParams = null; // no longer needed
// add missing properties; just as proxy to direct to hotspot
const hotspotKeys = h.dataFunctions.getKeys();
for (const key of hotspotKeys) {
if (!this.hasOwnProperty(key)) _defineProperty(this, h, key);
}
// override my own properties if needed
_defineProperty(this, h, 'active');
this.addCss(this.hotspotType);
super._onDomAttached(dom);
}
/**
* @override
* @param {Help4.jscore.ControlBase.PropertyChangeEvent} event - the change event
*/
_applyPropertyToDom({name, value, oldValue}) {
const hs = this._hotspot;
if (hs && hs.hasOwnProperty(name)) {
const attConfig = hs.dataFunctions.getConfig(name);
if (attConfig.readonly) return;
hs[name] = value;
name === 'visible' && super._applyPropertyToDom({name, value, oldValue}); // XRAY-3603
} else {
super._applyPropertyToDom({name, value, oldValue});
}
}
}
// XRAY-3664: new function is needed to solve issue with IE11
/**
* @memberof Help4.control2.hotspot.Connected#
* @param {Help4.control2.hotspot.Connected} scope
* @param {Help4.control2.hotspot.Hotspot} hotspot
* @param {string} property
* @private
*/
function _defineProperty(scope, hotspot, property) {
// XXX: dataFunctions.onChange tracking most likely not working for this construct
// needs to attach a listener to the hotspot and forward its events through me
Object.defineProperty(scope, property, {
get: () => hotspot[property],
set: value => hotspot[property] = value
});
}
})();