- full refactoring of the "signature module" #679

- improved "character info" tooltips in e.g. signature Table
- improved performance with the live time counters in e.g. signature table
- fixed a bug where mass delete signatures on a system sometimes failed
- fixed a bug where "signature type" sometimes not get saved correctly
- fixed a bug where "responsive table columns" were not shown on larger screens
This commit is contained in:
Mark Friedrich
2018-08-31 19:11:39 +02:00
parent 9eb3676543
commit 57f6d4d29b
52 changed files with 12396 additions and 533 deletions

View File

@@ -173,20 +173,25 @@ class Signature extends Controller\AccessController {
* @throws \Exception
*/
public function delete(\Base $f3){
$signatureIds = $f3->get('POST.signatureIds');
$signatureIds = array_unique(array_map('intval', (array)$f3->get('POST.signatureIds')));
$activeCharacter = $this->getCharacter();
$return = (object) [];
$return->deletedSignatureIds = [];
/**
* @var Model\SystemSignatureModel $signature
*/
$signature = Model\BasicModel::getNew('SystemSignatureModel');
foreach($signatureIds as $signatureId){
$signature->getById($signatureId);
$signature->delete( $activeCharacter );
if($signature->delete($activeCharacter)){
$return->deletedSignatureIds[] = $signatureId;
}
$signature->reset();
}
echo json_encode([]);
echo json_encode($return);
}
}

View File

