- new connection "size" flags for wormholes, closed #645, closed #568

- new persistent map zoom level between sessions
- improved wormhole type names. Color codes represent their target systems security level (green → HS, red → C5/6/0.0,…)
- improved JS performance for map updates, signature updates, map zoom, map overlays
- improved "manual" section for connections (size info added)
- upgraded [_farahey_](https://github.com/jsplumb/farahey) js lib `v0.5` → `v1.1.2`
This commit is contained in:
Mark Friedrich
2019-06-16 14:59:22 +02:00
parent 37b8e04bdb
commit 8c8189d205
55 changed files with 2850 additions and 1927 deletions

View File

@@ -155,7 +155,7 @@ class Route extends Controller\AccessController {
}
if( $filterData['wormholesFrigate'] !== true ){
$excludeTypes[] = 'frigate';
$excludeTypes[] = 'wh_jump_mass_s';
}
if( $filterData['wormholesEOL'] === false ){

View File

@@ -401,8 +401,7 @@ class Sso extends Api\User{
if( !empty($authCodeRequestData['expiresIn']) ){
// expire time for accessToken
try{
$timezone = $this->getF3()->get('getTimeZone')();
$accessTokenExpires = new \DateTime('now', $timezone);
$accessTokenExpires = $this->getF3()->get('getDateTime')();
$accessTokenExpires->add(new \DateInterval('PT' . (int)$authCodeRequestData['expiresIn'] . 'S'));
$accessData->esiAccessTokenExpires = $accessTokenExpires->format('Y-m-d H:i:s');

View File

@@ -92,7 +92,23 @@ class ConnectionModel extends AbstractMapTrackingModel {
* allowed connection types
* @var array
*/
protected static $connectionTypeWhitelist = ['stargate', 'abyssal', 'wh_fresh', 'wh_reduced', 'wh_critical', 'frigate', 'preserve_mass'];
protected static $connectionTypeWhitelist = [
// base type for scopes
'stargate',
'abyssal',
// wh mass reduction types
'wh_fresh',
'wh_reduced',
'wh_critical',
// wh jump mass types
'wh_jump_mass_s',
'wh_jump_mass_m',
'wh_jump_mass_l',
'wh_jump_mass_xl',
// other types
'wh_eol',
'preserve_mass'
];
/**
* get connection data

View File

@@ -658,7 +658,7 @@ class MapModel extends AbstractMapTrackingModel {
/**
* @var $connection ConnectionModel
*/
$connectionData[] = $connection->getData();
$connectionData[] = $connection->getData(true);
}
return $connectionData;

View File

@@ -812,7 +812,9 @@ class SystemModel extends AbstractMapTrackingModel {
}
/**
* CharacterModel $character
* Updates the signature history cache
* -> each (bulk) change to signatures of this system must result in a new signature history cache entry
* -> This method also clears the cache of this system, so that new signature data gets returned for in getData()
* @param CharacterModel $character
* @param string $action
* @throws \Exception
@@ -833,6 +835,11 @@ class SystemModel extends AbstractMapTrackingModel {
array_splice($signaturesHistoryData, self::MAX_HISTORY_SIGNATURES);
$this->updateCacheData($signaturesHistoryData, self::DATA_CACHE_KEY_SIGNATURES, self::TTL_HISTORY_SIGNATURES);
// clear system cache here
// -> Signature model updates should NOT update the system cache on change
// because a "bulk" change to signatures would clear the system cache multiple times
$this->clearCacheData();
}
}

View File

@@ -34,7 +34,7 @@ requirejs.config({
velocityUI: 'lib/velocity.ui.min', // v5.2.0 plugin for velocity - http://julian.com/research/velocity/#uiPack
slidebars: 'lib/slidebars', // v2.0.2 Slidebars - side menu plugin https://www.adchsm.com/slidebars/
jsPlumb: 'lib/jsplumb', // v2.9.3 jsPlumb main map draw plugin http://jsplumb.github.io/jsplumb/home.html
farahey: 'lib/farahey-0.5', // v0.5 jsPlumb "magnetizing" extension - https://github.com/jsplumb/farahey
farahey: 'lib/farahey', // v1.1.2 jsPlumb "magnetizing" plugin extension - https://github.com/ThomasChan/farahey
customScrollbar: 'lib/jquery.mCustomScrollbar.min', // v3.1.5 Custom scroll bars - http://manos.malihu.gr
mousewheel: 'lib/jquery.mousewheel.min', // v3.1.13 Mousewheel - https://github.com/jquery/jquery-mousewheel
xEditable: 'lib/bootstrap-editable.min', // v1.5.1 X-editable - in placed editing

View File

@@ -145,7 +145,6 @@ define(['jquery'], ($) => {
unicode: ''
}
],
classes: {
// log types
logTypes: {
@@ -354,17 +353,61 @@ define(['jquery'], ($) => {
wh_critical: {
cssClass: 'pf-map-connection-wh-critical'
},
frigate: {
cssClass: 'pf-map-connection-frig',
wh_jump_mass_s: {
cssClass: 'pf-map-connection-wh-size-s',
paintStyle: {
dashstyle: '0.5 1'
dashstyle: '0.5 1',
strokeWidth: 3
},
overlays: [
['Label',
{
label: 'frig',
cssClass: ['pf-map-component-overlay', 'frig'].join(' '),
location: 0.7
label: '<i class="fas fa-char pf-jump-mass-s" data-char-content="S"></i>',
cssClass: ['pf-map-component-overlay', 'small', 'text-center'].join(' '),
location: 0.65,
id: 'pf-map-connection-jump-mass-overlay'
}]
]
},
wh_jump_mass_m: {
cssClass: 'pf-map-connection-wh-size-m',
paintStyle: {
dashstyle: '3 1'
},
overlays: [
['Label',
{
label: '<i class="fas fa-char pf-jump-mass-m" data-char-content="M"></i>',
cssClass: ['pf-map-component-overlay', 'small', 'text-center'].join(' '),
location: 0.65,
id: 'pf-map-connection-jump-mass-overlay'
}]
]
},
wh_jump_mass_l: {
cssClass: 'pf-map-connection-wh-size-l',
overlays: [
['Label',
{
label: '<i class="fas fa-char pf-jump-mass-l" data-char-content="L"></i>',
cssClass: ['pf-map-component-overlay', 'small', 'text-center'].join(' '),
location: 0.65,
id: 'pf-map-connection-jump-mass-overlay'
}]
]
},
wh_jump_mass_xl: {
cssClass: 'pf-map-connection-wh-size-xl',
paintStyle: {
strokeWidth: 6
},
overlays: [
['Label',
{
label: '<i class="fas fa-char pf-jump-mass-xl" data-char-content="XL"></i>',
cssClass: ['pf-map-component-overlay', 'small', 'text-center'].join(' '),
location: 0.65,
id: 'pf-map-connection-jump-mass-overlay'
}]
]
},
@@ -375,7 +418,7 @@ define(['jquery'], ($) => {
{
label: '<i class="fas fa-fw fa-exclamation-triangle"></i>&nbsp;save mass',
cssClass: ['pf-map-component-overlay', 'mass'].join(' '),
location: 0.3
location: 0.35
}]
]
},
@@ -387,8 +430,8 @@ define(['jquery'], ($) => {
cssClass: 'pf-map-connection-arrow-overlay',
width: 12,
length: 15,
foldback: 0.8,
direction: 1,
foldback: 0.8,
location: 0.5
}]
]
@@ -408,6 +451,32 @@ define(['jquery'], ($) => {
]
}
},
wormholeSizes: {
wh_jump_mass_xl: {
jumpMassMin: 1000000000,
type: 'wh_jump_mass_xl',
class: 'pf-jump-mass-xl',
label: 'XL'
},
wh_jump_mass_l: {
jumpMassMin: 300000000,
type: 'wh_jump_mass_l',
class: 'pf-jump-mass-l',
label: 'L'
},
wh_jump_mass_m: {
jumpMassMin: 20000000,
type: 'wh_jump_mass_m',
class: 'pf-jump-mass-m',
label: 'M'
},
wh_jump_mass_s: {
jumpMassMin: 1000,
type: 'wh_jump_mass_s',
class: 'pf-jump-mass-s',
label: 'S'
}
},
// signature groups
signatureGroups: {
1: {

View File

@@ -93,20 +93,25 @@ define([
let moduleData = {
id: config.connectionContextMenuId,
items: [
{icon: 'fa-plane', action: 'frigate', text: 'frigate hole'},
{icon: 'fa-hourglass-end', action: 'wh_eol', text: 'toggle EOL'},
{icon: 'fa-exclamation-triangle', action: 'preserve_mass', text: 'preserve mass'},
{icon: 'fa-crosshairs', action: 'change_scope', text: 'change scope', subitems: [
{subIcon: 'fa-minus-circle', subIconClass: '', subAction: 'scope_wh', subText: 'wormhole'},
{subIcon: 'fa-minus-circle', subIconClass: 'txt-color txt-color-indigoDarkest', subAction: 'scope_stargate', subText: 'stargate'},
{subIcon: 'fa-minus-circle', subIconClass: 'txt-color txt-color-tealLighter', subAction: 'scope_jumpbridge', subText: 'jumpbridge'}
{icon: 'fa-reply fa-rotate-180', action: 'change_status', text: 'mass status', subitems: [
{subIcon: 'fa-circle', subIconClass: 'txt-color txt-color-gray', subAction: 'status_fresh', subText: 'stage 1 (fresh)'},
{subIcon: 'fa-circle', subIconClass: 'txt-color txt-color-orange', subAction: 'status_reduced', subText: 'stage 2 (reduced)'},
{subIcon: 'fa-circle', subIconClass: 'txt-color txt-color-redDarker', subAction: 'status_critical', subText: 'stage 3 (critical)'}
]},
{icon: 'fa-reply fa-rotate-180', action: 'change_status', text: 'change status', subitems: [
{subIcon: 'fa-clock', subAction: 'wh_eol', subText: 'toggle EOL'},
{subDivider: true},
{subIcon: 'fa-circle', subAction: 'status_fresh', subText: 'stage 1 (fresh)'},
{subIcon: 'fa-adjust', subAction: 'status_reduced', subText: 'stage 2 (reduced)'},
{subIcon: 'fa-circle', subAction: 'status_critical', subText: 'stage 3 (critical)'}
{icon: 'fa-reply fa-rotate-180', action: 'wh_jump_mass_change', text: 'ship size', subitems: [
{subIcon: 'fa-char', subChar: 'S', subAction: 'wh_jump_mass_s', subText: 'smallest ships'},
{subIcon: 'fa-char', subChar: 'M', subAction: 'wh_jump_mass_m', subText: 'medium ships'},
{subIcon: 'fa-char', subChar: 'L', subAction: 'wh_jump_mass_l', subText: 'larger ships'},
{subIcon: 'fa-char', subChar: 'XL', subAction: 'wh_jump_mass_xl', subText: 'capital ships'}
]},
{icon: 'fa-crosshairs', action: 'change_scope', text: 'change scope', subitems: [
{subIcon: 'fa-minus-circle', subIconClass: '', subAction: 'scope_wh', subText: 'wormhole'},
{subIcon: 'fa-minus-circle', subIconClass: 'txt-color txt-color-indigoDarkest', subAction: 'scope_stargate', subText: 'stargate'},
{subIcon: 'fa-minus-circle', subIconClass: 'txt-color txt-color-tealLighter', subAction: 'scope_jumpbridge', subText: 'jumpbridge'}
]},
{divider: true, action: 'separator'} ,

View File

@@ -1,135 +1,173 @@
/**
* Map "magnetizing" feature
* jsPlumb extension used: http://morrisonpitt.com/farahey/
* jsPlumb extension used: https://github.com/ThomasChan/farahey
*/
define([
'jquery',
'app/map/util',
'farahey'
], function($, MapUtil){
], ($, MapUtil) => {
'use strict';
/**
* Cached current "Magnetizer" object
* @type {Magnetizer}
* active magnetizer instances (cache object)
* @type {{}}
*/
let m8 = null;
let magnetizerInstances = {};
/**
* init a jsPlumb (map) Element for "magnetised" function.
* this is optional and prevents systems from being overlapped
* magnetizer instance exists for mapId
* @param mapId
* @returns {boolean}
*/
$.fn.initMagnetizer = function(){
let mapContainer = this;
let systems = mapContainer.getSystems();
let hasInstance = mapId => magnetizerInstances.hasOwnProperty(mapId);
/**
* helper function
* get current system offset
* @param system
* @returns {{left, top}}
* @private
*/
let _offset = function(system){
/**
* get magnetizer instance by mapId
* @param mapId
* @returns {null}
*/
let getInstance = mapId => hasInstance(mapId) ? magnetizerInstances[mapId] : null;
let _ = function(p){
let v = system.style[p];
return parseInt(v.substring(0, v.length - 2));
};
return {
left:_('left'),
top:_('top')
};
};
/**
* helper function
* set new system offset
* @param system
* @param o
* @private
*/
let _setOffset = function(system, o){
let markAsUpdated = false;
// new position must be within parent container
// no negative offset!
if(
o.left >= 0 &&
o.left <= 2300
){
markAsUpdated = true;
system.style.left = o.left + 'px';
}
if(
o.top >= 0 &&
o.top <= 498
){
markAsUpdated = true;
system.style.top = o.top + 'px';
}
if(markAsUpdated === true){
MapUtil.markAsChanged($(system));
}
};
/**
* helper function
* exclude current dragged element(s) from position update
* @param id
* @returns {boolean}
* @private
*/
let _dragFilter = function(id){
return !$('#' + id).is('.jsPlumb_dragged, .pf-system-locked');
};
let gridConstrain = function(gridX, gridY){
return function(id, current, delta){
if( mapContainer.hasClass(MapUtil.config.mapGridClass) ){
// active grid
return {
left:(gridX * Math.floor( (current[0] + delta.left) / gridX )) - current[0],
top:(gridY * Math.floor( (current[1] + delta.top) / gridY )) - current[1]
};
}else{
// no grid
return delta;
}
};
};
// main init for "magnetize" feature ------------------------------------------------------
m8 = new Magnetizer({
container: mapContainer,
getContainerPosition: function(c){
return c.offset();
},
getPosition:_offset,
getSize: function(system){
return [ $(system).outerWidth(), $(system).outerHeight() ];
},
getId : function(system){
return $(system).attr('id');
},
setPosition:_setOffset,
elements: systems,
filter: _dragFilter,
padding: [6, 6],
constrain: gridConstrain(MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension)
});
/**
* set new magnetizer instance for mapId
* @param mapId
* @param magnetizer
*/
let setInstance = (mapId, magnetizer) => {
if(mapId && magnetizer){
magnetizerInstances[mapId] = magnetizer;
}
};
$.fn.destroyMagnetizer = function(){
let mapContainer = this;
/**
* init new magnetizer instance for a map
* @param mapContainer
*/
let initMagnetizer = mapContainer => {
let mapId = mapContainer.data('id');
// remove cached "magnetizer" instance
m8 = null;
if(!hasInstance(mapId)){
// magnetizer not exist -> new instance
let systems = mapContainer.getSystems();
/**
* function that takes an element from your list and returns its position as a JS object
* @param system
* @returns {{top: number, left: number}}
* @private
*/
let _offset = system => {
let _ = p => {
let v = system.style[p];
return parseInt(v.substring(0, v.length - 2));
};
return {left: _('left'), top: _('top')};
};
/**
* function that takes an element id and position, and applies that position to the related element
* @param system
* @param o
* @private
*/
let _setOffset = (system, o) => {
o.left = Math.round(o.left);
o.top = Math.round(o.top);
let left = o.left + 'px';
let top = o.top + 'px';
let markAsUpdated = false;
// new position must be within parent container
// no negative offset!
if(
o.left >= 0 && o.left <= 2300 &&
system.style.left !== left
){
system.style.left = left;
markAsUpdated = true;
}
if(
o.top >= 0 && o.top <= 1400 &&
system.style.top !== top
){
system.style.top = top;
markAsUpdated = true;
}
if(markAsUpdated){
MapUtil.markAsChanged($(system));
}
};
/**
* filter some element8s) from being moved
* @param systemId
* @returns {boolean}
* @private
*/
let _dragFilter = systemId => {
let filterClasses = ['jtk-drag', 'pf-system-locked'];
return ![...document.getElementById(systemId).classList].some(className => filterClasses.indexOf(className) >= 0);
};
/**
* grid snap constraint
* @param gridX
* @param gridY
* @returns {Function}
*/
let gridConstrain = (gridX, gridY) => {
return (id, current, delta) => {
if(mapContainer.hasClass(MapUtil.config.mapGridClass)){
// active grid
return {
left: (gridX * Math.floor( (Math.round(current[0]) + delta.left) / gridX )) - current[0],
top: (gridY * Math.floor( (Math.round(current[1]) + delta.top) / gridY )) - current[1]
};
}else{
// no grid
delta.left = Math.round(delta.left);
delta.top = Math.round(delta.top);
return delta;
}
};
};
// create new magnetizer instance -------------------------------------------------------------------------
setInstance(mapId, window.Farahey.getInstance({
container: mapContainer,
getContainerPosition: mapContainer => mapContainer.offset(),
getPosition:_offset,
getSize: system => {
let clientRect = system.getBoundingClientRect();
return [Math.floor(clientRect.width), Math.floor(clientRect.height)];
},
getId : system => system.id,
setPosition:_setOffset,
elements: systems.toArray(),
filter: _dragFilter,
padding: [3, 3],
constrain: gridConstrain(MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension),
executeNow: false, // no initial rearrange after initialization
excludeFocus: true
}));
}
};
/**
* destroy Magnetizer instance
*/
let destroyMagnetizer = mapContainer => {
let mapId = mapContainer.data('id');
let magnetizer = getInstance(mapId);
if(magnetizer){
magnetizer.reset();
delete magnetizerInstances[mapId];
}
};
/**
@@ -137,44 +175,50 @@ define([
* @param map
* @param e
*/
let executeAtEvent = function(map, e){
if(m8 !== null && e ){
m8.executeAtEvent(e);
let executeAtEvent = (map, e) => {
let mapContainer = $(map.getContainer());
let mapId = mapContainer.data('id');
let magnetizer = getInstance(mapId);
if(magnetizer && e){
magnetizer.executeAtEvent(e, {
iterations: 2,
excludeFocus: true
});
map.repaintEverything();
}
};
/**
* rearrange all systems of a map
* needs "magnetization" to be active
* @param map
* add system to magnetizer instance
* @param mapId
* @param system
* @param doNotTestForDuplicates
*/
let executeAtCenter = function(map){
if(m8 !== null){
m8.executeAtCenter();
map.repaintEverything();
let addElement = (mapId, system, doNotTestForDuplicates) => {
let magnetizer = getInstance(mapId);
if(magnetizer){
magnetizer.addElement(system, doNotTestForDuplicates);
}
};
/**
* set/update elements for "magnetization"
* -> (e.g. new systems was added)
* @param map
* remove system element from magnetizer instance
* @param mapId
* @param system
*/
let setElements = function(map){
if(m8 !== null){
let mapContainer = $(map.getContainer());
let systems = mapContainer.getSystems();
m8.setElements(systems);
// re-arrange systems
executeAtCenter(map);
let removeElement = (mapId, system) => {
let magnetizer = getInstance(mapId);
if(magnetizer){
magnetizer.removeElement(system);
}
};
return {
executeAtCenter: executeAtCenter,
initMagnetizer: initMagnetizer,
destroyMagnetizer: destroyMagnetizer,
executeAtEvent: executeAtEvent,
setElements: setElements
addElement: addElement,
removeElement: removeElement
};
});

View File

@@ -18,7 +18,7 @@ define([
'app/map/scrollbar',
'dragToSelect',
'app/map/local'
], ($, Init, Util, Key, bootbox, MapUtil, MapContextMenu, MapOverlay, MapOverlayUtil, System, Layout, MagnetizerWrapper, Scrollbar) => {
], ($, Init, Util, Key, bootbox, MapUtil, MapContextMenu, MapOverlay, MapOverlayUtil, System, Layout, Magnetizer, Scrollbar) => {
'use strict';
@@ -62,6 +62,33 @@ define([
// -> those maps queue their updates until "pf:unlocked" event
let mapUpdateQueue = [];
// map menu options
let mapOptions = {
mapMagnetizer: {
buttonId: Util.config.menuButtonMagnetizerId,
description: 'Magnetizer',
onEnable: Magnetizer.initMagnetizer,
onDisable: Magnetizer.destroyMagnetizer
},
mapSnapToGrid : {
buttonId: Util.config.menuButtonGridId,
description: 'Grid snapping',
class: 'mapGridClass'
},
mapSignatureOverlays : {
buttonId: Util.config.menuButtonEndpointId,
description: 'Endpoint overlay',
onEnable: MapOverlay.showInfoSignatureOverlays,
onDisable: MapOverlay.hideInfoSignatureOverlays,
},
mapCompact : {
buttonId: Util.config.menuButtonCompactId,
description: 'Compact system layout',
class: 'mapCompactClass'
}
};
/**
* checks mouse events on system head elements
* -> prevents drag/drop system AND drag/drop connections on some child elements
@@ -131,8 +158,8 @@ define([
// check if there is a Label overlay
let overlay = endpoint.getOverlay(MapOverlayUtil.config.endpointOverlayId);
if(overlay instanceof jsPlumb.Overlays.Label){
let label = overlay.getParameter('label');
overlay.setLocation(MapUtil.getEndpointOverlaySignatureLocation(endpoint, label));
let labels = overlay.getParameter('signatureLabels');
overlay.setLocation(MapUtil.getEndpointOverlaySignatureLocation(endpoint, labels));
}
}
}
@@ -675,7 +702,7 @@ define([
}
}
// save filterScopes in IndexDB
// store filterScopes in IndexDB
MapUtil.storeLocalData('map', mapId, 'filterScopes', filterScopes);
MapUtil.filterMapByScopes(map, filterScopes);
@@ -726,7 +753,6 @@ define([
}
});
break;
case 'frigate': // set as frigate hole
case 'preserve_mass': // set "preserve mass
case 'wh_eol': // set "end of life"
MapOverlayUtil.getMapOverlay(mapElement, 'timer').startMapUpdateCounter();
@@ -741,6 +767,14 @@ define([
MapUtil.setConnectionWHStatus(connection, 'wh_' + newStatus);
MapUtil.markAsChanged(connection);
break;
case 'wh_jump_mass_s':
case 'wh_jump_mass_m':
case 'wh_jump_mass_l':
case 'wh_jump_mass_xl':
MapOverlayUtil.getMapOverlay(mapElement, 'timer').startMapUpdateCounter();
MapUtil.setConnectionJumpMassType(connection, action);
MapUtil.markAsChanged(connection);
break;
case 'scope_wh':
case 'scope_stargate':
case 'scope_jumpbridge':
@@ -969,13 +1003,13 @@ define([
if(checkAvailability(['fresh', 'reduced', 'critical'], type)){
MapUtil.setConnectionWHStatus(connection, type);
}else if(connection.hasType(type) !== true){
// additional types e.g. eol, frig, preserve mass
// additional types e.g. eol, preserve mass
connection.addType(type);
}
}
for(let type of removeType){
if(checkAvailability(['wh_eol', 'frigate', 'preserve_mass', 'state_process'], type)){
if(checkAvailability(['wh_eol', 'preserve_mass', 'state_process'], type)){
connection.removeType(type);
}
}
@@ -1178,7 +1212,6 @@ define([
let mapContainer = mapConfig.map ? $(mapConfig.map.getContainer()) : null;
if(mapContainer){
let mapId = mapConfig.config.id;
let newSystems = 0;
// add additional information for this map
if(mapContainer.data('updated') !== mapConfig.config.updated.updated){
@@ -1219,9 +1252,8 @@ define([
}
}
if( addNewSystem === true){
if(addNewSystem === true){
drawSystem(mapConfig.map, systemData);
newSystems++;
}
}
@@ -1324,11 +1356,6 @@ define([
// update local connection cache
updateConnectionsCache(mapConfig.map);
// update map "magnetization" when new systems where added
if(newSystems > 0){
MagnetizerWrapper.setElements(mapConfig.map);
}
}else{
// map is currently logged -> queue update for this map until unlock
if( mapUpdateQueue.indexOf(mapId) === -1 ){
@@ -1345,10 +1372,13 @@ define([
});
};
return new Promise(updateMapExecutor).then(payload => {
/**
* apply current active scope filter
* @param payload
* @returns {Promise<any>}
*/
let filterMapByScopes = payload => {
let filterMapByScopesExecutor = (resolve, reject) => {
// apply current active scope filter ==================================================================
let promiseStore = MapUtil.getLocaleData('map', payload.data.mapConfig.config.id);
promiseStore.then(dataStore => {
let scopes = [];
@@ -1362,7 +1392,31 @@ define([
};
return new Promise(filterMapByScopesExecutor);
});
};
/**
* show signature overlays
* @param payload
* @returns {Promise<any>}
*/
let showInfoSignatureOverlays = payload => {
let showInfoSignatureOverlaysExecutor = (resolve, reject) => {
let promiseStore = MapUtil.getLocaleData('map', payload.data.mapConfig.config.id);
promiseStore.then(dataStore => {
if(dataStore && dataStore.mapSignatureOverlays){
MapOverlay.showInfoSignatureOverlays($(payload.data.mapConfig.map.getContainer()));
}
resolve(payload);
});
};
return new Promise(showInfoSignatureOverlaysExecutor);
};
return new Promise(updateMapExecutor)
.then(showInfoSignatureOverlays)
.then(filterMapByScopes);
};
/**
@@ -1504,6 +1558,9 @@ define([
// set system observer
setSystemObserver(map, newSystem);
// register system to "magnetizer"
Magnetizer.addElement(systemData.mapId, newSystem[0]);
// connect new system (if connection data is given)
if(connectedSystem){
@@ -1736,22 +1793,26 @@ define([
// hidden menu actions
if(scope === 'abyssal'){
options.hidden.push('frigate');
options.hidden.push('wh_eol');
options.hidden.push('preserve_mass');
options.hidden.push('change_status');
options.hidden.push('wh_jump_mass_change');
options.hidden.push('change_scope');
options.hidden.push('separator');
}else if(scope === 'stargate'){
options.hidden.push('frigate');
options.hidden.push('wh_eol');
options.hidden.push('preserve_mass');
options.hidden.push('change_status');
options.hidden.push('wh_jump_mass_change');
options.hidden.push('scope_stargate');
}else if(scope === 'jumpbridge'){
options.hidden.push('frigate');
options.hidden.push('wh_eol');
options.hidden.push('preserve_mass');
options.hidden.push('change_status');
options.hidden.push('wh_jump_mass_change');
options.hidden.push('scope_jumpbridge');
}else if(scope === 'wh'){
options.hidden.push('scope_wh');
@@ -1761,13 +1822,14 @@ define([
if(connection.hasType('wh_eol') === true){
options.active.push('wh_eol');
}
if(connection.hasType('frigate') === true){
options.active.push('frigate');
}
if(connection.hasType('preserve_mass') === true){
options.active.push('preserve_mass');
}
for(let sizeName of Object.keys(Init.wormholeSizes)){
if(connection.hasType(sizeName)){
options.active.push(sizeName);
}
}
if(connection.hasType('wh_reduced') === true){
options.active.push('status_reduced');
}else if(connection.hasType('wh_critical') === true){
@@ -1777,6 +1839,11 @@ define([
options.active.push('status_fresh');
}
// disabled menu actions
if(connection.getParameter('sizeLocked')){
options.disabled.push('wh_jump_mass_change');
}
resolve(options);
};
@@ -1864,7 +1931,7 @@ define([
// update system positions for "all" systems that are effected by drag&drop
// this requires "magnet" feature to be active! (optional)
MagnetizerWrapper.executeAtEvent(map, p.e);
Magnetizer.executeAtEvent(map, p.e);
},
stop: function(params){
let dragSystem = $(params.el);
@@ -1967,11 +2034,7 @@ define([
* @param sourceSystem
*/
let saveSystemCallback = (map, newSystemData, sourceSystem) => {
// draw new system to map
drawSystem(map, newSystemData, sourceSystem);
// re/arrange systems (prevent overlapping)
MagnetizerWrapper.setElements(map);
};
/**
@@ -2224,6 +2287,13 @@ define([
// Notification the current zoom was changed
newJsPlumbInstance.bind('zoom', function(zoom){
MapOverlay.updateZoomOverlay(this);
// store new zoom level in IndexDB
if(zoom === 1){
MapUtil.deleteLocalData('map', mapId, 'mapZoom');
}else{
MapUtil.storeLocalData('map', mapId, 'mapZoom', zoom);
}
});
// ========================================================================================================
@@ -2523,7 +2593,7 @@ define([
let mapElement = $(this);
// get map menu config options
let data = MapUtil.mapOptions[mapOption.option];
let data = mapOptions[mapOption.option];
let promiseStore = MapUtil.getLocaleData('map', mapElement.data('id'));
promiseStore.then(function(dataStore){
@@ -2538,50 +2608,50 @@ define([
dataExists = true;
}
if(dataExists === mapOption.toggle){
if(dataExists === this.mapOption.toggle){
// toggle button class
button.removeClass('active');
// toggle map class (e.g. for grid)
if(this.data.class){
this.mapElement.removeClass( MapUtil.config[this.data.class] );
this.mapElement.removeClass(MapUtil.config[this.data.class]);
}
// call optional jQuery extension on mapElement
if(this.data.onDisable){
$.fn[ this.data.onDisable ].apply(this.mapElement);
if(this.data.onDisable && !this.mapOption.skipOnDisable){
this.data.onDisable(this.mapElement);
}
// show map overlay info icon
MapOverlayUtil.getMapOverlay(this.mapElement, 'info').updateOverlayIcon(this.mapOption.option, 'hide');
// delete map option
MapUtil.deleteLocalData('map', this.mapElement.data('id'), this.mapOption.option );
MapUtil.deleteLocalData('map', this.mapElement.data('id'), this.mapOption.option);
}else{
// toggle button class
button.addClass('active');
// toggle map class (e.g. for grid)
if(this.data.class){
this.mapElement.addClass( MapUtil.config[this.data.class] );
this.mapElement.addClass(MapUtil.config[this.data.class]);
}
// call optional jQuery extension on mapElement
if(this.data.onEnable){
$.fn[ this.data.onEnable ].apply(this.mapElement);
if(this.data.onEnable && !this.mapOption.skipOnEnable){
this.data.onEnable(this.mapElement);
}
// hide map overlay info icon
MapOverlayUtil.getMapOverlay(this.mapElement, 'info').updateOverlayIcon(this.mapOption.option, 'show');
// store map option
MapUtil.storeLocalData('map', this.mapElement.data('id'), this.mapOption.option, 1 );
MapUtil.storeLocalData('map', this.mapElement.data('id'), this.mapOption.option, 1);
notificationText = 'enabled';
}
if(mapOption.toggle){
if(this.mapOption.toggle){
Util.showNotify({title: this.data.description, text: notificationText, type: 'info'});
}
}.bind({
@@ -3009,7 +3079,8 @@ define([
let mapElement = $(mapConfig.map.getContainer());
MapUtil.setMapDefaultOptions(mapElement, mapConfig.config)
.then(payload => MapUtil.visualizeMap(mapElement, 'show'))
.then(payload => MapUtil.scrollToDefaultPosition(mapElement))
.then(payload => MapUtil.zoomToDefaultScale(mapConfig.map))
.then(payload => MapUtil.scrollToDefaultPosition(mapConfig.map))
.then(payload => {
Util.showNotify({title: 'Map initialized', text: mapConfig.config.name + ' - loaded', type: 'success'});
})
@@ -3039,7 +3110,7 @@ define([
*/
let loadMapExecutor = (resolve, reject) => {
// init jsPlumb
jsPlumb.ready(function(){
jsPlumb.ready(() => {
// get new map instance or load existing
mapConfig.map = getMapInstance(mapConfig.config.id);

View File

@@ -11,81 +11,93 @@ define([
], ($, Init, Util, MapOverlayUtil, MapUtil) => {
'use strict';
/**
* get MapObject (jsPlumb) from mapElement
* @param mapElement
* @returns {*}
*/
let getMapObjectFromMapElement = mapElement => {
let Map = require('app/map/map');
return Map.getMapInstance(mapElement.data('id'));
};
/**
* get map object (jsPlumb) from iconElement
* @param overlayIcon
* @returns {*}
*/
let getMapObjectFromOverlayIcon = overlayIcon => {
let mapElement = Util.getMapElementFromOverlay(overlayIcon);
return getMapObjectFromMapElement(mapElement);
return MapUtil.getMapInstance(Util.getMapElementFromOverlay(overlayIcon).data('id'));
};
/**
* add overlay to endpoint with signature data
* add/update endpoints with overlays from signature mapping
* @param endpoint
* @param labelData
*/
let addEndpointOverlaySignatureLabel = (endpoint, labelData) => {
let label = labelData.labels.join(', ');
let name = labelData.names.join(', ');
let updateEndpointOverlaySignatureLabel = (endpoint, labelData) => {
let labels = labelData.labels;
let names = labelData.names;
let overlay = endpoint.getOverlay(MapOverlayUtil.config.endpointOverlayId);
if(overlay instanceof jsPlumb.Overlays.Label){
// update existing overlay
if(
label !== overlay.getParameter('label') ||
name !== overlay.getParameter('signatureName')
!labels.equalValues(overlay.getParameter('signatureLabels')) ||
!names.equalValues(overlay.getParameter('signatureNames'))
){
// update label only on label changes
overlay.setLabel(MapUtil.formatEndpointOverlaySignatureLabel(label));
overlay.setLabel(MapUtil.formatEndpointOverlaySignatureLabel(labels));
overlay.setParameter('fullSize', false);
overlay.setParameter('label', label);
overlay.setParameter('signatureName', name);
overlay.updateClasses(label.length ? 'small' : 'icon', label.length ? 'icon' : 'small');
overlay.setLocation(MapUtil.getEndpointOverlaySignatureLocation(endpoint, label));
overlay.setParameter('signatureLabels', labels);
overlay.setParameter('signatureNames', names);
overlay.updateClasses(labels.length ? 'small' : 'icon', labels.length ? 'icon' : 'small');
overlay.setLocation(MapUtil.getEndpointOverlaySignatureLocation(endpoint, labels));
}
}else{
// add new overlay
endpoint.addOverlay([
'Label',
{
label: MapUtil.formatEndpointOverlaySignatureLabel(label),
label: MapUtil.formatEndpointOverlaySignatureLabel(labels),
id: MapOverlayUtil.config.endpointOverlayId,
cssClass: [MapOverlayUtil.config.componentOverlayClass, label.length ? 'small' : 'icon'].join(' '),
location: MapUtil.getEndpointOverlaySignatureLocation(endpoint, label),
cssClass: [MapOverlayUtil.config.componentOverlayClass, labels.length ? 'small' : 'icon'].join(' '),
location: MapUtil.getEndpointOverlaySignatureLocation(endpoint, labels),
events: {
toggleSize: function(fullSize){
let signatureName = this.getParameter('signatureName');
if(fullSize && !this.getParameter('fullSize') && signatureName){
this.setLabel(this.getLabel() + '<br>' + '<span class="initialism">' + signatureName + '</span>');
let signatureNames = this.getParameter('signatureNames');
if(fullSize && !this.getParameter('fullSize') && signatureNames.length){
this.setLabel(this.getLabel() + '<br>' + '<span class="initialism">' + signatureNames.join(', ') + '</span>');
this.setParameter('fullSize', true);
}else if(this.getParameter('fullSize')){
this.setLabel(MapUtil.formatEndpointOverlaySignatureLabel(this.getParameter('label')));
this.setLabel(MapUtil.formatEndpointOverlaySignatureLabel(this.getParameter('signatureLabels')));
this.setParameter('fullSize', false);
}
}
},
parameters: {
fullSize: false,
label: label,
signatureName: name
signatureLabels: labels,
signatureNames: names
}
}
]);
}
};
/**
* get overlay parameters for connection overlay (type 'diamond' or 'arrow')
* @param overlayType
* @param direction
* @returns {{length: number, foldback: number, direction: number}}
*/
let getConnectionArrowOverlayParams = (overlayType, direction = 1) => {
switch(overlayType){
case 'arrow':
return {
length: 15,
direction: direction,
foldback: 0.8
};
default: // diamond
return {
length: 10,
direction: 1,
foldback: 2
};
}
};
/**
* add overlays to connections (signature based data)
* @param map
@@ -93,26 +105,18 @@ define([
*/
let updateInfoSignatureOverlays = (map, connectionsData) => {
let type = 'info_signature';
let SystemSignatures = require('app/ui/module/system_signature');
connectionsData = Util.arrayToObject(connectionsData);
map.batch(function(){
map.getAllConnections().forEach(function(connection){
map.batch(() => {
map.getAllConnections().forEach(connection => {
let connectionId = connection.getParameter('connectionId');
let sourceEndpoint = connection.endpoints[0];
let targetEndpoint = connection.endpoints[1];
let signatureTypeData = {
source: {
names: [],
labels: []
},
target: {
names: [],
labels: []
}
};
let connectionData = connectionsData.hasOwnProperty(connectionId) ? connectionsData[connectionId] : undefined;
let signatureTypeData = MapUtil.getConnectionDataFromSignatures(connection, connectionData);
let sizeLockedBySignature = false;
if(connection.scope === 'wh'){
if(!connection.hasType(type)){
@@ -127,49 +131,49 @@ define([
connection.canvas.appendChild(overlayArrow.path);
}
let overlayType = 'Diamond'; // not specified
// since there "could" be multiple sig labels on each endpoint,
// there can only one "primary label picked up for wormhole jump mass detection!
let primLabel;
let overlayType = 'diamond'; // not specified
let arrowDirection = 1;
let arrowFoldback = 2;
if(connectionsData.hasOwnProperty(connectionId)){
if(connectionData && connectionData.signatures){
// signature data found for current connection
signatureTypeData = MapUtil.getConnectionDataFromSignatures(connection, connectionsData[connectionId]);
let sourceLabel = signatureTypeData.source.labels;
let targetLabel = signatureTypeData.target.labels;
// add arrow (connection) overlay that points from "XXX" => "K162" ------------------------------------
// add arrow (connection) overlay that points from "XXX" => "K162" ----------------------------
if(
(sourceLabel.indexOf('K162') !== -1 && targetLabel.indexOf('K162') !== -1) ||
(sourceLabel.includes('K162') && targetLabel.includes('K162')) ||
(sourceLabel.length === 0 && targetLabel.length === 0) ||
(
sourceLabel.length > 0 && targetLabel.length > 0 &&
sourceLabel.indexOf('K162') === -1 && targetLabel.indexOf('K162') === -1
!sourceLabel.includes('K162') && !targetLabel.includes('K162')
)
){
// unknown direction
overlayType = 'Diamond'; // not specified
arrowDirection = 1;
arrowFoldback = 2;
// unknown direction -> show default 'diamond' overlay
overlayType = 'diamond';
}else if(
(sourceLabel.indexOf('K162') !== -1) ||
(sourceLabel.length === 0 && targetLabel.indexOf('K162') === -1)
(sourceLabel.includes('K162')) ||
(sourceLabel.length === 0 && !targetLabel.includes('K162'))
){
// convert default arrow direction
overlayType = 'Arrow';
overlayType = 'arrow';
arrowDirection = -1;
arrowFoldback = 0.8;
primLabel = targetLabel.find(label => label !== 'K162');
}else{
// default arrow direction is fine
overlayType = 'Arrow';
arrowDirection = 1;
arrowFoldback = 0.8;
overlayType = 'arrow';
primLabel = sourceLabel.find(label => label !== 'K162');
}
}
// class changes must be done on "connection" itself not on "overlayArrow"
// -> because Arrow might not be rendered to map at this point (if it does not exist already)
if(overlayType === 'Arrow'){
if(overlayType === 'arrow'){
connection.updateClasses(
MapOverlayUtil.config.connectionArrowOverlaySuccessClass,
MapOverlayUtil.config.connectionArrowOverlayDangerClass
@@ -181,20 +185,39 @@ define([
);
}
overlayArrow.updateFrom({
direction: arrowDirection,
foldback: arrowFoldback
});
overlayArrow.updateFrom(getConnectionArrowOverlayParams(overlayType, arrowDirection));
// add endpoint overlays --------------------------------------------------------------------------
addEndpointOverlaySignatureLabel(sourceEndpoint, signatureTypeData.source);
addEndpointOverlaySignatureLabel(targetEndpoint, signatureTypeData.target);
// update/add endpoint overlays -------------------------------------------------------------------
updateEndpointOverlaySignatureLabel(sourceEndpoint, signatureTypeData.source);
updateEndpointOverlaySignatureLabel(targetEndpoint, signatureTypeData.target);
// fix/overwrite existing jump mass connection type -----------------------------------------------
// if a connection type for "jump mass" (e.g. S, M, L, XL) is set for this connection
// we should check/compare it with the current primary signature label from signature mapping
// and change it if necessary
if(Init.wormholes.hasOwnProperty(primLabel)){
// connection size from mapped signature
sizeLockedBySignature = true;
let wormholeData = Object.assign({}, Init.wormholes[primLabel]);
if(
wormholeData.size && wormholeData.size.type &&
!connection.hasType(wormholeData.size.type)
){
MapOverlayUtil.getMapOverlay(connection.canvas, 'timer').startMapUpdateCounter();
MapUtil.setConnectionJumpMassType(connection, wormholeData.size.type);
MapUtil.markAsChanged(connection);
}
}
}else{
// connection is not 'wh' scope
if(connection.hasType(type)){
connection.removeType(type);
}
}
// lock/unlock connection for manual size changes (from contextmenu)
connection.setParameter('sizeLocked', sizeLockedBySignature);
});
});
};
@@ -246,58 +269,45 @@ define([
};
/**
* git signature data that is linked to a connection for a mapId
* @param mapElement
* @param callback
* get overlay icon from e.g. mapElement
* @param element
* @param iconClass
* @param overlayType
* @returns {*}
*/
let getConnectionSignatureData = (mapElement, callback) => {
let mapOverlay = MapOverlayUtil.getMapOverlay(mapElement, 'info');
let overlayConnectionIcon = mapOverlay.find('.pf-map-overlay-endpoint');
showLoading(overlayConnectionIcon);
let requestData = {
mapId: mapElement.data('id'),
addData : ['signatures'],
filterData : ['signatures']
};
$.ajax({
type: 'POST',
url: Init.path.getMapConnectionData,
data: requestData,
dataType: 'json',
context: {
mapElement: mapElement,
overlayConnectionIcon: overlayConnectionIcon
}
}).done(function(connectionsData){
let map = getMapObjectFromMapElement(this.mapElement);
callback(map, connectionsData);
}).always(function(){
hideLoading(this.overlayConnectionIcon);
});
let getOverlayIcon = (element, iconClass, overlayType = 'info') => {
return MapOverlayUtil.getMapOverlay(element, overlayType).find('.' + iconClass);
};
/**
* showInfoSignatureOverlays
* -> used by "refresh" overlays (hover) AND/OR initial menu trigger
*/
$.fn.showInfoSignatureOverlays = function(){
let mapElement = $(this);
getConnectionSignatureData(mapElement, updateInfoSignatureOverlays);
let showInfoSignatureOverlays = mapElement => {
let mapId = mapElement.data('id');
let map = MapUtil.getMapInstance(mapId);
let mapData = Util.getCurrentMapData(mapId);
let connectionsData = Util.getObjVal(mapData, 'data.connections');
if(connectionsData){
let overlayIcon = getOverlayIcon(mapElement, options.mapSignatureOverlays.class);
showLoading(overlayIcon);
updateInfoSignatureOverlays(map, connectionsData);
hideLoading(overlayIcon);
}
};
/**
* hideInfoSignatureOverlays
* -> see showInfoSignatureOverlays()
*/
$.fn.hideInfoSignatureOverlays = function(){
let map = getMapObjectFromMapElement($(this));
let hideInfoSignatureOverlays = mapElement => {
let mapId = mapElement.data('id');
let map = MapUtil.getMapInstance(mapId);
let type = 'info_signature';
map.batch(function(){
map.getAllConnections().forEach(function(connection){
map.batch(() => {
map.getAllConnections().forEach(connection => {
let overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId);
if(overlayArrow){
@@ -382,20 +392,11 @@ define([
}
}
},
mapEndpoint: {
mapSignatureOverlays: {
title: 'refresh signature overlays',
trigger: 'refresh',
class: 'pf-map-overlay-endpoint',
iconClass: ['fas', 'fa-fw', 'fa-link'],
hoverIntent: {
over: function(e){
let mapElement = Util.getMapElementFromOverlay(this);
mapElement.showInfoSignatureOverlays();
},
out: function(e){
// just "refresh" on hover
}
}
iconClass: ['fas', 'fa-fw', 'fa-link']
},
mapCompact: {
title: 'compact layout',
@@ -427,8 +428,8 @@ define([
// format overlay label
let labels = [
'<i class="fas fa-fw fa-plus-square"></i>&nbsp;' + formatTimeParts(createdDiff),
'<i class="fas fa-fw fa-pen-square"></i>&nbsp;' + formatTimeParts(updatedDiff)
formatTimeParts(createdDiff) + '&nbsp;<i class="fas fa-fw fa-plus-square"></i>',
formatTimeParts(updatedDiff) + '&nbsp;<i class="fas fa-fw fa-pen-square"></i>'
];
// add label overlay --------------------------------------------------------------------------
@@ -437,7 +438,7 @@ define([
{
label: labels.join('<br>'),
id: MapOverlayUtil.config.connectionOverlayWhId,
cssClass: [MapOverlayUtil.config.componentOverlayClass, 'small'].join(' '),
cssClass: [MapOverlayUtil.config.componentOverlayClass, 'small', 'text-right'].join(' '),
location: 0.35
}
]);
@@ -457,7 +458,7 @@ define([
title: 'EOL timer',
trigger: 'hover',
class: 'pf-map-overlay-connection-eol',
iconClass: ['far', 'fa-fw', 'fa-clock'],
iconClass: ['fas', 'fa-fw', 'fa-hourglass-end'],
hoverIntent: {
over: function(e){
let map = getMapObjectFromOverlayIcon(this);
@@ -472,7 +473,7 @@ define([
connection.addOverlay([
'Label',
{
label: '<i class="far fa-fw fa-clock"></i>&nbsp;' + formatTimeParts(diff),
label: '<i class="fas fa-fw fa-hourglass-end"></i>&nbsp;' + formatTimeParts(diff),
id: MapOverlayUtil.config.connectionOverlayEolId,
cssClass: [MapOverlayUtil.config.componentOverlayClass, 'eol'].join(' '),
location: 0.25
@@ -542,7 +543,7 @@ define([
let percentPerCount = 100 / maxSeconds;
// update counter
let updateChart = function(tempSeconds){
let updateChart = tempSeconds => {
let pieChart = counterChart.data('easyPieChart');
if(pieChart !== undefined){
@@ -552,7 +553,7 @@ define([
};
// main timer function is called on any counter update
let timer = function(mapUpdateCounter){
let timer = mapUpdateCounter => {
// decrease timer
let currentSeconds = counterChart.data('currentSeconds');
currentSeconds--;
@@ -883,6 +884,8 @@ define([
};
return {
showInfoSignatureOverlays: showInfoSignatureOverlays,
hideInfoSignatureOverlays: hideInfoSignatureOverlays,
updateZoomOverlay: updateZoomOverlay,
initMapDebugOverlays: initMapDebugOverlays
};

View File

@@ -9,8 +9,9 @@ define([
'app/util',
'bootbox',
'app/map/util',
'app/map/layout'
], ($, Init, Util, bootbox, MapUtil, Layout) => {
'app/map/layout',
'app/map/magnetizing'
], ($, Init, Util, bootbox, MapUtil, Layout, Magnetizer) => {
'use strict';
let config = {
@@ -702,6 +703,9 @@ define([
// remove connections do not fire a "connectionDetached" event
map.deleteConnectionsForElement(system, {fireEvent: false});
// unregister from "magnetizer"
Magnetizer.removeElement(system.data('mapid'), system[0]);
// destroy tooltip/popover
system.toggleSystemTooltip('destroy', {});
system.destroyPopover(true);

View File

@@ -41,32 +41,6 @@ define([
tableCellEllipsis100Class: 'pf-table-cell-100'
};
// map menu options
let mapOptions = {
mapMagnetizer: {
buttonId: Util.config.menuButtonMagnetizerId,
description: 'Magnetizer',
onEnable: 'initMagnetizer', // jQuery extension function
onDisable: 'destroyMagnetizer' // jQuery extension function
},
mapSnapToGrid : {
buttonId: Util.config.menuButtonGridId,
description: 'Grid snapping',
class: 'mapGridClass'
},
mapEndpoint : {
buttonId: Util.config.menuButtonEndpointId,
description: 'Endpoint overlay',
onEnable: 'showInfoSignatureOverlays', // jQuery extension function
onDisable: 'hideInfoSignatureOverlays' // jQuery extension function
},
mapCompact : {
buttonId: Util.config.menuButtonCompactId,
description: 'Compact system layout',
class: 'mapCompactClass'
}
};
// active jsPlumb instances currently running =====================================================================
let activeInstances = {};
@@ -518,6 +492,7 @@ define([
if(
connection &&
connectionData &&
connectionData.signatures // signature data is required...
){
let SystemSignatures = require('app/ui/module/system_signature');
@@ -556,7 +531,7 @@ define([
if(flattenSigTypeNames.hasOwnProperty(signatureData.typeId)){
let label = flattenSigTypeNames[signatureData.typeId];
// shorten label, just take the in game name
// shorten label, just take the ingame name
label = label.substr(0, label.indexOf(' '));
signatureTypeData[tmpSystemType].names.push(signatureData.name);
signatureTypeData[tmpSystemType].labels.push(label);
@@ -574,13 +549,13 @@ define([
* -> Coordinates are relative to the Endpoint (not the system!)
* -> jsPlumb specific format
* @param endpoint
* @param label
* @param labels
* @returns {number[]}
*/
let getEndpointOverlaySignatureLocation = (endpoint, label) => {
let chars = label.length ? label.length : 2;
let xLeft = chars === 2 ? -0.5 : chars <= 4 ? -1 : 3;
let xRight = chars === 2 ? +1.5 : chars <= 4 ? +2.20 : 3;
let getEndpointOverlaySignatureLocation = (endpoint, labels) => {
let count = labels.length;
let xLeft = count ? count === 1 ? -1.00 : 3 : -0.5;
let xRight = count ? count === 1 ? +2.20 : 3 : +1.5;
switch(endpoint.anchor.getCurrentFace()){
case 'top': return [0.5, -0.75];
@@ -593,24 +568,25 @@ define([
/**
* get overlay HTML for connection endpoints by Label array
* @param label
* @param labels
* @returns {string}
*/
let formatEndpointOverlaySignatureLabel = label => {
let formatEndpointOverlaySignatureLabel = labels => {
// default K162 in label array, or multiple labels
let colorClass = 'txt-color-grayLighter';
let label = labels.join(', ');
if(label.length > 0){
// check if multiple labels found => conflict
if( label.includes(', ') ){
colorClass = 'txt-color-orangeLight';
}else if( !label.includes('K162') ){
colorClass = 'txt-color-yellow';
}
}else{
if(labels.length === 0){
// endpoint not connected with a signature
label = '<i class="fas fa-question-circle"></i>';
colorClass = 'txt-color-red';
}else if(
labels.length === 1 &&
!labels.includes('K162')
){
colorClass = Init.wormholes[labels[0]].class;
}
return '<span class="txt-color ' + colorClass + '">' + label + '</span>';
};
@@ -639,62 +615,64 @@ define([
*/
let filterMapByScopes = (map, scopes) => {
if(map){
let mapElement = $(map.getContainer());
let allSystems = mapElement.getSystems();
let allConnections = map.getAllConnections();
map.batch(() => {
let mapElement = $(map.getContainer());
let allSystems = mapElement.getSystems();
let allConnections = map.getAllConnections();
if(scopes && scopes.length){
// filter connections -------------------------------------------------------------------------------------
let visibleSystems = [];
let visibleConnections = searchConnectionsByScopeAndType(map, scopes);
if(scopes && scopes.length){
// filter connections -------------------------------------------------------------------------------------
let visibleSystems = [];
let visibleConnections = searchConnectionsByScopeAndType(map, scopes);
for(let connection of allConnections){
if(visibleConnections.indexOf(connection) >= 0){
setConnectionVisible(connection, true);
// source/target system should always be visible -> even if filter scope not matches system type
if(visibleSystems.indexOf(connection.endpoints[0].element) < 0){
visibleSystems.push(connection.endpoints[0].element);
for(let connection of allConnections){
if(visibleConnections.indexOf(connection) >= 0){
setConnectionVisible(connection, true);
// source/target system should always be visible -> even if filter scope not matches system type
if(visibleSystems.indexOf(connection.endpoints[0].element) < 0){
visibleSystems.push(connection.endpoints[0].element);
}
if(visibleSystems.indexOf(connection.endpoints[1].element) < 0){
visibleSystems.push(connection.endpoints[1].element);
}
}else{
setConnectionVisible(connection, false);
}
if(visibleSystems.indexOf(connection.endpoints[1].element) < 0){
visibleSystems.push(connection.endpoints[1].element);
}
}else{
setConnectionVisible(connection, false);
}
}
// filter systems -----------------------------------------------------------------------------------------
let visibleTypeIds = [];
if(scopes.indexOf('wh') >= 0){
visibleTypeIds.push(1);
}
if(scopes.indexOf('abyssal') >= 0){
visibleTypeIds.push(4);
}
// filter systems -----------------------------------------------------------------------------------------
let visibleTypeIds = [];
if(scopes.indexOf('wh') >= 0){
visibleTypeIds.push(1);
}
if(scopes.indexOf('abyssal') >= 0){
visibleTypeIds.push(4);
}
for(let system of allSystems){
if(
visibleTypeIds.indexOf($(system).data('typeId')) >= 0 ||
visibleSystems.indexOf(system) >= 0
){
for(let system of allSystems){
if(
visibleTypeIds.indexOf($(system).data('typeId')) >= 0 ||
visibleSystems.indexOf(system) >= 0
){
setSystemVisible(system, map, true);
}else{
setSystemVisible(system, map, false);
}
}
MapOverlayUtil.getMapOverlay(mapElement, 'info').updateOverlayIcon('filter', 'show');
}else{
// clear filter
for(let system of allSystems){
setSystemVisible(system, map, true);
}else{
setSystemVisible(system, map, false);
}
}
for(let connection of allConnections){
setConnectionVisible(connection, true);
}
MapOverlayUtil.getMapOverlay(mapElement, 'info').updateOverlayIcon('filter', 'show');
}else{
// clear filter
for(let system of allSystems){
setSystemVisible(system, map, true);
MapOverlayUtil.getMapOverlay(mapElement, 'info').updateOverlayIcon('filter', 'hide');
}
for(let connection of allConnections){
setConnectionVisible(connection, true);
}
MapOverlayUtil.getMapOverlay(mapElement, 'info').updateOverlayIcon('filter', 'hide');
}
});
}
};
@@ -761,12 +739,11 @@ define([
'height': scrollableHeight ? scaledHeight + 'px' : (wrapperHeight) + 'px',
});
let mapWrapperElement = mapContainer.closest('.mCustomScrollbar');
if(scrollableWidth && scrollableHeight){
mapWrapperElement.mCustomScrollbar('update');
}else{
mapWrapperElement.mCustomScrollbar('scrollTo', '#pf-map-1', {
mapWrapperElement.mCustomScrollbar('scrollTo', '#' + mapContainer.attr('id'), {
scrollInertia: 0,
scrollEasing: 'linear',
timeout: 0,
@@ -844,40 +821,45 @@ define([
* with the addition of respecting active Arrow overlay direction
* @param action
* @param connection
* @param type
* @param types
* @param params
* @param doNotRepaint
*/
let changeConnectionType = (action, connection, type, params = {}, doNotRepaint = false) => {
// check for active Arrow overlay
let overlayArrow, overlayArrowParams;
if(
type !== 'info_signature' &&
connection.hasType('info_signature')
){
overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId);
if(overlayArrow){
overlayArrowParams = {
direction: overlayArrow.direction,
foldback: overlayArrow.foldback,
};
let changeConnectionTypes = (action, connection, types = [], params = [], doNotRepaint = false) => {
if(connection && types.length){
// check for active Arrow overlay
let overlayArrow, overlayArrowParams;
if(
!types.includes('info_signature') &&
connection.hasType('info_signature')
){
overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId);
if(overlayArrow){
overlayArrowParams = {
direction: overlayArrow.direction,
foldback: overlayArrow.foldback,
};
}
}
}
// add the new type
connection[action](type, params, doNotRepaint);
for(let i = 0; i < types.length; i++){
// change the new type
connection[action](types[i], typeof params[i] === 'object' ? params[i] : {}, doNotRepaint);
}
// change Arrow overlay data back to initial direction
if(
overlayArrow &&
(
overlayArrow.direction !== overlayArrowParams.direction ||
overlayArrow.foldback !== overlayArrowParams.foldback
)
){
overlayArrow.updateFrom(overlayArrowParams);
if(!doNotRepaint){
connection.repaint();
// change Arrow overlay data back to initial direction
if(
overlayArrow &&
(
overlayArrow.direction !== overlayArrowParams.direction ||
overlayArrow.foldback !== overlayArrowParams.foldback
)
){
overlayArrow.updateFrom(overlayArrowParams);
if(!doNotRepaint){
connection.repaint();
}
}
}
};
@@ -889,8 +871,12 @@ define([
* @param params
* @param doNotRepaint
*/
let addConnectionType = (connection, type, params = {}, doNotRepaint = false) => {
changeConnectionType('addType', connection, type, params, doNotRepaint);
let addConnectionType = (connection, type, params, doNotRepaint = false) => {
addConnectionTypes(connection, [type], typeof params === 'object' ? [params] : [], doNotRepaint);
};
let addConnectionTypes = (connection, types = [], params = [], doNotRepaint = false) => {
changeConnectionTypes('addType', connection, types, params, doNotRepaint);
};
/**
@@ -900,12 +886,16 @@ define([
* @param params
* @param doNotRepaint
*/
let removeConnectionType = (connection, type, params = {}, doNotRepaint = false) => {
changeConnectionType('removeType', connection, type, params, doNotRepaint);
let removeConnectionType = (connection, type, params, doNotRepaint = false) => {
removeConnectionTypes(connection, [type], typeof params === 'object' ? [params] : [], doNotRepaint);
};
let toggleConnectionType = (connection, type, params = {}, doNotRepaint = false) => {
changeConnectionType('toggleType', connection, type, params, doNotRepaint);
let removeConnectionTypes = (connection, types = [], params = [], doNotRepaint = false) => {
changeConnectionTypes('removeType', connection, types, params, doNotRepaint);
};
let toggleConnectionType = (connection, type, params, doNotRepaint = false) => {
changeConnectionTypes('toggleType', connection, [type], typeof params === 'object' ? [params] : [], doNotRepaint);
};
/**
@@ -1136,10 +1126,10 @@ define([
* @param types
* @returns {string[]}
*/
let getConnectionFakeClassesByTypes = (types) => {
let getConnectionFakeClassesByTypes = types => {
let connectionClasses = ['pf-fake-connection'];
for(let i = 0; i < types.length; i++){
connectionClasses.push(getConnectionInfo( types[i], 'cssClass'));
connectionClasses.push(getConnectionInfo(types[i], 'cssClass'));
}
return connectionClasses;
};
@@ -1147,9 +1137,9 @@ define([
/**
* get all direct connections between two given systems
* @param map
* @param {JQuery} systemA
* @param {JQuery} systemB
* @returns {Array}
* @param systemA
* @param systemB
* @returns {*[]}
*/
let checkForConnection = (map, systemA, systemB) => {
let connections = [];
@@ -1224,6 +1214,31 @@ define([
});
};
/**
* set/change connection jump mass of a wormhole
* @param connection
* @param mass
*/
let setConnectionJumpMassType = (connection, mass) => {
let allMassTypes = ['wh_jump_mass_s', 'wh_jump_mass_m', 'wh_jump_mass_l', 'wh_jump_mass_xl'];
let addMassType = [];
let removeMassTypes = [];
connection._jsPlumb.instance.batch(() => {
if(allMassTypes.includes(mass)){
if(connection.hasType(mass)){
removeMassTypes = allMassTypes;
}else{
addMassType = [mass];
removeMassTypes = allMassTypes.filter(e => e !== mass);
}
removeConnectionTypes(connection, removeMassTypes);
addConnectionTypes(connection, addMassType);
}
});
};
/**
* get some scope info for a given info string
* @param {string} info
@@ -1430,8 +1445,9 @@ define([
};
/**
* set default map Options (
* -> HINT: This function triggers Events! Promise is resolved before trigger completed
* Set default map options (from right menu)
* This function is called only ONCE per map after create!
* -> HINT: This function triggers events! Promise is resolved before trigger callback finishes
* @param mapElement
* @param mapConfig
* @returns {Promise<any>}
@@ -1464,8 +1480,10 @@ define([
// init endpoint overlay --------------------------------------------------------------------------
mapElement.triggerMenuEvent('MapOption', {
option: 'mapEndpoint',
toggle: false
option: 'mapSignatureOverlays',
toggle: false,
skipOnEnable: true, // skip callback -> Otherwise it would run 2 times on map create
skipOnDisable: true // skip callback -> Otherwise it would run 2 times on map create
});
resolve({
@@ -1494,30 +1512,61 @@ define([
/**
* scroll map to default (stored) x/y coordinates
* @param mapElement
* @param map
* @returns {Promise<any>}
*/
let scrollToDefaultPosition = (mapElement) => {
let scrollToDefaultPosition = map => {
let scrollToDefaultPositionExecutor = (resolve, reject) => {
let mapWrapper = mapElement.parents('.' + config.mapWrapperClass);
let scrollToDefaultPositionExecutor = resolve => {
let payload = {
action: 'scrollToDefaultPosition',
data: false
};
// auto scroll map to previous stored position
// no map scroll on zoomed maps -> scrollbar offset on zoomed maps does not work properly
// -> implementation would be difficult...
if(map.getZoom() === 1){
let mapElement = $(map.getContainer());
let promiseStore = getLocaleData('map', mapElement.data('id'));
promiseStore.then(data => {
if(data && data.scrollOffset){
let mapWrapper = mapElement.parents('.' + config.mapWrapperClass);
Scrollbar.scrollToPosition(mapWrapper, [data.scrollOffset.y, data.scrollOffset.x]);
}
resolve(payload);
});
}else{
resolve(payload);
}
};
return new Promise(scrollToDefaultPositionExecutor);
};
/**
* zoom map to default (stored) scale()
* @param map
* @returns {Promise<any>}
*/
let zoomToDefaultScale = map => {
let zoomToDefaultScaleExecutor = resolve => {
let mapElement = $(map.getContainer());
let promiseStore = getLocaleData('map', mapElement.data('id'));
promiseStore.then(data => {
// This code runs once the value has been loaded from offline storage
if(data && data.scrollOffset){
Scrollbar.scrollToPosition(mapWrapper, [data.scrollOffset.y, data.scrollOffset.x]);
if(data && data.mapZoom){
setZoom(map, data.mapZoom);
}
resolve({
action: 'scrollToDefaultPosition',
action: 'zoomToDefaultScale',
data: false
});
});
};
return new Promise(scrollToDefaultPositionExecutor);
return new Promise(zoomToDefaultScaleExecutor);
};
/**
@@ -1825,13 +1874,12 @@ define([
let title = tooltipData.name;
if(tooltipData.size){
title += '&nbsp;<kbd>' + tooltipData.size.label + '</kbd>';
}
if(tooltipData.security){
// K162 has no security
if(!tooltipData.class){
tooltipData.class = Util.getSecurityClassForSystem(tooltipData.security);
}
title += '<span class="pull-right ' + tooltipData.class +'">' + tooltipData.security + '</span>';
}
@@ -1961,7 +2009,6 @@ define([
return {
config: config,
mapOptions: mapOptions,
setMapInstance: setMapInstance,
getMapInstance: getMapInstance,
existsMapInstance: existsMapInstance,
@@ -1996,6 +2043,7 @@ define([
checkForConnection: checkForConnection,
getDefaultConnectionTypeByScope: getDefaultConnectionTypeByScope,
setConnectionWHStatus: setConnectionWHStatus,
setConnectionJumpMassType: setConnectionJumpMassType,
getScopeInfoForConnection: getScopeInfoForConnection,
getDataByConnections: getDataByConnections,
deleteConnections: deleteConnections,
@@ -2016,6 +2064,7 @@ define([
setMapDefaultOptions: setMapDefaultOptions,
getSystemPosition: getSystemPosition,
scrollToDefaultPosition: scrollToDefaultPosition,
zoomToDefaultScale: zoomToDefaultScale,
getSystemId: getSystemId,
checkRight: checkRight,
getMapDeeplinkUrl: getMapDeeplinkUrl

View File

@@ -121,6 +121,25 @@ define([
*/
let initData = () => {
/**
* add wormhole size data for each wormhole
* @param wormholes
* @returns {*}
*/
let addWormholeSizeData = wormholes => {
for(let [wormholeName, wormholeData] of Object.entries(wormholes)){
wormholeData.class = Util.getSecurityClassForSystem(wormholeData.security);
for(let [sizeName, sizeData] of Object.entries(Init.wormholeSizes)){
if(wormholeData.massIndividual >= sizeData.jumpMassMin){
wormholeData.size = sizeData;
break;
}
}
}
return wormholes;
};
let initDataExecutor = (resolve, reject) => {
$.getJSON(Init.path.initData).done(response => {
if( response.error.length > 0 ){
@@ -139,7 +158,7 @@ define([
Init.connectionScopes = response.connectionScopes;
Init.systemStatus = response.systemStatus;
Init.systemType = response.systemType;
Init.wormholes = response.wormholes;
Init.wormholes = addWormholeSizeData(response.wormholes);
Init.characterStatus = response.characterStatus;
Init.routes = response.routes;
Init.url = response.url;

View File

@@ -220,17 +220,7 @@ define([
}),
$('<a>', {
class: 'list-group-item list-group-item-info',
html: '&nbsp;&nbsp;Effect info'
}).prepend(
$('<i>',{
class: 'fas fa-crosshairs fa-fw'
})
).on('click', function(){
$(document).triggerMenuEvent('ShowSystemEffectInfo');
}),
$('<a>', {
class: 'list-group-item list-group-item-info',
html: '&nbsp;&nbsp;Jump info'
html: '&nbsp;&nbsp;Wormhole data'
}).prepend(
$('<i>',{
class: 'fas fa-space-shuttle fa-fw'
@@ -238,6 +228,16 @@ define([
).on('click', function(){
$(document).triggerMenuEvent('ShowJumpInfo');
}),
$('<a>', {
class: 'list-group-item list-group-item-info',
html: '&nbsp;&nbsp;Wormhole effects'
}).prepend(
$('<i>',{
class: 'fas fa-crosshairs fa-fw'
})
).on('click', function(){
$(document).triggerMenuEvent('ShowSystemEffectInfo');
}),
getMenuHeadline('Settings'),
$('<a>', {
class: 'list-group-item',
@@ -379,7 +379,7 @@ define([
})
).on('click', function(){
Util.getMapModule().getActiveMap().triggerMenuEvent('MapOption', {
option: 'mapEndpoint',
option: 'mapSignatureOverlays',
toggle: true
});
}),

View File

@@ -26,13 +26,6 @@ define([
let data = {
config: config,
wormholes: Object.keys(Init.wormholes).map(function(k){ return Init.wormholes[k]; }), // convert Json to array
securityClass: function(){
return function(value, render){
return this.Util.getSecurityClassForSystem( render(value) );
}.bind(this);
}.bind({
Util: Util
}),
massValue: function(){
return function(value, render){
let mass = render(value);

View File

@@ -382,9 +382,7 @@ define([
let statics = [];
for(let wormholeName of cellData){
let wormholeData = Object.assign({}, Init.wormholes[wormholeName]);
let security = wormholeData.security;
let secClass = Util.getSecurityClassForSystem(security);
statics.push('<span class="' + secClass + '">' + security + '</span>');
statics.push('<span class="' + wormholeData.class + '">' + wormholeData.security + '</span>');
}
return statics.join('&nbsp;&nbsp;');
}
@@ -596,7 +594,7 @@ define([
display: (cellData, type, rowData, meta) => {
let connectionClasses = MapUtil.getConnectionFakeClassesByTypes(cellData);
connectionClasses = connectionClasses.join(' ');
return '<div class="pf-fake-connection ' + connectionClasses + '"></div>';
return '<div class="' + connectionClasses + '"></div>';
}
}
},{

View File

@@ -57,7 +57,7 @@ define([
for(let [areaId, areaData] of Object.entries(effectData)){
let systemType = 'C' + areaId;
let securityClass = Util.getSecurityClassForSystem( systemType );
let securityClass = Util.getSecurityClassForSystem(systemType);
if(areaId === '1'){
rows.push( $('<tr>') );

View File

@@ -166,7 +166,7 @@ define([
if(type.includes('wh_critical')){
styleClass.push('pf-wh-critical');
}
if(type.includes('frigate')){
if(type.includes('wh_jump_mass_s')){
styleClass.push('pf-wh-frig');
}
}

View File

@@ -306,22 +306,22 @@ define([
let connection = $().getConnectionById(data.mapId, data.connectionId);
let signatureTypeNames = MapUtil.getConnectionDataFromSignatures(connection, connectionData);
let sourceLabel = signatureTypeNames.source.labels;
let targetLabel = signatureTypeNames.target.labels;
sourceLabelElement.html(MapUtil.formatEndpointOverlaySignatureLabel(sourceLabel.join(', ')));
targetLabelElement.html(MapUtil.formatEndpointOverlaySignatureLabel(targetLabel.join(', ')));
let sourceLabels = signatureTypeNames.source.labels;
let targetLabels = signatureTypeNames.target.labels;
sourceLabelElement.html(MapUtil.formatEndpointOverlaySignatureLabel(sourceLabels));
targetLabelElement.html(MapUtil.formatEndpointOverlaySignatureLabel(targetLabels));
// remove K162
sourceLabel = sourceLabel.diff(['K162']);
targetLabel = targetLabel.diff(['K162']);
sourceLabels = sourceLabels.diff(['K162']);
targetLabels = targetLabels.diff(['K162']);
// get static wormhole data by endpoint Labels
let wormholeName = '';
let wormholeData = null;
if(sourceLabel.length === 1 && targetLabel.length === 0){
wormholeName = sourceLabel[0];
}else if(sourceLabel.length === 0 && targetLabel.length === 1){
wormholeName = targetLabel[0];
if(sourceLabels.length === 1 && targetLabels.length === 0){
wormholeName = sourceLabels[0];
}else if(sourceLabels.length === 0 && targetLabels.length === 1){
wormholeName = targetLabels[0];
}
if(
@@ -329,7 +329,6 @@ define([
Init.wormholes.hasOwnProperty(wormholeName)
){
wormholeData = Object.assign({}, Init.wormholes[wormholeName]);
wormholeData.class = Util.getSecurityClassForSystem(wormholeData.security);
// init wormhole tooltip ----------------------------------------------
let massTotalTooltipCell = tableElement.find('.' + config.connectionInfoTableCellMassTotalTooltipClass);

View File

@@ -143,7 +143,6 @@ define([
){
for(let wormholeName of systemData.statics){
let wormholeData = Object.assign({}, Init.wormholes[wormholeName]);
wormholeData.class = Util.getSecurityClassForSystem(wormholeData.security);
staticsData.push(wormholeData);
}
}

View File

@@ -970,6 +970,16 @@ define([
return this.filter(function(i){return a.indexOf(i) < 0;});
};
/**
* compares two arrays if all elements in a are also in b
* element order is ignored
* @param a
* @returns {boolean}
*/
Array.prototype.equalValues = function(a){
return this.diff(a).concat(a.diff(this)).length === 0;
};
/**
* sort array of objects by property name
* @param p

View File

@@ -1,350 +0,0 @@
;(function() {
var root = this;
var Farahey;
if (typeof exports !== 'undefined') {
Farahey = exports;
} else {
Farahey = root.Farahey = {};
}
var findInsertionPoint = function(sortedArr, val, comparator) {
var low = 0, high = sortedArr.length;
var mid = -1, c = 0;
while(low < high) {
mid = parseInt((low + high)/2);
c = comparator(sortedArr[mid], val);
if(c < 0) {
low = mid + 1;
}else if(c > 0) {
high = mid;
}else {
return mid;
}
}
return low;
},
geomSupport = typeof jsPlumbGeom !== "undefined" ? jsPlumbGeom : Biltong,
insertSorted = function(array, value, comparator) {
var ip = findInsertionPoint(array, value, comparator);
array.splice(ip, 0, value);
},
distanceFromOriginComparator = function(r1, r2, origin) {
var d1 = geomSupport.lineLength(origin, [ r1.x + (r1.w / 2), r1.y + (r1.h / 2)]),
d2 = geomSupport.lineLength(origin, [ r2.x + (r2.w / 2), r2.y + (r2.h / 2)]);
return d1 < d2 ? -1 : d1 == d2 ? 0 : 1;
},
EntryComparator = function(origin, getSize) {
var _origin = origin,
_cache = {},
_get = function(entry) {
if (!_cache[entry[1]]) {
var s = getSize(entry[2]);
_cache[entry[1]] = {
l:entry[0][0],
t:entry[0][1],
w:s[0],
h:s[1],
center:[entry[0][0] + (s[0] / 2), entry[0][1] + (s[1] / 2) ]
};
}
return _cache[entry[1]];
}
this.setOrigin = function(o) {
_origin = o;
_cache = {};
};
this.compare = function(e1, e2) {
var d1 = geomSupport.lineLength(_origin, _get(e1).center),
d2 = geomSupport.lineLength(_origin, _get(e2).center);
return d1 < d2 ? -1 : d1 == d2 ? 0 : 1;
};
};
var _isOnEdge = function(r, axis, dim, v) { return (r[axis] <= v && v <= r[axis] + r[dim]); },
_xAdj = [ function(r1, r2) { return r1.x + r1.w - r2.x; }, function(r1, r2) { return r1.x - (r2.x + r2.w); } ],
_yAdj = [ function(r1, r2) { return r1.y + r1.h - r2.y; }, function(r1, r2) { return r1.y - (r2.y + r2.h); } ],
_adj = [ null, [ _xAdj[0], _yAdj[1] ], [ _xAdj[0], _yAdj[0] ], [ _xAdj[1], _yAdj[0] ], [ _xAdj[1], _yAdj[1] ] ],
_genAdj = function(r1, r2, m, b, s) {
if (isNaN(m)) m = 0;
var y = r2.y + r2.h,
x = (m == Infinity || m == -Infinity) ? r2.x + (r2.w / 2) : (y - b) / m,
theta = Math.atan(m);
if (_isOnEdge(r2, "x", "w", x)) {
var rise = _adj[s][1](r1, r2),
hyp = rise / Math.sin(theta),
run = hyp * Math.cos(theta);
return { left:run, top:rise };
}
else {
var run = _adj[s][0](r1, r2),
hyp = run / Math.cos(theta),
rise = hyp * Math.sin(theta);
return { left:run, top:rise };
}
},
/*
* Calculates how far to move r2 from r1 so that it no longer overlaps.
* if origin is supplied, then it means we want r2 to move along a vector joining r2's center to that point.
* otherwise we want it to move along a vector joining the two rectangle centers.
*/
_calculateSpacingAdjustment = Farahey.calculateSpacingAdjustment = function(r1, r2) {
var c1 = r1.center || [ r1.x + (r1.w / 2), r1.y + (r1.h / 2) ],
c2 = r2.center || [ r2.x + (r2.w / 2), r2.y + (r2.h / 2) ],
m = geomSupport.gradient(c1, c2),
s = geomSupport.quadrant(c1, c2),
b = (m == Infinity || m == -Infinity || isNaN(m)) ? 0 : c1[1] - (m * c1[0]);
return _genAdj(r1, r2, m, b, s);
},
// calculate a padded rectangle for the given element with offset & size, and desired padding.
_paddedRectangle = Farahey.paddedRectangle = function(o, s, p) {
return { x:o[0] - p[0], y: o[1] - p[1], w:s[0] + (2 * p[0]), h:s[1] + (2 * p[1]) };
},
_magnetize = function(positionArray, positions, sizes, padding,
constrain, origin, filter,
updateOnStep, stepInterval, stepCallback)
{
origin = origin || [0,0];
stepCallback = stepCallback || function() { };
var focus = _paddedRectangle(origin, [1,1], padding),
iterations = 100, iteration = 1, uncleanRun = true, adjustBy, constrainedAdjustment,
_movedElements = {},
_move = function(id, o, x, y) {
_movedElements[id] = true;
o[0] += x;
o[1] += y;
},
step = function() {
for (var i = 0; i < positionArray.length; i++) {
var o1 = positions[positionArray[i][1]],
oid = positionArray[i][1],
a1 = positionArray[i][2], // angle to node from magnet origin
s1 = sizes[positionArray[i][1]],
// create a rectangle for first element: this encompasses the element and padding on each
//side
r1 = _paddedRectangle(o1, s1, padding);
if (filter(positionArray[i][1]) && geomSupport.intersects(focus, r1)) {
adjustBy = _calculateSpacingAdjustment(focus, r1);
constrainedAdjustment = constrain(positionArray[i][1], o1, adjustBy);
_move(oid, o1, constrainedAdjustment.left, constrainedAdjustment.top);
}
// now move others to account for this one, if necessary.
// reset rectangle for node
r1 = _paddedRectangle(o1, s1, padding);
for (var j = 0; j < positionArray.length; j++) {
if (i != j) {
var o2 = positions[positionArray[j][1]],
a2 = positionArray[j][2], // angle to node from magnet origin
s2 = sizes[positionArray[j][1]],
// create a rectangle for the second element, again by putting padding of the desired
// amount around the bounds of the element.
r2 = _paddedRectangle(o2, s2, padding);
// if the two rectangles intersect then figure out how much to move the second one by.
if (geomSupport.intersects(r1, r2)) {
// TODO in 0.3, instead of moving neither, the other node should move.
if (filter(positionArray[j][1])) {
uncleanRun = true;
adjustBy = _calculateSpacingAdjustment(r1, r2),
constrainedAdjustment = constrain(positionArray[j][1], o2, adjustBy);
_move(positionArray[j][1], o2, constrainedAdjustment.left, constrainedAdjustment.top);
}
}
}
}
}
if (updateOnStep)
stepCallback();
if (uncleanRun && iteration < iterations) {
uncleanRun = false;
iteration++;
if (updateOnStep) {
window.setTimeout(step, stepInterval);
}
else
step();
}
};
step();
return _movedElements;
};
/**
* @name Magnetizer
* @classdesc Applies repulsive magnetism to a set of elements relative to a given point, with a specified
* amount of padding around the point.
*/
/**
* @name Magnetizer#constructor
* @function
* @param {Selector|Element} [container] Element that contains the elements to magnetize. Only required if you intend to use the `executeAtEvent` method.
* @param {Function} [getContainerPosition] Function that returns the position of the container (as an object of the form `{left:.., top:..}`) when requested. Only required if you intend to use the `executeAtEvent` method.
* @param {Function} getPosition A function that takes an element id and returns its position. It does not matter to which element this position is computed as long as you remain consistent with this method, `setPosition` and the `origin` property.
* @param {Function} setPosition A function that takes an element id and position, and sets it. See note about offset parent above.
* @param {Function} getSize A function that takes an element id and returns its size, in pixels.
* @param {Integer[]} [padding] Optional padding for x and y directions. Defaults to 20 pixels in each direction.
* @param {Function} [constrain] Optional function that takes an id and a proposed amount of movement in each axis, and returns the allowed amount of movement in each axis. You can use this to constrain your elements to a grid, for instance, or a path, etc.
* @param {Integer[]} [origin] The origin of magnetization, in pixels. Defaults to 0,0. You can also supply this to the `execute` call.
* @param {Selector|String[]|Element[]} elements List of elements on which to operate.
* @param {Boolean} [executeNow=false] Whether or not to execute the routine immediately.
* @param {Function} [filter] Optional function that takes an element id and returns whether or not that element can be moved.
* @param {Boolean} [orderByDistanceFromOrigin=false] Whether or not to sort elements first by distance from origin. Can have better results but takes more time.
*/
root.Magnetizer = function(params) {
var getPosition = params.getPosition,
getSize = params.getSize,
getId = params.getId,
setPosition = params.setPosition,
padding = params.padding || [20, 20],
// expects a { left:.., top:... } object. returns how far it can actually go.
constrain = params.constrain || function(id, current, delta) { return delta; },
positionArray = [],
positions = {},
sizes = {},
elements = params.elements || [],
origin = params.origin || [0,0],
executeNow = params.executeNow,
minx, miny, maxx, maxy,
getOrigin = this.getOrigin = function() { return origin; },
filter = params.filter || function(_) { return true; },
orderByDistanceFromOrigin = params.orderByDistanceFromOrigin,
comparator = new EntryComparator(origin, getSize),
updateOnStep = params.updateOnStep,
stepInterval = params.stepInterval || 350,
originDebugMarker,
debug = params.debug,
createOriginDebugger = function() {
var d = document.createElement("div");
d.style.position = "absolute";
d.style.width = "10px";
d.style.height = "10px";
d.style.backgroundColor = "red";
document.body.appendChild(d);
originDebugMarker = d;
},
_addToPositionArray = function(p) {
if (!orderByDistanceFromOrigin || positionArray.length == 0)
positionArray.push(p);
else {
insertSorted(positionArray, p, comparator.compare);
}
},
_updatePositions = function() {
comparator.setOrigin(origin);
positionArray = []; positions = {}; sizes = {};
minx = miny = Infinity;
maxx = maxy = -Infinity;
for (var i = 0; i < elements.length; i++) {
var p = getPosition(elements[i]),
s = getSize(elements[i]),
id = getId(elements[i]);
positions[id] = [p.left, p.top];
_addToPositionArray([ [p.left, p.top], id, elements[i]]);
sizes[id] = s;
minx = Math.min(minx, p.left);
miny = Math.min(miny, p.top);
maxx = Math.max(maxx, p.left + s[0]);
maxy = Math.max(maxy, p.top + s[1]);
}
},
_run = function() {
if (elements.length > 1) {
var _movedElements = _magnetize(positionArray, positions, sizes, padding, constrain, origin, filter, updateOnStep, stepInterval, _positionElements);
_positionElements(_movedElements);
}
},
_positionElements = function(_movedElements) {
for (var i = 0; i < elements.length; i++) {
var id = getId(elements[i]);
if (_movedElements[id])
setPosition(elements[i], { left:positions[id][0], top:positions[id][1] });
}
},
setOrigin = function(o) {
if (o != null) {
origin = o;
comparator.setOrigin(o);
}
};
/**
* @name Magnetizer#execute
* @function
* @desc Runs the magnetize routine.
* @param {Integer[]} [o] Optional origin to use. You may have set this in the constructor and do not wish to supply it, or you may be happy with the default of [0,0].
*/
this.execute = function(o) {
setOrigin(o);
_updatePositions();
_run();
};
/**
* @name Magnetizer#executeAtCenter
* @function
* @desc Computes the center of all the nodes and then uses that as the magnetization origin when it runs the routine.
*/
this.executeAtCenter = function() {
_updatePositions();
setOrigin([
(minx + maxx) / 2,
(miny + maxy) / 2
]);
_run();
};
/**
* @name Magnetizer#executeAtEvent
* @function
* @desc Runs the magnetize routine using the location of the given event as the origin. To use this
* method you need to have provided a `container`, and a `getContainerPosition` function to the
* constructor.
* @param {Event} e Event to get origin location from.
*/
this.executeAtEvent = function(e) {
var c = params.container,
o = params.getContainerPosition(c),
x = e.pageX - o.left + c[0].scrollLeft,
y = e.pageY - o.top + c[0].scrollTop;
if (debug) {
originDebugMarker.style.left = e.pageX + "px";
originDebugMarker.style.top = e.pageY + "px";
}
this.execute([x,y]);
};
/**
* @name Magnetize#setElements
* @function
* @desc Sets the current list of elements.
* @param {Object[]} _els List of elements, in whatever format the magnetizer is setup to use.
*/
this.setElements = function(_els) {
elements = _els;
};
if (debug)
createOriginDebugger();
if (executeNow) this.execute();
};
}).call(this);

518
js/lib/farahey.js Normal file
View File

@@ -0,0 +1,518 @@
;(function() {
"use strict";
var root = this;
var Farahey = root.Farahey = {};
if (typeof exports !== 'undefined') {
exports.Farahey = Farahey;
}
var findInsertionPoint = function(sortedArr, val, comparator) {
var low = 0, high = sortedArr.length;
var mid = -1, c = 0;
while(low < high) {
mid = parseInt((low + high)/2);
c = comparator(sortedArr[mid], val);
if(c < 0) {
low = mid + 1;
}else if(c > 0) {
high = mid;
}else {
return mid;
}
}
return low;
},
geomSupport = root.Biltong,
insertSorted = function(array, value, comparator) {
var ip = findInsertionPoint(array, value, comparator);
array.splice(ip, 0, value);
},
EntryComparator = function(origin, getSize) {
var _origin = origin,
_cache = {},
_get = function(entry) {
if (!_cache[entry[1]]) {
var s = getSize(entry[2]);
_cache[entry[1]] = {
l:entry[0][0],
t:entry[0][1],
w:s[0],
h:s[1],
center:[entry[0][0] + (s[0] / 2), entry[0][1] + (s[1] / 2) ]
};
}
return _cache[entry[1]];
};
this.setOrigin = function(o) {
_origin = o;
_cache = {};
};
this.compare = function(e1, e2) {
var d1 = geomSupport.lineLength(_origin, _get(e1).center),
d2 = geomSupport.lineLength(_origin, _get(e2).center);
return d1 < d2 ? -1 : d1 == d2 ? 0 : 1;
};
};
var _isOnEdge = function(r, axis, dim, v) { return (r[axis] <= v && v <= r[axis] + r[dim]); },
_xAdj = [ function(r1, r2) { return r1.x + r1.w - r2.x; }, function(r1, r2) { return r1.x - (r2.x + r2.w); } ],
_yAdj = [ function(r1, r2) { return r1.y + r1.h - r2.y; }, function(r1, r2) { return r1.y - (r2.y + r2.h); } ],
_adj = [ null, [ _xAdj[0], _yAdj[1] ], [ _xAdj[0], _yAdj[0] ], [ _xAdj[1], _yAdj[0] ], [ _xAdj[1], _yAdj[1] ] ],
_genAdj = function(r1, r2, m, b, s) {
if (isNaN(m)) m = 0;
var y = r2.y + r2.h,
x = (m == Infinity || m == -Infinity) ? r2.x + (r2.w / 2) : (y - b) / m,
theta = Math.atan(m),
rise, hyp, run;
if (_isOnEdge(r2, "x", "w", x)) {
rise = _adj[s][1](r1, r2);
hyp = rise / Math.sin(theta);
run = hyp * Math.cos(theta);
return { left:run, top:rise };
}
else {
run = _adj[s][0](r1, r2);
hyp = run / Math.cos(theta);
rise = hyp * Math.sin(theta);
return { left:run, top:rise };
}
},
/*
* Calculates how far to move r2 from r1 so that it no longer overlaps.
* if origin is supplied, then it means we want r2 to move along a vector joining r2's center to that point.
* otherwise we want it to move along a vector joining the two rectangle centers.
*/
_calculateSpacingAdjustment = Farahey.calculateSpacingAdjustment = function(r1, r2) {
var c1 = r1.center || [ r1.x + (r1.w / 2), r1.y + (r1.h / 2) ],
c2 = r2.center || [ r2.x + (r2.w / 2), r2.y + (r2.h / 2) ],
m = geomSupport.gradient(c1, c2),
s = geomSupport.quadrant(c1, c2),
b = (m == Infinity || m == -Infinity || isNaN(m)) ? 0 : c1[1] - (m * c1[0]);
return _genAdj(r1, r2, m, b, s);
},
// calculate a padded rectangle for the given element with offset & size, and desired padding.
_paddedRectangle = Farahey.paddedRectangle = function(o, s, p) {
return { x:o[0] - p[0], y: o[1] - p[1], w:s[0] + (2 * p[0]), h:s[1] + (2 * p[1]) };
},
_magnetize = function(positionArray, positions, sizes, padding,
constrain, origin, filter,
updateOnStep, stepInterval, stepCallback, iterations,
exclude, excludeFocus)
{
origin = origin || [0,0];
stepCallback = stepCallback || function() { };
iterations = iterations || 2;
var focus = _paddedRectangle(origin, [1,1], padding),
iteration = 1, uncleanRun = true, adjustBy, constrainedAdjustment,
_movedElements = {},
_move = function(id, o, x, y) {
_movedElements[id] = true;
o[0] += x;
o[1] += y;
},
step = function() {
for (var i = 0; i < positionArray.length; i++) {
if (exclude(positionArray[i][1], positionArray[i][2])) {
continue;
}
var o1 = positions[positionArray[i][1]],
oid = positionArray[i][1],
a1 = positionArray[i][2], // angle to node from magnet origin
s1 = sizes[positionArray[i][1]],
// create a rectangle for first element: this encompasses the element and padding on each
//side
r1 = _paddedRectangle(o1, s1, padding);
if (!excludeFocus && filter(positionArray[i][1], positionArray[i][2]) && geomSupport.intersects(focus, r1)) {
adjustBy = _calculateSpacingAdjustment(focus, r1);
constrainedAdjustment = constrain(positionArray[i][1], o1, adjustBy);
_move(oid, o1, constrainedAdjustment.left, constrainedAdjustment.top);
}
// now move others to account for this one, if necessary.
// reset rectangle for node
r1 = _paddedRectangle(o1, s1, padding);
for (var j = 0; j < positionArray.length; j++) {
if (i != j) {
if (exclude(positionArray[j][1], positionArray[j][2])) {
continue;
}
if (filter(positionArray[j][1], positionArray[j][2])) {
var o2 = positions[positionArray[j][1]],
s2 = sizes[positionArray[j][1]],
// create a rectangle for the second element, again by putting padding of the desired
// amount around the bounds of the element.
r2 = _paddedRectangle(o2, s2, padding);
// if the two rectangles intersect then figure out how much to move the second one by.
if (geomSupport.intersects(r1, r2)) {
// TODO (?), instead of moving neither, the other node should move.
uncleanRun = true;
adjustBy = _calculateSpacingAdjustment(r1, r2);
constrainedAdjustment = constrain(positionArray[j][1], o2, adjustBy);
_move(positionArray[j][1], o2, constrainedAdjustment.left, constrainedAdjustment.top);
}
}
}
}
}
if (updateOnStep)
stepCallback();
if (uncleanRun && iteration < iterations) {
uncleanRun = false;
iteration++;
if (updateOnStep) {
window.setTimeout(step, stepInterval);
}
else
step();
}
};
step();
return _movedElements;
};
var _convertElements = function(l) {
if (l == null) return null;
else if (Object.prototype.toString.call(l) === "[object Array]") {
var a = [];
a.push.apply(a, l);
return a;
}
else {
var o = [];
for (var i in l) o.push(l[i]);
}
return o;
};
/**
* Applies repulsive magnetism to a set of elements relative to a given point, with a specified
* amount of padding around the point.
* @class FaraheyInstance
* @constructor
* @param {Object} params Constructor parameters.
* @param {Selector|Element} [params.container] Element that contains the elements to magnetize. Only required if you intend to use the `executeAtEvent` method.
* @param {Function} [params.getContainerPosition] Function that returns the position of the container (as an object of the form `{left:.., top:..}`) when requested. Only required if you intend to use the `executeAtEvent` method.
* @param {Function} params.getPosition A function that takes an element and returns its position. It does not matter to which element this position is computed as long as you remain consistent with this method, `setPosition` and the `origin` property.
* @param {Function} params.setPosition A function that takes an element and position, and sets it. See note about offset parent above.
* @param {Function} params.getSize A function that takes an element and returns its size, in pixels.
* @param {Number[]} [params.padding] Optional padding for x and y directions. Defaults to 20 pixels in each direction.
* @param {Function} [params.constrain] Optional function that takes an id and a proposed amount of movement in each axis, and returns the allowed amount of movement in each axis. You can use this to constrain your elements to a grid, for instance, or a path, etc.
* @param {Number[]} [params.origin] The origin of magnetization, in pixels. Defaults to 0,0. You can also supply this to the `execute` call.
* @param {Selector|String[]|Element[]} params.elements List, or object hash, of elements on which to operate.
* @param {Boolean} [params.executeNow=false] Whether or not to execute the routine immediately.
* @param {Function} [params.filter] Optional function that takes an element id and returns whether or not that element can be moved.
* @param {Boolean} [params.orderByDistanceFromOrigin=false] Whether or not to sort elements first by distance from origin. Can have better results but takes more time.
*/
var FaraheyInstance = function(params) {
var getPosition = params.getPosition,
getSize = params.getSize,
getId = params.getId,
setPosition = params.setPosition,
padding = params.padding || [20, 20],
// expects a { left:.., top:... } object. returns how far it can actually go.
constrain = params.constrain || function(id, current, delta) { return delta; },
positionArray = [],
positions = {},
sizes = {},
elements = _convertElements(params.elements || []),
origin = params.origin || [0,0],
executeNow = params.executeNow,
//minx, miny, maxx, maxy,
getOrigin = this.getOrigin = function() { return origin; },
filter = params.filter || function(_) { return true; },
exclude = params.exclude || function(_) { return false;},
orderByDistanceFromOrigin = params.orderByDistanceFromOrigin,
comparator = new EntryComparator(origin, getSize),
updateOnStep = params.updateOnStep,
stepInterval = params.stepInterval || 350,
originDebugMarker,
debug = params.debug,
createOriginDebugger = function() {
var d = document.createElement("div");
d.style.position = "absolute";
d.style.width = "10px";
d.style.height = "10px";
d.style.backgroundColor = "red";
document.body.appendChild(d);
originDebugMarker = d;
},
_addToPositionArray = function(p) {
if (!orderByDistanceFromOrigin || positionArray.length == 0)
positionArray.push(p);
else {
insertSorted(positionArray, p, comparator.compare);
}
},
_computeExtents = function(els) {
var minx, miny, maxx, maxy;
minx = miny = Infinity;
maxx = maxy = -Infinity;
for (var i = 0; i < els.length; i++) {
var p = getPosition(els[i]),
s = getSize(els[i]),
id = getId(els[i]);
positions[id] = [p.left, p.top];
_addToPositionArray([ [p.left, p.top], id, els[i]]);
sizes[id] = s;
minx = Math.min(minx, p.left);
miny = Math.min(miny, p.top);
maxx = Math.max(maxx, p.left + s[0]);
maxy = Math.max(maxy, p.top + s[1]);
}
return [ minx, maxx, miny, maxy ];
},
_updatePositions = function() {
comparator.setOrigin(origin);
positionArray = []; positions = {}; sizes = {};
return _computeExtents(elements);
},
_run = function(options) {
if (elements.length > 1) {
options = options || {};
var f = options.filter || filter;
var p = options.padding || padding;
var i = options.iterations;
var e = options.exclude || exclude;
var ef = options.excludeFocus;
var _movedElements = _magnetize(positionArray, positions, sizes, p, constrain, origin, f, updateOnStep, stepInterval, _positionElements, i, e, ef);
_positionElements(_movedElements);
}
},
_positionElements = function(_movedElements) {
for (var i = 0; i < elements.length; i++) {
var id = getId(elements[i]);
if (_movedElements[id])
setPosition(elements[i], { left:positions[id][0], top:positions[id][1] });
}
},
setOrigin = function(o) {
if (o != null) {
origin = o;
comparator.setOrigin(o);
}
};
/**
* Runs the magnetize routine.
* @method execute
* @param {Number[]} [o] Optional origin to use. You may have set this in the constructor and do not wish to supply it, or you may be happy with the default of [0,0].
* @param {Function} [options] Options to override defaults.
* @param {Function} [options.filter] Optional function to indicate whether a given element may be moved or not. Returning boolean false indicates it may not.
* @param {Number[]} [options.padding] Optional [x,y] padding values for elements.
* @param {Number} [options.iterations] Optional max number of iterations to run. The greater this number, the more comprehensive the magnetisation,
* but the slower it runs. The default is 2.
* @param {Function} [options.exclude] Optional function to return whether or not a given element should be completely excluded from the magnetisation: it neither
* moves, nor has any bearing on the movement of other elements.
* @param {Boolean} [options.excludeFocus=false] If true, do not pad any elements around the focus point.
*/
this.execute = function(o, options) {
setOrigin(o);
_updatePositions();
_run(options);
};
/**
* Computes the center of all the nodes and then uses that as the magnetization origin when it runs the routine.
* @method executeAtCenter
* @param {Function} [options] Options to override defaults.
* @param {Function} [options.filter] Optional function to indicate whether a given element may be moved or not. Returning boolean false indicates it may not.
* @param {Number[]} [options.padding] Optional [x,y] padding values for elements.
* @param {Number} [options.iterations] Optional max number of iterations to run. The greater this number, the more comprehensive the magnetisation,
* but the slower it runs. The default is 2.
* @param {Function} [options.exclude] Optional function to return whether or not a given element should be completely excluded from the magnetisation: it neither
* moves, nor has any bearing on the movement of other elements.
* @param {Boolean} [options.excludeFocus=false] If true, do not pad any elements around the focus point.
*/
this.executeAtCenter = function(options) {
var extents = _updatePositions();
setOrigin([
(extents[0] + extents[1]) / 2,
(extents[2] + extents[3]) / 2
]);
_run(options);
};
/**
* Runs the magnetize routine using the location of the given event as the origin. To use this
* method you need to have provided a `container`, and a `getContainerPosition` function to the
* constructor.
* @method executeAtEvent
* @param {Event} e Event to get origin location from.
* @param {Function} [options] Options to override defaults.
* @param {Function} [options.filter] Optional function to indicate whether a given element may be moved or not. Returning boolean false indicates it may not.
* @param {Number[]} [options.padding] Optional [x,y] padding values for elements.
* @param {Number} [options.iterations] Optional max number of iterations to run. The greater this number, the more comprehensive the magnetisation,
* but the slower it runs. The default is 2.
* @param {Function} [options.exclude] Optional function to return whether or not a given element should be completely excluded from the magnetisation: it neither
* moves, nor has any bearing on the movement of other elements.
* @param {Boolean} [options.excludeFocus=false] If true, do not pad any elements around the focus point.
*/
this.executeAtEvent = function(e, options) {
var c = params.container,
o = params.getContainerPosition(c),
x = e.pageX - o.left + c.scrollLeft,
y = e.pageY - o.top + c.scrollTop;
if (debug) {
originDebugMarker.style.left = e.pageX + "px";
originDebugMarker.style.top = e.pageY + "px";
}
this.execute([x,y], options);
};
/**
* Sets the current set of elements on which to operate.
* @method setElements
* @param {Object[]|Object} _els List, or object hash, of elements, in whatever format the Magnetizer is setup to use. If you supply an object hash then a list is generated from the hash's values (the keys are ignored).
*/
this.setElements = function(_els) {
elements = _convertElements(_els);
return this;
};
/**
* Adds the given element to the set of elements on which to operate.
* @method addElement
* @param el {Object} Element to add.
* @param {Boolean} [doNotTestForDuplicates=false] If true, we skip the check for duplicates. This makes
* for a much faster call when there are lots of elements, just use it with care.
*/
this.addElement = function(el, doNotTestForDuplicates) {
if (el != null && (doNotTestForDuplicates || elements.indexOf(el) === -1)) {
elements.push(el);
}
return this;
};
/**
* Adds the given elements to the set of elements on which to operate.
* @method addElements
* @param els {Object[]} Elements to add.
* @param {Boolean} [doNotTestForDuplicates=false] If true, we skip the check for duplicates. This makes
* for a much faster call when there are lots of elements, just use it with care.
*/
this.addElements = function(els, doNotTestForDuplicates) {
if (doNotTestForDuplicates) {
Array.prototype.push.apply(elements, els);
}
else {
for (var i = 0; i < els.length; i++) {
this.addElement(els[i]);
}
}
return this;
};
/**
* Gets the list of elements currently being managed.
* @method getElements
*/
this.getElements = function() {
return elements;
};
/**
* Removes the given element from the set of elements on which to operate.
* @method removeElement
* @param el {Object} Element to remove.
*/
this.removeElement = function(el) {
var idx = -1;
for (var i = 0; i < elements.length; i++) {
if (elements[i] == el) {
idx = i; break;
}
}
if (idx != -1) elements.splice(idx, 1);
return this;
};
/**
* Sets the padding to insert between magnetized elements.
* @method setPadding
* @param {Number[]} p Array of padding for each axis.
*/
this.setPadding = function(p) {
padding = p;
};
/**
* Sets the function used to constrain the movement of some element that the magnetizer wishes to relocate.
* The function is given an element ID and an array of [x,y] values, where each value indicates the proposed amount
* of movement in the given axis. The function is expected to return an array of [x,y] that indicates the allowed
* amount of movement in each axis.
* @method setConstrain
* @param {Function} c
*/
this.setConstrain = function(c) {
constrain = c;
};
/**
* Sets the function used to determine whether or not a given element should be considered during the magnetization process.
* @method setFilter
* @param {Function} f Filter function to use. Takes an element ID and returns whether or not that element can be moved.
*/
this.setFilter = function(f) {
filter = f;
};
/**
* Reset the Farahey instance. Use this to avoid memory leaks.
* @method reset
*/
this.reset = function() {
elements.length = 0;
};
if (debug)
createOriginDebugger();
if (executeNow) this.execute();
return this;
};
/**
* Gets a new FaraheyInstance
* @method
* @param {Object} params Method parameters.
* @param {Selector|Element} [params.container] Element that contains the elements to magnetize. Only required if you intend to use the `executeAtEvent` method.
* @param {Function} [params.getContainerPosition] Function that returns the position of the container (as an object of the form `{left:.., top:..}`) when requested. Only required if you intend to use the `executeAtEvent` method.
* @param {Function} params.getPosition A function that takes an element and returns its position. It does not matter to which element this position is computed as long as you remain consistent with this method, `setPosition` and the `origin` property.
* @param {Function} params.setPosition A function that takes an element and position, and sets it. See note about offset parent above.
* @param {Function} params.getSize A function that takes an element and returns its size, in pixels.
* @param {Number[]} [params.padding] Optional padding for x and y directions. Defaults to 20 pixels in each direction.
* @param {Function} [params.constrain] Optional function that takes an id and a proposed amount of movement in each axis, and returns the allowed amount of movement in each axis. You can use this to constrain your elements to a grid, for instance, or a path, etc.
* @param {Number[]} [params.origin] The origin of magnetization, in pixels. Defaults to 0,0. You can also supply this to the `execute` call.
* @param {Selector|String[]|Element[]} params.elements List, or object hash, of elements on which to operate.
* @param {Boolean} [params.executeNow=false] Whether or not to execute the routine immediately.
* @param {Function} [params.filter] Optional function that takes an element id and returns whether or not that element can be moved.
* @param {Boolean} [params.orderByDistanceFromOrigin=false] Whether or not to sort elements first by distance from origin. Can have better results but takes more time.
*/
Farahey.getInstance = function(params) {
return new FaraheyInstance(params);
};
}).call(typeof window !== 'undefined' ? window : this);

View File

@@ -67,8 +67,12 @@ $.fn.dragToSelect = function (conf) {
onRefresh: function () {return true;}
}, c);
var realParent = $(this);
var parent = realParent;
var realParent = $(this);
var parent = realParent;
// container for lasso element
// -> the only reason for NOT using the .pf-map is because of the zoom [scale()] feature or .pf-map
var lassoContainer = realParent.parent();
var animationFrameId;
var mouseIsDown = false;
@@ -124,7 +128,7 @@ $.fn.dragToSelect = function (conf) {
// Create select box
var selectBox = $('<div>')
.appendTo(parent)
.appendTo(lassoContainer)
.attr('class', config.className)
.css('position', 'absolute');
@@ -156,20 +160,6 @@ $.fn.dragToSelect = function (conf) {
return refreshed;
}
// get scroll position
/*
var leftScroll = 0;
var topScroll = 0;
if(realParent.attr('data-scroll-left')){
leftScroll = parseInt(realParent.attr('data-scroll-left'));
}
if(realParent.attr('data-scroll-top')){
topScroll = parseInt(realParent.attr('data-scroll-top'));
}
*/
var left = lastMousePosition.x - parentDim.left + parent[0].scrollLeft;
var top = lastMousePosition.y - parentDim.top + parent[0].scrollTop;
var tempWidth = selectBoxOrigin.left - left;
@@ -220,30 +210,6 @@ $.fn.dragToSelect = function (conf) {
}
};
// Scrolls parent if needed
var scrollPerhaps = function (e) {
if (!selectBox.is('.' + config.activeClass) || parent.is('.' + config.disabledClass)) {
return;
}
// Scroll down
if ((e.pageY + config.scrollTH) > (parentDim.top + parentDim.height)) {
parent[0].scrollTop += config.scrollTH;
}
// Scroll up
if ((e.pageY - config.scrollTH) < parentDim.top) {
parent[0].scrollTop -= config.scrollTH;
}
// Scroll right
if ((e.pageX + config.scrollTH) > (parentDim.left + parentDim.width)) {
parent[0].scrollLeft += config.scrollTH;
}
// Scroll left
if ((e.pageX - config.scrollTH) < parentDim.left) {
parent[0].scrollLeft -= config.scrollTH;
}
};
// Selects all the elements in the select box's range
var selectElementsInRange = function () {
if (!selectBox.is('.' + config.activeClass) || parent.is('.' + config.disabledClass)) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -34,7 +34,7 @@ requirejs.config({
velocityUI: 'lib/velocity.ui.min', // v5.2.0 plugin for velocity - http://julian.com/research/velocity/#uiPack
slidebars: 'lib/slidebars', // v2.0.2 Slidebars - side menu plugin https://www.adchsm.com/slidebars/
jsPlumb: 'lib/jsplumb', // v2.9.3 jsPlumb main map draw plugin http://jsplumb.github.io/jsplumb/home.html
farahey: 'lib/farahey-0.5', // v0.5 jsPlumb "magnetizing" extension - https://github.com/jsplumb/farahey
farahey: 'lib/farahey', // v1.1.2 jsPlumb "magnetizing" plugin extension - https://github.com/ThomasChan/farahey
customScrollbar: 'lib/jquery.mCustomScrollbar.min', // v3.1.5 Custom scroll bars - http://manos.malihu.gr
mousewheel: 'lib/jquery.mousewheel.min', // v3.1.13 Mousewheel - https://github.com/jquery/jquery-mousewheel
xEditable: 'lib/bootstrap-editable.min', // v1.5.1 X-editable - in placed editing

View File

@@ -145,7 +145,6 @@ define(['jquery'], ($) => {
unicode: '&#xf619;'
}
],
classes: {
// log types
logTypes: {
@@ -354,17 +353,61 @@ define(['jquery'], ($) => {
wh_critical: {
cssClass: 'pf-map-connection-wh-critical'
},
frigate: {
cssClass: 'pf-map-connection-frig',
wh_jump_mass_s: {
cssClass: 'pf-map-connection-wh-size-s',
paintStyle: {
dashstyle: '0.5 1'
dashstyle: '0.5 1',
strokeWidth: 3
},
overlays: [
['Label',
{
label: 'frig',
cssClass: ['pf-map-component-overlay', 'frig'].join(' '),
location: 0.7
label: '<i class="fas fa-char pf-jump-mass-s" data-char-content="S"></i>',
cssClass: ['pf-map-component-overlay', 'small', 'text-center'].join(' '),
location: 0.65,
id: 'pf-map-connection-jump-mass-overlay'
}]
]
},
wh_jump_mass_m: {
cssClass: 'pf-map-connection-wh-size-m',
paintStyle: {
dashstyle: '3 1'
},
overlays: [
['Label',
{
label: '<i class="fas fa-char pf-jump-mass-m" data-char-content="M"></i>',
cssClass: ['pf-map-component-overlay', 'small', 'text-center'].join(' '),
location: 0.65,
id: 'pf-map-connection-jump-mass-overlay'
}]
]
},
wh_jump_mass_l: {
cssClass: 'pf-map-connection-wh-size-l',
overlays: [
['Label',
{
label: '<i class="fas fa-char pf-jump-mass-l" data-char-content="L"></i>',
cssClass: ['pf-map-component-overlay', 'small', 'text-center'].join(' '),
location: 0.65,
id: 'pf-map-connection-jump-mass-overlay'
}]
]
},
wh_jump_mass_xl: {
cssClass: 'pf-map-connection-wh-size-xl',
paintStyle: {
strokeWidth: 6
},
overlays: [
['Label',
{
label: '<i class="fas fa-char pf-jump-mass-xl" data-char-content="XL"></i>',
cssClass: ['pf-map-component-overlay', 'small', 'text-center'].join(' '),
location: 0.65,
id: 'pf-map-connection-jump-mass-overlay'
}]
]
},
@@ -375,7 +418,7 @@ define(['jquery'], ($) => {
{
label: '<i class="fas fa-fw fa-exclamation-triangle"></i>&nbsp;save mass',
cssClass: ['pf-map-component-overlay', 'mass'].join(' '),
location: 0.3
location: 0.35
}]
]
},
@@ -387,8 +430,8 @@ define(['jquery'], ($) => {
cssClass: 'pf-map-connection-arrow-overlay',
width: 12,
length: 15,
foldback: 0.8,
direction: 1,
foldback: 0.8,
location: 0.5
}]
]
@@ -408,6 +451,32 @@ define(['jquery'], ($) => {
]
}
},
wormholeSizes: {
wh_jump_mass_xl: {
jumpMassMin: 1000000000,
type: 'wh_jump_mass_xl',
class: 'pf-jump-mass-xl',
label: 'XL'
},
wh_jump_mass_l: {
jumpMassMin: 300000000,
type: 'wh_jump_mass_l',
class: 'pf-jump-mass-l',
label: 'L'
},
wh_jump_mass_m: {
jumpMassMin: 20000000,
type: 'wh_jump_mass_m',
class: 'pf-jump-mass-m',
label: 'M'
},
wh_jump_mass_s: {
jumpMassMin: 1000,
type: 'wh_jump_mass_s',
class: 'pf-jump-mass-s',
label: 'S'
}
},
// signature groups
signatureGroups: {
1: {

View File

@@ -93,20 +93,25 @@ define([
let moduleData = {
id: config.connectionContextMenuId,
items: [
{icon: 'fa-plane', action: 'frigate', text: 'frigate hole'},
{icon: 'fa-hourglass-end', action: 'wh_eol', text: 'toggle EOL'},
{icon: 'fa-exclamation-triangle', action: 'preserve_mass', text: 'preserve mass'},
{icon: 'fa-crosshairs', action: 'change_scope', text: 'change scope', subitems: [
{subIcon: 'fa-minus-circle', subIconClass: '', subAction: 'scope_wh', subText: 'wormhole'},
{subIcon: 'fa-minus-circle', subIconClass: 'txt-color txt-color-indigoDarkest', subAction: 'scope_stargate', subText: 'stargate'},
{subIcon: 'fa-minus-circle', subIconClass: 'txt-color txt-color-tealLighter', subAction: 'scope_jumpbridge', subText: 'jumpbridge'}
{icon: 'fa-reply fa-rotate-180', action: 'change_status', text: 'mass status', subitems: [
{subIcon: 'fa-circle', subIconClass: 'txt-color txt-color-gray', subAction: 'status_fresh', subText: 'stage 1 (fresh)'},
{subIcon: 'fa-circle', subIconClass: 'txt-color txt-color-orange', subAction: 'status_reduced', subText: 'stage 2 (reduced)'},
{subIcon: 'fa-circle', subIconClass: 'txt-color txt-color-redDarker', subAction: 'status_critical', subText: 'stage 3 (critical)'}
]},
{icon: 'fa-reply fa-rotate-180', action: 'change_status', text: 'change status', subitems: [
{subIcon: 'fa-clock', subAction: 'wh_eol', subText: 'toggle EOL'},
{subDivider: true},
{subIcon: 'fa-circle', subAction: 'status_fresh', subText: 'stage 1 (fresh)'},
{subIcon: 'fa-adjust', subAction: 'status_reduced', subText: 'stage 2 (reduced)'},
{subIcon: 'fa-circle', subAction: 'status_critical', subText: 'stage 3 (critical)'}
{icon: 'fa-reply fa-rotate-180', action: 'wh_jump_mass_change', text: 'ship size', subitems: [
{subIcon: 'fa-char', subChar: 'S', subAction: 'wh_jump_mass_s', subText: 'smallest ships'},
{subIcon: 'fa-char', subChar: 'M', subAction: 'wh_jump_mass_m', subText: 'medium ships'},
{subIcon: 'fa-char', subChar: 'L', subAction: 'wh_jump_mass_l', subText: 'larger ships'},
{subIcon: 'fa-char', subChar: 'XL', subAction: 'wh_jump_mass_xl', subText: 'capital ships'}
]},
{icon: 'fa-crosshairs', action: 'change_scope', text: 'change scope', subitems: [
{subIcon: 'fa-minus-circle', subIconClass: '', subAction: 'scope_wh', subText: 'wormhole'},
{subIcon: 'fa-minus-circle', subIconClass: 'txt-color txt-color-indigoDarkest', subAction: 'scope_stargate', subText: 'stargate'},
{subIcon: 'fa-minus-circle', subIconClass: 'txt-color txt-color-tealLighter', subAction: 'scope_jumpbridge', subText: 'jumpbridge'}
]},
{divider: true, action: 'separator'} ,

View File

@@ -1,135 +1,173 @@
/**
* Map "magnetizing" feature
* jsPlumb extension used: http://morrisonpitt.com/farahey/
* jsPlumb extension used: https://github.com/ThomasChan/farahey
*/
define([
'jquery',
'app/map/util',
'farahey'
], function($, MapUtil){
], ($, MapUtil) => {
'use strict';
/**
* Cached current "Magnetizer" object
* @type {Magnetizer}
* active magnetizer instances (cache object)
* @type {{}}
*/
let m8 = null;
let magnetizerInstances = {};
/**
* init a jsPlumb (map) Element for "magnetised" function.
* this is optional and prevents systems from being overlapped
* magnetizer instance exists for mapId
* @param mapId
* @returns {boolean}
*/
$.fn.initMagnetizer = function(){
let mapContainer = this;
let systems = mapContainer.getSystems();
let hasInstance = mapId => magnetizerInstances.hasOwnProperty(mapId);
/**
* helper function
* get current system offset
* @param system
* @returns {{left, top}}
* @private
*/
let _offset = function(system){
/**
* get magnetizer instance by mapId
* @param mapId
* @returns {null}
*/
let getInstance = mapId => hasInstance(mapId) ? magnetizerInstances[mapId] : null;
let _ = function(p){
let v = system.style[p];
return parseInt(v.substring(0, v.length - 2));
};
return {
left:_('left'),
top:_('top')
};
};
/**
* helper function
* set new system offset
* @param system
* @param o
* @private
*/
let _setOffset = function(system, o){
let markAsUpdated = false;
// new position must be within parent container
// no negative offset!
if(
o.left >= 0 &&
o.left <= 2300
){
markAsUpdated = true;
system.style.left = o.left + 'px';
}
if(
o.top >= 0 &&
o.top <= 498
){
markAsUpdated = true;
system.style.top = o.top + 'px';
}
if(markAsUpdated === true){
MapUtil.markAsChanged($(system));
}
};
/**
* helper function
* exclude current dragged element(s) from position update
* @param id
* @returns {boolean}
* @private
*/
let _dragFilter = function(id){
return !$('#' + id).is('.jsPlumb_dragged, .pf-system-locked');
};
let gridConstrain = function(gridX, gridY){
return function(id, current, delta){
if( mapContainer.hasClass(MapUtil.config.mapGridClass) ){
// active grid
return {
left:(gridX * Math.floor( (current[0] + delta.left) / gridX )) - current[0],
top:(gridY * Math.floor( (current[1] + delta.top) / gridY )) - current[1]
};
}else{
// no grid
return delta;
}
};
};
// main init for "magnetize" feature ------------------------------------------------------
m8 = new Magnetizer({
container: mapContainer,
getContainerPosition: function(c){
return c.offset();
},
getPosition:_offset,
getSize: function(system){
return [ $(system).outerWidth(), $(system).outerHeight() ];
},
getId : function(system){
return $(system).attr('id');
},
setPosition:_setOffset,
elements: systems,
filter: _dragFilter,
padding: [6, 6],
constrain: gridConstrain(MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension)
});
/**
* set new magnetizer instance for mapId
* @param mapId
* @param magnetizer
*/
let setInstance = (mapId, magnetizer) => {
if(mapId && magnetizer){
magnetizerInstances[mapId] = magnetizer;
}
};
$.fn.destroyMagnetizer = function(){
let mapContainer = this;
/**
* init new magnetizer instance for a map
* @param mapContainer
*/
let initMagnetizer = mapContainer => {
let mapId = mapContainer.data('id');
// remove cached "magnetizer" instance
m8 = null;
if(!hasInstance(mapId)){
// magnetizer not exist -> new instance
let systems = mapContainer.getSystems();
/**
* function that takes an element from your list and returns its position as a JS object
* @param system
* @returns {{top: number, left: number}}
* @private
*/
let _offset = system => {
let _ = p => {
let v = system.style[p];
return parseInt(v.substring(0, v.length - 2));
};
return {left: _('left'), top: _('top')};
};
/**
* function that takes an element id and position, and applies that position to the related element
* @param system
* @param o
* @private
*/
let _setOffset = (system, o) => {
o.left = Math.round(o.left);
o.top = Math.round(o.top);
let left = o.left + 'px';
let top = o.top + 'px';
let markAsUpdated = false;
// new position must be within parent container
// no negative offset!
if(
o.left >= 0 && o.left <= 2300 &&
system.style.left !== left
){
system.style.left = left;
markAsUpdated = true;
}
if(
o.top >= 0 && o.top <= 1400 &&
system.style.top !== top
){
system.style.top = top;
markAsUpdated = true;
}
if(markAsUpdated){
MapUtil.markAsChanged($(system));
}
};
/**
* filter some element8s) from being moved
* @param systemId
* @returns {boolean}
* @private
*/
let _dragFilter = systemId => {
let filterClasses = ['jtk-drag', 'pf-system-locked'];
return ![...document.getElementById(systemId).classList].some(className => filterClasses.indexOf(className) >= 0);
};
/**
* grid snap constraint
* @param gridX
* @param gridY
* @returns {Function}
*/
let gridConstrain = (gridX, gridY) => {
return (id, current, delta) => {
if(mapContainer.hasClass(MapUtil.config.mapGridClass)){
// active grid
return {
left: (gridX * Math.floor( (Math.round(current[0]) + delta.left) / gridX )) - current[0],
top: (gridY * Math.floor( (Math.round(current[1]) + delta.top) / gridY )) - current[1]
};
}else{
// no grid
delta.left = Math.round(delta.left);
delta.top = Math.round(delta.top);
return delta;
}
};
};
// create new magnetizer instance -------------------------------------------------------------------------
setInstance(mapId, window.Farahey.getInstance({
container: mapContainer,
getContainerPosition: mapContainer => mapContainer.offset(),
getPosition:_offset,
getSize: system => {
let clientRect = system.getBoundingClientRect();
return [Math.floor(clientRect.width), Math.floor(clientRect.height)];
},
getId : system => system.id,
setPosition:_setOffset,
elements: systems.toArray(),
filter: _dragFilter,
padding: [3, 3],
constrain: gridConstrain(MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension),
executeNow: false, // no initial rearrange after initialization
excludeFocus: true
}));
}
};
/**
* destroy Magnetizer instance
*/
let destroyMagnetizer = mapContainer => {
let mapId = mapContainer.data('id');
let magnetizer = getInstance(mapId);
if(magnetizer){
magnetizer.reset();
delete magnetizerInstances[mapId];
}
};
/**
@@ -137,44 +175,50 @@ define([
* @param map
* @param e
*/
let executeAtEvent = function(map, e){
if(m8 !== null && e ){
m8.executeAtEvent(e);
let executeAtEvent = (map, e) => {
let mapContainer = $(map.getContainer());
let mapId = mapContainer.data('id');
let magnetizer = getInstance(mapId);
if(magnetizer && e){
magnetizer.executeAtEvent(e, {
iterations: 2,
excludeFocus: true
});
map.repaintEverything();
}
};
/**
* rearrange all systems of a map
* needs "magnetization" to be active
* @param map
* add system to magnetizer instance
* @param mapId
* @param system
* @param doNotTestForDuplicates
*/
let executeAtCenter = function(map){
if(m8 !== null){
m8.executeAtCenter();
map.repaintEverything();
let addElement = (mapId, system, doNotTestForDuplicates) => {
let magnetizer = getInstance(mapId);
if(magnetizer){
magnetizer.addElement(system, doNotTestForDuplicates);
}
};
/**
* set/update elements for "magnetization"
* -> (e.g. new systems was added)
* @param map
* remove system element from magnetizer instance
* @param mapId
* @param system
*/
let setElements = function(map){
if(m8 !== null){
let mapContainer = $(map.getContainer());
let systems = mapContainer.getSystems();
m8.setElements(systems);
// re-arrange systems
executeAtCenter(map);
let removeElement = (mapId, system) => {
let magnetizer = getInstance(mapId);
if(magnetizer){
magnetizer.removeElement(system);
}
};
return {
executeAtCenter: executeAtCenter,
initMagnetizer: initMagnetizer,
destroyMagnetizer: destroyMagnetizer,
executeAtEvent: executeAtEvent,
setElements: setElements
addElement: addElement,
removeElement: removeElement
};
});

View File

@@ -18,7 +18,7 @@ define([
'app/map/scrollbar',
'dragToSelect',
'app/map/local'
], ($, Init, Util, Key, bootbox, MapUtil, MapContextMenu, MapOverlay, MapOverlayUtil, System, Layout, MagnetizerWrapper, Scrollbar) => {
], ($, Init, Util, Key, bootbox, MapUtil, MapContextMenu, MapOverlay, MapOverlayUtil, System, Layout, Magnetizer, Scrollbar) => {
'use strict';
@@ -62,6 +62,33 @@ define([
// -> those maps queue their updates until "pf:unlocked" event
let mapUpdateQueue = [];
// map menu options
let mapOptions = {
mapMagnetizer: {
buttonId: Util.config.menuButtonMagnetizerId,
description: 'Magnetizer',
onEnable: Magnetizer.initMagnetizer,
onDisable: Magnetizer.destroyMagnetizer
},
mapSnapToGrid : {
buttonId: Util.config.menuButtonGridId,
description: 'Grid snapping',
class: 'mapGridClass'
},
mapSignatureOverlays : {
buttonId: Util.config.menuButtonEndpointId,
description: 'Endpoint overlay',
onEnable: MapOverlay.showInfoSignatureOverlays,
onDisable: MapOverlay.hideInfoSignatureOverlays,
},
mapCompact : {
buttonId: Util.config.menuButtonCompactId,
description: 'Compact system layout',
class: 'mapCompactClass'
}
};
/**
* checks mouse events on system head elements
* -> prevents drag/drop system AND drag/drop connections on some child elements
@@ -131,8 +158,8 @@ define([
// check if there is a Label overlay
let overlay = endpoint.getOverlay(MapOverlayUtil.config.endpointOverlayId);
if(overlay instanceof jsPlumb.Overlays.Label){
let label = overlay.getParameter('label');
overlay.setLocation(MapUtil.getEndpointOverlaySignatureLocation(endpoint, label));
let labels = overlay.getParameter('signatureLabels');
overlay.setLocation(MapUtil.getEndpointOverlaySignatureLocation(endpoint, labels));
}
}
}
@@ -675,7 +702,7 @@ define([
}
}
// save filterScopes in IndexDB
// store filterScopes in IndexDB
MapUtil.storeLocalData('map', mapId, 'filterScopes', filterScopes);
MapUtil.filterMapByScopes(map, filterScopes);
@@ -726,7 +753,6 @@ define([
}
});
break;
case 'frigate': // set as frigate hole
case 'preserve_mass': // set "preserve mass
case 'wh_eol': // set "end of life"
MapOverlayUtil.getMapOverlay(mapElement, 'timer').startMapUpdateCounter();
@@ -741,6 +767,14 @@ define([
MapUtil.setConnectionWHStatus(connection, 'wh_' + newStatus);
MapUtil.markAsChanged(connection);
break;
case 'wh_jump_mass_s':
case 'wh_jump_mass_m':
case 'wh_jump_mass_l':
case 'wh_jump_mass_xl':
MapOverlayUtil.getMapOverlay(mapElement, 'timer').startMapUpdateCounter();
MapUtil.setConnectionJumpMassType(connection, action);
MapUtil.markAsChanged(connection);
break;
case 'scope_wh':
case 'scope_stargate':
case 'scope_jumpbridge':
@@ -969,13 +1003,13 @@ define([
if(checkAvailability(['fresh', 'reduced', 'critical'], type)){
MapUtil.setConnectionWHStatus(connection, type);
}else if(connection.hasType(type) !== true){
// additional types e.g. eol, frig, preserve mass
// additional types e.g. eol, preserve mass
connection.addType(type);
}
}
for(let type of removeType){
if(checkAvailability(['wh_eol', 'frigate', 'preserve_mass', 'state_process'], type)){
if(checkAvailability(['wh_eol', 'preserve_mass', 'state_process'], type)){
connection.removeType(type);
}
}
@@ -1178,7 +1212,6 @@ define([
let mapContainer = mapConfig.map ? $(mapConfig.map.getContainer()) : null;
if(mapContainer){
let mapId = mapConfig.config.id;
let newSystems = 0;
// add additional information for this map
if(mapContainer.data('updated') !== mapConfig.config.updated.updated){
@@ -1219,9 +1252,8 @@ define([
}
}
if( addNewSystem === true){
if(addNewSystem === true){
drawSystem(mapConfig.map, systemData);
newSystems++;
}
}
@@ -1324,11 +1356,6 @@ define([
// update local connection cache
updateConnectionsCache(mapConfig.map);
// update map "magnetization" when new systems where added
if(newSystems > 0){
MagnetizerWrapper.setElements(mapConfig.map);
}
}else{
// map is currently logged -> queue update for this map until unlock
if( mapUpdateQueue.indexOf(mapId) === -1 ){
@@ -1345,10 +1372,13 @@ define([
});
};
return new Promise(updateMapExecutor).then(payload => {
/**
* apply current active scope filter
* @param payload
* @returns {Promise<any>}
*/
let filterMapByScopes = payload => {
let filterMapByScopesExecutor = (resolve, reject) => {
// apply current active scope filter ==================================================================
let promiseStore = MapUtil.getLocaleData('map', payload.data.mapConfig.config.id);
promiseStore.then(dataStore => {
let scopes = [];
@@ -1362,7 +1392,31 @@ define([
};
return new Promise(filterMapByScopesExecutor);
});
};
/**
* show signature overlays
* @param payload
* @returns {Promise<any>}
*/
let showInfoSignatureOverlays = payload => {
let showInfoSignatureOverlaysExecutor = (resolve, reject) => {
let promiseStore = MapUtil.getLocaleData('map', payload.data.mapConfig.config.id);
promiseStore.then(dataStore => {
if(dataStore && dataStore.mapSignatureOverlays){
MapOverlay.showInfoSignatureOverlays($(payload.data.mapConfig.map.getContainer()));
}
resolve(payload);
});
};
return new Promise(showInfoSignatureOverlaysExecutor);
};
return new Promise(updateMapExecutor)
.then(showInfoSignatureOverlays)
.then(filterMapByScopes);
};
/**
@@ -1504,6 +1558,9 @@ define([
// set system observer
setSystemObserver(map, newSystem);
// register system to "magnetizer"
Magnetizer.addElement(systemData.mapId, newSystem[0]);
// connect new system (if connection data is given)
if(connectedSystem){
@@ -1736,22 +1793,26 @@ define([
// hidden menu actions
if(scope === 'abyssal'){
options.hidden.push('frigate');
options.hidden.push('wh_eol');
options.hidden.push('preserve_mass');
options.hidden.push('change_status');
options.hidden.push('wh_jump_mass_change');
options.hidden.push('change_scope');
options.hidden.push('separator');
}else if(scope === 'stargate'){
options.hidden.push('frigate');
options.hidden.push('wh_eol');
options.hidden.push('preserve_mass');
options.hidden.push('change_status');
options.hidden.push('wh_jump_mass_change');
options.hidden.push('scope_stargate');
}else if(scope === 'jumpbridge'){
options.hidden.push('frigate');
options.hidden.push('wh_eol');
options.hidden.push('preserve_mass');
options.hidden.push('change_status');
options.hidden.push('wh_jump_mass_change');
options.hidden.push('scope_jumpbridge');
}else if(scope === 'wh'){
options.hidden.push('scope_wh');
@@ -1761,13 +1822,14 @@ define([
if(connection.hasType('wh_eol') === true){
options.active.push('wh_eol');
}
if(connection.hasType('frigate') === true){
options.active.push('frigate');
}
if(connection.hasType('preserve_mass') === true){
options.active.push('preserve_mass');
}
for(let sizeName of Object.keys(Init.wormholeSizes)){
if(connection.hasType(sizeName)){
options.active.push(sizeName);
}
}
if(connection.hasType('wh_reduced') === true){
options.active.push('status_reduced');
}else if(connection.hasType('wh_critical') === true){
@@ -1777,6 +1839,11 @@ define([
options.active.push('status_fresh');
}
// disabled menu actions
if(connection.getParameter('sizeLocked')){
options.disabled.push('wh_jump_mass_change');
}
resolve(options);
};
@@ -1864,7 +1931,7 @@ define([
// update system positions for "all" systems that are effected by drag&drop
// this requires "magnet" feature to be active! (optional)
MagnetizerWrapper.executeAtEvent(map, p.e);
Magnetizer.executeAtEvent(map, p.e);
},
stop: function(params){
let dragSystem = $(params.el);
@@ -1967,11 +2034,7 @@ define([
* @param sourceSystem
*/
let saveSystemCallback = (map, newSystemData, sourceSystem) => {
// draw new system to map
drawSystem(map, newSystemData, sourceSystem);
// re/arrange systems (prevent overlapping)
MagnetizerWrapper.setElements(map);
};
/**
@@ -2224,6 +2287,13 @@ define([
// Notification the current zoom was changed
newJsPlumbInstance.bind('zoom', function(zoom){
MapOverlay.updateZoomOverlay(this);
// store new zoom level in IndexDB
if(zoom === 1){
MapUtil.deleteLocalData('map', mapId, 'mapZoom');
}else{
MapUtil.storeLocalData('map', mapId, 'mapZoom', zoom);
}
});
// ========================================================================================================
@@ -2523,7 +2593,7 @@ define([
let mapElement = $(this);
// get map menu config options
let data = MapUtil.mapOptions[mapOption.option];
let data = mapOptions[mapOption.option];
let promiseStore = MapUtil.getLocaleData('map', mapElement.data('id'));
promiseStore.then(function(dataStore){
@@ -2538,50 +2608,50 @@ define([
dataExists = true;
}
if(dataExists === mapOption.toggle){
if(dataExists === this.mapOption.toggle){
// toggle button class
button.removeClass('active');
// toggle map class (e.g. for grid)
if(this.data.class){
this.mapElement.removeClass( MapUtil.config[this.data.class] );
this.mapElement.removeClass(MapUtil.config[this.data.class]);
}
// call optional jQuery extension on mapElement
if(this.data.onDisable){
$.fn[ this.data.onDisable ].apply(this.mapElement);
if(this.data.onDisable && !this.mapOption.skipOnDisable){
this.data.onDisable(this.mapElement);
}
// show map overlay info icon
MapOverlayUtil.getMapOverlay(this.mapElement, 'info').updateOverlayIcon(this.mapOption.option, 'hide');
// delete map option
MapUtil.deleteLocalData('map', this.mapElement.data('id'), this.mapOption.option );
MapUtil.deleteLocalData('map', this.mapElement.data('id'), this.mapOption.option);
}else{
// toggle button class
button.addClass('active');
// toggle map class (e.g. for grid)
if(this.data.class){
this.mapElement.addClass( MapUtil.config[this.data.class] );
this.mapElement.addClass(MapUtil.config[this.data.class]);
}
// call optional jQuery extension on mapElement
if(this.data.onEnable){
$.fn[ this.data.onEnable ].apply(this.mapElement);
if(this.data.onEnable && !this.mapOption.skipOnEnable){
this.data.onEnable(this.mapElement);
}
// hide map overlay info icon
MapOverlayUtil.getMapOverlay(this.mapElement, 'info').updateOverlayIcon(this.mapOption.option, 'show');
// store map option
MapUtil.storeLocalData('map', this.mapElement.data('id'), this.mapOption.option, 1 );
MapUtil.storeLocalData('map', this.mapElement.data('id'), this.mapOption.option, 1);
notificationText = 'enabled';
}
if(mapOption.toggle){
if(this.mapOption.toggle){
Util.showNotify({title: this.data.description, text: notificationText, type: 'info'});
}
}.bind({
@@ -3009,7 +3079,8 @@ define([
let mapElement = $(mapConfig.map.getContainer());
MapUtil.setMapDefaultOptions(mapElement, mapConfig.config)
.then(payload => MapUtil.visualizeMap(mapElement, 'show'))
.then(payload => MapUtil.scrollToDefaultPosition(mapElement))
.then(payload => MapUtil.zoomToDefaultScale(mapConfig.map))
.then(payload => MapUtil.scrollToDefaultPosition(mapConfig.map))
.then(payload => {
Util.showNotify({title: 'Map initialized', text: mapConfig.config.name + ' - loaded', type: 'success'});
})
@@ -3039,7 +3110,7 @@ define([
*/
let loadMapExecutor = (resolve, reject) => {
// init jsPlumb
jsPlumb.ready(function(){
jsPlumb.ready(() => {
// get new map instance or load existing
mapConfig.map = getMapInstance(mapConfig.config.id);

View File

@@ -11,81 +11,93 @@ define([
], ($, Init, Util, MapOverlayUtil, MapUtil) => {
'use strict';
/**
* get MapObject (jsPlumb) from mapElement
* @param mapElement
* @returns {*}
*/
let getMapObjectFromMapElement = mapElement => {
let Map = require('app/map/map');
return Map.getMapInstance(mapElement.data('id'));
};
/**
* get map object (jsPlumb) from iconElement
* @param overlayIcon
* @returns {*}
*/
let getMapObjectFromOverlayIcon = overlayIcon => {
let mapElement = Util.getMapElementFromOverlay(overlayIcon);
return getMapObjectFromMapElement(mapElement);
return MapUtil.getMapInstance(Util.getMapElementFromOverlay(overlayIcon).data('id'));
};
/**
* add overlay to endpoint with signature data
* add/update endpoints with overlays from signature mapping
* @param endpoint
* @param labelData
*/
let addEndpointOverlaySignatureLabel = (endpoint, labelData) => {
let label = labelData.labels.join(', ');
let name = labelData.names.join(', ');
let updateEndpointOverlaySignatureLabel = (endpoint, labelData) => {
let labels = labelData.labels;
let names = labelData.names;
let overlay = endpoint.getOverlay(MapOverlayUtil.config.endpointOverlayId);
if(overlay instanceof jsPlumb.Overlays.Label){
// update existing overlay
if(
label !== overlay.getParameter('label') ||
name !== overlay.getParameter('signatureName')
!labels.equalValues(overlay.getParameter('signatureLabels')) ||
!names.equalValues(overlay.getParameter('signatureNames'))
){
// update label only on label changes
overlay.setLabel(MapUtil.formatEndpointOverlaySignatureLabel(label));
overlay.setLabel(MapUtil.formatEndpointOverlaySignatureLabel(labels));
overlay.setParameter('fullSize', false);
overlay.setParameter('label', label);
overlay.setParameter('signatureName', name);
overlay.updateClasses(label.length ? 'small' : 'icon', label.length ? 'icon' : 'small');
overlay.setLocation(MapUtil.getEndpointOverlaySignatureLocation(endpoint, label));
overlay.setParameter('signatureLabels', labels);
overlay.setParameter('signatureNames', names);
overlay.updateClasses(labels.length ? 'small' : 'icon', labels.length ? 'icon' : 'small');
overlay.setLocation(MapUtil.getEndpointOverlaySignatureLocation(endpoint, labels));
}
}else{
// add new overlay
endpoint.addOverlay([
'Label',
{
label: MapUtil.formatEndpointOverlaySignatureLabel(label),
label: MapUtil.formatEndpointOverlaySignatureLabel(labels),
id: MapOverlayUtil.config.endpointOverlayId,
cssClass: [MapOverlayUtil.config.componentOverlayClass, label.length ? 'small' : 'icon'].join(' '),
location: MapUtil.getEndpointOverlaySignatureLocation(endpoint, label),
cssClass: [MapOverlayUtil.config.componentOverlayClass, labels.length ? 'small' : 'icon'].join(' '),
location: MapUtil.getEndpointOverlaySignatureLocation(endpoint, labels),
events: {
toggleSize: function(fullSize){
let signatureName = this.getParameter('signatureName');
if(fullSize && !this.getParameter('fullSize') && signatureName){
this.setLabel(this.getLabel() + '<br>' + '<span class="initialism">' + signatureName + '</span>');
let signatureNames = this.getParameter('signatureNames');
if(fullSize && !this.getParameter('fullSize') && signatureNames.length){
this.setLabel(this.getLabel() + '<br>' + '<span class="initialism">' + signatureNames.join(', ') + '</span>');
this.setParameter('fullSize', true);
}else if(this.getParameter('fullSize')){
this.setLabel(MapUtil.formatEndpointOverlaySignatureLabel(this.getParameter('label')));
this.setLabel(MapUtil.formatEndpointOverlaySignatureLabel(this.getParameter('signatureLabels')));
this.setParameter('fullSize', false);
}
}
},
parameters: {
fullSize: false,
label: label,
signatureName: name
signatureLabels: labels,
signatureNames: names
}
}
]);
}
};
/**
* get overlay parameters for connection overlay (type 'diamond' or 'arrow')
* @param overlayType
* @param direction
* @returns {{length: number, foldback: number, direction: number}}
*/
let getConnectionArrowOverlayParams = (overlayType, direction = 1) => {
switch(overlayType){
case 'arrow':
return {
length: 15,
direction: direction,
foldback: 0.8
};
default: // diamond
return {
length: 10,
direction: 1,
foldback: 2
};
}
};
/**
* add overlays to connections (signature based data)
* @param map
@@ -93,26 +105,18 @@ define([
*/
let updateInfoSignatureOverlays = (map, connectionsData) => {
let type = 'info_signature';
let SystemSignatures = require('app/ui/module/system_signature');
connectionsData = Util.arrayToObject(connectionsData);
map.batch(function(){
map.getAllConnections().forEach(function(connection){
map.batch(() => {
map.getAllConnections().forEach(connection => {
let connectionId = connection.getParameter('connectionId');
let sourceEndpoint = connection.endpoints[0];
let targetEndpoint = connection.endpoints[1];
let signatureTypeData = {
source: {
names: [],
labels: []
},
target: {
names: [],
labels: []
}
};
let connectionData = connectionsData.hasOwnProperty(connectionId) ? connectionsData[connectionId] : undefined;
let signatureTypeData = MapUtil.getConnectionDataFromSignatures(connection, connectionData);
let sizeLockedBySignature = false;
if(connection.scope === 'wh'){
if(!connection.hasType(type)){
@@ -127,49 +131,49 @@ define([
connection.canvas.appendChild(overlayArrow.path);
}
let overlayType = 'Diamond'; // not specified
// since there "could" be multiple sig labels on each endpoint,
// there can only one "primary label picked up for wormhole jump mass detection!
let primLabel;
let overlayType = 'diamond'; // not specified
let arrowDirection = 1;
let arrowFoldback = 2;
if(connectionsData.hasOwnProperty(connectionId)){
if(connectionData && connectionData.signatures){
// signature data found for current connection
signatureTypeData = MapUtil.getConnectionDataFromSignatures(connection, connectionsData[connectionId]);
let sourceLabel = signatureTypeData.source.labels;
let targetLabel = signatureTypeData.target.labels;
// add arrow (connection) overlay that points from "XXX" => "K162" ------------------------------------
// add arrow (connection) overlay that points from "XXX" => "K162" ----------------------------
if(
(sourceLabel.indexOf('K162') !== -1 && targetLabel.indexOf('K162') !== -1) ||
(sourceLabel.includes('K162') && targetLabel.includes('K162')) ||
(sourceLabel.length === 0 && targetLabel.length === 0) ||
(
sourceLabel.length > 0 && targetLabel.length > 0 &&
sourceLabel.indexOf('K162') === -1 && targetLabel.indexOf('K162') === -1
!sourceLabel.includes('K162') && !targetLabel.includes('K162')
)
){
// unknown direction
overlayType = 'Diamond'; // not specified
arrowDirection = 1;
arrowFoldback = 2;
// unknown direction -> show default 'diamond' overlay
overlayType = 'diamond';
}else if(
(sourceLabel.indexOf('K162') !== -1) ||
(sourceLabel.length === 0 && targetLabel.indexOf('K162') === -1)
(sourceLabel.includes('K162')) ||
(sourceLabel.length === 0 && !targetLabel.includes('K162'))
){
// convert default arrow direction
overlayType = 'Arrow';
overlayType = 'arrow';
arrowDirection = -1;
arrowFoldback = 0.8;
primLabel = targetLabel.find(label => label !== 'K162');
}else{
// default arrow direction is fine
overlayType = 'Arrow';
arrowDirection = 1;
arrowFoldback = 0.8;
overlayType = 'arrow';
primLabel = sourceLabel.find(label => label !== 'K162');
}
}
// class changes must be done on "connection" itself not on "overlayArrow"
// -> because Arrow might not be rendered to map at this point (if it does not exist already)
if(overlayType === 'Arrow'){
if(overlayType === 'arrow'){
connection.updateClasses(
MapOverlayUtil.config.connectionArrowOverlaySuccessClass,
MapOverlayUtil.config.connectionArrowOverlayDangerClass
@@ -181,20 +185,39 @@ define([
);
}
overlayArrow.updateFrom({
direction: arrowDirection,
foldback: arrowFoldback
});
overlayArrow.updateFrom(getConnectionArrowOverlayParams(overlayType, arrowDirection));
// add endpoint overlays --------------------------------------------------------------------------
addEndpointOverlaySignatureLabel(sourceEndpoint, signatureTypeData.source);
addEndpointOverlaySignatureLabel(targetEndpoint, signatureTypeData.target);
// update/add endpoint overlays -------------------------------------------------------------------
updateEndpointOverlaySignatureLabel(sourceEndpoint, signatureTypeData.source);
updateEndpointOverlaySignatureLabel(targetEndpoint, signatureTypeData.target);
// fix/overwrite existing jump mass connection type -----------------------------------------------
// if a connection type for "jump mass" (e.g. S, M, L, XL) is set for this connection
// we should check/compare it with the current primary signature label from signature mapping
// and change it if necessary
if(Init.wormholes.hasOwnProperty(primLabel)){
// connection size from mapped signature
sizeLockedBySignature = true;
let wormholeData = Object.assign({}, Init.wormholes[primLabel]);
if(
wormholeData.size && wormholeData.size.type &&
!connection.hasType(wormholeData.size.type)
){
MapOverlayUtil.getMapOverlay(connection.canvas, 'timer').startMapUpdateCounter();
MapUtil.setConnectionJumpMassType(connection, wormholeData.size.type);
MapUtil.markAsChanged(connection);
}
}
}else{
// connection is not 'wh' scope
if(connection.hasType(type)){
connection.removeType(type);
}
}
// lock/unlock connection for manual size changes (from contextmenu)
connection.setParameter('sizeLocked', sizeLockedBySignature);
});
});
};
@@ -246,58 +269,45 @@ define([
};
/**
* git signature data that is linked to a connection for a mapId
* @param mapElement
* @param callback
* get overlay icon from e.g. mapElement
* @param element
* @param iconClass
* @param overlayType
* @returns {*}
*/
let getConnectionSignatureData = (mapElement, callback) => {
let mapOverlay = MapOverlayUtil.getMapOverlay(mapElement, 'info');
let overlayConnectionIcon = mapOverlay.find('.pf-map-overlay-endpoint');
showLoading(overlayConnectionIcon);
let requestData = {
mapId: mapElement.data('id'),
addData : ['signatures'],
filterData : ['signatures']
};
$.ajax({
type: 'POST',
url: Init.path.getMapConnectionData,
data: requestData,
dataType: 'json',
context: {
mapElement: mapElement,
overlayConnectionIcon: overlayConnectionIcon
}
}).done(function(connectionsData){
let map = getMapObjectFromMapElement(this.mapElement);
callback(map, connectionsData);
}).always(function(){
hideLoading(this.overlayConnectionIcon);
});
let getOverlayIcon = (element, iconClass, overlayType = 'info') => {
return MapOverlayUtil.getMapOverlay(element, overlayType).find('.' + iconClass);
};
/**
* showInfoSignatureOverlays
* -> used by "refresh" overlays (hover) AND/OR initial menu trigger
*/
$.fn.showInfoSignatureOverlays = function(){
let mapElement = $(this);
getConnectionSignatureData(mapElement, updateInfoSignatureOverlays);
let showInfoSignatureOverlays = mapElement => {
let mapId = mapElement.data('id');
let map = MapUtil.getMapInstance(mapId);
let mapData = Util.getCurrentMapData(mapId);
let connectionsData = Util.getObjVal(mapData, 'data.connections');
if(connectionsData){
let overlayIcon = getOverlayIcon(mapElement, options.mapSignatureOverlays.class);
showLoading(overlayIcon);
updateInfoSignatureOverlays(map, connectionsData);
hideLoading(overlayIcon);
}
};
/**
* hideInfoSignatureOverlays
* -> see showInfoSignatureOverlays()
*/
$.fn.hideInfoSignatureOverlays = function(){
let map = getMapObjectFromMapElement($(this));
let hideInfoSignatureOverlays = mapElement => {
let mapId = mapElement.data('id');
let map = MapUtil.getMapInstance(mapId);
let type = 'info_signature';
map.batch(function(){
map.getAllConnections().forEach(function(connection){
map.batch(() => {
map.getAllConnections().forEach(connection => {
let overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId);
if(overlayArrow){
@@ -382,20 +392,11 @@ define([
}
}
},
mapEndpoint: {
mapSignatureOverlays: {
title: 'refresh signature overlays',
trigger: 'refresh',
class: 'pf-map-overlay-endpoint',
iconClass: ['fas', 'fa-fw', 'fa-link'],
hoverIntent: {
over: function(e){
let mapElement = Util.getMapElementFromOverlay(this);
mapElement.showInfoSignatureOverlays();
},
out: function(e){
// just "refresh" on hover
}
}
iconClass: ['fas', 'fa-fw', 'fa-link']
},
mapCompact: {
title: 'compact layout',
@@ -427,8 +428,8 @@ define([
// format overlay label
let labels = [
'<i class="fas fa-fw fa-plus-square"></i>&nbsp;' + formatTimeParts(createdDiff),
'<i class="fas fa-fw fa-pen-square"></i>&nbsp;' + formatTimeParts(updatedDiff)
formatTimeParts(createdDiff) + '&nbsp;<i class="fas fa-fw fa-plus-square"></i>',
formatTimeParts(updatedDiff) + '&nbsp;<i class="fas fa-fw fa-pen-square"></i>'
];
// add label overlay --------------------------------------------------------------------------
@@ -437,7 +438,7 @@ define([
{
label: labels.join('<br>'),
id: MapOverlayUtil.config.connectionOverlayWhId,
cssClass: [MapOverlayUtil.config.componentOverlayClass, 'small'].join(' '),
cssClass: [MapOverlayUtil.config.componentOverlayClass, 'small', 'text-right'].join(' '),
location: 0.35
}
]);
@@ -457,7 +458,7 @@ define([
title: 'EOL timer',
trigger: 'hover',
class: 'pf-map-overlay-connection-eol',
iconClass: ['far', 'fa-fw', 'fa-clock'],
iconClass: ['fas', 'fa-fw', 'fa-hourglass-end'],
hoverIntent: {
over: function(e){
let map = getMapObjectFromOverlayIcon(this);
@@ -472,7 +473,7 @@ define([
connection.addOverlay([
'Label',
{
label: '<i class="far fa-fw fa-clock"></i>&nbsp;' + formatTimeParts(diff),
label: '<i class="fas fa-fw fa-hourglass-end"></i>&nbsp;' + formatTimeParts(diff),
id: MapOverlayUtil.config.connectionOverlayEolId,
cssClass: [MapOverlayUtil.config.componentOverlayClass, 'eol'].join(' '),
location: 0.25
@@ -542,7 +543,7 @@ define([
let percentPerCount = 100 / maxSeconds;
// update counter
let updateChart = function(tempSeconds){
let updateChart = tempSeconds => {
let pieChart = counterChart.data('easyPieChart');
if(pieChart !== undefined){
@@ -552,7 +553,7 @@ define([
};
// main timer function is called on any counter update
let timer = function(mapUpdateCounter){
let timer = mapUpdateCounter => {
// decrease timer
let currentSeconds = counterChart.data('currentSeconds');
currentSeconds--;
@@ -883,6 +884,8 @@ define([
};
return {
showInfoSignatureOverlays: showInfoSignatureOverlays,
hideInfoSignatureOverlays: hideInfoSignatureOverlays,
updateZoomOverlay: updateZoomOverlay,
initMapDebugOverlays: initMapDebugOverlays
};

View File

@@ -9,8 +9,9 @@ define([
'app/util',
'bootbox',
'app/map/util',
'app/map/layout'
], ($, Init, Util, bootbox, MapUtil, Layout) => {
'app/map/layout',
'app/map/magnetizing'
], ($, Init, Util, bootbox, MapUtil, Layout, Magnetizer) => {
'use strict';
let config = {
@@ -702,6 +703,9 @@ define([
// remove connections do not fire a "connectionDetached" event
map.deleteConnectionsForElement(system, {fireEvent: false});
// unregister from "magnetizer"
Magnetizer.removeElement(system.data('mapid'), system[0]);
// destroy tooltip/popover
system.toggleSystemTooltip('destroy', {});
system.destroyPopover(true);

View File

@@ -41,32 +41,6 @@ define([
tableCellEllipsis100Class: 'pf-table-cell-100'
};
// map menu options
let mapOptions = {
mapMagnetizer: {
buttonId: Util.config.menuButtonMagnetizerId,
description: 'Magnetizer',
onEnable: 'initMagnetizer', // jQuery extension function
onDisable: 'destroyMagnetizer' // jQuery extension function
},
mapSnapToGrid : {
buttonId: Util.config.menuButtonGridId,
description: 'Grid snapping',
class: 'mapGridClass'
},
mapEndpoint : {
buttonId: Util.config.menuButtonEndpointId,
description: 'Endpoint overlay',
onEnable: 'showInfoSignatureOverlays', // jQuery extension function
onDisable: 'hideInfoSignatureOverlays' // jQuery extension function
},
mapCompact : {
buttonId: Util.config.menuButtonCompactId,
description: 'Compact system layout',
class: 'mapCompactClass'
}
};
// active jsPlumb instances currently running =====================================================================
let activeInstances = {};
@@ -518,6 +492,7 @@ define([
if(
connection &&
connectionData &&
connectionData.signatures // signature data is required...
){
let SystemSignatures = require('app/ui/module/system_signature');
@@ -556,7 +531,7 @@ define([
if(flattenSigTypeNames.hasOwnProperty(signatureData.typeId)){
let label = flattenSigTypeNames[signatureData.typeId];
// shorten label, just take the in game name
// shorten label, just take the ingame name
label = label.substr(0, label.indexOf(' '));
signatureTypeData[tmpSystemType].names.push(signatureData.name);
signatureTypeData[tmpSystemType].labels.push(label);
@@ -574,13 +549,13 @@ define([
* -> Coordinates are relative to the Endpoint (not the system!)
* -> jsPlumb specific format
* @param endpoint
* @param label
* @param labels
* @returns {number[]}
*/
let getEndpointOverlaySignatureLocation = (endpoint, label) => {
let chars = label.length ? label.length : 2;
let xLeft = chars === 2 ? -0.5 : chars <= 4 ? -1 : 3;
let xRight = chars === 2 ? +1.5 : chars <= 4 ? +2.20 : 3;
let getEndpointOverlaySignatureLocation = (endpoint, labels) => {
let count = labels.length;
let xLeft = count ? count === 1 ? -1.00 : 3 : -0.5;
let xRight = count ? count === 1 ? +2.20 : 3 : +1.5;
switch(endpoint.anchor.getCurrentFace()){
case 'top': return [0.5, -0.75];
@@ -593,24 +568,25 @@ define([
/**
* get overlay HTML for connection endpoints by Label array
* @param label
* @param labels
* @returns {string}
*/
let formatEndpointOverlaySignatureLabel = label => {
let formatEndpointOverlaySignatureLabel = labels => {
// default K162 in label array, or multiple labels
let colorClass = 'txt-color-grayLighter';
let label = labels.join(', ');
if(label.length > 0){
// check if multiple labels found => conflict
if( label.includes(', ') ){
colorClass = 'txt-color-orangeLight';
}else if( !label.includes('K162') ){
colorClass = 'txt-color-yellow';
}
}else{
if(labels.length === 0){
// endpoint not connected with a signature
label = '<i class="fas fa-question-circle"></i>';
colorClass = 'txt-color-red';
}else if(
labels.length === 1 &&
!labels.includes('K162')
){
colorClass = Init.wormholes[labels[0]].class;
}
return '<span class="txt-color ' + colorClass + '">' + label + '</span>';
};
@@ -639,62 +615,64 @@ define([
*/
let filterMapByScopes = (map, scopes) => {
if(map){
let mapElement = $(map.getContainer());
let allSystems = mapElement.getSystems();
let allConnections = map.getAllConnections();
map.batch(() => {
let mapElement = $(map.getContainer());
let allSystems = mapElement.getSystems();
let allConnections = map.getAllConnections();
if(scopes && scopes.length){
// filter connections -------------------------------------------------------------------------------------
let visibleSystems = [];
let visibleConnections = searchConnectionsByScopeAndType(map, scopes);
if(scopes && scopes.length){
// filter connections -------------------------------------------------------------------------------------
let visibleSystems = [];
let visibleConnections = searchConnectionsByScopeAndType(map, scopes);
for(let connection of allConnections){
if(visibleConnections.indexOf(connection) >= 0){
setConnectionVisible(connection, true);
// source/target system should always be visible -> even if filter scope not matches system type
if(visibleSystems.indexOf(connection.endpoints[0].element) < 0){
visibleSystems.push(connection.endpoints[0].element);
for(let connection of allConnections){
if(visibleConnections.indexOf(connection) >= 0){
setConnectionVisible(connection, true);
// source/target system should always be visible -> even if filter scope not matches system type
if(visibleSystems.indexOf(connection.endpoints[0].element) < 0){
visibleSystems.push(connection.endpoints[0].element);
}
if(visibleSystems.indexOf(connection.endpoints[1].element) < 0){
visibleSystems.push(connection.endpoints[1].element);
}
}else{
setConnectionVisible(connection, false);
}
if(visibleSystems.indexOf(connection.endpoints[1].element) < 0){
visibleSystems.push(connection.endpoints[1].element);
}
}else{
setConnectionVisible(connection, false);
}
}
// filter systems -----------------------------------------------------------------------------------------
let visibleTypeIds = [];
if(scopes.indexOf('wh') >= 0){
visibleTypeIds.push(1);
}
if(scopes.indexOf('abyssal') >= 0){
visibleTypeIds.push(4);
}
// filter systems -----------------------------------------------------------------------------------------
let visibleTypeIds = [];
if(scopes.indexOf('wh') >= 0){
visibleTypeIds.push(1);
}
if(scopes.indexOf('abyssal') >= 0){
visibleTypeIds.push(4);
}
for(let system of allSystems){
if(
visibleTypeIds.indexOf($(system).data('typeId')) >= 0 ||
visibleSystems.indexOf(system) >= 0
){
for(let system of allSystems){
if(
visibleTypeIds.indexOf($(system).data('typeId')) >= 0 ||
visibleSystems.indexOf(system) >= 0
){
setSystemVisible(system, map, true);
}else{
setSystemVisible(system, map, false);
}
}
MapOverlayUtil.getMapOverlay(mapElement, 'info').updateOverlayIcon('filter', 'show');
}else{
// clear filter
for(let system of allSystems){
setSystemVisible(system, map, true);
}else{
setSystemVisible(system, map, false);
}
}
for(let connection of allConnections){
setConnectionVisible(connection, true);
}
MapOverlayUtil.getMapOverlay(mapElement, 'info').updateOverlayIcon('filter', 'show');
}else{
// clear filter
for(let system of allSystems){
setSystemVisible(system, map, true);
MapOverlayUtil.getMapOverlay(mapElement, 'info').updateOverlayIcon('filter', 'hide');
}
for(let connection of allConnections){
setConnectionVisible(connection, true);
}
MapOverlayUtil.getMapOverlay(mapElement, 'info').updateOverlayIcon('filter', 'hide');
}
});
}
};
@@ -761,12 +739,11 @@ define([
'height': scrollableHeight ? scaledHeight + 'px' : (wrapperHeight) + 'px',
});
let mapWrapperElement = mapContainer.closest('.mCustomScrollbar');
if(scrollableWidth && scrollableHeight){
mapWrapperElement.mCustomScrollbar('update');
}else{
mapWrapperElement.mCustomScrollbar('scrollTo', '#pf-map-1', {
mapWrapperElement.mCustomScrollbar('scrollTo', '#' + mapContainer.attr('id'), {
scrollInertia: 0,
scrollEasing: 'linear',
timeout: 0,
@@ -844,40 +821,45 @@ define([
* with the addition of respecting active Arrow overlay direction
* @param action
* @param connection
* @param type
* @param types
* @param params
* @param doNotRepaint
*/
let changeConnectionType = (action, connection, type, params = {}, doNotRepaint = false) => {
// check for active Arrow overlay
let overlayArrow, overlayArrowParams;
if(
type !== 'info_signature' &&
connection.hasType('info_signature')
){
overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId);
if(overlayArrow){
overlayArrowParams = {
direction: overlayArrow.direction,
foldback: overlayArrow.foldback,
};
let changeConnectionTypes = (action, connection, types = [], params = [], doNotRepaint = false) => {
if(connection && types.length){
// check for active Arrow overlay
let overlayArrow, overlayArrowParams;
if(
!types.includes('info_signature') &&
connection.hasType('info_signature')
){
overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId);
if(overlayArrow){
overlayArrowParams = {
direction: overlayArrow.direction,
foldback: overlayArrow.foldback,
};
}
}
}
// add the new type
connection[action](type, params, doNotRepaint);
for(let i = 0; i < types.length; i++){
// change the new type
connection[action](types[i], typeof params[i] === 'object' ? params[i] : {}, doNotRepaint);
}
// change Arrow overlay data back to initial direction
if(
overlayArrow &&
(
overlayArrow.direction !== overlayArrowParams.direction ||
overlayArrow.foldback !== overlayArrowParams.foldback
)
){
overlayArrow.updateFrom(overlayArrowParams);
if(!doNotRepaint){
connection.repaint();
// change Arrow overlay data back to initial direction
if(
overlayArrow &&
(
overlayArrow.direction !== overlayArrowParams.direction ||
overlayArrow.foldback !== overlayArrowParams.foldback
)
){
overlayArrow.updateFrom(overlayArrowParams);
if(!doNotRepaint){
connection.repaint();
}
}
}
};
@@ -889,8 +871,12 @@ define([
* @param params
* @param doNotRepaint
*/
let addConnectionType = (connection, type, params = {}, doNotRepaint = false) => {
changeConnectionType('addType', connection, type, params, doNotRepaint);
let addConnectionType = (connection, type, params, doNotRepaint = false) => {
addConnectionTypes(connection, [type], typeof params === 'object' ? [params] : [], doNotRepaint);
};
let addConnectionTypes = (connection, types = [], params = [], doNotRepaint = false) => {
changeConnectionTypes('addType', connection, types, params, doNotRepaint);
};
/**
@@ -900,12 +886,16 @@ define([
* @param params
* @param doNotRepaint
*/
let removeConnectionType = (connection, type, params = {}, doNotRepaint = false) => {
changeConnectionType('removeType', connection, type, params, doNotRepaint);
let removeConnectionType = (connection, type, params, doNotRepaint = false) => {
removeConnectionTypes(connection, [type], typeof params === 'object' ? [params] : [], doNotRepaint);
};
let toggleConnectionType = (connection, type, params = {}, doNotRepaint = false) => {
changeConnectionType('toggleType', connection, type, params, doNotRepaint);
let removeConnectionTypes = (connection, types = [], params = [], doNotRepaint = false) => {
changeConnectionTypes('removeType', connection, types, params, doNotRepaint);
};
let toggleConnectionType = (connection, type, params, doNotRepaint = false) => {
changeConnectionTypes('toggleType', connection, [type], typeof params === 'object' ? [params] : [], doNotRepaint);
};
/**
@@ -1136,10 +1126,10 @@ define([
* @param types
* @returns {string[]}
*/
let getConnectionFakeClassesByTypes = (types) => {
let getConnectionFakeClassesByTypes = types => {
let connectionClasses = ['pf-fake-connection'];
for(let i = 0; i < types.length; i++){
connectionClasses.push(getConnectionInfo( types[i], 'cssClass'));
connectionClasses.push(getConnectionInfo(types[i], 'cssClass'));
}
return connectionClasses;
};
@@ -1147,9 +1137,9 @@ define([
/**
* get all direct connections between two given systems
* @param map
* @param {JQuery} systemA
* @param {JQuery} systemB
* @returns {Array}
* @param systemA
* @param systemB
* @returns {*[]}
*/
let checkForConnection = (map, systemA, systemB) => {
let connections = [];
@@ -1224,6 +1214,31 @@ define([
});
};
/**
* set/change connection jump mass of a wormhole
* @param connection
* @param mass
*/
let setConnectionJumpMassType = (connection, mass) => {
let allMassTypes = ['wh_jump_mass_s', 'wh_jump_mass_m', 'wh_jump_mass_l', 'wh_jump_mass_xl'];
let addMassType = [];
let removeMassTypes = [];
connection._jsPlumb.instance.batch(() => {
if(allMassTypes.includes(mass)){
if(connection.hasType(mass)){
removeMassTypes = allMassTypes;
}else{
addMassType = [mass];
removeMassTypes = allMassTypes.filter(e => e !== mass);
}
removeConnectionTypes(connection, removeMassTypes);
addConnectionTypes(connection, addMassType);
}
});
};
/**
* get some scope info for a given info string
* @param {string} info
@@ -1430,8 +1445,9 @@ define([
};
/**
* set default map Options (
* -> HINT: This function triggers Events! Promise is resolved before trigger completed
* Set default map options (from right menu)
* This function is called only ONCE per map after create!
* -> HINT: This function triggers events! Promise is resolved before trigger callback finishes
* @param mapElement
* @param mapConfig
* @returns {Promise<any>}
@@ -1464,8 +1480,10 @@ define([
// init endpoint overlay --------------------------------------------------------------------------
mapElement.triggerMenuEvent('MapOption', {
option: 'mapEndpoint',
toggle: false
option: 'mapSignatureOverlays',
toggle: false,
skipOnEnable: true, // skip callback -> Otherwise it would run 2 times on map create
skipOnDisable: true // skip callback -> Otherwise it would run 2 times on map create
});
resolve({
@@ -1494,30 +1512,61 @@ define([
/**
* scroll map to default (stored) x/y coordinates
* @param mapElement
* @param map
* @returns {Promise<any>}
*/
let scrollToDefaultPosition = (mapElement) => {
let scrollToDefaultPosition = map => {
let scrollToDefaultPositionExecutor = (resolve, reject) => {
let mapWrapper = mapElement.parents('.' + config.mapWrapperClass);
let scrollToDefaultPositionExecutor = resolve => {
let payload = {
action: 'scrollToDefaultPosition',
data: false
};
// auto scroll map to previous stored position
// no map scroll on zoomed maps -> scrollbar offset on zoomed maps does not work properly
// -> implementation would be difficult...
if(map.getZoom() === 1){
let mapElement = $(map.getContainer());
let promiseStore = getLocaleData('map', mapElement.data('id'));
promiseStore.then(data => {
if(data && data.scrollOffset){
let mapWrapper = mapElement.parents('.' + config.mapWrapperClass);
Scrollbar.scrollToPosition(mapWrapper, [data.scrollOffset.y, data.scrollOffset.x]);
}
resolve(payload);
});
}else{
resolve(payload);
}
};
return new Promise(scrollToDefaultPositionExecutor);
};
/**
* zoom map to default (stored) scale()
* @param map
* @returns {Promise<any>}
*/
let zoomToDefaultScale = map => {
let zoomToDefaultScaleExecutor = resolve => {
let mapElement = $(map.getContainer());
let promiseStore = getLocaleData('map', mapElement.data('id'));
promiseStore.then(data => {
// This code runs once the value has been loaded from offline storage
if(data && data.scrollOffset){
Scrollbar.scrollToPosition(mapWrapper, [data.scrollOffset.y, data.scrollOffset.x]);
if(data && data.mapZoom){
setZoom(map, data.mapZoom);
}
resolve({
action: 'scrollToDefaultPosition',
action: 'zoomToDefaultScale',
data: false
});
});
};
return new Promise(scrollToDefaultPositionExecutor);
return new Promise(zoomToDefaultScaleExecutor);
};
/**
@@ -1825,13 +1874,12 @@ define([
let title = tooltipData.name;
if(tooltipData.size){
title += '&nbsp;<kbd>' + tooltipData.size.label + '</kbd>';
}
if(tooltipData.security){
// K162 has no security
if(!tooltipData.class){
tooltipData.class = Util.getSecurityClassForSystem(tooltipData.security);
}
title += '<span class="pull-right ' + tooltipData.class +'">' + tooltipData.security + '</span>';
}
@@ -1961,7 +2009,6 @@ define([
return {
config: config,
mapOptions: mapOptions,
setMapInstance: setMapInstance,
getMapInstance: getMapInstance,
existsMapInstance: existsMapInstance,
@@ -1996,6 +2043,7 @@ define([
checkForConnection: checkForConnection,
getDefaultConnectionTypeByScope: getDefaultConnectionTypeByScope,
setConnectionWHStatus: setConnectionWHStatus,
setConnectionJumpMassType: setConnectionJumpMassType,
getScopeInfoForConnection: getScopeInfoForConnection,
getDataByConnections: getDataByConnections,
deleteConnections: deleteConnections,
@@ -2016,6 +2064,7 @@ define([
setMapDefaultOptions: setMapDefaultOptions,
getSystemPosition: getSystemPosition,
scrollToDefaultPosition: scrollToDefaultPosition,
zoomToDefaultScale: zoomToDefaultScale,
getSystemId: getSystemId,
checkRight: checkRight,
getMapDeeplinkUrl: getMapDeeplinkUrl

View File

@@ -121,6 +121,25 @@ define([
*/
let initData = () => {
/**
* add wormhole size data for each wormhole
* @param wormholes
* @returns {*}
*/
let addWormholeSizeData = wormholes => {
for(let [wormholeName, wormholeData] of Object.entries(wormholes)){
wormholeData.class = Util.getSecurityClassForSystem(wormholeData.security);
for(let [sizeName, sizeData] of Object.entries(Init.wormholeSizes)){
if(wormholeData.massIndividual >= sizeData.jumpMassMin){
wormholeData.size = sizeData;
break;
}
}
}
return wormholes;
};
let initDataExecutor = (resolve, reject) => {
$.getJSON(Init.path.initData).done(response => {
if( response.error.length > 0 ){
@@ -139,7 +158,7 @@ define([
Init.connectionScopes = response.connectionScopes;
Init.systemStatus = response.systemStatus;
Init.systemType = response.systemType;
Init.wormholes = response.wormholes;
Init.wormholes = addWormholeSizeData(response.wormholes);
Init.characterStatus = response.characterStatus;
Init.routes = response.routes;
Init.url = response.url;

View File

@@ -220,17 +220,7 @@ define([
}),
$('<a>', {
class: 'list-group-item list-group-item-info',
html: '&nbsp;&nbsp;Effect info'
}).prepend(
$('<i>',{
class: 'fas fa-crosshairs fa-fw'
})
).on('click', function(){
$(document).triggerMenuEvent('ShowSystemEffectInfo');
}),
$('<a>', {
class: 'list-group-item list-group-item-info',
html: '&nbsp;&nbsp;Jump info'
html: '&nbsp;&nbsp;Wormhole data'
}).prepend(
$('<i>',{
class: 'fas fa-space-shuttle fa-fw'
@@ -238,6 +228,16 @@ define([
).on('click', function(){
$(document).triggerMenuEvent('ShowJumpInfo');
}),
$('<a>', {
class: 'list-group-item list-group-item-info',
html: '&nbsp;&nbsp;Wormhole effects'
}).prepend(
$('<i>',{
class: 'fas fa-crosshairs fa-fw'
})
).on('click', function(){
$(document).triggerMenuEvent('ShowSystemEffectInfo');
}),
getMenuHeadline('Settings'),
$('<a>', {
class: 'list-group-item',
@@ -379,7 +379,7 @@ define([
})
).on('click', function(){
Util.getMapModule().getActiveMap().triggerMenuEvent('MapOption', {
option: 'mapEndpoint',
option: 'mapSignatureOverlays',
toggle: true
});
}),

View File

@@ -26,13 +26,6 @@ define([
let data = {
config: config,
wormholes: Object.keys(Init.wormholes).map(function(k){ return Init.wormholes[k]; }), // convert Json to array
securityClass: function(){
return function(value, render){
return this.Util.getSecurityClassForSystem( render(value) );
}.bind(this);
}.bind({
Util: Util
}),
massValue: function(){
return function(value, render){
let mass = render(value);

View File

@@ -382,9 +382,7 @@ define([
let statics = [];
for(let wormholeName of cellData){
let wormholeData = Object.assign({}, Init.wormholes[wormholeName]);
let security = wormholeData.security;
let secClass = Util.getSecurityClassForSystem(security);
statics.push('<span class="' + secClass + '">' + security + '</span>');
statics.push('<span class="' + wormholeData.class + '">' + wormholeData.security + '</span>');
}
return statics.join('&nbsp;&nbsp;');
}
@@ -596,7 +594,7 @@ define([
display: (cellData, type, rowData, meta) => {
let connectionClasses = MapUtil.getConnectionFakeClassesByTypes(cellData);
connectionClasses = connectionClasses.join(' ');
return '<div class="pf-fake-connection ' + connectionClasses + '"></div>';
return '<div class="' + connectionClasses + '"></div>';
}
}
},{

View File

@@ -57,7 +57,7 @@ define([
for(let [areaId, areaData] of Object.entries(effectData)){
let systemType = 'C' + areaId;
let securityClass = Util.getSecurityClassForSystem( systemType );
let securityClass = Util.getSecurityClassForSystem(systemType);
if(areaId === '1'){
rows.push( $('<tr>') );

View File

@@ -166,7 +166,7 @@ define([
if(type.includes('wh_critical')){
styleClass.push('pf-wh-critical');
}
if(type.includes('frigate')){
if(type.includes('wh_jump_mass_s')){
styleClass.push('pf-wh-frig');
}
}

View File

@@ -306,22 +306,22 @@ define([
let connection = $().getConnectionById(data.mapId, data.connectionId);
let signatureTypeNames = MapUtil.getConnectionDataFromSignatures(connection, connectionData);
let sourceLabel = signatureTypeNames.source.labels;
let targetLabel = signatureTypeNames.target.labels;
sourceLabelElement.html(MapUtil.formatEndpointOverlaySignatureLabel(sourceLabel.join(', ')));
targetLabelElement.html(MapUtil.formatEndpointOverlaySignatureLabel(targetLabel.join(', ')));
let sourceLabels = signatureTypeNames.source.labels;
let targetLabels = signatureTypeNames.target.labels;
sourceLabelElement.html(MapUtil.formatEndpointOverlaySignatureLabel(sourceLabels));
targetLabelElement.html(MapUtil.formatEndpointOverlaySignatureLabel(targetLabels));
// remove K162
sourceLabel = sourceLabel.diff(['K162']);
targetLabel = targetLabel.diff(['K162']);
sourceLabels = sourceLabels.diff(['K162']);
targetLabels = targetLabels.diff(['K162']);
// get static wormhole data by endpoint Labels
let wormholeName = '';
let wormholeData = null;
if(sourceLabel.length === 1 && targetLabel.length === 0){
wormholeName = sourceLabel[0];
}else if(sourceLabel.length === 0 && targetLabel.length === 1){
wormholeName = targetLabel[0];
if(sourceLabels.length === 1 && targetLabels.length === 0){
wormholeName = sourceLabels[0];
}else if(sourceLabels.length === 0 && targetLabels.length === 1){
wormholeName = targetLabels[0];
}
if(
@@ -329,7 +329,6 @@ define([
Init.wormholes.hasOwnProperty(wormholeName)
){
wormholeData = Object.assign({}, Init.wormholes[wormholeName]);
wormholeData.class = Util.getSecurityClassForSystem(wormholeData.security);
// init wormhole tooltip ----------------------------------------------
let massTotalTooltipCell = tableElement.find('.' + config.connectionInfoTableCellMassTotalTooltipClass);

View File

@@ -143,7 +143,6 @@ define([
){
for(let wormholeName of systemData.statics){
let wormholeData = Object.assign({}, Init.wormholes[wormholeName]);
wormholeData.class = Util.getSecurityClassForSystem(wormholeData.security);
staticsData.push(wormholeData);
}
}

View File

@@ -970,6 +970,16 @@ define([
return this.filter(function(i){return a.indexOf(i) < 0;});
};
/**
* compares two arrays if all elements in a are also in b
* element order is ignored
* @param a
* @returns {boolean}
*/
Array.prototype.equalValues = function(a){
return this.diff(a).concat(a.diff(this)).length === 0;
};
/**
* sort array of objects by property name
* @param p

View File

@@ -1,350 +0,0 @@
;(function() {
var root = this;
var Farahey;
if (typeof exports !== 'undefined') {
Farahey = exports;
} else {
Farahey = root.Farahey = {};
}
var findInsertionPoint = function(sortedArr, val, comparator) {
var low = 0, high = sortedArr.length;
var mid = -1, c = 0;
while(low < high) {
mid = parseInt((low + high)/2);
c = comparator(sortedArr[mid], val);
if(c < 0) {
low = mid + 1;
}else if(c > 0) {
high = mid;
}else {
return mid;
}
}
return low;
},
geomSupport = typeof jsPlumbGeom !== "undefined" ? jsPlumbGeom : Biltong,
insertSorted = function(array, value, comparator) {
var ip = findInsertionPoint(array, value, comparator);
array.splice(ip, 0, value);
},
distanceFromOriginComparator = function(r1, r2, origin) {
var d1 = geomSupport.lineLength(origin, [ r1.x + (r1.w / 2), r1.y + (r1.h / 2)]),
d2 = geomSupport.lineLength(origin, [ r2.x + (r2.w / 2), r2.y + (r2.h / 2)]);
return d1 < d2 ? -1 : d1 == d2 ? 0 : 1;
},
EntryComparator = function(origin, getSize) {
var _origin = origin,
_cache = {},
_get = function(entry) {
if (!_cache[entry[1]]) {
var s = getSize(entry[2]);
_cache[entry[1]] = {
l:entry[0][0],
t:entry[0][1],
w:s[0],
h:s[1],
center:[entry[0][0] + (s[0] / 2), entry[0][1] + (s[1] / 2) ]
};
}
return _cache[entry[1]];
}
this.setOrigin = function(o) {
_origin = o;
_cache = {};
};
this.compare = function(e1, e2) {
var d1 = geomSupport.lineLength(_origin, _get(e1).center),
d2 = geomSupport.lineLength(_origin, _get(e2).center);
return d1 < d2 ? -1 : d1 == d2 ? 0 : 1;
};
};
var _isOnEdge = function(r, axis, dim, v) { return (r[axis] <= v && v <= r[axis] + r[dim]); },
_xAdj = [ function(r1, r2) { return r1.x + r1.w - r2.x; }, function(r1, r2) { return r1.x - (r2.x + r2.w); } ],
_yAdj = [ function(r1, r2) { return r1.y + r1.h - r2.y; }, function(r1, r2) { return r1.y - (r2.y + r2.h); } ],
_adj = [ null, [ _xAdj[0], _yAdj[1] ], [ _xAdj[0], _yAdj[0] ], [ _xAdj[1], _yAdj[0] ], [ _xAdj[1], _yAdj[1] ] ],
_genAdj = function(r1, r2, m, b, s) {
if (isNaN(m)) m = 0;
var y = r2.y + r2.h,
x = (m == Infinity || m == -Infinity) ? r2.x + (r2.w / 2) : (y - b) / m,
theta = Math.atan(m);
if (_isOnEdge(r2, "x", "w", x)) {
var rise = _adj[s][1](r1, r2),
hyp = rise / Math.sin(theta),
run = hyp * Math.cos(theta);
return { left:run, top:rise };
}
else {
var run = _adj[s][0](r1, r2),
hyp = run / Math.cos(theta),
rise = hyp * Math.sin(theta);
return { left:run, top:rise };
}
},
/*
* Calculates how far to move r2 from r1 so that it no longer overlaps.
* if origin is supplied, then it means we want r2 to move along a vector joining r2's center to that point.
* otherwise we want it to move along a vector joining the two rectangle centers.
*/
_calculateSpacingAdjustment = Farahey.calculateSpacingAdjustment = function(r1, r2) {
var c1 = r1.center || [ r1.x + (r1.w / 2), r1.y + (r1.h / 2) ],
c2 = r2.center || [ r2.x + (r2.w / 2), r2.y + (r2.h / 2) ],
m = geomSupport.gradient(c1, c2),
s = geomSupport.quadrant(c1, c2),
b = (m == Infinity || m == -Infinity || isNaN(m)) ? 0 : c1[1] - (m * c1[0]);
return _genAdj(r1, r2, m, b, s);
},
// calculate a padded rectangle for the given element with offset & size, and desired padding.
_paddedRectangle = Farahey.paddedRectangle = function(o, s, p) {
return { x:o[0] - p[0], y: o[1] - p[1], w:s[0] + (2 * p[0]), h:s[1] + (2 * p[1]) };
},
_magnetize = function(positionArray, positions, sizes, padding,
constrain, origin, filter,
updateOnStep, stepInterval, stepCallback)
{
origin = origin || [0,0];
stepCallback = stepCallback || function() { };
var focus = _paddedRectangle(origin, [1,1], padding),
iterations = 100, iteration = 1, uncleanRun = true, adjustBy, constrainedAdjustment,
_movedElements = {},
_move = function(id, o, x, y) {
_movedElements[id] = true;
o[0] += x;
o[1] += y;
},
step = function() {
for (var i = 0; i < positionArray.length; i++) {
var o1 = positions[positionArray[i][1]],
oid = positionArray[i][1],
a1 = positionArray[i][2], // angle to node from magnet origin
s1 = sizes[positionArray[i][1]],
// create a rectangle for first element: this encompasses the element and padding on each
//side
r1 = _paddedRectangle(o1, s1, padding);
if (filter(positionArray[i][1]) && geomSupport.intersects(focus, r1)) {
adjustBy = _calculateSpacingAdjustment(focus, r1);
constrainedAdjustment = constrain(positionArray[i][1], o1, adjustBy);
_move(oid, o1, constrainedAdjustment.left, constrainedAdjustment.top);
}
// now move others to account for this one, if necessary.
// reset rectangle for node
r1 = _paddedRectangle(o1, s1, padding);
for (var j = 0; j < positionArray.length; j++) {
if (i != j) {
var o2 = positions[positionArray[j][1]],
a2 = positionArray[j][2], // angle to node from magnet origin
s2 = sizes[positionArray[j][1]],
// create a rectangle for the second element, again by putting padding of the desired
// amount around the bounds of the element.
r2 = _paddedRectangle(o2, s2, padding);
// if the two rectangles intersect then figure out how much to move the second one by.
if (geomSupport.intersects(r1, r2)) {
// TODO in 0.3, instead of moving neither, the other node should move.
if (filter(positionArray[j][1])) {
uncleanRun = true;
adjustBy = _calculateSpacingAdjustment(r1, r2),
constrainedAdjustment = constrain(positionArray[j][1], o2, adjustBy);
_move(positionArray[j][1], o2, constrainedAdjustment.left, constrainedAdjustment.top);
}
}
}
}
}
if (updateOnStep)
stepCallback();
if (uncleanRun && iteration < iterations) {
uncleanRun = false;
iteration++;
if (updateOnStep) {
window.setTimeout(step, stepInterval);
}
else
step();
}
};
step();
return _movedElements;
};
/**
* @name Magnetizer
* @classdesc Applies repulsive magnetism to a set of elements relative to a given point, with a specified
* amount of padding around the point.
*/
/**
* @name Magnetizer#constructor
* @function
* @param {Selector|Element} [container] Element that contains the elements to magnetize. Only required if you intend to use the `executeAtEvent` method.
* @param {Function} [getContainerPosition] Function that returns the position of the container (as an object of the form `{left:.., top:..}`) when requested. Only required if you intend to use the `executeAtEvent` method.
* @param {Function} getPosition A function that takes an element id and returns its position. It does not matter to which element this position is computed as long as you remain consistent with this method, `setPosition` and the `origin` property.
* @param {Function} setPosition A function that takes an element id and position, and sets it. See note about offset parent above.
* @param {Function} getSize A function that takes an element id and returns its size, in pixels.
* @param {Integer[]} [padding] Optional padding for x and y directions. Defaults to 20 pixels in each direction.
* @param {Function} [constrain] Optional function that takes an id and a proposed amount of movement in each axis, and returns the allowed amount of movement in each axis. You can use this to constrain your elements to a grid, for instance, or a path, etc.
* @param {Integer[]} [origin] The origin of magnetization, in pixels. Defaults to 0,0. You can also supply this to the `execute` call.
* @param {Selector|String[]|Element[]} elements List of elements on which to operate.
* @param {Boolean} [executeNow=false] Whether or not to execute the routine immediately.
* @param {Function} [filter] Optional function that takes an element id and returns whether or not that element can be moved.
* @param {Boolean} [orderByDistanceFromOrigin=false] Whether or not to sort elements first by distance from origin. Can have better results but takes more time.
*/
root.Magnetizer = function(params) {
var getPosition = params.getPosition,
getSize = params.getSize,
getId = params.getId,
setPosition = params.setPosition,
padding = params.padding || [20, 20],
// expects a { left:.., top:... } object. returns how far it can actually go.
constrain = params.constrain || function(id, current, delta) { return delta; },
positionArray = [],
positions = {},
sizes = {},
elements = params.elements || [],
origin = params.origin || [0,0],
executeNow = params.executeNow,
minx, miny, maxx, maxy,
getOrigin = this.getOrigin = function() { return origin; },
filter = params.filter || function(_) { return true; },
orderByDistanceFromOrigin = params.orderByDistanceFromOrigin,
comparator = new EntryComparator(origin, getSize),
updateOnStep = params.updateOnStep,
stepInterval = params.stepInterval || 350,
originDebugMarker,
debug = params.debug,
createOriginDebugger = function() {
var d = document.createElement("div");
d.style.position = "absolute";
d.style.width = "10px";
d.style.height = "10px";
d.style.backgroundColor = "red";
document.body.appendChild(d);
originDebugMarker = d;
},
_addToPositionArray = function(p) {
if (!orderByDistanceFromOrigin || positionArray.length == 0)
positionArray.push(p);
else {
insertSorted(positionArray, p, comparator.compare);
}
},
_updatePositions = function() {
comparator.setOrigin(origin);
positionArray = []; positions = {}; sizes = {};
minx = miny = Infinity;
maxx = maxy = -Infinity;
for (var i = 0; i < elements.length; i++) {
var p = getPosition(elements[i]),
s = getSize(elements[i]),
id = getId(elements[i]);
positions[id] = [p.left, p.top];
_addToPositionArray([ [p.left, p.top], id, elements[i]]);
sizes[id] = s;
minx = Math.min(minx, p.left);
miny = Math.min(miny, p.top);
maxx = Math.max(maxx, p.left + s[0]);
maxy = Math.max(maxy, p.top + s[1]);
}
},
_run = function() {
if (elements.length > 1) {
var _movedElements = _magnetize(positionArray, positions, sizes, padding, constrain, origin, filter, updateOnStep, stepInterval, _positionElements);
_positionElements(_movedElements);
}
},
_positionElements = function(_movedElements) {
for (var i = 0; i < elements.length; i++) {
var id = getId(elements[i]);
if (_movedElements[id])
setPosition(elements[i], { left:positions[id][0], top:positions[id][1] });
}
},
setOrigin = function(o) {
if (o != null) {
origin = o;
comparator.setOrigin(o);
}
};
/**
* @name Magnetizer#execute
* @function
* @desc Runs the magnetize routine.
* @param {Integer[]} [o] Optional origin to use. You may have set this in the constructor and do not wish to supply it, or you may be happy with the default of [0,0].
*/
this.execute = function(o) {
setOrigin(o);
_updatePositions();
_run();
};
/**
* @name Magnetizer#executeAtCenter
* @function
* @desc Computes the center of all the nodes and then uses that as the magnetization origin when it runs the routine.
*/
this.executeAtCenter = function() {
_updatePositions();
setOrigin([
(minx + maxx) / 2,
(miny + maxy) / 2
]);
_run();
};
/**
* @name Magnetizer#executeAtEvent
* @function
* @desc Runs the magnetize routine using the location of the given event as the origin. To use this
* method you need to have provided a `container`, and a `getContainerPosition` function to the
* constructor.
* @param {Event} e Event to get origin location from.
*/
this.executeAtEvent = function(e) {
var c = params.container,
o = params.getContainerPosition(c),
x = e.pageX - o.left + c[0].scrollLeft,
y = e.pageY - o.top + c[0].scrollTop;
if (debug) {
originDebugMarker.style.left = e.pageX + "px";
originDebugMarker.style.top = e.pageY + "px";
}
this.execute([x,y]);
};
/**
* @name Magnetize#setElements
* @function
* @desc Sets the current list of elements.
* @param {Object[]} _els List of elements, in whatever format the magnetizer is setup to use.
*/
this.setElements = function(_els) {
elements = _els;
};
if (debug)
createOriginDebugger();
if (executeNow) this.execute();
};
}).call(this);

View File

@@ -0,0 +1,518 @@
;(function() {
"use strict";
var root = this;
var Farahey = root.Farahey = {};
if (typeof exports !== 'undefined') {
exports.Farahey = Farahey;
}
var findInsertionPoint = function(sortedArr, val, comparator) {
var low = 0, high = sortedArr.length;
var mid = -1, c = 0;
while(low < high) {
mid = parseInt((low + high)/2);
c = comparator(sortedArr[mid], val);
if(c < 0) {
low = mid + 1;
}else if(c > 0) {
high = mid;
}else {
return mid;
}
}
return low;
},
geomSupport = root.Biltong,
insertSorted = function(array, value, comparator) {
var ip = findInsertionPoint(array, value, comparator);
array.splice(ip, 0, value);
},
EntryComparator = function(origin, getSize) {
var _origin = origin,
_cache = {},
_get = function(entry) {
if (!_cache[entry[1]]) {
var s = getSize(entry[2]);
_cache[entry[1]] = {
l:entry[0][0],
t:entry[0][1],
w:s[0],
h:s[1],
center:[entry[0][0] + (s[0] / 2), entry[0][1] + (s[1] / 2) ]
};
}
return _cache[entry[1]];
};
this.setOrigin = function(o) {
_origin = o;
_cache = {};
};
this.compare = function(e1, e2) {
var d1 = geomSupport.lineLength(_origin, _get(e1).center),
d2 = geomSupport.lineLength(_origin, _get(e2).center);
return d1 < d2 ? -1 : d1 == d2 ? 0 : 1;
};
};
var _isOnEdge = function(r, axis, dim, v) { return (r[axis] <= v && v <= r[axis] + r[dim]); },
_xAdj = [ function(r1, r2) { return r1.x + r1.w - r2.x; }, function(r1, r2) { return r1.x - (r2.x + r2.w); } ],
_yAdj = [ function(r1, r2) { return r1.y + r1.h - r2.y; }, function(r1, r2) { return r1.y - (r2.y + r2.h); } ],
_adj = [ null, [ _xAdj[0], _yAdj[1] ], [ _xAdj[0], _yAdj[0] ], [ _xAdj[1], _yAdj[0] ], [ _xAdj[1], _yAdj[1] ] ],
_genAdj = function(r1, r2, m, b, s) {
if (isNaN(m)) m = 0;
var y = r2.y + r2.h,
x = (m == Infinity || m == -Infinity) ? r2.x + (r2.w / 2) : (y - b) / m,
theta = Math.atan(m),
rise, hyp, run;
if (_isOnEdge(r2, "x", "w", x)) {
rise = _adj[s][1](r1, r2);
hyp = rise / Math.sin(theta);
run = hyp * Math.cos(theta);
return { left:run, top:rise };
}
else {
run = _adj[s][0](r1, r2);
hyp = run / Math.cos(theta);
rise = hyp * Math.sin(theta);
return { left:run, top:rise };
}
},
/*
* Calculates how far to move r2 from r1 so that it no longer overlaps.
* if origin is supplied, then it means we want r2 to move along a vector joining r2's center to that point.
* otherwise we want it to move along a vector joining the two rectangle centers.
*/
_calculateSpacingAdjustment = Farahey.calculateSpacingAdjustment = function(r1, r2) {
var c1 = r1.center || [ r1.x + (r1.w / 2), r1.y + (r1.h / 2) ],
c2 = r2.center || [ r2.x + (r2.w / 2), r2.y + (r2.h / 2) ],
m = geomSupport.gradient(c1, c2),
s = geomSupport.quadrant(c1, c2),
b = (m == Infinity || m == -Infinity || isNaN(m)) ? 0 : c1[1] - (m * c1[0]);
return _genAdj(r1, r2, m, b, s);
},
// calculate a padded rectangle for the given element with offset & size, and desired padding.
_paddedRectangle = Farahey.paddedRectangle = function(o, s, p) {
return { x:o[0] - p[0], y: o[1] - p[1], w:s[0] + (2 * p[0]), h:s[1] + (2 * p[1]) };
},
_magnetize = function(positionArray, positions, sizes, padding,
constrain, origin, filter,
updateOnStep, stepInterval, stepCallback, iterations,
exclude, excludeFocus)
{
origin = origin || [0,0];
stepCallback = stepCallback || function() { };
iterations = iterations || 2;
var focus = _paddedRectangle(origin, [1,1], padding),
iteration = 1, uncleanRun = true, adjustBy, constrainedAdjustment,
_movedElements = {},
_move = function(id, o, x, y) {
_movedElements[id] = true;
o[0] += x;
o[1] += y;
},
step = function() {
for (var i = 0; i < positionArray.length; i++) {
if (exclude(positionArray[i][1], positionArray[i][2])) {
continue;
}
var o1 = positions[positionArray[i][1]],
oid = positionArray[i][1],
a1 = positionArray[i][2], // angle to node from magnet origin
s1 = sizes[positionArray[i][1]],
// create a rectangle for first element: this encompasses the element and padding on each
//side
r1 = _paddedRectangle(o1, s1, padding);
if (!excludeFocus && filter(positionArray[i][1], positionArray[i][2]) && geomSupport.intersects(focus, r1)) {
adjustBy = _calculateSpacingAdjustment(focus, r1);
constrainedAdjustment = constrain(positionArray[i][1], o1, adjustBy);
_move(oid, o1, constrainedAdjustment.left, constrainedAdjustment.top);
}
// now move others to account for this one, if necessary.
// reset rectangle for node
r1 = _paddedRectangle(o1, s1, padding);
for (var j = 0; j < positionArray.length; j++) {
if (i != j) {
if (exclude(positionArray[j][1], positionArray[j][2])) {
continue;
}
if (filter(positionArray[j][1], positionArray[j][2])) {
var o2 = positions[positionArray[j][1]],
s2 = sizes[positionArray[j][1]],
// create a rectangle for the second element, again by putting padding of the desired
// amount around the bounds of the element.
r2 = _paddedRectangle(o2, s2, padding);
// if the two rectangles intersect then figure out how much to move the second one by.
if (geomSupport.intersects(r1, r2)) {
// TODO (?), instead of moving neither, the other node should move.
uncleanRun = true;
adjustBy = _calculateSpacingAdjustment(r1, r2);
constrainedAdjustment = constrain(positionArray[j][1], o2, adjustBy);
_move(positionArray[j][1], o2, constrainedAdjustment.left, constrainedAdjustment.top);
}
}
}
}
}
if (updateOnStep)
stepCallback();
if (uncleanRun && iteration < iterations) {
uncleanRun = false;
iteration++;
if (updateOnStep) {
window.setTimeout(step, stepInterval);
}
else
step();
}
};
step();
return _movedElements;
};
var _convertElements = function(l) {
if (l == null) return null;
else if (Object.prototype.toString.call(l) === "[object Array]") {
var a = [];
a.push.apply(a, l);
return a;
}
else {
var o = [];
for (var i in l) o.push(l[i]);
}
return o;
};
/**
* Applies repulsive magnetism to a set of elements relative to a given point, with a specified
* amount of padding around the point.
* @class FaraheyInstance
* @constructor
* @param {Object} params Constructor parameters.
* @param {Selector|Element} [params.container] Element that contains the elements to magnetize. Only required if you intend to use the `executeAtEvent` method.
* @param {Function} [params.getContainerPosition] Function that returns the position of the container (as an object of the form `{left:.., top:..}`) when requested. Only required if you intend to use the `executeAtEvent` method.
* @param {Function} params.getPosition A function that takes an element and returns its position. It does not matter to which element this position is computed as long as you remain consistent with this method, `setPosition` and the `origin` property.
* @param {Function} params.setPosition A function that takes an element and position, and sets it. See note about offset parent above.
* @param {Function} params.getSize A function that takes an element and returns its size, in pixels.
* @param {Number[]} [params.padding] Optional padding for x and y directions. Defaults to 20 pixels in each direction.
* @param {Function} [params.constrain] Optional function that takes an id and a proposed amount of movement in each axis, and returns the allowed amount of movement in each axis. You can use this to constrain your elements to a grid, for instance, or a path, etc.
* @param {Number[]} [params.origin] The origin of magnetization, in pixels. Defaults to 0,0. You can also supply this to the `execute` call.
* @param {Selector|String[]|Element[]} params.elements List, or object hash, of elements on which to operate.
* @param {Boolean} [params.executeNow=false] Whether or not to execute the routine immediately.
* @param {Function} [params.filter] Optional function that takes an element id and returns whether or not that element can be moved.
* @param {Boolean} [params.orderByDistanceFromOrigin=false] Whether or not to sort elements first by distance from origin. Can have better results but takes more time.
*/
var FaraheyInstance = function(params) {
var getPosition = params.getPosition,
getSize = params.getSize,
getId = params.getId,
setPosition = params.setPosition,
padding = params.padding || [20, 20],
// expects a { left:.., top:... } object. returns how far it can actually go.
constrain = params.constrain || function(id, current, delta) { return delta; },
positionArray = [],
positions = {},
sizes = {},
elements = _convertElements(params.elements || []),
origin = params.origin || [0,0],
executeNow = params.executeNow,
//minx, miny, maxx, maxy,
getOrigin = this.getOrigin = function() { return origin; },
filter = params.filter || function(_) { return true; },
exclude = params.exclude || function(_) { return false;},
orderByDistanceFromOrigin = params.orderByDistanceFromOrigin,
comparator = new EntryComparator(origin, getSize),
updateOnStep = params.updateOnStep,
stepInterval = params.stepInterval || 350,
originDebugMarker,
debug = params.debug,
createOriginDebugger = function() {
var d = document.createElement("div");
d.style.position = "absolute";
d.style.width = "10px";
d.style.height = "10px";
d.style.backgroundColor = "red";
document.body.appendChild(d);
originDebugMarker = d;
},
_addToPositionArray = function(p) {
if (!orderByDistanceFromOrigin || positionArray.length == 0)
positionArray.push(p);
else {
insertSorted(positionArray, p, comparator.compare);
}
},
_computeExtents = function(els) {
var minx, miny, maxx, maxy;
minx = miny = Infinity;
maxx = maxy = -Infinity;
for (var i = 0; i < els.length; i++) {
var p = getPosition(els[i]),
s = getSize(els[i]),
id = getId(els[i]);
positions[id] = [p.left, p.top];
_addToPositionArray([ [p.left, p.top], id, els[i]]);
sizes[id] = s;
minx = Math.min(minx, p.left);
miny = Math.min(miny, p.top);
maxx = Math.max(maxx, p.left + s[0]);
maxy = Math.max(maxy, p.top + s[1]);
}
return [ minx, maxx, miny, maxy ];
},
_updatePositions = function() {
comparator.setOrigin(origin);
positionArray = []; positions = {}; sizes = {};
return _computeExtents(elements);
},
_run = function(options) {
if (elements.length > 1) {
options = options || {};
var f = options.filter || filter;
var p = options.padding || padding;
var i = options.iterations;
var e = options.exclude || exclude;
var ef = options.excludeFocus;
var _movedElements = _magnetize(positionArray, positions, sizes, p, constrain, origin, f, updateOnStep, stepInterval, _positionElements, i, e, ef);
_positionElements(_movedElements);
}
},
_positionElements = function(_movedElements) {
for (var i = 0; i < elements.length; i++) {
var id = getId(elements[i]);
if (_movedElements[id])
setPosition(elements[i], { left:positions[id][0], top:positions[id][1] });
}
},
setOrigin = function(o) {
if (o != null) {
origin = o;
comparator.setOrigin(o);
}
};
/**
* Runs the magnetize routine.
* @method execute
* @param {Number[]} [o] Optional origin to use. You may have set this in the constructor and do not wish to supply it, or you may be happy with the default of [0,0].
* @param {Function} [options] Options to override defaults.
* @param {Function} [options.filter] Optional function to indicate whether a given element may be moved or not. Returning boolean false indicates it may not.
* @param {Number[]} [options.padding] Optional [x,y] padding values for elements.
* @param {Number} [options.iterations] Optional max number of iterations to run. The greater this number, the more comprehensive the magnetisation,
* but the slower it runs. The default is 2.
* @param {Function} [options.exclude] Optional function to return whether or not a given element should be completely excluded from the magnetisation: it neither
* moves, nor has any bearing on the movement of other elements.
* @param {Boolean} [options.excludeFocus=false] If true, do not pad any elements around the focus point.
*/
this.execute = function(o, options) {
setOrigin(o);
_updatePositions();
_run(options);
};
/**
* Computes the center of all the nodes and then uses that as the magnetization origin when it runs the routine.
* @method executeAtCenter
* @param {Function} [options] Options to override defaults.
* @param {Function} [options.filter] Optional function to indicate whether a given element may be moved or not. Returning boolean false indicates it may not.
* @param {Number[]} [options.padding] Optional [x,y] padding values for elements.
* @param {Number} [options.iterations] Optional max number of iterations to run. The greater this number, the more comprehensive the magnetisation,
* but the slower it runs. The default is 2.
* @param {Function} [options.exclude] Optional function to return whether or not a given element should be completely excluded from the magnetisation: it neither
* moves, nor has any bearing on the movement of other elements.
* @param {Boolean} [options.excludeFocus=false] If true, do not pad any elements around the focus point.
*/
this.executeAtCenter = function(options) {
var extents = _updatePositions();
setOrigin([
(extents[0] + extents[1]) / 2,
(extents[2] + extents[3]) / 2
]);
_run(options);
};
/**
* Runs the magnetize routine using the location of the given event as the origin. To use this
* method you need to have provided a `container`, and a `getContainerPosition` function to the
* constructor.
* @method executeAtEvent
* @param {Event} e Event to get origin location from.
* @param {Function} [options] Options to override defaults.
* @param {Function} [options.filter] Optional function to indicate whether a given element may be moved or not. Returning boolean false indicates it may not.
* @param {Number[]} [options.padding] Optional [x,y] padding values for elements.
* @param {Number} [options.iterations] Optional max number of iterations to run. The greater this number, the more comprehensive the magnetisation,
* but the slower it runs. The default is 2.
* @param {Function} [options.exclude] Optional function to return whether or not a given element should be completely excluded from the magnetisation: it neither
* moves, nor has any bearing on the movement of other elements.
* @param {Boolean} [options.excludeFocus=false] If true, do not pad any elements around the focus point.
*/
this.executeAtEvent = function(e, options) {
var c = params.container,
o = params.getContainerPosition(c),
x = e.pageX - o.left + c.scrollLeft,
y = e.pageY - o.top + c.scrollTop;
if (debug) {
originDebugMarker.style.left = e.pageX + "px";
originDebugMarker.style.top = e.pageY + "px";
}
this.execute([x,y], options);
};
/**
* Sets the current set of elements on which to operate.
* @method setElements
* @param {Object[]|Object} _els List, or object hash, of elements, in whatever format the Magnetizer is setup to use. If you supply an object hash then a list is generated from the hash's values (the keys are ignored).
*/
this.setElements = function(_els) {
elements = _convertElements(_els);
return this;
};
/**
* Adds the given element to the set of elements on which to operate.
* @method addElement
* @param el {Object} Element to add.
* @param {Boolean} [doNotTestForDuplicates=false] If true, we skip the check for duplicates. This makes
* for a much faster call when there are lots of elements, just use it with care.
*/
this.addElement = function(el, doNotTestForDuplicates) {
if (el != null && (doNotTestForDuplicates || elements.indexOf(el) === -1)) {
elements.push(el);
}
return this;
};
/**
* Adds the given elements to the set of elements on which to operate.
* @method addElements
* @param els {Object[]} Elements to add.
* @param {Boolean} [doNotTestForDuplicates=false] If true, we skip the check for duplicates. This makes
* for a much faster call when there are lots of elements, just use it with care.
*/
this.addElements = function(els, doNotTestForDuplicates) {
if (doNotTestForDuplicates) {
Array.prototype.push.apply(elements, els);
}
else {
for (var i = 0; i < els.length; i++) {
this.addElement(els[i]);
}
}
return this;
};
/**
* Gets the list of elements currently being managed.
* @method getElements
*/
this.getElements = function() {
return elements;
};
/**
* Removes the given element from the set of elements on which to operate.
* @method removeElement
* @param el {Object} Element to remove.
*/
this.removeElement = function(el) {
var idx = -1;
for (var i = 0; i < elements.length; i++) {
if (elements[i] == el) {
idx = i; break;
}
}
if (idx != -1) elements.splice(idx, 1);
return this;
};
/**
* Sets the padding to insert between magnetized elements.
* @method setPadding
* @param {Number[]} p Array of padding for each axis.
*/
this.setPadding = function(p) {
padding = p;
};
/**
* Sets the function used to constrain the movement of some element that the magnetizer wishes to relocate.
* The function is given an element ID and an array of [x,y] values, where each value indicates the proposed amount
* of movement in the given axis. The function is expected to return an array of [x,y] that indicates the allowed
* amount of movement in each axis.
* @method setConstrain
* @param {Function} c
*/
this.setConstrain = function(c) {
constrain = c;
};
/**
* Sets the function used to determine whether or not a given element should be considered during the magnetization process.
* @method setFilter
* @param {Function} f Filter function to use. Takes an element ID and returns whether or not that element can be moved.
*/
this.setFilter = function(f) {
filter = f;
};
/**
* Reset the Farahey instance. Use this to avoid memory leaks.
* @method reset
*/
this.reset = function() {
elements.length = 0;
};
if (debug)
createOriginDebugger();
if (executeNow) this.execute();
return this;
};
/**
* Gets a new FaraheyInstance
* @method
* @param {Object} params Method parameters.
* @param {Selector|Element} [params.container] Element that contains the elements to magnetize. Only required if you intend to use the `executeAtEvent` method.
* @param {Function} [params.getContainerPosition] Function that returns the position of the container (as an object of the form `{left:.., top:..}`) when requested. Only required if you intend to use the `executeAtEvent` method.
* @param {Function} params.getPosition A function that takes an element and returns its position. It does not matter to which element this position is computed as long as you remain consistent with this method, `setPosition` and the `origin` property.
* @param {Function} params.setPosition A function that takes an element and position, and sets it. See note about offset parent above.
* @param {Function} params.getSize A function that takes an element and returns its size, in pixels.
* @param {Number[]} [params.padding] Optional padding for x and y directions. Defaults to 20 pixels in each direction.
* @param {Function} [params.constrain] Optional function that takes an id and a proposed amount of movement in each axis, and returns the allowed amount of movement in each axis. You can use this to constrain your elements to a grid, for instance, or a path, etc.
* @param {Number[]} [params.origin] The origin of magnetization, in pixels. Defaults to 0,0. You can also supply this to the `execute` call.
* @param {Selector|String[]|Element[]} params.elements List, or object hash, of elements on which to operate.
* @param {Boolean} [params.executeNow=false] Whether or not to execute the routine immediately.
* @param {Function} [params.filter] Optional function that takes an element id and returns whether or not that element can be moved.
* @param {Boolean} [params.orderByDistanceFromOrigin=false] Whether or not to sort elements first by distance from origin. Can have better results but takes more time.
*/
Farahey.getInstance = function(params) {
return new FaraheyInstance(params);
};
}).call(typeof window !== 'undefined' ? window : this);

View File

@@ -67,8 +67,12 @@ $.fn.dragToSelect = function (conf) {
onRefresh: function () {return true;}
}, c);
var realParent = $(this);
var parent = realParent;
var realParent = $(this);
var parent = realParent;
// container for lasso element
// -> the only reason for NOT using the .pf-map is because of the zoom [scale()] feature or .pf-map
var lassoContainer = realParent.parent();
var animationFrameId;
var mouseIsDown = false;
@@ -124,7 +128,7 @@ $.fn.dragToSelect = function (conf) {
// Create select box
var selectBox = $('<div>')
.appendTo(parent)
.appendTo(lassoContainer)
.attr('class', config.className)
.css('position', 'absolute');
@@ -156,20 +160,6 @@ $.fn.dragToSelect = function (conf) {
return refreshed;
}
// get scroll position
/*
var leftScroll = 0;
var topScroll = 0;
if(realParent.attr('data-scroll-left')){
leftScroll = parseInt(realParent.attr('data-scroll-left'));
}
if(realParent.attr('data-scroll-top')){
topScroll = parseInt(realParent.attr('data-scroll-top'));
}
*/
var left = lastMousePosition.x - parentDim.left + parent[0].scrollLeft;
var top = lastMousePosition.y - parentDim.top + parent[0].scrollTop;
var tempWidth = selectBoxOrigin.left - left;
@@ -220,30 +210,6 @@ $.fn.dragToSelect = function (conf) {
}
};
// Scrolls parent if needed
var scrollPerhaps = function (e) {
if (!selectBox.is('.' + config.activeClass) || parent.is('.' + config.disabledClass)) {
return;
}
// Scroll down
if ((e.pageY + config.scrollTH) > (parentDim.top + parentDim.height)) {
parent[0].scrollTop += config.scrollTH;
}
// Scroll up
if ((e.pageY - config.scrollTH) < parentDim.top) {
parent[0].scrollTop -= config.scrollTH;
}
// Scroll right
if ((e.pageX + config.scrollTH) > (parentDim.left + parentDim.width)) {
parent[0].scrollLeft += config.scrollTH;
}
// Scroll left
if ((e.pageX - config.scrollTH) < parentDim.left) {
parent[0].scrollLeft -= config.scrollTH;
}
};
// Selects all the elements in the select box's range
var selectElementsInRange = function () {
if (!selectBox.is('.' + config.activeClass) || parent.is('.' + config.disabledClass)) {

View File

@@ -4,11 +4,12 @@
<tr>
<th class=""></th>
<th class="text-center">Leads to</th>
<th class="text-center pf-table-cell-10" title="is static"><i class="fas fa-thumbtack"></i></th>
<th class="text-center">Size</th>
<th class="text-center" title="is static"><i class="fas fa-thumbtack"></i></th>
<th class="text-right">Mass total</th>
<th class="text-right">Mass individual</th>
<th class="text-right">Mass regeneration</th>
<th class="text-right">Lifetime</th>
<th class="text-right" title="lifetime"><i class="fas fa-hourglass-start"></i></th>
<th class="text-right">Sig strength</th>
</tr>
</thead>
@@ -16,7 +17,8 @@
{{#wormholes}}
<tr>
<td>{{name}}</td>
<td class="text-center {{#securityClass}}{{security}}{{/securityClass}}">{{security}}</td>
<td class="text-center {{class}}">{{security}}</td>
<td class="text-center {{#size}}{{size.class}}{{/size}}" data-order="{{size.jumpMassMin}}">{{#size}}{{size.label}}{{/size}}</td>
<td class="text-center" data-order="{{static}}">{{#formatStatic}}{{static}}{{/formatStatic}}</td>
<td class="text-right" data-order="{{massTotal}}">{{#massValue}}{{massTotal}}{{/massValue}}</td>
<td class="text-right" data-order="{{massIndividual}}">{{#massValue}}{{massIndividual}}{{/massValue}}</td>

View File

@@ -177,7 +177,7 @@
<tr>
<td class="col-sm-2 text-center">k-space</td>
<td></td>
<td class="col-sm-3 text-right">security</td>
<td></td>
</tr>
</thead>
<tbody>
@@ -210,7 +210,7 @@
<tr>
<td class="col-sm-2 text-center">w-space</td>
<td></td>
<td class="col-sm-3 text-right">security</td>
<td></td>
</tr>
</thead>
<tbody>
@@ -237,7 +237,7 @@
<tr>
<td class="col-sm-2 text-center">w-space</td>
<td></td>
<td class="col-sm-3 text-right">security</td>
<td></td>
</tr>
</thead>
<tbody>
@@ -393,42 +393,50 @@
The <em>"Scope"</em> of a connection can be changed by using the context menu of a connection <small>(<a href="#" data-target="#pf-manual-scrollspy-anchor-connection-contextmenu">more</a>)</small>.
</p>
<ul class="list-unstyled" style=" margin-left: 10px;">
<li><div class="pf-fake-connection"></div>&nbsp;&nbsp;Wormhole<small> (Wormhole has not yet had its stability significantly disrupted)</small></li>
<li><div class="pf-fake-connection pf-map-connection-stargate"></div>&nbsp;&nbsp;Stargate<small> (Stargates are static <em>"K-space"</em> connections)</small></li>
<li><div class="pf-fake-connection pf-map-connection-jumpbridge"></div>&nbsp;&nbsp;Jumpbridge<small> (Jumpbridges are player build <em>"K-space"</em> connections)</small></li>
<li><div class="pf-fake-connection pf-map-connection-abyssal"></div>&nbsp;&nbsp;Abyssal<small> (abyssal zone entrance <em>"a-space"</em> connections)</small></li>
<li><div class="pf-fake-connection"></div>&nbsp;&nbsp;Wormhole - <em>"k-space"</em> connections</li>
<li><div class="pf-fake-connection pf-map-connection-stargate"></div>&nbsp;&nbsp;Stargate - static <em>"k-space"</em> connections</li>
<li><div class="pf-fake-connection pf-map-connection-jumpbridge"></div>&nbsp;&nbsp;Jumpbridge - player build <em>"k-space"</em> connections</li>
<li><div class="pf-fake-connection pf-map-connection-abyssal"></div>&nbsp;&nbsp;Abyssal - PVE zone entrance <em>"a-space"</em> connections</li>
</ul>
</div>
<div class="col-xs-12 col-md-6">
<h4 id="pf-manual-scrollspy-anchor-connection-status">Connection status</h4>
<h4 id="pf-manual-scrollspy-anchor-connection-status">Connection mass status</h4>
<p>
Wormholes will gain various statuses during its <em>"Lifetime"</em>.<br>
In addition to this, connections can get custom statuses.<br>
Wormholes go through various mass stages over lifetime.<br>
To reflect the mass status on map, wormholes can be flagged.<br>
The status can be changed by using the context menu <small>(<a href="#" data-target="#pf-manual-scrollspy-anchor-connection-contextmenu">more</a>)</small>.
</p>
<ul class="list-unstyled" style=" margin-left: 10px;">
<li><div class="pf-fake-connection pf-map-connection-wh-eol"></div> end of life<small> (Wormhole is the end of its natural lifetime)</small></li>
<li><div class="pf-fake-connection pf-map-connection-wh-reduced"></div> reduced<small> (Wormhole had its stability reduced, but not to a critical degree) &lt;50% mass left</small></li>
<li><div class="pf-fake-connection pf-map-connection-wh-critical"></div> critical<small> (Wormhole is on the verge of collapse) &lt;10% mass left</small></li>
<li><div class="pf-fake-connection pf-map-connection-frig"></div> Frigate hole<small> (Only the smallest ships pass through)</small></li>
<li><div class="pf-fake-connection"></div>&nbsp;&nbsp;fresh - <em>"… has not yet had its stability significantly disrupted"</em> (>50%)</li>
<li><div class="pf-fake-connection pf-map-connection-wh-reduced"></div>&nbsp;&nbsp;reduced - <em>"… had its stability reduced, but not to a critical degree"</em> (50% - 10%)</li>
<li><div class="pf-fake-connection pf-map-connection-wh-critical"></div>&nbsp;&nbsp;critical - <em>"… is on the verge of collapse"</em> (&lt;10%)</li>
</ul>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-6">
<h4 id="pf-manual-scrollspy-anchor-connection-label"><i class="fas fa-tag fa-fw"></i> Connection label</h4>
<h4 id="pf-manual-scrollspy-anchor-connection-size">Connection ship size</h4>
<p>
In addition to its <em>"Lifetime Status" </em><small>(<a href="#" data-target="#pf-manual-scrollspy-anchor-connection-status">more</a>)</small>, connections can be labeled with custom labels
to supply further information.<br>
Labels can be set by using the context menu <small>(<a href="#" data-target="#pf-manual-scrollspy-anchor-connection-contextmenu">more</a>)</small>.
Wormhole connections have a "max jump mass". Ships whose total mass exceed this mass, can not go through the hole.<br>
The size of a wormhole can be set by using the context menu <small>(<a href="#" data-target="#pf-manual-scrollspy-anchor-connection-contextmenu">more</a>)</small>.
</p>
<ul class="list-unstyled" style=" margin-left: 10px;">
<li><span class="label label-warning" style="color: #1d1d1d">frig</span> Frigate hole<small> (Only the smallest ships pass through)</small></li>
<li style="margin-top: 3px;"><span class="label label-danger"><i class="fas fa-exclamation-triangle"></i> save mass</span> save mass<small> (Wormhole mass has to be saved)</small></li>
<ul class="fa-ul">
<li><i class="fa-li fas fa-char pf-jump-mass-s" data-char-content="S"></i> Small - <em>"Only the smallest ships can pass through this wormhole"</em> (Frigates, Destroyers,…)</li>
<li><i class="fa-li fas fa-char pf-jump-mass-m" data-char-content="M"></i> Medium - <em>"Up to medium size ships can pass through this wormhole"</em> (Nestors, BCs,…)</li>
<li><i class="fa-li fas fa-char pf-jump-mass-l" data-char-content="L"></i> Large - <em>"Larger ships can pass through this wormhole"</em> (BSs, Orcas,…)</li>
<li><i class="fa-li fas fa-char pf-jump-mass-xl" data-char-content="XL"></i> Capital - <em>"Very large ships can pass through this wormhole"</em> (FAXes, Dreads, Freighters,…)</li>
</ul>
</div>
<div class="col-xs-12 col-md-6">
<h4 id="pf-manual-scrollspy-anchor-connection-label">Connection lifetime</h4>
<p>
The lifetime information of a wormhole indicates roughly what kind of time is remaining on the wormhole before it dies of natural causes.<br>
Wormholes set to "EOL" (End Of Lifetime) can automatically be removed from the map 4h later on (check map settings).<br>
EOL status can be set by using the context menu <small>(<a href="#" data-target="#pf-manual-scrollspy-anchor-connection-contextmenu">more</a>)</small>.
</p>
<ul class="list-unstyled" style=" margin-left: 10px;">
<li><div class="pf-fake-connection pf-map-connection-wh-eol"></div>&nbsp;&nbsp;EOL - <em>"Wormhole is reaching the end of its natural lifetime"</em> (less than 4 hours)</li>
</ul>
</div>
</div>
<h4 id="pf-manual-scrollspy-anchor-connection-contextmenu">Context menu (connection)</h4>
@@ -436,32 +444,24 @@
<kbd>right click</kbd> a connection on the map to open the context menu.
</p>
<ul class="list-unstyled well" style=" margin-left: 10px;">
<li><i class="fas fa-plane fa-fw"></i> Toggles this connection as <em>"Frigate Hole"</em> <small><a href="#" data-target="#pf-manual-scrollspy-anchor-connection-frig">more</a></small></li>
<li><i class="fas fa-exclamation-triangle fa-fw"></i> Toggles this connection as <em>"Preserve Mass"</em> <small><a href="#" data-target="#pf-manual-scrollspy-anchor-connection-mass">more</a></small></li>
<li><i class="fas fa-crosshairs fa-fw"></i> Changes the scope of this connection <small><a href="#" data-target="#pf-manual-scrollspy-anchor-connection-scope">more</a></small></li>
<li><i class="fas fa-reply fa-rotate-180 fa-fw"></i> Changes the status of this connection <small><a href="#" data-target="#pf-manual-scrollspy-anchor-connection-status">more</a></small></li>
<li><i class="fas fa-trash fa-fw"></i> Delete this connection <small><a href="#" data-target="#pf-manual-scrollspy-anchor-connection-delete">more</a></small></li>
<li><i class="fas fa-hourglass-end fa-fw"></i> Toggle "EOL" (End Of Lifetime). Less than 4 hours until collapse <small><a href="#" data-target="#pf-manual-scrollspy-anchor-connection-frig">more</a></small></li>
<li><i class="fas fa-exclamation-triangle fa-fw"></i> Toggle "Preserve Mass" for connection <small><a href="#" data-target="#pf-manual-scrollspy-anchor-connection-mass">more</a></small></li>
<li><i class="fas fa-reply fa-rotate-180 fa-fw"></i> Set "mass status". Total mass passed through ['fresh', 'reduced', 'critical'] <small><a href="#" data-target="#pf-manual-scrollspy-anchor-connection-status">more</a></small></li>
<li><i class="fas fa-reply fa-rotate-180 fa-fw"></i> Set "ship size" for connection (mass per jump) ['S', 'M', 'L', 'XL'] <small><a href="#" data-target="#pf-manual-scrollspy-anchor-connection-size">more</a></small></li>
<li><i class="fas fa-crosshairs fa-fw"></i> Changes connection scope ['wormhole', 'stargate', 'jumpbridge'] <small><a href="#" data-target="#pf-manual-scrollspy-anchor-connection-scope">more</a></small></li>
<li><i class="fas fa-unlink fa-fw"></i> Delete/detach this connection <small><a href="#" data-target="#pf-manual-scrollspy-anchor-connection-delete">more</a></small></li>
</ul>
<div class="row">
<div class="col-xs-12 col-md-6">
<h4 id="pf-manual-scrollspy-anchor-connection-frig"><i class="fas fa-plane fa-fw"></i> Frigate hole</h4>
<p>
Wormholes can be labeled <small>(<i class="fas fa-tag fa-fw"></i><a href="#" data-target="#pf-manual-scrollspy-anchor-connection-label">more</a>)</small> as <em>"Frigate hole"</em>.
Only frigate-sized Spaceships can pass through a <em>"Frigate hole"</em> <small>(<a href="#" data-target="#pf-manual-scrollspy-anchor-connection-status">more</a>)</small>.
</p>
</div>
<div class="col-xs-12 col-md-6">
<h4 id="pf-manual-scrollspy-anchor-connection-mass"><i class="fas fa-exclamation-triangle fa-fw"></i> Preserve mass</h4>
<p>
Wormholes can be labeled <small>(<i class="fas fa-tag fa-fw"></i><a href="#" data-target="#pf-manual-scrollspy-anchor-connection-label">more</a>)</small> as <em>"Preserve mass"</em>.
Wormholes can be labeled with a&nbsp;&nbsp;<span class="label label-danger"><i class="fas fa-exclamation-triangle"></i> save mass</span> flag.<br>
Let your mates know about critical connections that should be mass-saved
(e.g. <span class="pf-system-sec-highSec">H</span> security exits) <small>(<a href="#" data-target="#pf-manual-scrollspy-anchor-system-security">more</a>)</small>.
(e.g. <span class="pf-system-sec-highSec">H</span> security exits).
</p>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-6">
<h4 id="pf-manual-scrollspy-anchor-connection-delete"><i class="fas fa-trash fa-fw"></i> Delete connection</h4>
<h4 id="pf-manual-scrollspy-anchor-connection-delete"><i class="fas fa-unlink fa-fw"></i> Delete/detach connection</h4>
<p>
Connections can be detached by several ways.
</p>
@@ -472,9 +472,6 @@
<li>Delete Systems: <small>Delete multiple systems by using the context menu of a map (<a href="#" data-target="#pf-manual-scrollspy-anchor-map-contextmenu">more</a>)</small></li>
</ul>
</div>
<div class="col-xs-12 col-md-6">
</div>
</div>
<h4 id="pf-manual-endpoint">Endpoints</h4>
<p>
@@ -514,9 +511,9 @@
Anytime a new signature is added to the <em>"Signature table"</em> the <em>"Progress bar"</em> will be updated automatically. The bar color indicates the progress.
</p>
<ul class="fa-ul">
<li><i class="fa-li fas fa-circle txt-color txt-color-danger"></i></span> not scanned <small>(System scanned to &lt;30%)</small></li>
<li><i class="fa-li fas fa-circle txt-color txt-color-warning"></i></span> bad scanned <small>(System scanned to &lt;100%)</small></li>
<li><i class="fa-li fas fa-circle txt-color txt-color-success"></i></span> full scanned <small>(System scanned to 100%)</small></li>
<li><i class="fa-li fas fa-circle txt-color txt-color-danger"></i> not scanned <small>(System scanned to &lt;30%)</small></li>
<li><i class="fa-li fas fa-circle txt-color txt-color-warning"></i> bad scanned <small>(System scanned to &lt;100%)</small></li>
<li><i class="fa-li fas fa-circle txt-color txt-color-success"></i> full scanned <small>(System scanned to 100%)</small></li>
</ul>
</div>
<div class="col-xs-12 col-md-6">
@@ -594,24 +591,50 @@
</p>
<div class="row">
<div class="col-xs-12 col-md-6">
<h4><i class="fas fa-align-left fa-fw"></i> Trade hub range</h4><p>
<h4><i class="fas fa-align-left fa-fw"></i> Routes</h4><p>
<p>
The <em>"Trade hub range"</em> table is available for any <em>"K-space"</em> system. Each row represents the jump distance to a main <em>"Trade-Hub"</em>.
The <em>"Route"</em> search module is available for any active system <small>(<a href="#" data-target="#pf-manual-scrollspy-anchor-system-active">more</a>)</small>.
Each system jump is represented by a <i class="fas fa-square fa-fw"></i> with the color of its <em>"Security status"</em> <small>(<a href="#" data-target="#pf-manual-scrollspy-anchor-system-security">more</a>)</small>.
</p>
<ul class="fa-ul">
<li><i class="fa-li fas fa-square pf-system-security-1-0"></i></span> <samp>1-0</samp></li>
<li><i class="fa-li fas fa-square pf-system-security-0-9"></i></span> <samp>0.9</samp></li>
<li><i class="fa-li fas fa-square pf-system-security-0-8"></i></span> <samp>0.8</samp></li>
<li><i class="fa-li fas fa-square pf-system-security-0-7"></i></span> <samp>0.7</samp></li>
<li><i class="fa-li fas fa-square pf-system-security-0-6"></i></span> <samp>0.6</samp></li>
<li><i class="fa-li fas fa-square pf-system-security-0-5"></i></span> <samp>0.5</samp></li>
<li><i class="fa-li fas fa-square pf-system-security-0-4"></i></span> <samp>0.4</samp></li>
<li><i class="fa-li fas fa-square pf-system-security-0-3"></i></span> <samp>0.3</samp></li>
<li><i class="fa-li fas fa-square pf-system-security-0-2"></i></span> <samp>0.2</samp></li>
<li><i class="fa-li fas fa-square pf-system-security-0-1"></i></span> <samp>0.1</samp></li>
<li><i class="fa-li fas fa-square pf-system-security-0-0"></i></span> <samp>0.0 - -1.0</samp></li>
</ul>
<table class="table table-condensed" style="font-size: 12px; width: 90%">
<thead>
<tr>
<td colspan="6">&nbsp;&nbsp;&nbsp;<i class="fas fa-fw fa-char pf-system-sec-highSec" data-char-content="H"></i></td>
<td colspan="4">&nbsp;&nbsp;&nbsp;<i class="fas fa-fw fa-char pf-system-sec-lowSec" data-char-content="L"></i></td>
<td colspan="2">&nbsp;&nbsp;&nbsp;&nbsp;<i class="fas fa-fw fa-char pf-system-sec-nullSec" data-char-content="0.0"></i></td>
</tr>
</thead>
<tbody>
<tr>
<td class="col-xs-1 text-center"><i class="fas fa-square pf-system-security-1-0"></i></td>
<td class="col-xs-1 text-center"><i class="fas fa-square pf-system-security-0-9"></i></td>
<td class="col-xs-1 text-center"><i class="fas fa-square pf-system-security-0-8"></i></td>
<td class="col-xs-1 text-center"><i class="fas fa-square pf-system-security-0-7"></i></td>
<td class="col-xs-1 text-center"><i class="fas fa-square pf-system-security-0-6"></i></td>
<td class="col-xs-1 text-center"><i class="fas fa-square pf-system-security-0-5"></i></td>
<td class="col-xs-1 text-center"><i class="fas fa-square pf-system-security-0-4"></i></td>
<td class="col-xs-1 text-center"><i class="fas fa-square pf-system-security-0-3"></i></td>
<td class="col-xs-1 text-center"><i class="fas fa-square pf-system-security-0-2"></i></td>
<td class="col-xs-1 text-center"><i class="fas fa-square pf-system-security-0-1"></i></td>
<td class="col-xs-1 text-center"><i class="fas fa-square pf-system-security-0-0"></i></td>
<td class="col-xs-1 text-center"><i class="fas fa-square pf-system-security-0-0"></i></td>
</tr>
<tr>
<td class="col-xs-1 text-center"><samp class="pf-system-security-1-0">1-0</samp></td>
<td class="col-xs-1 text-center"><samp class="pf-system-security-0-9">0.9</samp></td>
<td class="col-xs-1 text-center"><samp class="pf-system-security-0-8">0.8</samp></td>
<td class="col-xs-1 text-center"><samp class="pf-system-security-0-7">0.7</samp></td>
<td class="col-xs-1 text-center"><samp class="pf-system-security-0-6">0.6</samp></td>
<td class="col-xs-1 text-center"><samp class="pf-system-security-0-5">0.5</samp></td>
<td class="col-xs-1 text-center"><samp class="pf-system-security-0-4">0.4</samp></td>
<td class="col-xs-1 text-center"><samp class="pf-system-security-0-3">0.3</samp></td>
<td class="col-xs-1 text-center"><samp class="pf-system-security-0-2">0.2</samp></td>
<td class="col-xs-1 text-center"><samp class="pf-system-security-0-1">0.1</samp></td>
<td class="col-xs-1 text-center"><samp class="pf-system-security-0-0">0.0</samp></td>
<td class="col-xs-1 text-center"><samp class="pf-system-security-0-0">-1.0</samp></td>
</tr>
</tbody>
</table>
</div>
<div class="col-xs-12 col-md-6">
<h4><i class="fas fa-chart-bar fa-fw"></i> Recent activity</h4><p>
@@ -653,7 +676,7 @@
Events that trigger desktop notification.
</p>
<ul>
<li>New Rally point</span> <small>(<i class="fas fa-volume-up fa-fw"></i> <a href="#" data-target="#pf-manual-scrollspy-anchor-system-rally">more</a>)</small></li>
<li>New Rally point <small>(<i class="fas fa-volume-up fa-fw"></i> <a href="#" data-target="#pf-manual-scrollspy-anchor-system-rally">more</a>)</small></li>
</ul>
<hr class="pf-manual-scroll-break">

View File

@@ -8,7 +8,7 @@
{{#subText}}
<li data-action="{{subAction}}">
<a href="#" tabindex="-1" data-action="{{subAction}}">
<i class="fas {{#subIcon}}{{subIcon}}{{/subIcon}} {{#subIconClass}}{{subIconClass}}{{/subIconClass}} fa-fw"></i>
<i class="fas {{#subIcon}}{{subIcon}}{{/subIcon}} {{#subIconClass}}{{subIconClass}}{{/subIconClass}} fa-fw" {{#subChar}}data-char-content="{{subChar}}"{{/subChar}}></i>
&nbsp;{{subText}}
</a>
</li>

View File

@@ -989,6 +989,9 @@ input[type="email"]{
}
}
.dropdown-submenu.disabled>a:after {
border-left-color: $gray-light;
}
.dropdown-submenu.pull-left {
float: none;

View File

@@ -44,6 +44,14 @@ em{
text-decoration: line-through;
}
.fa-char{
&:before{
font-family: Arial, sans-serif;
font-weight: bold;
content: attr(data-char-content);
}
}
.no-scroll{
overflow: hidden;
}
@@ -651,7 +659,6 @@ table{
//background:url("#{$base-url}/#{$body-background-image}") $body-background-color;
background-color: $body-background-color;
background-repeat: no-repeat;
will-change: transform;
}
.pf-menu{
@@ -1202,6 +1209,23 @@ table{
color: $planet-color-temperate;
}
// connection size by jump mass ===================================================================
.pf-jump-mass-xl{
//color: $indigo;
}
.pf-jump-mass-l{
//color: $blue;
}
.pf-jump-mass-m{
//color: $yellow-dark;
}
.pf-jump-mass-s{
//color: $orange;
}
// "fake connection" classes for the map manual
.pf-fake-connection{
box-sizing: content-box;
@@ -1210,7 +1234,7 @@ table{
height: 4px;
border-top: 2px solid $gray-light;
border-bottom: 2px solid $gray-light;
background-color: #3c3f41;
background-color: $gray;
position: relative;
font-size: 10px;
font-family: $font-family-sans-serif;
@@ -1256,35 +1280,66 @@ table{
background-color: $red-darker;
}
&.pf-map-connection-frig{
border-style: dashed;
border-left: none;
border-right: none;
&.pf-map-connection-wh-size-s,
&.pf-map-connection-wh-size-m,
&.pf-map-connection-wh-size-l,
&.pf-map-connection-wh-size-xl,
&.pf-map-connection-preserve-mass{
&:after{
content: '?';
background-color: $gray;
color: $gray-lighter;
padding: 1px 2px;
position: absolute;
left: calc(50% - 7px);
top: -5px;
font-family: Arial, sans-serif;
font-size: 11px;
line-height: 12px;
min-width: 14px;
text-align: center;
@include border-radius(3px);
@include box-shadow(0 3px 6px rgba(0,0,0,.3));
}
}
&.pf-map-connection-wh-size-s{
border-style: dotted;
&:after{
content: 'frig';
background-color: $orange;
color: $gray-darkest;
padding: 0px 3px;
position: absolute;
left: 25px;
top: -6px;
font-family: $font-family-bold;
@include border-radius(3px);
content: 'S';
}
}
&.pf-map-connection-wh-size-m{
border-style: dashed;
&:after{
content: 'M';
}
}
&.pf-map-connection-wh-size-l{
&:after{
content: 'L';
}
}
&.pf-map-connection-wh-size-xl{
&:after{
content: 'XL';
}
}
&.pf-map-connection-preserve-mass{
&:after{
content: 'save mass';
background-color: $red-darker;
color: $gray-lightest;
padding: 0px 3px;
position: absolute;
left: 8px;
top: -6px;
font-family: $font-family-bold;
@include border-radius(3px);
left: calc(50% - 28px);
}
}
}
@@ -1313,8 +1368,8 @@ table{
}
&.pf-wh-frig{
border-top-style: dashed;
border-bottom-style: dashed;
border-top-style: dotted;
border-bottom-style: dotted;
}
}

View File

@@ -283,6 +283,20 @@ $mapBubbleWidth: 30px;
}
}
// class gets applied to body tag if system/connection gets dragged
// to prevent cursor "flicker" we need to change the cursor for "neighbored" elements
.jtk-drag-select{
.pf-map{
cursor: grab !important;
.pf-system{
cursor: grab !important;
.pf-system-head{
cursor: grab !important;
}
}
}
}
.pf-map{
width: $mapWidth;
height: $mapHeight;
@@ -607,6 +621,7 @@ $mapBubbleWidth: 30px;
.jtk-connector{
z-index: 40; // min z-index for connections
cursor: pointer;
overflow: unset; // fixes a strange 1px flicker issue when connection is hovered and "jumps" up+down
@include transition(stroke 0.18s ease-out, opacity 0.18s ease-out);
will-change: all;
@@ -713,10 +728,11 @@ $mapBubbleWidth: 30px;
}
}
.pf-map-connection-frig{
.pf-map-connection-wh-size-s,
.pf-map-connection-wh-size-m {
path:nth-child(2){
stroke-linecap: square !important;
stroke-linecap: square !important; // smoother look for "inner" path
}
}
@@ -745,7 +761,7 @@ $mapBubbleWidth: 30px;
}
%map-overlay{
font-size: 10px;
font-size: 11px;
z-index: 1020;
background-color: $gray;
color: $gray-lighter;
@@ -756,15 +772,15 @@ $mapBubbleWidth: 30px;
line-height: 14px;
padding: 1px 4px;
@include border-radius(6px);
@include box-shadow(0 6px 12px rgba(0,0,0,.4));
@include box-shadow(0 3px 6px rgba(0,0,0,.3));
&.small{
-webkit-font-smoothing: antialiased;
font-family: Arial, sans-serif; // fix for element width on custom font family
padding: 2px;
font-family: Arial, sans-serif;
padding: 1px 2px;
line-height: 12px;
min-width: 14px;
@include border-radius(3px);
@include box-shadow(0 3px 6px rgba(0,0,0,.3));
}
&.icon{
@@ -776,7 +792,6 @@ $mapBubbleWidth: 30px;
overflow: hidden;
text-align: center;
@include border-radius(50%);
@include box-shadow(0 3px 6px rgba(0,0,0,.3));
}
&.debug{
@@ -853,6 +868,10 @@ $mapBubbleWidth: 30px;
i{
width: 20px;
pointer-events: none;
&.fa-char:before{
font-weight: bolder;
}
}
// nested (sub) menu

View File

@@ -12,6 +12,12 @@
text-transform: capitalize;
font-family: $font-family-readable;
font-weight: bold;
kbd{
font-size: 80%;
line-height: 80%;
vertical-align: middle;
}
}
.popover-content {

View File

@@ -230,22 +230,21 @@
cursor: initial;
}
&.pf-map-connection-frig{
width: 32px;
&:after{
left: 4px;
}
&[class*=' pf-map-connection-wh-size-']{
width: 25px;
}
&.pf-map-connection-preserve-mass{
width: 26px;
&:after{
font-size: 10px;
}
&.pf-map-connection-preserve-mass{
&:after{
content: "\f071";
font-family: "Font Awesome 5 Free";
font-style: normal;
font-weight: bold;
left: 4px;
left: calc(50% - 7px);
}
}
}