Files
pathfinder/js/app/ui/module/global_thera.js
Sam 5efad1da50 2.2.4 (#216)
* changes for eve-scout API

* updates composer for new pathfinder-esi version

* updates public files

* updates signature and jump info

* adds public files

* typo fix: Turnur, not Turner

Signed-off-by: Tyr Heimdal <tyr.heimdal@warning.no>

---------

Signed-off-by: Tyr Heimdal <tyr.heimdal@warning.no>
Co-authored-by: Tyr Heimdal <tyr.heimdal@warning.no>
2024-04-01 15:38:01 +13:00

1025 lines
50 KiB
JavaScript

define([
'jquery',
'app/init',
'app/util',
'module/base',
'app/map/map',
'app/map/util',
'app/ui/form_element'
], ($, Init, Util, BaseModule, Map, MapUtil, FormElement) => {
'use strict';
/**
* TheraModule class
* @type {TheraModule}
*/
let TheraModule = class TheraModule extends BaseModule {
constructor(config = {}) {
config.eveScoutUrl = new URL(Init.url.eveScout);
super(Object.assign({}, new.target.defaultConfig, config));
}
/**
* get icon for undefined/missing cell value
* @returns {string}
*/
getIconForUndefinedCellValue(){
return '<i class="fas fa-question txt-color txt-color-orangeDark"></i>';
}
/**
* get icon for "syncStatus" (connection is mapped) cell value
* @param status
* @returns {string}
*/
getIconForStatusCellValue(status = ''){
let title = '';
switch(status){
case 'warning':
title = `not in ${this._config.eveScoutUrl.hostname.replace('www.', '')} connections`;
break;
case 'success':
title = `in ${this._config.eveScoutUrl.hostname.replace('www.', '')} connections`;
break;
case 'hint':
title += `sync connections/signatures`;
break;
default:
title = `not mapped`;
}
return `<i class="fas fa-circle txt-color txt-color-${status}" title="${title}"></i>`;
}
/**
* get dataTable id
* @param {...string} parts e.g. 'tableType', 'mapId', 'systemId'
* @returns {string}
*/
getTableId(...parts){
return BaseModule.Util.getTableId(this._config.theraTableId, ...parts);
}
/**
* get dataTable row id
* @param tableType
* @param id
* @returns {string}
*/
getRowId(tableType, id){
return BaseModule.Util.getTableRowId(this._config.theraTableRowIdPrefix, tableType, id);
}
/**
* get <tr> DOM id by id
* @param tableApi
* @param id
* @returns {*}
*/
getRowById(tableApi, id){
return tableApi.rows().ids().toArray().find(rowId =>
rowId === this.getRowId(BaseModule.Util.getObjVal(this.getTableMetaData(tableApi), 'type'), id)
);
}
/**
* get custom "metaData" from dataTables API
* @param tableApi
* @returns {*}
*/
getTableMetaData(tableApi){
return tableApi ? tableApi.init().pfMeta : null;
}
/**
* initial module render method
* -> implementation is enforced by BaseModule
* -> must return a single node element
* @param mapId
* @returns {HTMLElement}
*/
render(mapId){
this._mapId = mapId;
// ... append your custom module body
this._bodyEl = Object.assign(document.createElement('div'), {
className: this._config.bodyClassName
});
this.moduleElement.append(this._bodyEl);
this._bodyEl.append(
this.newControlElement(
'Thera not found on map. Click here to add',
[this._config.moduleHeadlineIconClass, this._config.controlAreaTheraClass, 'hidden'],
['fa-plus']
)
);
$(this.moduleElement).showLoadingAnimation();
this.initTheraTable();
this.setModuleObserver();
return this.moduleElement;
}
/**
* init 'Thera connections' table (EveScout)
*/
initTheraTable(){
let module = this;
let tableEl = document.createElement('table');
tableEl.id = module.getTableId('thera', module._mapId);
tableEl.classList.add('compact', 'stripe', 'order-column', 'row-border', 'pf-table-fixed', module._config.globalTheraTableClass);
this._bodyEl.append(tableEl);
this._tableApi = $(tableEl).DataTable( {
pfMeta: {
type: 'theraConnection'
},
paging: false,
ordering: true,
order: [[0, 'desc'], [8, 'desc']],
orderFixed: [[ 10, 'desc']],
info: false,
searching: false,
hover: false,
autoWidth: false,
rowId: rowData => module.getRowId('theraConnection', rowData.id),
select: {
style: 'os',
selector: 'td:not(.' + module._config.tableCellActionClass + ')'
},
language: {
emptyTable: 'No Thera connections found'
},
columnDefs: [
{
targets: 0,
name: 'syncStatus',
title: '',
width: 2,
className: 'text-center',
data: 'syncStatus',
defaultContent: module.getIconForStatusCellValue(),
render: {
display: (cellData, type, rowData, meta) => {
return module.getIconForStatusCellValue(cellData);
},
sort: syncStatus => ['warning', 'hint', 'success'].indexOf(syncStatus)
}
},{
targets: 1,
name: 'trueSec',
title: 'sec',
width: 10,
className:'text-center',
data: 'target.system_class',
defaultContent: module.getIconForUndefinedCellValue(),
render: {
display: (cellData, type, rowData, meta) => {
if(cellData !== undefined){
let systemTrueSecClass = BaseModule.Util.getTrueSecClassForSystem(cellData);
return '<span class="' + systemTrueSecClass + '">' + cellData.toFixed(1) + '</span>';
return cellData
}
}
}
},{
targets: 2,
name: 'systemName',
title: 'system',
className: module._config.tableCellEllipsisClass,
data: 'target',
defaultContent: module.getIconForUndefinedCellValue(),
render: {
display: (cellData, type, rowData, meta) => {
let systemId = cellData.id || '';
let cls = (
cellData.name && (
BaseModule.Util.getObjVal(rowData, 'relRowId') || // secondary table
BaseModule.Util.getObjVal(rowData, 'connectionId') // primary table
)
) ? module._config.linkClass : '';
return `<span data-systemid="${systemId}" class="${cls}">${cellData.name || ''}</span>`;
},
sort: 'name'
}
},{
targets: 3,
name: 'region',
title: 'region',
className: module._config.tableCellEllipsisClass,
data: 'target',
defaultContent: module.getIconForUndefinedCellValue(),
render: {
_: 'region.name'
}
},{
targets: 4,
name: 'outSig',
title: '<i title="Out signature" data-toggle="tooltip" class="fas fa-sign-out-alt fa-rotate-270"></i>',
width: 20,
className: ['text-center', module._config.fontUppercaseClass].join(' '),
data: 'sourceSignature',
defaultContent: module.getIconForUndefinedCellValue(),
render: {
_: 'short_name'
}
},{
targets: 5,
name: 'fakeConnection',
title: 'con.',
orderable: false,
width: 30,
className: 'text-center',
data: 'fakeConnection',
defaultContent: ''
},{
targets: 6,
name: 'inSig',
title: '<i title="In signature" data-toggle="tooltip" class="fas fa-sign-in-alt fa-rotate-90"></i>',
width: 20,
className: ['text-center', module._config.fontUppercaseClass].join(' '),
data: 'targetSignature',
defaultContent: module.getIconForUndefinedCellValue(),
render: {
_: 'short_name'
}
},{
targets: 7,
name: 'wormholeLabel',
title: 'type',
width: 50,
className: module._config.tableCellTypeClass,
data: 'wormholeLabel',
defaultContent: module.getIconForUndefinedCellValue(),
render: {
display: (cellData, type, rowData, meta) => {
if(cellData){
let wormholeData = BaseModule.Util.getObjVal(Init, `wormholes.${cellData}`);
let security = BaseModule.Util.getObjVal(wormholeData, `security`);
let typeNodes = FormElement.formatSignatureTypeSelectionData({text: `${cellData} - ${security}`}, undefined, {showWhSizeLabel: false});
return [...typeNodes].reduce((acc, node) => {
acc += node.outerHTML;
return acc;
}, '');
}
}
}
},{
targets: 8,
name: 'estimatedLife',
title: '<i title="estimated lifetime" data-toggle="tooltip" class="fas fa-hourglass-start"></i>',
width: 15,
className: 'text-right',
data: 'estimatedEol',
defaultContent: module.getIconForUndefinedCellValue(),
render: {
display: (cellData, type, rowData, meta) => {
if(cellData > 0){
return `< ${cellData}h`;
} else if (cellData == 0 ){
return `< 1h`;
}
},
sort: dateVal => Date.parse(dateVal)
}
},{
targets: 9,
name: 'action',
title: '',
orderable: false,
width: 10,
className: ['text-center', module._config.tableCellActionClass, module._config.moduleHeadlineIconClass].join(' '),
data: null,
render: {
display: data => {
let icon = '';
if(data.connectionId){
icon = '<i class="fas fa-times txt-color txt-color-redDark"></i>';
}else if(!data.syncStatus){
icon = '<i class="fas fa-plus"></i>';
}
return icon;
}
},
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
let tableApi = this.api();
cell.addEventListener('click', e => {
e.stopPropagation();
let rowClicked = tableApi.row(e.target.closest('tr'));
rowData = rowClicked.data();
if(rowData.connectionId){
// delete row(s) ------------------------------------------------------------------
let deleteData = {
systemIds: [],
connectionIds: [],
};
let value, label;
// check if multiple rows are selected + current row is one of them -> bulk edit
let rowsSelected = tableApi.rows({selected: true});
if(rowsSelected.count() > 1 && tableApi.row(rowIndex, {selected: true}).count()){
deleteData = rowsSelected.data().toArray()
.reduce((acc, rowData) => {
acc.systemIds.push(BaseModule.Util.getObjVal(rowData, 'target.realId'));
acc.connectionIds.push(BaseModule.Util.getObjVal(rowData, 'connectionId'));
return acc;
}, deleteData);
label = `delete ${deleteData.systemIds.length} systems`;
}else{
// get current row data (important!)
// -> "rowData" param is not current state, values are "on createCell()" state
rowsSelected = tableApi.row( $(cell).parents('tr'));
rowData = rowsSelected.data();
deleteData.systemIds.push(BaseModule.Util.getObjVal(rowData, 'target.realId') || 0);
deleteData.connectionIds.push(BaseModule.Util.getObjVal(rowData, 'connectionId') || 0);
label = `delete '${BaseModule.Util.getObjVal(rowData, 'target.name')}'`;
}
let confirmationSettings = {
title: '---',
template: BaseModule.Util.getConfirmationTemplate(BaseModule.Util.getConfirmationContent([{
name: 'deleteSystems',
value: 1,
label: label,
class: 'pf-editable-warn',
checked: false
}]), {
size: 'small',
noTitle: true
}),
trigger: 'manual',
onConfirm : function(e, target){
// stop scroll to top
e.preventDefault();
// get form data (check if form tag is not hidden!) from confirmation popover
let tip = target.data('bs.confirmation').tip();
let form = tip.find('form:not(.hidden)').first();
let formData = form.getFormValues();
let deleteSystems = BaseModule.Util.getObjVal(formData, 'deleteSystems');
if(deleteSystems){
// delete systems -> connections get auto removed as well
let activeMap = BaseModule.Util.getMapModule().getActiveMap();
let systems = deleteData.systemIds.map(systemId =>
document.getElementById(MapUtil.getSystemId(module._mapId, systemId))
).filter(Boolean);
$(module.moduleElement).showLoadingAnimation();
activeMap.trigger('pf:deleteSystems', [{
systems: systems,
callback: deletedSystems => {
// callback function after ajax "delete" success
// check if system was deleted
if(deletedSystems.length){
// remove table row
rowsSelected.remove().draw();
}else{
// error
this.showNotify({title: 'Failed to delete system', type: 'error'});
}
$(module.moduleElement).hideLoadingAnimation();
}
}]);
}else{
// just delete connections
let connections = deleteData.connectionIds.map(connectionId =>
$().getConnectionById(module._mapId, connectionId)
).filter(Boolean);
$(module.moduleElement).showLoadingAnimation();
MapUtil.deleteConnections(connections, () => {
// callback function after ajax "delete" success
// remove table row
rowsSelected.remove().draw();
$(module.moduleElement).hideLoadingAnimation();
});
}
}
};
// init confirmation dialog
$(cell).confirmation('destroy').confirmation(confirmationSettings).confirmation('show');
}else if(!rowData.syncStatus){
// add row ------------------------------------------------------------------------
let systemDataThera = MapUtil.getSystemData(module._mapId, TheraModule.systemIdThera, 'systemId');
let systemDataSource = MapUtil.getSystemData(module._mapId, BaseModule.Util.getObjVal(rowData, 'target.id'), 'systemId');
if(systemDataThera && systemDataSource){
// source & target system are already on map -> just draw connection
let map = MapUtil.getMapInstance(module._mapId);
let connectionData = {
source: systemDataThera.id,
target: systemDataSource.id,
scope: rowData.scope,
type: rowData.type
};
$(module.moduleElement).showLoadingAnimation();
Map.drawConnection(map, connectionData)
.then(payload => Map.saveConnection(payload.data.connection, true))
.catch(console.warn);
}else{
let options = {
systemData: {
id: BaseModule.Util.getObjVal(rowData, 'target.id'),
name: BaseModule.Util.getObjVal(rowData, 'target.name')
}
};
if(systemDataThera){
let systemThera = document.getElementById(MapUtil.getSystemId(systemDataThera.mapId, systemDataThera.id));
if(systemThera){
options.sourceSystem = $(systemThera);
options.connectionData = {
scope: rowData.scope,
type: rowData.type,
disableAutoScope: true
};
}
}
module.showNewSystemDialog(options);
}
}
});
}
},{
targets: 10,
name: 'rowGroupData',
className: 'never', // never show this column. see: https://datatables.net/extensions/responsive/classes
data: 'rowGroupData',
defaultContent: 0,
visible: false,
render: {
sort: 'id'
}
}
],
drawCallback: function(settings){
let animationRows = this.api().rows().nodes().to$().filter(function(){
return (
$(this).data('animationStatus') ||
$(this).data('animationTimer')
);
});
animationRows.initTooltips();
for(let i = 0; i < animationRows.length; i++){
let animationRow = $(animationRows[i]);
animationRow.pulseBackgroundColor(animationRow.data('animationStatus'));
animationRow.removeData('animationStatus');
}
},
initComplete: function(settings, json){
module.getTheraConnectionsData('callbackUpdateTableRows');
// hover effect -----------------------------------------------------------------------------------
let tableApi = this.api();
let relRowId = null;
tableApi.tables().nodes().to$().on('mouseenter', 'td', function(){
relRowId = BaseModule.Util.getObjVal(tableApi.row(this.parentElement).data(), 'relRowId') || null;
if(relRowId){
let colIndex = tableApi.cell(this).index().column;
if(colIndex <= 8){
let cell = tableApi.cell(this);
let relCell = tableApi.cell(
`#${module.getRowById(tableApi, relRowId)}`,
colIndex
);
cell.nodes().to$().addClass('cellHighlight');
relCell.nodes().to$().addClass('cellHighlight');
if([4,5,6,7,8].includes(colIndex)){
let cellNode = cell.node();
let relCellNode = relCell.node(); // might not exist if relRow was recently removed
if(
cellNode && relCellNode &&
cellNode.innerHTML !== relCellNode.innerHTML
){
cellNode.dataset.origValue = cellNode.innerHTML;
cellNode.innerHTML = relCellNode.innerHTML;
}
}
}
}
}).on('mouseleave', 'td', function(){
if(relRowId){
if(this.dataset.origValue){
this.innerHTML = this.dataset.origValue;
delete this.dataset.origValue;
}
tableApi.cells().nodes().to$().removeClass('cellHighlight');
relRowId = null;
}
});
// click on "fake connection" ---------------------------------------------------------------------
$(this).on('click', '.pf-fake-connection', function(){
let rowData = tableApi.row(this.closest('tr')).data();
if(rowData.connectionId){
let connection = $().getConnectionById(module._mapId, rowData.connectionId);
if(connection){
let map = connection._jsPlumb.instance;
MapUtil.showConnectionInfo(map, [connection]);
}
}
});
// click on "target system name" ------------------------------------------------------------------
$(this).on('click', `.${module._config.linkClass}[data-systemid]:not([data-systemid=''])`, e => {
BaseModule.Util.triggerMenuAction(
BaseModule.Util.getMapModule().getActiveMap(),
'SelectSystem',
{systemId: MapUtil.getSystemData(module._mapId, parseInt(e.target.dataset.systemid), 'systemId').id}
);
});
},
data: [] // will be added dynamic
});
let buttons = new $.fn.dataTable.Buttons(this._tableApi, {
dom: {
container: {
tag: 'h5',
className: 'pull-right'
},
button: {
tag: 'i',
className: ['fas', 'fa-fw', module._config.moduleHeadlineIconClass].join(' '),
},
buttonLiner: {
tag: null
}
},
name: 'tableTools',
buttons: [
{
name: 'eveScout',
className: 'fa-external-link-alt',
titleAttr: module._config.eveScoutUrl.hostname.replace('www.', ''),
attr: {
'data-toggle': 'tooltip',
'data-html': true
},
action: function(e, tableApi, node, config){
e.stopPropagation();
window.open(`//${module._config.eveScoutUrl.hostname}`, '_blank');
}
}, {
name: 'refresh',
className: 'fa-sync',
titleAttr: 'refresh',
attr: {
'data-toggle': 'tooltip',
'data-html': true
},
action: function(e, tableApi, node, config){
$(module.moduleElement).showLoadingAnimation();
module.getTheraConnectionsData('callbackUpdateTableRows');
}
}
]
});
// "RowGroup" Datatables Plugin
this._tableApi.buttons().container().appendTo(module.moduleElement.querySelector('.' + module._config.headClassName));
new $.fn.dataTable.RowGroup(this._tableApi, {
dataSrc: 'rowGroupData.name',
startRender: function(rows, group, level){
return `<div class="flex-row flex-between">` +
`<span class="flex-col flex-grow">${group}</span>` +
`<span class="flex-col">${rows.count()}</span>` +
`</div>`;
}
});
// "Select" Datatables Plugin
this._tableApi.select();
this._tableApi.on('user-select', function(e, tableApi, type, cell, originalEvent){
let rowData = tableApi.row(cell.index().row).data();
if(!BaseModule.Util.getObjVal(rowData, 'connectionId')){
e.preventDefault();
}
});
}
/**
* @param options
* @param isThera
*/
showNewSystemDialog(options = {}, isThera = false){
let activeMap = BaseModule.Util.getMapModule().getActiveMap();
// custom callback -> refresh Thera table
options.callback = (map, newSystemData, sourceSystem, connectionData) => {
$(this.moduleElement).showLoadingAnimation();
Map.saveSystemCallback(map, newSystemData, sourceSystem, connectionData);
};
BaseModule.Util.triggerMenuAction(activeMap, 'AddSystem', options);
}
/**
* get thera connections data
* @param callback
* @param system
*/
getTheraConnectionsData(callback, system = null){
this.getLocalStore().getItem('eveScout')
.then(cacheEntry => new Promise(resolve => {
if(
cacheEntry && cacheEntry.tSet &&
TheraModule.ttlEveScoutResponse > BaseModule.now() - cacheEntry.tSet
){
$(this.moduleElement).hideLoadingAnimation();
resolve(cacheEntry.value);
}else{
this.request('GET', 'SystemThera', [], {}, this, context => {
$(this.moduleElement).hideLoadingAnimation();
}).then(payload => {
let cacheEntry = {
tSet: BaseModule.now(),
value: Array.isArray(payload.data) ? payload.data : []
};
this.getLocalStore().setItem('eveScout', cacheEntry)
.then(cacheEntry => resolve(cacheEntry.value));
}).catch(payload => {
let reason = payload.data.status + ' ' + payload.data.error;
this.showNotify({
title: payload.data.jqXHR.status + ': Thera connections data',
text: reason,
type: 'warning'
});
resolve([]);
});
}
}))
.then(connectionsData => this[callback](connectionsData, system));
}
/**
* update thera table with data
* @param connectionsData
* @param systemThera
*/
callbackUpdateTableRows(connectionsData = [], systemThera = null){
if(!this._tableApi){
return;
}
connectionsData = Array.isArray(connectionsData) ? connectionsData : [];
let getGroupedRowId = (groupData, rowId) => {
return `${groupData.id}-${rowId}`;
};
let touchedRows = [];
let hadData = this._tableApi.rows().any();
let notificationCounter = {
added: 0,
changed: 0,
deleted: 0
};
let map = MapUtil.getMapInstance(this._mapId);
let currentMapData = BaseModule.Util.getCurrentMapData(this._mapId);
if(map && currentMapData){
let groupDataMap = {
id: this._mapId,
name: BaseModule.Util.getObjVal(currentMapData, 'config.name')
};
let groupDataEveScout = {
id: 0,
name: 'eve-scout.com'
};
// get data from all Thera connections from current map -----------------------------------------------
let currentConnectionsData = {};
let systemTheraFound = false;
if(systemThera){
systemTheraFound = true;
let currentConnections = MapUtil.searchConnectionsBySystems(map, [systemThera], '*');
currentConnectionsData = BaseModule.getConnectionsDataFromConnections(this._mapId, currentConnections);
}else{
let systemDataThera = MapUtil.getSystemData(this._mapId, TheraModule.systemIdThera, 'systemId');
if(systemDataThera){
systemTheraFound = true;
let systemIdThera = MapUtil.getSystemId(this._mapId, systemDataThera.id);
let currentConnections = MapUtil.searchConnectionsBySystems(map, [systemIdThera], '*');
currentConnectionsData = BaseModule.getConnectionsDataFromConnections(this._mapId, currentConnections);
}
}
this._bodyEl.querySelector(`.${this._config.controlAreaTheraClass}`).classList.toggle('hidden', systemTheraFound);
let dateNow = new Date();
let tableDataPrimary = [];
let tableDataSecondary = [];
let formatSignatureData = data => ({
id: data.ids,
name: data.names.length ? data.names.map(name => name.substring(0, 3)).join(', ') : null,
type: {
name: data.labels.length ? data.labels.join(', ') : null
}
});
let equalPropVal = (val, prop = 'id') => b => b[prop] === val;
// add mapped Thera connections (primary) -------------------------------------------------------------
for(let currentConnectionData of Object.values(currentConnectionsData)){
let connectionId = BaseModule.Util.getObjVal(currentConnectionData, 'connection.id') || 0;
let updated = BaseModule.Util.getObjVal(currentConnectionData, 'connection.updated') || 0;
let source = currentConnectionData.source;
let target = currentConnectionData.target;
let connectionHash = BaseModule.getConnectionDataCacheKey(source.name, target.name);
// mapped connection not exists in eveScout data
let currentConnectionDataFull = MapUtil.getConnectionDataFromMapData(currentMapData, connectionId);
let connection = $().getConnectionById(this._mapId, connectionId);
let signatureTypeNames = MapUtil.getConnectionDataFromSignatures(connection, currentConnectionDataFull);
let sourceSignature = formatSignatureData(signatureTypeNames.source);
let targetSignature = formatSignatureData(signatureTypeNames.target);
// swap source <-> target. 'Thera' should be the source system
// -> In order to match the eve-scout response format for connections
if(target.name.toLowerCase() === 'thera'){
[source, target] = [target, source];
[sourceSignature, targetSignature] = [targetSignature, sourceSignature];
}
let whLabel = null;
let whSigId = 0;
for(let data of Object.values(signatureTypeNames)){
if(!data.labels){
continue;
}
let i = data.labels.findIndex(label => label !== 'K162');
if(i !== -1){
whLabel = data.labels[i];
whSigId = data.ids[i];
break;
}
}
// to calc the remaining lifetime of the wormhole, we need the 'created' date of linked sig.
// -> This is "hacky"! We re-search all signatures by whSigId === id... :(
let estimatedEol;
let maxStableTime = BaseModule.Util.getObjVal(Object.assign({}, Init.wormholes[whLabel]), 'maxStableTime');
if(maxStableTime && whSigId){
let whSignatureData = (BaseModule.Util.getObjVal(currentConnectionDataFull, 'signatures') || [])
.find(equalPropVal(whSigId));
let whSignatureCreated = BaseModule.Util.getObjVal(whSignatureData, 'created.created') || 0;
if(whSignatureCreated){
let createdDate = new Date( whSignatureCreated * 1000);
createdDate.setHours(createdDate.getHours() + maxStableTime);
//estimatedEol = Math.floor(createdDate.getTime() / 1000);
estimatedEol = createdDate.toISOString();
}
}
let rowData = {
id: getGroupedRowId(groupDataMap, connectionHash),
hash: connectionHash,
scope: BaseModule.Util.getObjVal(currentConnectionData, 'connection.scope'),
type: BaseModule.Util.getObjVal(currentConnectionData, 'connection.type'),
source: (({id:realId, systemId:id, name, trueSec, constellation, region}) =>
({
realId,
id,
name,
trueSec,
constellation,
region
}))(MapUtil.getSystemDataFromMapData(currentMapData, source.id)),
target: (({id:realId, systemId:id, name, trueSec, constellation, region}) =>
({
realId,
id,
name,
trueSec,
constellation,
region
}))(MapUtil.getSystemDataFromMapData(currentMapData, target.id)),
sourceSignature: sourceSignature,
targetSignature: targetSignature,
wormholeLabel: whLabel,
syncStatus: 'warning',
rowGroupData: groupDataMap,
connectionId: connectionId,
fakeConnection: BaseModule.getFakeConnectionElement(currentConnectionData),
estimatedEol: estimatedEol,
updated: updated,
updatedHash: [updated, signatureTypeNames.hash].join().hashCode()
};
tableDataPrimary.push(rowData);
}
// add EveScout Thera connections (secondary) ---------------------------------------------------------
for(let rowData of connectionsData){
let systemNameSource = BaseModule.Util.getObjVal(rowData, 'source.name');
let systemNameTarget = BaseModule.Util.getObjVal(rowData, 'target.name');
let connectionHash = BaseModule.getConnectionDataCacheKey(systemNameSource, systemNameTarget);
let whLabel = BaseModule.Util.getObjVal(rowData, 'sourceSignature.type.name') || null;
if(!whLabel){
whLabel = BaseModule.Util.getObjVal(rowData, 'targetSignature.type.name') || null;
}
let massType = BaseModule.Util.getObjVal(Object.assign({}, Init.wormholes[whLabel]), 'size.type');
let connectionType = [...rowData.type, massType];
// overwrite eveScout id with new id (hash)
rowData.id = getGroupedRowId(groupDataEveScout, connectionHash);
rowData.hash = connectionHash;
rowData.wormholeLabel = whLabel;
rowData.syncStatus = null;
rowData.rowGroupData = groupDataEveScout;
rowData.connectionId = null;
rowData.type = connectionType;
rowData.fakeConnection = BaseModule.getFakeConnectionElement(BaseModule.getFakeConnectionData(
{system: systemNameSource},
{system: systemNameTarget},
rowData.scope,
rowData.type
));
rowData.updatedHash = String(rowData.updated).hashCode();
tableDataSecondary.push(rowData);
}
// + Merge Primary + Secondary table data -------------------------------------------------------------
for(let primaryRowData of tableDataPrimary){
let secondaryIndex = tableDataSecondary.findIndex(equalPropVal(primaryRowData.hash, 'hash'));
if(secondaryIndex !== -1){
// get a new hash key from both 'updated' values (primary + secondary) tableData
// -> if a signature gets updated on map, table row should be updated, too
// -> so we can check it later on if either prim. or sec. data has changed...
let updatedHash = [
primaryRowData.updatedHash,
tableDataSecondary[secondaryIndex].updatedHash
].join().hashCode();
primaryRowData.syncStatus = 'success';
primaryRowData.relRowId = tableDataSecondary[secondaryIndex].id;
primaryRowData.updatedHash = updatedHash;
// remove from secondary table data -> no duplicated rows
//tableDataSecondary.splice(secondaryIndex, 1);
tableDataSecondary[secondaryIndex].syncStatus = 'hint';
tableDataSecondary[secondaryIndex].relRowId = primaryRowData.id;
tableDataSecondary[secondaryIndex].updatedHash = updatedHash;
}
}
let tableData = [...tableDataPrimary, ...tableDataSecondary];
// update table data ----------------------------------------------------------------------------------
for(let rowData of tableData){
let rowId = this.getRowById(this._tableApi, rowData.id);
if(rowId){
// update row
let api = this._tableApi.row('#' + rowId);
let rowDataCurrent = api.data();
// check for update
if(
BaseModule.Util.getObjVal(rowDataCurrent, 'updatedHash') !==
BaseModule.Util.getObjVal(rowData, 'updatedHash')
){
// row data changed -> update
api.data(rowData);
api.nodes().to$().data('animationStatus', 'changed');
notificationCounter.changed++;
}
touchedRows.push(api.id());
}else{
// insert new row
let api = this._tableApi.row.add(rowData);
api.nodes().to$().data('animationStatus', 'added');
notificationCounter.added++;
touchedRows.push(api.id());
}
}
let api = this._tableApi.rows((idx, data, node) => !touchedRows.includes(node.id));
notificationCounter.deleted += api.ids().count();
api.remove();
if(Math.max(...Object.values(notificationCounter))){
this._tableApi.draw();
}
// show notification ----------------------------------------------------------------------------------
let notification = Object.keys(notificationCounter).reduce((acc, key) => {
return `${acc}${notificationCounter[key] ? `${notificationCounter[key]} ${key}<br>` : ''}`;
}, '');
if(hadData && notification.length){
this.showNotify({title: 'Thera connections updated', text: notification, type: 'success', textTrusted: true});
}
}else{
console.warn('Table update failed for Thera connections. MapData is missing');
}
}
/**
* set module observer
*/
setModuleObserver(){
// add Thera system
this._bodyEl.querySelector(`.${this._config.controlAreaTheraClass}`).addEventListener('click', e => {
this.showNewSystemDialog({
systemData: {
id: TheraModule.systemIdThera,
name: 'Thera'
}
}, true);
}, false);
// signature column - "type" popover
MapUtil.initWormholeInfoTooltip(
$(this.moduleElement).find('.' + this._config.globalTheraTableClass),
`.${this._config.tableCellTypeClass} span[class^="pf-system-sec-"]`
);
// init tooltips
$(this.moduleElement).initTooltips();
}
/**
* update module
* compare data and update module
* @param mapId
* @returns {Promise}
*/
update(mapId){
return super.update(mapId).then(mapId => new Promise(resolve => {
this.getTheraConnectionsData('callbackUpdateTableRows');
resolve({
action: 'update',
data: {
module: this
}
});
}));
}
};
TheraModule.isPlugin = false; // module is defined as 'plugin'
TheraModule.scope = 'global'; // module scope controls how module gets updated and what type of data is injected
TheraModule.sortArea = 'b'; // default sortable area
TheraModule.position = 3; // default sort/order position within sortable area
TheraModule.label = 'Thera'; // static module label (e.g. description)
TheraModule.systemIdThera = 31000005;
TheraModule.ttlEveScoutResponse = 120;
TheraModule.defaultConfig = {
className: 'pf-global-thera-module', // class for module
sortTargetAreas: ['a', 'b', 'c'], // sortable areas where module can be dragged into
headline: 'Thera Connection',
// Thera module
theraTableId: 'pf-thera-table-', // id prefix for all tables in module
theraTableRowIdPrefix: 'pf-thera-row-', // id prefix for table rows
globalTheraTableClass: 'pf-global-thera-table', // class for NPC owned stations table
controlAreaTheraClass: 'pf-global-thera-control', // class for "thera system exists" label
// fonts
fontUppercaseClass: 'pf-font-uppercase', // class for "uppercase" font
linkClass: 'pf-link',
// Thera connections table
tableCellTypeClass: 'pf-table-type-cell', // class for "type" cells
tableCellEllipsisClass: 'pf-table-cell-ellipses-auto', // class for table "ellipsis" cells
tableCellActionClass: 'pf-table-action-cell'
};
return TheraModule;
});