@@ -218,14 +218,17 @@ class SystemSignatureModel extends AbstractMapTrackingModel {
/**
* delete signature
* @param CharacterModel $characterModel
* @return bool
*/
public function delete(CharacterModel $characterModel){
public function delete(CharacterModel $characterModel) : bool {
$deleted = false;
if( !$this->dry() ){
// check if character has access
if($this->hasAccess($characterModel)){
$this->erase();
$deleted = $this->erase();
}
}
return $deleted;
}
/**

View File

@@ -2,7 +2,7 @@ define([
'jquery',
'app/init',
'app/util'
], function($, Init, Util) {
], ($, Init, Util) => {
'use strict';
let config = {
@@ -16,7 +16,7 @@ define([
* @param tempDate
* @param round
*/
let updateDateDiff = function(element, tempDate, round){
let updateDateDiff = (element, tempDate, round) => {
let diff = Util.getTimeDiffParts(tempDate, new Date());
let days = diff.days;
let hrs = diff.hours;
@@ -65,17 +65,21 @@ define([
/**
* destroy all active counter recursive
*/
$.fn.destroyTimestampCounter = function(){
$.fn.destroyTimestampCounter = function(recursive){
return this.each(function(){
let parentElement = $(this);
parentElement.find('[data-counter="init"]').each(function(){
let element = $(this);
let counterSelector = '[data-counter="init"]';
let counterElements = element.filter(counterSelector);
if(recursive){
counterElements = counterElements.add(element.find(counterSelector));
}
counterElements.each(function(){
let element = $(this);
let interval = element.data('interval');
if(interval){
clearInterval(interval);
element.removeAttr('data-counter')
.removeData('interval')
.removeClass('stopCounter');
element.removeAttr('data-counter').removeData('interval').removeClass('stopCounter');
}
});
});
@@ -102,18 +106,27 @@ define([
// show element (if invisible) after first update
element.css({'visibility': 'initial'});
let refreshIntervalId = window.setInterval(function(){
// calc ms until next second
// -> makes sure all counter update in sync no matter when init
let msUntilSecond = 1500 - new Date().getMilliseconds();
setTimeout(function(){
let refreshIntervalId = window.setInterval(function(){
// update element with current time
if( !element.hasClass('stopCounter')){
updateDateDiff(element, date, round);
}else{
clearInterval( element.data('interval') );
}
}, 500);
// update element with current time
if( !element.hasClass('stopCounter')){
updateDateDiff(element, date, round);
}else{
clearInterval( element.data('interval') );
}
}, 500);
element.data('interval', refreshIntervalId);
element.data('interval', refreshIntervalId);
}, msUntilSecond);
}
});
};
return {
updateDateDiff: updateDateDiff
};
});

View File

@@ -1,11 +1,41 @@
define([
'jquery',
'app/init',
'datatables.net',
'datatables.net-buttons',
'datatables.net-buttons-html',
'datatables.net-responsive',
'datatables.net-select'
], (a, b) => {
], ($, Init) => {
'use strict';
// all Datatables stuff is available...
let initDefaultDatatablesConfig = () => {
$.extend(true, $.fn.dataTable.defaults, {
pageLength: -1,
pagingType: 'simple_numbers',
lengthMenu: [[5, 10, 25, 50, -1], [5, 10, 25, 50, 'All']],
order: [], // no default order because columnDefs is empty
autoWidth: false,
responsive: {
breakpoints: Init.breakpoints,
details: false
},
columnDefs: [],
data: []
});
// global open event
$(document).on('destroy.dt', '.dataTable ', function(e, settings){
let table = $(this);
// remove all active counters in table
table.destroyTimestampCounter(true);
});
};
initDefaultDatatablesConfig();
});

View File

@@ -60,10 +60,12 @@ define(['jquery'], ($) => {
gitHubReleases: '/api/github/releases' // ajax URL - get release info from GitHub
},
breakpoints: [
{ name: 'desktop', width: Infinity },
{ name: 'tablet', width: 1200 },
{ name: 'fablet', width: 780 },
{ name: 'phone', width: 480 }
{ name: 'screen-xl', width: Infinity },
{ name: 'screen-l', width: 1600 },
{ name: 'screen-m', width: 1200 },
{ name: 'screen-d', width: 1000 },
{ name: 'screen-s', width: 780 },
{ name: 'screen-xs', width: 480 }
],
animationSpeed: {
splashOverlay: 300, // "splash" loading overlay

View File

@@ -65,7 +65,7 @@ define([
* @param connectionsData
*/
let addConnectionsOverlay = (connections, connectionsData) => {
let SystemSignatures = require('app/ui/system_signature');
let SystemSignatures = require('app/ui/module/system_signature_new');
/**
* add label to endpoint

View File

@@ -367,7 +367,7 @@ define([
connection &&
connectionData.signatures // signature data is required...
){
let SystemSignatures = require('app/ui/system_signature');
let SystemSignatures = require('app/ui/module/system_signature_new');
let connectionId = connection.getParameter('connectionId');
let sourceEndpoint = connection.endpoints[0];
@@ -1426,6 +1426,7 @@ define([
/**
* add a wormhole tooltip with wh specific data to elements
* @param tooltipData
* @param options
* @returns {*}
*/
$.fn.addWormholeInfoTooltip = function(tooltipData, options){

View File

@@ -5,13 +5,14 @@ define([
'app/map/map',
'app/map/util',
'sortable',
'app/ui/system_info',
'app/ui/system_graph',
'app/ui/system_signature',
'app/ui/system_route',
'app/ui/system_intel',
'app/ui/system_killboard',
'app/ui/connection_info',
'app/ui/module/system_info',
'app/ui/module/system_graph',
//'app/ui/module/system_signature',
'app/ui/module/system_signature_new',
'app/ui/module/system_route',
'app/ui/module/system_intel',
'app/ui/module/system_killboard',
'app/ui/module/connection_info',
'app/counter'
], (
$,
@@ -180,11 +181,12 @@ define([
* @param moduleElement
* @param Module
* @param callback
* @param addSpacer
*/
let removeModule = (moduleElement, Module, callback, addSpacer) => {
if(moduleElement.length > 0){
if(typeof Module.beforeReDraw === 'function'){
Module.beforeReDraw();
if(typeof Module.beforeHide === 'function'){
Module.beforeHide(moduleElement);
}
moduleElement.velocity('reverse',{
@@ -336,8 +338,6 @@ define([
}
};
return new Promise(drawModuleExecutor);
};
@@ -539,23 +539,25 @@ define([
let clickY = e.pageY - posY;
// check for top-left click
if(clickX <= 8 && clickY <= 8 && clickX >= 0 && clickY >= 0){
if(clickX <= 9 && clickY <= 9 && clickX >= 0 && clickY >= 0){
// remember height
if(! moduleElement.data('origHeight')){
if( !moduleElement.data('origHeight') ){
moduleElement.data('origHeight', moduleElement.outerHeight());
}
if(moduleElement.hasClass( config.moduleClosedClass )){
if(moduleElement.hasClass(config.moduleClosedClass)){
let moduleHeight = moduleElement.data('origHeight');
moduleElement.velocity('finish').velocity({
height: [ moduleHeight + 'px', [ 400, 15 ] ]
},{
duration: 400,
easing: 'easeOutSine',
complete: function(){
moduleElement.removeClass( config.moduleClosedClass );
complete: function(moduleElement){
moduleElement = $(moduleElement);
moduleElement.removeClass(config.moduleClosedClass);
moduleElement.removeData('origHeight');
moduleElement.css({height: ''});
}
});
}else{
@@ -564,8 +566,9 @@ define([
},{
duration: 400,
easing: 'easeOutSine',
complete: function(){
moduleElement.addClass( config.moduleClosedClass );
complete: function(moduleElement){
moduleElement = $(moduleElement);
moduleElement.addClass(config.moduleClosedClass);
}
});
}

View File

@@ -70,7 +70,7 @@ define([
dynamicElementWrapperId: 'pf-dialog-wrapper', // class for container element that holds hidden "context menus"
// system signature module
systemSignatureModuleClass: 'pf-signature-table-module', // module wrapper (signatures)
systemSignatureModuleClass: 'pf-system-signature-module', // module wrapper (signatures)
systemIntelModuleClass: 'pf-system-intel-module', // module wrapper (intel)
};
@@ -744,7 +744,7 @@ define([
// global "modal" callback (for all modals)
$('body').on('hide.bs.modal', '> .modal', function(e) {
let modalElement = $(this);
modalElement.destroyTimestampCounter();
modalElement.destroyTimestampCounter(true);
// destroy all Select2
modalElement.find('.' + Util.config.select2Class)

View File

@@ -321,10 +321,6 @@ define([
tooltipElements.tooltip();
});
systemTable.on('destroy.dt', function(){
$(this).destroyTimestampCounter();
});
// prepare data for dataTables
let systemsData = [];
for(let i = 0; i < mapData.data.systems.length; i++){
@@ -470,11 +466,6 @@ define([
lengthMenu: [[5, 10, 20, 50, -1], [5, 10, 20, 50, 'All']],
ordering: true,
order: [[ 9, 'desc' ], [ 3, 'asc' ]],
autoWidth: false,
responsive: {
breakpoints: Init.breakpoints,
details: false
},
hover: false,
data: systemsData,
columnDefs: [],
@@ -488,7 +479,7 @@ define([
{
title: 'type',
width: '25px',
className: ['min-desktop'].join(' '),
className: ['min-screen-l'].join(' '),
data: 'type',
render: {
_: 'type',
@@ -506,7 +497,7 @@ define([
},{
title: 'sec',
width: '18px',
className: ['text-center', 'min-desktop'].join(' '),
className: ['text-center', 'min-screen-l'].join(' '),
searchable: false,
data: 'trueSec',
render: {
@@ -516,7 +507,7 @@ define([
},{
title: '<i class="fas fa-skull" title="shattered" data-toggle="tooltip"></i>',
width: '10px',
className: ['text-center', 'min-desktop'].join(' '),
className: ['text-center', 'min-screen-l'].join(' '),
searchable: false,
data: 'shattered',
render: {
@@ -593,7 +584,7 @@ define([
title: 'updated',
width: '80px',
searchable: false,
className: ['text-right', config.tableCellCounterClass, 'min-desktop'].join(' '),
className: ['text-right', config.tableCellCounterClass, 'min-screen-l'].join(' '),
data: 'updated',
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
$(cell).initTimestampCounter();
@@ -863,11 +854,6 @@ define([
lengthMenu: [[5, 10, 20, 50, -1], [5, 10, 20, 50, 'All']],
ordering: true,
order: [[ 3, 'asc' ]],
autoWidth: false,
responsive: {
breakpoints: Init.breakpoints,
details: false
},
hover: false,
data: usersData,
language: {
@@ -962,7 +948,7 @@ define([
width: 26,
orderable: false,
searchable: false,
className: [config.tableCellImageClass, config.tableCellImageSmallClass, 'min-desktop'].join(' '),
className: [config.tableCellImageClass, config.tableCellImageSmallClass, 'min-screen-l'].join(' '),
data: 'corporation',
render: {
_: function(data, type, row, meta){
@@ -978,7 +964,7 @@ define([
title: 'corporation',
orderable: true,
searchable: true,
className: [config.tableCellActionClass, 'min-desktop'].join(' '),
className: [config.tableCellActionClass, 'min-screen-l'].join(' '),
data: 'corporation',
render: {
_: function (data, type, row, meta) {
@@ -1041,7 +1027,7 @@ define([
width: 30,
orderable: true,
searchable: true,
className: ['text-right', 'min-desktop'].join(' '),
className: ['text-right', 'min-screen-l'].join(' '),
data: 'role',
render: {
_: function (data, type, row, meta) {

View File

@@ -654,7 +654,7 @@ define([
*/
$.fn.initSignatureTypeSelect = function(options, hasOptGroups){
let defaultConfig = {
minimumResultsForSearch: 6,
minimumResultsForSearch: 10,
width: '220px',
dropdownParent: this.parents('.popover-content')
};

View File

@@ -1,5 +1,5 @@
/**
* connection info module
* Connection info module
*/
define([
@@ -764,7 +764,7 @@ define([
});
for(let i = 0; i < animationRows.length; i++){
$(animationRows[i]).pulseTableRow($(animationRows[i]).data('animationStatus'));
$(animationRows[i]).pulseBackgroundColor($(animationRows[i]).data('animationStatus'));
$(animationRows[i]).removeData('animationStatus');
}
@@ -808,10 +808,6 @@ define([
$(cell).html(content);
});
});
logTable.on('destroy.dt', function(){
$(this).destroyTimestampCounter();
});
}
};
@@ -981,7 +977,6 @@ define([
* @returns {*|jQuery|HTMLElement}
*/
let getModule = (parentElement, mapId, connections) => {
// create new module container
let moduleElement = $('<div>').append(
$('<div>', {
class: config.moduleHeadClass

View File

@@ -1,5 +1,5 @@
/**
* System info module
* System info module
*/
define([
@@ -156,8 +156,6 @@ define([
* @param systemData
*/
let getModule = (parentElement, mapId, systemData) => {
// create new module container
let moduleElement = $('<div>');
// store systemId -> module can be updated with the correct data

View File

@@ -1,5 +1,5 @@
/**
* system route module
* System intel module
*/
define([
@@ -16,6 +16,8 @@ define([
moduleName: 'systemIntel',
moduleHeadClass: 'pf-module-head', // class for module header
moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler
// system info module
moduleTypeClass: 'pf-system-intel-module', // class for this module
// headline toolbar
@@ -398,7 +400,6 @@ define([
let getModule = (parentElement, mapId, systemData) => {
let corporationId = Util.getCurrentUserInfo('corporationId');
// create new module container
let moduleElement = $('<div>').append(
$('<div>', {
class: config.moduleHeadClass
@@ -460,7 +461,7 @@ define([
class: 'text-center',
data: 'status',
render: {
display: data => getStatusData(data),
display: data => getStatusData(data),
sort: data => data.id
},
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
@@ -542,7 +543,7 @@ define([
data: null,
render: {
display: data => {
let icon = '<i class="fas fa-pencil-alt"></i>';
let icon = '<i class="fas fa-pen"></i>';
if(data.corporation.id !== corporationId){
icon = '';
}
@@ -660,7 +661,7 @@ define([
for(let i = 0; i < animationRows.length; i++){
let animationRow = $(animationRows[i]);
animationRow.pulseTableRow(animationRow.data('animationStatus'));
animationRow.pulseBackgroundColor(animationRow.data('animationStatus'));
animationRow.removeData('animationStatus');
}
},
@@ -817,11 +818,24 @@ define([
});
};
/**
* before module destroy callback
* @param moduleElement
*/
let beforeDestroy = moduleElement => {
// Destroying the data tables throws
// -> safety remove all dataTables
let structureTableElement = moduleElement.find('.' + config.systemStructuresTableClass);
let tableApi = structureTableElement.DataTable();
tableApi.destroy();
};
return {
config: config,
getModule: getModule,
initModule: initModule,
updateModule: updateModule
updateModule: updateModule,
beforeDestroy: beforeDestroy
};
});

View File

@@ -1,3 +1,7 @@
/**
* System killboard module
*/
define([
'jquery',
'app/init',
@@ -378,7 +382,6 @@ define([
* @returns {jQuery}
*/
let getModule = (parentElement, mapId, systemData) => {
// create new module container
let moduleElement = $('<div>').append(
$('<div>', {
class: config.moduleHeadClass

View File

@@ -1,5 +1,5 @@
/**
* system route module
* System route module
*/
define([
@@ -18,8 +18,6 @@ define([
moduleHeadClass: 'pf-module-head', // class for module header
moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler
routeCacheTTL: 5, // route cache timer (client) in seconds
// system route module
moduleTypeClass: 'pf-system-route-module', // class for this module
@@ -42,7 +40,9 @@ define([
dataTableRouteCellClass: 'pf-table-route-cell', // class for "route" cells
dataTableJumpCellClass: 'pf-table-jump-cell', // class for "route jump" cells
rallyClass: 'pf-rally' // class for "rally point" style
rallyClass: 'pf-rally', // class for "rally point" style
routeCacheTTL: 5 // route cache timer (client) in seconds
};
// cache for system routes
@@ -875,8 +875,6 @@ define([
* @returns {jQuery}
*/
let getModule = (parentElement, mapId, systemData) => {
// create new module container
let moduleElement = $('<div>').append(
$('<div>', {
class: config.moduleHeadClass
@@ -921,7 +919,7 @@ define([
moduleElement.append(table);
// init empty table
let routesTable = table.DataTable( {
let routesTable = table.dataTable( {
paging: false,
ordering: true,
order: [[ 2, 'asc' ], [ 0, 'asc' ]],
@@ -1137,7 +1135,7 @@ define([
for(let i = 0; i < animationRows.length; i++){
let animationRow = $(animationRows[i]);
animationRow.pulseTableRow(animationRow.data('animationStatus'));
animationRow.pulseBackgroundColor(animationRow.data('animationStatus'));
animationRow.removeData('animationStatus');
}
},

View File

@@ -1,5 +1,5 @@
/**
* System signature module
* System signature module
*/
define([
@@ -24,7 +24,7 @@ define([
moduleClass: 'pf-module', // class for each module
// system signature module
moduleTypeClass: 'pf-signature-table-module', // module wrapper
moduleTypeClass: 'pf-system-signature-module', // module wrapper
// headline toolbar
moduleHeadlineIconClass: 'pf-module-icon-button', // class for toolbar icons in the head
@@ -395,7 +395,7 @@ define([
if(changedRowElement){
// highlight
changedRowElement.pulseTableRow('changed');
changedRowElement.pulseBackgroundColor('changed');
notificationCounter.changed++;
}
}
@@ -439,7 +439,7 @@ define([
if(newRowElement){
// highlight
newRowElement.pulseTableRow('added');
newRowElement.pulseBackgroundColor('added');
notificationCounter.added++;
}
}
@@ -921,7 +921,7 @@ define([
/**
* check the "delete signature" button. show/hide the button if a signature is selected
* @param moduleElement
* @param tableApi
*/
let checkDeleteSignaturesButton = tableApi => {
let selectedRows = getSelectedRows(tableApi);
@@ -1907,8 +1907,8 @@ define([
highlight: false,
title: 'filter groups',
value: selectedValues,
source: sourceOptions,
prepend: prependOptions,
source: sourceOptions,
inputclass: config.editableUnknownInputClass,
display: function(value, sourceData){
// update filter button label
@@ -2231,7 +2231,7 @@ define([
if(newRowElement){
// highlight
newRowElement.pulseTableRow('added');
newRowElement.pulseBackgroundColor('added');
// prepare "add signature" table for new entry -> reset -------------------
let signatureData = formatSignatureData(systemData, [emptySignatureData], emptySignatureOptions);
@@ -2429,7 +2429,6 @@ define([
* @returns {*|jQuery|HTMLElement}
*/
let getModule = function(parentElement, mapId, systemData){
// create new module container
let moduleElement = $('<div>').append(
$('<div>', {
class: config.moduleHeadClass
@@ -2481,9 +2480,9 @@ define([
};
/**
* before module reDraw callback
* before module hide callback
*/
let beforeReDraw = () => {
let beforeHide = () => {
// disable update
lockSignatureTable();
};
@@ -2504,8 +2503,8 @@ define([
config: config,
getModule: getModule,
initModule: initModule,
beforeReDraw: beforeReDraw,
updateModule: updateModule,
beforeHide: beforeHide,
beforeDestroy: beforeDestroy,
getAllSignatureNamesBySystem: getAllSignatureNamesBySystem
};

File diff suppressed because it is too large Load Diff

View File

@@ -33,7 +33,6 @@ define([
// head
headMapTrackingId: 'pf-head-map-tracking', // id for "map tracking" toggle (checkbox)
headCharacterSwitchId: 'pf-head-character-switch', // id for "character switch" popover
headCurrentLocationId: 'pf-head-current-location', // id for "show current location" element
// menu
@@ -66,10 +65,12 @@ define([
// animation
animationPulseSuccessClass: 'pf-animation-pulse-success', // animation class
animationPulseWarningClass: 'pf-animation-pulse-warning', // animation class
animationPulseDangerClass: 'pf-animation-pulse-danger', // animation class
// popover
popoverSmallClass: 'pf-popover-small', // class for "small" popover
popoverTriggerClass: 'pf-popover-trigger', // class for "popover" trigger elements
popoverSmallClass: 'pf-popover-small', // class for small "popover"
popoverCharacterClass: 'pf-popover-character', // class for character "popover"
// help
helpDefaultClass: 'pf-help-default', // class for "help" tooltip elements
@@ -231,7 +232,7 @@ define([
}else{
callback(responseData.img);
}
}).fail(function( jqXHR, status, error) {
}).fail(function( jqXHR, status, error){
let reason = status + ' ' + error;
showNotify({title: jqXHR.status + ': getCaptchaImage', text: reason, type: 'error'});
});
@@ -424,7 +425,7 @@ define([
let width = element.offsetWidth;
let height = element.offsetHeight;
while(element.offsetParent) {
while(element.offsetParent){
element = element.offsetParent;
top += element.offsetTop;
left += element.offsetLeft;
@@ -494,7 +495,7 @@ define([
* @returns {*}
*/
$.fn.destroyTooltip = function(recursive){
return this.each(function() {
return this.each(function(){
let element = $(this);
let tooltipSelector = '[title]';
let tooltipElements = element.filter(tooltipSelector);
@@ -502,18 +503,20 @@ define([
tooltipElements = tooltipElements.add(element.find(tooltipSelector));
}
tooltipElements.each(function() {
tooltipElements.each(function(){
$(this).tooltip('destroy');
});
});
};
/**
* adds a popup tooltip with character information (created/updated)
* add a popup tooltip with character information (created/updated)
* @param tooltipData
* @param options
* @returns {*}
*/
$.fn.addCharacterInfoTooltip = function(tooltipData){
let element = $(this);
$.fn.addCharacterInfoTooltip = function(tooltipData, options){
let data = {};
if(
tooltipData.created.character &&
@@ -522,59 +525,60 @@ define([
let createdData = tooltipData.created;
let updatedData = tooltipData.updated;
// check if data has changed
if(
element.data('created') !== createdData.created ||
element.data('updated') !== updatedData.updated
){
// data changed
// set new data for next check
element.data('created', createdData.created);
element.data('updated', updatedData.updated);
let statusCreatedClass = getStatusInfoForCharacter(createdData.character, 'class');
let statusUpdatedClass = getStatusInfoForCharacter(updatedData.character, 'class');
let statusCreatedClass = getStatusInfoForCharacter(createdData.character, 'class');
let statusUpdatedClass = getStatusInfoForCharacter(updatedData.character, 'class');
// convert timestamps
let dateCreated = new Date(createdData.created * 1000);
let dateUpdated = new Date(updatedData.updated * 1000);
let dateCreatedUTC = convertDateToUTC(dateCreated);
let dateUpdatedUTC = convertDateToUTC(dateUpdated);
// convert timestamps
let dateCreated = new Date(createdData.created * 1000);
let dateUpdated = new Date(updatedData.updated * 1000);
let dateCreatedUTC = convertDateToUTC(dateCreated);
let dateUpdatedUTC = convertDateToUTC(dateUpdated);
let data = {
created: createdData,
updated: updatedData,
createdTime: convertDateToString(dateCreatedUTC),
updatedTime: convertDateToString(dateUpdatedUTC),
createdStatusClass: statusCreatedClass,
updatedStatusClass: statusUpdatedClass
};
requirejs(['text!templates/tooltip/character_info.html', 'mustache'], function(template, Mustache) {
let content = Mustache.render(template, data);
element.popover({
placement: 'top',
html: true,
trigger: 'hover',
content: '',
container: 'body',
title: 'Created / Updated',
delay: {
show: 250,
hide: 0
}
});
// set new popover content
let popover = element.data('bs.popover');
popover.options.content = content;
});
}
data = {
popoverClass: config.popoverCharacterClass,
ccpImageServerUrl: Init.url.ccpImageServer,
created: createdData,
updated: updatedData,
createdTime: convertDateToString(dateCreatedUTC),
updatedTime: convertDateToString(dateUpdatedUTC),
createdStatusClass: statusCreatedClass,
updatedStatusClass: statusUpdatedClass
};
}
return element;
let defaultOptions = {
placement: 'top',
html: true,
trigger: 'hover',
container: 'body',
title: 'Created / Updated',
delay: {
show: 150,
hide: 0
}
};
options = $.extend({}, defaultOptions, options);
return this.each(function(){
let element = $(this);
requirejs(['text!templates/tooltip/character_info.html', 'mustache'], (template, Mustache) => {
let content = Mustache.render(template, data);
element.popover(options);
// set new popover content
let popover = element.data('bs.popover');
popover.options.content = content;
if(options.show){
element.popover('show');
}
});
});
};
/**
@@ -585,10 +589,10 @@ define([
let elements = $(this);
let eventNamespace = 'hideCharacterPopup';
requirejs(['text!templates/tooltip/character_switch.html', 'mustache'], function (template, Mustache) {
requirejs(['text!templates/tooltip/character_switch.html', 'mustache'], function (template, Mustache){
let data = {
id: config.headCharacterSwitchId,
popoverClass: config.popoverCharacterClass,
browserTabId: getBrowserTabId(),
routes: Init.routes,
userData: userData,
@@ -608,7 +612,7 @@ define([
let content = Mustache.render(template, data);
return elements.each(function() {
return elements.each(function(){
let element = $(this);
// check if popover already exists -> remove it
@@ -616,7 +620,7 @@ define([
element.off('click').popover('destroy');
}
element.on('click', function(e) {
element.on('click', function(e){
e.preventDefault();
e.stopPropagation();
@@ -631,7 +635,7 @@ define([
if(popoverData === undefined){
button.on('shown.bs.popover', function (e) {
button.on('shown.bs.popover', function (e){
let tmpPopupElement = $(this).data('bs.popover').tip();
tmpPopupElement.find('.btn').on('click', function(e){
// close popover
@@ -662,7 +666,7 @@ define([
// character switch detected
$('body').data('characterSwitch', true);
// ... and remove "characterSwitch" data again! after "unload"
setTimeout(function() {
setTimeout(function(){
$('body').removeData('characterSwitch');
}, 500);
});
@@ -690,7 +694,7 @@ define([
* @returns {*}
*/
$.fn.destroyPopover = function(recursive){
return this.each(function() {
return this.each(function(){
let element = $(this);
let popoverSelector = '.' + config.popoverTriggerClass;
let popoverElements = element.filter(popoverSelector);
@@ -698,7 +702,7 @@ define([
popoverElements = popoverElements.add(element.find(popoverSelector));
}
popoverElements.each(function() {
popoverElements.each(function(){
let popoverElement = $(this);
if(popoverElement.data('bs.popover')){
popoverElement.popover('destroy');
@@ -713,9 +717,9 @@ define([
* @returns {*}
*/
$.fn.initPopoverClose = function(eventNamespace){
return this.each(function() {
$('body').off('click.' + eventNamespace).on('click.' + eventNamespace + ' contextmenu', function (e) {
$('.' + config.popoverTriggerClass).each(function () {
return this.each(function(){
$('body').off('click.' + eventNamespace).on('click.' + eventNamespace + ' contextmenu', function (e){
$('.' + config.popoverTriggerClass).each(function (){
let popoverElement = $(this);
//the 'is' for buttons that trigger popups
//the 'has' for icons within a button that triggers a popup
@@ -743,7 +747,7 @@ define([
* @returns {*}
*/
$.fn.setPopoverSmall = function(){
return this.each(function() {
return this.each(function(){
let element = $(this);
let popover = element.data('bs.popover');
if(popover){
@@ -760,7 +764,7 @@ define([
$.fn.showMessage = function(config){
let containerElement = $(this);
requirejs(['text!templates/form/message.html', 'mustache'], function(template, Mustache) {
requirejs(['text!templates/form/message.html', 'mustache'], function(template, Mustache){
let messageTypeClass = 'alert-danger';
let messageTextClass = 'txt-color-danger';
@@ -809,7 +813,7 @@ define([
* add/remove css class for keyframe animation
* @returns {any|JQuery|*}
*/
$.fn.pulseTableRow = function(status, clear){
$.fn.pulseBackgroundColor = function(status, clear){
let animationClass = '';
switch(status){
@@ -819,9 +823,12 @@ define([
case 'changed':
animationClass = config.animationPulseWarningClass;
break;
case 'deleted':
animationClass = config.animationPulseDangerClass;
break;
}
let clearTimer = function(element) {
let clearTimer = element => {
element.removeClass( animationClass );
let currentTimer = element.data('animationTimer');
@@ -841,7 +848,7 @@ define([
}
if(clear !== true){
element.addClass( animationClass );
element.addClass(animationClass);
let timer = setTimeout(clearTimer, 1500, element);
element.data('animationTimer', timer);
animationTimerCache[timer] = true;
@@ -902,14 +909,14 @@ define([
const prepareSafeListener = (listener, passive) => {
if (!passive) return listener;
return function (e) {
return function (e){
e.preventDefault = () => {};
return listener.call(this, e);
};
};
const overwriteAddEvent = (superMethod) => {
EventTarget.prototype.addEventListener = function (type, listener, options) { // jshint ignore:line
EventTarget.prototype.addEventListener = function (type, listener, options){ // jshint ignore:line
const usesListenerOptions = typeof options === 'object';
const useCapture = usesListenerOptions ? options.capture : options;
@@ -927,20 +934,20 @@ define([
try {
const opts = Object.defineProperty({}, 'passive', {
get() {
get(){
supported = true;
}
});
window.addEventListener('test', null, opts);
window.removeEventListener('test', null, opts);
} catch (e) {}
} catch (e){}
return supported;
};
let supportsPassive = eventListenerOptionsSupported ();
if (supportsPassive) {
if (supportsPassive){
const addEvent = EventTarget.prototype.addEventListener; // jshint ignore:line
overwriteAddEvent(addEvent);
}
@@ -953,8 +960,8 @@ define([
// Array diff
// [1,2,3,4,5,6].diff( [3,4,5] );
// => [1, 2, 6]
Array.prototype.diff = function(a) {
return this.filter(function(i) {return a.indexOf(i) < 0;});
Array.prototype.diff = function(a){
return this.filter(function(i){return a.indexOf(i) < 0;});
};
/**
@@ -962,7 +969,7 @@ define([
* @param p
* @returns {Array.<T>}
*/
Array.prototype.sortBy = function(p) {
Array.prototype.sortBy = function(p){
return this.slice(0).sort((a,b) => {
return (a[p] > b[p]) ? 1 : (a[p] < b[p]) ? -1 : 0;
});
@@ -972,10 +979,10 @@ define([
* get hash from string
* @returns {number}
*/
String.prototype.hashCode = function() {
String.prototype.hashCode = function(){
let hash = 0, i, chr;
if (this.length === 0) return hash;
for (i = 0; i < this.length; i++) {
for (i = 0; i < this.length; i++){
chr = this.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
@@ -1094,6 +1101,12 @@ define([
if(resultsWrapper){
resultsWrapper.mCustomScrollbar('destroy');
}
// select2 sets :focus to the select2 <input> field. This is bad!
// we want to keep the focus where it is (e.g. signature table cell)
// the only way to prevent this is to remove the element
// https://stackoverflow.com/questions/17995057/prevent-select2-from-autmatically-focussing-its-search-input-when-dropdown-is-op
$(this).parents('.editableform').find(this).next().find('.select2-selection').remove();
});
};
@@ -1262,7 +1275,7 @@ define([
// line breaks are 2 characters!
let newLines = value.match(/(\r\n|\n|\r)/g);
let addition = 0;
if (newLines != null) {
if(newLines != null){
addition = newLines.length;
}
inputLength += addition;
@@ -1300,7 +1313,7 @@ define([
* stop browser tab title "blinking"
*/
let stopTabBlink = function(){
requirejs(['notification'], function(Notification) {
requirejs(['notification'], function(Notification){
Notification.stopTabBlink();
});
};
@@ -1388,7 +1401,7 @@ define([
*/
let ajaxSetup = () => {
$.ajaxSetup({
beforeSend: function(jqXHR, settings) {
beforeSend: function(jqXHR, settings){
// store request URL for later use (e.g. in error messages)
jqXHR.url = location.protocol + '//' + location.host + settings.url;
@@ -1579,11 +1592,13 @@ define([
};
/**
* get Area ID by security string
* get areaId by security string
* areaId is required as a key for signature names
* if areaId is 0, no signature data is available for this system
* @param security
* @returns {number}
*/
let getAreaIdBySecurity = (security) => {
let getAreaIdBySecurity = security => {
let areaId = 0;
switch(security){
case 'H':
@@ -1646,7 +1661,6 @@ define([
* @returns {string}
*/
let getStatusInfoForCharacter = (characterData, option) => {
let statusInfo = '';
// character status can not be checked if there are no reference data
@@ -1898,20 +1912,17 @@ define([
/**
* get signature group information
* @param option
* @returns {{}}
* @returns {Array}
*/
let getSignatureGroupInfo = function(option){
let groupInfo = {};
for (let prop in Init.signatureGroups) {
if(Init.signatureGroups.hasOwnProperty(prop)){
prop = parseInt(prop);
groupInfo[prop] = Init.signatureGroups[prop][option];
}
let getSignatureGroupOptions = option => {
let options = [];
for(let [key, data] of Object.entries(Init.signatureGroups)){
options.push({
value: parseInt(key),
text: data[option]
});
}
return groupInfo;
return options;
};
/**
@@ -1943,28 +1954,22 @@ define([
* @param name
* @returns {number}
*/
let getSignatureTypeIdByName = function(systemData, sigGroupId, name){
let getSignatureTypeIdByName = (systemData, sigGroupId, name) => {
let signatureTypeId = 0;
let areaId = getAreaIdBySecurity(systemData.security);
if(areaId > 0){
let signatureNames = getAllSignatureNames(systemData.type.id, areaId, sigGroupId );
let signatureNames = getAllSignatureNames(systemData.type.id, areaId, sigGroupId);
name = name.toLowerCase();
for(let prop in signatureNames) {
for(let prop in signatureNames){
if(
signatureNames.hasOwnProperty(prop) &&
signatureNames[prop].toLowerCase() === name
){
signatureTypeId = parseInt( prop );
signatureTypeId = parseInt(prop);
break;
}
}
}
return signatureTypeId;
};
@@ -1995,9 +2000,8 @@ define([
* to keep the data always up2data
* @param mapUserData
*/
let setCurrentMapUserData = (mapUserData) => {
let setCurrentMapUserData = mapUserData => {
Init.currentMapUserData = mapUserData;
return getCurrentMapUserData();
};
@@ -2006,7 +2010,7 @@ define([
* @param mapId
* @returns {boolean}
*/
let getCurrentMapUserData = (mapId) => {
let getCurrentMapUserData = mapId => {
let currentMapUserData = false;
if(Init.currentMapUserData){
@@ -2041,7 +2045,7 @@ define([
* @param mapId
* @returns {boolean|int}
*/
let getCurrentMapUserDataIndex = (mapId) => {
let getCurrentMapUserDataIndex = mapId => {
return getDataIndexByMapId(Init.currentMapUserData, mapId);
};
@@ -2049,7 +2053,7 @@ define([
* update cached mapUserData for a single map
* @param mapUserData
*/
let updateCurrentMapUserData = (mapUserData) => {
let updateCurrentMapUserData = mapUserData => {
let mapUserDataIndex = getCurrentMapUserDataIndex( mapUserData.config.id );
if( !Array.isArray(Init.currentMapUserData) ){
@@ -2072,7 +2076,7 @@ define([
* to keep the data always up2data
* @param mapData
*/
let setCurrentMapData = (mapData) => {
let setCurrentMapData = mapData => {
Init.currentMapData = mapData;
return getCurrentMapData();
@@ -2083,7 +2087,7 @@ define([
* @param mapId
* @returns {boolean}
*/
let getCurrentMapData = (mapId) => {
let getCurrentMapData = mapId => {
let currentMapData = false;
if( mapId === parseInt(mapId, 10) ){
@@ -2107,7 +2111,7 @@ define([
* @param mapId
* @returns {boolean|int}
*/
let getCurrentMapDataIndex = (mapId) => {
let getCurrentMapDataIndex = mapId => {
return getDataIndexByMapId(Init.currentMapData, mapId);
};
@@ -2115,7 +2119,7 @@ define([
* update cached mapData for a single map
* @param mapData
*/
let updateCurrentMapData = (mapData) => {
let updateCurrentMapData = mapData => {
let mapDataIndex = getCurrentMapDataIndex( mapData.config.id );
if(mapDataIndex !== false){
@@ -2146,7 +2150,7 @@ define([
* delete map data by mapId from currentMapData
* @param mapId
*/
let deleteCurrentMapData = (mapId) => {
let deleteCurrentMapData = mapId => {
Init.currentMapData = Init.currentMapData.filter((mapData) => {
return (mapData.config.id !== mapId);
});
@@ -2176,7 +2180,7 @@ define([
* @param option
* @returns {boolean}
*/
let getCurrentUserInfo = (option) => {
let getCurrentUserInfo = option => {
let currentUserData = getCurrentUserData();
let userInfo = false;
@@ -2297,7 +2301,7 @@ define([
characterData = characterData.filter(function(tmpCharacterData, index, allData){
let keepData = true;
for(let tmpJump in data) {
for(let tmpJump in data){
// just scan systems with > jumps than current system
if(tmpJump > jumps){
let filteredFinalData = data[tmpJump].filter(filterFinalCharData, tmpCharacterData);
@@ -2325,7 +2329,7 @@ define([
}
jumps++;
for(let prop in nearBySystems.tree) {
for(let prop in nearBySystems.tree){
if( nearBySystems.tree.hasOwnProperty(prop) ){
let tmpSystemData = nearBySystems.tree[prop];
data = getNearByCharacterData(tmpSystemData, userData, jumps, data);
@@ -2374,7 +2378,7 @@ define([
responseData.systemData &&
responseData.systemData.length > 0
){
for (let j = 0; j < responseData.systemData.length; j++) {
for (let j = 0; j < responseData.systemData.length; j++){
showNotify({title: this.description, text: 'System: ' + responseData.systemData[j].name, type: 'success'});
}
}
@@ -2388,7 +2392,7 @@ define([
}
}
}).fail(function( jqXHR, status, error) {
}).fail(function( jqXHR, status, error){
let reason = status + ' ' + error;
showNotify({title: jqXHR.status + ': ' + this.description, text: reason, type: 'warning'});
});
@@ -2452,7 +2456,7 @@ define([
}else{
showNotify({title: 'Open window in client', text: 'Check your EVE client', type: 'success'});
}
}).fail(function( jqXHR, status, error) {
}).fail(function( jqXHR, status, error){
let reason = status + ' ' + error;
showNotify({title: jqXHR.status + ': openWindow', text: reason, type: 'error'});
});
@@ -2585,9 +2589,9 @@ define([
element.off(eventName).on(eventName, function(e){
clicks++;
if (clicks === 1) {
if (clicks === 1){
setTimeout(element => {
if(clicks === 1) {
if(clicks === 1){
singleClickCallback.call(element, e);
} else {
doubleClickCallback.call(element, e);
@@ -2666,7 +2670,7 @@ define([
if(data.reroute){
redirect(data.reroute, ['logout']);
}
}).fail(function( jqXHR, status, error) {
}).fail(function( jqXHR, status, error){
let reason = status + ' ' + error;
showNotify({title: jqXHR.status + ': logout', text: reason, type: 'error'});
});
@@ -2709,13 +2713,13 @@ define([
let name = cname + '=';
let ca = document.cookie.split(';');
for(let i = 0; i <ca.length; i++) {
for(let i = 0; i <ca.length; i++){
let c = ca[i];
while (c.charAt(0) === ' ') {
while (c.charAt(0) === ' '){
c = c.substring(1);
}
if (c.indexOf(name) === 0) {
if (c.indexOf(name) === 0){
return c.substring(name.length,c.length);
}
}
@@ -2757,7 +2761,7 @@ define([
getSecurityClassForSystem: getSecurityClassForSystem,
getTrueSecClassForSystem: getTrueSecClassForSystem,
getStatusInfoForSystem: getStatusInfoForSystem,
getSignatureGroupInfo: getSignatureGroupInfo,
getSignatureGroupOptions: getSignatureGroupOptions,
getAllSignatureNames: getAllSignatureNames,
getSignatureTypeIdByName: getSignatureTypeIdByName,
getAreaIdBySecurity: getAreaIdBySecurity,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,7 @@ define([
'jquery',
'app/init',
'app/util'
], function($, Init, Util) {
], ($, Init, Util) => {
'use strict';
let config = {
@@ -16,7 +16,7 @@ define([
* @param tempDate
* @param round
*/
let updateDateDiff = function(element, tempDate, round){
let updateDateDiff = (element, tempDate, round) => {
let diff = Util.getTimeDiffParts(tempDate, new Date());
let days = diff.days;
let hrs = diff.hours;
@@ -65,17 +65,21 @@ define([
/**
* destroy all active counter recursive
*/
$.fn.destroyTimestampCounter = function(){
$.fn.destroyTimestampCounter = function(recursive){
return this.each(function(){
let parentElement = $(this);
parentElement.find('[data-counter="init"]').each(function(){
let element = $(this);
let counterSelector = '[data-counter="init"]';
let counterElements = element.filter(counterSelector);
if(recursive){
counterElements = counterElements.add(element.find(counterSelector));
}
counterElements.each(function(){
let element = $(this);
let interval = element.data('interval');
if(interval){
clearInterval(interval);
element.removeAttr('data-counter')
.removeData('interval')
.removeClass('stopCounter');
element.removeAttr('data-counter').removeData('interval').removeClass('stopCounter');
}
});
});
@@ -102,18 +106,27 @@ define([
// show element (if invisible) after first update
element.css({'visibility': 'initial'});
let refreshIntervalId = window.setInterval(function(){
// calc ms until next second
// -> makes sure all counter update in sync no matter when init
let msUntilSecond = 1500 - new Date().getMilliseconds();
setTimeout(function(){
let refreshIntervalId = window.setInterval(function(){
// update element with current time
if( !element.hasClass('stopCounter')){
updateDateDiff(element, date, round);
}else{
clearInterval( element.data('interval') );
}
}, 500);
// update element with current time
if( !element.hasClass('stopCounter')){
updateDateDiff(element, date, round);
}else{
clearInterval( element.data('interval') );
}
}, 500);
element.data('interval', refreshIntervalId);
element.data('interval', refreshIntervalId);
}, msUntilSecond);
}
});
};
return {
updateDateDiff: updateDateDiff
};
});

View File

@@ -1,11 +1,41 @@
define([
'jquery',
'app/init',
'datatables.net',
'datatables.net-buttons',
'datatables.net-buttons-html',
'datatables.net-responsive',
'datatables.net-select'
], (a, b) => {
], ($, Init) => {
'use strict';
// all Datatables stuff is available...
let initDefaultDatatablesConfig = () => {
$.extend(true, $.fn.dataTable.defaults, {
pageLength: -1,
pagingType: 'simple_numbers',
lengthMenu: [[5, 10, 25, 50, -1], [5, 10, 25, 50, 'All']],
order: [], // no default order because columnDefs is empty
autoWidth: false,
responsive: {
breakpoints: Init.breakpoints,
details: false
},
columnDefs: [],
data: []
});
// global open event
$(document).on('destroy.dt', '.dataTable ', function(e, settings){
let table = $(this);
// remove all active counters in table
table.destroyTimestampCounter(true);
});
};
initDefaultDatatablesConfig();
});

View File

@@ -60,10 +60,12 @@ define(['jquery'], ($) => {
gitHubReleases: '/api/github/releases' // ajax URL - get release info from GitHub
},
breakpoints: [
{ name: 'desktop', width: Infinity },
{ name: 'tablet', width: 1200 },
{ name: 'fablet', width: 780 },
{ name: 'phone', width: 480 }
{ name: 'screen-xl', width: Infinity },
{ name: 'screen-l', width: 1600 },
{ name: 'screen-m', width: 1200 },
{ name: 'screen-d', width: 1000 },
{ name: 'screen-s', width: 780 },
{ name: 'screen-xs', width: 480 }
],
animationSpeed: {
splashOverlay: 300, // "splash" loading overlay

View File

@@ -65,7 +65,7 @@ define([
* @param connectionsData
*/
let addConnectionsOverlay = (connections, connectionsData) => {
let SystemSignatures = require('app/ui/system_signature');
let SystemSignatures = require('app/ui/module/system_signature_new');
/**
* add label to endpoint

View File

@@ -367,7 +367,7 @@ define([
connection &&
connectionData.signatures // signature data is required...
){
let SystemSignatures = require('app/ui/system_signature');
let SystemSignatures = require('app/ui/module/system_signature_new');
let connectionId = connection.getParameter('connectionId');
let sourceEndpoint = connection.endpoints[0];
@@ -1426,6 +1426,7 @@ define([
/**
* add a wormhole tooltip with wh specific data to elements
* @param tooltipData
* @param options
* @returns {*}
*/
$.fn.addWormholeInfoTooltip = function(tooltipData, options){

View File

@@ -5,13 +5,14 @@ define([
'app/map/map',
'app/map/util',
'sortable',
'app/ui/system_info',
'app/ui/system_graph',
'app/ui/system_signature',
'app/ui/system_route',
'app/ui/system_intel',
'app/ui/system_killboard',
'app/ui/connection_info',
'app/ui/module/system_info',
'app/ui/module/system_graph',
//'app/ui/module/system_signature',
'app/ui/module/system_signature_new',
'app/ui/module/system_route',
'app/ui/module/system_intel',
'app/ui/module/system_killboard',
'app/ui/module/connection_info',
'app/counter'
], (
$,
@@ -180,11 +181,12 @@ define([
* @param moduleElement
* @param Module
* @param callback
* @param addSpacer
*/
let removeModule = (moduleElement, Module, callback, addSpacer) => {
if(moduleElement.length > 0){
if(typeof Module.beforeReDraw === 'function'){
Module.beforeReDraw();
if(typeof Module.beforeHide === 'function'){
Module.beforeHide(moduleElement);
}
moduleElement.velocity('reverse',{
@@ -336,8 +338,6 @@ define([
}
};
return new Promise(drawModuleExecutor);
};
@@ -539,23 +539,25 @@ define([
let clickY = e.pageY - posY;
// check for top-left click
if(clickX <= 8 && clickY <= 8 && clickX >= 0 && clickY >= 0){
if(clickX <= 9 && clickY <= 9 && clickX >= 0 && clickY >= 0){
// remember height
if(! moduleElement.data('origHeight')){
if( !moduleElement.data('origHeight') ){
moduleElement.data('origHeight', moduleElement.outerHeight());
}
if(moduleElement.hasClass( config.moduleClosedClass )){
if(moduleElement.hasClass(config.moduleClosedClass)){
let moduleHeight = moduleElement.data('origHeight');
moduleElement.velocity('finish').velocity({
height: [ moduleHeight + 'px', [ 400, 15 ] ]
},{
duration: 400,
easing: 'easeOutSine',
complete: function(){
moduleElement.removeClass( config.moduleClosedClass );
complete: function(moduleElement){
moduleElement = $(moduleElement);
moduleElement.removeClass(config.moduleClosedClass);
moduleElement.removeData('origHeight');
moduleElement.css({height: ''});
}
});
}else{
@@ -564,8 +566,9 @@ define([
},{
duration: 400,
easing: 'easeOutSine',
complete: function(){
moduleElement.addClass( config.moduleClosedClass );
complete: function(moduleElement){
moduleElement = $(moduleElement);
moduleElement.addClass(config.moduleClosedClass);
}
});
}

View File

@@ -70,7 +70,7 @@ define([
dynamicElementWrapperId: 'pf-dialog-wrapper', // class for container element that holds hidden "context menus"
// system signature module
systemSignatureModuleClass: 'pf-signature-table-module', // module wrapper (signatures)
systemSignatureModuleClass: 'pf-system-signature-module', // module wrapper (signatures)
systemIntelModuleClass: 'pf-system-intel-module', // module wrapper (intel)
};
@@ -744,7 +744,7 @@ define([
// global "modal" callback (for all modals)
$('body').on('hide.bs.modal', '> .modal', function(e) {
let modalElement = $(this);
modalElement.destroyTimestampCounter();
modalElement.destroyTimestampCounter(true);
// destroy all Select2
modalElement.find('.' + Util.config.select2Class)

View File

@@ -321,10 +321,6 @@ define([
tooltipElements.tooltip();
});
systemTable.on('destroy.dt', function(){
$(this).destroyTimestampCounter();
});
// prepare data for dataTables
let systemsData = [];
for(let i = 0; i < mapData.data.systems.length; i++){
@@ -470,11 +466,6 @@ define([
lengthMenu: [[5, 10, 20, 50, -1], [5, 10, 20, 50, 'All']],
ordering: true,
order: [[ 9, 'desc' ], [ 3, 'asc' ]],
autoWidth: false,
responsive: {
breakpoints: Init.breakpoints,
details: false
},
hover: false,
data: systemsData,
columnDefs: [],
@@ -488,7 +479,7 @@ define([
{
title: 'type',
width: '25px',
className: ['min-desktop'].join(' '),
className: ['min-screen-l'].join(' '),
data: 'type',
render: {
_: 'type',
@@ -506,7 +497,7 @@ define([
},{
title: 'sec',
width: '18px',
className: ['text-center', 'min-desktop'].join(' '),
className: ['text-center', 'min-screen-l'].join(' '),
searchable: false,
data: 'trueSec',
render: {
@@ -516,7 +507,7 @@ define([
},{
title: '<i class="fas fa-skull" title="shattered" data-toggle="tooltip"></i>',
width: '10px',
className: ['text-center', 'min-desktop'].join(' '),
className: ['text-center', 'min-screen-l'].join(' '),
searchable: false,
data: 'shattered',
render: {
@@ -593,7 +584,7 @@ define([
title: 'updated',
width: '80px',
searchable: false,
className: ['text-right', config.tableCellCounterClass, 'min-desktop'].join(' '),
className: ['text-right', config.tableCellCounterClass, 'min-screen-l'].join(' '),
data: 'updated',
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
$(cell).initTimestampCounter();
@@ -863,11 +854,6 @@ define([
lengthMenu: [[5, 10, 20, 50, -1], [5, 10, 20, 50, 'All']],
ordering: true,
order: [[ 3, 'asc' ]],
autoWidth: false,
responsive: {
breakpoints: Init.breakpoints,
details: false
},
hover: false,
data: usersData,
language: {
@@ -962,7 +948,7 @@ define([
width: 26,
orderable: false,
searchable: false,
className: [config.tableCellImageClass, config.tableCellImageSmallClass, 'min-desktop'].join(' '),
className: [config.tableCellImageClass, config.tableCellImageSmallClass, 'min-screen-l'].join(' '),
data: 'corporation',
render: {
_: function(data, type, row, meta){
@@ -978,7 +964,7 @@ define([
title: 'corporation',
orderable: true,
searchable: true,
className: [config.tableCellActionClass, 'min-desktop'].join(' '),
className: [config.tableCellActionClass, 'min-screen-l'].join(' '),
data: 'corporation',
render: {
_: function (data, type, row, meta) {
@@ -1041,7 +1027,7 @@ define([
width: 30,
orderable: true,
searchable: true,
className: ['text-right', 'min-desktop'].join(' '),
className: ['text-right', 'min-screen-l'].join(' '),
data: 'role',
render: {
_: function (data, type, row, meta) {

View File

@@ -654,7 +654,7 @@ define([
*/
$.fn.initSignatureTypeSelect = function(options, hasOptGroups){
let defaultConfig = {
minimumResultsForSearch: 6,
minimumResultsForSearch: 10,
width: '220px',
dropdownParent: this.parents('.popover-content')
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,248 @@
/**
* 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

@@ -0,0 +1,419 @@
/**
* 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) => {
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

@@ -0,0 +1,841 @@
/**
* System intel 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
// system info module
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');
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-pen"></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.pulseBackgroundColor(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
});
});
};
/**
* before module destroy callback
* @param moduleElement
*/
let beforeDestroy = moduleElement => {
// Destroying the data tables throws
// -> safety remove all dataTables
let structureTableElement = moduleElement.find('.' + config.systemStructuresTableClass);
let tableApi = structureTableElement.DataTable();
tableApi.destroy();
};
return {
config: config,
getModule: getModule,
initModule: initModule,
updateModule: updateModule,
beforeDestroy: beforeDestroy
};
});

View File

@@ -0,0 +1,407 @@
/**
* System killboard module
*/
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) => {
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

File diff suppressed because it is too large Load Diff

View File

@@ -33,7 +33,6 @@ define([
// head
headMapTrackingId: 'pf-head-map-tracking', // id for "map tracking" toggle (checkbox)
headCharacterSwitchId: 'pf-head-character-switch', // id for "character switch" popover
headCurrentLocationId: 'pf-head-current-location', // id for "show current location" element
// menu
@@ -66,10 +65,12 @@ define([
// animation
animationPulseSuccessClass: 'pf-animation-pulse-success', // animation class
animationPulseWarningClass: 'pf-animation-pulse-warning', // animation class
animationPulseDangerClass: 'pf-animation-pulse-danger', // animation class
// popover
popoverSmallClass: 'pf-popover-small', // class for "small" popover
popoverTriggerClass: 'pf-popover-trigger', // class for "popover" trigger elements
popoverSmallClass: 'pf-popover-small', // class for small "popover"
popoverCharacterClass: 'pf-popover-character', // class for character "popover"
// help
helpDefaultClass: 'pf-help-default', // class for "help" tooltip elements
@@ -231,7 +232,7 @@ define([
}else{
callback(responseData.img);
}
}).fail(function( jqXHR, status, error) {
}).fail(function( jqXHR, status, error){
let reason = status + ' ' + error;
showNotify({title: jqXHR.status + ': getCaptchaImage', text: reason, type: 'error'});
});
@@ -424,7 +425,7 @@ define([
let width = element.offsetWidth;
let height = element.offsetHeight;
while(element.offsetParent) {
while(element.offsetParent){
element = element.offsetParent;
top += element.offsetTop;
left += element.offsetLeft;
@@ -494,7 +495,7 @@ define([
* @returns {*}
*/
$.fn.destroyTooltip = function(recursive){
return this.each(function() {
return this.each(function(){
let element = $(this);
let tooltipSelector = '[title]';
let tooltipElements = element.filter(tooltipSelector);
@@ -502,18 +503,20 @@ define([
tooltipElements = tooltipElements.add(element.find(tooltipSelector));
}
tooltipElements.each(function() {
tooltipElements.each(function(){
$(this).tooltip('destroy');
});
});
};
/**
* adds a popup tooltip with character information (created/updated)
* add a popup tooltip with character information (created/updated)
* @param tooltipData
* @param options
* @returns {*}
*/
$.fn.addCharacterInfoTooltip = function(tooltipData){
let element = $(this);
$.fn.addCharacterInfoTooltip = function(tooltipData, options){
let data = {};
if(
tooltipData.created.character &&
@@ -522,59 +525,60 @@ define([
let createdData = tooltipData.created;
let updatedData = tooltipData.updated;
// check if data has changed
if(
element.data('created') !== createdData.created ||
element.data('updated') !== updatedData.updated
){
// data changed
// set new data for next check
element.data('created', createdData.created);
element.data('updated', updatedData.updated);
let statusCreatedClass = getStatusInfoForCharacter(createdData.character, 'class');
let statusUpdatedClass = getStatusInfoForCharacter(updatedData.character, 'class');
let statusCreatedClass = getStatusInfoForCharacter(createdData.character, 'class');
let statusUpdatedClass = getStatusInfoForCharacter(updatedData.character, 'class');
// convert timestamps
let dateCreated = new Date(createdData.created * 1000);
let dateUpdated = new Date(updatedData.updated * 1000);
let dateCreatedUTC = convertDateToUTC(dateCreated);
let dateUpdatedUTC = convertDateToUTC(dateUpdated);
// convert timestamps
let dateCreated = new Date(createdData.created * 1000);
let dateUpdated = new Date(updatedData.updated * 1000);
let dateCreatedUTC = convertDateToUTC(dateCreated);
let dateUpdatedUTC = convertDateToUTC(dateUpdated);
let data = {
created: createdData,
updated: updatedData,
createdTime: convertDateToString(dateCreatedUTC),
updatedTime: convertDateToString(dateUpdatedUTC),
createdStatusClass: statusCreatedClass,
updatedStatusClass: statusUpdatedClass
};
requirejs(['text!templates/tooltip/character_info.html', 'mustache'], function(template, Mustache) {
let content = Mustache.render(template, data);
element.popover({
placement: 'top',
html: true,
trigger: 'hover',
content: '',
container: 'body',
title: 'Created / Updated',
delay: {
show: 250,
hide: 0
}
});
// set new popover content
let popover = element.data('bs.popover');
popover.options.content = content;
});
}
data = {
popoverClass: config.popoverCharacterClass,
ccpImageServerUrl: Init.url.ccpImageServer,
created: createdData,
updated: updatedData,
createdTime: convertDateToString(dateCreatedUTC),
updatedTime: convertDateToString(dateUpdatedUTC),
createdStatusClass: statusCreatedClass,
updatedStatusClass: statusUpdatedClass
};
}
return element;
let defaultOptions = {
placement: 'top',
html: true,
trigger: 'hover',
container: 'body',
title: 'Created / Updated',
delay: {
show: 150,
hide: 0
}
};
options = $.extend({}, defaultOptions, options);
return this.each(function(){
let element = $(this);
requirejs(['text!templates/tooltip/character_info.html', 'mustache'], (template, Mustache) => {
let content = Mustache.render(template, data);
element.popover(options);
// set new popover content
let popover = element.data('bs.popover');
popover.options.content = content;
if(options.show){
element.popover('show');
}
});
});
};
/**
@@ -585,10 +589,10 @@ define([
let elements = $(this);
let eventNamespace = 'hideCharacterPopup';
requirejs(['text!templates/tooltip/character_switch.html', 'mustache'], function (template, Mustache) {
requirejs(['text!templates/tooltip/character_switch.html', 'mustache'], function (template, Mustache){
let data = {
id: config.headCharacterSwitchId,
popoverClass: config.popoverCharacterClass,
browserTabId: getBrowserTabId(),
routes: Init.routes,
userData: userData,
@@ -608,7 +612,7 @@ define([
let content = Mustache.render(template, data);
return elements.each(function() {
return elements.each(function(){
let element = $(this);
// check if popover already exists -> remove it
@@ -616,7 +620,7 @@ define([
element.off('click').popover('destroy');
}
element.on('click', function(e) {
element.on('click', function(e){
e.preventDefault();
e.stopPropagation();
@@ -631,7 +635,7 @@ define([
if(popoverData === undefined){
button.on('shown.bs.popover', function (e) {
button.on('shown.bs.popover', function (e){
let tmpPopupElement = $(this).data('bs.popover').tip();
tmpPopupElement.find('.btn').on('click', function(e){
// close popover
@@ -662,7 +666,7 @@ define([
// character switch detected
$('body').data('characterSwitch', true);
// ... and remove "characterSwitch" data again! after "unload"
setTimeout(function() {
setTimeout(function(){
$('body').removeData('characterSwitch');
}, 500);
});
@@ -690,7 +694,7 @@ define([
* @returns {*}
*/
$.fn.destroyPopover = function(recursive){
return this.each(function() {
return this.each(function(){
let element = $(this);
let popoverSelector = '.' + config.popoverTriggerClass;
let popoverElements = element.filter(popoverSelector);
@@ -698,7 +702,7 @@ define([
popoverElements = popoverElements.add(element.find(popoverSelector));
}
popoverElements.each(function() {
popoverElements.each(function(){
let popoverElement = $(this);
if(popoverElement.data('bs.popover')){
popoverElement.popover('destroy');
@@ -713,9 +717,9 @@ define([
* @returns {*}
*/
$.fn.initPopoverClose = function(eventNamespace){
return this.each(function() {
$('body').off('click.' + eventNamespace).on('click.' + eventNamespace + ' contextmenu', function (e) {
$('.' + config.popoverTriggerClass).each(function () {
return this.each(function(){
$('body').off('click.' + eventNamespace).on('click.' + eventNamespace + ' contextmenu', function (e){
$('.' + config.popoverTriggerClass).each(function (){
let popoverElement = $(this);
//the 'is' for buttons that trigger popups
//the 'has' for icons within a button that triggers a popup
@@ -743,7 +747,7 @@ define([
* @returns {*}
*/
$.fn.setPopoverSmall = function(){
return this.each(function() {
return this.each(function(){
let element = $(this);
let popover = element.data('bs.popover');
if(popover){
@@ -760,7 +764,7 @@ define([
$.fn.showMessage = function(config){
let containerElement = $(this);
requirejs(['text!templates/form/message.html', 'mustache'], function(template, Mustache) {
requirejs(['text!templates/form/message.html', 'mustache'], function(template, Mustache){
let messageTypeClass = 'alert-danger';
let messageTextClass = 'txt-color-danger';
@@ -809,7 +813,7 @@ define([
* add/remove css class for keyframe animation
* @returns {any|JQuery|*}
*/
$.fn.pulseTableRow = function(status, clear){
$.fn.pulseBackgroundColor = function(status, clear){
let animationClass = '';
switch(status){
@@ -819,9 +823,12 @@ define([
case 'changed':
animationClass = config.animationPulseWarningClass;
break;
case 'deleted':
animationClass = config.animationPulseDangerClass;
break;
}
let clearTimer = function(element) {
let clearTimer = element => {
element.removeClass( animationClass );
let currentTimer = element.data('animationTimer');
@@ -841,7 +848,7 @@ define([
}
if(clear !== true){
element.addClass( animationClass );
element.addClass(animationClass);
let timer = setTimeout(clearTimer, 1500, element);
element.data('animationTimer', timer);
animationTimerCache[timer] = true;
@@ -902,14 +909,14 @@ define([
const prepareSafeListener = (listener, passive) => {
if (!passive) return listener;
return function (e) {
return function (e){
e.preventDefault = () => {};
return listener.call(this, e);
};
};
const overwriteAddEvent = (superMethod) => {
EventTarget.prototype.addEventListener = function (type, listener, options) { // jshint ignore:line
EventTarget.prototype.addEventListener = function (type, listener, options){ // jshint ignore:line
const usesListenerOptions = typeof options === 'object';
const useCapture = usesListenerOptions ? options.capture : options;
@@ -927,20 +934,20 @@ define([
try {
const opts = Object.defineProperty({}, 'passive', {
get() {
get(){
supported = true;
}
});
window.addEventListener('test', null, opts);
window.removeEventListener('test', null, opts);
} catch (e) {}
} catch (e){}
return supported;
};
let supportsPassive = eventListenerOptionsSupported ();
if (supportsPassive) {
if (supportsPassive){
const addEvent = EventTarget.prototype.addEventListener; // jshint ignore:line
overwriteAddEvent(addEvent);
}
@@ -953,8 +960,8 @@ define([
// Array diff
// [1,2,3,4,5,6].diff( [3,4,5] );
// => [1, 2, 6]
Array.prototype.diff = function(a) {
return this.filter(function(i) {return a.indexOf(i) < 0;});
Array.prototype.diff = function(a){
return this.filter(function(i){return a.indexOf(i) < 0;});
};
/**
@@ -962,7 +969,7 @@ define([
* @param p
* @returns {Array.<T>}
*/
Array.prototype.sortBy = function(p) {
Array.prototype.sortBy = function(p){
return this.slice(0).sort((a,b) => {
return (a[p] > b[p]) ? 1 : (a[p] < b[p]) ? -1 : 0;
});
@@ -972,10 +979,10 @@ define([
* get hash from string
* @returns {number}
*/
String.prototype.hashCode = function() {
String.prototype.hashCode = function(){
let hash = 0, i, chr;
if (this.length === 0) return hash;
for (i = 0; i < this.length; i++) {
for (i = 0; i < this.length; i++){
chr = this.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
@@ -1094,6 +1101,12 @@ define([
if(resultsWrapper){
resultsWrapper.mCustomScrollbar('destroy');
}
// select2 sets :focus to the select2 <input> field. This is bad!
// we want to keep the focus where it is (e.g. signature table cell)
// the only way to prevent this is to remove the element
// https://stackoverflow.com/questions/17995057/prevent-select2-from-autmatically-focussing-its-search-input-when-dropdown-is-op
$(this).parents('.editableform').find(this).next().find('.select2-selection').remove();
});
};
@@ -1262,7 +1275,7 @@ define([
// line breaks are 2 characters!
let newLines = value.match(/(\r\n|\n|\r)/g);
let addition = 0;
if (newLines != null) {
if(newLines != null){
addition = newLines.length;
}
inputLength += addition;
@@ -1300,7 +1313,7 @@ define([
* stop browser tab title "blinking"
*/
let stopTabBlink = function(){
requirejs(['notification'], function(Notification) {
requirejs(['notification'], function(Notification){
Notification.stopTabBlink();
});
};
@@ -1388,7 +1401,7 @@ define([
*/
let ajaxSetup = () => {
$.ajaxSetup({
beforeSend: function(jqXHR, settings) {
beforeSend: function(jqXHR, settings){
// store request URL for later use (e.g. in error messages)
jqXHR.url = location.protocol + '//' + location.host + settings.url;
@@ -1579,11 +1592,13 @@ define([
};
/**
* get Area ID by security string
* get areaId by security string
* areaId is required as a key for signature names
* if areaId is 0, no signature data is available for this system
* @param security
* @returns {number}
*/
let getAreaIdBySecurity = (security) => {
let getAreaIdBySecurity = security => {
let areaId = 0;
switch(security){
case 'H':
@@ -1646,7 +1661,6 @@ define([
* @returns {string}
*/
let getStatusInfoForCharacter = (characterData, option) => {
let statusInfo = '';
// character status can not be checked if there are no reference data
@@ -1898,20 +1912,17 @@ define([
/**
* get signature group information
* @param option
* @returns {{}}
* @returns {Array}
*/
let getSignatureGroupInfo = function(option){
let groupInfo = {};
for (let prop in Init.signatureGroups) {
if(Init.signatureGroups.hasOwnProperty(prop)){
prop = parseInt(prop);
groupInfo[prop] = Init.signatureGroups[prop][option];
}
let getSignatureGroupOptions = option => {
let options = [];
for(let [key, data] of Object.entries(Init.signatureGroups)){
options.push({
value: parseInt(key),
text: data[option]
});
}
return groupInfo;
return options;
};
/**
@@ -1943,28 +1954,22 @@ define([
* @param name
* @returns {number}
*/
let getSignatureTypeIdByName = function(systemData, sigGroupId, name){
let getSignatureTypeIdByName = (systemData, sigGroupId, name) => {
let signatureTypeId = 0;
let areaId = getAreaIdBySecurity(systemData.security);
if(areaId > 0){
let signatureNames = getAllSignatureNames(systemData.type.id, areaId, sigGroupId );
let signatureNames = getAllSignatureNames(systemData.type.id, areaId, sigGroupId);
name = name.toLowerCase();
for(let prop in signatureNames) {
for(let prop in signatureNames){
if(
signatureNames.hasOwnProperty(prop) &&
signatureNames[prop].toLowerCase() === name
){
signatureTypeId = parseInt( prop );
signatureTypeId = parseInt(prop);
break;
}
}
}
return signatureTypeId;
};
@@ -1995,9 +2000,8 @@ define([
* to keep the data always up2data
* @param mapUserData
*/
let setCurrentMapUserData = (mapUserData) => {
let setCurrentMapUserData = mapUserData => {
Init.currentMapUserData = mapUserData;
return getCurrentMapUserData();
};
@@ -2006,7 +2010,7 @@ define([
* @param mapId
* @returns {boolean}
*/
let getCurrentMapUserData = (mapId) => {
let getCurrentMapUserData = mapId => {
let currentMapUserData = false;
if(Init.currentMapUserData){
@@ -2041,7 +2045,7 @@ define([
* @param mapId
* @returns {boolean|int}
*/
let getCurrentMapUserDataIndex = (mapId) => {
let getCurrentMapUserDataIndex = mapId => {
return getDataIndexByMapId(Init.currentMapUserData, mapId);
};
@@ -2049,7 +2053,7 @@ define([
* update cached mapUserData for a single map
* @param mapUserData
*/
let updateCurrentMapUserData = (mapUserData) => {
let updateCurrentMapUserData = mapUserData => {
let mapUserDataIndex = getCurrentMapUserDataIndex( mapUserData.config.id );
if( !Array.isArray(Init.currentMapUserData) ){
@@ -2072,7 +2076,7 @@ define([
* to keep the data always up2data
* @param mapData
*/
let setCurrentMapData = (mapData) => {
let setCurrentMapData = mapData => {
Init.currentMapData = mapData;
return getCurrentMapData();
@@ -2083,7 +2087,7 @@ define([
* @param mapId
* @returns {boolean}
*/
let getCurrentMapData = (mapId) => {
let getCurrentMapData = mapId => {
let currentMapData = false;
if( mapId === parseInt(mapId, 10) ){
@@ -2107,7 +2111,7 @@ define([
* @param mapId
* @returns {boolean|int}
*/
let getCurrentMapDataIndex = (mapId) => {
let getCurrentMapDataIndex = mapId => {
return getDataIndexByMapId(Init.currentMapData, mapId);
};
@@ -2115,7 +2119,7 @@ define([
* update cached mapData for a single map
* @param mapData
*/
let updateCurrentMapData = (mapData) => {
let updateCurrentMapData = mapData => {
let mapDataIndex = getCurrentMapDataIndex( mapData.config.id );
if(mapDataIndex !== false){
@@ -2146,7 +2150,7 @@ define([
* delete map data by mapId from currentMapData
* @param mapId
*/
let deleteCurrentMapData = (mapId) => {
let deleteCurrentMapData = mapId => {
Init.currentMapData = Init.currentMapData.filter((mapData) => {
return (mapData.config.id !== mapId);
});
@@ -2176,7 +2180,7 @@ define([
* @param option
* @returns {boolean}
*/
let getCurrentUserInfo = (option) => {
let getCurrentUserInfo = option => {
let currentUserData = getCurrentUserData();
let userInfo = false;
@@ -2297,7 +2301,7 @@ define([
characterData = characterData.filter(function(tmpCharacterData, index, allData){
let keepData = true;
for(let tmpJump in data) {
for(let tmpJump in data){
// just scan systems with > jumps than current system
if(tmpJump > jumps){
let filteredFinalData = data[tmpJump].filter(filterFinalCharData, tmpCharacterData);
@@ -2325,7 +2329,7 @@ define([
}
jumps++;
for(let prop in nearBySystems.tree) {
for(let prop in nearBySystems.tree){
if( nearBySystems.tree.hasOwnProperty(prop) ){
let tmpSystemData = nearBySystems.tree[prop];
data = getNearByCharacterData(tmpSystemData, userData, jumps, data);
@@ -2374,7 +2378,7 @@ define([
responseData.systemData &&
responseData.systemData.length > 0
){
for (let j = 0; j < responseData.systemData.length; j++) {
for (let j = 0; j < responseData.systemData.length; j++){
showNotify({title: this.description, text: 'System: ' + responseData.systemData[j].name, type: 'success'});
}
}
@@ -2388,7 +2392,7 @@ define([
}
}
}).fail(function( jqXHR, status, error) {
}).fail(function( jqXHR, status, error){
let reason = status + ' ' + error;
showNotify({title: jqXHR.status + ': ' + this.description, text: reason, type: 'warning'});
});
@@ -2452,7 +2456,7 @@ define([
}else{
showNotify({title: 'Open window in client', text: 'Check your EVE client', type: 'success'});
}
}).fail(function( jqXHR, status, error) {
}).fail(function( jqXHR, status, error){
let reason = status + ' ' + error;
showNotify({title: jqXHR.status + ': openWindow', text: reason, type: 'error'});
});
@@ -2585,9 +2589,9 @@ define([
element.off(eventName).on(eventName, function(e){
clicks++;
if (clicks === 1) {
if (clicks === 1){
setTimeout(element => {
if(clicks === 1) {
if(clicks === 1){
singleClickCallback.call(element, e);
} else {
doubleClickCallback.call(element, e);
@@ -2666,7 +2670,7 @@ define([
if(data.reroute){
redirect(data.reroute, ['logout']);
}
}).fail(function( jqXHR, status, error) {
}).fail(function( jqXHR, status, error){
let reason = status + ' ' + error;
showNotify({title: jqXHR.status + ': logout', text: reason, type: 'error'});
});
@@ -2709,13 +2713,13 @@ define([
let name = cname + '=';
let ca = document.cookie.split(';');
for(let i = 0; i <ca.length; i++) {
for(let i = 0; i <ca.length; i++){
let c = ca[i];
while (c.charAt(0) === ' ') {
while (c.charAt(0) === ' '){
c = c.substring(1);
}
if (c.indexOf(name) === 0) {
if (c.indexOf(name) === 0){
return c.substring(name.length,c.length);
}
}
@@ -2757,7 +2761,7 @@ define([
getSecurityClassForSystem: getSecurityClassForSystem,
getTrueSecClassForSystem: getTrueSecClassForSystem,
getStatusInfoForSystem: getStatusInfoForSystem,
getSignatureGroupInfo: getSignatureGroupInfo,
getSignatureGroupOptions: getSignatureGroupOptions,
getAllSignatureNames: getAllSignatureNames,
getSignatureTypeIdByName: getSignatureTypeIdByName,
getAreaIdBySecurity: getAreaIdBySecurity,

View File

@@ -36,7 +36,7 @@
<label class="col-sm-3 control-label">Username</label>
<div class="col-sm-9">
<p class="form-control-static">
<i class="fas fa-fw fa-lg fa-pencil-alt pull-right pf-dialog-icon-button collapsed" data-toggle="collapse" data-target="#collapseUsername" aria-expanded="false" aria-controls="collapseUsername"></i>
<i class="fas fa-fw fa-lg fa-pen pull-right pf-dialog-icon-button collapsed" data-toggle="collapse" data-target="#collapseUsername" aria-expanded="false" aria-controls="collapseUsername"></i>
{{userData.name}}
</p>
</div>
@@ -68,7 +68,7 @@
<label class="col-sm-3 control-label">Email</label>
<div class="col-sm-9">
<p class="form-control-static">
<i class="fas fa-fw fa-lg fa-pencil-alt pull-right pf-dialog-icon-button collapsed" data-toggle="collapse" data-target="#collapseEmail" aria-expanded="false" aria-controls="collapseEmail"></i>
<i class="fas fa-fw fa-lg fa-pen pull-right pf-dialog-icon-button collapsed" data-toggle="collapse" data-target="#collapseEmail" aria-expanded="false" aria-controls="collapseEmail"></i>
{{#userData.email}}
{{userData.email}}
{{/userData.email}}

View File

@@ -1,14 +1,14 @@
<form role="form" class="form-horizontal">
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<div class="form-group" style="margin-bottom: 0;">
<label class="col-sm-2 control-label" for="form_result">Scan result</label>
<div class="col-sm-10">
<textarea style="resize: vertical" rows="3" id="form_result" name="clipboard" class="form-control custom-scroll" autofocus></textarea>
<span class="help-block">
Copy and paste signatures from your probe scanning window.
Hit <kbd>ctrl</kbd> + <kbd>c</kbd> (copy)
then <kbd>ctrl</kbd> + <kbd>v</kbd> (paste). This tool can add and update signatures.
Copy and paste signatures from your probe scanning window.<br>
Hit <kbd>ctrl</kbd> + <kbd>c</kbd> (copy) then <kbd>ctrl</kbd> + <kbd>v</kbd> (paste).
This tool can add and update signatures.
</span>
</div>
</div>
@@ -17,7 +17,7 @@
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<div class="form-group" style="margin-bottom: 0;">
<div class="col-sm-offset-2 col-sm-10 col-xs-9">
<div class="checkbox checkbox-warning">
<input id="form_delete" name="deleteOld" value="1" type="checkbox">

View File

@@ -37,7 +37,7 @@
{{! info text ================================================================================================ }}
<div class="col-xs-12 col-sm-7">
<div class="pf-dynamic-area pf-system-info-description-area">
<i class="fas fa-fw fa-lg fa-pencil-alt pull-right pf-module-icon-button {{descriptionButtonClass}}" data-toggle="tooltip" data-container="body" title="edit description"></i>
<i class="fas fa-fw fa-lg fa-pen pull-right pf-module-icon-button {{descriptionButtonClass}}" data-toggle="tooltip" data-container="body" title="edit description"></i>
<div class="{{tableToolsActionClass}}">
<a href="#" class="{{descriptionTextareaClass}}"></a>

View File

@@ -1,31 +1,31 @@
<div class="pf-popover">
<img class="pull-right" src="//image.eveonline.com/Character/{{created.character.id}}_32.jpg">
<h6>
<small>
<i class="fas fa-fw fa-circle pf-user-status {{createdStatusClass}}"></i>
</small> {{created.character.name}}
</h6>
<div class="well well-sm text-center">
{{createdTime}}
</div>
<div style="height: 1px">&nbsp;</div>
<img class="pull-right" src="//image.eveonline.com/Character/{{updated.character.id}}_32.jpg">
<h6>
<small>
<i class="fas fa-fw fa-circle pf-user-status {{updatedStatusClass}}"></i>
</small> {{updated.character.name}}
</h6>
<div class="well well-sm text-center">
{{updatedTime}}
</div>
<div class="{{popoverClass}}">
<table class="table table-condensed">
<tbody>
<tr>
<td>
<img src="{{ccpImageServerUrl}}/Character/{{created.character.id}}_32.jpg" alt="{{created.character.name}}">
</td>
<td>
<i class="fas fa-fw fa-circle pf-user-status {{createdStatusClass}}"></i>
{{created.character.name}}
</td>
<td>
<div class="well well-sm text-center"><i class="fas fa-fw fa-plus"></i>&nbsp;{{createdTime}}</div>
</td>
</tr>
<tr>
<td>
<img src="{{ccpImageServerUrl}}/Character/{{updated.character.id}}_32.jpg" alt="{{updated.character.name}}">
</td>
<td>
<i class="fas fa-fw fa-circle pf-user-status {{updatedStatusClass}}"></i>
{{updated.character.name}}
</td>
<td>
<div class="well well-sm text-center"><i class="fas fa-fw fa-pen"></i>&nbsp;{{updatedTime}}</div>
</td>
</tr>
</tbody>
</table>
</div>

View File

@@ -1,16 +1,17 @@
<table id="{{id}}" class="table table-condensed">
<tbody>
<div class="{{popoverClass}}">
<table class="table table-condensed">
<tbody>
{{#otherCharacters}}
<tr>
<td><img src="{{image}}" alt="{{name}}"></td>
<td>{{name}}</td>
<td class="text-right text-nowrap">
<a class="btn btn-sm btn-default" href="{{routes.ssoLogin}}?characterId={{id}}" target="_blank" title="new tab" rel="noopener">
<i class="fas fa-fw fa-external-link-alt"></i>
</a>
<a class="btn btn-sm btn-primary" href="{{routes.ssoLogin}}?characterId={{id}}&tabId={{browserTabId}}" target="_self" title="switch&nbsp;character">
<i class="fas fa-fw fa-exchange-alt"></i>
</a>
<a class="btn btn-sm btn-default" href="{{routes.ssoLogin}}?characterId={{id}}" target="_blank" title="new tab" rel="noopener">
<i class="fas fa-fw fa-external-link-alt"></i>
</a>
<a class="btn btn-sm btn-primary" href="{{routes.ssoLogin}}?characterId={{id}}&tabId={{browserTabId}}" target="_self" title="switch&nbsp;character">
<i class="fas fa-fw fa-exchange-alt"></i>
</a>
</td>
</tr>
{{/otherCharacters}}
@@ -24,5 +25,6 @@
</a>
</td>
</tr>
</tbody>
</table>
</tbody>
</table>
</div>

View File

@@ -29,6 +29,6 @@
border-radius: $border-radius-large;
}
.well-sm {
padding: 9px;
padding: 7px;
border-radius: $border-radius-small;
}

View File

@@ -69,6 +69,16 @@
}
}
.pf-animation-pulse-danger{
@include animation( pulseBackgroundDanger 1s 1 );
@include animation-timing-function( cubic-bezier(0.53, -0.03, 0.68, 0.38) );
.sorting_1{
@include animation( pulseBackgroundDangerActive 1s 1 );
@include animation-timing-function( cubic-bezier(0.53, -0.03, 0.68, 0.38) );
}
}
@include keyframes(pulseBackgroundSuccess){
0% {}
10% {
@@ -103,6 +113,23 @@
100% {}
};
@include keyframes(pulseBackgroundDanger){
0% {}
10% {
background-color: $red;
color: $gray-darker;
}
100% {}
};
@include keyframes(pulseBackgroundDangerActive){
0% {}
10% {
background-color: darken($red, 5%);
color: $gray-darker;
}
100% {}
};
// rotate =========================================================================================
.pf-animate-rotate{
@include transition( all 0.08s linear );

View File

@@ -167,6 +167,11 @@ a{
// form fields custom styles ======================================================================
.editable-input{
.pf-editable-name{
text-transform: uppercase;
}
optgroup[label]{
background-color: $gray;
color: $gray-light;
@@ -292,11 +297,23 @@ select:active, select:hover {
// "special" column styles
td{
&:focus-within{
&.editable-disabled:focus{
// disabled xEditable td´s should not get focus style
// -> "pointer-events = none" would be nicer but does not work because "mousedown" should work -> select row function
outline: none;
background-color: transparent;
}
&.editable-click:not(.editable-disabled){
cursor: pointer;
}
&:focus, &.editable-open{
// set td as :focus even is td content is focused
outline: 1px solid $orange-dark;
background-color: rgba($orange-dark, .08) !important;
outline-offset: -1px;
background-color: rgba($orange-dark, .08);
}
> .fa-circle{
@@ -691,7 +708,7 @@ table{
top: 0;
left: 0;
border-style: solid;
border-width: 0 0 8px 8px;
border-width: 0 0 9px 9px;
border-color: transparent transparent transparent $gray;
cursor: ns-resize;
}
@@ -1340,18 +1357,6 @@ code {
}
}
// character switch popover
#pf-head-character-switch{
td{
border: none;
&:first-child + td{
// 2nd column
padding: 0 5px;
}
}
}
// footer (map) ===================================================================================
#pf-footer{
position: absolute;

View File

@@ -20,7 +20,7 @@
// image in popover
img{
@include border-radius(5px);
@include border-radius(3px);
}
h4{
@@ -103,4 +103,20 @@
}
}
}
}
// character popover --------------------------------------------------------------------------------------------------
.pf-popover-character{
.table>tbody>tr>td{
border: none;
&:first-child + td{
// 2nd column
padding: 0 5px;
}
}
.well{
margin-bottom: 0; // overwrite default
}
}

View File

@@ -31,8 +31,8 @@
}
}
// signature table module ==================================================
.pf-signature-table-module{
// system signature module ==================================================
.pf-system-signature-module{
.progress-label-right{
margin-right: 20px;
@@ -61,17 +61,8 @@
.pf-sig-table{
font-size: 10px;
.pf-sig-table-edit-desc-text{
white-space: normal;
&.editable-empty{
border-bottom: none;
@extend .pf-dialog-icon-button;
}
}
.fa-plus{
@extend .pf-dialog-icon-button;
.pf-sig-table-edit-name-input{
text-transform: uppercase;
}
// textarea field
@@ -95,8 +86,8 @@
padding: 3px 6px;
}
.pf-sig-table-edit-name-input{
text-transform: uppercase;
.fa-plus{
@extend .pf-dialog-icon-button;
}
}

View File

@@ -24,15 +24,16 @@ table.dataTable thead th.sorting_desc:after {
table.dataTable thead th.sorting:after {
content: "\f0dc";
color: $green;
font-size: 0.8em;
margin-top: -8px;
}
table.dataTable thead th.sorting_asc:after {
content: "\f0de";
color: $green;
}
table.dataTable thead th.sorting_desc:after {
content: "\f0dd";
color: $green;
}
div.dataTables_scrollBody table.dataTable thead th.sorting:after,

View File

@@ -57,11 +57,10 @@
height: 25px;
width: auto;
min-width: 25px;
text-align: center;
i {
margin-top: 10px;
}
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.editable-inline .editableform-loading {