- Improved signature table column "type", new connection "size" label shown in cell, closed #874

- Improved client side JS in-memory cache performance
- Fixed some UI glitches on small (mobile) screens with some _tooltip_ select fields
This commit is contained in:
Mark Friedrich
2019-11-16 18:36:10 +01:00
parent ad0c592a3f
commit 36a11d53d3
21 changed files with 721 additions and 86 deletions

275
js/app/cache.js Normal file
View File

@@ -0,0 +1,275 @@
define([], () => {
'use strict';
/**
* Abstract Cache Strategy class
* @type {AbstractStrategy}
*/
let AbstractStrategy = class AbstractStrategy {
constructor(){
if(new.target === AbstractStrategy){
throw new TypeError('Cannot construct AbstractStrategy instances directly');
}
}
/**
* factory for Strategy* instances
* @returns {AbstractStrategy}
*/
static create(){
return new this();
}
};
/**
* LIFO Cache Strategy - First In First Out
* -> The cache evicts the entries in the order they were added,
* without any regard to how often or how many times they were accessed before.
* @type {StrategyFIFO}
*/
let StrategyFIFO = class StrategyFIFO extends AbstractStrategy {
valueToCompare(metaData){
return metaData.age();
}
compare(a, b){
return b - a;
}
};
/**
* LFU Cache Strategy - Least Frequently Used
* -> The cache evicts the entries in order how often have been accessed.
* Those that are used least often are discarded first
* @type {StrategyLFU}
*/
let StrategyLFU = class StrategyLFU extends AbstractStrategy {
valueToCompare(metaData){
return metaData.hitCount;
}
compare(a, b){
return a - b;
}
};
/**
* LRU Cache Strategy - Least Recently Used
* -> The cache evicts entries that have not been used for the longest amount of time.
* No matter how often they have been accessed.
* @type {StrategyLRU}
*/
let StrategyLRU = class StrategyLRU extends AbstractStrategy {
valueToCompare(metaData){
return metaData.hits[metaData.hits.length - 1] || metaData.set;
}
compare(a, b){
return a - b;
}
};
/**
* Each entry in cache also has its own instance of CacheEntryMeta
* -> The configured Cache Strategy use this meta data for eviction policy
* @type {CacheEntryMeta}
*/
let CacheEntryMeta = class CacheEntryMeta {
constructor(ttl, tSet){
this.ttl = ttl;
this.tSet = tSet || this.constructor.now();
this.tHits = [];
}
get set(){
return this.tSet;
}
get hits(){
return this.tHits;
}
get hitCount(){
return this.hits.length;
}
newHit(current){
this.tHits.push(current || this.constructor.now());
}
age(current){
return (current || this.constructor.now()) - this.tSet;
}
expired(current){
return this.ttl < this.age(current);
}
static now(){
return new Date().getTime() / 1000;
}
static create(ttl, tSet){
return new this(ttl, tSet);
}
};
/**
* Each instance of Cache represents a key value in memory data store
* -> Name should be set to identify current Cache instance
* -> Default ttl can be overwritten by individual entries
* -> Cache Strategy handles eviction policy
* -> Buffer Size (in percent) can be used to remove e.g. 10% of all entries
* if cache reaches maxSize limit, to increase performance.
* @type {Cache}
*/
let Cache = class Cache {
constructor(config){
this.config = Object.assign({
name: 'Default', // custom name for identification
ttl: 3600,
maxSize: 600,
bufferSize: 10, // in percent
strategy: 'FIFO',
debug: false
}, config);
this.store = new Map();
this.metaStore = new WeakMap();
this.strategy = this.constructor.setStrategy(this.config.strategy);
this.debug = (msg,...data) => {
if(this.config.debug){
data = (data || []);
data.unshift(this.config.name);
console.info('debug: CACHE %o | ' + msg, ...data);
}
};
this.debug('New Cache instance');
}
get size(){
return this.store.size;
}
isFull(){
return this.size>= this.config.maxSize;
}
set(key, value, ttl){
if(this.store.has(key)){
this.debug('SET key %o, UPDATE value %o', key, value);
this.store.set(key, value);
}else{
this.debug('SET key %o, NEW value %o', key, value);
if(this.isFull()){
this.debug(' ↪ FULL trim cache…');
this.trim(this.trimCount(1));
}
this.store.set(key, value);
}
this.metaStore.set(value, CacheEntryMeta.create(ttl || this.config.ttl));
}
get(key){
if(this.store.has(key)){
let value = this.store.get(key);
if(value){
let metaData = this.metaStore.get(value);
if(metaData.expired()){
this.debug('EXPIRED key %o delete', key);
this.delete(key);
}else{
this.debug('HIT key %o', key);
metaData.newHit();
return value;
}
}
}
this.debug('MISS key %o', key);
}
getOrDefault(key, def){
return this.get(key) || def;
}
keysForTrim(count){
let trimKeys = [];
let compare = [];
for(let [key, value] of this.store){
let metaData = this.metaStore.get(value);
if(metaData.expired()){
trimKeys.push(key);
if(count === trimKeys.length){
break;
}
}else{
compare.push({
key: key,
value: this.strategy.valueToCompare(metaData)
});
}
}
let countLeft = count - trimKeys.length;
if(countLeft > 0){
compare = compare.sort((a, b) => this.strategy.compare(a.value, b.value));
trimKeys = trimKeys.concat(compare.splice(0, countLeft).map(a => a.key));
}
return trimKeys;
}
keys(){
return this.store.keys();
}
delete(key){
return this.store.delete(key);
}
clear(){
this.store.clear();
}
trimCount(spaceLeft){
let bufferSize = Math.max(Math.round(this.config.maxSize / 100 * this.config.bufferSize), spaceLeft);
return Math.min(Math.max(this.size - this.config.maxSize + bufferSize, 0), this.size);
}
trim(count){
if(count > 0){
let trimKeys = this.keysForTrim(count);
if(count > trimKeys.length){
console.warn(' ↪ Failed to trim(%i) entries. Only %i in store', count, this.size);
}
this.debug(' ↪ DELETE min %i keys: %o', count, trimKeys);
trimKeys.forEach(key => this.delete(key));
}
}
status(){
return {
config: this.config,
store: this.store,
metaStore: this.metaStore
};
}
static setStrategy(name){
switch(name){
case 'FIFO': return StrategyFIFO.create();
case 'LFU': return StrategyLFU.create();
case 'LRU': return StrategyLRU.create();
default:
throw new ReferenceError('Unknown cache strategy name: ' + name);
}
}
};
return Cache;
});

