- improved search results for "Corporation" dropdown

This commit is contained in:
Mark Friedrich
2018-09-02 15:04:21 +02:00
parent 7b800ebcc7
commit 4afd3b6707
12 changed files with 10 additions and 9269 deletions

View File

@@ -461,7 +461,7 @@ define([
* @param term search term
*/
function sortResultData (data, term){
let levenshtein = function (a,b){
let levenshtein = (a,b) => {
let matrix = new Array(a.length+1);
for(let i = 0; i < matrix.length; i++){
matrix[i] = new Array(b.length+1).fill(0);
@@ -480,7 +480,7 @@ define([
matrix[ai][bi] = Math.min(
matrix[ai-1][bi]+1,
matrix[ai][bi-1]+1,
matrix[ai-1][bi-1]+(a[ai-1] == b[bi-1] ? 0 : 1)
matrix[ai-1][bi-1]+(a[ai-1] === b[bi-1] ? 0 : 1)
);
}
}
@@ -488,7 +488,7 @@ define([
return matrix[a.length][b.length];
};
data.sort(function(a,b){
data.sort((a,b) => {
let levA = levenshtein(term, a.name.toLowerCase());
let levB = levenshtein(term, b.name.toLowerCase());
return levA === levB ? 0 : (levA > levB ? 1 : -1);
@@ -531,6 +531,7 @@ define([
for(let category in result){
// skip custom functions in case result = [] (array functions)
if(result.hasOwnProperty(category)){
// sort results (optional)
sortResultData(result[category], page.term);
data.results.push({
text: category,

View File

@@ -1220,7 +1220,7 @@ define([
searchable: true,
title: 'id',
type: 'string',
width: 15,
width: 12,
class: [config.tableCellFocusClass, config.sigTableEditSigNameInput].join(' '),
data: 'name',
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){

File diff suppressed because it is too large Load Diff

View File

@@ -461,7 +461,7 @@ define([
* @param term search term
*/
function sortResultData (data, term){
let levenshtein = function (a,b){
let levenshtein = (a,b) => {
let matrix = new Array(a.length+1);
for(let i = 0; i < matrix.length; i++){
matrix[i] = new Array(b.length+1).fill(0);
@@ -480,7 +480,7 @@ define([
matrix[ai][bi] = Math.min(
matrix[ai-1][bi]+1,
matrix[ai][bi-1]+1,
matrix[ai-1][bi-1]+(a[ai-1] == b[bi-1] ? 0 : 1)
matrix[ai-1][bi-1]+(a[ai-1] === b[bi-1] ? 0 : 1)
);
}
}
@@ -488,7 +488,7 @@ define([
return matrix[a.length][b.length];
};
data.sort(function(a,b){
data.sort((a,b) => {
let levA = levenshtein(term, a.name.toLowerCase());
let levB = levenshtein(term, b.name.toLowerCase());
return levA === levB ? 0 : (levA > levB ? 1 : -1);
@@ -531,6 +531,7 @@ define([
for(let category in result){
// skip custom functions in case result = [] (array functions)
if(result.hasOwnProperty(category)){
// sort results (optional)
sortResultData(result[category], page.term);
data.results.push({
text: category,

View File

@@ -1220,7 +1220,7 @@ define([
searchable: true,
title: 'id',
type: 'string',
width: 15,
width: 12,
class: [config.tableCellFocusClass, config.sigTableEditSigNameInput].join(' '),
data: 'name',
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){

File diff suppressed because it is too large Load Diff

View File

@@ -1,248 +0,0 @@
/**
* System graph module
*/
define([
'jquery',
'app/init',
'app/util',
'morris'
], ($, Init, Util, Morris) => {
'use strict';
let config = {
// module info
modulePosition: 3,
moduleName: 'systemGraph',
moduleHeadClass: 'pf-module-head', // class for module header
moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler
// system graph module
moduleTypeClass: 'pf-system-graph-module', // class for this module
systemGraphClass: 'pf-system-graph', // class for each graph
// system graph labels
systemGraphs: {
jumps: {
headline: 'Jumps',
units: 'jumps',
ykeys: ['y'],
labels: ['jumps'],
lineColors: ['#375959'],
pointFillColors: ['#477372']
},
shipKills: {
headline: 'Ship/POD Kills',
units: 'kills',
ykeys: ['y', 'z'],
labels: ['Ship kills', 'POD kills'],
lineColors: ['#375959', '#477372'],
pointFillColors: ['#477372', '#568a89']
},
factionKills: {
headline: 'NPC Kills',
units: 'kills',
ykeys: ['y'],
labels: ['kills'],
lineColors: ['#375959'],
pointFillColors: ['#477372']
}
}
};
/**
* get info for a given graph key
* @param graphKey
* @param option
* @returns {string}
*/
let getInfoForGraph = function(graphKey, option){
let info = '';
if(config.systemGraphs.hasOwnProperty(graphKey)){
info = config.systemGraphs[graphKey][option];
}
return info;
};
/**
* init Morris Graph
* @param graphElement
* @param graphKey
* @param graphData
* @param eventLine
*/
let initGraph = function(graphElement, graphKey, graphData, eventLine){
if(graphData.length > 0){
let labelYFormat = function(y){
return Math.round(y);
};
let graphConfig = {
element: graphElement,
data: graphData,
xkey: 'x',
ykeys: getInfoForGraph(graphKey, 'ykeys'),
labels: getInfoForGraph(graphKey, 'labels'),
parseTime: false,
ymin: 0,
yLabelFormat: labelYFormat,
padding: 10,
hideHover: true,
pointSize: 3,
lineColors: getInfoForGraph(graphKey, 'lineColors'),
pointFillColors: getInfoForGraph(graphKey, 'pointFillColors'),
pointStrokeColors: ['#141413'],
lineWidth: 2,
grid: true,
gridStrokeWidth: 0.3,
gridTextSize: 9,
gridTextFamily: 'Oxygen Bold',
gridTextColor: '#63676a',
behaveLikeLine: false,
goals: [],
goalLineColors: ['#5cb85c'],
smooth: true,
fillOpacity: 0.2,
resize: true,
redraw: true,
eventStrokeWidth: 2,
eventLineColors: ['#5CB85C']
};
if(eventLine >= 0){
graphConfig.events = [eventLine];
}
Morris.Area(graphConfig);
}
};
/**
* request graphs data
* @param requestData
* @param context
* @param callback
*/
let requestGraphData = (requestData, context, callback) => {
// show loading animation
context.moduleElement.find('.' + config.systemGraphClass).showLoadingAnimation();
$.ajax({
type: 'POST',
url: Init.path.getSystemGraphData,
data: requestData,
dataType: 'json',
context: context
}).done(function(systemGraphsData){
callback(this, systemGraphsData);
}).fail(function( jqXHR, status, error) {
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': System graph data', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
this.moduleElement.hide();
}).always(function(){
// hide loading animation
context.moduleElement.find('.' + config.systemGraphClass).hideLoadingAnimation();
});
};
/**
* update graph elements with data
* @param context
* @param systemGraphsData
*/
let addGraphData = (context, systemGraphsData) => {
// calculate time offset until system created -----------------------------------------------------------------
let serverData = Util.getServerTime();
let timestampNow = Math.floor(serverData.getTime() / 1000);
let timeSinceUpdate = timestampNow - context.systemData.updated.updated;
let timeInHours = Math.floor(timeSinceUpdate / 3600);
let timeInMinutes = Math.floor((timeSinceUpdate % 3600) / 60);
let timeInMinutesPercent = ( timeInMinutes / 60 ).toFixed(2);
let eventLine = timeInHours + timeInMinutesPercent;
// graph is from right to left -> convert event line
eventLine = 23 - eventLine;
// update graph data ------------------------------------------------------------------------------------------
for (let [systemId, graphsData] of Object.entries(systemGraphsData)){
for (let [graphKey, graphData] of Object.entries(graphsData)){
let graphElement = context.moduleElement.find('[data-graph="' + graphKey + '"]');
initGraph(graphElement, graphKey, graphData, eventLine);
}
}
};
/**
* @see requestGraphData
* @param moduleElement
* @param mapId
* @param systemData
*/
let updateGraphPanel = (moduleElement, mapId, systemData) => {
let requestData = {
systemIds: [systemData.systemId]
};
let contextData = {
moduleElement: moduleElement,
systemData: systemData
};
requestGraphData(requestData, contextData, addGraphData);
};
/**
* get module element
* @param parentElement
* @param mapId
* @param systemData
* @returns {*}
*/
let getModule = (parentElement, mapId, systemData) => {
// graph data is available for k-space systems
let moduleElement = null;
if(systemData.type.id === 2){
moduleElement = $('<div>');
let rowElement = $('<div>', {
class: 'row'
});
for (let [graphKey, graphConfig] of Object.entries(config.systemGraphs)){
rowElement.append(
$('<div>', {
class: ['col-xs-12', 'col-sm-6', 'col-md-4'].join(' ')
}).append(
$('<div>', {
class: config.moduleHeadClass
}).append(
$('<h5>', {
class: config.moduleHandlerClass
}),
$('<h5>', {
text: getInfoForGraph(graphKey, 'headline')
})
),
$('<div>', {
class: config.systemGraphClass
}).attr('data-graph', graphKey)
)
);
}
moduleElement.append(rowElement);
updateGraphPanel(moduleElement, mapId, systemData);
}
return moduleElement;
};
return {
config: config,
getModule: getModule
};
});

View File

@@ -1,421 +0,0 @@
/**
* System info module
*/
define([
'jquery',
'app/init',
'app/util',
'app/render',
'app/map/util'
], ($, Init, Util, Render, MapUtil) => {
'use strict';
let config = {
// module info
modulePosition: 2,
moduleName: 'systemInfo',
// system info module
moduleTypeClass: 'pf-system-info-module', // class for this module
// breadcrumb
constellationLinkClass: 'pf-system-info-constellation', // class for "constellation" name
regionLinkClass: 'pf-system-info-region', // class for "region" name
typeLinkClass: 'pf-system-info-type', // class for "type" name
// info table
systemInfoTableClass: 'pf-module-table', // class for system info table
systemInfoNameClass: 'pf-system-info-name', // class for "name" information element
systemInfoEffectClass: 'pf-system-info-effect', // class for "effect" information element
systemInfoPlanetsClass: 'pf-system-info-planets', // class for "planets" information element
systemInfoStatusLabelClass: 'pf-system-info-status-label', // class for "status" information element
systemInfoStatusAttributeName: 'data-status', // attribute name for status label
systemInfoWormholeClass: 'pf-system-info-wormhole-', // class prefix for static wormhole element
// description field
descriptionArea: 'pf-system-info-description-area', // class for "description" area
addDescriptionButtonClass: 'pf-system-info-description-button', // class for "add description" button
moduleElementToolbarClass: 'pf-table-tools', // class for "module toolbar" element
tableToolsActionClass: 'pf-table-tools-action', // class for "edit" action
descriptionTextareaElementClass: 'pf-system-info-description', // class for "description" textarea element (xEditable)
descriptionTextareaCharCounter: 'pf-form-field-char-count', // class for "character counter" element for form field
// fonts
fontTriglivianClass: 'pf-triglivian' // class for "Triglivian" names (e.g. Abyssal systems)
};
// disable Module update temporary (in case e.g. textarea is currently active)
let disableModuleUpdate = false;
// animation speed values
let animationSpeedToolbarAction = 200;
// max character length for system description
let maxDescriptionLength = 512;
/**
* shows the tool action element by animation
* @param toolsActionElement
*/
let showToolsActionElement = (toolsActionElement) => {
toolsActionElement.velocity('stop').velocity({
opacity: 1,
height: '100%'
},{
duration: animationSpeedToolbarAction,
display: 'block',
visibility: 'visible'
});
};
/**
* hides the tool action element by animation
* @param toolsActionElement
*/
let hideToolsActionElement = (toolsActionElement) => {
toolsActionElement.velocity('stop').velocity('reverse', {
display: 'none',
visibility: 'hidden'
});
};
/**
* update trigger function for this module
* compare data and update module
* @param moduleElement
* @param systemData
*/
let updateModule = (moduleElement, systemData) => {
let systemId = moduleElement.data('id');
if(systemId === systemData.id){
// update system status -----------------------------------------------------------------------------------
let systemStatusLabelElement = moduleElement.find('.' + config.systemInfoStatusLabelClass);
let systemStatusId = parseInt( systemStatusLabelElement.attr( config.systemInfoStatusAttributeName ) );
if(systemStatusId !== systemData.status.id){
// status changed
let currentStatusClass = Util.getStatusInfoForSystem(systemStatusId, 'class');
let newStatusClass = Util.getStatusInfoForSystem(systemData.status.id, 'class');
let newStatusLabel = Util.getStatusInfoForSystem(systemData.status.id, 'label');
systemStatusLabelElement.removeClass(currentStatusClass).addClass(newStatusClass).text(newStatusLabel);
// set new status attribute
systemStatusLabelElement.attr( config.systemInfoStatusAttributeName, systemData.status.id);
}
// update description textarea ----------------------------------------------------------------------------
let descriptionTextareaElement = moduleElement.find('.' + config.descriptionTextareaElementClass);
let description = descriptionTextareaElement.editable('getValue', true);
if(
!disableModuleUpdate && // don´t update if field is active
description !== systemData.description
){
// description changed
let descriptionButton = moduleElement.find('.' + config.addDescriptionButtonClass);
// set new value
descriptionTextareaElement.editable('setValue', systemData.description);
let actionElement = descriptionButton.siblings('.' + config.tableToolsActionClass);
if(systemData.description.length === 0){
// show/activate description field
// show button if value is empty
descriptionButton.show();
hideToolsActionElement(actionElement);
}else{
// hide/disable description field
// hide tool button
descriptionButton.hide();
showToolsActionElement(actionElement);
}
}
// created/updated tooltip --------------------------------------------------------------------------------
let nameRowElement = moduleElement.find('.' + config.systemInfoNameClass);
let tooltipData = {
created: systemData.created,
updated: systemData.updated
};
nameRowElement.addCharacterInfoTooltip( tooltipData );
}
moduleElement.find('.' + config.descriptionArea).hideLoadingAnimation();
};
/**
* get module element
* @param parentElement
* @param mapId
* @param systemData
*/
let getModule = (parentElement, mapId, systemData) => {
// create new module container
let moduleElement = $('<div>');
// store systemId -> module can be updated with the correct data
moduleElement.data('id', systemData.id);
// system "static" wh data
let staticsData = [];
if(
systemData.statics &&
systemData.statics.length > 0
){
for(let wormholeName of systemData.statics){
let wormholeData = Object.assign({}, Init.wormholes[wormholeName]);
wormholeData.class = Util.getSecurityClassForSystem(wormholeData.security);
staticsData.push(wormholeData);
}
}
let effectName = MapUtil.getEffectInfoForSystem(systemData.effect, 'name');
let effectClass = MapUtil.getEffectInfoForSystem(systemData.effect, 'class');
// systemInfo template config
let moduleConfig = {
name: 'modules/system_info',
position: moduleElement,
link: 'append',
functions: {
after: function(conf){
let tempModuleElement = conf.position;
// lock "description" field until first update
tempModuleElement.find('.' + config.descriptionArea).showLoadingAnimation();
// "add description" button
let descriptionButton = tempModuleElement.find('.' + config.addDescriptionButtonClass);
// description textarea element
let descriptionTextareaElement = tempModuleElement.find('.' + config.descriptionTextareaElementClass);
// init description textarea
descriptionTextareaElement.editable({
url: Init.path.saveSystem,
dataType: 'json',
pk: systemData.id,
type: 'textarea',
mode: 'inline',
emptytext: '',
onblur: 'cancel',
showbuttons: true,
value: '', // value is set by trigger function updateModule()
rows: 5,
name: 'description',
inputclass: config.descriptionTextareaElementClass,
tpl: '<textarea maxlength="' + maxDescriptionLength + '"></textarea>',
params: function(params){
params.mapData = {
id: mapId
};
params.systemData = {};
params.systemData.id = params.pk;
params.systemData[params.name] = params.value;
// clear unnecessary data
delete params.pk;
delete params.name;
delete params.value;
return params;
},
validate: function(value){
if(value.length > 0 && $.trim(value).length === 0) {
return {newValue: ''};
}
},
success: function(response, newValue){
Util.showNotify({title: 'System updated', text: 'Name: ' + response.name, type: 'success'});
},
error: function(jqXHR, newValue){
let reason = '';
let status = '';
if(jqXHR.name){
// save error new sig (mass save)
reason = jqXHR.name;
status = 'Error';
}else{
reason = jqXHR.responseJSON.text;
status = jqXHR.status;
}
Util.showNotify({title: status + ': save system information', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
return reason;
}
});
// on xEditable open ------------------------------------------------------------------------------
descriptionTextareaElement.on('shown', function(e, editable){
let textarea = editable.input.$input;
// disable module update until description field is open
disableModuleUpdate = true;
// create character counter
let charCounter = $('<kbd>', {
class: [config.descriptionTextareaCharCounter, 'txt-color', 'text-right'].join(' ')
});
textarea.parent().next().append(charCounter);
// update character counter
Util.updateCounter(textarea, charCounter, maxDescriptionLength);
textarea.on('keyup', function(){
Util.updateCounter($(this), charCounter, maxDescriptionLength);
});
});
// on xEditable close -----------------------------------------------------------------------------
descriptionTextareaElement.on('hidden', function(e){
let value = $(this).editable('getValue', true);
if(value.length === 0){
// show button if value is empty
hideToolsActionElement(descriptionButton.siblings('.' + config.tableToolsActionClass));
descriptionButton.show();
}
// enable module update
disableModuleUpdate = false;
});
// enable xEditable field on Button click ---------------------------------------------------------
descriptionButton.on('click', function(e){
e.stopPropagation();
let descriptionButton = $(this);
// hide tool buttons
descriptionButton.hide();
// show field *before* showing the element
descriptionTextareaElement.editable('show');
showToolsActionElement(descriptionButton.siblings('.' + config.tableToolsActionClass));
});
// init tooltips ----------------------------------------------------------------------------------
let tooltipElements = tempModuleElement.find('[data-toggle="tooltip"]');
tooltipElements.tooltip();
// init system effect popover ---------------------------------------------------------------------
$(moduleElement).find('.' + config.systemInfoEffectClass).addSystemEffectTooltip(systemData.security, systemData.effect);
// init planets popover ---------------------------------------------------------------------------
$(moduleElement).find('.' + config.systemInfoPlanetsClass).addSystemPlanetsTooltip(systemData.planets);
// init static wormhole information ---------------------------------------------------------------
for(let staticData of staticsData){
let staticRowElement = tempModuleElement.find('.' + config.systemInfoWormholeClass + staticData.name);
staticRowElement.addWormholeInfoTooltip(staticData);
}
// constellation popover --------------------------------------------------------------------------
tempModuleElement.find('a.popup-ajax').popover({
html: true,
trigger: 'hover',
placement: 'top',
delay: 200,
container: 'body',
content: function(){
return details_in_popup(this);
}
});
function details_in_popup(popoverElement){
popoverElement = $(popoverElement);
let popover = popoverElement.data('bs.popover');
$.ajax({
url: popoverElement.data('url'),
success: function(data){
let systemEffectTable = Util.getSystemsInfoTable( data.systemsData );
popover.options.content = systemEffectTable;
// reopen popover (new content size)
popover.show();
}
});
return 'Loading...';
}
}
}
};
let moduleData = {
system: systemData,
static: staticsData,
tableClass: config.systemInfoTableClass,
nameInfoClass: config.systemInfoNameClass,
effectInfoClass: config.systemInfoEffectClass,
planetsInfoClass: config.systemInfoPlanetsClass,
wormholePrefixClass: config.systemInfoWormholeClass,
statusInfoClass: config.systemInfoStatusLabelClass,
systemTypeName: MapUtil.getSystemTypeInfo(systemData.type.id, 'name'),
systemIsWormhole: MapUtil.getSystemTypeInfo(systemData.type.id, 'name') === 'w-space',
systemStatusId: systemData.status.id,
systemStatusClass: Util.getStatusInfoForSystem(systemData.status.id, 'class'),
systemStatusLabel: Util.getStatusInfoForSystem(systemData.status.id, 'label'),
securityClass: Util.getSecurityClassForSystem( systemData.security ),
trueSec: systemData.trueSec.toFixed(1),
trueSecClass: Util.getTrueSecClassForSystem( systemData.trueSec ),
effectName: effectName,
effectClass: effectClass,
moduleToolbarClass: config.moduleElementToolbarClass,
descriptionButtonClass: config.addDescriptionButtonClass,
tableToolsActionClass: config.tableToolsActionClass,
descriptionTextareaClass: config.descriptionTextareaElementClass,
systemNameClass: () => {
return (val, render) => {
return render(val) === 'A' ? config.fontTriglivianClass : '';
};
},
formatUrl: () => {
return (val, render) => render(val).replace(/ /g, '_');
},
planetCount: systemData.planets ? systemData.planets.length : 0,
shatteredClass: Util.getSecurityClassForSystem('SH'),
ajaxConstellationInfoUrl: Init.path.getConstellationData,
systemConstellationLinkClass: config.constellationLinkClass,
systemRegionLinkClass: config.regionLinkClass,
systemTypeLinkClass: config.typeLinkClass
};
Render.showModule(moduleConfig, moduleData);
return moduleElement;
};
/**
* efore module destroy callback
* @param moduleElement
*/
let beforeDestroy = (moduleElement) => {
// remove xEditable description textarea
let descriptionTextareaElement = moduleElement.find('.' + config.descriptionTextareaElementClass);
descriptionTextareaElement.editable('destroy');
};
return {
config: config,
getModule: getModule,
updateModule: updateModule,
beforeDestroy: beforeDestroy
};
});

View File

@@ -1,827 +0,0 @@
/**
* system route module
*/
define([
'jquery',
'app/init',
'app/util',
'bootbox'
], ($, Init, Util, bootbox) => {
'use strict';
let config = {
// module info
modulePosition: 1,
moduleName: 'systemIntel',
moduleHeadClass: 'pf-module-head', // class for module header
moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler
moduleTypeClass: 'pf-system-intel-module', // class for this module
// headline toolbar
moduleHeadlineIconClass: 'pf-module-icon-button', // class for toolbar icons in the head
moduleHeadlineIconAddClass: 'pf-module-icon-button-add', // class for "add structure" icon
moduleHeadlineIconReaderClass: 'pf-module-icon-button-reader', // class for "dScan reader" icon
moduleHeadlineIconRefreshClass: 'pf-module-icon-button-refresh', // class for "refresh" icon
// system intel module
systemStructuresTableClass: 'pf-system-structure-table', // class for route tables
// structure dialog
structureDialogId: 'pf-structure-dialog', // id for "structure" dialog
statusSelectId: 'pf-structure-dialog-status-select', // id for "status" select
typeSelectId: 'pf-structure-dialog-type-select', // id for "type" select
corporationSelectId: 'pf-structure-dialog-corporation-select', // id for "corporation" select
descriptionTextareaId: 'pf-structure-dialog-description-textarea', // id for "description" textarea
descriptionTextareaCharCounter: 'pf-form-field-char-count', // class for "character counter" element for form field
// dataTable
tableRowIdPrefix: 'pf-structure-row_', // id prefix for table rows
tableCellImageClass: 'pf-table-image-smaller-cell', // class for table "image" cells
tableCellCounterClass: 'pf-table-counter-cell', // class for table "counter" cells
tableCellEllipsisClass: 'pf-table-cell-ellipses-auto', // class for table "ellipsis" cells
dataTableActionCellClass: 'pf-table-action-cell' // class for "action" cells
};
let maxDescriptionLength = 512;
/**
* get status icon for structure
* @param statusData
* @returns {string}
*/
let getStatusData = (statusData) => {
return '<i class="fas fa-fw fa-circle ' + statusData.class + '" title="' + statusData.label + '"></i>';
};
/**
* get <tr> DOM id by id
* @param tableApi
* @param id
* @returns {*}
*/
let getRowId = (tableApi, id) => {
return tableApi.rows().ids().toArray().find(rowId => rowId === config.tableRowIdPrefix + id);
};
/**
* callback -> add structure rows from responseData
* @param context
* @param responseData
*/
let callbackAddStructureRows = (context, responseData) => {
let systemData = Util.getObjVal(responseData, 'system');
callbackUpdateStructureRows(context, systemData);
};
/**
* callback -> add structure rows from systemData
* @param context
* @param systemData
*/
let callbackUpdateStructureRows = (context, systemData) => {
let touchedRows = [];
let hadData = context.tableApi.rows().any();
let notificationCounter = {
added: 0,
changed: 0,
deleted: 0
};
if(systemData){
let corporations = Util.getObjVal(systemData, 'structures');
if(corporations) {
for (let [corporationId, corporationData] of Object.entries(corporations)){
if(corporationData.structures && corporationData.structures.length){
for(let structureData of corporationData.structures){
let rowId = getRowId(context.tableApi, structureData.id);
// add corporation data
structureData.corporation = {
id: corporationData.id,
name: corporationData.name
};
if(rowId){
// update row
let api = context.tableApi.row('#' + rowId);
let rowData = api.data();
// check for update
if(rowData.updated.updated !== structureData.updated.updated){
// row data changed -> update
api.data(structureData);
api.nodes().to$().data('animationStatus', 'changed').destroyTimestampCounter();
notificationCounter.changed++;
}
touchedRows.push(api.id());
}else{
// insert new row
let api = context.tableApi.row.add(structureData);
api.nodes().to$().data('animationStatus', 'added');
notificationCounter.added++;
touchedRows.push(api.id());
}
}
}
}
}
}
if(context.removeMissing){
let api = context.tableApi.rows((idx, data, node) => !touchedRows.includes(node.id));
notificationCounter.deleted += api.ids().count();
api.remove();
}
context.tableApi.draw();
// show notification ------------------------------------------------------------------------------------------
let notification = '';
notification += notificationCounter.added > 0 ? notificationCounter.added + ' added<br>' : '';
notification += notificationCounter.changed > 0 ? notificationCounter.changed + ' changed<br>' : '';
notification += notificationCounter.deleted > 0 ? notificationCounter.deleted + ' deleted<br>' : '';
if(hadData && notification.length){
Util.showNotify({title: 'Structures updated', text: notification, type: 'success'});
}
};
/**
* callback -> delete structure rows
* @param context
* @param responseData
*/
let callbackDeleteStructures = (context, responseData) => {
let structureIds = Util.getObjVal(responseData, 'deletedStructureIds');
let deletedCounter = 0;
if(structureIds && structureIds.length){
for(let structureId of structureIds){
let rowId = getRowId(context.tableApi, structureId);
if(rowId){
context.tableApi.row('#' + rowId).remove();
deletedCounter++;
}
}
}
if(deletedCounter){
context.tableApi.draw();
Util.showNotify({title: 'Structure deleted', text: deletedCounter + ' deleted', type: 'success'});
}
};
/**
* send ajax request
* @param url
* @param requestData
* @param context
* @param callback
*/
let sendRequest = (url, requestData, context, callback) => {
context.moduleElement.showLoadingAnimation();
$.ajax({
url: url,
type: 'POST',
dataType: 'json',
data: requestData,
context: context
}).done(function(data){
callback(this, data);
}).fail(function( jqXHR, status, error) {
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': System intel data', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
}).always(function(){
// hide loading animation
this.moduleElement.hideLoadingAnimation();
});
};
/**
* requests system data
* @param requestData
* @param context
* @param callback
*/
let getStructureData = (requestData, context, callback) => {
sendRequest(Init.path.getSystemData, requestData, context, callback);
};
/**
* save structure data
* @param requestData
* @param context
* @param callback
*/
let saveStructureData = (requestData, context, callback) => {
sendRequest(Init.path.saveStructureData, requestData, context, callback);
};
/**
* delete structure
* @param requestData
* @param context
* @param callback
*/
let deleteStructure = (requestData, context, callback) => {
sendRequest(Init.path.deleteStructureData, requestData, context, callback);
};
/**
* show structure dialog
* @param moduleElement
* @param tableApi
* @param systemId
* @param structureData
*/
let showStructureDialog = (moduleElement, tableApi, systemId, structureData) => {
let structureStatusData = Util.getObjVal(Init, 'structureStatus');
let structureTypeData = Util.getObjVal(Init, 'structureStatus');
let statusData = Object.keys(structureStatusData).map((k) => {
let data = structureStatusData[k];
data.selected = data.id === Util.getObjVal(structureData, 'status.id');
return data;
});
let data = {
id: config.structureDialogId,
structureData: structureData,
structureStatus: statusData,
statusSelectId: config.statusSelectId,
typeSelectId: config.typeSelectId,
corporationSelectId: config.corporationSelectId,
descriptionTextareaId: config.descriptionTextareaId,
descriptionTextareaCharCounter: config.descriptionTextareaCharCounter,
maxDescriptionLength: maxDescriptionLength
};
requirejs(['text!templates/dialog/structure.html', 'mustache'], (template, Mustache) => {
let content = Mustache.render(template, data);
let structureDialog = bootbox.dialog({
title: 'Structure',
message: content,
show: false,
buttons: {
close: {
label: 'cancel',
className: 'btn-default'
},
success: {
label: '<i class="fas fa-fw fa-check"></i>&nbsp;save',
className: 'btn-success',
callback: function (){
let form = this.find('form');
// validate form
form.validator('validate');
// check whether the form is valid
let formValid = form.isValidForm();
if(formValid){
// get form data
let formData = form.getFormValues();
formData.id = Util.getObjVal(structureData, 'id') | 0;
formData.structureId = Util.getObjVal(formData, 'structureId') | 0;
formData.corporationId = Util.getObjVal(formData, 'corporationId') | 0;
formData.systemId = systemId | 0;
saveStructureData({
structures: [formData]
}, {
moduleElement: moduleElement,
tableApi: tableApi
}, callbackUpdateStructureRows);
}else{
return false;
}
}
}
}
});
structureDialog.on('show.bs.modal', function(e) {
let modalContent = $('#' + config.structureDialogId);
// init type select live search
let selectElementType = modalContent.find('#' + config.typeSelectId);
selectElementType.initUniverseTypeSelect({
categoryIds: [65],
maxSelectionLength: 1,
selected: [Util.getObjVal(structureData, 'structure.id')]
});
// init corporation select live search
let selectElementCorporation = modalContent.find('#' + config.corporationSelectId);
selectElementCorporation.initUniverseSearch({
categoryNames: ['corporation'],
maxSelectionLength: 1
});
// init status select2
modalContent.find('#' + config.statusSelectId).initStatusSelect({
data: statusData
});
// init character counter
let textarea = modalContent.find('#' + config.descriptionTextareaId);
let charCounter = modalContent.find('.' + config.descriptionTextareaCharCounter);
Util.updateCounter(textarea, charCounter, maxDescriptionLength);
textarea.on('keyup', function(){
Util.updateCounter($(this), charCounter, maxDescriptionLength);
});
// set form validator (after select2 init finish)
modalContent.find('form').initFormValidation();
});
// show dialog
structureDialog.modal('show');
});
};
/**
* show D-Scan reader dialog
* @param moduleElement
* @param tableApi
* @param systemData
*/
let showDscanReaderDialog = (moduleElement, tableApi, systemData) => {
requirejs(['text!templates/dialog/dscan_reader.html', 'mustache'], (template, Mustache) => {
let structureDialog = bootbox.dialog({
title: 'D-Scan reader',
message: Mustache.render(template, {}),
show: true,
buttons: {
close: {
label: 'cancel',
className: 'btn-default'
},
success: {
label: '<i class="fas fa-fw fa-paste fa-fw"></i>&nbsp;update intel',
className: 'btn-success',
callback: function (){
let form = this.find('form');
let formData = form.getFormValues();
updateStructureTableByClipboard(systemData, formData.clipboard, {
moduleElement: moduleElement,
tableApi: tableApi
});
}
}
}
});
// dialog shown event
structureDialog.on('shown.bs.modal', function(e) {
// set focus on textarea
structureDialog.find('textarea').focus();
});
});
};
/**
* get module element
* @param parentElement
* @param mapId
* @param systemData
* @returns {jQuery}
*/
let getModule = (parentElement, mapId, systemData) => {
let corporationId = Util.getCurrentUserInfo('corporationId');
// create new module container
let moduleElement = $('<div>').append(
$('<div>', {
class: config.moduleHeadClass
}).append(
$('<h5>', {
class: config.moduleHandlerClass
}),
$('<h5>', {
class: 'pull-right'
}).append(
$('<i>', {
class: ['fas', 'fa-fw', 'fa-plus', config.moduleHeadlineIconClass, config.moduleHeadlineIconAddClass].join(' '),
title: 'add'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
$('<i>', {
class: ['fas', 'fa-fw', 'fa-paste', config.moduleHeadlineIconClass, config.moduleHeadlineIconReaderClass].join(' '),
title: 'D-Scan&nbsp;reader'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
$('<i>', {
class: ['fas', 'fa-fw', 'fa-sync', config.moduleHeadlineIconClass, config.moduleHeadlineIconRefreshClass].join(' '),
title: 'refresh&nbsp;all'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip')
),
$('<h5>', {
text: 'Intel'
})
)
);
let table = $('<table>', {
class: ['compact', 'stripe', 'order-column', 'row-border', 'pf-table-fixed', config.systemStructuresTableClass].join(' ')
});
moduleElement.append(table);
let structureTable = table.DataTable({
paging: false,
lengthChange: false,
ordering: true,
order: [[ 10, 'desc' ], [ 0, 'asc' ]],
info: false,
searching: false,
hover: false,
autoWidth: false,
rowId: rowData => config.tableRowIdPrefix + rowData.id,
language: {
emptyTable: 'No structures recorded',
info: '_START_ to _END_ of _MAX_',
infoEmpty: ''
},
rowGroup: {
enable: true,
dataSrc: 'systemId'
},
columnDefs: [
{
targets: 0,
title: '',
width: 2,
class: 'text-center',
data: 'status',
render: {
display: data => getStatusData(data),
sort: data => data.id
},
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
$(cell).find('i').tooltip();
}
},{
targets: 1,
title: '',
width: 26,
orderable: false,
className: [config.tableCellImageClass, 'text-center'].join(' '),
data: 'structure.id',
defaultContent: '<i class="fas fa-question txt-color txt-color-orangeDark"></i>',
render: {
_: function(data, type, row, meta){
let value = data;
if(type === 'display' && value){
value = '<img src="' + Init.url.ccpImageServer + '/Type/' + value + '_32.png" />';
}
return value;
}
}
},{
targets: 2,
title: 'type',
width: 30,
className: [config.tableCellEllipsisClass].join(' '),
data: 'structure.name',
defaultContent: '<i class="fas fa-question txt-color txt-color-orangeDark"></i>',
},{
targets: 3,
title: 'name',
width: 60,
className: [config.tableCellEllipsisClass].join(' '),
data: 'name'
},{
targets: 4,
title: '',
width: 26,
orderable: false,
className: [config.tableCellImageClass, 'text-center'].join(' '),
data: 'owner.id',
defaultContent: '<i class="fas fa-question txt-color txt-color-orangeDark"></i>',
render: {
_: function(data, type, row, meta){
let value = data;
if(type === 'display' && value){
value = '<a href="https://zkillboard.com/corporation/' + data + '/" target="_blank" rel="noopener">';
value += '<img src="' + Init.url.ccpImageServer + '/Corporation/' + data + '_32.png" />';
value += '</a>';
}
return value;
}
}
},{
targets: 5,
title: 'owner',
width: 50,
className: [config.tableCellEllipsisClass].join(' '),
data: 'owner.name',
defaultContent: '<i class="fas fa-question txt-color txt-color-orangeDark"></i>',
},{
targets: 6,
title: 'note',
className: [config.tableCellEllipsisClass].join(' '),
data: 'description'
},{
targets: 7,
title: 'updated',
width: 60,
className: ['text-right', config.tableCellCounterClass].join(' '),
data: 'updated.updated'
},{
targets: 8,
title: '',
orderable: false,
width: 10,
class: ['text-center', config.dataTableActionCellClass, config.moduleHeadlineIconClass].join(' '),
data: null,
render: {
display: data => {
let icon = '<i class="fas fa-pencil-alt"></i>';
if(data.corporation.id !== corporationId){
icon = '';
}
return icon;
}
},
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
let tableApi = this.api();
if($(cell).is(':empty')){
$(cell).removeClass(config.dataTableActionCellClass + ' ' + config.moduleHeadlineIconClass);
}else{
$(cell).on('click', function(e) {
// get current row data (important!)
// -> "rowData" param is not current state, values are "on createCell()" state
rowData = tableApi.row( $(cell).parents('tr')).data();
showStructureDialog(moduleElement, tableApi, systemData.systemId, rowData);
});
}
}
},{
targets: 9,
title: '',
orderable: false,
width: 10,
class: ['text-center', config.dataTableActionCellClass].join(' '),
data: null,
render: {
display: data => {
let icon = '<i class="fas fa-times txt-color txt-color-redDarker"></i>';
if(data.corporation.id !== corporationId){
icon = '<i class="fas fa-ban txt-color txt-color-grayLight" title="restricted" data-placement="left"></i>';
}
return icon;
}
},
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
let tableApi = this.api();
if($(cell).find('.fa-ban').length){
$(cell).removeClass(config.dataTableActionCellClass + ' ' + config.moduleHeadlineIconClass);
$(cell).find('i').tooltip();
}else{
let confirmationSettings = {
container: 'body',
placement: 'left',
btnCancelClass: 'btn btn-sm btn-default',
btnCancelLabel: 'cancel',
btnCancelIcon: 'fas fa-fw fa-ban',
title: 'delete structure',
btnOkClass: 'btn btn-sm btn-danger',
btnOkLabel: 'delete',
btnOkIcon: 'fas fa-fw fa-times',
onConfirm : function(e, target){
// get current row data (important!)
// -> "rowData" param is not current state, values are "on createCell()" state
rowData = tableApi.row( $(cell).parents('tr')).data();
// let deleteRowElement = $(cell).parents('tr');
// tableApi.rows(deleteRowElement).remove().draw();
deleteStructure({
id: rowData.id
},{
moduleElement: moduleElement,
tableApi: tableApi
}, callbackDeleteStructures);
}
};
// init confirmation dialog
$(cell).confirmation(confirmationSettings);
}
}
},{
targets: 10,
name: 'corporation',
data: 'corporation',
visible: false,
render: {
sort: function(data){
return data.name;
}
}
}
],
drawCallback: function (settings){
let tableApi = this.api();
let columnCount = tableApi.columns(':visible').count();
let rows = tableApi.rows( {page:'current'} ).nodes();
let last= null;
tableApi.column('corporation:name', {page:'current'} ).data().each( function ( group, i ) {
if ( !last || last.id !== group.id ) {
$(rows).eq(i).before(
'<tr class="group">' +
'<td></td>' +
'<td class="' + config.tableCellImageClass + '">' +
'<img src="' + Init.url.ccpImageServer + '/Corporation/' + group.id + '_32.png" />' +
'</td>' +
'<td colspan="' + (columnCount - 2 ) + '">' + group.name + '</td>' +
'</tr>'
);
last = group;
}
});
rows.to$().find('.' + config.tableCellCounterClass + ':not([data-counter])').initTimestampCounter('d');
let animationRows = rows.to$().filter(function() {
return (
$(this).data('animationStatus') ||
$(this).data('animationTimer')
);
});
for(let i = 0; i < animationRows.length; i++){
let animationRow = $(animationRows[i]);
animationRow.pulseTableRow(animationRow.data('animationStatus'));
animationRow.removeData('animationStatus');
}
},
initComplete: function(settings){
// table data is load in updateModule() method
// -> no need to trigger additional ajax call here for data
// -> in case table update failed -> each if this initComplete() function finished before table updata
// e.g. return now promise in getModule() function
}
});
// init tooltips for this module
let tooltipElements = moduleElement.find('[data-toggle="tooltip"]');
tooltipElements.tooltip({
container: 'body'
});
moduleElement.showLoadingAnimation();
return moduleElement;
};
/**
* get universe typeIds for given categoryIds
* @param categoryIds
* @returns {Array}
*/
let getUniverseTypeIdsByCategoryIds = (categoryIds) => {
let typeIds = [];
let mapIds = type => type.id;
for(let categoryId of categoryIds){
let categoryData = Util.getObjVal(Init, 'universeCategories.' + categoryId);
if(categoryData && categoryData.groups){
for(let groupData of categoryData.groups){
if(groupData && groupData.types){
typeIds = typeIds.concat(groupData.types.map(mapIds));
}
}
}
}
return typeIds;
};
/**
* parse a copy&paste string from ingame dScan windows
* @param systemData
* @param clipboard
* @returns {Array}
*/
let parseDscanString = (systemData, clipboard) => {
let dScanData = [];
let structureTypeIds = getUniverseTypeIdsByCategoryIds([65]);
if(clipboard.length){
let dScanRows = clipboard.split(/\r\n|\r|\n/g);
for(let rowData of dScanRows){
rowData = rowData.split(/\t/g);
if(rowData.length === 4){
rowData[0] = parseInt(rowData[0]);
// valid dScan result
if(structureTypeIds.indexOf( rowData[0] ) !== -1){
dScanData.push({
structureId: rowData[0],
name: rowData[1],
systemId: systemData.systemId
});
}
}
}
}
return dScanData;
};
/**
* parse clipboard data for structures and update table
* @param systemData
* @param clipboard
* @param context
*/
let updateStructureTableByClipboard = (systemData, clipboard, context) => {
let structureData = parseDscanString(systemData, clipboard);
if(structureData.length){
saveStructureData({
structures: structureData
}, context, callbackUpdateStructureRows);
}
};
/**
* update trigger function for this module
* compare data and update module
* @param moduleElement
* @param systemData
*/
let updateModule = (moduleElement, systemData) => {
// update structure table data
let structureTableElement = moduleElement.find('.' + config.systemStructuresTableClass);
let tableApi = structureTableElement.DataTable();
let context = {
tableApi: tableApi,
removeMissing: true
};
callbackUpdateStructureRows(context, systemData);
moduleElement.hideLoadingAnimation();
};
/**
* init intel module
* @param moduleElement
* @param mapId
* @param systemData
*/
let initModule = (moduleElement, mapId, systemData) => {
let structureTableElement = moduleElement.find('.' + config.systemStructuresTableClass);
let tableApi = structureTableElement.DataTable();
// init structure dialog --------------------------------------------------------------------------------------
moduleElement.find('.' + config.moduleHeadlineIconAddClass).on('click', function(e){
showStructureDialog(moduleElement, tableApi, systemData.systemId);
});
// init structure dialog --------------------------------------------------------------------------------------
moduleElement.find('.' + config.moduleHeadlineIconReaderClass).on('click', function(e){
showDscanReaderDialog(moduleElement, tableApi, systemData);
});
// init refresh button ----------------------------------------------------------------------------------------
moduleElement.find('.' + config.moduleHeadlineIconRefreshClass).on('click', function(e){
getStructureData({
mapId: mapId,
systemId: systemData.id
},{
moduleElement: moduleElement,
tableApi: tableApi,
removeMissing: true
}, callbackAddStructureRows);
});
// init listener for global "past" dScan into this page -------------------------------------------------------
moduleElement.on('pf:updateIntelModuleByClipboard', function(e, clipboard){
updateStructureTableByClipboard(systemData, clipboard, {
moduleElement: moduleElement,
tableApi: tableApi
});
});
};
return {
config: config,
getModule: getModule,
initModule: initModule,
updateModule: updateModule
};
});

View File

@@ -1,404 +0,0 @@
define([
'jquery',
'app/init',
'app/util',
'morris'
], ($, Init, Util, Morris) => {
'use strict';
let config = {
// module info
modulePosition: 2,
moduleName: 'systemKillboard',
moduleHeadClass: 'pf-module-head', // class for module header
moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler
// headline toolbar
moduleHeadlineIconClass: 'pf-module-icon-button', // class for toolbar icons in the head
// system killboard module
moduleTypeClass: 'pf-system-killboard-module', // class for this module
systemKillboardGraphKillsClass: 'pf-system-killboard-graph-kills', // class for system kill graph
// system killboard list
systemKillboardListClass: 'pf-system-killboard-list', // class for a list with kill entries
systemKillboardListEntryClass: 'pf-system-killboard-list-entry', // class for a list entry
systemKillboardListImgShip: 'pf-system-killboard-img-ship', // class for all ship images
systemKillboardListImgChar: 'pf-system-killboard-img-char', // class for all character logos
systemKillboardListImgAlly: 'pf-system-killboard-img-ally', // class for all alliance logos
systemKillboardListImgCorp: 'pf-system-killboard-img-corp' // class for all corp logos
};
let cache = {
systemKillsGraphData: {} // data for system kills info graph
};
/**
*
* @param text
* @param options
* @returns {jQuery}
*/
let getLabel = (text, options) => {
let label = $('<span>', {
class: ['label', options.type, options.align].join(' ')
}).text( text );
return label;
};
/**
* show killMails
* @param moduleElement
* @param killboardData
*/
let showKillmails = (moduleElement, killboardData) => {
// show number of killMails
let killMailCounterMax = 20;
let killMailCounter = 0;
// change order (show right to left)
killboardData.tableData.reverse();
let data = {
tableData: killboardData.tableData,
systemKillboardListClass: config.systemKillboardListClass,
systemKillboardListEntryClass: config.systemKillboardListEntryClass,
systemKillboardListImgShip: config.systemKillboardListImgShip,
systemKillboardListImgChar: config.systemKillboardListImgChar,
systemKillboardListImgAlly: config.systemKillboardListImgAlly,
systemKillboardListImgCorp: config.systemKillboardListImgCorp,
zKillboardUrl: 'https://zkillboard.com',
ccpImageServerUrl: Init.url.ccpImageServer,
dateFormat: () => {
return (val, render) => {
let killDate = Util.convertDateToUTC(new Date(render(val)));
return Util.convertDateToString(killDate);
};
},
iskFormat: () => {
return (val, render) => {
return Util.formatPrice(render(val));
};
},
checkRender : () => {
return (val, render) => {
if(killMailCounter < killMailCounterMax){
return render(val);
}
};
},
increaseCount : () => {
return (val, render) => {
killMailCounter++;
};
}
};
requirejs(['text!templates/modules/killboard.html', 'mustache'], function(template, Mustache) {
let content = Mustache.render(template, data);
moduleElement.append(content);
// animate kill li-elements
$('.' + config.systemKillboardListEntryClass).velocity('transition.expandIn', {
stagger: 50,
complete: function(){
// init tooltips
moduleElement.find('[title]').tooltip({
container: 'body'
});
}
});
});
};
/**
* updates the system info graph
* @param systemData
*/
$.fn.updateSystemInfoGraphs = function(systemData){
let moduleElement = $(this);
let killboardGraphElement = $('<div>', {
class: config.systemKillboardGraphKillsClass
});
moduleElement.append(killboardGraphElement);
let showHours = 24;
let maxKillmailCount = 200; // limited by API
let labelOptions = {
align: 'center-block'
};
let label = '';
// private function draws a "system kills" graph
let drawGraph = function(data){
let tableData = data.tableData;
// change order (show right to left)
tableData.reverse();
if(data.count === 0){
labelOptions.type = 'label-success';
label = getLabel( 'No kills found within the last 24h', labelOptions );
killboardGraphElement.append( label );
minifyKillboardGraphElement(killboardGraphElement);
return;
}
let labelYFormat = function(y){
return Math.round(y);
};
// draw chart
Morris.Bar({
element: killboardGraphElement,
resize: true,
redraw: true,
grid: true,
gridStrokeWidth: 0.3,
gridTextSize: 9,
gridTextColor: '#63676a',
gridTextFamily: 'Oxygen Bold',
hideHover: true,
data: tableData,
xkey: 'label',
ykeys: ['kills'],
labels: ['Kills'],
yLabelFormat: labelYFormat,
xLabelMargin: 10,
padding: 10,
parseTime: false,
barOpacity: 0.8,
barRadius: [2, 2, 0, 0],
barSizeRatio: 0.5,
barGap: 3,
barColors: function (row, series, type) {
if (type === 'bar') {
// highlight last row -> recent kills found
if(this.xmax === row.x){
return '#c2760c';
}
}
return '#375959';
}
});
// show hint for recent kills
if(tableData[tableData.length - 1].kills > 0){
labelOptions.type = 'label-warning';
label = getLabel( tableData[tableData.length - 1].kills + ' kills within the last hour', labelOptions );
killboardGraphElement.prepend( label );
}
};
// get recent KB stats (last 24h))
let localDate = new Date();
// cache result for 5min
let cacheKey = systemData.systemId + '_' + localDate.getHours() + '_' + ( Math.ceil( localDate.getMinutes() / 5 ) * 5);
if(cache.systemKillsGraphData.hasOwnProperty(cacheKey) ){
// cached results
drawGraph( cache.systemKillsGraphData[cacheKey] );
// show killmail information
showKillmails(moduleElement, cache.systemKillsGraphData[cacheKey]);
}else{
// chart data
let chartData = [];
for(let i = 0; i < showHours; i++){
let tempData = {
label: i + 'h',
kills: 0
};
chartData.push(tempData);
}
// get kills within the last 24h
let timeFrameInSeconds = 60 * 60 * 24;
// get current server time
let serverDate= Util.getServerTime();
// if system is w-space system -> add link modifier
let wSpaceLinkModifier = '';
if(systemData.type.id === 1){
wSpaceLinkModifier = 'w-space/';
}
let url = Init.url.zKillboard + '/';
url += 'no-items/' + wSpaceLinkModifier + 'no-attackers/solarSystemID/' + systemData.systemId + '/pastSeconds/' + timeFrameInSeconds + '/';
killboardGraphElement.showLoadingAnimation();
$.ajax({
url: url,
type: 'GET',
dataType: 'json'
}).done(function(kbData) {
// the API wont return more than 200KMs ! - remember last bar block with complete KM information
let lastCompleteDiffHourData = 0;
// loop kills and count kills by hour
for (let i = 0; i < kbData.length; i++) {
let killmailData = kbData[i];
let killDate = Util.convertDateToUTC(new Date(killmailData.killmail_time));
// get time diff
let timeDiffMin = Math.round(( serverDate - killDate ) / 1000 / 60);
let timeDiffHour = Math.floor(timeDiffMin / 60);
// update chart data
if (chartData[timeDiffHour]) {
chartData[timeDiffHour].kills++;
// add kill mail data
if (chartData[timeDiffHour].killmails === undefined) {
chartData[timeDiffHour].killmails = [];
}
chartData[timeDiffHour].killmails.push(killmailData);
if (timeDiffHour > lastCompleteDiffHourData) {
lastCompleteDiffHourData = timeDiffHour;
}
}
}
// remove empty chart Data
if (kbData.length >= maxKillmailCount) {
chartData = chartData.splice(0, lastCompleteDiffHourData + 1);
}
// fill cache
cache.systemKillsGraphData[cacheKey] = {};
cache.systemKillsGraphData[cacheKey].tableData = chartData;
cache.systemKillsGraphData[cacheKey].count = kbData.length;
// draw table
drawGraph(cache.systemKillsGraphData[cacheKey]);
// show killmail information
showKillmails(moduleElement, cache.systemKillsGraphData[cacheKey]);
killboardGraphElement.hideLoadingAnimation();
}).fail(function(e){
labelOptions.type = 'label-danger';
label = getLabel( 'zKillboard is not responding', labelOptions );
killboardGraphElement.prepend( label );
killboardGraphElement.hideLoadingAnimation();
minifyKillboardGraphElement(killboardGraphElement);
Util.showNotify({title: e.status + ': Get system kills', text: 'Loading failed', type: 'error'});
});
}
// init tooltips
let tooltipElements = moduleElement.find('[data-toggle="tooltip"]');
tooltipElements.tooltip({
container: 'body'
});
};
/**
* minify the killboard graph element e.g. if no kills where found, or on error
* @param killboardGraphElement
*/
let minifyKillboardGraphElement = (killboardGraphElement) => {
killboardGraphElement.velocity({
height: '20px',
marginBottom: '0px'
},{
duration: Init.animationSpeed.mapModule
});
};
/**
* get module toolbar element
* @param systemData
* @returns {*|jQuery|HTMLElement|void}
*/
let getHeadlineToolbar = (systemData) => {
let headlineToolbar = $('<h5>', {
class: 'pull-right'
}).append(
$('<i>', {
class: ['fas', 'fa-fw', 'fa-external-link-alt ', config.moduleHeadlineIconClass].join(' '),
title: 'zkillboard.com'
}).on('click', function(e){
window.open(
'//zkillboard.com/system/' + systemData.systemId,
'_blank'
);
}).attr('data-toggle', 'tooltip')
);
headlineToolbar.find('[data-toggle="tooltip"]').tooltip({
container: 'body'
});
return headlineToolbar;
};
/**
* before module "show" callback
* @param moduleElement
* @param systemData
*/
let beforeShow = (moduleElement, systemData) => {
// update graph
moduleElement.updateSystemInfoGraphs(systemData);
};
/**
* get module element
* @param parentElement
* @param mapId
* @param systemData
* @returns {jQuery}
*/
let getModule = (parentElement, mapId, systemData) => {
// create new module container
let moduleElement = $('<div>').append(
$('<div>', {
class: config.moduleHeadClass
}).append(
$('<h5>', {
class: config.moduleHandlerClass
}),
$('<h5>', {
text: 'Killboard'
}),
getHeadlineToolbar(systemData)
)
);
return moduleElement;
};
return {
config: config,
getModule: getModule,
beforeShow: beforeShow
};
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff