/** * map overlay functions */ define([ 'jquery', 'app/init', 'app/util', 'app/map/overlay/util', 'app/map/util', 'app/lib/cron' ], ($, Init, Util, MapOverlayUtil, MapUtil, Cron) => { 'use strict'; /** * get map object (jsPlumb) from iconElement * @param overlayIcon * @returns {*} */ let getMapObjectFromOverlayIcon = overlayIcon => { return MapUtil.getMapInstance(MapOverlayUtil.getMapElementFromOverlay(overlayIcon).data('id')); }; /** * add/update endpoints with overlays from signature mapping * @param endpoint * @param labelData */ let updateEndpointOverlaySignatureLabel = (endpoint, labelData) => { let labels = labelData.labels; let names = labelData.names; let overlay = endpoint.getOverlay(MapOverlayUtil.config.endpointOverlayId); if(overlay instanceof jsPlumb.Overlays.Label){ // update existing overlay if( !labels.equalValues(overlay.getParameter('signatureLabels')) || !names.equalValues(overlay.getParameter('signatureNames')) ){ // update label only on label changes overlay.setLabel(MapUtil.formatEndpointOverlaySignatureLabel(labels)); overlay.setParameter('fullSize', false); overlay.setParameter('signatureLabels', labels); overlay.setParameter('signatureNames', names); overlay.updateClasses(labels.length ? 'small' : 'icon', labels.length ? 'icon' : 'small'); overlay.setLocation(MapUtil.getEndpointOverlaySignatureLocation(endpoint, labels)); } }else{ // add new overlay endpoint.addOverlay([ 'Label', { label: MapUtil.formatEndpointOverlaySignatureLabel(labels), id: MapOverlayUtil.config.endpointOverlayId, cssClass: [MapOverlayUtil.config.componentOverlayClass, labels.length ? 'small' : 'icon'].join(' '), location: MapUtil.getEndpointOverlaySignatureLocation(endpoint, labels), events: { toggleSize: function(fullSize){ let signatureNames = this.getParameter('signatureNames'); if(fullSize && !this.getParameter('fullSize') && signatureNames.length){ this.setLabel(this.getLabel() + '
' + '' + signatureNames.join(', ') + ''); this.setParameter('fullSize', true); }else if(this.getParameter('fullSize')){ this.setLabel(MapUtil.formatEndpointOverlaySignatureLabel(this.getParameter('signatureLabels'))); this.setParameter('fullSize', false); } } }, parameters: { fullSize: false, signatureLabels: labels, signatureNames: names } } ]); } }; /** * get overlay parameters for connection overlay (type 'diamond' or 'arrow') * @param overlayType * @param direction * @param prefix for obj keys -> for parameterized Overlays * @returns {{length: number, foldback: number, direction: number}} */ let getConnectionArrowOverlayParams = (overlayType, direction = 1, prefix = 'arrow') => { switch(overlayType){ case 'arrow': return { [`${prefix}length`]: 15, [`${prefix}direction`]: direction, [`${prefix}foldback`]: 0.8 }; default: // diamond return { [`${prefix}length`]: 10, [`${prefix}direction`]: 1, [`${prefix}foldback`]: 2 }; } }; /** * add overlays to connections (signature based data) * @param map * @param connectionsData */ let updateInfoSignatureOverlays = (map, connectionsData) => { let type = 'info_signature'; connectionsData = Util.arrayToObject(connectionsData); map.setSuspendDrawing(true); // ... redraw should be suspended (no repaint until function ends) let doNotRepaint = map.isSuspendDrawing(); map.getAllConnections().forEach(connection => { let connectionId = connection.getParameter('connectionId'); let sourceEndpoint = connection.endpoints[0]; let targetEndpoint = connection.endpoints[1]; let connectionData = Util.getObjVal(connectionsData, `${connectionId}`); let signatureTypeData = MapUtil.getConnectionDataFromSignatures(connection, connectionData); let sizeLockedBySignature = false; if(connection.scope === 'wh'){ /* TODO check if we still need this, commented out after jsPlumb `v2.9.3` → `v2.13.1` upgrade if(!connection.hasType(type)){ connection.addType(type); } let overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId); // Arrow overlay needs to be cleared() (removed) if 'info_signature' gets removed! // jsPlumb does not handle overlay updates for Arrow overlays... so we need to re-apply the overlay manually if(overlayArrow.path && !overlayArrow.path.isConnected){ connection.canvas.appendChild(overlayArrow.path); } */ // since there "could" be multiple sig labels on each endpoint, // there can only one "primary label picked up for wormhole jump mass detection! let primeLabel; let overlayType = 'diamond'; // not specified let arrowDirection = 1; if(connectionData && connectionData.signatures){ // signature data found for current connection let sourceLabel = signatureTypeData.source.labels; let targetLabel = signatureTypeData.target.labels; // add arrow (connection) overlay that points from "XXX" => "K162" ---------------------------- if( (sourceLabel.includes('K162') && targetLabel.includes('K162')) || (sourceLabel.length === 0 && targetLabel.length === 0) || ( sourceLabel.length > 0 && targetLabel.length > 0 && !sourceLabel.includes('K162') && !targetLabel.includes('K162') ) ){ // unknown direction -> show default 'diamond' overlay overlayType = 'diamond'; }else if( (sourceLabel.includes('K162')) || (sourceLabel.length === 0 && !targetLabel.includes('K162')) ){ // convert default arrow direction overlayType = 'arrow'; arrowDirection = -1; primeLabel = targetLabel.find(label => label !== 'K162'); }else{ // default arrow direction is fine overlayType = 'arrow'; primeLabel = sourceLabel.find(label => label !== 'K162'); } } // class changes must be done on "connection" itself not on "overlayArrow" // -> because Arrow might not be rendered to map at this point (if it does not exist already) if(overlayType === 'arrow'){ connection.updateClasses( MapOverlayUtil.config.connectionArrowOverlaySuccessClass, MapOverlayUtil.config.connectionArrowOverlayDangerClass ); }else{ connection.updateClasses( MapOverlayUtil.config.connectionArrowOverlayDangerClass, MapOverlayUtil.config.connectionArrowOverlaySuccessClass ); } let arrowParams = getConnectionArrowOverlayParams(overlayType, arrowDirection); if(!connection.hasType(type)){ connection.addType(type, arrowParams, doNotRepaint); }else{ connection.reapplyTypes(arrowParams, doNotRepaint); } // update/add endpoint overlays ------------------------------------------------------------------- updateEndpointOverlaySignatureLabel(sourceEndpoint, signatureTypeData.source); updateEndpointOverlaySignatureLabel(targetEndpoint, signatureTypeData.target); // fix/overwrite existing jump mass connection type ----------------------------------------------- // if a connection type for "jump mass" (e.g. S, M, L, XL) is set for this connection // we should check/compare it with the current primary signature label from signature mapping // and change it if necessary if(Init.wormholes.hasOwnProperty(primeLabel)){ // connection size from mapped signature sizeLockedBySignature = true; // get 'connection mass type' from wormholeData let massType = Util.getObjVal(Object.assign({}, Init.wormholes[primeLabel]), 'size.type'); if(massType && !connection.hasType(massType)){ MapOverlayUtil.getMapOverlay(connection.canvas, 'timer').startMapUpdateCounter(); MapUtil.setConnectionJumpMassType(connection, massType); MapUtil.markAsChanged(connection); } } }else{ // connection is not 'wh' scope if(connection.hasType(type)){ connection.removeType(type, undefined, doNotRepaint); } } // lock/unlock connection for manual size changes (from contextmenu) connection.setParameter('sizeLocked', sizeLockedBySignature); }); map.setSuspendDrawing(false, true); }; /** * hide default icon and replace it with "loading" icon * @param iconElement */ let showLoading = iconElement => { iconElement = $(iconElement); let dataName = 'default-icon'; let defaultIconClass = iconElement.data(dataName); // get default icon class if( !defaultIconClass ){ // index 0 == 'fa-fw', index 1 == IconName defaultIconClass = $(iconElement).attr('class').match(/\bfa-\S*/g)[1]; iconElement.data(dataName, defaultIconClass); } iconElement.toggleClass( defaultIconClass + ' fa-sync fa-spin'); }; /** * hide "loading" icon and replace with default icon * @param iconElement */ let hideLoading = iconElement => { iconElement = $(iconElement); let dataName = 'default-icon'; let defaultIconClass = iconElement.data(dataName); iconElement.toggleClass( defaultIconClass + ' fa-sync fa-spin'); }; /** * get overlay icon from e.g. mapElement * @param element * @param iconClass * @param overlayType * @returns {*} */ let getOverlayIcon = (element, iconClass, overlayType = 'info') => { return MapOverlayUtil.getMapOverlay(element, overlayType).find('.' + iconClass); }; /** * showInfoSignatureOverlays * -> used by "refresh" overlays (hover) AND/OR initial menu trigger */ let showInfoSignatureOverlays = mapElement => { let mapId = mapElement.data('id'); let map = MapUtil.getMapInstance(mapId); let mapData = Util.getCurrentMapData(mapId); let connectionsData = Util.getObjVal(mapData, 'data.connections'); if(connectionsData){ let overlayIcon = getOverlayIcon(mapElement, options.connectionSignatureOverlays.class); showLoading(overlayIcon); updateInfoSignatureOverlays(map, connectionsData); hideLoading(overlayIcon); } }; /** * hideInfoSignatureOverlays * -> see showInfoSignatureOverlays() */ let hideInfoSignatureOverlays = mapElement => { let mapId = mapElement.data('id'); let map = MapUtil.getMapInstance(mapId); let type = 'info_signature'; map.setSuspendDrawing(true); map.getAllConnections().forEach(connection => { let overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId); if(overlayArrow){ overlayArrow.cleanup(); } if(connection.hasType(type)){ connection.removeType(type, {}, true); } }); map.selectEndpoints().removeOverlay(MapOverlayUtil.config.endpointOverlayId); map.setSuspendDrawing(false, true); }; /** * callback after applying map option "systemRegion" * -> system dimension changed -> redraw connections * @param mapElement */ let toggleInfoSystemRegion = mapElement => { let mapId = mapElement.data('id'); let map = MapUtil.getMapInstance(mapId); map.repaintEverything(); }; /** * Overlay options (all available map options shown in overlay) * "active": (active || hover) indicated whether an icon/option * is marked as "active". * "active": Makes icon active when visible * "hover": Make icon active on hover */ let options = { filter: { title: 'active filter', trigger: 'active', class: 'pf-map-overlay-filter', iconClass: ['fas', 'fa-fw', 'fa-filter'], onClick: function(e){ // clear all filter let mapElement = MapOverlayUtil.getMapElementFromOverlay(this); let map = getMapObjectFromOverlayIcon(this); Util.getLocalStore('map').setItem(`${mapElement.data('id')}.filterScopes`, []); MapUtil.filterMapByScopes(map, []); } }, mapSnapToGrid: { title: 'grid', trigger: 'active', class: 'pf-map-overlay-grid', iconClass: ['fas', 'fa-fw', 'fa-th'] }, mapMagnetizer: { title: 'magnetizer', trigger: 'active', class: 'pf-map-overlay-magnetizer', iconClass: ['fas', 'fa-fw', 'fa-magnet'] }, systemRegion: { title: 'regions', trigger: 'active', class: 'pf-map-overlay-region', iconClass: ['fas', 'fa-fw', 'fa-map-marked-alt'] }, systemCompact: { title: 'compact layout', trigger: 'active', class: 'pf-map-overlay-compact', iconClass: ['fas', 'fa-fw', 'fa-compress'] }, connectionSignatureOverlays: { title: 'signature overlays', trigger: 'active', class: 'pf-map-overlay-endpoint', iconClass: ['fas', 'fa-fw', 'fa-link'] }, systemPopover: { title: 'sovereignty', trigger: 'hover', class: 'pf-map-overlay-popover', iconClass: ['fas', 'fa-fw', 'fa-landmark'], hoverIntent: { over: function(e){ let mapElement = MapOverlayUtil.getMapElementFromOverlay(this); mapElement.find('.' + MapOverlayUtil.config.systemHeadClass).each(function(){ let systemHead = $(this); // init popover if not already exists if(!systemHead.data('bs.popover')){ let system = systemHead.parent(); let systemData = system.data(); let sovereignty = Util.getObjVal(systemData, 'sovereignty'); if(sovereignty){ systemHead.popover({ placement: 'bottom', html: true, trigger: 'manual', container: mapElement, title: false, content: Util.getSystemSovereigntyTable(sovereignty) }); } } systemHead.setPopoverSmall(); systemHead.popover('show'); }); }, out: function(e){ let mapElement = MapOverlayUtil.getMapElementFromOverlay(this); mapElement.find('.' + MapOverlayUtil.config.systemHeadClass).popover('hide'); } } }, connection: { title: 'WH data', trigger: 'hover', class: 'pf-map-overlay-connection-wh', iconClass: ['fas', 'fa-fw', 'fa-fighter-jet'], hoverIntent: { over: function(e){ let map = getMapObjectFromOverlayIcon(this); let connections = MapUtil.searchConnectionsByScopeAndType(map, 'wh'); let serverDate = Util.getServerTime(); // show connection overlays ----------------------------------------------------------------------- for(let connection of connections){ let createdTimestamp = connection.getParameter('created'); let updatedTimestamp = connection.getParameter('updated'); let createdDate = Util.convertTimestampToServerTime(createdTimestamp); let updatedDate = Util.convertTimestampToServerTime(updatedTimestamp); let createdDiff = Util.getTimeDiffParts(createdDate, serverDate); let updatedDiff = Util.getTimeDiffParts(updatedDate, serverDate); // format overlay label let labels = [ Util.formatTimeParts(createdDiff) + ' ', Util.formatTimeParts(updatedDiff) + ' ' ]; // add label overlay -------------------------------------------------------------------------- connection.addOverlay([ 'Label', { label: labels.join('
'), id: MapOverlayUtil.config.connectionOverlayWhId, cssClass: [MapOverlayUtil.config.componentOverlayClass, 'small', 'text-right'].join(' '), location: 0.35 } ]); } }, out: function(e){ let map = getMapObjectFromOverlayIcon(this); let connections = MapUtil.searchConnectionsByScopeAndType(map, 'wh'); for(let connection of connections){ connection.removeOverlay(MapOverlayUtil.config.connectionOverlayWhId); } } } }, connectionEol: { title: 'EOL', trigger: 'hover', class: 'pf-map-overlay-connection-eol', iconClass: ['fas', 'fa-fw', 'fa-hourglass-end'], hoverIntent: { over: function(e){ let map = getMapObjectFromOverlayIcon(this); let connections = MapUtil.searchConnectionsByScopeAndType(map, 'wh', ['wh_eol']); let serverDate = Util.getServerTime(); for(let connection of connections){ let eolTimestamp = connection.getParameter('eolUpdated'); let eolDate = Util.convertTimestampToServerTime(eolTimestamp); let diff = Util.getTimeDiffParts(eolDate, serverDate); connection.addOverlay([ 'Label', { label: ' ' + Util.formatTimeParts(diff), id: MapOverlayUtil.config.connectionOverlayEolId, cssClass: [MapOverlayUtil.config.componentOverlayClass, 'eol'].join(' '), location: 0.25 } ]); } }, out: function(e){ let map = getMapObjectFromOverlayIcon(this); let connections = MapUtil.searchConnectionsByScopeAndType(map, 'wh', ['wh_eol']); for(let connection of connections){ connection.removeOverlay(MapOverlayUtil.config.connectionOverlayEolId); } } } } }; /** * draws the map update counter to the map overlay timer * @param mapOverlayTimer * @param percent * @param value */ let setMapUpdateCounter = (mapOverlayTimer, percent, value = '') => { // check if counter already exists if(!MapOverlayUtil.getMapCounter(mapOverlayTimer)){ // create new counter let chartEl = Object.assign(document.createElement('div'), { className: `${Init.classes.pieChart.class} ${Init.classes.pieChart.pieChartMapCounterClass}` }); let chartInnerEl = Object.assign(document.createElement('span'), { textContent: value }); let iconEl = Object.assign(document.createElement('i'), { className: ['fas', 'fa-fw', 'fa-lock'].join(' ') }); chartInnerEl.append(iconEl); chartEl.append(chartInnerEl); mapOverlayTimer.append(chartEl); // init counter $(chartEl).initMapUpdateCounter(); // set tooltip mapOverlayTimer.dataset.placement = 'left'; mapOverlayTimer.setAttribute('title', 'update counter'); $(mapOverlayTimer).tooltip(); } }; /** * start the map update counter or reset */ $.fn.startMapUpdateCounter = function(){ if(!this.length){ console.warn('startMapUpdateCounter() failed. Missing DOM node'); return; } let mapOverlayTimer = this[0]; let counterChart = MapOverlayUtil.getMapCounter(mapOverlayTimer); let pieChart = $(counterChart).data('easyPieChart'); if(!pieChart){ console.warn('startMapUpdateCounter() failed. easyPieChart not initialized'); return; } let updateChart = (percent = 0) => { if(pieChart){ pieChart.update(percent); } }; let task = counterChart.getData('counterTask'); if(!task){ let tabContentEl = mapOverlayTimer.closest(`.${Util.config.mapTabContentClass}`); let mapId = parseInt(tabContentEl.dataset.mapId) || 0; task = Cron.new(`mapUpdateCounter_${mapId}`, { precision: 'secondTenths', isParallel: true, targetRunCount: 10 * MapOverlayUtil.config.logTimerCount }); task.task = (timer, task) => { // debounce 80% (reduce repaint) if(task.runCount % 5 === 0){ let progress = Math.round(task.targetProgress); updateChart(100 - progress); } if(task.targetAchieved){ $(mapOverlayTimer).velocity('transition.whirlOut', { duration: Init.animationSpeed.mapOverlay, complete: function(){ MapOverlayUtil.getMapElementFromOverlay(mapOverlayTimer).trigger('pf:unlocked'); } }); } }; counterChart.setData('counterTask', task); } // task is not connected if: 'targetAchieved' or not started if(!task.isConnected()){ $(mapOverlayTimer).velocity('stop').velocity('transition.whirlIn', { duration: Init.animationSpeed.mapOverlay }); } updateChart(100); task.reset(); task.start(); }; /** * update (show/hide) a overlay icon in the "info"-overlay * show/hide the overlay itself is no icons are visible * @param option * @param viewType */ $.fn.updateOverlayIcon = function(option, viewType){ let mapOverlayInfo = $(this); let showOverlay = false; let mapOverlayIconClass = options[option].class; // look for the overlay icon that should be updated let iconElement = mapOverlayInfo.find('.' + mapOverlayIconClass); if(iconElement){ if(viewType === 'show'){ showOverlay = true; // check "trigger" and mark as "active" if( options[option].trigger === 'active' || options[option].trigger === 'refresh' ){ iconElement.addClass('active'); } // check if icon is not already visible // -> prevents unnecessary "show" animation if( !iconElement.data('visible') ){ // display animation for icon iconElement.velocity({ opacity: [0.8, 0], scale: [1, 0], width: ['20px', 0], height: ['20px', 0], marginRight: ['10px', 0] },{ duration: 240, easing: 'easeInOutQuad' }); iconElement.data('visible', true); } }else if(viewType === 'hide'){ // check if icon is not already visible // -> prevents unnecessary "hide" animation if(iconElement.data('visible')){ iconElement.removeClass('active').velocity('reverse'); iconElement.data('visible', false); } // check if there is any visible icon remaining let visibleIcons = mapOverlayInfo.find('i:visible'); if(visibleIcons.length > 0){ showOverlay = true; } } } // show the entire overlay if there is at least one active icon if( showOverlay === true && mapOverlayInfo.is(':hidden') ){ // show overlay mapOverlayInfo.velocity('stop').velocity('transition.whirlIn', { duration: Init.animationSpeed.mapOverlay }); }else if( showOverlay === false && mapOverlayInfo.is(':visible') ){ // hide overlay mapOverlayInfo.velocity('stop').velocity('transition.whirlOut', { duration: Init.animationSpeed.mapOverlay }); } }; /** * update map zoom overlay information * @param map */ let updateZoomOverlay = map => { let zoom = map.getZoom(); let zoomPercent = Math.round(zoom * 1000) / 10; let zoomOverlay = MapOverlayUtil.getMapOverlay(map.getContainer(), 'zoom'); let zoomValue = zoomOverlay.find('.' + MapOverlayUtil.config.zoomOverlayValueClass); let zoomUp = zoomOverlay.find('.' + MapOverlayUtil.config.zoomOverlayUpClass); let zoomDown = zoomOverlay.find('.' + MapOverlayUtil.config.zoomOverlayDownClass); zoomValue.toggleClass('active', zoom !== 1).text(zoomPercent); zoomUp.toggleClass('disabled', zoom >= MapUtil.config.zoomMax); zoomDown.toggleClass('disabled', zoom <= MapUtil.config.zoomMin); }; /** * map debug overlays for connections/endpoints * -> requires manual added "debug" GET param to URL * @param map */ let initMapDebugOverlays = map => { let url = new URL(window.location.href); if(url.searchParams.has('debug')){ let mapContainer = $(map.getContainer()); // debug map overlays for connection/endpoints mapContainer.on('mouseover', '.jtk-connector', function(e){ e.stopPropagation(); if(e.target.classList.contains('jtk-connector-outline')){ let connection = e.currentTarget._jsPlumb; // show debug overlay only if there is no active debug if(!connection.getOverlay(MapOverlayUtil.config.debugOverlayId)){ // find nearby connections let connections = []; let endpoints = []; let hasComponentId = id => { return component => component.id === id; }; for(let endpoint of connection.endpoints){ let connectionsInfo = map.anchorManager.getConnectionsFor(endpoint.elementId); for(let connectionInfo of connectionsInfo){ if(!connections.some(hasComponentId(connectionInfo[0].id))){ connections.push(connectionInfo[0]); for(let endpointTemp of connectionInfo[0].endpoints){ if(!endpoints.some(hasComponentId(endpointTemp.id))){ endpoints.push(endpointTemp); } } } } } let createConnectionOverlay = connection => { let data = MapUtil.getDataByConnection(connection); let html = '
'; html += ''; html += ''; html += ''; html += '
' + connection.id + '' + data.id + '
Scope:' + data.scope + '
Type:' + data.type.toString() + '
'; return $(html).on('click', function(){ console.info(connection); }); }; for(let connection of connections){ connection.addOverlay([ 'Custom', { id: MapOverlayUtil.config.debugOverlayId, cssClass: [MapOverlayUtil.config.componentOverlayClass, 'debug'].join(' '), create: createConnectionOverlay } ]); } let createEndpointOverlay = endpoint => { let types = MapUtil.filterDefaultTypes(endpoint.getType()); let html = '
'; html += ''; html += ''; html += ''; html += '
' + endpoint.id + '
Scope:' + endpoint.scope + '
Type:' + types.toString() + '
'; return $(html).on('click', function(){ console.info(endpoint); }); }; for(let endpoint of endpoints){ endpoint.addOverlay([ 'Custom', { id: MapOverlayUtil.config.debugOverlayId, cssClass: [MapOverlayUtil.config.componentOverlayClass, 'debug'].join(' '), create: createEndpointOverlay } ]); } } } }); mapContainer.on('mouseover', function(e){ e.stopPropagation(); if(e.target === e.currentTarget){ map.select().removeOverlay(MapOverlayUtil.config.debugOverlayId); map.selectEndpoints().removeOverlay(MapOverlayUtil.config.debugOverlayId); } }); } }; /** * init map zoom overlay * @returns {jQuery} */ let initZoomOverlay = () => { let clickHandler = e => { let zoomIcon = $(e.target); if(!zoomIcon.hasClass('disabled')){ let zoomAction = zoomIcon.attr('data-zoom'); let map = getMapObjectFromOverlayIcon(zoomIcon); MapUtil.changeZoom(map, zoomAction); } }; return $('
', { class: [MapOverlayUtil.config.mapOverlayClass, MapOverlayUtil.config.mapOverlayZoomClass].join(' ') }).append( $('', { class: ['fas', 'fa-caret-up', MapOverlayUtil.config.zoomOverlayUpClass].join(' ') }).attr('data-zoom', 'up').on('click', clickHandler), $('', { class: MapOverlayUtil.config.zoomOverlayValueClass, text: '100' }), $('', { class: ['fas', 'fa-caret-down', MapOverlayUtil.config.zoomOverlayDownClass].join(' ') }).attr('data-zoom', 'down').on('click', clickHandler) ); }; /** * init all map overlays on a "parent" element * @returns {*} */ $.fn.initMapOverlays = function(){ return this.each(function(){ let parentElement = $(this); let mapOverlayTimer = $('
', { class: [MapOverlayUtil.config.mapOverlayClass, MapOverlayUtil.config.mapOverlayTimerClass].join(' ') }); parentElement.append(mapOverlayTimer); parentElement.append(initZoomOverlay()); // -------------------------------------------------------------------------------------------------------- // add map overlay info. after scrollbar is initialized let mapOverlayInfo = $('
', { class: [MapOverlayUtil.config.mapOverlayClass, MapOverlayUtil.config.mapOverlayInfoClass].join(' ') }); // add all overlay elements Object.entries(options).forEach(([key, option]) => { let icon = $('', { class: option.iconClass.concat(['pull-right', option.class]).join(' ') }).attr('title', option.title).tooltip({ placement: 'bottom', container: 'body', delay: 150 }); // add "hover" action for some icons if( option.trigger === 'hover' || option.trigger === 'refresh' ){ icon.hoverIntent(option.hoverIntent); } // add "click" handler for some icons if(option.hasOwnProperty('onClick')){ icon.on('click', option.onClick); } mapOverlayInfo.append(icon); }); parentElement.append(mapOverlayInfo); // reset map update timer setMapUpdateCounter(mapOverlayTimer[0], 100); }); }; return { showInfoSignatureOverlays, hideInfoSignatureOverlays, toggleInfoSystemRegion, updateZoomOverlay, initMapDebugOverlays }; });