View File

@@ -530,7 +530,7 @@ define([
// ... get endpoint label for source || target system
if(tmpSystem && tmpSystem){
// ... get all available signature type (wormholes) names
let availableSigTypeNames = SystemSignatures.getAllSignatureNamesBySystem(tmpSystem, 5);
let availableSigTypeNames = SystemSignatures.getSignatureTypeOptionsBySystem(tmpSystem, 5);
let flattenSigTypeNames = Util.flattenXEditableSelectArray(availableSigTypeNames);
if(flattenSigTypeNames.hasOwnProperty(signatureData.typeId)){

View File

@@ -9,7 +9,7 @@ define([
'app/render',
'app/map/worker',
'peityInlineChart',
], function($, Init, Util, Render, MapWorker){
], ($, Init, Util, Render, MapWorker) => {
'use strict';
let config = {

View File

@@ -77,14 +77,23 @@ define([
/**
* format results data for signature type select
* @param state
* @returns {*|jQuery|HTMLElement}
* @param container
* @param customOptions
* @returns {*|k.fn.init|jQuery|HTMLElement}
*/
let formatSignatureTypeSelectionData = state => {
let formatSignatureTypeSelectionData = (state, container, customOptions) => {
let parts = state.text.split(' - ');
let markup = '';
if(parts.length === 2){
// wormhole data -> 2 columns
let name = parts[0];
let sizeLabel;
if(Util.getObjVal(customOptions, 'showWhSizeLabel')){
let wormholeSizeData = Util.getObjVal(Init, 'wormholes.' + name + '.size');
sizeLabel = Util.getObjVal(wormholeSizeData, 'label') || '';
}
let securityClass = Util.getSecurityClassForSystem(getSystemSecurityFromLabel(parts[1]));
// some labels have a "suffix" label that should not have the securityClass
let labelParts = parts[1].split(/\s(.+)/);
@@ -93,9 +102,14 @@ define([
let classes = [securityClass, Util.config.popoverTriggerClass, Util.config.helpDefaultClass];
markup += '<span>' + parts[0] + '</span>&nbsp;&nbsp;';
markup += '<span>' + name + '</span>';
if(sizeLabel !== undefined){
markup += '<span><kbd>' + sizeLabel + '</kbd></span>';
}else{
markup += '&nbsp;&nbsp;';
}
markup += '<i class="fas fa-long-arrow-alt-right txt-color txt-color-grayLight"></i>';
markup += '<span class="' + classes.join(' ') + '" data-name="' + parts[0] + '">&nbsp;&nbsp;' + label + '</span>';
markup += '<span class="' + classes.join(' ') + '" data-name="' + name + '">&nbsp;&nbsp;' + label + '</span>';
if(suffix.length){
markup += '&nbsp;<span>' + suffix + '</span>';
}
@@ -343,15 +357,15 @@ define([
let hideShatteredClass = !data.shattered ? 'hide' : '';
let markup = '<div class="clearfix ' + config.resultOptionImageClass + '">';
markup += '<div class="col-sm-4 pf-select-item-anchor ' + systemNameClass + '">' + data.text + '</div>';
markup += '<div class="col-sm-2 text-right ' + data.effectClass + '">';
markup += '<div class="col-xs-4 pf-select-item-anchor ' + systemNameClass + '">' + data.text + '</div>';
markup += '<div class="col-xs-2 text-right ' + data.effectClass + '">';
markup += '<i class="fas fa-fw fa-square ' + hideEffectClass + '"></i>';
markup += '</div>';
markup += '<div class="col-sm-2 text-right ' + data.secClass + '">' + data.security + '</div>';
markup += '<div class="col-sm-2 text-right ' + shatteredClass + '">';
markup += '<div class="col-xs-2 text-right ' + data.secClass + '">' + data.security + '</div>';
markup += '<div class="col-xs-2 text-right ' + shatteredClass + '">';
markup += '<i class="fas fa-fw fa-chart-pie ' + hideShatteredClass + '"></i>';
markup += '</div>';
markup += '<div class="col-sm-2 text-right ' + data.trueSecClass + '">' + data.trueSec + '</div></div>';
markup += '<div class="col-xs-2 text-right ' + data.trueSecClass + '">' + data.trueSec + '</div></div>';
return markup;
}

View File

@@ -604,7 +604,7 @@ define([
break;
}
}
})
});
// init tooltips ------------------------------------------------------------------------------------------
let tooltipElements = moduleElement.find('[data-toggle="tooltip"]');

View File

@@ -6,8 +6,9 @@ define([
'jquery',
'app/init',
'app/util',
'app/cache',
'morris'
], ($, Init, Util, Morris) => {
], ($, Init, Util, Cache, Morris) => {
'use strict';
let config = {
@@ -39,7 +40,12 @@ define([
maxCountKills: 43
};
let cache = {};
let cache = new Cache({
name: 'killboardModule',
ttl: 60 * 60,
maxSize: 600,
debug: false
});
/**
*
@@ -59,9 +65,10 @@ define([
*/
let loadKillmailData = (requestData, context, callback) => {
let cacheKey = 'killmail_' + requestData.killId;
if(cache[cacheKey]){
let responseData = cache.get(cacheKey);
if(responseData){
// ... already cached -> return from cache
callback(context, cache[cacheKey])
callback(context, responseData)
.then(payload => showKills(payload.data.killboardElement, payload.data.systemId, payload.data.chunkSize));
}else{
// ...not cached -> request data
@@ -73,7 +80,7 @@ define([
dataType: 'json',
context: context
}).done(function(responseData){
cache[cacheKey] = responseData;
cache.set(cacheKey, responseData);
callback(this, responseData)
.then(payload => showKills(payload.data.killboardElement, payload.data.systemId, payload.data.chunkSize));
@@ -93,12 +100,14 @@ define([
*/
let showKills = (killboardElement, systemId, chunkSize) => {
if(chunkSize){
let cacheKey = 'zkb_' + systemId;
let data = cache.getOrDefault(cacheKey, []);
if(
killboardElement.children().length < config.maxCountKills &&
cache['zkb_' + systemId].length
data.length
){
// next killmail to load
let nextZkb = cache['zkb_' + systemId].shift();
let nextZkb = data.shift();
loadKillmailData({
killId: parseInt(nextZkb.killmail_id) || 0,
@@ -219,7 +228,7 @@ define([
}).done(function(result){
// zkb result needs to be cached and becomes reduced on "load more"
let cacheKey = 'zkb_' + systemData.systemId;
cache[cacheKey] = result;
cache.set(cacheKey, result);
if(result.length){
// kills found -> insert hidden warning for recent kills

View File

@@ -6,12 +6,13 @@ define([
'jquery',
'app/init',
'app/util',
'app/cache',
'bootbox',
'app/counter',
'app/map/map',
'app/map/util',
'app/ui/form_element'
], ($, Init, Util, bootbox, Counter, Map, MapUtil, FormElement) => {
], ($, Init, Util, Cache, bootbox, Counter, Map, MapUtil, FormElement) => {
'use strict';
let config = {
@@ -51,6 +52,7 @@ define([
sigTableEditSigNameInput: 'pf-sig-table-edit-name-input', // class for editable fields (sig name)
tableCellTypeClass: 'pf-table-type-cell', // class for "type" cells
tableCellConnectionClass: 'pf-table-connection-cell', // class for "connection" cells
tableCellFocusClass: 'pf-table-focus-cell', // class for "tab-able" cells. enable focus()
tableCellCounterClass: 'pf-table-counter-cell', // class for "counter" cells
@@ -76,7 +78,12 @@ define([
sigInfoCountConDeleteId: 'pf-sig-info-count-con-delete' // id for "connection delete" counter
};
let sigNameCache = {}; // cache signature names
let sigTypeOptionsCache = new Cache({ // cache signature names
name: 'sigTypeOptions',
ttl: 60 * 5,
maxSize: 100,
debug: false
});
let validSignatureNames = [ // allowed signature type/names
'Cosmic Anomaly',
@@ -244,7 +251,7 @@ define([
* @param groupId
* @returns {Array}
*/
let getAllSignatureNames = (systemData, systemTypeId, areaId, groupId) => {
let getSignatureTypeOptions = (systemData, systemTypeId, areaId, groupId) => {
systemTypeId = parseInt(systemTypeId || 0);
areaId = parseInt(areaId || 0);
groupId = parseInt(groupId || 0);
@@ -257,16 +264,18 @@ define([
let cacheKey = [systemTypeId, areaId, groupId].join('_');
newSelectOptions = sigTypeOptionsCache.getOrDefault(cacheKey, []);
// check for cached signature names
if(sigNameCache.hasOwnProperty( cacheKey )){
if(newSelectOptions.length){
// cached signatures do not include static WHs!
// -> ".slice(0)" creates copy
newSelectOptions = sigNameCache[cacheKey].slice(0);
newSelectOptions = newSelectOptions.slice(0);
newSelectOptionsCount = getOptionsCount('children', newSelectOptions);
}else{
// get new Options ----------
// get all possible "static" signature names by the selected groupId
let tempSelectOptions = Util.getAllSignatureNames(systemTypeId, areaId, groupId);
let tempSelectOptions = Util.getSignatureTypeNames(systemTypeId, areaId, groupId);
// format options into array with objects advantages: keep order, add more options (whs), use optgroup
if(tempSelectOptions){
@@ -351,7 +360,7 @@ define([
}
// update cache (clone array) -> further manipulation to this array, should not be cached
sigNameCache[cacheKey] = newSelectOptions.slice(0);
sigTypeOptionsCache.set(cacheKey, newSelectOptions.slice(0));
}
// static wormholes (DO NOT CACHE) (not all C2 WHs have the same statics,...
@@ -382,11 +391,11 @@ define([
* @param groupId
* @returns {Array}
*/
let getAllSignatureNamesBySystem = (systemElement, groupId) => {
let getSignatureTypeOptionsBySystem = (systemElement, groupId) => {
let systemTypeId = systemElement.data('typeId');
let areaId = Util.getAreaIdBySecurity(systemElement.data('security'));
let systemData = {statics: systemElement.data('statics')};
return getAllSignatureNames(systemData, systemTypeId, areaId, groupId);
return getSignatureTypeOptions(systemData, systemTypeId, areaId, groupId);
};
/**
@@ -882,7 +891,7 @@ define([
// try to get "typeId" from description string
let sigDescriptionLowerCase = sigDescription.toLowerCase();
let typeOptions = getAllSignatureNames(
let typeOptions = getSignatureTypeOptions(
systemData,
systemData.type.id,
Util.getAreaIdBySecurity(systemData.security),
@@ -1721,7 +1730,7 @@ define([
title: 'type',
type: 'string', // required for sort/filter because initial data type is numeric
width: 180,
class: [config.tableCellFocusClass].join(' '),
class: [config.tableCellFocusClass, config.tableCellTypeClass].join(' '),
data: 'typeId',
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
let tableApi = this.api();
@@ -1749,7 +1758,7 @@ define([
// -> "rowData" param is not current state, values are "on createCell()" state
let rowData = tableApi.row($(cell).parents('tr')).data();
let typeOptions = getAllSignatureNames(
let typeOptions = getSignatureTypeOptions(
systemData,
systemData.type.id,
Util.getAreaIdBySecurity(systemData.security),
@@ -1760,7 +1769,7 @@ define([
display: function(value, sourceData){
let selected = $.fn.editableutils.itemsByValue(value, sourceData);
if(selected.length && selected[0].value > 0){
$(this).html(FormElement.formatSignatureTypeSelectionData({text: selected[0].text}));
$(this).html(FormElement.formatSignatureTypeSelectionData({text: selected[0].text}, undefined, {showWhSizeLabel: true}));
}else{
$(this).empty();
}
@@ -3307,6 +3316,6 @@ define([
updateModule: updateModule,
beforeHide: beforeHide,
beforeDestroy: beforeDestroy,
getAllSignatureNamesBySystem: getAllSignatureNamesBySystem
getSignatureTypeOptionsBySystem: getSignatureTypeOptionsBySystem
};
});

View File

@@ -2586,7 +2586,7 @@ define([
* @param sigGroupId
* @returns {{}}
*/
let getAllSignatureNames = (systemTypeId, areaId, sigGroupId) => {
let getSignatureTypeNames = (systemTypeId, areaId, sigGroupId) => {
let signatureNames = {};
if(
SignatureType[systemTypeId] &&
@@ -3553,7 +3553,7 @@ define([
getTrueSecClassForSystem: getTrueSecClassForSystem,
getStatusInfoForSystem: getStatusInfoForSystem,
getSignatureGroupOptions: getSignatureGroupOptions,
getAllSignatureNames: getAllSignatureNames,
getSignatureTypeNames: getSignatureTypeNames,
getAreaIdBySecurity: getAreaIdBySecurity,
setCurrentMapUserData: setCurrentMapUserData,
getCurrentMapUserData: getCurrentMapUserData,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,275 @@
define([], () => {
'use strict';
/**
* Abstract Cache Strategy class
* @type {AbstractStrategy}
*/
let AbstractStrategy = class AbstractStrategy {
constructor(){
if(new.target === AbstractStrategy){
throw new TypeError('Cannot construct AbstractStrategy instances directly');
}
}
/**
* factory for Strategy* instances
* @returns {AbstractStrategy}
*/
static create(){
return new this();
}
};
/**
* LIFO Cache Strategy - First In First Out
* -> The cache evicts the entries in the order they were added,
* without any regard to how often or how many times they were accessed before.
* @type {StrategyFIFO}
*/
let StrategyFIFO = class StrategyFIFO extends AbstractStrategy {
valueToCompare(metaData){
return metaData.age();
}
compare(a, b){
return b - a;
}
};
/**
* LFU Cache Strategy - Least Frequently Used
* -> The cache evicts the entries in order how often have been accessed.
* Those that are used least often are discarded first
* @type {StrategyLFU}
*/
let StrategyLFU = class StrategyLFU extends AbstractStrategy {
valueToCompare(metaData){
return metaData.hitCount;
}
compare(a, b){
return a - b;
}
};
/**
* LRU Cache Strategy - Least Recently Used
* -> The cache evicts entries that have not been used for the longest amount of time.
* No matter how often they have been accessed.
* @type {StrategyLRU}
*/
let StrategyLRU = class StrategyLRU extends AbstractStrategy {
valueToCompare(metaData){
return metaData.hits[metaData.hits.length - 1] || metaData.set;
}
compare(a, b){
return a - b;
}
};
/**
* Each entry in cache also has its own instance of CacheEntryMeta
* -> The configured Cache Strategy use this meta data for eviction policy
* @type {CacheEntryMeta}
*/
let CacheEntryMeta = class CacheEntryMeta {
constructor(ttl, tSet){
this.ttl = ttl;
this.tSet = tSet || this.constructor.now();
this.tHits = [];
}
get set(){
return this.tSet;
}
get hits(){
return this.tHits;
}
get hitCount(){
return this.hits.length;
}
newHit(current){
this.tHits.push(current || this.constructor.now());
}
age(current){
return (current || this.constructor.now()) - this.tSet;
}
expired(current){
return this.ttl < this.age(current);
}
static now(){
return new Date().getTime() / 1000;
}
static create(ttl, tSet){
return new this(ttl, tSet);
}
};
/**
* Each instance of Cache represents a key value in memory data store
* -> Name should be set to identify current Cache instance
* -> Default ttl can be overwritten by individual entries
* -> Cache Strategy handles eviction policy
* -> Buffer Size (in percent) can be used to remove e.g. 10% of all entries
* if cache reaches maxSize limit, to increase performance.
* @type {Cache}
*/
let Cache = class Cache {
constructor(config){
this.config = Object.assign({
name: 'Default', // custom name for identification
ttl: 3600,
maxSize: 600,
bufferSize: 10, // in percent
strategy: 'FIFO',
debug: false
}, config);
this.store = new Map();
this.metaStore = new WeakMap();
this.strategy = this.constructor.setStrategy(this.config.strategy);
this.debug = (msg,...data) => {
if(this.config.debug){
data = (data || []);
data.unshift(this.config.name);
console.info('debug: CACHE %o | ' + msg, ...data);
}
};
this.debug('New Cache instance');
}
get size(){
return this.store.size;
}
isFull(){
return this.size>= this.config.maxSize;
}
set(key, value, ttl){
if(this.store.has(key)){
this.debug('SET key %o, UPDATE value %o', key, value);
this.store.set(key, value);
}else{
this.debug('SET key %o, NEW value %o', key, value);
if(this.isFull()){
this.debug(' ↪ FULL trim cache…');
this.trim(this.trimCount(1));
}
this.store.set(key, value);
}
this.metaStore.set(value, CacheEntryMeta.create(ttl || this.config.ttl));
}
get(key){
if(this.store.has(key)){
let value = this.store.get(key);
if(value){
let metaData = this.metaStore.get(value);
if(metaData.expired()){
this.debug('EXPIRED key %o delete', key);
this.delete(key);
}else{
this.debug('HIT key %o', key);
metaData.newHit();
return value;
}
}
}
this.debug('MISS key %o', key);
}
getOrDefault(key, def){
return this.get(key) || def;
}
keysForTrim(count){
let trimKeys = [];
let compare = [];
for(let [key, value] of this.store){
let metaData = this.metaStore.get(value);
if(metaData.expired()){
trimKeys.push(key);
if(count === trimKeys.length){
break;
}
}else{
compare.push({
key: key,
value: this.strategy.valueToCompare(metaData)
});
}
}
let countLeft = count - trimKeys.length;
if(countLeft > 0){
compare = compare.sort((a, b) => this.strategy.compare(a.value, b.value));
trimKeys = trimKeys.concat(compare.splice(0, countLeft).map(a => a.key));
}
return trimKeys;
}
keys(){
return this.store.keys();
}
delete(key){
return this.store.delete(key);
}
clear(){
this.store.clear();
}
trimCount(spaceLeft){
let bufferSize = Math.max(Math.round(this.config.maxSize / 100 * this.config.bufferSize), spaceLeft);
return Math.min(Math.max(this.size - this.config.maxSize + bufferSize, 0), this.size);
}
trim(count){
if(count > 0){
let trimKeys = this.keysForTrim(count);
if(count > trimKeys.length){
console.warn(' ↪ Failed to trim(%i) entries. Only %i in store', count, this.size);
}
this.debug(' ↪ DELETE min %i keys: %o', count, trimKeys);
trimKeys.forEach(key => this.delete(key));
}
}
status(){
return {
config: this.config,
store: this.store,
metaStore: this.metaStore
};
}
static setStrategy(name){
switch(name){
case 'FIFO': return StrategyFIFO.create();
case 'LFU': return StrategyLFU.create();
case 'LRU': return StrategyLRU.create();
default:
throw new ReferenceError('Unknown cache strategy name: ' + name);
}
}
};
return Cache;
});

View File

@@ -530,7 +530,7 @@ define([
// ... get endpoint label for source || target system
if(tmpSystem && tmpSystem){
// ... get all available signature type (wormholes) names
let availableSigTypeNames = SystemSignatures.getAllSignatureNamesBySystem(tmpSystem, 5);
let availableSigTypeNames = SystemSignatures.getSignatureTypeOptionsBySystem(tmpSystem, 5);
let flattenSigTypeNames = Util.flattenXEditableSelectArray(availableSigTypeNames);
if(flattenSigTypeNames.hasOwnProperty(signatureData.typeId)){

View File

@@ -9,7 +9,7 @@ define([
'app/render',
'app/map/worker',
'peityInlineChart',
], function($, Init, Util, Render, MapWorker){
], ($, Init, Util, Render, MapWorker) => {
'use strict';
let config = {

View File

@@ -77,14 +77,23 @@ define([
/**
* format results data for signature type select
* @param state
* @returns {*|jQuery|HTMLElement}
* @param container
* @param customOptions
* @returns {*|k.fn.init|jQuery|HTMLElement}
*/
let formatSignatureTypeSelectionData = state => {
let formatSignatureTypeSelectionData = (state, container, customOptions) => {
let parts = state.text.split(' - ');
let markup = '';
if(parts.length === 2){
// wormhole data -> 2 columns
let name = parts[0];
let sizeLabel;
if(Util.getObjVal(customOptions, 'showWhSizeLabel')){
let wormholeSizeData = Util.getObjVal(Init, 'wormholes.' + name + '.size');
sizeLabel = Util.getObjVal(wormholeSizeData, 'label') || '';
}
let securityClass = Util.getSecurityClassForSystem(getSystemSecurityFromLabel(parts[1]));
// some labels have a "suffix" label that should not have the securityClass
let labelParts = parts[1].split(/\s(.+)/);
@@ -93,9 +102,14 @@ define([
let classes = [securityClass, Util.config.popoverTriggerClass, Util.config.helpDefaultClass];
markup += '<span>' + parts[0] + '</span>&nbsp;&nbsp;';
markup += '<span>' + name + '</span>';
if(sizeLabel !== undefined){
markup += '<span><kbd>' + sizeLabel + '</kbd></span>';
}else{
markup += '&nbsp;&nbsp;';
}
markup += '<i class="fas fa-long-arrow-alt-right txt-color txt-color-grayLight"></i>';
markup += '<span class="' + classes.join(' ') + '" data-name="' + parts[0] + '">&nbsp;&nbsp;' + label + '</span>';
markup += '<span class="' + classes.join(' ') + '" data-name="' + name + '">&nbsp;&nbsp;' + label + '</span>';
if(suffix.length){
markup += '&nbsp;<span>' + suffix + '</span>';
}
@@ -343,15 +357,15 @@ define([
let hideShatteredClass = !data.shattered ? 'hide' : '';
let markup = '<div class="clearfix ' + config.resultOptionImageClass + '">';
markup += '<div class="col-sm-4 pf-select-item-anchor ' + systemNameClass + '">' + data.text + '</div>';
markup += '<div class="col-sm-2 text-right ' + data.effectClass + '">';
markup += '<div class="col-xs-4 pf-select-item-anchor ' + systemNameClass + '">' + data.text + '</div>';
markup += '<div class="col-xs-2 text-right ' + data.effectClass + '">';
markup += '<i class="fas fa-fw fa-square ' + hideEffectClass + '"></i>';
markup += '</div>';
markup += '<div class="col-sm-2 text-right ' + data.secClass + '">' + data.security + '</div>';
markup += '<div class="col-sm-2 text-right ' + shatteredClass + '">';
markup += '<div class="col-xs-2 text-right ' + data.secClass + '">' + data.security + '</div>';
markup += '<div class="col-xs-2 text-right ' + shatteredClass + '">';
markup += '<i class="fas fa-fw fa-chart-pie ' + hideShatteredClass + '"></i>';
markup += '</div>';
markup += '<div class="col-sm-2 text-right ' + data.trueSecClass + '">' + data.trueSec + '</div></div>';
markup += '<div class="col-xs-2 text-right ' + data.trueSecClass + '">' + data.trueSec + '</div></div>';
return markup;
}

View File

@@ -604,7 +604,7 @@ define([
break;
}
}
})
});
// init tooltips ------------------------------------------------------------------------------------------
let tooltipElements = moduleElement.find('[data-toggle="tooltip"]');

View File

@@ -6,8 +6,9 @@ define([
'jquery',
'app/init',
'app/util',
'app/cache',
'morris'
], ($, Init, Util, Morris) => {
], ($, Init, Util, Cache, Morris) => {
'use strict';
let config = {
@@ -39,7 +40,12 @@ define([
maxCountKills: 43
};
let cache = {};
let cache = new Cache({
name: 'killboardModule',
ttl: 60 * 60,
maxSize: 600,
debug: false
});
/**
*
@@ -59,9 +65,10 @@ define([
*/
let loadKillmailData = (requestData, context, callback) => {
let cacheKey = 'killmail_' + requestData.killId;
if(cache[cacheKey]){
let responseData = cache.get(cacheKey);
if(responseData){
// ... already cached -> return from cache
callback(context, cache[cacheKey])
callback(context, responseData)
.then(payload => showKills(payload.data.killboardElement, payload.data.systemId, payload.data.chunkSize));
}else{
// ...not cached -> request data
@@ -73,7 +80,7 @@ define([
dataType: 'json',
context: context
}).done(function(responseData){
cache[cacheKey] = responseData;
cache.set(cacheKey, responseData);
callback(this, responseData)
.then(payload => showKills(payload.data.killboardElement, payload.data.systemId, payload.data.chunkSize));
@@ -93,12 +100,14 @@ define([
*/
let showKills = (killboardElement, systemId, chunkSize) => {
if(chunkSize){
let cacheKey = 'zkb_' + systemId;
let data = cache.getOrDefault(cacheKey, []);
if(
killboardElement.children().length < config.maxCountKills &&
cache['zkb_' + systemId].length
data.length
){
// next killmail to load
let nextZkb = cache['zkb_' + systemId].shift();
let nextZkb = data.shift();
loadKillmailData({
killId: parseInt(nextZkb.killmail_id) || 0,
@@ -219,7 +228,7 @@ define([
}).done(function(result){
// zkb result needs to be cached and becomes reduced on "load more"
let cacheKey = 'zkb_' + systemData.systemId;
cache[cacheKey] = result;
cache.set(cacheKey, result);
if(result.length){
// kills found -> insert hidden warning for recent kills

View File

@@ -6,12 +6,13 @@ define([
'jquery',
'app/init',
'app/util',
'app/cache',
'bootbox',
'app/counter',
'app/map/map',
'app/map/util',
'app/ui/form_element'
], ($, Init, Util, bootbox, Counter, Map, MapUtil, FormElement) => {
], ($, Init, Util, Cache, bootbox, Counter, Map, MapUtil, FormElement) => {
'use strict';
let config = {
@@ -51,6 +52,7 @@ define([
sigTableEditSigNameInput: 'pf-sig-table-edit-name-input', // class for editable fields (sig name)
tableCellTypeClass: 'pf-table-type-cell', // class for "type" cells
tableCellConnectionClass: 'pf-table-connection-cell', // class for "connection" cells
tableCellFocusClass: 'pf-table-focus-cell', // class for "tab-able" cells. enable focus()
tableCellCounterClass: 'pf-table-counter-cell', // class for "counter" cells
@@ -76,7 +78,12 @@ define([
sigInfoCountConDeleteId: 'pf-sig-info-count-con-delete' // id for "connection delete" counter
};
let sigNameCache = {}; // cache signature names
let sigTypeOptionsCache = new Cache({ // cache signature names
name: 'sigTypeOptions',
ttl: 60 * 5,
maxSize: 100,
debug: false
});
let validSignatureNames = [ // allowed signature type/names
'Cosmic Anomaly',
@@ -244,7 +251,7 @@ define([
* @param groupId
* @returns {Array}
*/
let getAllSignatureNames = (systemData, systemTypeId, areaId, groupId) => {
let getSignatureTypeOptions = (systemData, systemTypeId, areaId, groupId) => {
systemTypeId = parseInt(systemTypeId || 0);
areaId = parseInt(areaId || 0);
groupId = parseInt(groupId || 0);
@@ -257,16 +264,18 @@ define([
let cacheKey = [systemTypeId, areaId, groupId].join('_');
newSelectOptions = sigTypeOptionsCache.getOrDefault(cacheKey, []);
// check for cached signature names
if(sigNameCache.hasOwnProperty( cacheKey )){
if(newSelectOptions.length){
// cached signatures do not include static WHs!
// -> ".slice(0)" creates copy
newSelectOptions = sigNameCache[cacheKey].slice(0);
newSelectOptions = newSelectOptions.slice(0);
newSelectOptionsCount = getOptionsCount('children', newSelectOptions);
}else{
// get new Options ----------
// get all possible "static" signature names by the selected groupId
let tempSelectOptions = Util.getAllSignatureNames(systemTypeId, areaId, groupId);
let tempSelectOptions = Util.getSignatureTypeNames(systemTypeId, areaId, groupId);
// format options into array with objects advantages: keep order, add more options (whs), use optgroup
if(tempSelectOptions){
@@ -351,7 +360,7 @@ define([
}
// update cache (clone array) -> further manipulation to this array, should not be cached
sigNameCache[cacheKey] = newSelectOptions.slice(0);
sigTypeOptionsCache.set(cacheKey, newSelectOptions.slice(0));
}
// static wormholes (DO NOT CACHE) (not all C2 WHs have the same statics,...
@@ -382,11 +391,11 @@ define([
* @param groupId
* @returns {Array}
*/
let getAllSignatureNamesBySystem = (systemElement, groupId) => {
let getSignatureTypeOptionsBySystem = (systemElement, groupId) => {
let systemTypeId = systemElement.data('typeId');
let areaId = Util.getAreaIdBySecurity(systemElement.data('security'));
let systemData = {statics: systemElement.data('statics')};
return getAllSignatureNames(systemData, systemTypeId, areaId, groupId);
return getSignatureTypeOptions(systemData, systemTypeId, areaId, groupId);
};
/**
@@ -882,7 +891,7 @@ define([
// try to get "typeId" from description string
let sigDescriptionLowerCase = sigDescription.toLowerCase();
let typeOptions = getAllSignatureNames(
let typeOptions = getSignatureTypeOptions(
systemData,
systemData.type.id,
Util.getAreaIdBySecurity(systemData.security),
@@ -1721,7 +1730,7 @@ define([
title: 'type',
type: 'string', // required for sort/filter because initial data type is numeric
width: 180,
class: [config.tableCellFocusClass].join(' '),
class: [config.tableCellFocusClass, config.tableCellTypeClass].join(' '),
data: 'typeId',
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
let tableApi = this.api();
@@ -1749,7 +1758,7 @@ define([
// -> "rowData" param is not current state, values are "on createCell()" state
let rowData = tableApi.row($(cell).parents('tr')).data();
let typeOptions = getAllSignatureNames(
let typeOptions = getSignatureTypeOptions(
systemData,
systemData.type.id,
Util.getAreaIdBySecurity(systemData.security),
@@ -1760,7 +1769,7 @@ define([
display: function(value, sourceData){
let selected = $.fn.editableutils.itemsByValue(value, sourceData);
if(selected.length && selected[0].value > 0){
$(this).html(FormElement.formatSignatureTypeSelectionData({text: selected[0].text}));
$(this).html(FormElement.formatSignatureTypeSelectionData({text: selected[0].text}, undefined, {showWhSizeLabel: true}));
}else{
$(this).empty();
}
@@ -3307,6 +3316,6 @@ define([
updateModule: updateModule,
beforeHide: beforeHide,
beforeDestroy: beforeDestroy,
getAllSignatureNamesBySystem: getAllSignatureNamesBySystem
getSignatureTypeOptionsBySystem: getSignatureTypeOptionsBySystem
};
});

View File

@@ -2586,7 +2586,7 @@ define([
* @param sigGroupId
* @returns {{}}
*/
let getAllSignatureNames = (systemTypeId, areaId, sigGroupId) => {
let getSignatureTypeNames = (systemTypeId, areaId, sigGroupId) => {
let signatureNames = {};
if(
SignatureType[systemTypeId] &&
@@ -3553,7 +3553,7 @@ define([
getTrueSecClassForSystem: getTrueSecClassForSystem,
getStatusInfoForSystem: getStatusInfoForSystem,
getSignatureGroupOptions: getSignatureGroupOptions,
getAllSignatureNames: getAllSignatureNames,
getSignatureTypeNames: getSignatureTypeNames,
getAreaIdBySecurity: getAreaIdBySecurity,
setCurrentMapUserData: setCurrentMapUserData,
getCurrentMapUserData: getCurrentMapUserData,

View File

@@ -69,10 +69,10 @@
<div id="{{sectionInfoId}}" class="collapse">
<div class="row">
<div class="col-xs-12 col-sm-6">
<div class="col-xs-5 col-sm-6">
<div class="form-group">
<label class="col-xs-4 col-sm-3 control-label">Alias</label>
<div class="col-xs-8 col-sm-9">
<label class="col-xs-3 col-sm-3 control-label">Alias</label>
<div class="col-xs-9 col-sm-9">
<div class="controls">
<div id="{{aliasId}}" class="form-control-static pf-dynamic-area"></div>
</div>
@@ -80,10 +80,10 @@
</div>
</div>
<div class="col-xs-12 col-sm-3">
<div class="col-xs-4 col-sm-3">
<div class="form-group">
<label class="col-xs-4 col-sm-8 control-label">Signatures</label>
<div class="col-xs-8 col-sm-4">
<label class="col-xs-7 col-sm-8 control-label">Signatures</label>
<div class="col-xs-5 col-sm-4">
<div class="controls">
<div id="{{signaturesId}}" class="form-control-static pf-dynamic-area text-right txt-color"></div>
</div>
@@ -91,17 +91,17 @@
</div>
</div>
<div class="col-xs-12 col-sm-3">
<div class="col-xs-3 col-sm-3">
<div id="{{createdId}}" class="well well-sm well-text-number text-center" title="first create"></div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-9">
<div class="col-xs-9 col-sm-9">
<div class="form-group">
<label class="col-xs-12 col-sm-2 control-label">Description</label>
<div class="col-xs-12 col-sm-10">
<label class="col-xs-3 col-sm-2 control-label">Description</label>
<div class="col-xs-9 col-sm-10">
<div class="controls">
<div id="{{descriptionId}}" class="form-control-static pf-dynamic-area"></div>
</div>
@@ -109,7 +109,7 @@
</div>
</div>
<div class="col-xs-12 col-sm-3">
<div class="col-xs-3 col-sm-3">
<div id="{{updatedId}}" class="well well-sm well-text-number text-center" title="last update/delete"></div>
</div>
</div>

View File

@@ -3,7 +3,6 @@
opacity: 0;
}
// splash loading animation ============================================================================
.pf-color-line{

View File

@@ -187,6 +187,9 @@ em,
letter-spacing: .02em;
line-height: 16px;
text-rendering: geometricPrecision;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
}
@@ -539,6 +542,25 @@ select:active, select:hover {
}
}
&.pf-table-type-cell{
> span {
&:first-child{
display: inline-block;
width: 28px;
}
&:nth-child(2){
display: inline-block;
width: 22px;
kbd{
&:empty{
display: none;
}
}
}
}
}
&.pf-table-image-cell{
padding: 0 !important;
image-rendering: -webkit-optimize-contrast;