/** * Connection info module */ define([ 'jquery', 'app/init', 'app/util', 'module/base', 'bootbox', 'app/counter', 'app/map/util' ], ($, Init, Util, BaseModule, bootbox, Counter, MapUtil) => { 'use strict'; let ConnectionInfoModule = class ConnectionInfoModule extends BaseModule { constructor(config = {}) { super(Object.assign({}, new.target.defaultConfig, config)); } /** * get HTML id by connectionId * @param connectionId * @returns {string} */ getConnectionElementId(connectionId = 0){ return [ this._config.connectionInfoPanelClass, this._mapId, connectionId ].join('-'); } /** * get all visible connection panel elements * @returns {*|T|{}} */ getConnectionElements(){ return [...this.moduleElement.getElementsByClassName(this._config.connectionInfoPanelClass)] .filter(el => el.id !== this.getConnectionElementId()); } /** * module header * @param text * @returns {HTMLDivElement} */ newHeaderElement(text){ let headEl = super.newHeaderElement(text); let toolbarEl = this.newHeadlineToolbarElement(); let iconMassEl = this.newIconElement([ 'fa-male', 'fa-fw', this._config.moduleHeadlineIconClass, this._config.moduleHeadlineIconCurrentMassClass, this._config.showShip ? 'active' : '' ]); iconMassEl.setAttribute('title', 'toggle current ship mass'); let iconRefreshEl = this.newIconElement([ 'fa-sync', 'fa-fw', this._config.moduleHeadlineIconClass, this._config.moduleHeadlineIconRefreshClass ]); iconRefreshEl.setAttribute('title', 'refresh all'); toolbarEl.append(iconMassEl, iconRefreshEl); headEl.append(toolbarEl); return headEl; } /** * get info control panel element * @param mapId * @returns {HTMLDivElement} */ newInfoPanelControlEl(mapId){ let connectionEl = this.newConnectionElement(mapId); connectionEl.append( this.newControlElement('add connection ctrl + click', [], ['fa-plus']) ); return connectionEl; } /** * get new connection element * @param mapId * @param connectionId * @returns {HTMLDivElement} */ newConnectionElement(mapId, connectionId = 0){ let connectionEl = document.createElement('div'); connectionEl.id = this.getConnectionElementId(connectionId); connectionEl.classList.add(this._config.connectionInfoPanelClass); $(connectionEl).data({ mapId: mapId, connectionId: connectionId }); return connectionEl; } /** * render module * @param mapId * @param connections * @returns {HTMLElement} */ render(mapId, connections){ this._mapId = mapId; let rowEl = document.createElement('div'); rowEl.classList.add(this._config.bodyClassName, 'grid'); rowEl.append(this.newInfoPanelControlEl(mapId)); this.moduleElement.append(rowEl); this.updateConnectionPanels(mapId, MapUtil.getDataByConnections(connections), []); this.setModuleObserver(); return this.moduleElement; } /** * set module observer */ setModuleObserver(){ $(document).off('pf:updateConnectionInfoModule').on('pf:updateConnectionInfoModule', (e, data) => { this.updateConnectionPanels( data.mapId, MapUtil.getDataByConnections(data.connectionsUpdate), MapUtil.getDataByConnections(data.connectionsRemove) ); }); $(document).off('pf:activeShip').on('pf:activeShip', (e) => { $(this.moduleElement).find('.' + this._config.connectionInfoPanelClass).each((i, connectionElement) => { $(connectionElement).find('.' + this._config.moduleTableClass).each((i, tableElement) => { $(tableElement).trigger('pf:calcInfoTable'); }); }); }); // init toggle active ship ---------------------------------------------------------------- $(this.moduleElement).find('.' + this._config.moduleHeadlineIconCurrentMassClass).on('click', e => { let currentMassIcon = $(e.target).toggleClass('active'); $(this.moduleElement).find('.' + this._config.connectionInfoPanelClass).each((i, connectionElement) => { $(connectionElement).find('.' + this._config.moduleTableClass).each((i, tableElement) => { $(tableElement).data('showShip', currentMassIcon.hasClass('active')).trigger('pf:calcInfoTable'); }); }); }); // init refresh connections --------------------------------------------------------------- $(this.moduleElement).find('.' + this._config.moduleHeadlineIconRefreshClass).on('click', e => { this.refreshConnectionPanels(); }); // init tooltips $(this.moduleElement).initTooltips({ html: true }); } /** * refresh all connection panels in a module */ refreshConnectionPanels(){ let connectionsData = this.getConnectionsDataFromModule(); this.updateConnectionPanels(this._mapId, connectionsData.connectionsDataUpdate, connectionsData.connectionsDataRemove); } /** * get connections from ModuleElement * -> validate with current map data * @returns {{connectionsDataUpdate: Array, connectionsDataRemove: Array}} */ getConnectionsDataFromModule(){ let activeMap = Util.getMapModule().getActiveMap(); let mapData = activeMap.getMapDataFromClient(['hasId']); let connectionsData = { connectionsDataUpdate: [], connectionsDataRemove: [], }; if(mapData !== false){ this.getConnectionElements().forEach((connectionElement, i) => { let removeConnectionPanel = true; let connectionData = {id: $(connectionElement).data('connectionId') }; let connection = $().getConnectionById(this._mapId, connectionData.id); if(connection){ let connectionDataTemp = MapUtil.getDataByConnection(connection); if(connectionDataTemp.id > 0){ // connection still on map - OK removeConnectionPanel = false; connectionData = connectionDataTemp; } } if(removeConnectionPanel){ connectionsData.connectionsDataRemove.push(connectionData); }else{ connectionsData.connectionsDataUpdate.push(connectionData); } }); } return connectionsData; } /** * enrich connectionData with "logs" data (if available) and other "missing" data * @param connectionsData * @param newConnectionsData * @returns {*} */ enrichConnectionsData(connectionsData, newConnectionsData){ for(let i = 0; i < connectionsData.length; i++){ for(let newConnectionData of newConnectionsData){ if(connectionsData[i].id === newConnectionData.id){ // copy some missing data connectionsData[i].character = newConnectionData.character; connectionsData[i].created = newConnectionData.created; connectionsData[i].type = newConnectionData.type; // check for mass logs and copy data if(newConnectionData.logs && newConnectionData.logs.length){ connectionsData[i].logs = newConnectionData.logs; } // check for signatures and copy data if(newConnectionData.signatures && newConnectionData.signatures.length){ connectionsData[i].signatures = newConnectionData.signatures; } break; } } } return connectionsData; } /** * @param mapId * @param connectionsData * @returns {Promise} */ getConnectionsLogData(mapId, connectionsData){ let connectionIds = connectionsData.map(connectionData => connectionData.id); // show loading animation for(let connectionId of connectionIds){ let tableEls = this.moduleElement.querySelector('#' + this.getConnectionElementId(connectionId)) .getElementsByTagName('table'); $(tableEls).showLoadingAnimation(); } return this.request('GET', 'Connection', connectionIds, { mapId: mapId, addData : ['signatures', 'logs'], // filterData : ['logs'] // do not exclude connections with NO "logs" -> sig data will be used as well }, { connectionsData: connectionsData }, context => { // hide loading animation for(let contextData of context.connectionsData){ let connectionEl = this.moduleElement.querySelector('#' + this.getConnectionElementId(contextData.id)); // connectionEl might be removed in meantime ( e.g. module removed) if(connectionEl){ let tableEls = connectionEl.getElementsByTagName('table'); $(tableEls).hideLoadingAnimation(); } } }); } /** * replace/insert dataTables log data * @param connectionsData */ addConnectionsData(connectionsData){ let getRowIndexesByData = (tableApi, colName, value) => { return tableApi.rows().eq(0).filter((rowIdx) => { return (tableApi.cell(rowIdx, colName + ':name').data() === value); }); }; for(let connectionData of connectionsData){ // find related dom element for current connection let connectionElement = document.getElementById(this.getConnectionElementId(connectionData.id)); if(connectionElement){ // attach connectionData to connection information for later use ------------------ let connectionInfoElement = $(connectionElement.querySelector('.' + this._config.moduleTableClass)); connectionInfoElement.data('connectionData', connectionData); // update dataTable --------------------------------------------------------------- let tableApi = $(connectionElement).find('.dataTable').dataTable().api(); if(connectionData.logs && connectionData.logs.length > 0){ for(let i = 0; i < connectionData.logs.length; i++){ let rowData = connectionData.logs[i]; let rowNew; let animationStatus = null; let indexes = getRowIndexesByData(tableApi, 'index', rowData.id); if(indexes.length === 0){ // row not found -> add new row rowNew = tableApi.row.add(rowData); animationStatus = 'added'; }else{ // update row with FIRST index let row = tableApi.row(parseInt(indexes[0])); let rowDataCurrent = row.data(); // check if row data changed if(rowDataCurrent.updated.updated !== rowData.updated.updated){ // ... row changed -> delete old and re-add // -> cell actions might have changed row.remove(); rowNew = tableApi.row.add(rowData); animationStatus = 'changed'; } } if( animationStatus !== null && rowNew.length > 0 ){ rowNew.nodes().to$().data('animationStatus', animationStatus); } } }else{ // clear table or leave empty tableApi.clear(); } // redraw dataTable tableApi.draw(false); } } } /** * get connection information element * @param connectionData * @returns {HTMLDivElement} */ getInformationElement(connectionData){ // connection scope ----------------------------------------------------------------------- let scopeLabel = MapUtil.getScopeInfoForConnection(connectionData.scope, 'label'); let element = document.createElement('div'); element.classList.add(BaseModule.Util.config.dynamicAreaClass, this._config.controlAreaClass); $(element).append( $('
| ', { class: ['pf-table-cell-20', 'text-right', Util.config.helpClass, 'pf-pie-chart'].join(' ') }).attr('data-toggle', 'tooltip').attr('data-percent', '-100').easyPieChart({ barColor: (percent) => { let color = '#e28a0d'; if((percent * -1) >= 100){ color = '#a52521'; } return color; }, overrideOptions: 'signed', trackColor: '#5cb85c', size: 14, scaleColor: false, lineWidth: 2, lineCap: 'butt', animate: false }), $(' | ', {
class: ['text-right'].join(' ')
}).attr('colspan', 2).append(
$('', {
class: 'pf-link',
html: connectionData.sourceAlias + ' '
}).on('click', function(){
Util.triggerMenuAction(Util.getMapModule().getActiveMap(), 'SelectSystem', {systemId: connectionData.source});
}),
$('', {
class: [this._config.connectionInfoTableLabelSourceClass].join(' ')
}),
$('', {
class: 'fas fa-fw fa-angle-double-right'
}),
$('', {
class: [this._config.connectionInfoTableLabelTargetClass].join(' ')
}),
$('', {
class: 'pf-link',
html: ' ' + connectionData.targetAlias
}).on('click', function(){
Util.triggerMenuAction(Util.getMapModule().getActiveMap(), 'SelectSystem', {systemId: connectionData.target});
})
)
)
),
$('').append(
$('| ', {
class: ['text-right', Util.config.helpClass, this._config.connectionInfoTableCellMassTotalTooltipClass].join(' '),
html: ''
}),
$(' | ', {
text: scopeLabel.capitalize()
}),
$(' | ', {
class: ['text-right', this._config.connectionInfoTableCellConnectionClass].join(' ')
}).append(
$(' | ', {
class: MapUtil.getConnectionFakeClassesByTypes(connectionData.type).join(' ')
})
)
),
$(' ', {
class: ['text-right', Util.config.helpClass].join(' '),
html: '',
title: 'initial mass. From signature table'
}).attr('data-toggle', 'tooltip'),
$(' | ', {
text: 'Total mass'
}),
$(' | ', {
class: ['text-right', 'txt-color', this._config.connectionInfoTableCellMassTotalClass].join(' ')
})
),
$(' | ', {
class: ['text-right', Util.config.helpClass].join(' '),
title: 'recorded total jump mass'
}).attr('data-toggle', 'tooltip').append(
$('', {
class: [
'fas', 'fa-fw', 'fa-question-circle'
].join(' ')
}),
$('', {
class: [
'fas', 'fa-fw', 'fa-adjust',
'txt-color', 'txt-color-warning',
'hidden'
].join(' ')
}),
$('', {
class: [
'far', 'fa-fw', 'fa-circle',
'txt-color', 'txt-color-danger',
'hidden'
].join(' ')
})
),
$(' | ', {
text: 'Logged mass'
}),
$(' | ', {
class: ['text-right', this._config.connectionInfoTableCellMassLogClass].join(' ')
})
),
$(' | ', {
class: ['text-right', Util.config.helpClass].join(' '),
title: 'current ship mass'
}).attr('data-toggle', 'tooltip').append(
$('', {
class: ['fas', 'fa-fw', 'fa-question-circle'].join(' ')
}),
$('', {
class: [
'fas', 'fa-fw', 'fa-exclamation-triangle',
'txt-color', 'txt-color-danger',
'hidden'
].join(' ')
})
),
$(' | ', {
class: ['pf-table-cell-ellipses-auto'].join(' '),
text: 'Ship mass'
}),
$(' | ', {
class: ['text-right', 'txt-color', this._config.connectionInfoTableCellMassShipClass].join(' ')
})
),
$(' | ', {
class: ['text-right', Util.config.helpClass].join(' '),
html: '',
title: 'max. mass left'
}).attr('data-toggle', 'tooltip'),
$(' | ', {
text: 'Mass left'
}),
$(' | ', {
class: ['text-right', 'txt-color', this._config.connectionInfoTableCellMassLeftClass].join(' ')
})
)
)
).on('pf:updateInfoTable', (e, data) => {
// update information table -------------------------------------------------------
let tableElement = $(e.target);
let connectionData = tableElement.data('connectionData');
if(connectionData){
if(connectionData.scope === 'wh'){
// update signature information -------------------------------------------
let sourceLabelElement = tableElement.find('.' + this._config.connectionInfoTableLabelSourceClass);
let targetLabelElement = tableElement.find('.' + this._config.connectionInfoTableLabelTargetClass);
// get related jsPlumb connection
let connection = $().getConnectionById(data.mapId, data.connectionId);
let signatureTypeNames = MapUtil.getConnectionDataFromSignatures(connection, connectionData);
let sourceLabels = signatureTypeNames.source.labels;
let targetLabels = signatureTypeNames.target.labels;
sourceLabelElement.html(MapUtil.formatEndpointOverlaySignatureLabel(sourceLabels));
targetLabelElement.html(MapUtil.formatEndpointOverlaySignatureLabel(targetLabels));
// remove K162
sourceLabels = sourceLabels.diff(['K162']);
targetLabels = targetLabels.diff(['K162']);
// get static wormhole data by endpoint Labels
let wormholeName = '';
let wormholeData = null;
if(sourceLabels.length === 1 && targetLabels.length === 0){
wormholeName = sourceLabels[0];
}else if(sourceLabels.length === 0 && targetLabels.length === 1){
wormholeName = targetLabels[0];
}
if(
wormholeName &&
Init.wormholes.hasOwnProperty(wormholeName)
){
wormholeData = Object.assign({}, Init.wormholes[wormholeName]);
// init wormhole tooltip ----------------------------------------------
let massTotalTooltipCell = tableElement.find('.' + this._config.connectionInfoTableCellMassTotalTooltipClass);
massTotalTooltipCell.addWormholeInfoTooltip(wormholeData);
}
// all required data is set -> re-calculate rows
tableElement.data('wormholeData', wormholeData);
tableElement.trigger('pf:calcInfoTable');
}
}
}).on('pf:calcInfoTable', e => {
// re-calculate information table from .data() cell values ------------------------
let tableElement = $(e.target);
let connectionData = tableElement.data('connectionData');
let massChartCell = tableElement.find('[data-percent]');
let wormholeData = tableElement.data('wormholeData');
let shipData = null;
let shipName = '';
let showShip = Boolean(tableElement.data('showShip'));
let massLogRow = tableElement.find('.' + this._config.connectionInfoTableRowMassLogClass);
let massShipRow = tableElement.find('.' + this._config.connectionInfoTableRowMassShipClass);
// icons
let massLogTooltipIcon = massLogRow.find('i.fa-question-circle');
let massLogStage2Icon = massLogRow.find('i.fa-adjust');
let massLogStage3Icon = massLogRow.find('i.fa-circle');
let massShipTooltipIcon = massShipRow.find('i.fa-question-circle');
let massShipWarningIcon = massShipRow.find('i.fa-exclamation-triangle');
// table cells
let connectionCell = tableElement.find('.' + this._config.connectionInfoTableCellConnectionClass);
let massTotalCell = tableElement.find('.' + this._config.connectionInfoTableCellMassTotalClass);
let massLogCell = tableElement.find('.' + this._config.connectionInfoTableCellMassLogClass);
let massShipCell = tableElement.find('.' + this._config.connectionInfoTableCellMassShipClass);
let massLeftCell = tableElement.find('.' + this._config.connectionInfoTableCellMassLeftClass);
let massTotal = null; // initial connection mass
let massReduction = 0; // default reduction (e.g. reduced, crit) in percent
let massLog = massLogCell.data('mass'); // recorded mass
let massLogTotal = massLog; // recorded mass + current ship
let massIndividual = null; // mass mass per jump
let massShip = 0; // current ship
let massIndividualError = false;
// get wormhole data from signature binding ---------------------------------------
if(wormholeData){
massTotal = parseInt(wormholeData.massTotal);
massIndividual = parseInt(wormholeData.massIndividual);
}
// get connection type (show fake connection div) ---------------------------------
connectionCell.find('div').removeClass().addClass(MapUtil.getConnectionFakeClassesByTypes(connectionData.type).join(' '));
// get wormhole status ------------------------------------------------------------
if(connectionData.type.indexOf('wh_critical') !== -1){
massReduction = 90;
massLogTooltipIcon.toggleClass('hidden', true);
massLogStage2Icon.toggleClass('hidden', true);
massLogStage3Icon.toggleClass('hidden', false);
massLogStage3Icon.parent().attr('title', 'stage 3 (critical)').tooltip('fixTitle');
}else if(connectionData.type.indexOf('wh_reduced') !== -1){
massReduction = 50;
massLogTooltipIcon.toggleClass('hidden', true);
massLogStage2Icon.toggleClass('hidden', false);
massLogStage3Icon.toggleClass('hidden', true);
massLogStage3Icon.parent().attr('title', 'stage 2 (reduced)').tooltip('fixTitle');
}else{
massLogTooltipIcon.toggleClass('hidden', false);
massLogStage2Icon.toggleClass('hidden', true);
massLogStage3Icon.toggleClass('hidden', true);
massLogStage3Icon.parent().attr('title', 'recorded total jump mass').tooltip('fixTitle');
}
if(massReduction){
let massLogReduction = massTotal / 100 * massReduction;
if(massLogReduction > massLog){
massLog = massLogTotal = massLogReduction;
}
}
// get current ship data ----------------------------------------------------------
massShipCell.parent().toggle(showShip);
if(showShip){
shipData = Util.getObjVal(Util.getCurrentCharacterData('log'), 'ship');
if(shipData){
if(shipData.mass){
massShip = parseInt(shipData.mass);
// check individual mass jump
if(massIndividual){
massIndividualError = massShip > massIndividual;
}
}
if(shipData.typeId && shipData.typeName){
shipName = shipData.typeName;
}
}
}
// update ship mass and "individual mass" cells ----------------------------------
massShipTooltipIcon.toggleClass('hidden', massIndividualError);
massShipWarningIcon.toggleClass('hidden', !massIndividualError);
let shipMassTooltip = 'current ship mass ' + (shipName ? '"' + shipName + '"' : '');
if(massIndividualError){
shipMassTooltip = '"' + shipName + '" exceeds max jump mass for this connection: ' + Util.formatMassValue(massIndividual);
}else{
// current ship mass check is OK -> add to massLogTotal
massLogTotal += massShip;
}
massShipTooltipIcon.parent().attr('title', shipMassTooltip).tooltip('fixTitle');
// current ship mass --------------------------------------------------------------
massShipCell.html( function(){
let cell = $(this);
let value = ' ';
let error = false;
let textLineThrough = false;
if(massShip > 0){
value += Util.formatMassValue(massShip);
if(massIndividualError){
error = textLineThrough = true;
value = ' ' + value;
}else{
value = '-' + value;
}
}else{
error = true;
value = 'undefined';
}
// change cell style
cell.toggleClass('txt-color-red', error)
.toggleClass('txt-color-warning', !error)
.toggleClass('pf-font-line-through', textLineThrough);
return value;
});
// calculate mass left ------------------------------------------------------------
let massLeft = massTotal - massLogTotal;
massLeft = (massLeft < 0) ? 0 : massLeft;
let massPercentLog = (massTotal > 0) ? Math.floor((100 / massTotal) * massLogTotal) : 0;
// update easyPieChart and tooltip ------------------------------------------------
let massPercentLeft = (100 - massPercentLog <= 0) ? 0 : '< ' + (100 - massPercentLog);
massChartCell.data('easyPieChart').enableAnimation().update(massPercentLog * -1);
massChartCell.attr('title', massPercentLeft + '% mass left').tooltip('fixTitle');
// update mass cells --------------------------------------------------------------
massTotalCell.html(massTotal > 0 ? Util.formatMassValue(massTotal) : 'undefined')
.toggleClass('txt-color-red', massTotal <= 0);
massLogCell.html('- ' + Util.formatMassValue(massLog));
massLeftCell.html(
massLeft > 0 ?
'~ ' + Util.formatMassValue(massLeft) :
(massLeft === 0 && massTotal) ?
'will collapse' : 'undefined')
.toggleClass('txt-color-red', massLeft <= 0)
.toggleClass('txt-color-success', massLeft > 0);
})
);
$(element).find('[data-toggle="tooltip"]').tooltip({
container: 'body'
});
return element;
}
/**
* update/init multiple connection panels at once
* @param mapId
* @param connectionsDataUpdate
* @param connectionsDataRemove
*/
updateConnectionPanels(mapId, connectionsDataUpdate, connectionsDataRemove){
for(let connectionData of connectionsDataRemove){
let connectionElement = this.moduleElement.querySelector('#' + this.getConnectionElementId(connectionData.id));
this.removeConnectionPanel(connectionElement);
}
for(let connectionData of connectionsDataUpdate){
this.updateConnectionPanel(mapId, connectionData);
}
// request connectionsLogData for each updated connection
if(connectionsDataUpdate.length){
this.getConnectionsLogData(mapId, connectionsDataUpdate)
//.then(payload => this.addConnectionsData(payload.data))
.then(payload => this.addConnectionsData(
this.enrichConnectionsData(payload.context.connectionsData, payload.data)
))
.catch(payload => {
console.error(payload);
});
}
// remove module if no connection panel left
// --> all connection deselected on map
let connectionElements = this.getConnectionElements();
if(connectionElements.length === 0){
MapUtil.getTabContentElementByMapElement(this.moduleElement).trigger('pf:removeConnectionModules');
}
// hide "control" panel when multiple connection
let connectionEl = this.moduleElement.querySelector('#' + this.getConnectionElementId());
connectionEl.style.display = connectionElements.length < 2 ? 'block' : 'none';
}
/**
* remove connection Panel from moduleElement
* @param connectionElement
*/
removeConnectionPanel(connectionElement){
connectionElement = $(connectionElement);
if(connectionElement.length){
// destroy dataTable (and remove table from DOM)
let logTable = connectionElement.find('.' + this._config.connectionInfoTableClass);
logTable.dataTable().api().destroy(true);
// remove belonging connectionElement
connectionElement.remove();
}
}
/**
* @param mapId
* @param connectionData
*/
updateConnectionPanel(mapId, connectionData){
let module = this;
let rowElement = module.moduleElement.querySelector('.' + module._config.bodyClassName);
let connectionElement = rowElement.querySelector('#' + module.getConnectionElementId(connectionData.id));
if(!connectionElement){
connectionElement = module.newConnectionElement(mapId, connectionData.id);
connectionElement.append(module.getInformationElement(connectionData));
let tableEl = document.createElement('table');
tableEl.classList.add('compact', 'stripe', 'order-column', 'row-border', 'nowrap', module._config.connectionInfoTableClass);
tableEl.insertAdjacentHTML('beforeend', ' | |
|---|