- NEW "Thera connections" UI module, closed #829

- Upgraded "[_pathfinder_esi_](https://github.com/exodus4d/pathfinder_esi)" Web API client`v1.3.2` → `v2.0.0`
- Fixed a js bug where current active(selected) system becomes deselected after system was dragged on map
- Fixed a js bug where new auto mapped systems (e.g. after jump) were positioned outside current map scroll viewport
- Fixed a js bug where map sync failed after map tabs switch
- Fixed blurry map when map zoom was changed
- Fixed multiple minor JS bugs where map render/update failed
This commit is contained in:
Mark Friedrich
2020-03-02 16:42:36 +01:00
parent 4a5636456e
commit a5f29ee2eb
123 changed files with 6169 additions and 5219 deletions

View File

@@ -35,7 +35,7 @@ class GitHub extends Controller\Controller {
$return->version->delta = null;
$return->version->dev = false;
$releases = $f3->gitHubClient()->getProjectReleases('exodus4d/pathfinder', $releaseCount);
$releases = $f3->gitHubClient()->send('getProjectReleases', 'exodus4d/pathfinder', $releaseCount);
foreach($releases as $key => &$release){
// check version ------------------------------------------------------------------------------------------
@@ -67,7 +67,7 @@ class GitHub extends Controller\Controller {
// convert Markdown to HTML -> use either gitHub API (in oder to create abs, issue links)
// -> or F3´s markdown as fallback
$html = $f3->gitHubClient()->markdownToHtml('exodus4d/pathfinder', $body);
$html = $f3->gitHubClient()->send('markdownToHtml', 'exodus4d/pathfinder', $body);
if(!empty($html)){
$body = $html;

View File

@@ -179,7 +179,8 @@ class Map extends Controller\AccessController {
'zKillboard' => Config::getPathfinderData('api.z_killboard'),
'eveeye' => Config::getPathfinderData('api.eveeye'),
'dotlan' => Config::getPathfinderData('api.dotlan'),
'anoik' => Config::getPathfinderData('api.anoik')
'anoik' => Config::getPathfinderData('api.anoik'),
'eveScout' => Config::getPathfinderData('api.eve_scout')
];
// get Plugin config --------------------------------------------------------------------------------------

View File

@@ -93,10 +93,16 @@ class Connection extends AbstractRestController {
$connection->mapId = $map;
$connection->source = $source;
$connection->target = $target;
$connection->copyfrom($connectionData, ['scope', 'type']);
// change the default type for the new connection
$connection->setDefaultTypeData();
// if scope + type data send -> use them ...
if($requestData['scope'] && !empty($requestData['type'])){
$connection->copyfrom($requestData, ['scope', 'type']);
}
// ... set/change default scope + type
if(!$requestData['disableAutoScope']){
$connection->setAutoScopeAndType();
}
if($connection->save($activeCharacter)){
$connectionData = $connection->getData();

View File

@@ -14,7 +14,7 @@ class Route extends AbstractRestController {
/**
* cache key for current Thera connections from eve-scout.com
*/
const CACHE_KEY_THERA_CONNECTIONS = 'CACHED_THERA_CONNECTIONS';
const CACHE_KEY_THERA_JUMP_DATA = 'CACHED_THERA_JUMP_DATA';
/**
* route search depth
@@ -260,9 +260,9 @@ class Route extends AbstractRestController {
* -> Connected wormholes pulled from eve-scout.com
*/
private function setTheraJumpData(){
if(!$this->getF3()->exists(self::CACHE_KEY_THERA_CONNECTIONS, $jumpData)){
if(!$this->getF3()->exists(self::CACHE_KEY_THERA_JUMP_DATA, $jumpData)){
$jumpData = [];
$connectionsData = $this->getF3()->eveScoutClient()->getTheraConnections();
$connectionsData = $this->getF3()->eveScoutClient()->send('getTheraConnections');
if(!empty($connectionsData) && !isset($connectionsData['error'])){
/**
@@ -294,12 +294,12 @@ class Route extends AbstractRestController {
};
foreach((array)$connectionsData['connections'] as $connectionData){
$enrichJumpData($connectionData, 'source', 'target');
$enrichJumpData($connectionData, 'target', 'source');
$enrichJumpData($connectionData, 'source', 'target');
$enrichJumpData($connectionData, 'target', 'source');
}
if(!empty($jumpData)){
$this->getF3()->set(self::CACHE_KEY_THERA_CONNECTIONS, $jumpData, $this->theraJumpDataCacheTime);
$this->getF3()->set(self::CACHE_KEY_THERA_JUMP_DATA, $jumpData, $this->theraJumpDataCacheTime);
}
}
}
@@ -337,7 +337,7 @@ class Route extends AbstractRestController {
if( !is_array($this->jumpArray[$systemId]) ){
$this->jumpArray[$systemId] = [];
}
$this->jumpArray[$systemId] = array_merge($row['jumpNodes'], $this->jumpArray[$systemId]);
$this->jumpArray[$systemId] = array_merge((array)$row['jumpNodes'], $this->jumpArray[$systemId]);
// add systemName to end (if not already there)
if(end($this->jumpArray[$systemId]) != $systemName){
@@ -668,7 +668,7 @@ class Route extends AbstractRestController {
'connections' => $connections
];
$result = $this->getF3()->ccpClient()->getRouteData($systemFromId, $systemToId, $options);
$result = $this->getF3()->ccpClient()->send('getRoute', $systemFromId, $systemToId, $options);
// format result ------------------------------------------------------------------------------------------

View File

@@ -0,0 +1,131 @@
<?php
namespace Exodus4D\Pathfinder\Controller\Api\Rest;
use Exodus4D\Pathfinder\Lib\Config;
class SystemThera extends AbstractRestController {
/**
* cache key for HTTP response
*/
const CACHE_KEY_THERA_CONNECTIONS = 'CACHED_THERA_CONNECTIONS';
/**
* get Thera connections data from Eve-Scout
* @param \Base $f3
*/
public function get(\Base $f3){
$ttl = 60 * 3;
if(!$exists = $f3->exists(self::CACHE_KEY_THERA_CONNECTIONS, $connectionsData)){
$connectionsData = $this->getEveScoutTheraConnections();
$f3->set(self::CACHE_KEY_THERA_CONNECTIONS, $connectionsData, $ttl);
}
$f3->expire(Config::ttlLeft($exists, $ttl));
$this->out($connectionsData);
}
/**
* get Thera connections data from EveScout API
* -> map response to Pathfinder format
* @return array
*/
protected function getEveScoutTheraConnections() : array {
$connectionsData = [];
/**
* map system data from eveScout response to Pathfinder´s 'system' format
* @param string $key
* @param array $eveScoutConnection
* @param array $connectionData
*/
$enrichWithSystemData = function(string $key, array $eveScoutConnection, array &$connectionData) : void {
$eveScoutSystem = (array)$eveScoutConnection[$key];
$systemData = [
'id' => (int)$eveScoutSystem['id'],
'name' => (string)$eveScoutSystem['name'],
'trueSec' => round((float)$eveScoutSystem['security'], 4)
];
if(!empty($eveScoutSystem['constellationID'])){
$systemData['constellation'] = ['id' => (int)$eveScoutSystem['constellationID']];
}
if(!empty($region = (array)$eveScoutSystem['region']) && !empty($region['id'])){
$systemData['region'] = ['id' => (int)$region['id'], 'name' => (string)$region['name']];
}
$connectionData[$key] = $systemData;
};
/**
* @param string $key
* @param array $eveScoutConnection
* @param array $connectionData
*/
$enrichWithSignatureData = function(string $key, array $eveScoutConnection, array &$connectionData) : void {
$eveScoutSignature = (array)$eveScoutConnection[$key];
$signatureData = [
'name' => $eveScoutSignature['name'] ? : null
];
if(!empty($sigType = (array)$eveScoutSignature['type']) && !empty($sigType['name'])){
$signatureData['type'] = ['name' => strtoupper((string)$sigType['name'])];
}
$connectionData[$key] = $signatureData;
};
/**
* map wormhole data from eveScout to Pathfinder´s connection format
* @param array $wormholeData
* @param array $connectionsData
*/
$enrichWithWormholeData = function(array $wormholeData, array &$connectionsData) : void {
$type = [];
if($wormholeData['mass'] === 'reduced'){
$type[] = 'wh_reduced';
}else if($wormholeData['mass'] === 'critical'){
$type[] = 'wh_critical';
}else{
$type[] = 'wh_fresh';
}
if($wormholeData['eol'] === 'critical'){
$type[] = 'wh_eol';
}
$connectionsData['type'] = $type;
$connectionsData['estimatedEol'] = $wormholeData['estimatedEol'];
};
$eveScoutResponse = $this->getF3()->eveScoutClient()->send('getTheraConnections');
if(!empty($eveScoutResponse) && !isset($eveScoutResponse['error'])){
foreach((array)$eveScoutResponse['connections'] as $eveScoutConnection){
if(
$eveScoutConnection['type'] === 'wormhole' &&
isset($eveScoutConnection['source']) && isset($eveScoutConnection['target'])
){
try{
$data = [
'id' => (int)$eveScoutConnection['id'],
'scope' => 'wh',
'created' => [
'created' => (new \DateTime($eveScoutConnection['created']))->getTimestamp(),
'character' => (array)$eveScoutConnection['character']
],
'updated' => (new \DateTime($eveScoutConnection['updated']))->getTimestamp()
];
$enrichWithWormholeData((array)$eveScoutConnection['wormhole'], $data);
$enrichWithSystemData('source', $eveScoutConnection, $data);
$enrichWithSystemData('target', $eveScoutConnection, $data);
$enrichWithSignatureData('sourceSignature', $eveScoutConnection, $data);
$enrichWithSignatureData('targetSignature', $eveScoutConnection, $data);
$connectionsData[] = $data;
}catch(\Exception $e){
// new \DateTime Exception -> skip this data
}
}
}
}
return $connectionsData;
}
}

View File

@@ -39,7 +39,7 @@ class System extends Controller\AccessController {
];
foreach($destData as $data){
$response = $f3->ccpClient()->setWaypoint((int)$data['id'], $accessToken, $options);
$response = $f3->ccpClient()->send('setWaypoint', (int)$data['id'], $accessToken, $options);
if(empty($response)){
$return->destData[] = $data;

View File

@@ -215,7 +215,7 @@ class User extends Controller\Controller{
if( $targetId = (int)$data['targetId']){
$activeCharacter = $this->getCharacter();
$response = $f3->ccpClient()->openWindow($targetId, $activeCharacter->getAccessToken());
$response = $f3->ccpClient()->send('openWindow', $targetId, $activeCharacter->getAccessToken());
if(empty($response)){
$return->targetId = $targetId;

View File

@@ -390,7 +390,7 @@ class Sso extends Api\User{
$accessData->refreshToken = null;
$accessData->esiAccessTokenExpires = 0;
$authCodeRequestData = $this->getF3()->ssoClient()->getAccessData($this->getAuthorizationData(), $requestParams);
$authCodeRequestData = $this->getF3()->ssoClient()->send('getAccess', $this->getAuthorizationData(), $requestParams);
if( !empty($authCodeRequestData) ){
if( !empty($authCodeRequestData['accessToken']) ){
@@ -429,7 +429,7 @@ class Sso extends Api\User{
* @return array
*/
public function verifyCharacterData(string $accessToken) : array {
$characterData = $this->getF3()->ssoClient()->getVerifyCharacterData($accessToken);
$characterData = $this->getF3()->ssoClient()->send('getVerifyCharacter', $accessToken);
if( !empty($characterData) ){
// convert string with scopes to array
@@ -451,8 +451,7 @@ class Sso extends Api\User{
$characterData = (object) [];
if($characterId){
$characterDataBasic = $this->getF3()->ccpClient()->getCharacterData($characterId);
$characterDataBasic = $this->getF3()->ccpClient()->send('getCharacter', $characterId);
if( !empty($characterDataBasic) ){
// remove some "unwanted" data -> not relevant for Pathfinder
$characterData->character = array_filter($characterDataBasic, function($key){

View File

@@ -22,7 +22,7 @@ class Universe extends Controller\Controller {
$regionsWhitelist = [
10000002 // The Forge (13 constellations -> 93 systems)
];
$regionIds = $f3->ccpClient()->getUniverseRegions();
$regionIds = $f3->ccpClient()->send('getUniverseRegions');
$regionIds = array_intersect($regionsWhitelist, $regionIds);
$region = Model\Universe\AbstractUniverseModel::getNew('RegionModel');
@@ -43,7 +43,7 @@ class Universe extends Controller\Controller {
$constellationsWhitelist = [
20000014 // Mal (11 systems)
];
$constellationIds = $f3->ccpClient()->getUniverseConstellations();
$constellationIds = $f3->ccpClient()->send('getUniverseConstellations');
$constellationIds = array_intersect($constellationsWhitelist, $constellationIds);
$constellation = Model\Universe\AbstractUniverseModel::getNew('ConstellationModel');
foreach($constellationIds as $constellationId){
@@ -306,13 +306,13 @@ class Universe extends Controller\Controller {
$f3 = \Base::instance();
$universeNameData = [];
if( !empty($categories) && !empty($search)){
$universeIds = $f3->ccpClient()->search($categories, $search, $strict);
$universeIds = $f3->ccpClient()->send('search', $categories, $search, $strict);
if(isset($universeIds['error'])){
// ESI error
$universeNameData = $universeIds;
}elseif( !empty($universeIds) ){
$universeIds = Util::arrayFlattenByValue($universeIds);
$universeNameData = $f3->ccpClient()->getUniverseNamesData($universeIds);
$universeNameData = $f3->ccpClient()->send('getUniverseNames', $universeIds);
}
}
return $universeNameData;

View File

@@ -565,7 +565,7 @@ class Controller {
'routes' => []
];
$serverStatus = $client->getServerStatus();
$serverStatus = $client->send('getServerStatus');
if( !isset($serverStatus['error']) ){
$statusData = $serverStatus['status'];
// calculate time diff since last server restart
@@ -587,7 +587,7 @@ class Controller {
$return->error[] = (new PathfinderException($serverStatus['error'], 500))->getError();
}
$apiStatus = $client->getStatusForRoutes('latest');
$apiStatus = $client->send('getStatus', 'latest', true);
if( !isset($apiStatus['error']) ){
// find top status
$status = 'OK';

View File

@@ -119,13 +119,13 @@ class CcpSystemsUpdate extends AbstractCron {
// get current jump data --------------------------------------------------------------------------------------
$time_start = microtime(true);
$jumpData = $f3->ccpClient()->getUniverseJumps();
$jumpData = $f3->ccpClient()->send('getUniverseJumps');
$time_end = microtime(true);
$execTimeGetJumpData = $time_end - $time_start;
// get current kill data --------------------------------------------------------------------------------------
$time_start = microtime(true);
$killData = $f3->ccpClient()->getUniverseKills();
$killData = $f3->ccpClient()->send('getUniverseKills');
$time_end = microtime(true);
$execTimeGetKillData = $time_end - $time_start;

View File

@@ -186,7 +186,7 @@ class Universe extends AbstractCron {
switch($type){
case 'system':
// load systems + dependencies (planets, star, types,...)
$ids = $f3->ccpClient()->getUniverseSystems();
$ids = $f3->ccpClient()->send('getUniverseSystems');
$modelClass = 'SystemModel';
$setupModel = function(Model\Universe\SystemModel &$model, int $id){
$model->loadById($id);
@@ -195,7 +195,7 @@ class Universe extends AbstractCron {
break;
case 'stargate':
// load all stargates. Systems must be present first!
$ids = $f3->ccpClient()->getUniverseSystems();
$ids = $f3->ccpClient()->send('getUniverseSystems');
$modelClass = 'SystemModel';
$setupModel = function(Model\Universe\SystemModel &$model, int $id){
$model->loadById($id);
@@ -203,7 +203,7 @@ class Universe extends AbstractCron {
};
break;
case 'station':
$ids = $f3->ccpClient()->getUniverseSystems();
$ids = $f3->ccpClient()->send('getUniverseSystems');
$modelClass = 'SystemModel';
$setupModel = function(Model\Universe\SystemModel &$model, int $id){
if($model->getById($id)){
@@ -216,7 +216,7 @@ class Universe extends AbstractCron {
break;
case 'sovereignty':
// load sovereignty map data. Systems must be present first!
$sovData = $f3->ccpClient()->getSovereigntyMap();
$sovData = $f3->ccpClient()->send('getSovereigntyMap');
$ids = !empty($sovData = $sovData['map']) ? array_keys($sovData): [];
$modelClass = 'SystemModel';
$setupModel = function(Model\Universe\SystemModel &$model, int $id) use ($sovData) {
@@ -229,7 +229,7 @@ class Universe extends AbstractCron {
};
break;
case 'faction_war_systems':
$fwSystems = $f3->ccpClient()->getFactionWarSystems();
$fwSystems = $f3->ccpClient()->send('getFactionWarSystems');
$ids = !empty($fwSystems = $fwSystems['systems']) ? array_keys($fwSystems): [];
$modelClass = 'SystemModel';
$setupModel = function(Model\Universe\SystemModel &$model, int $id) use ($fwSystems) {
@@ -243,7 +243,7 @@ class Universe extends AbstractCron {
break;
case 'index_system':
// setup system index, Systems must be present first!
$ids = $f3->ccpClient()->getUniverseSystems();
$ids = $f3->ccpClient()->send('getUniverseSystems');
$modelClass = 'SystemModel';
$setupModel = function(Model\Universe\SystemModel &$model, int $id){
$model->getById($id); // no loadById() here! would take "forever" when system not exists and must be build up first...
@@ -310,8 +310,8 @@ class Universe extends AbstractCron {
*/
$system = Model\Universe\AbstractUniverseModel::getNew('SystemModel');
$sovData = $f3->ccpClient()->getSovereigntyMap();
$fwSystems = $f3->ccpClient()->getFactionWarSystems();
$sovData = $f3->ccpClient()->send('getSovereigntyMap');
$fwSystems = $f3->ccpClient()->send('getFactionWarSystems');
$fwSystems = $fwSystems['systems'];
$ids = !empty($sovData = $sovData['map']) ? array_keys($sovData): [];
sort($ids, SORT_NUMERIC);

View File

@@ -298,8 +298,8 @@ abstract class AbstractClient extends \Prefab {
public function __call(string $name, array $arguments = []){
$return = [];
if(is_object($this->client)){
if( method_exists($this->client, $name) ){
$return = call_user_func_array([$this->client, $name], $arguments);
if(method_exists($this->client, $name)){
$return = call_user_func_array([$this->client, $name], $arguments);
}else{
$errorMsg = $this->getMissingMethodError(get_class($this->client), $name);
$this->getLogger('ERROR')->write($errorMsg);
@@ -336,18 +336,20 @@ abstract class AbstractClient extends \Prefab {
$client->setNewLog($this->newLog());
$client->setIsLoggable($this->isLoggable($f3));
$client->setLogStats(true); // add cURL stats (e.g. transferTime) to logged requests
$client->setLogCache(true); // add cache info (e.g. from cached) to logged requests
//$client->setLogAllStatus(true); // log all requests regardless of response HTTP status code
$client->setLogFile('esi_requests');//
$client->setLogStats(true); // add cURL stats (e.g. transferTime) to loggable requests
$client->setLogCache(true); // add cache info (e.g. from cached) to loggable requests
$client->setLogAllStatus(false); // log all requests regardless of response HTTP status code
$client->setLogRequestHeaders(false); // add request HTTP headers to loggable requests
$client->setLogResponseHeaders(false); // add response HTTP headers to loggable requests
$client->setLogFile('esi_requests');
$client->setRetryLogFile('esi_retry_requests');
$client->setCacheDebug(true);
$client->setCachePool($this->getCachePool($f3));
// use local proxy server for debugging requests
//$client->setProxy('127.0.0.1:8888');
//$client->setProxy('127.0.0.1:8888'); // use local proxy server for debugging requests
// disable SSL certificate verification -> allow proxy to decode(view) request
//$client->setVerify(false);

View File

@@ -11,14 +11,13 @@ namespace Exodus4D\Pathfinder\Lib\Api;
use Exodus4D\Pathfinder\Lib\Config;
use Exodus4D\ESI\Client\ApiInterface;
use Exodus4D\ESI\Client\Ccp\Esi\Esi as Client;
use Exodus4D\ESI\Client\Ccp\Esi\EsiInterface as ClientInterface;
/**
* Class CcpClient
* @package lib\api
*
* @method ClientInterface getServerStatus()
* @method ClientInterface getStatusForRoutes(string $version)
* @method ApiInterface send(string $requestHandler, ...$handlerParams)
* @method ApiInterface sendBatch(array $configs)
*/
class CcpClient extends AbstractClient {

View File

@@ -6,13 +6,13 @@ namespace Exodus4D\Pathfinder\Lib\Api;
use Exodus4D\Pathfinder\Lib\Config;
use Exodus4D\ESI\Client\ApiInterface;
use Exodus4D\ESI\Client\EveScout\EveScout as Client;
use Exodus4D\ESI\Client\EveScout\EveScoutInterface as ClientInterface;
/**
* Class EveScoutClient
* @package lib\api
*
* @method ClientInterface getTheraConnections()
* @method ApiInterface send(string $requestHandler, ...$handlerParams)
* @method ApiInterface sendBatch(array $configs)
*/
class EveScoutClient extends AbstractClient {

View File

@@ -11,14 +11,13 @@ namespace Exodus4D\Pathfinder\Lib\Api;
use Exodus4D\Pathfinder\Lib\Config;
use Exodus4D\ESI\Client\ApiInterface;
use Exodus4D\ESI\Client\GitHub\GitHub as Client;
use Exodus4D\ESI\Client\GitHub\GitHubInterface as ClientInterface;
/**
* Class GitHubClient
* @package lib\api
*
* @method ClientInterface getProjectReleases(string $projectName, int $count) : array
* @method ClientInterface markdownToHtml(string $context, string $markdown) : string
* @method ApiInterface send(string $requestHandler, ...$handlerParams)
* @method ApiInterface sendBatch(array $configs)
*/
class GitHubClient extends AbstractClient {

View File

@@ -150,7 +150,7 @@ class AllianceModel extends AbstractPathfinderModel {
$loaded = parent::getById($id, $ttl, $isActive);
if($this->isOutdated()){
// request alliance data
$allianceData = self::getF3()->ccpClient()->getAllianceData($id);
$allianceData = self::getF3()->ccpClient()->send('getAlliance', $id);
if(!empty($allianceData) && !isset($allianceData['error'])){
$this->copyfrom($allianceData, ['id', 'name', 'ticker']);
$this->save();

View File

@@ -792,7 +792,7 @@ class CharacterModel extends AbstractPathfinderModel {
*/
public function updateCloneData(){
if($accessToken = $this->getAccessToken()){
$clonesData = self::getF3()->ccpClient()->getCharacterClonesData($this->_id, $accessToken);
$clonesData = self::getF3()->ccpClient()->send('getCharacterClones', $this->_id, $accessToken);
if(!isset($clonesData['error'])){
if(!empty($homeLocationData = $clonesData['home']['location'])){
// clone home location data
@@ -816,7 +816,7 @@ class CharacterModel extends AbstractPathfinderModel {
* @return array
*/
protected function getOnlineData(string $accessToken) : array {
return self::getF3()->ccpClient()->getCharacterOnlineData($this->_id, $accessToken);
return self::getF3()->ccpClient()->send('getCharacterOnline', $this->_id, $accessToken);
}
/**
@@ -855,7 +855,7 @@ class CharacterModel extends AbstractPathfinderModel {
// Try to pull data from API
if($accessToken = $this->getAccessToken()){
if($this->isOnline($accessToken)){
$locationData = self::getF3()->ccpClient()->getCharacterLocationData($this->_id, $accessToken);
$locationData = self::getF3()->ccpClient()->send('getCharacterLocation', $this->_id, $accessToken);
if(!empty($locationData['system']['id'])){
// character is currently in-game
@@ -886,7 +886,7 @@ class CharacterModel extends AbstractPathfinderModel {
// get "more" data for systemId ---------------------------------------------------------------
if(!empty($lookupUniverseIds)){
// get "more" information for some Ids (e.g. name)
$universeData = self::getF3()->ccpClient()->getUniverseNamesData($lookupUniverseIds);
$universeData = self::getF3()->ccpClient()->send('getUniverseNames', $lookupUniverseIds);
if(!empty($universeData) && !isset($universeData['error'])){
// We expect max ONE system AND/OR station data, not an array of e.g. systems
@@ -967,7 +967,7 @@ class CharacterModel extends AbstractPathfinderModel {
// check ship data for changes ----------------------------------------------------------------
if(!$deleteLog){
$shipData = self::getF3()->ccpClient()->getCharacterShipData($this->_id, $accessToken);
$shipData = self::getF3()->ccpClient()->send('getCharacterShip', $this->_id, $accessToken);
// IDs for "shipTypeId" that require more data
$lookupShipTypeId = 0;

View File

@@ -130,7 +130,7 @@ class ConnectionModel extends AbstractMapTrackingModel {
$connectionData->source = $this->source->id;
$connectionData->target = $this->target->id;
$connectionData->scope = $this->scope;
$connectionData->type = $this->type;
$connectionData->type = (array)json_decode($this->get('type', true));
$connectionData->updated = strtotime($this->updated);
$connectionData->created = strtotime($this->created);
$connectionData->eolUpdated = strtotime($this->eolUpdated);
@@ -219,10 +219,10 @@ class ConnectionModel extends AbstractMapTrackingModel {
}
/**
* set default connection type by search route between endpoints
* set default connection scope + type by search route between endpoints
* @throws \Exception
*/
public function setDefaultTypeData(){
public function setAutoScopeAndType(){
if(
is_object($this->source) &&
is_object($this->target)
@@ -233,15 +233,16 @@ class ConnectionModel extends AbstractMapTrackingModel {
){
$this->scope = 'abyssal';
$this->type = ['abyssal'];
}elseif(
$this->source->isKspace() &&
$this->target->isKspace() &&
(new Route())->searchRoute($this->source->systemId, $this->target->systemId, 1)['routePossible']
){
$this->scope = 'stargate';
$this->type = ['stargate'];
}else{
$route = (new Route())->searchRoute($this->source->systemId, $this->target->systemId, 1);
if($route['routePossible']){
$this->scope = 'stargate';
$this->type = ['stargate'];
}else{
$this->scope = 'wh';
$this->type = ['wh_fresh'];
}
$this->scope = 'wh';
$this->type = ['wh_fresh'];
}
}
}
@@ -294,7 +295,7 @@ class ConnectionModel extends AbstractMapTrackingModel {
!$this->scope ||
empty($types)
){
$this->setDefaultTypeData();
$this->setAutoScopeAndType();
}
return $this->isValid() ? parent::beforeInsertEvent($self, $pkeys) : false;

View File

@@ -292,7 +292,7 @@ class CorporationModel extends AbstractPathfinderModel {
!empty($accessToken) &&
!$this->isNPC
){
$response = self::getF3()->ccpClient()->getCorporationRoles($this->_id, $accessToken);
$response = self::getF3()->ccpClient()->send('getCorporationRoles', $this->_id, $accessToken);
if( !empty($response['roles']) ){
$characterRolesData = (array)$response['roles'];
}
@@ -356,10 +356,10 @@ class CorporationModel extends AbstractPathfinderModel {
$loaded = parent::getById($id, $ttl, $isActive);
if($this->isOutdated()){
// request corporation data
$corporationData = self::getF3()->ccpClient()->getCorporationData($id);
$corporationData = self::getF3()->ccpClient()->send('getCorporation', $id);
if(!empty($corporationData) && !isset($corporationData['error'])){
// check for NPC corporation
$corporationData['isNPC'] = self::getF3()->ccpClient()->isNpcCorporation($id);
$corporationData['isNPC'] = in_array($id, self::getF3()->ccpClient()->send('getNpcCorporations'));
$this->copyfrom($corporationData, ['id', 'name', 'ticker', 'memberCount', 'isNPC']);
$this->save();

View File

@@ -85,7 +85,7 @@ class AllianceModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getAllianceData($id);
$data = self::getF3()->ccpClient()->send('getAlliance', $id);
if(!empty($data) && !isset($data['error'])){
if($data['factionId']){
/**

View File

@@ -178,14 +178,14 @@ class CategoryModel extends AbstractUniverseModel {
* @return array
*/
public static function getUniverseCategoryData(int $id) : array {
return self::getF3()->ccpClient()->getUniverseCategoryData($id);
return self::getF3()->ccpClient()->send('getUniverseCategory', $id);
}
/**
* @return array
*/
public static function getUniverseCategories() : array {
return self::getF3()->ccpClient()->getUniverseCategories();
return self::getF3()->ccpClient()->send('getUniverseCategories');
}
/**

View File

@@ -80,7 +80,7 @@ class ConstellationModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseConstellationData($id);
$data = self::getF3()->ccpClient()->send('getUniverseConstellation', $id);
if(!empty($data)){
/**
* @var $region RegionModel
@@ -99,7 +99,7 @@ class ConstellationModel extends AbstractUniverseModel {
*/
public function loadSystemsData(){
if( !$this->dry() ){
$data = self::getF3()->ccpClient()->getUniverseConstellationData($this->_id);
$data = self::getF3()->ccpClient()->send('getUniverseConstellation', $this->_id);
if(!empty($data)){
foreach((array)$data['systems'] as $systemId){
/**

View File

@@ -105,10 +105,10 @@ class CorporationModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getCorporationData($id);
$data = self::getF3()->ccpClient()->send('getCorporation', $id);
if(!empty($data) && !isset($data['error'])){
// check for NPC corporation
$data['isNPC'] = self::getF3()->ccpClient()->isNpcCorporation($id);
$data['isNPC'] = in_array($id, self::getF3()->ccpClient()->send('getNpcCorporations'));
if($data['factionId']){
/**

View File

@@ -88,7 +88,7 @@ class DogmaAttributeModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getDogmaAttributeData($id);
$data = self::getF3()->ccpClient()->send('getDogmaAttribute', $id);
if(!empty($data) && !isset($data['error'])){
$this->copyfrom($data, ['id', 'name', 'displayName', 'description', 'published', 'stackable', 'highIsGood', 'defaultValue', 'iconId', 'unitId']);
$this->save();

View File

@@ -84,7 +84,7 @@ class FactionModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseFactionData($id);
$data = self::getF3()->ccpClient()->send('getUniverseFaction', $id);
if(!empty($data) && !isset($data['error'])){
$this->copyfrom($data, ['id', 'name', 'description', 'sizeFactor', 'stationCount', 'stationSystemCount']);
$this->save();

View File

@@ -178,14 +178,14 @@ class GroupModel extends AbstractUniverseModel {
* @return array
*/
public static function getUniverseGroupData(int $id) : array {
return self::getF3()->ccpClient()->getUniverseGroupData($id);
return self::getF3()->ccpClient()->send('getUniverseGroup', $id);
}
/**
* @return array
*/
public static function getUniverseGroups() : array {
return self::getF3()->ccpClient()->getUniverseGroups();
return self::getF3()->ccpClient()->send('getUniverseGroups');
}
/**

View File

@@ -87,7 +87,7 @@ class PlanetModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniversePlanetData($id);
$data = self::getF3()->ccpClient()->send('getUniversePlanet', $id);
if(!empty($data)){
/**
* @var $system SystemModel

View File

@@ -66,7 +66,7 @@ class RaceModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseRaceData($id);
$data = self::getF3()->ccpClient()->send('getUniverseRace', $id);
if(!empty($data) && !isset($data['error'])){
/**
* @var $faction FactionModel

View File

@@ -56,7 +56,7 @@ class RegionModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseRegionData($id);
$data = self::getF3()->ccpClient()->send('getUniverseRegion', $id);
if(!empty($data)){
$this->copyfrom($data, ['id', 'name', 'description']);
$this->save();
@@ -68,7 +68,7 @@ class RegionModel extends AbstractUniverseModel {
*/
public function loadConstellationsData(){
if( !$this->dry() ){
$data = self::getF3()->ccpClient()->getUniverseRegionData($this->_id);
$data = self::getF3()->ccpClient()->send('getUniverseRegion', $this->_id);
if(!empty($data)){
foreach((array)$data['constellations'] as $constellationsId){
/**

View File

@@ -86,7 +86,7 @@ class StarModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseStarData($id);
$data = self::getF3()->ccpClient()->send('getUniverseStar', $id);
if(!empty($data)){
/**
* @var $type TypeModel

View File

@@ -94,7 +94,7 @@ class StargateModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseStargateData($id);
$data = self::getF3()->ccpClient()->send('getUniverseStargate', $id);
if(!empty($data)){

View File

@@ -123,7 +123,7 @@ class StationModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseStationData($id);
$data = self::getF3()->ccpClient()->send('getUniverseStation', $id);
if(!empty($data) && !isset($data['error'])){
/**
* @var $system SystemModel

View File

@@ -90,7 +90,7 @@ class StructureModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseStructureData($id, $accessToken);
$data = self::getF3()->ccpClient()->send('getUniverseStructure', $id, $accessToken);
if(!empty($data) && !isset($data['error'])){
/**
* @var $type TypeModel

View File

@@ -491,7 +491,7 @@ class SystemModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseSystemData($id);
$data = self::getF3()->ccpClient()->send('getUniverseSystem', $id);
if(!empty($data)){
/**
@@ -521,7 +521,7 @@ class SystemModel extends AbstractUniverseModel {
*/
public function loadPlanetsData(){
if($this->valid()){
$data = self::getF3()->ccpClient()->getUniverseSystemData($this->_id);
$data = self::getF3()->ccpClient()->send('getUniverseSystem', $this->_id);
if($data['planets']){
// planets are optional since ESI v4 (e.g. Abyssal systems)
foreach((array)$data['planets'] as $planetData){
@@ -542,7 +542,7 @@ class SystemModel extends AbstractUniverseModel {
*/
public function loadStargatesData(){
if($this->valid()){
$data = self::getF3()->ccpClient()->getUniverseSystemData($this->_id);
$data = self::getF3()->ccpClient()->send('getUniverseSystem', $this->_id);
if($data['stargates']){
foreach((array)$data['stargates'] as $stargateId){
/**
@@ -561,7 +561,7 @@ class SystemModel extends AbstractUniverseModel {
*/
public function loadStationsData(){
if($this->valid()){
$data = self::getF3()->ccpClient()->getUniverseSystemData($this->_id);
$data = self::getF3()->ccpClient()->send('getUniverseSystem', $this->_id);
if($data['stations']){
foreach((array)$data['stations'] as $stationId){
/**

View File

@@ -352,7 +352,7 @@ class TypeModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseTypesData($id);
$data = self::getF3()->ccpClient()->send('getUniverseType', $id);
if(!empty($data)){
$this->manipulateDogmaAttributes($data);

View File

@@ -49,7 +49,7 @@
"cache/namespaced-cache": "1.0.*",
"react/socket": "1.3.*",
"react/promise-stream": "1.2.*",
"clue/ndjson-react": "1.0.*",
"clue/ndjson-react": "1.1.*",
"exodus4d/pathfinder_esi": "dev-develop as 0.0.x-dev"
},
"suggest": {

View File

@@ -49,8 +49,8 @@
"cache/namespaced-cache": "1.0.*",
"react/socket": "1.3.*",
"react/promise-stream": "1.2.*",
"clue/ndjson-react": "1.0.*",
"exodus4d/pathfinder_esi": "1.3.3"
"clue/ndjson-react": "1.1.*",
"exodus4d/pathfinder_esi": "2.0.0"
},
"suggest": {
"ext-redis": "Redis can be used as cache backend."

View File

@@ -516,7 +516,7 @@ gulp.task('task:hintJS', () => {
* concat/build JS files by modules
*/
gulp.task('task:concatJS', () => {
let modules = ['login', 'mappage', 'setup', 'admin', 'PNotify.loader', 'datatables.loader'];
let modules = ['login', 'mappage', 'setup', 'admin', 'pnotify.loader', 'datatables.loader', 'summernote.loader'];
let srcModules = ['./js/app/*(' + modules.join('|') + ').js'];
return gulp.src(srcModules, {base: 'js'})

View File

@@ -70,16 +70,16 @@ requirejs.config({
'datatables.net-buttons': 'lib/datatables/Buttons-1.5.6/js/dataTables.buttons.min',
'datatables.net-buttons-html': 'lib/datatables/Buttons-1.5.6/js/buttons.html5.min',
'datatables.net-responsive': 'lib/datatables/Responsive-2.2.2/js/dataTables.responsive.min',
'datatables.net-rowgroup': 'lib/datatables/RowGroup-1.1.1/js/dataTables.rowGroup.min',
'datatables.net-select': 'lib/datatables/Select-1.3.0/js/dataTables.select.min',
'datatables.plugins.render.ellipsis': 'lib/datatables/plugins/render/ellipsis',
// PNotify // v4.0.0 PNotify - notification core file - https://sciactive.com/pnotify
'PNotify.loader': './app/pnotify.loader',
'pnotify.loader': './app/pnotify.loader',
'PNotify': 'lib/pnotify/PNotify',
'PNotifyButtons': 'lib/pnotify/PNotifyButtons',
'PNotifyNonBlock': 'lib/pnotify/PNotifyNonBlock',
'PNotifyDesktop': 'lib/pnotify/PNotifyDesktop',
'PNotifyCallbacks': 'lib/pnotify/PNotifyCallbacks',
'PNotifyDesktop': 'lib/pnotify/PNotifyDesktop',
'NonBlock': 'lib/pnotify/NonBlock' // v1.0.8 NonBlock.js - for PNotify "nonblock" feature
},
shim: {
@@ -116,6 +116,9 @@ requirejs.config({
'datatables.net-responsive': {
deps: ['datatables.net']
},
'datatables.net-rowgroup': {
deps: ['datatables.net']
},
'datatables.net-select': {
deps: ['datatables.net']
},

View File

@@ -107,7 +107,7 @@ define([
updateDateDiff(element, date, round);
}
};
Cron.set(counterTask);
counterTask.start();
element.attr(config.counterTaskAttr, taskName);
}
@@ -138,7 +138,7 @@ define([
counterTask.task = timer => {
tableApi.cells(null, columnSelector).every(cellUpdate);
};
Cron.set(counterTask);
counterTask.start();
tableElement.attr(config.counterTaskAttr, taskName);
};

View File

@@ -8,7 +8,8 @@ define([
'datatables.net-select',
'datatables.net-buttons',
'datatables.net-buttons-html',
'datatables.net-responsive'
'datatables.net-responsive',
'datatables.net-rowgroup'
], ($, Init, Counter, DeferredPromise, TimeoutPromise) => {
'use strict';

View File

@@ -5,7 +5,7 @@ define([], () => {
* Abstract Cache Strategy class
* @type {AbstractStrategy}
*/
let AbstractStrategy = class AbstractStrategy {
class AbstractStrategy {
constructor(){
if(new.target === AbstractStrategy){
throw new TypeError('Cannot construct AbstractStrategy instances directly');
@@ -19,7 +19,7 @@ define([], () => {
static create(){
return new this();
}
};
}
/**
* LIFO Cache Strategy - First In First Out
@@ -27,7 +27,7 @@ define([], () => {
* without any regard to how often or how many times they were accessed before.
* @type {StrategyFIFO}
*/
let StrategyFIFO = class StrategyFIFO extends AbstractStrategy {
class StrategyFIFO extends AbstractStrategy {
valueToCompare(metaData){
return metaData.age();
}
@@ -35,7 +35,7 @@ define([], () => {
compare(a, b){
return b - a;
}
};
}
/**
* LFU Cache Strategy - Least Frequently Used
@@ -43,7 +43,7 @@ define([], () => {
* Those that are used least often are discarded first
* @type {StrategyLFU}
*/
let StrategyLFU = class StrategyLFU extends AbstractStrategy {
class StrategyLFU extends AbstractStrategy {
valueToCompare(metaData){
return metaData.hitCount;
}
@@ -51,7 +51,7 @@ define([], () => {
compare(a, b){
return a - b;
}
};
}
/**
* LRU Cache Strategy - Least Recently Used
@@ -59,7 +59,7 @@ define([], () => {
* No matter how often they have been accessed.
* @type {StrategyLRU}
*/
let StrategyLRU = class StrategyLRU extends AbstractStrategy {
class StrategyLRU extends AbstractStrategy {
valueToCompare(metaData){
return metaData.hits[metaData.hits.length - 1] || metaData.set;
}
@@ -67,26 +67,26 @@ define([], () => {
compare(a, b){
return a - b;
}
};
}
/**
* Each entry in cache also has its own instance of CacheEntryMeta
* -> The configured Cache Strategy use this meta data for eviction policy
* @type {CacheEntryMeta}
*/
let CacheEntryMeta = class CacheEntryMeta {
class CacheEntryMeta {
constructor(ttl, tSet){
this.ttl = ttl;
this.tSet = tSet || this.constructor.now();
this.tHits = [];
this._ttl = ttl; // ttl < 0 => no expire
this._tSet = tSet || this.constructor.now();
this._tHits = [];
}
get set(){
return this.tSet;
return this._tSet;
}
get hits(){
return this.tHits;
return this._tHits;
}
get hitCount(){
@@ -94,15 +94,15 @@ define([], () => {
}
newHit(current){
this.tHits.push(current || this.constructor.now());
this._tHits.push(current || this.constructor.now());
}
age(current){
return (current || this.constructor.now()) - this.tSet;
return (current || this.constructor.now()) - this._tSet;
}
expired(current){
return this.ttl < this.age(current);
return this._ttl < 0 ? false : this._ttl < this.age(current);
}
static now(){
@@ -112,7 +112,7 @@ define([], () => {
static create(ttl, tSet){
return new this(ttl, tSet);
}
};
}
/**
* Each instance of Cache represents a key value in memory data store
@@ -123,26 +123,18 @@ define([], () => {
* if cache reaches maxSize limit, to increase performance.
* @type {Cache}
*/
let Cache = class Cache {
class Cache {
constructor(config){
this.config = Object.assign({},{
name: 'Default', // custom unique name for identification
ttl: 3600, // default ttl for cache entries
maxSize: 600, // max cache entries
bufferSize: 10, // cache entry count in percent to be removed if maxSize reached
strategy: 'FIFO', // cache strategy policy
debug: false // debug output in console
}, config);
this.store = new Map();
this.metaStore = new WeakMap();
this.strategy = this.constructor.setStrategy(this.config.strategy);
constructor(config = {}){
this._config = Object.assign({}, Cache.defaultConfig, config);
this._store = new Map();
this._metaStore = new WeakMap();
this._strategy = this.constructor.setStrategy(this._config.strategy);
this.debug = (msg,...data) => {
if(this.config.debug){
if(this._config.debug){
data = (data || []);
data.unshift(this.config.name);
data.unshift(this._config.name);
console.debug('debug: CACHE %o | ' + msg, ...data);
}
};
@@ -151,34 +143,34 @@ define([], () => {
}
get size(){
return this.store.size;
return this._store.size;
}
isFull(){
return this.size>= this.config.maxSize;
return this.size>= this._config.maxSize;
}
set(key, value, ttl){
if(this.store.has(key)){
if(this._store.has(key)){
this.debug('SET key %o, UPDATE value %o', key, value);
this.store.set(key, value);
this._store.set(key, value);
}else{
this.debug('SET key %o, NEW value %o', key, value);
if(this.isFull()){
this.debug(' ↪ FULL trim cache…');
this.trim(this.trimCount(1));
}
this.store.set(key, value);
this._store.set(key, value);
}
this.metaStore.set(value, CacheEntryMeta.create(ttl || this.config.ttl));
this._metaStore.set(value, CacheEntryMeta.create(ttl || this._config.ttl));
}
get(key){
if(this.store.has(key)){
let value = this.store.get(key);
if(this._store.has(key)){
let value = this._store.get(key);
if(value){
let metaData = this.metaStore.get(value);
let metaData = this._metaStore.get(value);
if(metaData.expired()){
this.debug('EXPIRED key %o delete', key);
this.delete(key);
@@ -199,8 +191,8 @@ define([], () => {
keysForTrim(count){
let trimKeys = [];
let compare = [];
for(let [key, value] of this.store){
let metaData = this.metaStore.get(value);
for(let [key, value] of this._store){
let metaData = this._metaStore.get(value);
if(metaData.expired()){
trimKeys.push(key);
if(count === trimKeys.length){
@@ -209,14 +201,14 @@ define([], () => {
}else{
compare.push({
key: key,
value: this.strategy.valueToCompare(metaData)
value: this._strategy.valueToCompare(metaData)
});
}
}
let countLeft = count - trimKeys.length;
if(countLeft > 0){
compare = compare.sort((a, b) => this.strategy.compare(a.value, b.value));
compare = compare.sort((a, b) => this._strategy.compare(a.value, b.value));
trimKeys = trimKeys.concat(compare.splice(0, countLeft).map(a => a.key));
}
@@ -224,20 +216,20 @@ define([], () => {
}
keys(){
return this.store.keys();
return this._store.keys();
}
delete(key){
return this.store.delete(key);
return this._store.delete(key);
}
clear(){
this.store.clear();
this._store.clear();
}
trimCount(spaceLeft){
let bufferSize = Math.max(Math.round(this.config.maxSize / 100 * this.config.bufferSize), spaceLeft);
return Math.min(Math.max(this.size - this.config.maxSize + bufferSize, 0), this.size);
let bufferSize = Math.max(Math.round(this._config.maxSize / 100 * this._config.bufferSize), spaceLeft);
return Math.min(Math.max(this.size - this._config.maxSize + bufferSize, 0), this.size);
}
trim(count){
@@ -253,9 +245,9 @@ define([], () => {
status(){
return {
config: this.config,
store: this.store,
metaStore: this.metaStore
config: this._config,
store: this._store,
metaStore: this._metaStore
};
}
@@ -269,6 +261,15 @@ define([], () => {
}
}
}
Cache.defaultConfig = {
name: 'Default', // custom unique name for identification
ttl: 3600, // default ttl for cache entries
maxSize: 600, // max cache entries
bufferSize: 10, // cache entry count in percent to be removed if maxSize reached
strategy: 'FIFO', // cache strategy policy
debug: false // debug output in console
};
return Cache;

View File

@@ -11,7 +11,7 @@ define([
console.info('task1 function():', timer.getTotalTimeValues());
return 'OK';
};
Cron.set(task1);
task1.start();
Example2 run task every 3 seconds ---------------------------------------------------------------------------------
let task1 = Cron.new('task1', {precision: 'seconds', interval: 3, timeout: 100});
@@ -19,7 +19,7 @@ define([
console.info('task1 function():', timer.getTotalTimeValues());
return 'OK';
};
Cron.set(task1);
task1.start();
Example3 resolve Promise on run ----------------------------------------------------------------------------------
let task1 = Cron.new('task1', {precision: 'seconds', interval: 1, timeout: 100});
@@ -35,14 +35,14 @@ define([
});
});
};
Cron.set(task1);
task1.start();
Example4 run task once at given Date() --------------------------------------------------------------------------
let dueDate = new Date();
dueDate.setSeconds(dueDate.getSeconds() + 5);
let task2 = Cron.new('task2', {precision: 'seconds', timeout: 100, dueDate: dueDate});
task2.task = () => 'OK task2';
Cron.set(task2);
task2.start();
*/
/**
@@ -51,35 +51,42 @@ define([
* @type {Task}
*/
let Task = class Task {
constructor(name, config){
/**
*
* @param {string} name
* @param {{}} config
* @param {CronManager} manager
*/
constructor(name, config, manager = null){
if(typeof name !== 'string'){
throw new TypeError('Task "name" must be instance of String, Type of "' + typeof name + '" given');
}
this._config = Object.assign({}, this.constructor.defaultConfig, config);
this._name = name; // unique name for identification
this._task = undefined; // task to run, instanceof Function, can also return a Promise
this._manager = undefined; // reference to CronManager() that handles this task
this._task = (timer, task) => {}; // task to run, instanceof Function, can also return a Promise
this._manager = manager; // reference to CronManager() that handles this task
this._runCount = 0; // counter for run() calls
this._runQueue = new Map(); // current run() processes. > 1 requires config.isParallel: true
this._runCount = 0; // total run counter for this task
this._lastTotalTimeValues = undefined; // time values of last run()
}
/**
* @returns {string}
*/
get name(){
return this._name;
}
get task(){
/**
* @returns {(function())} task
*/
get task() {
return this._task;
}
get runCount(){
return this._runCount;
}
get precision(){
return this._config.precision;
}
/**
* @param {(function())} task
*/
set task(task){
if(task instanceof Function){
this._task = task;
@@ -88,34 +95,120 @@ define([
}
}
/**
* @returns {number}
*/
get runCount(){
return this._runCount;
}
/**
* @returns {string}
*/
get precision(){
return this.get('precision');
}
/**
* @returns {boolean}
*/
get paused(){
return this.get('paused');
}
/**
* @returns {boolean}
*/
get targetAchieved(){
return this.get('targetRunCount') ? this.runCount >= this.get('targetRunCount') : false;
}
/**
* @returns {number}
*/
get targetProgress(){
return parseFloat(
parseFloat(
(!this.get('targetRunCount') || !this.runCount) ?
0 :
(100 / this.get('targetRunCount') * this.runCount)
).toFixed(2));
}
/**
* @param option
* @returns {*}
*/
get(option){
return this._config[option];
}
/**
* @param {string} option
* @param {*} value
*/
set(option, value){
this._config[option] = value;
}
setManager(manager){
this._manager = manager;
/**
* connect CronManager with instance
* @param {CronManager} manager
*/
connect(manager = this._manager){
if(manager instanceof CronManager){
if(manager !== this._manager){
// disconnect from current manager (if exists)
this.disconnect();
this._manager = manager;
}
this._manager.set(this);
}else{
throw new TypeError('Parameter must be instance of CronManager. Type of "' + typeof manager + '" given');
}
}
/**
* disconnect from CronManager
* @param {CronManager} manager
*/
disconnect(manager = this._manager){
if(manager instanceof CronManager){
if(this.isConnected(manager)){
this._manager.delete(this._name);
}
}else{
throw new TypeError('Parameter must be instance of CronManager. Type of "' + typeof manager + '" given');
}
}
/**
* checks if CronManager is connected with instance
* @param {CronManager} manager
* @returns {boolean}
*/
isConnected(manager = this._manager){
return (manager instanceof CronManager) &&
manager === this._manager &&
manager.has(this._name);
}
/**
* if task is currently running
* @returns {boolean}
*/
isRunning(){
return !!this._runQueue.size;
}
delete(){
let isDeleted = false;
if(this._manager){
isDeleted = this._manager.delete(this._name);
}
return isDeleted;
}
/**
* @param timer
* @returns {boolean}
*/
isDue(timer){
if(this._config.dueDate instanceof Date){
if(this.get('dueDate') instanceof Date){
// run once at dueDate
if(new Date().getTime() >= this._config.dueDate.getTime()){
if(new Date().getTime() >= this.get('dueDate').getTime()){
return true;
}
}else{
@@ -124,8 +217,8 @@ define([
let totalTimeValuePrecision = totalTimeValues[this.precision];
totalTimeValuePrecision -= this._lastTotalTimeValues ? this._lastTotalTimeValues[this.precision] : 0;
if(
this._config.interval === 1 ||
totalTimeValuePrecision % this._config.interval === 0
this.get('interval') === 1 ||
totalTimeValuePrecision % this.get('interval') === 0
){
return true;
}
@@ -133,23 +226,30 @@ define([
return false;
}
/**
* @param timer
*/
invoke(timer){
if(
!this.paused &&
this.isDue(timer) &&
(!this.isRunning() || this._config.isParallel)
(!this.isRunning() || this.get('isParallel'))
){
this.run(timer);
}
}
/**
* @param timer
*/
run(timer){
this._lastTotalTimeValues = Object.assign({}, timer.getTotalTimeValues());
let runId = 'run_' + (++this._runCount);
let runExec = resolve => {
resolve(this.task(timer, this));
resolve(this._task(timer, this));
};
let myProm = this._config.timeout > 0 ? new TimeoutPromise(runExec, this._config.timeout) : new Promise(runExec);
let myProm = this.get('timeout') > 0 ? new TimeoutPromise(runExec, this.get('timeout')) : new Promise(runExec);
myProm.then(payload => {
// resolved within timeout -> wait for finally() block
}).catch(error => {
@@ -162,14 +262,37 @@ define([
// -> remove from _runQueue
this._runQueue.delete(runId);
// remove this task from store after run
if(this._config.dueDate instanceof Date){
this.delete();
if(this.get('dueDate') instanceof Date){
this.disconnect();
}
if(this.targetAchieved){
this.stop();
}
});
this._runQueue.set(runId, myProm);
}
// Task controls ----------------------------------------------------------------------------------------------
start(){
this.set('paused', false);
this.connect();
}
stop(){
this.reset();
this.disconnect();
}
pause(){
this.set('paused', true);
}
reset(){
this._runCount = 0;
}
};
Task.defaultConfig = {
@@ -177,7 +300,9 @@ define([
isParallel: false, // if true this task can run parallel, e.g. if prev execution has not finished
interval: 1, // relates to 'precision'. 'interval' = 3 and 'precision' = "seconds" -> run every 3 seconds
dueDate: undefined, // if Date() instance is set, task only runs once at dueDate
timeout: 50 // if > 0, execution time that exceeds timeout (ms) throw error
timeout: 50, // if > 0, execution time that exceeds timeout (ms) throw error
paused: false, // if true this task will not run() but will be invoce()´ed
targetRunCount: 0 // if > 0, task will stop if targetRunCount is reached
};
@@ -188,6 +313,9 @@ define([
*/
let CronManager = class CronManager {
/**
* @param {{}} config
*/
constructor(config){
this._config = Object.assign({}, this.constructor.defaultConfig, config);
this._timerConfig = Object.assign({}, this.constructor.defaultTimerConfig);
@@ -211,47 +339,75 @@ define([
};
}
/**
* @param {string} name
* @param {{}} config
* @returns {Task}
*/
new(name, config){
return new Task(name, config);
return new Task(name, config, this);
}
/**
* @param {Task} task
*/
set(task){
if(task instanceof Task){
// check for unique task name, or update existing task
if(!this.has(task.name) || (this.get(task.name) === task)){
// set new or update existing task
task.setManager(this);
// check for unique task name
if(!this.has(task.name)){
// connect new task
// -> must be before connect(this)! (prevents infinite loop)
this._tasks.set(task.name, task);
task.connect(this);
this.debug('SET/UPDATE task: %o config: %o', task.name, task);
// start timer (if it is not already running)
this.auto();
}else{
console.warn('FAILED to set task. Task name %o already exists', task.name);
}
}else{
throw new TypeError('Parameter must be instance of Task');
}
}
/**
* @param {string} name
* @param {{}} config
*/
setNew(name, config){
this.set(this.new(name, config));
}
/**
* @param {string} name
* @returns {Task|undefined}
*/
get(name){
return this._tasks.get(name);
}
/**
* @param {string} name
* @returns {boolean}
*/
has(name){
return this._tasks.has(name);
}
/**
* @param {string} name
*/
delete(name){
let isDeleted = this._tasks.delete(name);
if(isDeleted){
if(this.has(name)){
let task = this._tasks.get(name);
// disconnect task
// -> must be before disconnect(this)! (prevents infinite loop)
this._tasks.delete(name);
task.disconnect(this);
this.debug('DELETE task: %o', name);
// stop timer (if no more tasks connected)
this.auto();
}
return isDeleted;
}
clear(){
@@ -260,6 +416,10 @@ define([
this.auto();
}
/**
* @param {string} precision
* @returns {Task[]}
*/
tasksByPrecision(precision){
let tasks = [];
this._tasks.forEach(task => {

View File

@@ -34,7 +34,7 @@ define([], () => {
}
get(obj, key) {
return this._store.has(obj) && this._store.get(obj).get(key);
return this._store.has(obj) && (key ? this._store.get(obj).get(key) : this._store.get(obj));
}
has(obj, key) {
@@ -45,6 +45,8 @@ define([], () => {
let ret = false;
if (this._store.has(obj)) {
ret = this._store.get(obj).delete(key);
// remove obj if store is empty
// -> 'size' property is does not exist if valueStore is WeakMap
if (!this._store.get(obj).size) {
this._store.delete(obj);
}

View File

@@ -193,10 +193,10 @@ define([
}
/**
* set LocalStoreManager for this instance
* connect LocalStoreManager with instance
* @param {LocalStoreManager} manager
*/
setManager(manager){
connect(manager){
if(manager instanceof LocalStoreManager){
this._manager = manager;
}else{
@@ -299,7 +299,7 @@ define([
/**
* check var for Object
* @param obj
* @returns {boolean|boolean}
* @returns {boolean}
*/
static isObject(obj){
return (!!obj) && (obj.constructor === Object);
@@ -372,7 +372,7 @@ define([
}, {
name: LocalStore.buildDbName(name)
});
store.setManager(this);
store.connect(this);
this._store.set(name, store);
}
return this._store.get(name);

View File

@@ -103,14 +103,9 @@ define([
* @returns {number}
*/
String.prototype.hashCode = function(){
let hash = 0, i, chr;
if(this.length === 0) return hash;
for(i = 0; i < this.length; i++){
chr = this.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
let hash = this.split('').reduce((a,b) => (((a << 5) - a) + b.charCodeAt(0))|0, 0);
// make positive
return (hash + 2147483647) + 1;
};
String.prototype.trimLeftChars = function(charList){

View File

@@ -222,7 +222,7 @@ define([
* @param excludeMenu
*/
let closeMenus = excludeMenu => {
let allMenus = $('.' + config.contextMenuClass + '[role="menu"]');
let allMenus = $('.' + config.contextMenuClass + '[role="menu"][style*="display: block"]');
if(excludeMenu){
allMenus = allMenus.not(excludeMenu);
}
@@ -291,17 +291,15 @@ define([
/**
* default config (skeleton) for valid context menu configuration
* @returns {{hidden: Array, active: Array, disabled: Array, id: string, selectCallback: null}}
* @returns {{hidden: [], active: [], disabled: [], id: string, selectCallback: null}}
*/
let defaultMenuOptionConfig = () => {
return {
'id': '',
'selectCallback': null,
'hidden': [],
'active': [],
'disabled': []
};
};
let defaultMenuOptionConfig = () => ({
'id': '',
'selectCallback': null,
'hidden': [],
'active': [],
'disabled': []
});
return {
config: config,

View File

@@ -29,7 +29,6 @@ define([
mapIdPrefix: 'pf-map-', // id prefix for all maps
systemClass: 'pf-system', // class for all systems
systemActiveClass: 'pf-system-active', // class for an active system on a map
systemSelectedClass: 'pf-system-selected', // class for selected systems on a map
systemHeadClass: 'pf-system-head', // class for system head
systemHeadNameClass: 'pf-system-head-name', // class for system name
@@ -116,7 +115,7 @@ define([
},
connectionsDetachable: true, // dragOptions are set -> allow detaching them
maxConnections: 10, // due to isTarget is true, this is the max count of !out!-going connections
// isSource:true
//isSource:true
},
target: {
filter: filterSystemHeadEvent,
@@ -125,10 +124,10 @@ define([
//allowLoopBack: false, // loopBack connections are not allowed
cssClass: config.endpointTargetClass,
dropOptions: {
hoverClass: config.systemActiveClass,
//hoverClass: '',
activeClass: 'dragActive'
},
// uniqueEndpoint: false
//uniqueEndpoint: false
},
endpointTypes: Init.endpointTypes,
connectionTypes: Init.connectionTypes
@@ -876,23 +875,23 @@ define([
* connect two systems
* @param map
* @param connectionData
* @returns new connection
* @returns {Promise<any>}
*/
let drawConnection = (map, connectionData) => {
let drawConnection = (map, connectionData) => new Promise((resolve, reject) => {
let mapContainer = $(map.getContainer());
let mapId = mapContainer.data('id');
let connectionId = connectionData.id || 0;
let connection;
let sourceSystem = $('#' + MapUtil.getSystemId(mapId, connectionData.source));
let targetSystem = $('#' + MapUtil.getSystemId(mapId, connectionData.target));
// check if both systems exists
// (If not -> something went wrong e.g. DB-Foreign keys for "ON DELETE",...)
if(
sourceSystem.length &&
targetSystem.length
){
connection = map.connect({
if(!sourceSystem.length){
reject(new Error(`drawConnection(): source system (id: ${connectionData.source}) not found`));
}else if(!targetSystem.length){
reject(new Error(`drawConnection(): target system (id: ${connectionData.target}) not found`));
}else{
let connection = map.connect({
source: sourceSystem[0],
target: targetSystem[0],
scope: connectionData.scope || map.Defaults.Scope,
@@ -948,18 +947,18 @@ define([
}
}
}
}
}else{
if( !sourceSystem.length ){
console.warn('drawConnection(): source system (id: ' + connectionData.source + ') not found');
}
if( !targetSystem.length ){
console.warn('drawConnection(): target system (id: ' + connectionData.target + ') not found');
resolve({
action: 'drawConnection',
data: {
connection: connection
}
});
}else{
reject(new Error(`drawConnection(): connection must be instanceof jsPlumb.Connection`));
}
}
return connection;
};
});
/**
* compares the current data and new data of a connection and updates status
@@ -1233,168 +1232,178 @@ define([
* @param reject
*/
let updateMapExecutor = (resolve, reject) => {
// jsPlumb needs to be initialized. This is not the case when switching between map tabs right after refresh
let mapContainer = mapConfig.map ? $(mapConfig.map.getContainer()) : null;
if(mapContainer){
let mapId = mapConfig.config.id;
// add additional information for this map
if(mapContainer.data('updated') !== mapConfig.config.updated.updated){
mapContainer.data('name', mapConfig.config.name);
mapContainer.data('scopeId', mapConfig.config.scope.id);
mapContainer.data('typeId', mapConfig.config.type.id);
mapContainer.data('typeName', mapConfig.config.type.name);
mapContainer.data('icon', mapConfig.config.icon);
mapContainer.data('created', mapConfig.config.created.created);
mapContainer.data('updated', mapConfig.config.updated.updated);
}
// get map data
let mapData = getMapDataForSync(mapContainer, [], true);
if(mapData !== false){
// map data available -> map not locked by update counter :)
let currentSystemData = mapData.data.systems;
let currentConnectionData = mapData.data.connections;
// update systems =================================================================================
for(let i = 0; i < mapConfig.data.systems.length; i++){
let systemData = mapConfig.data.systems[i];
// add system
let addNewSystem = true;
for(let k = 0; k < currentSystemData.length; k++){
if(currentSystemData[k].id === systemData.id){
if(currentSystemData[k].updated.updated < systemData.updated.updated){
// system changed -> update
mapContainer.getSystem(mapConfig.map, systemData);
}
addNewSystem = false;
break;
}
}
if(addNewSystem === true){
drawSystem(mapConfig.map, systemData);
}
}
// check for systems that are gone -> delete system
for(let a = 0; a < currentSystemData.length; a++){
let deleteThisSystem = true;
for(let b = 0; b < mapConfig.data.systems.length; b++){
let deleteSystemData = mapConfig.data.systems[b];
if(deleteSystemData.id === currentSystemData[a].id){
deleteThisSystem = false;
break;
}
}
if(deleteThisSystem === true){
let deleteSystem = $('#' + MapUtil.getSystemId(mapContainer.data('id'), currentSystemData[a].id));
// system not found -> delete system
System.removeSystems(mapConfig.map, deleteSystem);
}
}
// update connections =============================================================================
// jsPlumb batch() is used, otherwise there are some "strange" visual bugs
// when switching maps (Endpoints are not displayed correctly)
mapConfig.map.batch(function(){
for(let j = 0; j < mapConfig.data.connections.length; j++){
let connectionData = mapConfig.data.connections[j];
// add connection
let addNewConnection= true;
for(let c = 0; c < currentConnectionData.length; c++){
if(currentConnectionData[c].id === connectionData.id){
// connection already exists -> check for updates
if(currentConnectionData[c].updated < connectionData.updated){
// connection changed -> update
updateConnection(currentConnectionData[c].connection, connectionData);
}
addNewConnection = false;
break;
}else if(
currentConnectionData[c].id === 0 &&
currentConnectionData[c].source === connectionData.source &&
currentConnectionData[c].target === connectionData.target
){
// if ids don´t match -> check for unsaved connection
updateConnection(currentConnectionData[c].connection, connectionData);
addNewConnection = false;
break;
}
}
if(addNewConnection === true){
drawConnection(mapConfig.map, connectionData);
}
}
// check for connections that are gone -> delete connection
for(let d = 0; d < currentConnectionData.length; d++){
// skip connections with id = 0 -> they might get updated before
if(currentConnectionData[d].id === 0){
continue;
}
let deleteThisConnection = true;
for(let e = 0; e < mapConfig.data.connections.length;e++){
let deleteConnectionData = mapConfig.data.connections[e];
if(deleteConnectionData.id === currentConnectionData[d].id){
deleteThisConnection = false;
break;
}
}
if(deleteThisConnection === true){
// connection not found -> delete connection
let deleteConnection = currentConnectionData[d].connection;
if(deleteConnection){
// check if "source" and "target" still exist before remove
// this is NOT the case if the system was removed previous
if(
deleteConnection.source &&
deleteConnection.target
){
mapConfig.map.deleteConnection(deleteConnection, {fireEvent: false});
}
}
}
}
});
// update local connection cache
updateConnectionsCache(mapConfig.map);
}else{
// map is currently logged -> queue update for this map until unlock
if( mapUpdateQueue.indexOf(mapId) === -1 ){
mapUpdateQueue.push(mapId);
}
}
}
resolve({
let payload = {
action: 'updateMap',
data: {
mapConfig: mapConfig
}
});
};
// jsPlumb needs to be initialized. This is not the case when switching between map tabs right after refresh
let mapContainer = mapConfig.map ? mapConfig.map.getContainer() : null;
if(!mapContainer){
return resolve(payload);
}
let mapId = mapConfig.config.id;
// mapData == false -> map locked by update counter. Skip update
let mapData = getMapDataForSync(mapContainer, [], true);
if(!mapData){
// map is currently locked -> queue update for this map until unlock
if(mapUpdateQueue.indexOf(mapId) === -1){
mapUpdateQueue.push(mapId);
}
return resolve(payload);
}
mapContainer = $(mapContainer);
// add additional information for this map
if(mapContainer.data('updated') !== mapConfig.config.updated.updated){
mapContainer.data('name', mapConfig.config.name);
mapContainer.data('scopeId', mapConfig.config.scope.id);
mapContainer.data('typeId', mapConfig.config.type.id);
mapContainer.data('typeName', mapConfig.config.type.name);
mapContainer.data('icon', mapConfig.config.icon);
mapContainer.data('created', mapConfig.config.created.created);
mapContainer.data('updated', mapConfig.config.updated.updated);
}
// map data available -> map not locked by update counter :)
let currentSystemData = mapData.data.systems;
let currentConnectionData = mapData.data.connections;
// update systems =========================================================================================
for(let i = 0; i < mapConfig.data.systems.length; i++){
let systemData = mapConfig.data.systems[i];
// add system
let addNewSystem = true;
for(let k = 0; k < currentSystemData.length; k++){
if(currentSystemData[k].id === systemData.id){
if(currentSystemData[k].updated.updated < systemData.updated.updated){
// system changed -> update
mapContainer.getSystem(mapConfig.map, systemData);
}
addNewSystem = false;
break;
}
}
if(addNewSystem === true){
drawSystem(mapConfig.map, systemData).catch(console.warn);
}
}
// check for systems that are gone -> delete system
for(let a = 0; a < currentSystemData.length; a++){
let deleteThisSystem = true;
for(let b = 0; b < mapConfig.data.systems.length; b++){
let deleteSystemData = mapConfig.data.systems[b];
if(deleteSystemData.id === currentSystemData[a].id){
deleteThisSystem = false;
break;
}
}
if(deleteThisSystem === true){
let deleteSystem = $('#' + MapUtil.getSystemId(mapContainer.data('id'), currentSystemData[a].id));
// system not found -> delete system
System.removeSystems(mapConfig.map, deleteSystem);
}
}
// update connections =====================================================================================
// jsPlumb setSuspendDrawing() (batch() did not work because it async 'scopes' out updates).
// -> Otherwise there are some "strange" visual bugs when switching maps (Endpoints are not displayed correctly)
// -> needs to be "disabled" later in this method.
mapConfig.map.setSuspendDrawing(true);
for(let j = 0; j < mapConfig.data.connections.length; j++){
let connectionData = mapConfig.data.connections[j];
// add connection
let addNewConnection= true;
for(let c = 0; c < currentConnectionData.length; c++){
if(currentConnectionData[c].id === connectionData.id){
// connection already exists -> check for updates
if(currentConnectionData[c].updated < connectionData.updated){
// connection changed -> update
updateConnection(currentConnectionData[c].connection, connectionData);
}
addNewConnection = false;
break;
}else if(
currentConnectionData[c].id === 0 &&
currentConnectionData[c].source === connectionData.source &&
currentConnectionData[c].target === connectionData.target
){
// if ids don´t match -> check for unsaved connection
updateConnection(currentConnectionData[c].connection, connectionData);
addNewConnection = false;
break;
}
}
if(addNewConnection === true){
drawConnection(mapConfig.map, connectionData).catch(console.warn);
}
}
// check for connections that are gone -> delete connection
for(let d = 0; d < currentConnectionData.length; d++){
// skip connections with id = 0 -> they might get updated before
if(currentConnectionData[d].id === 0){
continue;
}
let deleteThisConnection = true;
for(let e = 0; e < mapConfig.data.connections.length;e++){
let deleteConnectionData = mapConfig.data.connections[e];
if(deleteConnectionData.id === currentConnectionData[d].id){
deleteThisConnection = false;
break;
}
}
if(deleteThisConnection === true){
// connection not found -> delete connection
let deleteConnection = currentConnectionData[d].connection;
if(deleteConnection){
// check if "source" and "target" still exist before remove
// this is NOT the case if the system was removed previous
if(
deleteConnection.source &&
deleteConnection.target
){
mapConfig.map.deleteConnection(deleteConnection, {fireEvent: false});
}
}
}
}
mapConfig.map.setSuspendDrawing(false, true);
// update local connection cache
updateConnectionsCache(mapConfig.map);
return resolve(payload);
};
/**
@@ -1402,44 +1411,53 @@ define([
* @param payload
* @returns {Promise<any>}
*/
let filterMapByScopes = payload => {
let filterMapByScopesExecutor = resolve => {
Util.getLocalStore('map').getItem(payload.data.mapConfig.config.id).then(dataStore => {
let scopes = [];
if(dataStore && dataStore.filterScopes){
scopes = dataStore.filterScopes;
}
MapUtil.filterMapByScopes(payload.data.mapConfig.map, scopes);
resolve(payload);
});
};
return new Promise(filterMapByScopesExecutor);
};
let filterMapByScopes = payload => new Promise(resolve => {
Util.getLocalStore('map').getItem(payload.data.mapConfig.config.id).then(dataStore => {
let scopes = [];
if(dataStore && dataStore.filterScopes){
scopes = dataStore.filterScopes;
}
MapUtil.filterMapByScopes(payload.data.mapConfig.map, scopes);
resolve(payload);
});
});
/**
* show signature overlays
* @param payload
* @returns {Promise<any>}
*/
let showInfoSignatureOverlays = payload => {
let showInfoSignatureOverlaysExecutor = resolve => {
Util.getLocalStore('map').getItem(payload.data.mapConfig.config.id).then(dataStore => {
if(dataStore && dataStore.mapSignatureOverlays){
MapOverlay.showInfoSignatureOverlays($(payload.data.mapConfig.map.getContainer()));
}
let showInfoSignatureOverlays = payload => new Promise(resolve => {
Util.getLocalStore('map').getItem(payload.data.mapConfig.config.id).then(dataStore => {
if(dataStore && dataStore.mapSignatureOverlays){
MapOverlay.showInfoSignatureOverlays($(payload.data.mapConfig.map.getContainer()));
}
resolve(payload);
});
});
resolve(payload);
/**
* after map update is complete
* -> trigger update event for 'global' modules
* @param payload
* @returns {Promise<any>}
*/
let afterUpdate = payload => new Promise(resolve => {
// in rare cases there is a bug where map is undefined (hard to reproduce
let map = Util.getObjVal(payload, 'data.mapConfig.map');
if(map){
let tabContentEl = map.getContainer().closest(`.${Util.config.mapTabContentClass}`);
$(tabContentEl).trigger('pf:updateGlobalModules', {
payload: Util.getObjVal(payload, 'data.mapConfig.config.id')
});
};
return new Promise(showInfoSignatureOverlaysExecutor);
};
}
resolve(payload);
});
return new Promise(updateMapExecutor)
.then(showInfoSignatureOverlays)
.then(filterMapByScopes);
.then(filterMapByScopes)
.then(afterUpdate);
};
/**
@@ -1530,28 +1548,22 @@ define([
* @param systemData
* @returns {boolean}
*/
let isValidSystem = systemData => {
let isValid = true;
if(
!systemData.hasOwnProperty('name') ||
systemData.name.length === 0
){
return false;
}
return isValid;
};
let isValidSystem = systemData => (Util.getObjVal(systemData, 'name') || '').length > 0;
/**
* draw a system with its data to a map
* @param map
* @param systemData
* @param connectedSystem
* @param connectionData
* @returns {Promise<any>}
*/
let drawSystem = (map, systemData, connectedSystem) => {
// check if systemData is valid
let drawSystem = (map, systemData, connectedSystem, connectionData = null) => new Promise((resolve, reject) => {
if(isValidSystem(systemData)){
let payloadDrawSystem = {
action: 'drawSystem'
};
let mapContainer = $(map.getContainer());
// get System Element by data
@@ -1575,24 +1587,37 @@ define([
// register system to "magnetizer"
Magnetizer.addElement(systemData.mapId, newSystem[0]);
payloadDrawSystem.data = {
system: newSystem
};
// connect new system (if connection data is given)
if(connectedSystem){
// hint: "scope + type" might be changed automatically when it gets saved
// -> based on jump distance,..
let connectionData = {
connectionData = Object.assign({}, {
source: $(connectedSystem).data('id'),
target: newSystem.data('id'),
scope: map.Defaults.Scope,
type: [MapUtil.getDefaultConnectionTypeByScope(map.Defaults.Scope)]
};
let connection = drawConnection(map, connectionData);
}, connectionData);
// store connection
saveConnection(connection);
drawConnection(map, connectionData)
.then(payload => saveConnection(payload.data.connection, Boolean(connectionData.disableAutoScope)))
.then(payload => {
payloadDrawSystem.data = {
connection: payload.data.connection
};
resolve(payloadDrawSystem);
})
.catch(reject);
}else{
resolve(payloadDrawSystem);
}
}else{
reject(new Error(`drawSystem() failed. Invalid systemData`));
}
};
});
/**
* make a system name/alias editable by x-editable
@@ -1656,56 +1681,69 @@ define([
/**
* stores a connection in database
* @param connection
* @param disableAutoScope
* @returns {Promise<any>}
*/
let saveConnection = connection => {
if(connection instanceof jsPlumb.Connection){
connection.addType('state_process');
let map = connection._jsPlumb.instance;
let mapContainer = $(map.getContainer());
let mapId = mapContainer.data('id');
let connectionData = MapUtil.getDataByConnection(connection);
connectionData.mapId = mapId;
Util.request('PUT', 'connection', [], connectionData, {
connection: connection,
map: map,
mapId: mapId,
oldConnectionData: connectionData
}).then(
payload => {
let newConnectionData = payload.data;
if(!$.isEmptyObject(newConnectionData)){
// update connection data e.g. "scope" has auto detected
connection = updateConnection(payload.context.connection, newConnectionData);
// new/updated connection should be cached immediately!
updateConnectionCache(payload.context.mapId, connection);
// connection scope
let scope = MapUtil.getScopeInfoForConnection(newConnectionData.scope, 'label');
let title = 'New connection established';
if(payload.context.oldConnectionData.id > 0){
title = 'Connection switched';
}
Util.showNotify({title: title, text: 'Scope: ' + scope, type: 'success'});
}else{
// some save errors
payload.context.map.deleteConnection(payload.context.connection, {fireEvent: false});
}
},
payload => {
// remove this connection from map
payload.context.map.deleteConnection(payload.context.connection, {fireEvent: false});
Util.handleAjaxErrorResponse(payload);
}
);
let saveConnection = (connection, disableAutoScope = false) => new Promise((resolve, reject) => {
if(!(connection instanceof jsPlumb.Connection)){
reject(new Error(`saveConnection(): connection must be instanceof jsPlumb.Connection`));
}
};
connection.addType('state_process');
let map = connection._jsPlumb.instance;
let mapContainer = $(map.getContainer());
let mapId = mapContainer.data('id');
let connectionData = MapUtil.getDataByConnection(connection);
connectionData.mapId = mapId;
connectionData.disableAutoScope = disableAutoScope;
Util.request('PUT', 'connection', [], connectionData, {
connection: connection,
map: map,
mapId: mapId,
oldConnectionData: connectionData
}).then(
payload => {
let newConnectionData = payload.data;
if(!$.isEmptyObject(newConnectionData)){
// update connection data e.g. "scope" has auto detected
connection = updateConnection(payload.context.connection, newConnectionData);
// new/updated connection should be cached immediately!
updateConnectionCache(payload.context.mapId, connection);
// connection scope
let scope = MapUtil.getScopeInfoForConnection(newConnectionData.scope, 'label');
let title = 'New connection established';
if(payload.context.oldConnectionData.id > 0){
title = 'Connection switched';
}
Util.showNotify({title: title, text: 'Scope: ' + scope, type: 'success'});
resolve({
action: 'saveConnection',
data: {
connection: connection
}
});
}else{
// some save errors
payload.context.map.deleteConnection(payload.context.connection, {fireEvent: false});
reject(new Error(`saveConnection(): response error`));
}
},
payload => {
// remove this connection from map
payload.context.map.deleteConnection(payload.context.connection, {fireEvent: false});
Util.handleAjaxErrorResponse(payload);
reject(new Error(`saveConnection(): request error`));
}
);
});
/**
* get context menu config for a map component (e.g. system, connection,..)
@@ -1742,7 +1780,7 @@ define([
options.hidden.push('delete_system');
}
if( !mapContainer.find('.' + config.systemActiveClass).length){
if( !mapContainer.find('.' + MapUtil.config.systemActiveClass).length){
options.hidden.push('find_route');
}
@@ -1755,7 +1793,7 @@ define([
}
// disabled menu actions
if(system.hasClass(config.systemActiveClass)){
if(system.hasClass(MapUtil.config.systemActiveClass)){
options.disabled.push('find_route');
}
@@ -1898,6 +1936,8 @@ define([
// map overlay will be set on "drag" start
let mapOverlayTimer = null;
let debounceDrag = false;
// make system draggable
map.draggable(system, {
containment: 'parent',
@@ -1907,7 +1947,7 @@ define([
snapThreshold: MapUtil.config.mapSnapToGridDimension, // distance for grid snapping "magnet" effect (optional)
start: function(params){
let dragSystem = $(params.el);
dragSystem.css('pointer-events','none');
mapOverlayTimer = MapOverlayUtil.getMapOverlay(dragSystem, 'timer');
// start map update timer
@@ -1920,9 +1960,6 @@ define([
delete( params.drag.params.grid );
}
// stop "system click event" right after drop event is finished
dragSystem.addClass('no-click');
// drag system is not always selected
let selectedSystems = mapContainer.getSelectedSystems().get();
selectedSystems = selectedSystems.concat(dragSystem.get());
@@ -1938,12 +1975,19 @@ define([
$(selectedSystems).updateSystemZIndex();
},
drag: function(p){
// start map update timer
mapOverlayTimer.startMapUpdateCounter();
if(!debounceDrag) {
requestAnimationFrame(() => {
// start map update timer
mapOverlayTimer.startMapUpdateCounter();
// update system positions for "all" systems that are effected by drag&drop
// this requires "magnet" feature to be active! (optional)
Magnetizer.executeAtEvent(map, p.e);
// update system positions for "all" systems that are effected by drag&drop
// this requires "magnet" feature to be active! (optional)
Magnetizer.executeAtEvent(map, p.e);
debounceDrag = false;
});
}
debounceDrag = true;
},
stop: function(params){
let dragSystem = $(params.el);
@@ -1951,10 +1995,6 @@ define([
// start map update timer
mapOverlayTimer.startMapUpdateCounter();
setTimeout(function(){
dragSystem.removeClass('no-click');
}, Init.timer.DBL_CLICK + 50);
// show tooltip
dragSystem.toggleSystemTooltip('show', {show: true});
@@ -1975,7 +2015,7 @@ define([
// update all dragged systems -> added to DragSelection
params.selection.forEach(elData => {
MapUtil.markAsChanged($(elData[0]));
MapUtil.markAsChanged($(elData[0]).css('pointer-events','initial'));
});
}
});
@@ -2016,23 +2056,20 @@ define([
if(!popoverClick){
let system = $(this);
// check if system is locked for "click" events
if(!system.hasClass('no-click')){
// left mouse button
if(e.which === 1){
if(e.ctrlKey === true){
// select system
MapUtil.toggleSystemsSelect(map, [system]);
}else{
MapUtil.showSystemInfo(map, system);
}
// left mouse button
if(e.which === 1){
if(e.ctrlKey === true){
// select system
MapUtil.toggleSystemsSelect(map, [system]);
}else{
MapUtil.showSystemInfo(map, system);
}
}
}
};
Util.singleDoubleClick(system, single, double);
Util.singleDoubleClick(system[0], single, double);
};
/**
@@ -2040,10 +2077,10 @@ define([
* @param map
* @param newSystemData
* @param sourceSystem
* @param connectionData
* @returns {Promise<any>}
*/
let saveSystemCallback = (map, newSystemData, sourceSystem) => {
drawSystem(map, newSystemData, sourceSystem);
};
let saveSystemCallback = (map, newSystemData, sourceSystem, connectionData = null) => drawSystem(map, newSystemData, sourceSystem, connectionData);
/**
* select all (selectable) systems on a mapElement
@@ -2193,17 +2230,8 @@ define([
return false;
}
// lock the target system for "click" events
// to prevent loading system information
let sourceSystem = $('#' + sourceId);
let targetSystem = $('#' + targetId);
sourceSystem.addClass('no-click');
targetSystem.addClass('no-click');
setTimeout(() => {
sourceSystem.removeClass('no-click');
targetSystem.removeClass('no-click');
}, Init.timer.DBL_CLICK + 50);
// switch connection type to "abyss" in case source OR target system belongs to "a-space"
if(sourceSystem.data('typeId') === 3 || targetSystem.data('typeId') === 3){
@@ -2227,7 +2255,7 @@ define([
}
// always save the new connection
saveConnection(connection);
saveConnection(connection).catch(console.warn);
return true;
});
@@ -2721,7 +2749,7 @@ define([
selectSystem(mapContainer, data);
break;
case 'AddSystem':
System.showNewSystemDialog(map, data, saveSystemCallback);
System.showNewSystemDialog(map, data, typeof data.callback === 'function' ? data.callback : saveSystemCallback);
break;
default:
console.warn('Unknown menuAction %o event name', action);
@@ -2938,16 +2966,16 @@ define([
/**
* collect all map data from client for server or client sync
* @param mapContainer
* @param {HTMLElement} mapContainer
* @param filter
* @param minimal
* @returns {boolean}
* @returns {boolean|{}}
*/
let getMapDataForSync = (mapContainer, filter = [], minimal = false) => {
let mapData = false;
// check if there is an active map counter that prevents collecting map data (locked map)
if(!MapOverlayUtil.getMapOverlayInterval(mapContainer)){
mapData = mapContainer.getMapDataFromClient(filter, minimal);
if(!MapOverlayUtil.isMapCounterOverlayActive(mapContainer)){
mapData = $(mapContainer).getMapDataFromClient(filter, minimal);
}
return mapData;
};
@@ -2957,6 +2985,7 @@ define([
* this function returns the "client" data NOT the "server" data for a map
* @param filter
* @param minimal
* @returns {{}}
*/
$.fn.getMapDataFromClient = function(filter = [], minimal = false){
let mapContainer = $(this);
@@ -3099,34 +3128,29 @@ define([
* @param options
* @returns {Promise<any>}
*/
let initMapOptions = (mapConfig, options) => {
let initMapOptionsExecutor = (resolve, reject) => {
let payload = {
action: 'initMapOptions',
data: {
mapConfig: mapConfig
}
};
if(options.showAnimation){
let mapElement = $(mapConfig.map.getContainer());
MapUtil.setMapDefaultOptions(mapElement, mapConfig.config)
.then(payload => MapUtil.visualizeMap(mapElement, 'show'))
.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'});
})
.then(() => resolve(payload));
}else{
// nothing to do here...
resolve(payload);
let initMapOptions = (mapConfig, options) => new Promise((resolve, reject) => {
let payload = {
action: 'initMapOptions',
data: {
mapConfig: mapConfig
}
};
return new Promise(initMapOptionsExecutor);
};
if(options.showAnimation){
let mapElement = $(mapConfig.map.getContainer());
MapUtil.setMapDefaultOptions(mapElement, mapConfig.config)
.then(payload => MapUtil.visualizeMap(mapElement, 'show'))
.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'});
})
.then(() => resolve(payload));
}else{
// nothing to do here...
resolve(payload);
}
});
/**
* load OR updates system map
@@ -3136,6 +3160,9 @@ define([
* @returns {Promise<any>}
*/
let loadMap = (areaMap, mapConfig, options) => {
// whether map gets loaded (initialized) for the first time
// or just updated an existing map
let isFirstLoad = false;
/**
* load map promise
@@ -3150,6 +3177,7 @@ define([
if(mapConfig.map.getContainer() === undefined){
// map not loaded -> create & update
isFirstLoad = true;
newMapElement(areaMap, mapConfig)
.then(payload => updateMap(payload.data.mapConfig))
.then(payload => resolve(payload));
@@ -3162,7 +3190,12 @@ define([
};
return new Promise(loadMapExecutor)
.then(payload => initMapOptions(payload.data.mapConfig, options));
.then(payload => initMapOptions(payload.data.mapConfig, options))
.then(payload => ({
action: 'loadMap',
data: payload.data,
isFirstLoad
}));
};
/**
@@ -3330,7 +3363,9 @@ define([
loadMap: loadMap,
updateUserData: updateUserData,
getMapDataForSync: getMapDataForSync,
saveSystemCallback: saveSystemCallback
saveSystemCallback: saveSystemCallback,
drawConnection: drawConnection,
saveConnection: saveConnection
};
});

View File

@@ -7,8 +7,9 @@ define([
'app/init',
'app/util',
'app/map/overlay/util',
'app/map/util'
], ($, Init, Util, MapOverlayUtil, MapUtil) => {
'app/map/util',
'app/lib/cron'
], ($, Init, Util, MapOverlayUtil, MapUtil, Cron) => {
'use strict';
/**
@@ -107,119 +108,120 @@ define([
let type = 'info_signature';
connectionsData = Util.arrayToObject(connectionsData);
map.batch(() => {
map.getAllConnections().forEach(connection => {
let connectionId = connection.getParameter('connectionId');
let sourceEndpoint = connection.endpoints[0];
let targetEndpoint = connection.endpoints[1];
map.setSuspendDrawing(true);
let connectionData = connectionsData.hasOwnProperty(connectionId) ? connectionsData[connectionId] : undefined;
let signatureTypeData = MapUtil.getConnectionDataFromSignatures(connection, connectionData);
map.getAllConnections().forEach(connection => {
let connectionId = connection.getParameter('connectionId');
let sourceEndpoint = connection.endpoints[0];
let targetEndpoint = connection.endpoints[1];
let sizeLockedBySignature = false;
let connectionData = Util.getObjVal(connectionsData, `${connectionId}`);
let signatureTypeData = MapUtil.getConnectionDataFromSignatures(connection, connectionData);
if(connection.scope === 'wh'){
if(!connection.hasType(type)){
connection.addType(type);
}
let sizeLockedBySignature = false;
let overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId);
if(connection.scope === 'wh'){
if(!connection.hasType(type)){
connection.addType(type);
}
// Arrow overlay needs to be cleared() (removed) if 'info_signature' gets removed!
// jsPlumb does not handle overlay updates for Arrow overlays... so we need to re-apply the the overlay manually
if(overlayArrow.path && !overlayArrow.path.isConnected){
connection.canvas.appendChild(overlayArrow.path);
}
let overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId);
// 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;
// Arrow overlay needs to be cleared() (removed) if 'info_signature' gets removed!
// jsPlumb does not handle overlay updates for Arrow overlays... so we need to re-apply the the overlay manually
if(overlayArrow.path && !overlayArrow.path.isConnected){
connection.canvas.appendChild(overlayArrow.path);
}
let overlayType = 'diamond'; // not specified
let arrowDirection = 1;
// since there "could" be multiple sig labels on each endpoint,
// there can only one "primary label picked up for wormhole jump mass detection!
let primeLabel;
if(connectionData && connectionData.signatures){
// signature data found for current connection
let sourceLabel = signatureTypeData.source.labels;
let targetLabel = signatureTypeData.target.labels;
let overlayType = 'diamond'; // not specified
let arrowDirection = 1;
// add arrow (connection) overlay that points from "XXX" => "K162" ----------------------------
if(
(sourceLabel.includes('K162') && targetLabel.includes('K162')) ||
(sourceLabel.length === 0 && targetLabel.length === 0) ||
(
sourceLabel.length > 0 && targetLabel.length > 0 &&
!sourceLabel.includes('K162') && !targetLabel.includes('K162')
)
){
// unknown direction -> show default 'diamond' overlay
overlayType = 'diamond';
}else if(
(sourceLabel.includes('K162')) ||
(sourceLabel.length === 0 && !targetLabel.includes('K162'))
){
// convert default arrow direction
overlayType = 'arrow';
arrowDirection = -1;
if(connectionData && connectionData.signatures){
// signature data found for current connection
let sourceLabel = signatureTypeData.source.labels;
let targetLabel = signatureTypeData.target.labels;
primLabel = targetLabel.find(label => label !== 'K162');
}else{
// default arrow direction is fine
overlayType = 'arrow';
// add arrow (connection) overlay that points from "XXX" => "K162" ----------------------------
if(
(sourceLabel.includes('K162') && targetLabel.includes('K162')) ||
(sourceLabel.length === 0 && targetLabel.length === 0) ||
(
sourceLabel.length > 0 && targetLabel.length > 0 &&
!sourceLabel.includes('K162') && !targetLabel.includes('K162')
)
){
// unknown direction -> show default 'diamond' overlay
overlayType = 'diamond';
}else if(
(sourceLabel.includes('K162')) ||
(sourceLabel.length === 0 && !targetLabel.includes('K162'))
){
// convert default arrow direction
overlayType = 'arrow';
arrowDirection = -1;
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'){
connection.updateClasses(
MapOverlayUtil.config.connectionArrowOverlaySuccessClass,
MapOverlayUtil.config.connectionArrowOverlayDangerClass
);
primeLabel = targetLabel.find(label => label !== 'K162');
}else{
connection.updateClasses(
MapOverlayUtil.config.connectionArrowOverlayDangerClass,
MapOverlayUtil.config.connectionArrowOverlaySuccessClass
);
}
// default arrow direction is fine
overlayType = 'arrow';
overlayArrow.updateFrom(getConnectionArrowOverlayParams(overlayType, arrowDirection));
// 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]);
// get 'connection mass type' from wormholeData
let massType = Util.getObjVal(wormholeData, 'size.type');
if(massType && !connection.hasType(massType)){
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);
primeLabel = sourceLabel.find(label => label !== 'K162');
}
}
// lock/unlock connection for manual size changes (from contextmenu)
connection.setParameter('sizeLocked', sizeLockedBySignature);
});
// 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'){
connection.updateClasses(
MapOverlayUtil.config.connectionArrowOverlaySuccessClass,
MapOverlayUtil.config.connectionArrowOverlayDangerClass
);
}else{
connection.updateClasses(
MapOverlayUtil.config.connectionArrowOverlayDangerClass,
MapOverlayUtil.config.connectionArrowOverlaySuccessClass
);
}
overlayArrow.updateFrom(getConnectionArrowOverlayParams(overlayType, arrowDirection));
// 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(primeLabel)){
// connection size from mapped signature
sizeLockedBySignature = true;
// get 'connection mass type' from wormholeData
let massType = Util.getObjVal(Object.assign({}, Init.wormholes[primeLabel]), 'size.type');
if(massType && !connection.hasType(massType)){
MapOverlayUtil.getMapOverlay(connection.canvas, 'timer').startMapUpdateCounter();
MapUtil.setConnectionJumpMassType(connection, massType);
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);
});
map.setSuspendDrawing(false, true);
};
/**
@@ -291,21 +293,23 @@ define([
let map = MapUtil.getMapInstance(mapId);
let type = 'info_signature';
map.batch(() => {
map.getAllConnections().forEach(connection => {
let overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId);
map.setSuspendDrawing(true);
if(overlayArrow){
overlayArrow.cleanup();
}
map.getAllConnections().forEach(connection => {
let overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId);
if(connection.hasType(type)){
connection.removeType(type, {}, true);
}
});
if(overlayArrow){
overlayArrow.cleanup();
}
map.selectEndpoints().removeOverlay(MapOverlayUtil.config.endpointOverlayId);
if(connection.hasType(type)){
connection.removeType(type, {}, true);
}
});
map.selectEndpoints().removeOverlay(MapOverlayUtil.config.endpointOverlayId);
map.setSuspendDrawing(false, true);
};
/**
@@ -321,7 +325,7 @@ define([
trigger: 'active',
class: 'pf-map-overlay-filter',
iconClass: ['fas', 'fa-fw', 'fa-filter'],
onClick: function(e){
onClick: function(e){
// clear all filter
let mapElement = MapOverlayUtil.getMapElementFromOverlay(this);
let map = getMapObjectFromOverlayIcon(this);
@@ -484,108 +488,96 @@ define([
* @param mapOverlayTimer
* @param percent
* @param value
* @returns {*}
*/
let setMapUpdateCounter = (mapOverlayTimer, percent, value) => {
let setMapUpdateCounter = (mapOverlayTimer, percent, value = '') => {
// check if counter already exists
let counterChart = MapOverlayUtil.getMapCounter(mapOverlayTimer);
if(counterChart.length === 0){
if(!MapOverlayUtil.getMapCounter(mapOverlayTimer)){
// create new counter
let chartEl = Object.assign(document.createElement('div'), {
className: `${Init.classes.pieChart.class} ${Init.classes.pieChart.pieChartMapCounterClass}`
});
counterChart = $('<div>', {
class: [Init.classes.pieChart.class, Init.classes.pieChart.pieChartMapCounterClass].join(' ')
}).attr('data-percent', percent).append(
$('<span>', {
text: value
})
);
let chartInnerEl = Object.assign(document.createElement('span'), {
textContent: value
});
let iconEl = Object.assign(document.createElement('i'), {
className: ['fas', 'fa-fw', 'fa-lock'].join(' ')
});
mapOverlayTimer.append(counterChart);
chartInnerEl.append(iconEl);
chartEl.append(chartInnerEl);
mapOverlayTimer.append(chartEl);
// init counter
counterChart.initMapUpdateCounter();
$(chartEl).initMapUpdateCounter();
// set tooltip
mapOverlayTimer.attr('data-placement', 'left');
mapOverlayTimer.attr('title', 'update counter');
mapOverlayTimer.tooltip();
mapOverlayTimer.dataset.placement = 'left';
mapOverlayTimer.setAttribute('title', 'update counter');
$(mapOverlayTimer).tooltip();
}
return counterChart;
};
/**
* start the map update counter or reset
*/
$.fn.startMapUpdateCounter = function(){
let mapOverlayTimer = $(this);
let counterChart = MapOverlayUtil.getMapCounter(mapOverlayTimer);
let maxSeconds = MapOverlayUtil.config.logTimerCount;
let counterChartLabel = counterChart.find('span');
let percentPerCount = 100 / maxSeconds;
// update counter
let updateChart = tempSeconds => {
let pieChart = counterChart.data('easyPieChart');
if(pieChart !== undefined){
counterChart.data('easyPieChart').update( percentPerCount * tempSeconds);
}
counterChartLabel.text(tempSeconds);
};
// main timer function is called on any counter update
let timer = mapUpdateCounter => {
// decrease timer
let currentSeconds = counterChart.data('currentSeconds');
currentSeconds--;
counterChart.data('currentSeconds', currentSeconds);
if(currentSeconds >= 0){
// update counter
updateChart(currentSeconds);
}else{
// hide counter and reset
clearInterval(mapUpdateCounter);
mapOverlayTimer.velocity('transition.whirlOut', {
duration: Init.animationSpeed.mapOverlay,
complete: function(){
counterChart.data('interval', false);
MapOverlayUtil.getMapElementFromOverlay(mapOverlayTimer).trigger('pf:unlocked');
}
});
}
};
// get current seconds (in case the timer is already running)
let currentSeconds = counterChart.data('currentSeconds');
// start values for timer and chart
counterChart.data('currentSeconds', maxSeconds);
updateChart(maxSeconds);
if(
currentSeconds === undefined ||
currentSeconds < 0
){
// start timer
let mapUpdateCounter = setInterval(() => {
timer(mapUpdateCounter);
}, 1000);
// store counter interval
counterChart.data('interval', mapUpdateCounter);
// show overlay
if(mapOverlayTimer.is(':hidden')){
mapOverlayTimer.velocity('stop').velocity('transition.whirlIn', { duration: Init.animationSpeed.mapOverlay });
}
if(!this.length){
console.warn('startMapUpdateCounter() failed. Missing DOM node');
return;
}
let mapOverlayTimer = this[0];
let counterChart = MapOverlayUtil.getMapCounter(mapOverlayTimer);
let pieChart = $(counterChart).data('easyPieChart');
if(!pieChart){
console.warn('startMapUpdateCounter() failed. easyPieChart not initialized');
return;
}
let updateChart = (percent = 0) => {
if(pieChart){
pieChart.update(percent);
}
};
let task = counterChart.getData('counterTask');
if(!task){
let tabContentEl = mapOverlayTimer.closest(`.${Util.config.mapTabContentClass}`);
let mapId = parseInt(tabContentEl.dataset.mapId) || 0;
task = Cron.new(`mapUpdateCounter_${mapId}`, {
precision: 'secondTenths',
isParallel: true,
targetRunCount: 10 * MapOverlayUtil.config.logTimerCount
});
task.task = (timer, task) => {
// debounce 80% (reduce repaint)
if(task.runCount % 5 === 0){
let progress = Math.round(task.targetProgress);
updateChart(100 - progress);
}
if(task.targetAchieved){
$(mapOverlayTimer).velocity('transition.whirlOut', {
duration: Init.animationSpeed.mapOverlay,
complete: function(){
MapOverlayUtil.getMapElementFromOverlay(mapOverlayTimer).trigger('pf:unlocked');
}
});
}
};
counterChart.setData('counterTask', task);
}
// task is not connected if: 'targetAchieved' or not started
if(!task.isConnected()){
$(mapOverlayTimer).velocity('stop').velocity('transition.whirlIn', { duration: Init.animationSpeed.mapOverlay });
}
updateChart(100);
task.reset();
task.start();
};
/**
@@ -836,36 +828,35 @@ define([
});
// add all overlay elements
for(let prop in options){
if(options.hasOwnProperty(prop)){
let icon = $('<i>', {
class: options[prop].iconClass.concat( ['pull-right', options[prop].class] ).join(' ')
}).attr('title', options[prop].title).tooltip({
placement: 'bottom',
container: 'body',
delay: 150
});
Object.entries(options).forEach(([key, option]) => {
let icon = $('<i>', {
class: option.iconClass.concat(['pull-right', option.class]).join(' ')
}).attr('title', option.title).tooltip({
placement: 'bottom',
container: 'body',
delay: 150
});
// add "hover" action for some icons
if(
options[prop].trigger === 'hover' ||
options[prop].trigger === 'refresh'
){
icon.hoverIntent(options[prop].hoverIntent);
}
// add "click" handler for some icons
if(options[prop].hasOwnProperty('onClick')){
icon.on('click', options[prop].onClick);
}
mapOverlayInfo.append(icon);
// add "hover" action for some icons
if(
option.trigger === 'hover' ||
option.trigger === 'refresh'
){
icon.hoverIntent(option.hoverIntent);
}
}
// add "click" handler for some icons
if(option.hasOwnProperty('onClick')){
icon.on('click', option.onClick);
}
mapOverlayInfo.append(icon);
});
parentElement.append(mapOverlayInfo);
// reset map update timer
setMapUpdateCounter(mapOverlayTimer, 100, MapOverlayUtil.config.logTimerCount);
setMapUpdateCounter(mapOverlayTimer[0], 100);
});
};

View File

@@ -84,22 +84,32 @@ define([
/**
* get the map counter chart from overlay
* @param element
* @returns {jQuery}
* @returns {Element}
*/
let getMapCounter = element => $(element).find('.' + Init.classes.pieChart.pieChartMapCounterClass);
let getMapCounter = element => element.querySelector(`.${Init.classes.pieChart.pieChartMapCounterClass}`);
/**
* get interval value from map timer overlay
* @param element
* @returns {*}
* if there is an "active" (connected) counter task
* -> lock overlay
* @param {HTMLElement} element
* @returns {boolean}
*/
let getMapOverlayInterval = element => getMapCounter(getMapOverlay(element, 'timer')).data('interval');
let isMapCounterOverlayActive = element => {
let mapOverlay = getMapOverlay(element, 'timer');
if(mapOverlay){
let mapCounter = getMapCounter(mapOverlay[0]);
if(mapCounter && mapCounter.getData('counterTask')){
return mapCounter.getData('counterTask').isConnected();
}
}
return false;
};
return {
config: config,
getMapOverlay: getMapOverlay,
getMapElementFromOverlay: getMapElementFromOverlay,
getMapCounter: getMapCounter,
getMapOverlayInterval: getMapOverlayInterval
isMapCounterOverlayActive: isMapCounterOverlayActive
};
});

View File

@@ -23,7 +23,6 @@ define([
systemHeadInfoLeftClass: 'pf-system-head-info-left', // class for left system info
systemHeadInfoRightClass: 'pf-system-head-info-right', // class for right system info
systemActiveClass: 'pf-system-active', // class for an active system on a map
systemTooltipInnerIdPrefix: 'pf-system-tooltip-inner-', // id prefix for system tooltip content
systemTooltipInnerClass: 'pf-system-tooltip-inner', // class for system tooltip content
@@ -231,24 +230,26 @@ define([
if(formValid === false) return false;
// calculate new system position ----------------------------------------------------------
let newPosition = {
x: 0,
y: 0
};
let newPosition;
// add new position
let sourceSystem = null;
let connectionData = null;
if(options.sourceSystem !== undefined){
// new position based on sourceSystem´s position
sourceSystem = options.sourceSystem;
connectionData = options.connectionData || null;
// get new position
newPosition = newSystemPositionBySystem(sourceSystem);
}else if(options.position){
// check mouse cursor position (add system to map)
// new position based on coordinated (e.g. mouse event)
newPosition = {
x: options.position.x,
y: options.position.y
};
}else{
// new position based on current map scroll offset
newPosition = MapUtil.newSystemPositionsByMapOffset(mapContainer)[0];
}
formData.position = newPosition;
@@ -262,7 +263,8 @@ define([
systemDialog: systemDialog,
formElement: form,
map: map,
sourceSystem: sourceSystem
sourceSystem: sourceSystem,
connectionData: connectionData
}, context => {
// always do
context.systemDialog.find('.modal-content').hideLoadingAnimation();
@@ -270,7 +272,7 @@ define([
payload => {
Util.showNotify({title: 'New system', text: payload.data.name, type: 'success'});
callback(payload.context.map, payload.data, payload.context.sourceSystem);
callback(payload.context.map, payload.data, payload.context.sourceSystem, payload.context.connectionData);
bootbox.hideAll();
},
Util.handleAjaxErrorResponse
@@ -699,10 +701,12 @@ define([
for(let system of systems){
system = $(system);
let mapId = parseInt(system.data('mapid')) || 0;
// check if system is "active"
if(system.hasClass(config.systemActiveClass)){
delete Init.currentSystemData;
if(system.hasClass(MapUtil.config.systemActiveClass)){
Util.deleteCurrentSystemData(mapId);
// get parent Tab Content and fire clear modules event
let tabContentElement = MapUtil.getTabContentElementByMapElement(system);
$(tabContentElement).trigger('pf:removeSystemModules');
@@ -712,7 +716,7 @@ define([
map.deleteConnectionsForElement(system, {fireEvent: false});
// unregister from "magnetizer"
Magnetizer.removeElement(system.data('mapid'), system[0]);
Magnetizer.removeElement(mapId, system[0]);
// destroy tooltip/popover
system.toggleSystemTooltip('destroy', {});

View File

@@ -186,25 +186,50 @@ define([
* @param mapData
* @param value
* @param key
* @returns {any}
* @returns {{}|boolean}
*/
let getSystemDataFromMapData = (mapData, value, key = 'id') => {
return mapData ? mapData.data.systems.find(system => system[key] === value) || false : false;
return (Util.getObjVal(mapData, `data.systems`) || [])
.find(systemData => systemData[key] === value) || false;
};
/**
* get system data by mapId system data selector
* get system data by mapId + system data selector
* -> e.g. value = 2 and key = 'id'
* -> e.g. value = 30002187 and key = 'systemId' => looks for 'Amarr' CCP systemId
* @param mapId
* @param value
* @param key
* @returns {any}
* @returns {{}|boolean}
*/
let getSystemData = (mapId, value, key = 'id') => {
return getSystemDataFromMapData(Util.getCurrentMapData(mapId), value, key);
};
/**
* get connection data from mapData
* @see getConnectionData
* @param mapData
* @param value
* @param key
* @returns {{}|boolean}
*/
let getConnectionDataFromMapData = (mapData, value, key = 'id') => {
return (Util.getObjVal(mapData, `data.connections`) || [])
.find(connectionData => connectionData[key] === value) || false;
};
/**
* get connection data by mapId + connection data selector
* @param mapId
* @param value
* @param key
* @returns {{}|boolean}
*/
let getConnectionData = (mapId, value, key = 'id') => {
return getConnectionDataFromMapData(Util.getCurrentMapData(mapId), value, key);
};
/**
* get system type information
* @param {number} systemTypeId
@@ -212,14 +237,7 @@ define([
* @returns {string}
*/
let getSystemTypeInfo = (systemTypeId, option) => {
let systemTypeInfo = '';
$.each(Init.systemType, function(prop, data){
if(systemTypeId === data.id){
systemTypeInfo = data[option];
return;
}
});
return systemTypeInfo;
return (Object.values(Init.systemType).find(data => data.id === systemTypeId) || {})[option] || '';
};
/**
@@ -229,11 +247,7 @@ define([
* @returns {string}
*/
let getEffectInfoForSystem = (effect, option) => {
let effectInfo = '';
if( Init.classes.systemEffects.hasOwnProperty(effect) ){
effectInfo = Init.classes.systemEffects[effect][option];
}
return effectInfo;
return Util.getObjVal(Init.classes.systemEffects, `${effect}.${option}`) || '';
};
/**
@@ -478,13 +492,16 @@ define([
let getConnectionDataFromSignatures = (connection, connectionData) => {
let signatureTypeData = {
source: {
ids: [],
names: [],
labels: []
},
target: {
ids: [],
names: [],
labels: []
}
},
hash: false // unique hash key build from all relevant signature for connection
};
if(
@@ -501,41 +518,50 @@ define([
let sourceId = sourceSystem.data('id');
let targetId = targetSystem.data('id');
// in case connection is currently "dragged" between systems, sourceId and/or targetId is undefined
if(!sourceId || !targetId){
return signatureTypeData;
}
let hash = [];
// ... collect overlay/label data from signatures
for(let signatureData of connectionData.signatures){
// ... typeId is required to get a valid name
if(signatureData.typeId > 0){
hash.push(Util.getObjVal(signatureData, 'updated.updated'));
// whether "source" or "target" system is relevant for current connection and current signature...
let tmpSystem = null;
let tmpSystemType = null;
// whether "source" or "target" system is relevant for current connection and current signature...
let tmpSystem = null;
let tmpSystemType = null;
if(signatureData.system.id === sourceId){
// relates to "source" endpoint
tmpSystemType = 'source';
tmpSystem = sourceSystem;
}else if(signatureData.system.id === targetId){
// relates to "target" endpoint
tmpSystemType = 'target';
tmpSystem = targetSystem;
}
if(signatureData.system.id === sourceId){
// relates to "source" endpoint
tmpSystemType = 'source';
tmpSystem = sourceSystem;
}else if(signatureData.system.id === targetId){
// relates to "target" endpoint
tmpSystemType = 'target';
tmpSystem = targetSystem;
}
// ... get endpoint label for source || target system
if(tmpSystem && tmpSystem){
// ... get all available signature type (wormholes) names
let availableSigTypeNames = SystemSignatureModule.getSignatureTypeOptionsBySystem(tmpSystem, 5);
let flattenSigTypeNames = Util.flattenXEditableSelectArray(availableSigTypeNames);
signatureTypeData[tmpSystemType].ids.push(signatureData.id);
signatureTypeData[tmpSystemType].names.push(signatureData.name.toUpperCase());
if(flattenSigTypeNames.hasOwnProperty(signatureData.typeId)){
let label = flattenSigTypeNames[signatureData.typeId];
// shorten label, just take the ingame name
label = label.substr(0, label.indexOf(' '));
signatureTypeData[tmpSystemType].names.push(signatureData.name);
signatureTypeData[tmpSystemType].labels.push(label);
}
// ... typeId is required to get a valid labels
// ... get endpoint label for source || target system
if(signatureData.typeId > 0 && tmpSystem && tmpSystem){
// ... get all available signature type (wormholes) names
let availableSigTypeNames = SystemSignatureModule.getSignatureTypeOptionsBySystem(tmpSystem, 5);
let flattenSigTypeNames = Util.flattenXEditableSelectArray(availableSigTypeNames);
if(flattenSigTypeNames.hasOwnProperty(signatureData.typeId)){
let label = flattenSigTypeNames[signatureData.typeId];
// shorten label, just take the ingame name
label = label.substr(0, label.indexOf(' '));
signatureTypeData[tmpSystemType].labels.push(label);
}
}
}
// ... build unique hash
signatureTypeData.hash = hash.join().hashCode();
}
return signatureTypeData;
@@ -621,64 +647,66 @@ define([
*/
let filterMapByScopes = (map, scopes) => {
if(map){
map.batch(() => {
let mapElement = $(map.getContainer());
let allSystems = mapElement.getSystems();
let allConnections = map.getAllConnections();
map.setSuspendDrawing(true);
if(scopes && scopes.length){
// filter connections -------------------------------------------------------------------------------------
let visibleSystems = [];
let visibleConnections = searchConnectionsByScopeAndType(map, scopes);
let mapElement = $(map.getContainer());
let allSystems = mapElement.getSystems();
let allConnections = map.getAllConnections();
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(scopes && scopes.length){
// filter connections -------------------------------------------------------------------------------------
let visibleSystems = [];
let visibleConnections = searchConnectionsByScopeAndType(map, scopes);
// 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
){
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);
}
for(let connection of allConnections){
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);
}
MapOverlayUtil.getMapOverlay(mapElement, 'info').updateOverlayIcon('filter', 'hide');
}
});
// 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
){
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);
}
for(let connection of allConnections){
setConnectionVisible(connection, true);
}
MapOverlayUtil.getMapOverlay(mapElement, 'info').updateOverlayIcon('filter', 'hide');
}
map.setSuspendDrawing(false, true);
}
};
@@ -792,17 +820,19 @@ define([
*/
let setSystemActive = (map, system) => {
// deselect all selected systems on map
let mapContainer = $( map.getContainer() );
mapContainer.find('.' + config.systemClass).removeClass(config.systemActiveClass);
let mapContainer = map.getContainer();
[...mapContainer.getElementsByClassName(config.systemClass)]
.forEach(systemEl =>
systemEl.classList.remove(config.systemActiveClass)
);
// set current system active
system.addClass(config.systemActiveClass);
// collect all required data from map module to update the info element
// store them global and assessable for each module
// collect all required systemData from map module -> cache
let systemData = system.getSystemData();
systemData.mapId = parseInt(system.attr('data-mapid')) || 0;
Util.setCurrentSystemData(systemData);
Util.setCurrentSystemData(systemData.mapId, systemData);
};
/**
@@ -913,20 +943,22 @@ define([
* @param connections
*/
let setConnectionsActive = (map, connections) => {
map.batch(() => {
// set all inactive
for(let connection of getConnectionsByType(map, 'state_active')){
if(!connections.includes(connection)){
removeConnectionType(connection, 'state_active');
}
}
map.setSuspendDrawing(true);
for(let connection of connections){
if(!connection.hasType('state_active')){
addConnectionType(connection, 'state_active');
}
// set all inactive
for(let connection of getConnectionsByType(map, 'state_active')){
if(!connections.includes(connection)){
removeConnectionType(connection, 'state_active');
}
});
}
for(let connection of connections){
if(!connection.hasType('state_active')){
addConnectionType(connection, 'state_active');
}
}
map.setSuspendDrawing(false, true);
};
/**
@@ -969,21 +1001,38 @@ define([
let toggleConnectionActive = (map, connections) => {
let selectedConnections = [];
let deselectedConnections = [];
map.batch(() => {
for(let connection of connections){
if(connection.hasType('state_active')){
removeConnectionType(connection, 'state_active');
deselectedConnections.push(connection);
}else{
addConnectionType(connection, 'state_active');
selectedConnections.push(connection);
}
map.setSuspendDrawing(true);
for(let connection of connections){
if(connection.hasType('state_active')){
removeConnectionType(connection, 'state_active');
deselectedConnections.push(connection);
}else{
addConnectionType(connection, 'state_active');
selectedConnections.push(connection);
}
});
}
map.setSuspendDrawing(false, true);
updateConnectionInfo(map, selectedConnections, deselectedConnections);
};
/**
* show global map info panels
* @param map
*/
let showMapInfo = map => {
// get parent Tab Content and fire update event
let mapContainer = $(map.getContainer());
getTabContentElementByMapElement(mapContainer).trigger('pf:renderGlobalModules', {
mapId: parseInt(mapContainer.data('id')),
payload: null
});
};
/**
* show system info panels
* @param map
@@ -994,10 +1043,11 @@ define([
// get parent Tab Content and fire update event
let mapContainer = $(map.getContainer());
let mapId = parseInt(mapContainer.data('id')) || 0;
getTabContentElementByMapElement(mapContainer).trigger('pf:renderSystemModules', {
mapId: parseInt(mapContainer.data('id')),
payload: Util.getCurrentSystemData()
mapId: mapId,
payload: Util.getCurrentSystemData(mapId)
});
};
@@ -1238,10 +1288,12 @@ define([
let setUniqueConnectionType = (connection, type, types) => {
type = types.includes(type) ? [type] : [];
connection._jsPlumb.instance.batch(() => {
removeConnectionTypes(connection, types.diff(type));
addConnectionTypes(connection, type);
});
connection._jsPlumb.instance.setSuspendDrawing(true);
removeConnectionTypes(connection, types.diff(type));
addConnectionTypes(connection, type);
connection._jsPlumb.instance.setSuspendDrawing(false, true);
};
/**
@@ -2074,6 +2126,40 @@ define([
return findNonOverlappingDimensions(options, maxResults, findChain);
};
/**
* calculate the x/y coordinates for a new system - relative to current map scroll offset
* @param mapContainer
* @param maxResults
* @returns {[{x: number, y: number}]}
*/
let newSystemPositionsByMapOffset = (mapContainer, maxResults = 1) => {
let scrollPosition = {
x: Math.abs(parseInt(mapContainer.attr('data-scroll-left')) || 0),
y: Math.abs(parseInt(mapContainer.attr('data-scroll-top')) || 0)
};
// space new positions from map top (e.g. used for tooltips)
scrollPosition.y = Math.max(scrollPosition.y, 30);
// default position -> current map section top/left
let positions = [scrollPosition];
// check default position for overlapping
let dimensions = newSystemPositionByCoordinates(mapContainer, {
center: [scrollPosition.x, scrollPosition.y],
minX: scrollPosition.x,
minY: scrollPosition.y
}, maxResults, true);
if(dimensions.length){
positions = dimensions.map(dim => ({
x: parseInt(dim.left) || 0,
y: parseInt(dim.top) || 0
}));
}
return positions;
};
/**
*
* @param mapContainer
@@ -2083,30 +2169,8 @@ define([
if(mapContainer){
let mapId = mapContainer.data('id');
let scrollPosition = {
x: Math.abs(parseInt(mapContainer.attr('data-scroll-left')) || 0),
y: Math.abs(parseInt(mapContainer.attr('data-scroll-top')) || 0)
};
// space new positions from map top (e.g. used for tooltips)
scrollPosition.y = Math.max(scrollPosition.y, 30);
// default position -> current map section top/left -------------------------------------------------------
positions.defaults = [scrollPosition];
// check default position for overlapping -----------------------------------------------------------------
let dimensions = newSystemPositionByCoordinates(mapContainer, {
center: [scrollPosition.x, scrollPosition.y],
minX: scrollPosition.x,
minY: scrollPosition.y
}, 2, true);
if(dimensions.length){
positions.defaults = dimensions.map(dim => ({
x: parseInt(dim.left) || 0,
y: parseInt(dim.top) || 0
}));
}
positions.defaults = newSystemPositionsByMapOffset(mapContainer, 2);
// -> calc possible coordinates for new system that should be used based on current user location ---------
let currentLocationData = Util.getCurrentLocationData();
@@ -2162,6 +2226,8 @@ define([
getInfoForSystem: getInfoForSystem,
getSystemDataFromMapData: getSystemDataFromMapData,
getSystemData: getSystemData,
getConnectionDataFromMapData: getConnectionDataFromMapData,
getConnectionData: getConnectionData,
getSystemTypeInfo: getSystemTypeInfo,
getEffectInfoForSystem: getEffectInfoForSystem,
markAsChanged: markAsChanged,
@@ -2172,6 +2238,7 @@ define([
toggleConnectionType: toggleConnectionType,
toggleConnectionActive: toggleConnectionActive,
setSystemActive: setSystemActive,
showMapInfo: showMapInfo,
showSystemInfo: showSystemInfo,
showConnectionInfo: showConnectionInfo,
showFindRouteDialog: showFindRouteDialog,
@@ -2180,6 +2247,7 @@ define([
getConnectionsByType: getConnectionsByType,
getEndpointsDataByConnection: getEndpointsDataByConnection,
getDataByConnection: getDataByConnection,
getDataByConnections: getDataByConnections,
searchConnectionsBySystems: searchConnectionsBySystems,
searchConnectionsByScopeAndType: searchConnectionsByScopeAndType,
getConnectionInfo: getConnectionInfo,
@@ -2191,7 +2259,6 @@ define([
setConnectionMassStatusType: setConnectionMassStatusType,
setConnectionJumpMassType: setConnectionJumpMassType,
getScopeInfoForConnection: getScopeInfoForConnection,
getDataByConnections: getDataByConnections,
deleteConnections: deleteConnections,
getConnectionDataFromSignatures: getConnectionDataFromSignatures,
getEndpointOverlaySignatureLocation: getEndpointOverlaySignatureLocation,
@@ -2212,6 +2279,7 @@ define([
checkRight: checkRight,
newSystemPositionBySystem: newSystemPositionBySystem,
newSystemPositionByCoordinates: newSystemPositionByCoordinates,
newSystemPositionsByMapOffset: newSystemPositionsByMapOffset,
newSystemPositionsByMap: newSystemPositionsByMap,
getMapDeeplinkUrl: getMapDeeplinkUrl
};

View File

@@ -308,15 +308,14 @@ define([
/**
* clear both main update timeouts
* clear both main update timeouts, and reset values
* -> stop program from working -> shutdown
*/
let clearUpdateTimeouts = () => {
for(let intervalKey in updateTimeouts){
if(updateTimeouts.hasOwnProperty(intervalKey)){
clearTimeout(updateTimeouts[intervalKey]);
}
}
Object.keys(updateTimeouts).forEach(intervalKey => {
clearTimeout(updateTimeouts[intervalKey]);
updateTimeouts[intervalKey] = 0;
});
};
@@ -417,20 +416,20 @@ define([
// IMPORTANT: Get user data for ONE map that is currently visible
// On later releases this can be easy changed to "full update" all maps for a user
let mapIds = [];
let mapId;
let newSystemPositions = null;
let activeMap = Util.getMapModule().getActiveMap();
if(activeMap){
mapIds = [activeMap.data('id')];
mapId = activeMap.data('id');
newSystemPositions = MapUtil.newSystemPositionsByMap(activeMap);
}
let updatedUserData = {
mapIds: mapIds,
mapIds: mapId ? [mapId] : [],
getMapUserData: Util.getSyncType() === 'webSocket' ? 0 : 1,
mapTracking: locationToggle.is(':checked') ? 1 : 0, // location tracking
systemData: Util.getCurrentSystemData()
systemData: Util.getCurrentSystemData(mapId)
};
if(newSystemPositions){

View File

@@ -13,6 +13,7 @@ define([
'module/system_route',
'module/system_intel',
'module/system_killboard',
'module/global_thera',
'module/connection_info',
'app/counter'
], (
@@ -30,6 +31,7 @@ define([
SystemRouteModule,
SystemIntelModule,
SystemKillboardModule,
TheraModule,
ConnectionInfoModule
) => {
'use strict';
@@ -56,6 +58,7 @@ define([
// editable 'settings' popover
editableSettingsClass: 'pf-editable-settings',
editableHeadlineClass: 'pf-editable-headline',
editableToggleClass: 'pf-editable-toggle',
editableToggleItemClass: 'pf-editable-toggle-item',
@@ -65,13 +68,6 @@ define([
let mapTabChangeBlocked = false; // flag for preventing map tab switch
/**
* get all maps for a maps module
* @param mapModule
* @returns {jQuery}
*/
let getMaps = mapModule => $(mapModule).find('.' + Util.config.mapClass);
/**
* get the current active mapElement
* @returns {bool|jQuery}
@@ -90,6 +86,12 @@ define([
* @param tabContentWrapperEl
*/
let setMapTabContentWrapperObserver = tabContentWrapperEl => {
$(tabContentWrapperEl).on('pf:renderGlobalModules', `.${Util.config.mapTabContentClass}`, function(e, data){
getModules()
.then(modules => filterModules(modules, 'global'))
.then(modules => renderModules(modules, e.target, data));
});
$(tabContentWrapperEl).on('pf:renderSystemModules', `.${Util.config.mapTabContentClass}`, function(e, data){
getModules()
.then(modules => filterModules(modules, 'system'))
@@ -114,6 +116,12 @@ define([
.then(modules => removeModules(modules, e.target));
});
$(tabContentWrapperEl).on('pf:updateGlobalModules', `.${Util.config.mapTabContentClass}`, (e, data) => {
getModules()
.then(modules => filterModules(modules, 'global'))
.then(modules => updateModules(modules, e.target, data));
});
$(tabContentWrapperEl).on('pf:updateSystemModules', `.${Util.config.mapTabContentClass}`, (e, data) => {
getModules()
.then(modules => filterModules(modules, true, 'fullDataUpdate'))
@@ -141,6 +149,7 @@ define([
SystemRouteModule,
SystemIntelModule,
SystemKillboardModule,
TheraModule,
ConnectionInfoModule
];
@@ -361,7 +370,7 @@ define([
});
};
removeModule(Module, gridArea, true).then(abc => render(Module, gridArea, defaultPosition, mapId, payload));
removeModule(Module, gridArea, false).then(abc => render(Module, gridArea, defaultPosition, mapId, payload));
};
return new Promise(renderModuleExecutor);
@@ -533,7 +542,7 @@ define([
let updateSystemModulesData = (mapModule, systemData) => {
if(systemData){
// check if current open system is still the requested info system
let currentSystemData = Util.getCurrentSystemData();
let currentSystemData = Util.getCurrentSystemData(systemData.mapId);
if(
currentSystemData &&
@@ -556,6 +565,7 @@ define([
let setTabContentObserver = (tabContent, mapId) => {
let defaultSortableOptions = {
invertSwap: true,
animation: Init.animationSpeed.mapModule,
handle: '.' + config.sortableHandleClass,
draggable: '.' + config.moduleClass,
@@ -926,10 +936,11 @@ define([
let mapId = parseInt(linkEl.dataset.mapId) || 0;
let defaultSystemId = parseInt(linkEl.dataset.defaultSystemId) || 0;
let tabMapData = Util.getCurrentMapData(mapId);
let tabContentEl = document.getElementById(config.mapTabIdPrefix + mapId);
if(tabMapData !== false){
// tabContentEl does not exist in case of error where all map elements got removed
if(tabMapData !== false && tabContentEl){
// load map
let tabContentEl = document.getElementById(config.mapTabIdPrefix + mapId);
let areaMap = tabContentEl.querySelector(`.${Util.getMapTabContentAreaClass('map')}`);
Map.loadMap(areaMap, tabMapData, {showAnimation: true}).then(payload => {
// "wake up" scrollbar for map and get previous state back
@@ -938,6 +949,11 @@ define([
let areaMap = mapElement.closest('.mCustomScrollbar');
$(areaMap).mCustomScrollbar('update');
// show "global" map panels of map was initial loaded
if(payload.isFirstLoad){
MapUtil.showMapInfo(mapConfig.map);
}
// if there is an already an "active" system -> setCurrentSystemData for that again
let activeSystemEl = mapElement.querySelector(`.${MapUtil.config.systemActiveClass}`);
if(activeSystemEl){
@@ -973,9 +989,6 @@ define([
// skip "add button"
if(newMapId > 0){
// delete currentSystemData -> will be set for new map (if there is is an active system)
delete Init.currentSystemData;
let currentTabContentEl = document.getElementById(config.mapTabIdPrefix + oldMapId);
// disable scrollbar for map that will be hidden. "freeze" current state
@@ -991,90 +1004,81 @@ define([
* @param options
* @returns {Promise<any>}
*/
let updateTabData = (tabLinkEl, options) => {
let updateTabData = (tabLinkEl, options) => new Promise(resolve => {
// set "main" data
tabLinkEl.dataset.mapId = options.id;
/**
* update tab promise
* @param resolve
*/
let updateTabExecutor = resolve => {
// set "main" data
tabLinkEl.dataset.mapId = options.id;
// add updated timestamp (not available for "add" tab
if(Util.getObjVal(options, 'updated.updated')){
tabLinkEl.dataset.updated = options.updated.updated;
}
// add updated timestamp (not available for "add" tab
if(Util.getObjVal(options, 'updated.updated')){
tabLinkEl.dataset.updated = options.updated.updated;
// change "tab" link
tabLinkEl.setAttribute('href', `#${config.mapTabIdPrefix}${options.id}`);
// change "map" icon
let mapIconEl = tabLinkEl.querySelector(`.${config.mapTabIconClass}`);
mapIconEl.classList.remove(...mapIconEl.classList);
mapIconEl.classList.add(config.mapTabIconClass, 'fas', 'fa-fw', options.icon);
// change "shared" icon
let mapSharedIconEl = tabLinkEl.querySelector(`.${config.mapTabSharedIconClass}`);
mapSharedIconEl.style.display = 'none';
// check if the map is a "shared" map
if(options.access){
if(
options.access.character.length > 1 ||
options.access.corporation.length > 1 ||
options.access.alliance.length > 1
){
mapSharedIconEl.style.display = 'initial';
}
}
// change "tab" link
tabLinkEl.setAttribute('href', `#${config.mapTabIdPrefix}${options.id}`);
// change map name label
let textEl = tabLinkEl.querySelector(`.${config.mapTabLinkTextClass}`);
textEl.textContent = options.name;
// change "map" icon
let mapIconEl = tabLinkEl.querySelector(`.${config.mapTabIconClass}`);
mapIconEl.classList.remove(...mapIconEl.classList);
mapIconEl.classList.add(config.mapTabIconClass, 'fas', 'fa-fw', options.icon);
// change tabClass
let listEl = tabLinkEl.parentNode;
// change "shared" icon
let mapSharedIconEl = tabLinkEl.querySelector(`.${config.mapTabSharedIconClass}`);
mapSharedIconEl.style.display = 'none';
// new tab classes
let tabClasses = [config.mapTabClass, options.type.classTab];
// check if the map is a "shared" map
if(options.access){
if(
options.access.character.length > 1 ||
options.access.corporation.length > 1 ||
options.access.alliance.length > 1
){
mapSharedIconEl.style.display = 'initial';
}
}
if(options.draggable === false){
tabClasses.push('noSort');
}
// change map name label
let textEl = tabLinkEl.querySelector(`.${config.mapTabLinkTextClass}`);
textEl.textContent = options.name;
// check if tab was "active" before
if(listEl.classList.contains('active')){
tabClasses.push('active');
}
listEl.classList.remove(...listEl.classList);
listEl.classList.add(...tabClasses);
// change tabClass
let listEl = tabLinkEl.parentNode;
// set title for tooltip
if(options.type.name !== undefined){
textEl.setAttribute('title', `${options.type.name} map`);
}
// new tab classes
let tabClasses = [config.mapTabClass, options.type.classTab];
if(options.draggable === false){
tabClasses.push('noSort');
}
// check if tab was "active" before
if(listEl.classList.contains('active')){
tabClasses.push('active');
}
listEl.classList.remove(...listEl.classList);
listEl.classList.add(...tabClasses);
// set title for tooltip
if(options.type.name !== undefined){
textEl.setAttribute('title', `${options.type.name} map`);
}
let mapTooltipOptions = {
placement: 'bottom',
container: 'body',
trigger: 'hover',
delay: 150
};
$(listEl.querySelector('[title]')).tooltip(mapTooltipOptions).tooltip('fixTitle');
resolve({
action: 'update',
data: {
mapId: options.id,
mapName: options.name
}
});
let mapTooltipOptions = {
placement: 'bottom',
container: 'body',
trigger: 'hover',
delay: 150
};
return new Promise(updateTabExecutor);
};
$(listEl.querySelector('[title]')).tooltip(mapTooltipOptions).tooltip('fixTitle');
resolve({
action: 'update',
data: {
mapId: options.id,
mapName: options.name
}
});
});
/**
* add a new tab to tab-map-module end return promise
@@ -1545,10 +1549,14 @@ define([
setMapTabLayout(tabEl, layoutCurrent);
// prepare select options for modules
let sourceOptions = modules.sort((a, b) => a.isPlugin - b.isPlugin).map(Module => ({
let modulePrioCounts = Array(BaseModule.scopeOrder.length).fill(0);
let sourceOptions = modules.sort((a, b) => a.getOrderPrio() - b.getOrderPrio()).map(Module => ({
value: Module.name,
text: `(${Module.scope.substring(0, 3)}) ${Module.label}`,
text: Module.label,
metaData: {
scope: Module.scope,
orderPrio: Module.getOrderPrio(),
prioCount: ++modulePrioCounts[Module.getOrderPrio()],
isPlugin: Module.isPlugin
}
}));
@@ -1628,6 +1636,19 @@ define([
}
}
}, {passive: false});
// add "headlines" to Modules checklist -------------------------------------------------------
anchorEl.childNodes.forEach((gridItem, i) => {
if(sourceOptions[i].metaData.prioCount === 1){
gridItem.classList.add(config.editableHeadlineClass);
gridItem.setAttribute('data-count',
modulePrioCounts[sourceOptions[i].metaData.orderPrio]
);
gridItem.setAttribute('data-headline',
BaseModule.scopeOrder[sourceOptions[i].metaData.orderPrio]
);
}
});
});
settingsLinkEl.on('save', {sourceOptions: sourceOptions}, (e, params) => {
@@ -1644,15 +1665,23 @@ define([
let showModules = filterModules(modules, params.newValue.diff(oldValue), 'name');
removeModules(hideModules, tabContentEl).then(payload => {
let showGlobalModules = showModules.filter(Module => Module.scope === 'global');
let showSystemModules = showModules.filter(Module => Module.scope === 'system');
let showConnectionModules = showModules.filter(Module => Module.scope === 'connection');
if(showGlobalModules.length){
renderModules(showGlobalModules, tabContentEl, {
mapId: activeMapId,
payload: null
});
}
if(
showSystemModules.length &&
Util.getCurrentSystemData()
Util.getCurrentSystemData(activeMapId)
){
renderModules(showSystemModules, tabContentEl, {
mapId: activeMapId,
payload: Util.getCurrentSystemData()
payload: Util.getCurrentSystemData(activeMapId)
});
}
@@ -1706,28 +1735,24 @@ define([
/**
* collect all data (systems/connections) for export/save from each active map in the map module
* if no change detected -> do not attach map data to return array
* @param mapModule
* @param {HTMLElement} mapModule
* @param filter
* @returns {Array}
* @returns {[]}
*/
let getMapModuleDataForUpdate = (mapModule, filter = ['hasId', 'hasChanged']) => {
// get all active map elements for module
let mapElements = getMaps(mapModule);
let data = [];
for(let i = 0; i < mapElements.length; i++){
[...mapModule.getElementsByClassName(Util.config.mapClass)].forEach(mapElement => {
// get all changed (system / connection) data from this map
let mapData = Map.getMapDataForSync($(mapElements[i]), filter);
if(mapData !== false){
if(
mapData.data.systems.length > 0 ||
mapData.data.connections.length > 0
){
data.push(mapData);
}
let mapData = Map.getMapDataForSync(mapElement, filter);
if(
mapData && (
(Util.getObjVal(mapData, 'data.systems') || []).length ||
(Util.getObjVal(mapData, 'data.connections') || []).length
)
){
data.push(mapData);
}
}
});
return data;
};

View File

@@ -1293,47 +1293,35 @@ define([
let mapUserUpdateKey = 'UPDATE_SERVER_USER_DATA';
// Set the name of the hidden property and the change event for visibility
let hidden, visibilityChange;
if(typeof document.hidden !== 'undefined'){ // Opera 12.10 and Firefox 18 and later support
hidden = 'hidden';
let visibilityState, visibilityChange;
if(typeof document.visibilityState !== 'undefined'){ // Opera 12.10 and Firefox 18 and later support
visibilityState = 'visibilityState';
visibilityChange = 'visibilitychange';
}else if(typeof document.mozHidden !== 'undefined'){
hidden = 'mozHidden';
visibilityChange = 'mozvisibilitychange';
}else if(typeof document.msHidden !== 'undefined'){
hidden = 'msHidden';
visibilityChange = 'msvisibilitychange';
}else if(typeof document.webkitHidden !== 'undefined'){
hidden = 'webkitHidden';
visibilityChange = 'webkitvisibilitychange';
}
// function is called if the tab becomes active/inactive
let handleVisibilityChange = () => {
if(document[hidden]){
// tab is invisible
// globally store current visibility status
window.isVisible = false;
Util.getCurrentTriggerDelay( mapUpdateKey, increaseTimer );
Util.getCurrentTriggerDelay( mapUserUpdateKey, increaseTimer );
}else{
if(document[visibilityState] === 'visible'){
// tab is visible
// globally store current visibility status
window.isVisible = true;
Util.getCurrentTriggerDelay( mapUpdateKey, -increaseTimer );
Util.getCurrentTriggerDelay( mapUserUpdateKey, -increaseTimer );
Util.getCurrentTriggerDelay(mapUpdateKey, -increaseTimer);
Util.getCurrentTriggerDelay(mapUserUpdateKey, -increaseTimer);
// stop blinking tab from previous notifications
Util.stopTabBlink();
}else{
// tab is invisible
// globally store current visibility status
window.isVisible = false;
Util.getCurrentTriggerDelay(mapUpdateKey, increaseTimer);
Util.getCurrentTriggerDelay(mapUserUpdateKey, increaseTimer);
}
};
if(
typeof document.addEventListener !== 'undefined' &&
typeof document[hidden] !== 'undefined'
){
if(visibilityState && visibilityChange){
// the current browser supports this feature
// Handle page visibility change

View File

@@ -1,7 +1,8 @@
define([
'PNotify',
'PNotifyDesktop',
'PNotifyButtons',
'PNotifyCallbacks',
'PNotifyDesktop',
'NonBlock'
], (PNotify) => {
'use strict';
@@ -30,6 +31,9 @@ define([
}
};
/**
* default PNotify config
*/
let initDefaultPNotifyConfig = () => {
PNotify.defaults.styling = 'bootstrap3';
PNotify.defaults.icons = 'fontawesome5';
@@ -54,7 +58,6 @@ define([
config.modules = {
Desktop: Object.assign({}, {desktop: true}, options.desktop)
};
startTabBlink(config.title); // make browser tab blink
}
switch(config.type){
@@ -99,60 +102,7 @@ define([
initDefaultPNotifyConfig();
// browser tab blink ==============================================================================================
// initial page title (cached)
let initialPageTitle = document.title;
// global blink timeout cache
let blinkTimer;
/**
* change document.title and make the browsers tab blink
* @param blinkTitle
*/
let startTabBlink = blinkTitle => {
let initBlink = (function(){
// count blinks if tab is currently active
let activeTabBlinkCount = 0;
let blink = (blinkTitle) => {
// number of "blinks" should be limited if tab is currently active
if(window.isVisible){
activeTabBlinkCount++;
}
// toggle page title
document.title = (document.title === blinkTitle) ? initialPageTitle : blinkTitle;
if(activeTabBlinkCount > 10){
stopTabBlink();
}
};
return () => {
if(!blinkTimer){
blinkTimer = setInterval(blink, 1000, blinkTitle);
}
};
}(blinkTitle));
initBlink();
};
/**
* stop blinking document.title
*/
let stopTabBlink = () => {
if(blinkTimer){
clearInterval(blinkTimer);
document.title = initialPageTitle;
blinkTimer = null;
}
};
return {
showNotify: showNotify,
startTabBlink: startTabBlink,
stopTabBlink: stopTabBlink
showNotify: showNotify
};
});

View File

@@ -212,7 +212,7 @@ define([
let systemsElement = $(this);
let systemTable = $('<table>', {
id: Util.getTableId(config.tableId, 'systems', mapData.config.id, ''),
id: Util.getTableId(config.tableId, 'systems', mapData.config.id),
class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ')
});
systemsElement.append(systemTable);
@@ -469,7 +469,7 @@ define([
let confirmationSettings = {
placement: 'left',
title: 'Delete system',
title: '---',
template: Util.getConfirmationTemplate(null, {
size: 'small',
noTitle: true
@@ -535,7 +535,7 @@ define([
let connectionsElement = $(this);
let connectionTable = $('<table>', {
id: Util.getTableId(config.tableId, 'connections', mapData.config.id, ''),
id: Util.getTableId(config.tableId, 'connections', mapData.config.id),
class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ')
});
connectionsElement.append(connectionTable);
@@ -678,7 +678,7 @@ define([
let confirmationSettings = {
placement: 'left',
title: 'Delete connection',
title: '---',
template: Util.getConfirmationTemplate(null, {
size: 'small',
noTitle: true
@@ -686,9 +686,7 @@ define([
onConfirm: function(e, target){
let deleteRowElement = $(target).parents('tr');
// deleteSignatures(row);
let connection = $().getConnectionById(mapData.config.id, rowData.id);
MapUtil.deleteConnections([connection], () => {
// callback function after ajax "delete" success
// remove table row

View File

@@ -90,8 +90,7 @@ define([
let name = parts[0];
let sizeLabel;
if(Util.getObjVal(customOptions, 'showWhSizeLabel')){
let wormholeSizeData = Util.getObjVal(Init, 'wormholes.' + name + '.size');
sizeLabel = Util.getObjVal(wormholeSizeData, 'label') || '';
sizeLabel = Util.getObjVal(Init, `wormholes.${name}.size.label`) || '';
}
let securityClass = Util.getSecurityClassForSystem(getSystemSecurityFromLabel(parts[1]));
@@ -102,19 +101,17 @@ define([
let classes = [securityClass, Util.config.popoverTriggerClass, Util.config.helpDefaultClass];
markup += '<span>' + name + '</span>';
markup += `<span>${name}</span>`;
if(sizeLabel !== undefined){
markup += '<span><kbd>' + sizeLabel + '</kbd></span>';
}else{
markup += '&nbsp;&nbsp;';
markup += `<span><kbd>${sizeLabel}</kbd></span>`;
}
markup += '<i class="fas fa-long-arrow-alt-right txt-color txt-color-grayLight"></i>';
markup += '<span class="' + classes.join(' ') + '" data-name="' + name + '">&nbsp;&nbsp;' + label + '</span>';
markup += `<span class="${classes.join(' ')}" data-name="${name}">&nbsp;&nbsp;${label}</span>`;
if(suffix.length){
markup += '&nbsp;<span>' + suffix + '</span>';
markup += `&nbsp;<span>${suffix}</span>`;
}
}else{
markup += '<span>' + state.text + '</span>';
markup += `<span>${state.text}</span>`;
}
return $(markup);
@@ -203,10 +200,10 @@ define([
}
let securityClass = Util.getSecurityClassForSystem(parts[1]);
markup += '<span class="' + styleClass.join(' ') + '">' + parts[0] + '</span>&nbsp;&nbsp;';
markup += '<span class="' + securityClass + '">' + parts[1] + '</span>';
markup += `<span class="${styleClass.join(' ')}">${parts[0]}</span>`;
markup += `<span class="${securityClass}">&nbsp;&nbsp;${parts[1]}</span>`;
}else{
markup += '<span>' + state.text + '</span>';
markup += `<span>${state.text}</span>`;
}
return $(markup);

View File

@@ -2,9 +2,11 @@ define([
'jquery',
'app/init',
'app/util',
'app/map/util',
'app/lib/cache',
'app/promises/promise.deferred',
'app/promises/promise.queue'
], ($, Init, Util, DeferredPromise, PromiseQueue) => {
], ($, Init, Util, MapUtil, Cache, DeferredPromise, PromiseQueue) => {
'use strict';
/**
@@ -127,6 +129,34 @@ define([
});
}
/**
* label element
* @param text
* @param cls
* @returns {HTMLSpanElement}
*/
newLabelElement(text, cls = []){
let labelEl = document.createElement('span');
labelEl.classList.add('label', 'center-block', ...cls);
labelEl.textContent = text || '';
return labelEl;
}
/**
* control button element
* @param text
* @param cls
* @param iconCls
* @returns {HTMLDivElement}
*/
newControlElement(text, cls = [], iconCls = ['fa-sync']){
let controlEl = document.createElement('div');
controlEl.classList.add(...[BaseModule.Util.config.dynamicAreaClass, this._config.controlAreaClass, ...cls]);
controlEl.insertAdjacentHTML('beforeend', `&nbsp;&nbsp;${text}`);
controlEl.prepend(this.newIconElement(iconCls));
return controlEl;
}
/**
* HTTP request handler for internal (Pathfinder) ajax calls
* @param args
@@ -245,6 +275,188 @@ define([
}
}
/**
* get a unique cache key name for "source"/"target"-name
* @param sourceName
* @param targetName
* @returns {string|boolean}
*/
static getConnectionDataCacheKey(sourceName, targetName){
let key = false;
if(sourceName && targetName){
// names can be "undefined" in case system is currently in drag/drop state
// sort() is important -> ignore direction
key = `con_` + `${ [String(sourceName).toLowerCase(), String(targetName).toLowerCase()].sort() }`.hashCode();
}
return key;
}
/**
* get a connectionsData object that holds all connections for given mapIds (used as cache for route search)
* @param mapIds
* @returns {{}}
*/
static getConnectionsDataFromMaps(mapIds){
let data = {};
for(let mapId of mapIds){
let map = MapUtil.getMapInstance(mapId);
if(map){
let cacheKey = `map_${mapId}`;
let cache = BaseModule.getCache('mapConnections');
let mapConnectionsData = cache.get(cacheKey);
if(!mapConnectionsData){
mapConnectionsData = this.getConnectionsDataFromConnections(mapId, map.getAllConnections());
// update cache
cache.set(cacheKey, mapConnectionsData);
}
Object.assign(data, mapConnectionsData);
}
}
return data;
}
/**
* get a connectionsData object for all connections
* @param mapId
* @param connections
* @returns {{}}
*/
static getConnectionsDataFromConnections(mapId = 0, connections = []){
let data = {};
if(connections.length){
let connectionsData = MapUtil.getDataByConnections(connections);
for(let connectionData of connectionsData){
let connectionDataCacheKey = BaseModule.getConnectionDataCacheKey(connectionData.sourceName, connectionData.targetName);
// skip double connections between same systems
if(connectionDataCacheKey && !Object.keys(data).includes(connectionDataCacheKey)){
data[connectionDataCacheKey] = {
map: {
id: mapId
},
connection: {
id: connectionData.id,
type: connectionData.type,
scope: connectionData.scope,
updated: connectionData.updated
},
source: {
id: connectionData.source,
name: connectionData.sourceName,
alias: connectionData.sourceAlias
},
target: {
id: connectionData.target,
name: connectionData.targetName,
alias: connectionData.targetAlias
}
};
}
}
}
return data;
}
/**
* search for a specific connection by "source"/"target"-name inside connectionsData cache
* @param connectionsData
* @param sourceName
* @param targetName
* @returns {*}
*/
static findConnectionsData(connectionsData, sourceName, targetName){
return this.Util.getObjVal(connectionsData, this.getConnectionDataCacheKey(sourceName, targetName));
}
/**
* get fake connection data (default connection type in case connection was not found on a map)
* @param sourceSystemData
* @param targetSystemData
* @param scope
* @param types
* @returns {{connection: {scope: string, id: number, type: [*]}, source: {name: number, alias: number, id: number}, target: {name: number, alias: number, id: number}}}
*/
static getFakeConnectionData(sourceSystemData, targetSystemData, scope = 'stargate', types = []){
return {
connection: {
id: 0,
scope: scope,
type: types.length ? types : [MapUtil.getDefaultConnectionTypeByScope(scope)],
updated: 0
},
source: {
id: 0,
name: sourceSystemData.system,
alias: sourceSystemData.system
},
target: {
id: 0,
name: targetSystemData.system,
alias: targetSystemData.system
}
};
}
/**
* get fake connection Element
* @param connectionData
* @returns {string}
*/
static getFakeConnectionElement(connectionData){
let mapId = this.Util.getObjVal(connectionData, 'map.id') || 0;
let connectionId = this.Util.getObjVal(connectionData, 'connection.id') || 0;
let scope = this.Util.getObjVal(connectionData, 'connection.scope') || '';
let classes = MapUtil.getConnectionFakeClassesByTypes(this.Util.getObjVal(connectionData, 'connection.type') || []);
let disabled = !mapId || !connectionId;
let connectionElement = '<div data-mapId="' + mapId + '" data-connectionId="' + connectionId + '" ';
connectionElement += (disabled ? 'data-disabled' : '');
connectionElement += ' class="' + classes.join(' ') + '" ';
connectionElement += ' title="' + scope + '" data-placement="bottom"></div>';
return connectionElement;
}
/**
* get static instance of in-memory Cache() store by 'name'
* -> not persistent across page reloads
* -> persistent across module instances (different and same maps)
* @param name
* @returns {Cache}
*/
static getCache(name){
let key = `CACHE-${name}`;
if(!this.Util.getObjVal(this, key)){
let configKey = `cacheConfig.${name}`;
let cacheConfig = this.Util.getObjVal(this, configKey);
if(!cacheConfig){
console.warn('Missing Cache config for %o. Expected at %o. Default config loaded…',
name, `${this.name}.${configKey}`
);
cacheConfig = {};
}else{
// set cache name
cacheConfig.name = name;
}
this[key] = new Cache(cacheConfig);
}
return this[key];
}
static now(){
return new Date().getTime() / 1000;
}
static getOrderPrio(){
return this.isPlugin ?
this.scopeOrder.indexOf('plugin') :
(this.scopeOrder.indexOf(this.scope) !== -1 ?
this.scopeOrder.indexOf(this.scope) :
this.scopeOrder.length - 1
);
}
static newDeferredPromise(){
return new DeferredPromise();
}
@@ -259,6 +471,14 @@ define([
BaseModule.fullDataUpdate = false; // static module requires additional data (e.g. system description,...)
BaseModule.Util = Util; // static access to Pathfinders Util object
BaseModule.scopeOrder = [
'system',
'connection',
'global',
'plugin',
'undefined'
];
BaseModule.handler = [
'render',
'init',
@@ -268,6 +488,14 @@ define([
'onSortableEvent'
];
BaseModule.cacheConfig = {
mapConnections: {
ttl: 5,
maxSize: 600,
debug: false
}
};
BaseModule.defaultConfig = {
position: 1,
className: 'pf-base-module', // class for module
@@ -276,6 +504,7 @@ define([
sortTargetAreas: ['a', 'b', 'c'], // sortable areas where module can be dragged into
headline: 'Base headline', // module headline
bodyClassName: 'pf-module-body', // class for module body [optional: can be used]
controlAreaClass: 'pf-module-control-area', // class for "control" areas
moduleHeadlineIconClass: 'pf-module-icon-button' // class for toolbar icons in the head
};

View File

@@ -78,11 +78,9 @@ define([
*/
newInfoPanelControlEl(mapId){
let connectionEl = this.newConnectionElement(mapId);
let controlEl = document.createElement('div');
controlEl.classList.add(Util.config.dynamicAreaClass, this._config.controlAreaClass);
controlEl.insertAdjacentHTML('beforeend', '<i class="fas fa-fw fa-plus"></i>&nbsp;add connection&nbsp;&nbsp;<kbd>ctrl</kbd>&nbsp;+&nbsp;<kbd>click</kbd>');
connectionEl.append(controlEl);
connectionEl.append(
this.newControlElement('add connection&nbsp;&nbsp;<kbd>ctrl</kbd>&nbsp;+&nbsp;<kbd>click</kbd>', [], ['fa-plus'])
);
return connectionEl;
}
@@ -267,9 +265,12 @@ define([
}, context => {
// hide loading animation
for(let contextData of context.connectionsData){
let tableEls = this.moduleElement.querySelector('#' + this.getConnectionElementId(contextData.id))
.getElementsByTagName('table');
$(tableEls).hideLoadingAnimation();
let connectionEl = this.moduleElement.querySelector('#' + this.getConnectionElementId(contextData.id));
// connectionEl might be removed in meantime ( e.g. module removed)
if(connectionEl){
let tableEls = connectionEl.getElementsByTagName('table');
$(tableEls).hideLoadingAnimation();
}
}
});
}
@@ -351,7 +352,7 @@ define([
let scopeLabel = MapUtil.getScopeInfoForConnection(connectionData.scope, 'label');
let element = document.createElement('div');
element.classList.add(Util.config.dynamicAreaClass, this._config.controlAreaClass);
element.classList.add(BaseModule.Util.config.dynamicAreaClass, this._config.controlAreaClass);
$(element).append(
$('<table>', {
@@ -990,7 +991,7 @@ define([
if(rowData.active){
let confirmationSettings = {
title: 'delete jump log',
title: '---',
template: Util.getConfirmationTemplate(null, {
size: 'small',
noTitle: true
@@ -1251,7 +1252,6 @@ define([
// body
connectionInfoPanelClass: 'pf-connection-info-panel', // class for connection info panels
controlAreaClass: 'pf-module-control-area', // class for "control" areas
// info table
moduleTableClass: 'pf-module-table', // class for module tables

View File

@@ -249,7 +249,7 @@ define([ // dependencies for this module
}
};
DemoModule.isPlugin = true; // module is defined as 'plugin'
DemoModule.isPlugin = true; // module is defined as 'plugin'
DemoModule.scope = 'system'; // module scope controls how module gets updated and what type of data is injected
DemoModule.sortArea = 'a'; // default sortable area
DemoModule.position = 10; // default sort/order position within sortable area

File diff suppressed because it is too large Load Diff

View File

@@ -42,18 +42,16 @@ define([
* @returns {*}
*/
getDataTableInstance(mapId, systemId, tableType){
return Util.getDataTableInstance(this._config.intelTableId, mapId, systemId, tableType);
return BaseModule.Util.getDataTableInstance(this._config.intelTableId, mapId, systemId, tableType);
}
/**
* get dataTable id
* @param mapId
* @param systemId
* @param tableType
* @param {...string} parts e.g. 'tableType', 'mapId', 'systemId'
* @returns {string}
*/
getTableId(tableType, mapId, systemId){
return Util.getTableId(this._config.intelTableId, tableType, mapId, systemId);
getTableId(...parts){
return BaseModule.Util.getTableId(this._config.intelTableId, ...parts);
}
/**
@@ -63,7 +61,7 @@ define([
* @returns {string}
*/
getRowId(tableType, id){
return Util.getTableRowId(this._config.intelTableRowIdPrefix, tableType, id);
return BaseModule.Util.getTableRowId(this._config.intelTableRowIdPrefix, tableType, id);
}
/**
@@ -73,7 +71,7 @@ define([
* @returns {*}
*/
getRowById(tableApi, id){
return tableApi.rows().ids().toArray().find(rowId => rowId === this.getRowId(Util.getObjVal(this.getTableMetaData(tableApi), 'type'), id));
return tableApi.rows().ids().toArray().find(rowId => rowId === this.getRowId(BaseModule.Util.getObjVal(this.getTableMetaData(tableApi), 'type'), id));
}
/**
@@ -85,7 +83,6 @@ define([
return tableApi ? tableApi.init().pfMeta : null;
}
/**
* vormat roman numeric string to int
* -> e.g. 'VII' => 7
@@ -331,7 +328,7 @@ define([
$(cell).find('i').tooltip();
}else{
let confirmationSettings = {
title: 'delete structure',
title: '---',
template: Util.getConfirmationTemplate(null, {
size: 'small',
noTitle: true
@@ -397,7 +394,6 @@ define([
// "Select" Datatables Plugin
tableApiStructure.select();
// "Buttons" Datatables Plugin
tableApiStructure.on('user-select', function(e, tableApi, type, cell, originalEvent){
let rowData = tableApi.row(cell.index().row).data();
if(Util.getObjVal(rowData, 'rowGroupData.id') !== corporationId){
@@ -405,6 +401,7 @@ define([
}
});
// "Buttons" Datatables Plugin
let buttons = new $.fn.dataTable.Buttons(tableApiStructure, {
dom: {
container: {
@@ -421,6 +418,18 @@ define([
},
name: 'tableTools',
buttons: [
{
name: 'add',
className: 'fa-plus',
titleAttr: 'add',
attr: {
'data-toggle': 'tooltip',
'data-html': true
},
action: function(e, tableApi, node, config){
module.showStructureDialog(tableApi);
}
},
{
name: 'selectToggle',
className: ['fa-check-double'].join(' '),
@@ -445,18 +454,6 @@ define([
}
}
},
{
name: 'add',
className: 'fa-plus',
titleAttr: 'add',
attr: {
'data-toggle': 'tooltip',
'data-html': true
},
action: function(e, tableApi, node, config){
module.showStructureDialog(tableApi);
}
},
{
name: 'dScan',
className: 'fa-paste',
@@ -1066,22 +1063,17 @@ define([
api.remove();
}
if(
notificationCounter.added > 0 ||
notificationCounter.changed > 0 ||
notificationCounter.deleted > 0
){
if(Math.max(...Object.values(notificationCounter))){
context.tableApi.draw();
}
// show notification ------------------------------------------------------------------------------------------
let notification = '';
notification += notificationCounter.added > 0 ? notificationCounter.added + ' added<br>' : '';
notification += notificationCounter.changed > 0 ? notificationCounter.changed + ' changed<br>' : '';
notification += notificationCounter.deleted > 0 ? notificationCounter.deleted + ' deleted<br>' : '';
// show notification --------------------------------------------------------------------------------------
let notification = Object.keys(notificationCounter).reduce((acc, key) => {
return `${acc}${notificationCounter[key] ? `${notificationCounter[key]} ${key}<br>` : ''}`;
}, '');
if(hadData && notification.length){
this.showNotify({title: 'Structures updated', text: notification, type: 'success'});
this.showNotify({title: 'Structures updated', text: notification, type: 'success', textTrusted: true});
}
}
@@ -1291,7 +1283,7 @@ define([
*/
update(systemData){
return super.update(systemData).then(systemData => new Promise(resolve => {
// update structure table data --------------------------------------------------------------------------------
// update structure table data ------------------------------------------------------------------------
let structureContext = {
tableApi: $(this.moduleElement.querySelector('.' + this._config.systemStructuresTableClass)).DataTable(),
removeMissing: true
@@ -1299,7 +1291,7 @@ define([
this.callbackUpdateTableRows(structureContext, Util.getObjVal(systemData, 'structures'));
// update station table data ----------------------------------------------------------------------------------
// update station table data --------------------------------------------------------------------------
let stationContext = {
tableApi: $(this.moduleElement.querySelector('.' + this._config.systemStationsTableClass)).DataTable(),
removeMissing: false

View File

@@ -7,9 +7,8 @@ define([
'app/init',
'app/util',
'module/base',
'app/lib/cache',
'app/map/util'
], ($, Init, Util, BaseModule, Cache, MapUtil) => {
], ($, Init, Util, BaseModule, MapUtil) => {
'use strict';
let SystemKillboardModule = class SystemKillboardModule extends BaseModule {
@@ -99,7 +98,7 @@ define([
getSystemKillsData(){
// check for cached responses "short term cache"
let cacheKey = SystemKillboardModule.getCacheKey('systemId', this._systemData.systemId);
let result = SystemKillboardModule.zkbCache.get(cacheKey);
let result = SystemKillboardModule.getCache('zkb').get(cacheKey);
if(result){
// could also be an empty array!
return Promise.resolve(result);
@@ -125,7 +124,7 @@ define([
return Promise.reject(result);
}else{
// zkb result needs to be cached and becomes reduced on "load more"
SystemKillboardModule.zkbCache.set(cacheKey, result);
SystemKillboardModule.getCache('zkb').set(cacheKey, result);
return result;
}
}).then(result => resolve(result)).catch(e => {
@@ -186,15 +185,10 @@ define([
this._killboardEl = document.createElement('ul');
this._killboardEl.classList.add(this._config.systemKillboardListClass);
let controlEl = document.createElement('div');
controlEl.classList.add(Util.config.dynamicAreaClass, this._config.controlAreaClass, this._config.moduleHeadlineIconClass);
controlEl.insertAdjacentHTML('beforeend', '&nbsp;&nbsp;load more');
controlEl.prepend(this.newIconElement(['fa-sync']));
this._bodyEl.append(
this._killboardLabelEl,
this._killboardEl,
controlEl
this.newControlElement('load more', [this._config.moduleHeadlineIconClass])
);
// set a "local" copy of all indexes from cached response
@@ -228,7 +222,7 @@ define([
showKills(chunkSize){
if(chunkSize){
let cacheKey = SystemKillboardModule.getCacheKey('systemId', this._systemData.systemId);
let result = SystemKillboardModule.zkbCache.get(cacheKey);
let result = SystemKillboardModule.getCache('zkb').get(cacheKey);
if(
this._killboardEl.children.length < this._config.maxCountKillHistoric &&
@@ -263,7 +257,7 @@ define([
*/
loadKillmailData(requestData, context, callback){
let cacheKey = SystemKillboardModule.getCacheKey('killmail', requestData.killId);
let cacheItem = SystemKillboardModule.killmailCache.get(cacheKey);
let cacheItem = SystemKillboardModule.getCache('killmail').get(cacheKey);
if(cacheItem){
// ... already cached -> show cache killmail
this[callback](cacheItem.zkb, cacheItem.killmailData, cacheItem.systemData, context.chunkSize)
@@ -275,7 +269,7 @@ define([
this.request(url).then(killmailData => {
let systemData = SystemKillboardModule.getSystemDataForCache(this._systemData);
SystemKillboardModule.killmailCache.set(cacheKey, {zkb: context.zkb, killmailData: killmailData, systemData: systemData});
SystemKillboardModule.getCache('killmail').set(cacheKey, {zkb: context.zkb, killmailData: killmailData, systemData: systemData});
this[callback](context.zkb, killmailData, systemData, context.chunkSize)
.then(payload => this.showKills(payload.data.chunkSize))
@@ -445,18 +439,6 @@ define([
return url;
}
/**
* @param text
* @param cls
* @returns {HTMLSpanElement}
*/
newLabelElement(text, cls = []){
let labelEl = document.createElement('span');
labelEl.classList.add('label', 'center-block', ...cls);
labelEl.textContent = text || '';
return labelEl;
}
/**
* Li headline (devider)
* @param text
@@ -668,7 +650,7 @@ define([
*/
static getWsRelevantSystemsFromMaps(){
let cacheKey = SystemKillboardModule.getCacheKey('tempSystemsData', 1);
let systemsData = SystemKillboardModule.zkbCache.get(cacheKey);
let systemsData = SystemKillboardModule.getCache('zkb').get(cacheKey);
if(!systemsData){
// KB cache ist for all maps (not just the current one)
@@ -683,7 +665,7 @@ define([
), {})
), {});
SystemKillboardModule.zkbCache.set(cacheKey, systemsData);
SystemKillboardModule.getCache('zkb').set(cacheKey, systemsData);
}
return systemsData;
}
@@ -705,7 +687,7 @@ define([
// system is on map! -> cache
systemData = BaseModule.Util.getObjVal(systemsData, String(killmailData.solar_system_id));
let cacheKey = SystemKillboardModule.getCacheKey('killmail', killmailData.killmail_id);
SystemKillboardModule.killmailCache.set(cacheKey, {
SystemKillboardModule.getCache('killmail').set(cacheKey, {
zkb: zkbData,
killmailData: killmailData,
systemData: systemData
@@ -800,19 +782,17 @@ define([
SystemKillboardModule.label = 'Killboard'; // static module label (e.g. description)
SystemKillboardModule.wsStatus = undefined;
SystemKillboardModule.serverTime = BaseModule.Util.getServerTime(); // static Date() with current EVE server time
SystemKillboardModule.zkbCache = new Cache({ // cache for "zKillboard" responses -> short term cache
name: 'zkb',
ttl: 60 * 3,
maxSize: 50,
debug: false
});
SystemKillboardModule.killmailCache = new Cache({ // cache for "Killmail" data -> long term cache
name: 'ccpKillmails',
ttl: 60 * 30,
maxSize: 500,
debug: false
});
SystemKillboardModule.wsSubscribtions = []; // static container for all KB module instances (from multiple maps) for WS responses
SystemKillboardModule.cacheConfig = {
zkb: { // cache for "zKillboard" responses -> short term cache
ttl: 60 * 3,
maxSize: 50
},
killmail: { // cache for "Killmail" data -> long term cache
ttl: 60 * 30,
maxSize: 500
}
};
SystemKillboardModule.defaultConfig = {
className: 'pf-system-killboard-module', // class for module
@@ -829,7 +809,6 @@ define([
wsStatusWrapperClass: 'pf-system-killboard-wsStatusWrapper', // class for WebSocket "status" wrapper
wsStatusClass: 'pf-system-killboard-wsStatus', // class for WebSocket "status" headline
labelRecentKillsClass: 'pf-system-killboard-label-recent', // class for "recent kills" label
controlAreaClass: 'pf-module-control-area', // class for "control" areas
minCountKills: 5,
chunkCountKills: 5,

View File

@@ -12,12 +12,6 @@ define([
], ($, Init, Util, bootbox, MapUtil, BaseModule) => {
'use strict';
// cache for system routes
let cache = {
systemRoutes: {}, // jump information between solar systems
mapConnections: {} // connection data read from UI
};
let SystemRouteModule = class SystemRouteModule extends BaseModule {
constructor(config = {}) {
super(Object.assign({}, new.target.defaultConfig, config));
@@ -275,7 +269,7 @@ define([
let tempTableElement = this;
let confirmationSettings = {
title: 'delete route',
title: '---',
template: Util.getConfirmationTemplate(null, {
size: 'small',
noTitle: true
@@ -434,7 +428,7 @@ define([
if(systemFromData.name !== systemToData.name){
// check for cached rowData
let cacheKey = this.getRouteDataCacheKey([mapId], systemFromData.name, systemToData.name);
let rowData = this.getCacheData('systemRoutes', cacheKey);
let rowData = SystemRouteModule.getCache('routes').get(cacheKey);
if(rowData){
// route data is cached (client side)
rowElements.push(this.addRow(rowData));
@@ -479,29 +473,7 @@ define([
* @returns {string}
*/
getRouteDataCacheKey(mapIds, sourceName, targetName){
return [mapIds.join('_'), sourceName.toLowerCase(), targetName.toLowerCase()].join('###');
}
/**
* get cache data
* @param cacheType
* @param cacheKey
* @returns {null}
*/
getCacheData(cacheType, cacheKey){
let cachedData = null;
let currentTimestamp = Util.getServerTime().getTime();
if(
cache[cacheType].hasOwnProperty(cacheKey) &&
Math.round(
( currentTimestamp - (new Date( cache[cacheType][cacheKey].updated * 1000).getTime())) / 1000
) <= this._config.routeCacheTTL
){
cachedData = cache[cacheType][cacheKey].data;
}
return cachedData;
return `route_` + `${mapIds.join('_')}${sourceName}${targetName}`.hashCode();
}
/**
@@ -578,7 +550,7 @@ define([
if(rowData.route){
// update route cache
let cacheKey = this.getRouteDataCacheKey(rowData.mapIds, routeData.systemFromData.name, routeData.systemToData.name);
this.setCacheData('systemRoutes', cacheKey, rowData);
SystemRouteModule.getCache('routes').set(cacheKey, rowData);
this.addRow(rowData);
}
@@ -731,7 +703,7 @@ define([
let routeJumpElements = [];
let avgSecTemp = 0;
let connectionsData = this.getConnectionsDataFromMaps(routeData.mapIds);
let connectionsData = BaseModule.getConnectionsDataFromMaps(routeData.mapIds);
let prevRouteNodeData = null;
// loop all systems on this route
for(let i = 0; i < routeData.route.length; i++){
@@ -740,11 +712,11 @@ define([
// fake connection elements between systems -----------------------------------------------------------
if(prevRouteNodeData){
let connectionData = this.findConnectionsData(connectionsData, prevRouteNodeData.system, systemName);
if(!connectionData.hasOwnProperty('connection')){
connectionData = this.getFakeConnectionData(prevRouteNodeData, routeNodeData, isWormholeSystemName(systemName) ? 'wh' : 'stargate');
let connectionData = BaseModule.findConnectionsData(connectionsData, prevRouteNodeData.system, systemName);
if(!connectionData){
connectionData = BaseModule.getFakeConnectionData(prevRouteNodeData, routeNodeData, isWormholeSystemName(systemName) ? 'wh' : 'stargate');
}
let connectionElement = this.getFakeConnectionElement(connectionData);
let connectionElement = BaseModule.getFakeConnectionElement(connectionData);
routeJumpElements.push(connectionElement);
}
@@ -809,153 +781,6 @@ define([
return tableRowData;
}
/**
* get a connectionsData object that holds all connections for given mapIds (used as cache for route search)
* @param mapIds
* @returns {{}}
*/
getConnectionsDataFromMaps(mapIds){
let connectionsData = {};
for(let mapId of mapIds){
let map = MapUtil.getMapInstance(mapId);
if(map){
let cacheKey = 'map_' + mapId;
let mapConnectionsData = this.getCacheData('mapConnections', cacheKey);
if(!mapConnectionsData){
mapConnectionsData = {};
let connections = map.getAllConnections();
if(connections.length){
let connectionsData = MapUtil.getDataByConnections(connections);
for(let connectionData of connectionsData){
let connectionDataCacheKey = this.getConnectionDataCacheKey(connectionData.sourceName, connectionData.targetName);
// skip double connections between same systems
if(!mapConnectionsData.hasOwnProperty(connectionDataCacheKey)){
mapConnectionsData[connectionDataCacheKey] = {
map: {
id: mapId
},
connection: {
id: connectionData.id,
type: connectionData.type,
scope: connectionData.scope
},
source: {
id: connectionData.source,
name: connectionData.sourceName,
alias: connectionData.sourceAlias
},
target: {
id: connectionData.target,
name: connectionData.targetName,
alias: connectionData.targetAlias
}
};
}
}
}
// update cache
this.setCacheData('mapConnections', cacheKey, mapConnectionsData);
}
if(connectionsData !== null){
connectionsData = Object.assign({}, mapConnectionsData, connectionsData);
}
}
}
return connectionsData;
}
/**
* get fake connection data (default connection type in case connection was not found on a map)
* @param sourceRouteNodeData
* @param targetRouteNodeData
* @param scope
* @returns {{connection: {id: number, type: string[], scope: string}, source: {id: number, name, alias}, target: {id: number, name, alias}}}
*/
getFakeConnectionData(sourceRouteNodeData, targetRouteNodeData, scope = 'stargate'){
return {
connection: {
id: 0,
type: [MapUtil.getDefaultConnectionTypeByScope(scope)],
scope: scope
},
source: {
id: 0,
name: sourceRouteNodeData.system,
alias: sourceRouteNodeData.system
},
target: {
id: 0,
name: targetRouteNodeData.system,
alias: targetRouteNodeData.system
}
};
}
/**
* get fake connection Element
* @param connectionData
* @returns {string}
*/
getFakeConnectionElement(connectionData){
let mapId = Util.getObjVal(connectionData, 'map.id') | 0;
let connectionId = Util.getObjVal(connectionData, 'connection.id') | 0;
let scope = Util.getObjVal(connectionData, 'connection.scope');
let classes = MapUtil.getConnectionFakeClassesByTypes(connectionData.connection.type);
let disabled = !mapId || !connectionId;
let connectionElement = '<div data-mapId="' + mapId + '" data-connectionId="' + connectionId + '" ';
connectionElement += (disabled ? 'data-disabled' : '');
connectionElement += ' class="' + classes.join(' ') + '" ';
connectionElement += ' title="' + scope + '" data-placement="bottom"></div>';
return connectionElement;
}
/**
* search for a specific connection by "source"/"target"-name inside connectionsData cache
* @param connectionsData
* @param sourceName
* @param targetName
* @returns {{}}
*/
findConnectionsData(connectionsData, sourceName, targetName){
let connectionDataCacheKey = this.getConnectionDataCacheKey(sourceName, targetName);
return connectionsData.hasOwnProperty(connectionDataCacheKey) ?
connectionsData[connectionDataCacheKey] : {};
}
/**
* get a unique cache key name for "source"/"target"-name
* @param sourceName
* @param targetName
* @returns {*}
*/
getConnectionDataCacheKey(sourceName, targetName){
let key = false;
if(sourceName && targetName){
// names can be "undefined" in case system is currently on drag/drop
key = [sourceName.toLowerCase(), targetName.toLowerCase()].sort().join('###');
}
return key;
}
/**
* set cache data
* @param cacheType
* @param cacheKey
* @param data
*/
setCacheData(cacheType, cacheKey, data){
cache[cacheType][cacheKey] = {
data: data,
updated: Util.getServerTime().getTime() / 1000
};
}
/**
*
* @param mapId
@@ -1351,6 +1176,12 @@ define([
SystemRouteModule.sortArea = 'b'; // default sortable area
SystemRouteModule.position = 1; // default sort/order position within sortable area
SystemRouteModule.label = 'Routes'; // static module label (e.g. description)
SystemRouteModule.cacheConfig = {
routes: {
ttl: 5,
maxSize: 100
}
};
SystemRouteModule.defaultConfig = {
className: 'pf-system-route-module', // class for module
@@ -1373,8 +1204,6 @@ define([
systemSecurityClassPrefix: 'pf-system-security-', // prefix class for system security level (color)
rallyClass: 'pf-rally', // class for "rally point" style
routeCacheTTL: 5 // route cache timer (client) in seconds
};
return SystemRouteModule;

View File

@@ -11,9 +11,8 @@ define([
'app/counter',
'app/map/map',
'app/map/util',
'app/lib/cache',
'app/ui/form_element'
], ($, Init, Util, BaseModule, bootbox, Counter, Map, MapUtil, Cache, FormElement) => {
], ($, Init, Util, BaseModule, bootbox, Counter, Map, MapUtil, FormElement) => {
'use strict';
let SystemSignatureModule = class SystemSignatureModule extends BaseModule {
@@ -32,13 +31,11 @@ define([
/**
* get dataTable id
* @param mapId
* @param systemId
* @param tableType
* @param {...string} parts e.g. 'tableType', 'mapId', 'systemId'
* @returns {string}
*/
getTableId(tableType, mapId, systemId){
return Util.getTableId(this._config.sigTableId, tableType, mapId, systemId);
getTableId(...parts){
return Util.getTableId(this._config.sigTableId, ...parts);
}
/**
@@ -1006,8 +1003,14 @@ define([
if(rowData.id){
// delete signature -----------------------------------------------------------------------
let confirmationSettings = {
title: 'Delete signature',
template: Util.getConfirmationTemplate(module.getConfirmationContent(), {
title: '---',
template: Util.getConfirmationTemplate(Util.getConfirmationContent([{
name: 'deleteConnection',
value: '1',
label: 'delete connection',
class: 'pf-editable-warn',
checked: true
}]), {
size: 'small',
noTitle: true
}),
@@ -1882,47 +1885,6 @@ define([
}
}
/**
* get HTML for "delete connection" confirmation popover
* @returns {string}
*/
getConfirmationContent(){
let checkOptions = [{
name: 'deleteConnection',
value: '1',
label: 'delete connection',
class: 'pf-editable-warn',
checked: true
}];
let getChecklist = checkOptions => {
let html = '<form class="form-inline editableform popover-content-inner">';
html += '<div class="control-group form-group">';
html += '<div class="editable-input">';
html += '<div class="editable-checklist">';
for(let option of checkOptions){
html += '<div><label>';
html += '<input type="checkbox" name="' + option.name + '" value="' + option.value + '" ';
html += 'class="' + option.class + '" ' + (option.checked ? 'checked' : '') + '>';
html += '<span>' + option.label + '</span>';
html += '</label></div>';
}
html += '</div>';
html += '</div>';
html += '</div>';
html += '</form>';
return html;
};
let html = '';
html += getChecklist(checkOptions);
return html;
}
/**
* open xEditable input field in "new Signature" table
*/
@@ -2723,18 +2685,12 @@ define([
return acc;
}, Object.assign({}, SystemSignatureModule.emptySignatureReaderCounterData));
let notification = '';
if(notificationCounter.added > 0){
notification += notificationCounter.added + ' added<br>';
}
if(notificationCounter.changed > 0){
notification += notificationCounter.changed + ' updated<br>';
}
if(notificationCounter.deleted > 0){
notification += notificationCounter.deleted + ' deleted<br>';
}
let notification = Object.keys(notificationCounter).reduce((acc, key) => {
return `${acc}${notificationCounter[key] ? `${notificationCounter[key]} ${key}<br>` : ''}`;
}, '');
if(notification.length){
Util.showNotify({title: 'Signatures updated', text: notification, type: 'success'});
Util.showNotify({title: 'Signatures updated', text: notification, type: 'success', textTrusted: true});
}
}
@@ -2917,7 +2873,7 @@ define([
let cacheKey = [systemTypeId, ...areaIds, groupId].join('_');
let newSelectOptions = SystemSignatureModule.sigTypeOptionsCache.get(cacheKey);
let newSelectOptions = SystemSignatureModule.getCache('sigTypeOptions').get(cacheKey);
// check for cached signature names
if(Array.isArray(newSelectOptions)){
@@ -3014,7 +2970,7 @@ define([
}
// update cache (clone array) -> further manipulation to this array, should not be cached
SystemSignatureModule.sigTypeOptionsCache.set(cacheKey, newSelectOptions.slice(0));
SystemSignatureModule.getCache('sigTypeOptions').set(cacheKey, newSelectOptions.slice(0));
}
// static wormholes (DO NOT CACHE) (not all C2 WHs have the same statics..)
@@ -3120,13 +3076,12 @@ define([
SystemSignatureModule.position = 4; // default sort/order position within sortable area
SystemSignatureModule.label = 'Signatures'; // static module label (e.g. description)
SystemSignatureModule.fullDataUpdate = true; // static module requires additional data (e.g. system description,...)
SystemSignatureModule.sigTypeOptionsCache = new Cache({ // cache signature names
name: 'sigTypeOptions',
ttl: 60 * 5,
maxSize: 100,
debug: false
});
SystemSignatureModule.cacheConfig = {
sigTypeOptions: { // cache signature names
ttl: 60 * 5,
maxSize: 100
}
};
SystemSignatureModule.validSignatureNames = [ // allowed signature type/names
'Cosmic Anomaly',

View File

@@ -6,6 +6,7 @@ define([
'app/init',
'app/lib/prototypes',
'app/lib/console',
'app/lib/cache',
'app/lib/localStore',
'conf/system_effect',
'conf/signature_type',
@@ -20,7 +21,7 @@ define([
'bootstrapConfirmation',
'bootstrapToggle',
'select2'
], ($, Init, Proto, Con, LocalStoreManager, SystemEffect, SignatureType, bootbox) => {
], ($, Init, Proto, Con, Cache, LocalStoreManager, SystemEffect, SignatureType, bootbox) => {
'use strict';
@@ -102,8 +103,17 @@ define([
localStoreNames: ['default', 'character', 'map', 'module'] // Allowed name identifiers for DB names
};
let stopTimerCache = {}; // cache for stopwatch timer
let currentSystemDataCache = new Cache({
name: 'currentSystemData',
ttl: -1,
maxSize: 20
});
// browser tab blink
let initialPageTitle = document.title; // initial page title (cached)
let blinkTimer; // global blink timeout cache
let stopTimerCache = {}; // cache for stopwatch timer
let animationTimerCache = {}; // cache for table row animation timeout
/*
@@ -402,22 +412,57 @@ define([
$.fn.initMapUpdateCounter = function(){
let counterChart = $(this);
counterChart.easyPieChart({
barColor: function(percent){
let color = '#568a89';
if(percent <= 30){
color = '#d9534f';
}else if(percent <= 50){
color = '#f0ad4e';
}
let gradient = [
[0, [217,83,79]],
[10, [217,83,79]],
[50, [240, 173, 78]],
[75, [79,158,79]],
[100, [86, 138, 137]]
];
return color;
},
let gradientWidth = 500;
let getColor = percent => {
percent = percent || 1;
let colorRangeEnd = gradient.findIndex(value => percent <= value[0]);
let colorRange = [colorRangeEnd - 1, colorRangeEnd];
//Get the two closest colors
let colorFirst = gradient[colorRange[0]][1];
let colorSecond = gradient[colorRange[1]][1];
//Calculate ratio between the two closest colors
let colorFirstX = gradientWidth * (gradient[colorRange[0]][0] / 100);
let colorSecondX = gradientWidth * (gradient[colorRange[1]][0] / 100) - colorFirstX;
let weightX = gradientWidth * (percent / 100) - colorFirstX;
let weight = weightX / colorSecondX;
//Get the color with pickHex(thx, less.js's mix function!)
let result = pickHex(colorSecond, colorFirst, weight);
return `rgb(${result.join()})`;
};
let pickHex = (color1, color2, weight) => {
let w1 = weight;
let w2 = 1 - w1;
return [Math.round(color1[0] * w1 + color2[0] * w2),
Math.round(color1[1] * w1 + color2[1] * w2),
Math.round(color1[2] * w1 + color2[2] * w2)];
};
counterChart.easyPieChart({
barColor: percent => getColor(Number(Number(percent).toFixed(1))),
trackColor: '#2b2b2b',
size: 30,
scaleColor: false,
lineWidth: 2,
animate: 1000
animate: {
duration: 550,
enabled: true
},
easing: function (x, t, b, c, d) { // easeInOutSine - jQuery Easing
return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
}
});
};
@@ -1172,6 +1217,39 @@ define([
return html;
};
/**
* get HTML for "delete connection" confirmation popover
* @returns {string}
*/
let getConfirmationContent = checkOptions => {
let getChecklist = checkOptions => {
let html = '<form class="form-inline editableform popover-content-inner">';
html += '<div class="control-group form-group">';
html += '<div class="editable-input">';
html += '<div class="editable-checklist">';
for(let option of checkOptions){
html += '<div><label>';
html += '<input type="checkbox" name="' + option.name + '" value="' + option.value + '" ';
html += 'class="' + option.class + '" ' + (option.checked ? 'checked' : '') + '>';
html += '<span>' + option.label + '</span>';
html += '</label></div>';
}
html += '</div>';
html += '</div>';
html += '</div>';
html += '</form>';
return html;
};
let html = '';
html += getChecklist(checkOptions);
return html;
};
/**
* convert XEditable Select <option> data into Select2 data format
* -> "prepend" (empty) options get added, too
@@ -1547,15 +1625,7 @@ define([
let parts = {};
let time1 = date1.getTime();
let time2 = date2.getTime();
let diff = 0;
if(
time1 >= 0 &&
time2 >= 0
){
diff = (date2.getTime() - date1.getTime()) / 1000;
}
let diff = (time1 >= 0 && time2 >= 0) ? (time2 - time1) / 1000 : 0;
diff = Math.abs(Math.floor(diff));
parts.days = Math.floor(diff/(24*60*60));
@@ -1664,21 +1734,62 @@ define([
/**
* trigger a notification (on screen or desktop)
* @param args
* @param config
* @param options
*/
let showNotify = (...args) => {
requirejs(['PNotify.loader'], Notification => {
Notification.showNotify(...args);
let showNotify = (config = {}, options = {}) => {
requirejs(['pnotify.loader'], Notification => {
// if notification is a "desktio" notification -> blink browser tab
if(options.desktop && config.title){
startTabBlink(config.title);
}
Notification.showNotify(config, options);
});
};
/**
* change document.title and make the browsers tab blink
* @param blinkTitle
*/
let startTabBlink = blinkTitle => {
let initBlink = (function(){
// count blinks if tab is currently active
let activeTabBlinkCount = 0;
let blink = (blinkTitle) => {
// number of "blinks" should be limited if tab is currently active
if(window.isVisible){
activeTabBlinkCount++;
}
// toggle page title
document.title = (document.title === blinkTitle) ? initialPageTitle : blinkTitle;
if(activeTabBlinkCount > 10){
stopTabBlink();
}
};
return () => {
if(!blinkTimer){
blinkTimer = setInterval(blink, 1000, blinkTitle);
}
};
}(blinkTitle));
initBlink();
};
/**
* stop browser tab title "blinking"
*/
let stopTabBlink = () => {
requirejs(['PNotify.loader'], Notification => {
Notification.stopTabBlink();
});
if(blinkTimer){
clearInterval(blinkTimer);
document.title = initialPageTitle;
blinkTimer = null;
}
};
/**
@@ -2707,9 +2818,7 @@ define([
* @param mapId
*/
let deleteCurrentMapData = mapId => {
Init.currentMapData = Init.currentMapData.filter(mapData => {
return (mapData.config.id !== mapId);
});
Init.currentMapData = Init.currentMapData.filter(mapData => mapData.config.id !== mapId);
};
/**
@@ -3036,19 +3145,45 @@ define([
};
/**
* set currentSystemData as "global" variable
* set currentSystemData (active system)
* @param mapId
* @param systemData
*/
let setCurrentSystemData = (systemData) => {
Init.currentSystemData = systemData;
let setCurrentSystemData = (mapId, systemData) => {
mapId = parseInt(mapId) || 0;
if(mapId && typeof systemData === 'object'){
currentSystemDataCache.set(`mapId_${mapId}`, systemData);
}else{
console.error('Invalid mapId %o or systemData %o');
}
};
/**
* get currentSystemData from "global" variables
* get currentSystemData (active system)
* @param mapId
* @returns {*}
*/
let getCurrentSystemData = () => {
return Init.currentSystemData;
let getCurrentSystemData = mapId => {
mapId = parseInt(mapId) || 0;
if(mapId){
return currentSystemDataCache.get(`mapId_${mapId}`);
}else{
console.error('Invalid mapId %o');
}
};
/**
* delete currentSystemData (active system)
* @param mapId
* @returns {*}
*/
let deleteCurrentSystemData = mapId => {
mapId = parseInt(mapId) || 0;
if(mapId){
return currentSystemDataCache.delete(`mapId_${mapId}`);
}else{
console.error('Invalid mapId %o');
}
};
/**
@@ -3217,40 +3352,32 @@ define([
* @param doubleClickCallback
* @param timeout
*/
let singleDoubleClick = (element, singleClickCallback, doubleClickCallback, timeout) => {
let eventName = 'mouseup.singleDouble';
if(!hasEvent(element, eventName)){
let clicks = 0;
// prevent default behaviour (e.g. open <a>-tag link)
element.off('click').on('click', function(e){
e.preventDefault();
});
let singleDoubleClick = (element, singleClickCallback, doubleClickCallback, timeout = Init.timer.DBL_CLICK) => {
element.addEventListener('click', e => {
if(e.detail === 1){
// single click -> setTimeout and check if there is a 2nd click incoming before timeout
let clickTimeoutId = setTimeout(element => {
singleClickCallback.call(element, e);
element.removeData('clickTimeoutId');
}, timeout, e.currentTarget);
element.off(eventName).on(eventName, function(e){
clicks++;
if(clicks === 1){
setTimeout(element => {
if(clicks === 1){
singleClickCallback.call(element, e);
}else{
doubleClickCallback.call(element, e);
}
clicks = 0;
}, timeout || Init.timer.DBL_CLICK, this);
}
});
}
e.currentTarget.setData('clickTimeoutId', clickTimeoutId);
}else if(e.detail === 2 ){
// double click -> clearTimeout, (triple, quadruple, etc. clicks are ignored)
doubleClickCallback.call(element, e);
clearTimeout(e.currentTarget.getData('clickTimeoutId'));
e.currentTarget.removeData('clickTimeoutId');
}
});
};
/**
* get dataTable id
* @param prefix
* @param mapId
* @param systemId
* @param tableType
* @param {...string} parts e.g. 'tableType', 'mapId', 'systemId'
* @returns {string}
*/
let getTableId = (prefix, tableType, mapId, systemId) => prefix + [tableType, mapId, systemId].join('-');
let getTableId = (prefix, ...parts) => prefix + parts.filter(Boolean).join('-');
/**
* get dataTable row id
@@ -3523,12 +3650,14 @@ define([
getCurrentCharacterId: getCurrentCharacterId,
setCurrentSystemData: setCurrentSystemData,
getCurrentSystemData: getCurrentSystemData,
deleteCurrentSystemData:deleteCurrentSystemData,
getCurrentLocationData: getCurrentLocationData,
getCurrentUserInfo: getCurrentUserInfo,
getCurrentCharacterLog: getCurrentCharacterLog,
findInViewport: findInViewport,
initScrollSpy: initScrollSpy,
getConfirmationTemplate: getConfirmationTemplate,
getConfirmationContent: getConfirmationContent,
convertXEditableOptionsToSelect2: convertXEditableOptionsToSelect2,
flattenXEditableSelectArray: flattenXEditableSelectArray,
getCharacterDataBySystemId: getCharacterDataBySystemId,

View File

@@ -0,0 +1,25 @@
/*!
Copyright 2017-2019 SpryMedia Ltd.
This source file is free software, available under the following license:
MIT license - http://datatables.net/license/mit
This source file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
For details please refer to: http://www.datatables.net
RowGroup 1.1.1
©2017-2019 SpryMedia Ltd - datatables.net/license
*/
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.findInternal=function(a,d,c){a instanceof String&&(a=String(a));for(var e=a.length,f=0;f<e;f++){var h=a[f];if(d.call(c,h,f,a))return{i:f,v:h}}return{i:-1,v:void 0}};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;
$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,d,c){a!=Array.prototype&&a!=Object.prototype&&(a[d]=c.value)};$jscomp.getGlobal=function(a){return"undefined"!=typeof window&&window===a?a:"undefined"!=typeof global&&null!=global?global:a};$jscomp.global=$jscomp.getGlobal(this);
$jscomp.polyfill=function(a,d,c,e){if(d){c=$jscomp.global;a=a.split(".");for(e=0;e<a.length-1;e++){var f=a[e];f in c||(c[f]={});c=c[f]}a=a[a.length-1];e=c[a];d=d(e);d!=e&&null!=d&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:d})}};$jscomp.polyfill("Array.prototype.find",function(a){return a?a:function(a,c){return $jscomp.findInternal(this,a,c).v}},"es6","es3");
(function(a){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(d){return a(d,window,document)}):"object"===typeof exports?module.exports=function(d,c){d||(d=window);c&&c.fn.dataTable||(c=require("datatables.net")(d,c).$);return a(c,d,d.document)}:a(jQuery,window,document)})(function(a,d,c,e){var f=a.fn.dataTable,h=function(b,g){if(!f.versionCheck||!f.versionCheck("1.10.8"))throw"RowGroup requires DataTables 1.10.8 or newer";this.c=a.extend(!0,{},f.defaults.rowGroup,
h.defaults,g);this.s={dt:new f.Api(b)};this.dom={};b=this.s.dt.settings()[0];if(g=b.rowGroup)return g;b.rowGroup=this;this._constructor()};a.extend(h.prototype,{dataSrc:function(b){if(b===e)return this.c.dataSrc;var g=this.s.dt;this.c.dataSrc=b;a(g.table().node()).triggerHandler("rowgroup-datasrc.dt",[g,b]);return this},disable:function(){this.c.enable=!1;return this},enable:function(b){if(!1===b)return this.disable();this.c.enable=!0;return this},_constructor:function(){var b=this,a=this.s.dt;a.on("draw.dtrg",
function(){b.c.enable&&b._draw()});a.on("column-visibility.dt.dtrg responsive-resize.dt.dtrg",function(){b._adjustColspan()});a.on("destroy",function(){a.off(".dtrg")});a.on("responsive-resize.dt",function(){b._adjustColspan()})},_adjustColspan:function(){a("tr."+this.c.className,this.s.dt.table().body()).find("td").attr("colspan",this._colspan())},_colspan:function(){return this.s.dt.columns().visible().reduce(function(b,a){return b+a},0)},_draw:function(){var b=this._group(0,this.s.dt.rows({page:"current"}).indexes());
this._groupDisplay(0,b)},_group:function(b,g){for(var c=a.isArray(this.c.dataSrc)?this.c.dataSrc:[this.c.dataSrc],d=f.ext.oApi._fnGetObjectDataFn(c[b]),h=this.s.dt,l,n,m=[],k=0,p=g.length;k<p;k++){var q=g[k];l=h.row(q).data();l=d(l);if(null===l||l===e)l=this.c.emptyDataGroup;if(n===e||l!==n)m.push({dataPoint:l,rows:[]}),n=l;m[m.length-1].rows.push(q)}if(c[b+1]!==e)for(k=0,p=m.length;k<p;k++)m[k].children=this._group(b+1,m[k].rows);return m},_groupDisplay:function(b,a){for(var c=this.s.dt,g,d=0,f=
a.length;d<f;d++){var e=a[d],h=e.dataPoint,k=e.rows;this.c.startRender&&(g=this.c.startRender.call(this,c.rows(k),h,b),(g=this._rowWrap(g,this.c.startClassName,b))&&g.insertBefore(c.row(k[0]).node()));this.c.endRender&&(g=this.c.endRender.call(this,c.rows(k),h,b),(g=this._rowWrap(g,this.c.endClassName,b))&&g.insertAfter(c.row(k[k.length-1]).node()));e.children&&this._groupDisplay(b+1,e.children)}},_rowWrap:function(b,g,c){if(null===b||""===b)b=this.c.emptyDataGroup;return b===e||null===b?null:("object"===
typeof b&&b.nodeName&&"tr"===b.nodeName.toLowerCase()?a(b):b instanceof a&&b.length&&"tr"===b[0].nodeName.toLowerCase()?b:a("<tr/>").append(a("<td/>").attr("colspan",this._colspan()).append(b))).addClass(this.c.className).addClass(g).addClass("dtrg-level-"+c)}});h.defaults={className:"dtrg-group",dataSrc:0,emptyDataGroup:"No group",enable:!0,endClassName:"dtrg-end",endRender:null,startClassName:"dtrg-start",startRender:function(b,a){return a}};h.version="1.1.1";a.fn.dataTable.RowGroup=h;a.fn.DataTable.RowGroup=
h;f.Api.register("rowGroup()",function(){return this});f.Api.register("rowGroup().disable()",function(){return this.iterator("table",function(a){a.rowGroup&&a.rowGroup.enable(!1)})});f.Api.register("rowGroup().enable()",function(a){return this.iterator("table",function(b){b.rowGroup&&b.rowGroup.enable(a===e?!0:a)})});f.Api.register("rowGroup().dataSrc()",function(a){return a===e?this.context[0].rowGroup.dataSrc():this.iterator("table",function(b){b.rowGroup&&b.rowGroup.dataSrc(a)})});a(c).on("preInit.dt.dtrg",
function(b,c,d){"dt"===b.namespace&&(b=c.oInit.rowGroup,d=f.defaults.rowGroup,b||d)&&(d=a.extend({},d,b),!1!==b&&new h(c,d))});return h});

View File

@@ -2036,5 +2036,4 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
function noop() {}
return PNotify_1;
});
//# sourceMappingURL=PNotify.js.map
});

View File

@@ -557,5 +557,4 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
function noop() {}
return PNotifyButtons;
});
//# sourceMappingURL=PNotifyButtons.js.map
});

View File

@@ -253,5 +253,4 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
}
return PNotifyCallbacks;
});
//# sourceMappingURL=PNotifyCallbacks.js.map
});

View File

@@ -460,5 +460,4 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
}
return PNotifyDesktop;
});
//# sourceMappingURL=PNotifyDesktop.js.map
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -70,16 +70,16 @@ requirejs.config({
'datatables.net-buttons': 'lib/datatables/Buttons-1.5.6/js/dataTables.buttons.min',
'datatables.net-buttons-html': 'lib/datatables/Buttons-1.5.6/js/buttons.html5.min',
'datatables.net-responsive': 'lib/datatables/Responsive-2.2.2/js/dataTables.responsive.min',
'datatables.net-rowgroup': 'lib/datatables/RowGroup-1.1.1/js/dataTables.rowGroup.min',
'datatables.net-select': 'lib/datatables/Select-1.3.0/js/dataTables.select.min',
'datatables.plugins.render.ellipsis': 'lib/datatables/plugins/render/ellipsis',
// PNotify // v4.0.0 PNotify - notification core file - https://sciactive.com/pnotify
'PNotify.loader': './app/pnotify.loader',
'pnotify.loader': './app/pnotify.loader',
'PNotify': 'lib/pnotify/PNotify',
'PNotifyButtons': 'lib/pnotify/PNotifyButtons',
'PNotifyNonBlock': 'lib/pnotify/PNotifyNonBlock',
'PNotifyDesktop': 'lib/pnotify/PNotifyDesktop',
'PNotifyCallbacks': 'lib/pnotify/PNotifyCallbacks',
'PNotifyDesktop': 'lib/pnotify/PNotifyDesktop',
'NonBlock': 'lib/pnotify/NonBlock' // v1.0.8 NonBlock.js - for PNotify "nonblock" feature
},
shim: {
@@ -116,6 +116,9 @@ requirejs.config({
'datatables.net-responsive': {
deps: ['datatables.net']
},
'datatables.net-rowgroup': {
deps: ['datatables.net']
},
'datatables.net-select': {
deps: ['datatables.net']
},

View File

@@ -107,7 +107,7 @@ define([
updateDateDiff(element, date, round);
}
};
Cron.set(counterTask);
counterTask.start();
element.attr(config.counterTaskAttr, taskName);
}
@@ -138,7 +138,7 @@ define([
counterTask.task = timer => {
tableApi.cells(null, columnSelector).every(cellUpdate);
};
Cron.set(counterTask);
counterTask.start();
tableElement.attr(config.counterTaskAttr, taskName);
};

View File

@@ -8,7 +8,8 @@ define([
'datatables.net-select',
'datatables.net-buttons',
'datatables.net-buttons-html',
'datatables.net-responsive'
'datatables.net-responsive',
'datatables.net-rowgroup'
], ($, Init, Counter, DeferredPromise, TimeoutPromise) => {
'use strict';

View File

@@ -5,7 +5,7 @@ define([], () => {
* Abstract Cache Strategy class
* @type {AbstractStrategy}
*/
let AbstractStrategy = class AbstractStrategy {
class AbstractStrategy {
constructor(){
if(new.target === AbstractStrategy){
throw new TypeError('Cannot construct AbstractStrategy instances directly');
@@ -19,7 +19,7 @@ define([], () => {
static create(){
return new this();
}
};
}
/**
* LIFO Cache Strategy - First In First Out
@@ -27,7 +27,7 @@ define([], () => {
* without any regard to how often or how many times they were accessed before.
* @type {StrategyFIFO}
*/
let StrategyFIFO = class StrategyFIFO extends AbstractStrategy {
class StrategyFIFO extends AbstractStrategy {
valueToCompare(metaData){
return metaData.age();
}
@@ -35,7 +35,7 @@ define([], () => {
compare(a, b){
return b - a;
}
};
}
/**
* LFU Cache Strategy - Least Frequently Used
@@ -43,7 +43,7 @@ define([], () => {
* Those that are used least often are discarded first
* @type {StrategyLFU}
*/
let StrategyLFU = class StrategyLFU extends AbstractStrategy {
class StrategyLFU extends AbstractStrategy {
valueToCompare(metaData){
return metaData.hitCount;
}
@@ -51,7 +51,7 @@ define([], () => {
compare(a, b){
return a - b;
}
};
}
/**
* LRU Cache Strategy - Least Recently Used
@@ -59,7 +59,7 @@ define([], () => {
* No matter how often they have been accessed.
* @type {StrategyLRU}
*/
let StrategyLRU = class StrategyLRU extends AbstractStrategy {
class StrategyLRU extends AbstractStrategy {
valueToCompare(metaData){
return metaData.hits[metaData.hits.length - 1] || metaData.set;
}
@@ -67,26 +67,26 @@ define([], () => {
compare(a, b){
return a - b;
}
};
}
/**
* Each entry in cache also has its own instance of CacheEntryMeta
* -> The configured Cache Strategy use this meta data for eviction policy
* @type {CacheEntryMeta}
*/
let CacheEntryMeta = class CacheEntryMeta {
class CacheEntryMeta {
constructor(ttl, tSet){
this.ttl = ttl;
this.tSet = tSet || this.constructor.now();
this.tHits = [];
this._ttl = ttl; // ttl < 0 => no expire
this._tSet = tSet || this.constructor.now();
this._tHits = [];
}
get set(){
return this.tSet;
return this._tSet;
}
get hits(){
return this.tHits;
return this._tHits;
}
get hitCount(){
@@ -94,15 +94,15 @@ define([], () => {
}
newHit(current){
this.tHits.push(current || this.constructor.now());
this._tHits.push(current || this.constructor.now());
}
age(current){
return (current || this.constructor.now()) - this.tSet;
return (current || this.constructor.now()) - this._tSet;
}
expired(current){
return this.ttl < this.age(current);
return this._ttl < 0 ? false : this._ttl < this.age(current);
}
static now(){
@@ -112,7 +112,7 @@ define([], () => {
static create(ttl, tSet){
return new this(ttl, tSet);
}
};
}
/**
* Each instance of Cache represents a key value in memory data store
@@ -123,26 +123,18 @@ define([], () => {
* if cache reaches maxSize limit, to increase performance.
* @type {Cache}
*/
let Cache = class Cache {
class Cache {
constructor(config){
this.config = Object.assign({},{
name: 'Default', // custom unique name for identification
ttl: 3600, // default ttl for cache entries
maxSize: 600, // max cache entries
bufferSize: 10, // cache entry count in percent to be removed if maxSize reached
strategy: 'FIFO', // cache strategy policy
debug: false // debug output in console
}, config);
this.store = new Map();
this.metaStore = new WeakMap();
this.strategy = this.constructor.setStrategy(this.config.strategy);
constructor(config = {}){
this._config = Object.assign({}, Cache.defaultConfig, config);
this._store = new Map();
this._metaStore = new WeakMap();
this._strategy = this.constructor.setStrategy(this._config.strategy);
this.debug = (msg,...data) => {
if(this.config.debug){
if(this._config.debug){
data = (data || []);
data.unshift(this.config.name);
data.unshift(this._config.name);
console.debug('debug: CACHE %o | ' + msg, ...data);
}
};
@@ -151,34 +143,34 @@ define([], () => {
}
get size(){
return this.store.size;
return this._store.size;
}
isFull(){
return this.size>= this.config.maxSize;
return this.size>= this._config.maxSize;
}
set(key, value, ttl){
if(this.store.has(key)){
if(this._store.has(key)){
this.debug('SET key %o, UPDATE value %o', key, value);
this.store.set(key, value);
this._store.set(key, value);
}else{
this.debug('SET key %o, NEW value %o', key, value);
if(this.isFull()){
this.debug(' ↪ FULL trim cache…');
this.trim(this.trimCount(1));
}
this.store.set(key, value);
this._store.set(key, value);
}
this.metaStore.set(value, CacheEntryMeta.create(ttl || this.config.ttl));
this._metaStore.set(value, CacheEntryMeta.create(ttl || this._config.ttl));
}
get(key){
if(this.store.has(key)){
let value = this.store.get(key);
if(this._store.has(key)){
let value = this._store.get(key);
if(value){
let metaData = this.metaStore.get(value);
let metaData = this._metaStore.get(value);
if(metaData.expired()){
this.debug('EXPIRED key %o delete', key);
this.delete(key);
@@ -199,8 +191,8 @@ define([], () => {
keysForTrim(count){
let trimKeys = [];
let compare = [];
for(let [key, value] of this.store){
let metaData = this.metaStore.get(value);
for(let [key, value] of this._store){
let metaData = this._metaStore.get(value);
if(metaData.expired()){
trimKeys.push(key);
if(count === trimKeys.length){
@@ -209,14 +201,14 @@ define([], () => {
}else{
compare.push({
key: key,
value: this.strategy.valueToCompare(metaData)
value: this._strategy.valueToCompare(metaData)
});
}
}
let countLeft = count - trimKeys.length;
if(countLeft > 0){
compare = compare.sort((a, b) => this.strategy.compare(a.value, b.value));
compare = compare.sort((a, b) => this._strategy.compare(a.value, b.value));
trimKeys = trimKeys.concat(compare.splice(0, countLeft).map(a => a.key));
}
@@ -224,20 +216,20 @@ define([], () => {
}
keys(){
return this.store.keys();
return this._store.keys();
}
delete(key){
return this.store.delete(key);
return this._store.delete(key);
}
clear(){
this.store.clear();
this._store.clear();
}
trimCount(spaceLeft){
let bufferSize = Math.max(Math.round(this.config.maxSize / 100 * this.config.bufferSize), spaceLeft);
return Math.min(Math.max(this.size - this.config.maxSize + bufferSize, 0), this.size);
let bufferSize = Math.max(Math.round(this._config.maxSize / 100 * this._config.bufferSize), spaceLeft);
return Math.min(Math.max(this.size - this._config.maxSize + bufferSize, 0), this.size);
}
trim(count){
@@ -253,9 +245,9 @@ define([], () => {
status(){
return {
config: this.config,
store: this.store,
metaStore: this.metaStore
config: this._config,
store: this._store,
metaStore: this._metaStore
};
}
@@ -269,6 +261,15 @@ define([], () => {
}
}
}
Cache.defaultConfig = {
name: 'Default', // custom unique name for identification
ttl: 3600, // default ttl for cache entries
maxSize: 600, // max cache entries
bufferSize: 10, // cache entry count in percent to be removed if maxSize reached
strategy: 'FIFO', // cache strategy policy
debug: false // debug output in console
};
return Cache;

View File

@@ -11,7 +11,7 @@ define([
console.info('task1 function():', timer.getTotalTimeValues());
return 'OK';
};
Cron.set(task1);
task1.start();
Example2 run task every 3 seconds ---------------------------------------------------------------------------------
let task1 = Cron.new('task1', {precision: 'seconds', interval: 3, timeout: 100});
@@ -19,7 +19,7 @@ define([
console.info('task1 function():', timer.getTotalTimeValues());
return 'OK';
};
Cron.set(task1);
task1.start();
Example3 resolve Promise on run ----------------------------------------------------------------------------------
let task1 = Cron.new('task1', {precision: 'seconds', interval: 1, timeout: 100});
@@ -35,14 +35,14 @@ define([
});
});
};
Cron.set(task1);
task1.start();
Example4 run task once at given Date() --------------------------------------------------------------------------
let dueDate = new Date();
dueDate.setSeconds(dueDate.getSeconds() + 5);
let task2 = Cron.new('task2', {precision: 'seconds', timeout: 100, dueDate: dueDate});
task2.task = () => 'OK task2';
Cron.set(task2);
task2.start();
*/
/**
@@ -51,35 +51,42 @@ define([
* @type {Task}
*/
let Task = class Task {
constructor(name, config){
/**
*
* @param {string} name
* @param {{}} config
* @param {CronManager} manager
*/
constructor(name, config, manager = null){
if(typeof name !== 'string'){
throw new TypeError('Task "name" must be instance of String, Type of "' + typeof name + '" given');
}
this._config = Object.assign({}, this.constructor.defaultConfig, config);
this._name = name; // unique name for identification
this._task = undefined; // task to run, instanceof Function, can also return a Promise
this._manager = undefined; // reference to CronManager() that handles this task
this._task = (timer, task) => {}; // task to run, instanceof Function, can also return a Promise
this._manager = manager; // reference to CronManager() that handles this task
this._runCount = 0; // counter for run() calls
this._runQueue = new Map(); // current run() processes. > 1 requires config.isParallel: true
this._runCount = 0; // total run counter for this task
this._lastTotalTimeValues = undefined; // time values of last run()
}
/**
* @returns {string}
*/
get name(){
return this._name;
}
get task(){
/**
* @returns {(function())} task
*/
get task() {
return this._task;
}
get runCount(){
return this._runCount;
}
get precision(){
return this._config.precision;
}
/**
* @param {(function())} task
*/
set task(task){
if(task instanceof Function){
this._task = task;
@@ -88,34 +95,120 @@ define([
}
}
/**
* @returns {number}
*/
get runCount(){
return this._runCount;
}
/**
* @returns {string}
*/
get precision(){
return this.get('precision');
}
/**
* @returns {boolean}
*/
get paused(){
return this.get('paused');
}
/**
* @returns {boolean}
*/
get targetAchieved(){
return this.get('targetRunCount') ? this.runCount >= this.get('targetRunCount') : false;
}
/**
* @returns {number}
*/
get targetProgress(){
return parseFloat(
parseFloat(
(!this.get('targetRunCount') || !this.runCount) ?
0 :
(100 / this.get('targetRunCount') * this.runCount)
).toFixed(2));
}
/**
* @param option
* @returns {*}
*/
get(option){
return this._config[option];
}
/**
* @param {string} option
* @param {*} value
*/
set(option, value){
this._config[option] = value;
}
setManager(manager){
this._manager = manager;
/**
* connect CronManager with instance
* @param {CronManager} manager
*/
connect(manager = this._manager){
if(manager instanceof CronManager){
if(manager !== this._manager){
// disconnect from current manager (if exists)
this.disconnect();
this._manager = manager;
}
this._manager.set(this);
}else{
throw new TypeError('Parameter must be instance of CronManager. Type of "' + typeof manager + '" given');
}
}
/**
* disconnect from CronManager
* @param {CronManager} manager
*/
disconnect(manager = this._manager){
if(manager instanceof CronManager){
if(this.isConnected(manager)){
this._manager.delete(this._name);
}
}else{
throw new TypeError('Parameter must be instance of CronManager. Type of "' + typeof manager + '" given');
}
}
/**
* checks if CronManager is connected with instance
* @param {CronManager} manager
* @returns {boolean}
*/
isConnected(manager = this._manager){
return (manager instanceof CronManager) &&
manager === this._manager &&
manager.has(this._name);
}
/**
* if task is currently running
* @returns {boolean}
*/
isRunning(){
return !!this._runQueue.size;
}
delete(){
let isDeleted = false;
if(this._manager){
isDeleted = this._manager.delete(this._name);
}
return isDeleted;
}
/**
* @param timer
* @returns {boolean}
*/
isDue(timer){
if(this._config.dueDate instanceof Date){
if(this.get('dueDate') instanceof Date){
// run once at dueDate
if(new Date().getTime() >= this._config.dueDate.getTime()){
if(new Date().getTime() >= this.get('dueDate').getTime()){
return true;
}
}else{
@@ -124,8 +217,8 @@ define([
let totalTimeValuePrecision = totalTimeValues[this.precision];
totalTimeValuePrecision -= this._lastTotalTimeValues ? this._lastTotalTimeValues[this.precision] : 0;
if(
this._config.interval === 1 ||
totalTimeValuePrecision % this._config.interval === 0
this.get('interval') === 1 ||
totalTimeValuePrecision % this.get('interval') === 0
){
return true;
}
@@ -133,23 +226,30 @@ define([
return false;
}
/**
* @param timer
*/
invoke(timer){
if(
!this.paused &&
this.isDue(timer) &&
(!this.isRunning() || this._config.isParallel)
(!this.isRunning() || this.get('isParallel'))
){
this.run(timer);
}
}
/**
* @param timer
*/
run(timer){
this._lastTotalTimeValues = Object.assign({}, timer.getTotalTimeValues());
let runId = 'run_' + (++this._runCount);
let runExec = resolve => {
resolve(this.task(timer, this));
resolve(this._task(timer, this));
};
let myProm = this._config.timeout > 0 ? new TimeoutPromise(runExec, this._config.timeout) : new Promise(runExec);
let myProm = this.get('timeout') > 0 ? new TimeoutPromise(runExec, this.get('timeout')) : new Promise(runExec);
myProm.then(payload => {
// resolved within timeout -> wait for finally() block
}).catch(error => {
@@ -162,14 +262,37 @@ define([
// -> remove from _runQueue
this._runQueue.delete(runId);
// remove this task from store after run
if(this._config.dueDate instanceof Date){
this.delete();
if(this.get('dueDate') instanceof Date){
this.disconnect();
}
if(this.targetAchieved){
this.stop();
}
});
this._runQueue.set(runId, myProm);
}
// Task controls ----------------------------------------------------------------------------------------------
start(){
this.set('paused', false);
this.connect();
}
stop(){
this.reset();
this.disconnect();
}
pause(){
this.set('paused', true);
}
reset(){
this._runCount = 0;
}
};
Task.defaultConfig = {
@@ -177,7 +300,9 @@ define([
isParallel: false, // if true this task can run parallel, e.g. if prev execution has not finished
interval: 1, // relates to 'precision'. 'interval' = 3 and 'precision' = "seconds" -> run every 3 seconds
dueDate: undefined, // if Date() instance is set, task only runs once at dueDate
timeout: 50 // if > 0, execution time that exceeds timeout (ms) throw error
timeout: 50, // if > 0, execution time that exceeds timeout (ms) throw error
paused: false, // if true this task will not run() but will be invoce()´ed
targetRunCount: 0 // if > 0, task will stop if targetRunCount is reached
};
@@ -188,6 +313,9 @@ define([
*/
let CronManager = class CronManager {
/**
* @param {{}} config
*/
constructor(config){
this._config = Object.assign({}, this.constructor.defaultConfig, config);
this._timerConfig = Object.assign({}, this.constructor.defaultTimerConfig);
@@ -211,47 +339,75 @@ define([
};
}
/**
* @param {string} name
* @param {{}} config
* @returns {Task}
*/
new(name, config){
return new Task(name, config);
return new Task(name, config, this);
}
/**
* @param {Task} task
*/
set(task){
if(task instanceof Task){
// check for unique task name, or update existing task
if(!this.has(task.name) || (this.get(task.name) === task)){
// set new or update existing task
task.setManager(this);
// check for unique task name
if(!this.has(task.name)){
// connect new task
// -> must be before connect(this)! (prevents infinite loop)
this._tasks.set(task.name, task);
task.connect(this);
this.debug('SET/UPDATE task: %o config: %o', task.name, task);
// start timer (if it is not already running)
this.auto();
}else{
console.warn('FAILED to set task. Task name %o already exists', task.name);
}
}else{
throw new TypeError('Parameter must be instance of Task');
}
}
/**
* @param {string} name
* @param {{}} config
*/
setNew(name, config){
this.set(this.new(name, config));
}
/**
* @param {string} name
* @returns {Task|undefined}
*/
get(name){
return this._tasks.get(name);
}
/**
* @param {string} name
* @returns {boolean}
*/
has(name){
return this._tasks.has(name);
}
/**
* @param {string} name
*/
delete(name){
let isDeleted = this._tasks.delete(name);
if(isDeleted){
if(this.has(name)){
let task = this._tasks.get(name);
// disconnect task
// -> must be before disconnect(this)! (prevents infinite loop)
this._tasks.delete(name);
task.disconnect(this);
this.debug('DELETE task: %o', name);
// stop timer (if no more tasks connected)
this.auto();
}
return isDeleted;
}
clear(){
@@ -260,6 +416,10 @@ define([
this.auto();
}
/**
* @param {string} precision
* @returns {Task[]}
*/
tasksByPrecision(precision){
let tasks = [];
this._tasks.forEach(task => {

View File

@@ -34,7 +34,7 @@ define([], () => {
}
get(obj, key) {
return this._store.has(obj) && this._store.get(obj).get(key);
return this._store.has(obj) && (key ? this._store.get(obj).get(key) : this._store.get(obj));
}
has(obj, key) {
@@ -45,6 +45,8 @@ define([], () => {
let ret = false;
if (this._store.has(obj)) {
ret = this._store.get(obj).delete(key);
// remove obj if store is empty
// -> 'size' property is does not exist if valueStore is WeakMap
if (!this._store.get(obj).size) {
this._store.delete(obj);
}

View File

@@ -193,10 +193,10 @@ define([
}
/**
* set LocalStoreManager for this instance
* connect LocalStoreManager with instance
* @param {LocalStoreManager} manager
*/
setManager(manager){
connect(manager){
if(manager instanceof LocalStoreManager){
this._manager = manager;
}else{
@@ -299,7 +299,7 @@ define([
/**
* check var for Object
* @param obj
* @returns {boolean|boolean}
* @returns {boolean}
*/
static isObject(obj){
return (!!obj) && (obj.constructor === Object);
@@ -372,7 +372,7 @@ define([
}, {
name: LocalStore.buildDbName(name)
});
store.setManager(this);
store.connect(this);
this._store.set(name, store);
}
return this._store.get(name);

View File

@@ -103,14 +103,9 @@ define([
* @returns {number}
*/
String.prototype.hashCode = function(){
let hash = 0, i, chr;
if(this.length === 0) return hash;
for(i = 0; i < this.length; i++){
chr = this.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
let hash = this.split('').reduce((a,b) => (((a << 5) - a) + b.charCodeAt(0))|0, 0);
// make positive
return (hash + 2147483647) + 1;
};
String.prototype.trimLeftChars = function(charList){

View File

@@ -222,7 +222,7 @@ define([
* @param excludeMenu
*/
let closeMenus = excludeMenu => {
let allMenus = $('.' + config.contextMenuClass + '[role="menu"]');
let allMenus = $('.' + config.contextMenuClass + '[role="menu"][style*="display: block"]');
if(excludeMenu){
allMenus = allMenus.not(excludeMenu);
}
@@ -291,17 +291,15 @@ define([
/**
* default config (skeleton) for valid context menu configuration
* @returns {{hidden: Array, active: Array, disabled: Array, id: string, selectCallback: null}}
* @returns {{hidden: [], active: [], disabled: [], id: string, selectCallback: null}}
*/
let defaultMenuOptionConfig = () => {
return {
'id': '',
'selectCallback': null,
'hidden': [],
'active': [],
'disabled': []
};
};
let defaultMenuOptionConfig = () => ({
'id': '',
'selectCallback': null,
'hidden': [],
'active': [],
'disabled': []
});
return {
config: config,

View File

@@ -29,7 +29,6 @@ define([
mapIdPrefix: 'pf-map-', // id prefix for all maps
systemClass: 'pf-system', // class for all systems
systemActiveClass: 'pf-system-active', // class for an active system on a map
systemSelectedClass: 'pf-system-selected', // class for selected systems on a map
systemHeadClass: 'pf-system-head', // class for system head
systemHeadNameClass: 'pf-system-head-name', // class for system name
@@ -116,7 +115,7 @@ define([
},
connectionsDetachable: true, // dragOptions are set -> allow detaching them
maxConnections: 10, // due to isTarget is true, this is the max count of !out!-going connections
// isSource:true
//isSource:true
},
target: {
filter: filterSystemHeadEvent,
@@ -125,10 +124,10 @@ define([
//allowLoopBack: false, // loopBack connections are not allowed
cssClass: config.endpointTargetClass,
dropOptions: {
hoverClass: config.systemActiveClass,
//hoverClass: '',
activeClass: 'dragActive'
},
// uniqueEndpoint: false
//uniqueEndpoint: false
},
endpointTypes: Init.endpointTypes,
connectionTypes: Init.connectionTypes
@@ -876,23 +875,23 @@ define([
* connect two systems
* @param map
* @param connectionData
* @returns new connection
* @returns {Promise<any>}
*/
let drawConnection = (map, connectionData) => {
let drawConnection = (map, connectionData) => new Promise((resolve, reject) => {
let mapContainer = $(map.getContainer());
let mapId = mapContainer.data('id');
let connectionId = connectionData.id || 0;
let connection;
let sourceSystem = $('#' + MapUtil.getSystemId(mapId, connectionData.source));
let targetSystem = $('#' + MapUtil.getSystemId(mapId, connectionData.target));
// check if both systems exists
// (If not -> something went wrong e.g. DB-Foreign keys for "ON DELETE",...)
if(
sourceSystem.length &&
targetSystem.length
){
connection = map.connect({
if(!sourceSystem.length){
reject(new Error(`drawConnection(): source system (id: ${connectionData.source}) not found`));
}else if(!targetSystem.length){
reject(new Error(`drawConnection(): target system (id: ${connectionData.target}) not found`));
}else{
let connection = map.connect({
source: sourceSystem[0],
target: targetSystem[0],
scope: connectionData.scope || map.Defaults.Scope,
@@ -948,18 +947,18 @@ define([
}
}
}
}
}else{
if( !sourceSystem.length ){
console.warn('drawConnection(): source system (id: ' + connectionData.source + ') not found');
}
if( !targetSystem.length ){
console.warn('drawConnection(): target system (id: ' + connectionData.target + ') not found');
resolve({
action: 'drawConnection',
data: {
connection: connection
}
});
}else{
reject(new Error(`drawConnection(): connection must be instanceof jsPlumb.Connection`));
}
}
return connection;
};
});
/**
* compares the current data and new data of a connection and updates status
@@ -1233,168 +1232,178 @@ define([
* @param reject
*/
let updateMapExecutor = (resolve, reject) => {
// jsPlumb needs to be initialized. This is not the case when switching between map tabs right after refresh
let mapContainer = mapConfig.map ? $(mapConfig.map.getContainer()) : null;
if(mapContainer){
let mapId = mapConfig.config.id;
// add additional information for this map
if(mapContainer.data('updated') !== mapConfig.config.updated.updated){
mapContainer.data('name', mapConfig.config.name);
mapContainer.data('scopeId', mapConfig.config.scope.id);
mapContainer.data('typeId', mapConfig.config.type.id);
mapContainer.data('typeName', mapConfig.config.type.name);
mapContainer.data('icon', mapConfig.config.icon);
mapContainer.data('created', mapConfig.config.created.created);
mapContainer.data('updated', mapConfig.config.updated.updated);
}
// get map data
let mapData = getMapDataForSync(mapContainer, [], true);
if(mapData !== false){
// map data available -> map not locked by update counter :)
let currentSystemData = mapData.data.systems;
let currentConnectionData = mapData.data.connections;
// update systems =================================================================================
for(let i = 0; i < mapConfig.data.systems.length; i++){
let systemData = mapConfig.data.systems[i];
// add system
let addNewSystem = true;
for(let k = 0; k < currentSystemData.length; k++){
if(currentSystemData[k].id === systemData.id){
if(currentSystemData[k].updated.updated < systemData.updated.updated){
// system changed -> update
mapContainer.getSystem(mapConfig.map, systemData);
}
addNewSystem = false;
break;
}
}
if(addNewSystem === true){
drawSystem(mapConfig.map, systemData);
}
}
// check for systems that are gone -> delete system
for(let a = 0; a < currentSystemData.length; a++){
let deleteThisSystem = true;
for(let b = 0; b < mapConfig.data.systems.length; b++){
let deleteSystemData = mapConfig.data.systems[b];
if(deleteSystemData.id === currentSystemData[a].id){
deleteThisSystem = false;
break;
}
}
if(deleteThisSystem === true){
let deleteSystem = $('#' + MapUtil.getSystemId(mapContainer.data('id'), currentSystemData[a].id));
// system not found -> delete system
System.removeSystems(mapConfig.map, deleteSystem);
}
}
// update connections =============================================================================
// jsPlumb batch() is used, otherwise there are some "strange" visual bugs
// when switching maps (Endpoints are not displayed correctly)
mapConfig.map.batch(function(){
for(let j = 0; j < mapConfig.data.connections.length; j++){
let connectionData = mapConfig.data.connections[j];
// add connection
let addNewConnection= true;
for(let c = 0; c < currentConnectionData.length; c++){
if(currentConnectionData[c].id === connectionData.id){
// connection already exists -> check for updates
if(currentConnectionData[c].updated < connectionData.updated){
// connection changed -> update
updateConnection(currentConnectionData[c].connection, connectionData);
}
addNewConnection = false;
break;
}else if(
currentConnectionData[c].id === 0 &&
currentConnectionData[c].source === connectionData.source &&
currentConnectionData[c].target === connectionData.target
){
// if ids don´t match -> check for unsaved connection
updateConnection(currentConnectionData[c].connection, connectionData);
addNewConnection = false;
break;
}
}
if(addNewConnection === true){
drawConnection(mapConfig.map, connectionData);
}
}
// check for connections that are gone -> delete connection
for(let d = 0; d < currentConnectionData.length; d++){
// skip connections with id = 0 -> they might get updated before
if(currentConnectionData[d].id === 0){
continue;
}
let deleteThisConnection = true;
for(let e = 0; e < mapConfig.data.connections.length;e++){
let deleteConnectionData = mapConfig.data.connections[e];
if(deleteConnectionData.id === currentConnectionData[d].id){
deleteThisConnection = false;
break;
}
}
if(deleteThisConnection === true){
// connection not found -> delete connection
let deleteConnection = currentConnectionData[d].connection;
if(deleteConnection){
// check if "source" and "target" still exist before remove
// this is NOT the case if the system was removed previous
if(
deleteConnection.source &&
deleteConnection.target
){
mapConfig.map.deleteConnection(deleteConnection, {fireEvent: false});
}
}
}
}
});
// update local connection cache
updateConnectionsCache(mapConfig.map);
}else{
// map is currently logged -> queue update for this map until unlock
if( mapUpdateQueue.indexOf(mapId) === -1 ){
mapUpdateQueue.push(mapId);
}
}
}
resolve({
let payload = {
action: 'updateMap',
data: {
mapConfig: mapConfig
}
});
};
// jsPlumb needs to be initialized. This is not the case when switching between map tabs right after refresh
let mapContainer = mapConfig.map ? mapConfig.map.getContainer() : null;
if(!mapContainer){
return resolve(payload);
}
let mapId = mapConfig.config.id;
// mapData == false -> map locked by update counter. Skip update
let mapData = getMapDataForSync(mapContainer, [], true);
if(!mapData){
// map is currently locked -> queue update for this map until unlock
if(mapUpdateQueue.indexOf(mapId) === -1){
mapUpdateQueue.push(mapId);
}
return resolve(payload);
}
mapContainer = $(mapContainer);
// add additional information for this map
if(mapContainer.data('updated') !== mapConfig.config.updated.updated){
mapContainer.data('name', mapConfig.config.name);
mapContainer.data('scopeId', mapConfig.config.scope.id);
mapContainer.data('typeId', mapConfig.config.type.id);
mapContainer.data('typeName', mapConfig.config.type.name);
mapContainer.data('icon', mapConfig.config.icon);
mapContainer.data('created', mapConfig.config.created.created);
mapContainer.data('updated', mapConfig.config.updated.updated);
}
// map data available -> map not locked by update counter :)
let currentSystemData = mapData.data.systems;
let currentConnectionData = mapData.data.connections;
// update systems =========================================================================================
for(let i = 0; i < mapConfig.data.systems.length; i++){
let systemData = mapConfig.data.systems[i];
// add system
let addNewSystem = true;
for(let k = 0; k < currentSystemData.length; k++){
if(currentSystemData[k].id === systemData.id){
if(currentSystemData[k].updated.updated < systemData.updated.updated){
// system changed -> update
mapContainer.getSystem(mapConfig.map, systemData);
}
addNewSystem = false;
break;
}
}
if(addNewSystem === true){
drawSystem(mapConfig.map, systemData).catch(console.warn);
}
}
// check for systems that are gone -> delete system
for(let a = 0; a < currentSystemData.length; a++){
let deleteThisSystem = true;
for(let b = 0; b < mapConfig.data.systems.length; b++){
let deleteSystemData = mapConfig.data.systems[b];
if(deleteSystemData.id === currentSystemData[a].id){
deleteThisSystem = false;
break;
}
}
if(deleteThisSystem === true){
let deleteSystem = $('#' + MapUtil.getSystemId(mapContainer.data('id'), currentSystemData[a].id));
// system not found -> delete system
System.removeSystems(mapConfig.map, deleteSystem);
}
}
// update connections =====================================================================================
// jsPlumb setSuspendDrawing() (batch() did not work because it async 'scopes' out updates).
// -> Otherwise there are some "strange" visual bugs when switching maps (Endpoints are not displayed correctly)
// -> needs to be "disabled" later in this method.
mapConfig.map.setSuspendDrawing(true);
for(let j = 0; j < mapConfig.data.connections.length; j++){
let connectionData = mapConfig.data.connections[j];
// add connection
let addNewConnection= true;
for(let c = 0; c < currentConnectionData.length; c++){
if(currentConnectionData[c].id === connectionData.id){
// connection already exists -> check for updates
if(currentConnectionData[c].updated < connectionData.updated){
// connection changed -> update
updateConnection(currentConnectionData[c].connection, connectionData);
}
addNewConnection = false;
break;
}else if(
currentConnectionData[c].id === 0 &&
currentConnectionData[c].source === connectionData.source &&
currentConnectionData[c].target === connectionData.target
){
// if ids don´t match -> check for unsaved connection
updateConnection(currentConnectionData[c].connection, connectionData);
addNewConnection = false;
break;
}
}
if(addNewConnection === true){
drawConnection(mapConfig.map, connectionData).catch(console.warn);
}
}
// check for connections that are gone -> delete connection
for(let d = 0; d < currentConnectionData.length; d++){
// skip connections with id = 0 -> they might get updated before
if(currentConnectionData[d].id === 0){
continue;
}
let deleteThisConnection = true;
for(let e = 0; e < mapConfig.data.connections.length;e++){
let deleteConnectionData = mapConfig.data.connections[e];
if(deleteConnectionData.id === currentConnectionData[d].id){
deleteThisConnection = false;
break;
}
}
if(deleteThisConnection === true){
// connection not found -> delete connection
let deleteConnection = currentConnectionData[d].connection;
if(deleteConnection){
// check if "source" and "target" still exist before remove
// this is NOT the case if the system was removed previous
if(
deleteConnection.source &&
deleteConnection.target
){
mapConfig.map.deleteConnection(deleteConnection, {fireEvent: false});
}
}
}
}
mapConfig.map.setSuspendDrawing(false, true);
// update local connection cache
updateConnectionsCache(mapConfig.map);
return resolve(payload);
};
/**
@@ -1402,44 +1411,53 @@ define([
* @param payload
* @returns {Promise<any>}
*/
let filterMapByScopes = payload => {
let filterMapByScopesExecutor = resolve => {
Util.getLocalStore('map').getItem(payload.data.mapConfig.config.id).then(dataStore => {
let scopes = [];
if(dataStore && dataStore.filterScopes){
scopes = dataStore.filterScopes;
}
MapUtil.filterMapByScopes(payload.data.mapConfig.map, scopes);
resolve(payload);
});
};
return new Promise(filterMapByScopesExecutor);
};
let filterMapByScopes = payload => new Promise(resolve => {
Util.getLocalStore('map').getItem(payload.data.mapConfig.config.id).then(dataStore => {
let scopes = [];
if(dataStore && dataStore.filterScopes){
scopes = dataStore.filterScopes;
}
MapUtil.filterMapByScopes(payload.data.mapConfig.map, scopes);
resolve(payload);
});
});
/**
* show signature overlays
* @param payload
* @returns {Promise<any>}
*/
let showInfoSignatureOverlays = payload => {
let showInfoSignatureOverlaysExecutor = resolve => {
Util.getLocalStore('map').getItem(payload.data.mapConfig.config.id).then(dataStore => {
if(dataStore && dataStore.mapSignatureOverlays){
MapOverlay.showInfoSignatureOverlays($(payload.data.mapConfig.map.getContainer()));
}
let showInfoSignatureOverlays = payload => new Promise(resolve => {
Util.getLocalStore('map').getItem(payload.data.mapConfig.config.id).then(dataStore => {
if(dataStore && dataStore.mapSignatureOverlays){
MapOverlay.showInfoSignatureOverlays($(payload.data.mapConfig.map.getContainer()));
}
resolve(payload);
});
});
resolve(payload);
/**
* after map update is complete
* -> trigger update event for 'global' modules
* @param payload
* @returns {Promise<any>}
*/
let afterUpdate = payload => new Promise(resolve => {
// in rare cases there is a bug where map is undefined (hard to reproduce
let map = Util.getObjVal(payload, 'data.mapConfig.map');
if(map){
let tabContentEl = map.getContainer().closest(`.${Util.config.mapTabContentClass}`);
$(tabContentEl).trigger('pf:updateGlobalModules', {
payload: Util.getObjVal(payload, 'data.mapConfig.config.id')
});
};
return new Promise(showInfoSignatureOverlaysExecutor);
};
}
resolve(payload);
});
return new Promise(updateMapExecutor)
.then(showInfoSignatureOverlays)
.then(filterMapByScopes);
.then(filterMapByScopes)
.then(afterUpdate);
};
/**
@@ -1530,28 +1548,22 @@ define([
* @param systemData
* @returns {boolean}
*/
let isValidSystem = systemData => {
let isValid = true;
if(
!systemData.hasOwnProperty('name') ||
systemData.name.length === 0
){
return false;
}
return isValid;
};
let isValidSystem = systemData => (Util.getObjVal(systemData, 'name') || '').length > 0;
/**
* draw a system with its data to a map
* @param map
* @param systemData
* @param connectedSystem
* @param connectionData
* @returns {Promise<any>}
*/
let drawSystem = (map, systemData, connectedSystem) => {
// check if systemData is valid
let drawSystem = (map, systemData, connectedSystem, connectionData = null) => new Promise((resolve, reject) => {
if(isValidSystem(systemData)){
let payloadDrawSystem = {
action: 'drawSystem'
};
let mapContainer = $(map.getContainer());
// get System Element by data
@@ -1575,24 +1587,37 @@ define([
// register system to "magnetizer"
Magnetizer.addElement(systemData.mapId, newSystem[0]);
payloadDrawSystem.data = {
system: newSystem
};
// connect new system (if connection data is given)
if(connectedSystem){
// hint: "scope + type" might be changed automatically when it gets saved
// -> based on jump distance,..
let connectionData = {
connectionData = Object.assign({}, {
source: $(connectedSystem).data('id'),
target: newSystem.data('id'),
scope: map.Defaults.Scope,
type: [MapUtil.getDefaultConnectionTypeByScope(map.Defaults.Scope)]
};
let connection = drawConnection(map, connectionData);
}, connectionData);
// store connection
saveConnection(connection);
drawConnection(map, connectionData)
.then(payload => saveConnection(payload.data.connection, Boolean(connectionData.disableAutoScope)))
.then(payload => {
payloadDrawSystem.data = {
connection: payload.data.connection
};
resolve(payloadDrawSystem);
})
.catch(reject);
}else{
resolve(payloadDrawSystem);
}
}else{
reject(new Error(`drawSystem() failed. Invalid systemData`));
}
};
});
/**
* make a system name/alias editable by x-editable
@@ -1656,56 +1681,69 @@ define([
/**
* stores a connection in database
* @param connection
* @param disableAutoScope
* @returns {Promise<any>}
*/
let saveConnection = connection => {
if(connection instanceof jsPlumb.Connection){
connection.addType('state_process');
let map = connection._jsPlumb.instance;
let mapContainer = $(map.getContainer());
let mapId = mapContainer.data('id');
let connectionData = MapUtil.getDataByConnection(connection);
connectionData.mapId = mapId;
Util.request('PUT', 'connection', [], connectionData, {
connection: connection,
map: map,
mapId: mapId,
oldConnectionData: connectionData
}).then(
payload => {
let newConnectionData = payload.data;
if(!$.isEmptyObject(newConnectionData)){
// update connection data e.g. "scope" has auto detected
connection = updateConnection(payload.context.connection, newConnectionData);
// new/updated connection should be cached immediately!
updateConnectionCache(payload.context.mapId, connection);
// connection scope
let scope = MapUtil.getScopeInfoForConnection(newConnectionData.scope, 'label');
let title = 'New connection established';
if(payload.context.oldConnectionData.id > 0){
title = 'Connection switched';
}
Util.showNotify({title: title, text: 'Scope: ' + scope, type: 'success'});
}else{
// some save errors
payload.context.map.deleteConnection(payload.context.connection, {fireEvent: false});
}
},
payload => {
// remove this connection from map
payload.context.map.deleteConnection(payload.context.connection, {fireEvent: false});
Util.handleAjaxErrorResponse(payload);
}
);
let saveConnection = (connection, disableAutoScope = false) => new Promise((resolve, reject) => {
if(!(connection instanceof jsPlumb.Connection)){
reject(new Error(`saveConnection(): connection must be instanceof jsPlumb.Connection`));
}
};
connection.addType('state_process');
let map = connection._jsPlumb.instance;
let mapContainer = $(map.getContainer());
let mapId = mapContainer.data('id');
let connectionData = MapUtil.getDataByConnection(connection);
connectionData.mapId = mapId;
connectionData.disableAutoScope = disableAutoScope;
Util.request('PUT', 'connection', [], connectionData, {
connection: connection,
map: map,
mapId: mapId,
oldConnectionData: connectionData
}).then(
payload => {
let newConnectionData = payload.data;
if(!$.isEmptyObject(newConnectionData)){
// update connection data e.g. "scope" has auto detected
connection = updateConnection(payload.context.connection, newConnectionData);
// new/updated connection should be cached immediately!
updateConnectionCache(payload.context.mapId, connection);
// connection scope
let scope = MapUtil.getScopeInfoForConnection(newConnectionData.scope, 'label');
let title = 'New connection established';
if(payload.context.oldConnectionData.id > 0){
title = 'Connection switched';
}
Util.showNotify({title: title, text: 'Scope: ' + scope, type: 'success'});
resolve({
action: 'saveConnection',
data: {
connection: connection
}
});
}else{
// some save errors
payload.context.map.deleteConnection(payload.context.connection, {fireEvent: false});
reject(new Error(`saveConnection(): response error`));
}
},
payload => {
// remove this connection from map
payload.context.map.deleteConnection(payload.context.connection, {fireEvent: false});
Util.handleAjaxErrorResponse(payload);
reject(new Error(`saveConnection(): request error`));
}
);
});
/**
* get context menu config for a map component (e.g. system, connection,..)
@@ -1742,7 +1780,7 @@ define([
options.hidden.push('delete_system');
}
if( !mapContainer.find('.' + config.systemActiveClass).length){
if( !mapContainer.find('.' + MapUtil.config.systemActiveClass).length){
options.hidden.push('find_route');
}
@@ -1755,7 +1793,7 @@ define([
}
// disabled menu actions
if(system.hasClass(config.systemActiveClass)){
if(system.hasClass(MapUtil.config.systemActiveClass)){
options.disabled.push('find_route');
}
@@ -1898,6 +1936,8 @@ define([
// map overlay will be set on "drag" start
let mapOverlayTimer = null;
let debounceDrag = false;
// make system draggable
map.draggable(system, {
containment: 'parent',
@@ -1907,7 +1947,7 @@ define([
snapThreshold: MapUtil.config.mapSnapToGridDimension, // distance for grid snapping "magnet" effect (optional)
start: function(params){
let dragSystem = $(params.el);
dragSystem.css('pointer-events','none');
mapOverlayTimer = MapOverlayUtil.getMapOverlay(dragSystem, 'timer');
// start map update timer
@@ -1920,9 +1960,6 @@ define([
delete( params.drag.params.grid );
}
// stop "system click event" right after drop event is finished
dragSystem.addClass('no-click');
// drag system is not always selected
let selectedSystems = mapContainer.getSelectedSystems().get();
selectedSystems = selectedSystems.concat(dragSystem.get());
@@ -1938,12 +1975,19 @@ define([
$(selectedSystems).updateSystemZIndex();
},
drag: function(p){
// start map update timer
mapOverlayTimer.startMapUpdateCounter();
if(!debounceDrag) {
requestAnimationFrame(() => {
// start map update timer
mapOverlayTimer.startMapUpdateCounter();
// update system positions for "all" systems that are effected by drag&drop
// this requires "magnet" feature to be active! (optional)
Magnetizer.executeAtEvent(map, p.e);
// update system positions for "all" systems that are effected by drag&drop
// this requires "magnet" feature to be active! (optional)
Magnetizer.executeAtEvent(map, p.e);
debounceDrag = false;
});
}
debounceDrag = true;
},
stop: function(params){
let dragSystem = $(params.el);
@@ -1951,10 +1995,6 @@ define([
// start map update timer
mapOverlayTimer.startMapUpdateCounter();
setTimeout(function(){
dragSystem.removeClass('no-click');
}, Init.timer.DBL_CLICK + 50);
// show tooltip
dragSystem.toggleSystemTooltip('show', {show: true});
@@ -1975,7 +2015,7 @@ define([
// update all dragged systems -> added to DragSelection
params.selection.forEach(elData => {
MapUtil.markAsChanged($(elData[0]));
MapUtil.markAsChanged($(elData[0]).css('pointer-events','initial'));
});
}
});
@@ -2016,23 +2056,20 @@ define([
if(!popoverClick){
let system = $(this);
// check if system is locked for "click" events
if(!system.hasClass('no-click')){
// left mouse button
if(e.which === 1){
if(e.ctrlKey === true){
// select system
MapUtil.toggleSystemsSelect(map, [system]);
}else{
MapUtil.showSystemInfo(map, system);
}
// left mouse button
if(e.which === 1){
if(e.ctrlKey === true){
// select system
MapUtil.toggleSystemsSelect(map, [system]);
}else{
MapUtil.showSystemInfo(map, system);
}
}
}
};
Util.singleDoubleClick(system, single, double);
Util.singleDoubleClick(system[0], single, double);
};
/**
@@ -2040,10 +2077,10 @@ define([
* @param map
* @param newSystemData
* @param sourceSystem
* @param connectionData
* @returns {Promise<any>}
*/
let saveSystemCallback = (map, newSystemData, sourceSystem) => {
drawSystem(map, newSystemData, sourceSystem);
};
let saveSystemCallback = (map, newSystemData, sourceSystem, connectionData = null) => drawSystem(map, newSystemData, sourceSystem, connectionData);
/**
* select all (selectable) systems on a mapElement
@@ -2193,17 +2230,8 @@ define([
return false;
}
// lock the target system for "click" events
// to prevent loading system information
let sourceSystem = $('#' + sourceId);
let targetSystem = $('#' + targetId);
sourceSystem.addClass('no-click');
targetSystem.addClass('no-click');
setTimeout(() => {
sourceSystem.removeClass('no-click');
targetSystem.removeClass('no-click');
}, Init.timer.DBL_CLICK + 50);
// switch connection type to "abyss" in case source OR target system belongs to "a-space"
if(sourceSystem.data('typeId') === 3 || targetSystem.data('typeId') === 3){
@@ -2227,7 +2255,7 @@ define([
}
// always save the new connection
saveConnection(connection);
saveConnection(connection).catch(console.warn);
return true;
});
@@ -2721,7 +2749,7 @@ define([
selectSystem(mapContainer, data);
break;
case 'AddSystem':
System.showNewSystemDialog(map, data, saveSystemCallback);
System.showNewSystemDialog(map, data, typeof data.callback === 'function' ? data.callback : saveSystemCallback);
break;
default:
console.warn('Unknown menuAction %o event name', action);
@@ -2938,16 +2966,16 @@ define([
/**
* collect all map data from client for server or client sync
* @param mapContainer
* @param {HTMLElement} mapContainer
* @param filter
* @param minimal
* @returns {boolean}
* @returns {boolean|{}}
*/
let getMapDataForSync = (mapContainer, filter = [], minimal = false) => {
let mapData = false;
// check if there is an active map counter that prevents collecting map data (locked map)
if(!MapOverlayUtil.getMapOverlayInterval(mapContainer)){
mapData = mapContainer.getMapDataFromClient(filter, minimal);
if(!MapOverlayUtil.isMapCounterOverlayActive(mapContainer)){
mapData = $(mapContainer).getMapDataFromClient(filter, minimal);
}
return mapData;
};
@@ -2957,6 +2985,7 @@ define([
* this function returns the "client" data NOT the "server" data for a map
* @param filter
* @param minimal
* @returns {{}}
*/
$.fn.getMapDataFromClient = function(filter = [], minimal = false){
let mapContainer = $(this);
@@ -3099,34 +3128,29 @@ define([
* @param options
* @returns {Promise<any>}
*/
let initMapOptions = (mapConfig, options) => {
let initMapOptionsExecutor = (resolve, reject) => {
let payload = {
action: 'initMapOptions',
data: {
mapConfig: mapConfig
}
};
if(options.showAnimation){
let mapElement = $(mapConfig.map.getContainer());
MapUtil.setMapDefaultOptions(mapElement, mapConfig.config)
.then(payload => MapUtil.visualizeMap(mapElement, 'show'))
.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'});
})
.then(() => resolve(payload));
}else{
// nothing to do here...
resolve(payload);
let initMapOptions = (mapConfig, options) => new Promise((resolve, reject) => {
let payload = {
action: 'initMapOptions',
data: {
mapConfig: mapConfig
}
};
return new Promise(initMapOptionsExecutor);
};
if(options.showAnimation){
let mapElement = $(mapConfig.map.getContainer());
MapUtil.setMapDefaultOptions(mapElement, mapConfig.config)
.then(payload => MapUtil.visualizeMap(mapElement, 'show'))
.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'});
})
.then(() => resolve(payload));
}else{
// nothing to do here...
resolve(payload);
}
});
/**
* load OR updates system map
@@ -3136,6 +3160,9 @@ define([
* @returns {Promise<any>}
*/
let loadMap = (areaMap, mapConfig, options) => {
// whether map gets loaded (initialized) for the first time
// or just updated an existing map
let isFirstLoad = false;
/**
* load map promise
@@ -3150,6 +3177,7 @@ define([
if(mapConfig.map.getContainer() === undefined){
// map not loaded -> create & update
isFirstLoad = true;
newMapElement(areaMap, mapConfig)
.then(payload => updateMap(payload.data.mapConfig))
.then(payload => resolve(payload));
@@ -3162,7 +3190,12 @@ define([
};
return new Promise(loadMapExecutor)
.then(payload => initMapOptions(payload.data.mapConfig, options));
.then(payload => initMapOptions(payload.data.mapConfig, options))
.then(payload => ({
action: 'loadMap',
data: payload.data,
isFirstLoad
}));
};
/**
@@ -3330,7 +3363,9 @@ define([
loadMap: loadMap,
updateUserData: updateUserData,
getMapDataForSync: getMapDataForSync,
saveSystemCallback: saveSystemCallback
saveSystemCallback: saveSystemCallback,
drawConnection: drawConnection,
saveConnection: saveConnection
};
});

View File

@@ -7,8 +7,9 @@ define([
'app/init',
'app/util',
'app/map/overlay/util',
'app/map/util'
], ($, Init, Util, MapOverlayUtil, MapUtil) => {
'app/map/util',
'app/lib/cron'
], ($, Init, Util, MapOverlayUtil, MapUtil, Cron) => {
'use strict';
/**
@@ -107,119 +108,120 @@ define([
let type = 'info_signature';
connectionsData = Util.arrayToObject(connectionsData);
map.batch(() => {
map.getAllConnections().forEach(connection => {
let connectionId = connection.getParameter('connectionId');
let sourceEndpoint = connection.endpoints[0];
let targetEndpoint = connection.endpoints[1];
map.setSuspendDrawing(true);
let connectionData = connectionsData.hasOwnProperty(connectionId) ? connectionsData[connectionId] : undefined;
let signatureTypeData = MapUtil.getConnectionDataFromSignatures(connection, connectionData);
map.getAllConnections().forEach(connection => {
let connectionId = connection.getParameter('connectionId');
let sourceEndpoint = connection.endpoints[0];
let targetEndpoint = connection.endpoints[1];
let sizeLockedBySignature = false;
let connectionData = Util.getObjVal(connectionsData, `${connectionId}`);
let signatureTypeData = MapUtil.getConnectionDataFromSignatures(connection, connectionData);
if(connection.scope === 'wh'){
if(!connection.hasType(type)){
connection.addType(type);
}
let sizeLockedBySignature = false;
let overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId);
if(connection.scope === 'wh'){
if(!connection.hasType(type)){
connection.addType(type);
}
// Arrow overlay needs to be cleared() (removed) if 'info_signature' gets removed!
// jsPlumb does not handle overlay updates for Arrow overlays... so we need to re-apply the the overlay manually
if(overlayArrow.path && !overlayArrow.path.isConnected){
connection.canvas.appendChild(overlayArrow.path);
}
let overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId);
// 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;
// Arrow overlay needs to be cleared() (removed) if 'info_signature' gets removed!
// jsPlumb does not handle overlay updates for Arrow overlays... so we need to re-apply the the overlay manually
if(overlayArrow.path && !overlayArrow.path.isConnected){
connection.canvas.appendChild(overlayArrow.path);
}
let overlayType = 'diamond'; // not specified
let arrowDirection = 1;
// since there "could" be multiple sig labels on each endpoint,
// there can only one "primary label picked up for wormhole jump mass detection!
let primeLabel;
if(connectionData && connectionData.signatures){
// signature data found for current connection
let sourceLabel = signatureTypeData.source.labels;
let targetLabel = signatureTypeData.target.labels;
let overlayType = 'diamond'; // not specified
let arrowDirection = 1;
// add arrow (connection) overlay that points from "XXX" => "K162" ----------------------------
if(
(sourceLabel.includes('K162') && targetLabel.includes('K162')) ||
(sourceLabel.length === 0 && targetLabel.length === 0) ||
(
sourceLabel.length > 0 && targetLabel.length > 0 &&
!sourceLabel.includes('K162') && !targetLabel.includes('K162')
)
){
// unknown direction -> show default 'diamond' overlay
overlayType = 'diamond';
}else if(
(sourceLabel.includes('K162')) ||
(sourceLabel.length === 0 && !targetLabel.includes('K162'))
){
// convert default arrow direction
overlayType = 'arrow';
arrowDirection = -1;
if(connectionData && connectionData.signatures){
// signature data found for current connection
let sourceLabel = signatureTypeData.source.labels;
let targetLabel = signatureTypeData.target.labels;
primLabel = targetLabel.find(label => label !== 'K162');
}else{
// default arrow direction is fine
overlayType = 'arrow';
// add arrow (connection) overlay that points from "XXX" => "K162" ----------------------------
if(
(sourceLabel.includes('K162') && targetLabel.includes('K162')) ||
(sourceLabel.length === 0 && targetLabel.length === 0) ||
(
sourceLabel.length > 0 && targetLabel.length > 0 &&
!sourceLabel.includes('K162') && !targetLabel.includes('K162')
)
){
// unknown direction -> show default 'diamond' overlay
overlayType = 'diamond';
}else if(
(sourceLabel.includes('K162')) ||
(sourceLabel.length === 0 && !targetLabel.includes('K162'))
){
// convert default arrow direction
overlayType = 'arrow';
arrowDirection = -1;
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'){
connection.updateClasses(
MapOverlayUtil.config.connectionArrowOverlaySuccessClass,
MapOverlayUtil.config.connectionArrowOverlayDangerClass
);
primeLabel = targetLabel.find(label => label !== 'K162');
}else{
connection.updateClasses(
MapOverlayUtil.config.connectionArrowOverlayDangerClass,
MapOverlayUtil.config.connectionArrowOverlaySuccessClass
);
}
// default arrow direction is fine
overlayType = 'arrow';
overlayArrow.updateFrom(getConnectionArrowOverlayParams(overlayType, arrowDirection));
// 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]);
// get 'connection mass type' from wormholeData
let massType = Util.getObjVal(wormholeData, 'size.type');
if(massType && !connection.hasType(massType)){
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);
primeLabel = sourceLabel.find(label => label !== 'K162');
}
}
// lock/unlock connection for manual size changes (from contextmenu)
connection.setParameter('sizeLocked', sizeLockedBySignature);
});
// 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'){
connection.updateClasses(
MapOverlayUtil.config.connectionArrowOverlaySuccessClass,
MapOverlayUtil.config.connectionArrowOverlayDangerClass
);
}else{
connection.updateClasses(
MapOverlayUtil.config.connectionArrowOverlayDangerClass,
MapOverlayUtil.config.connectionArrowOverlaySuccessClass
);
}
overlayArrow.updateFrom(getConnectionArrowOverlayParams(overlayType, arrowDirection));
// 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(primeLabel)){
// connection size from mapped signature
sizeLockedBySignature = true;
// get 'connection mass type' from wormholeData
let massType = Util.getObjVal(Object.assign({}, Init.wormholes[primeLabel]), 'size.type');
if(massType && !connection.hasType(massType)){
MapOverlayUtil.getMapOverlay(connection.canvas, 'timer').startMapUpdateCounter();
MapUtil.setConnectionJumpMassType(connection, massType);
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);
});
map.setSuspendDrawing(false, true);
};
/**
@@ -291,21 +293,23 @@ define([
let map = MapUtil.getMapInstance(mapId);
let type = 'info_signature';
map.batch(() => {
map.getAllConnections().forEach(connection => {
let overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId);
map.setSuspendDrawing(true);
if(overlayArrow){
overlayArrow.cleanup();
}
map.getAllConnections().forEach(connection => {
let overlayArrow = connection.getOverlay(MapOverlayUtil.config.connectionOverlayArrowId);
if(connection.hasType(type)){
connection.removeType(type, {}, true);
}
});
if(overlayArrow){
overlayArrow.cleanup();
}
map.selectEndpoints().removeOverlay(MapOverlayUtil.config.endpointOverlayId);
if(connection.hasType(type)){
connection.removeType(type, {}, true);
}
});
map.selectEndpoints().removeOverlay(MapOverlayUtil.config.endpointOverlayId);
map.setSuspendDrawing(false, true);
};
/**
@@ -321,7 +325,7 @@ define([
trigger: 'active',
class: 'pf-map-overlay-filter',
iconClass: ['fas', 'fa-fw', 'fa-filter'],
onClick: function(e){
onClick: function(e){
// clear all filter
let mapElement = MapOverlayUtil.getMapElementFromOverlay(this);
let map = getMapObjectFromOverlayIcon(this);
@@ -484,108 +488,96 @@ define([
* @param mapOverlayTimer
* @param percent
* @param value
* @returns {*}
*/
let setMapUpdateCounter = (mapOverlayTimer, percent, value) => {
let setMapUpdateCounter = (mapOverlayTimer, percent, value = '') => {
// check if counter already exists
let counterChart = MapOverlayUtil.getMapCounter(mapOverlayTimer);
if(counterChart.length === 0){
if(!MapOverlayUtil.getMapCounter(mapOverlayTimer)){
// create new counter
let chartEl = Object.assign(document.createElement('div'), {
className: `${Init.classes.pieChart.class} ${Init.classes.pieChart.pieChartMapCounterClass}`
});
counterChart = $('<div>', {
class: [Init.classes.pieChart.class, Init.classes.pieChart.pieChartMapCounterClass].join(' ')
}).attr('data-percent', percent).append(
$('<span>', {
text: value
})
);
let chartInnerEl = Object.assign(document.createElement('span'), {
textContent: value
});
let iconEl = Object.assign(document.createElement('i'), {
className: ['fas', 'fa-fw', 'fa-lock'].join(' ')
});
mapOverlayTimer.append(counterChart);
chartInnerEl.append(iconEl);
chartEl.append(chartInnerEl);
mapOverlayTimer.append(chartEl);
// init counter
counterChart.initMapUpdateCounter();
$(chartEl).initMapUpdateCounter();
// set tooltip
mapOverlayTimer.attr('data-placement', 'left');
mapOverlayTimer.attr('title', 'update counter');
mapOverlayTimer.tooltip();
mapOverlayTimer.dataset.placement = 'left';
mapOverlayTimer.setAttribute('title', 'update counter');
$(mapOverlayTimer).tooltip();
}
return counterChart;
};
/**
* start the map update counter or reset
*/
$.fn.startMapUpdateCounter = function(){
let mapOverlayTimer = $(this);
let counterChart = MapOverlayUtil.getMapCounter(mapOverlayTimer);
let maxSeconds = MapOverlayUtil.config.logTimerCount;
let counterChartLabel = counterChart.find('span');
let percentPerCount = 100 / maxSeconds;
// update counter
let updateChart = tempSeconds => {
let pieChart = counterChart.data('easyPieChart');
if(pieChart !== undefined){
counterChart.data('easyPieChart').update( percentPerCount * tempSeconds);
}
counterChartLabel.text(tempSeconds);
};
// main timer function is called on any counter update
let timer = mapUpdateCounter => {
// decrease timer
let currentSeconds = counterChart.data('currentSeconds');
currentSeconds--;
counterChart.data('currentSeconds', currentSeconds);
if(currentSeconds >= 0){
// update counter
updateChart(currentSeconds);
}else{
// hide counter and reset
clearInterval(mapUpdateCounter);
mapOverlayTimer.velocity('transition.whirlOut', {
duration: Init.animationSpeed.mapOverlay,
complete: function(){
counterChart.data('interval', false);
MapOverlayUtil.getMapElementFromOverlay(mapOverlayTimer).trigger('pf:unlocked');
}
});
}
};
// get current seconds (in case the timer is already running)
let currentSeconds = counterChart.data('currentSeconds');
// start values for timer and chart
counterChart.data('currentSeconds', maxSeconds);
updateChart(maxSeconds);
if(
currentSeconds === undefined ||
currentSeconds < 0
){
// start timer
let mapUpdateCounter = setInterval(() => {
timer(mapUpdateCounter);
}, 1000);
// store counter interval
counterChart.data('interval', mapUpdateCounter);
// show overlay
if(mapOverlayTimer.is(':hidden')){
mapOverlayTimer.velocity('stop').velocity('transition.whirlIn', { duration: Init.animationSpeed.mapOverlay });
}
if(!this.length){
console.warn('startMapUpdateCounter() failed. Missing DOM node');
return;
}
let mapOverlayTimer = this[0];
let counterChart = MapOverlayUtil.getMapCounter(mapOverlayTimer);
let pieChart = $(counterChart).data('easyPieChart');
if(!pieChart){
console.warn('startMapUpdateCounter() failed. easyPieChart not initialized');
return;
}
let updateChart = (percent = 0) => {
if(pieChart){
pieChart.update(percent);
}
};
let task = counterChart.getData('counterTask');
if(!task){
let tabContentEl = mapOverlayTimer.closest(`.${Util.config.mapTabContentClass}`);
let mapId = parseInt(tabContentEl.dataset.mapId) || 0;
task = Cron.new(`mapUpdateCounter_${mapId}`, {
precision: 'secondTenths',
isParallel: true,
targetRunCount: 10 * MapOverlayUtil.config.logTimerCount
});
task.task = (timer, task) => {
// debounce 80% (reduce repaint)
if(task.runCount % 5 === 0){
let progress = Math.round(task.targetProgress);
updateChart(100 - progress);
}
if(task.targetAchieved){
$(mapOverlayTimer).velocity('transition.whirlOut', {
duration: Init.animationSpeed.mapOverlay,
complete: function(){
MapOverlayUtil.getMapElementFromOverlay(mapOverlayTimer).trigger('pf:unlocked');
}
});
}
};
counterChart.setData('counterTask', task);
}
// task is not connected if: 'targetAchieved' or not started
if(!task.isConnected()){
$(mapOverlayTimer).velocity('stop').velocity('transition.whirlIn', { duration: Init.animationSpeed.mapOverlay });
}
updateChart(100);
task.reset();
task.start();
};
/**
@@ -836,36 +828,35 @@ define([
});
// add all overlay elements
for(let prop in options){
if(options.hasOwnProperty(prop)){
let icon = $('<i>', {
class: options[prop].iconClass.concat( ['pull-right', options[prop].class] ).join(' ')
}).attr('title', options[prop].title).tooltip({
placement: 'bottom',
container: 'body',
delay: 150
});
Object.entries(options).forEach(([key, option]) => {
let icon = $('<i>', {
class: option.iconClass.concat(['pull-right', option.class]).join(' ')
}).attr('title', option.title).tooltip({
placement: 'bottom',
container: 'body',
delay: 150
});
// add "hover" action for some icons
if(
options[prop].trigger === 'hover' ||
options[prop].trigger === 'refresh'
){
icon.hoverIntent(options[prop].hoverIntent);
}
// add "click" handler for some icons
if(options[prop].hasOwnProperty('onClick')){
icon.on('click', options[prop].onClick);
}
mapOverlayInfo.append(icon);
// add "hover" action for some icons
if(
option.trigger === 'hover' ||
option.trigger === 'refresh'
){
icon.hoverIntent(option.hoverIntent);
}
}
// add "click" handler for some icons
if(option.hasOwnProperty('onClick')){
icon.on('click', option.onClick);
}
mapOverlayInfo.append(icon);
});
parentElement.append(mapOverlayInfo);
// reset map update timer
setMapUpdateCounter(mapOverlayTimer, 100, MapOverlayUtil.config.logTimerCount);
setMapUpdateCounter(mapOverlayTimer[0], 100);
});
};

View File

@@ -84,22 +84,32 @@ define([
/**
* get the map counter chart from overlay
* @param element
* @returns {jQuery}
* @returns {Element}
*/
let getMapCounter = element => $(element).find('.' + Init.classes.pieChart.pieChartMapCounterClass);
let getMapCounter = element => element.querySelector(`.${Init.classes.pieChart.pieChartMapCounterClass}`);
/**
* get interval value from map timer overlay
* @param element
* @returns {*}
* if there is an "active" (connected) counter task
* -> lock overlay
* @param {HTMLElement} element
* @returns {boolean}
*/
let getMapOverlayInterval = element => getMapCounter(getMapOverlay(element, 'timer')).data('interval');
let isMapCounterOverlayActive = element => {
let mapOverlay = getMapOverlay(element, 'timer');
if(mapOverlay){
let mapCounter = getMapCounter(mapOverlay[0]);
if(mapCounter && mapCounter.getData('counterTask')){
return mapCounter.getData('counterTask').isConnected();
}
}
return false;
};
return {
config: config,
getMapOverlay: getMapOverlay,
getMapElementFromOverlay: getMapElementFromOverlay,
getMapCounter: getMapCounter,
getMapOverlayInterval: getMapOverlayInterval
isMapCounterOverlayActive: isMapCounterOverlayActive
};
});

View File

@@ -23,7 +23,6 @@ define([
systemHeadInfoLeftClass: 'pf-system-head-info-left', // class for left system info
systemHeadInfoRightClass: 'pf-system-head-info-right', // class for right system info
systemActiveClass: 'pf-system-active', // class for an active system on a map
systemTooltipInnerIdPrefix: 'pf-system-tooltip-inner-', // id prefix for system tooltip content
systemTooltipInnerClass: 'pf-system-tooltip-inner', // class for system tooltip content
@@ -231,24 +230,26 @@ define([
if(formValid === false) return false;
// calculate new system position ----------------------------------------------------------
let newPosition = {
x: 0,
y: 0
};
let newPosition;
// add new position
let sourceSystem = null;
let connectionData = null;
if(options.sourceSystem !== undefined){
// new position based on sourceSystem´s position
sourceSystem = options.sourceSystem;
connectionData = options.connectionData || null;
// get new position
newPosition = newSystemPositionBySystem(sourceSystem);
}else if(options.position){
// check mouse cursor position (add system to map)
// new position based on coordinated (e.g. mouse event)
newPosition = {
x: options.position.x,
y: options.position.y
};
}else{
// new position based on current map scroll offset
newPosition = MapUtil.newSystemPositionsByMapOffset(mapContainer)[0];
}
formData.position = newPosition;
@@ -262,7 +263,8 @@ define([
systemDialog: systemDialog,
formElement: form,
map: map,
sourceSystem: sourceSystem
sourceSystem: sourceSystem,
connectionData: connectionData
}, context => {
// always do
context.systemDialog.find('.modal-content').hideLoadingAnimation();
@@ -270,7 +272,7 @@ define([
payload => {
Util.showNotify({title: 'New system', text: payload.data.name, type: 'success'});
callback(payload.context.map, payload.data, payload.context.sourceSystem);
callback(payload.context.map, payload.data, payload.context.sourceSystem, payload.context.connectionData);
bootbox.hideAll();
},
Util.handleAjaxErrorResponse
@@ -699,10 +701,12 @@ define([
for(let system of systems){
system = $(system);
let mapId = parseInt(system.data('mapid')) || 0;
// check if system is "active"
if(system.hasClass(config.systemActiveClass)){
delete Init.currentSystemData;
if(system.hasClass(MapUtil.config.systemActiveClass)){
Util.deleteCurrentSystemData(mapId);
// get parent Tab Content and fire clear modules event
let tabContentElement = MapUtil.getTabContentElementByMapElement(system);
$(tabContentElement).trigger('pf:removeSystemModules');
@@ -712,7 +716,7 @@ define([
map.deleteConnectionsForElement(system, {fireEvent: false});
// unregister from "magnetizer"
Magnetizer.removeElement(system.data('mapid'), system[0]);
Magnetizer.removeElement(mapId, system[0]);
// destroy tooltip/popover
system.toggleSystemTooltip('destroy', {});

View File

@@ -186,25 +186,50 @@ define([
* @param mapData
* @param value
* @param key
* @returns {any}
* @returns {{}|boolean}
*/
let getSystemDataFromMapData = (mapData, value, key = 'id') => {
return mapData ? mapData.data.systems.find(system => system[key] === value) || false : false;
return (Util.getObjVal(mapData, `data.systems`) || [])
.find(systemData => systemData[key] === value) || false;
};
/**
* get system data by mapId system data selector
* get system data by mapId + system data selector
* -> e.g. value = 2 and key = 'id'
* -> e.g. value = 30002187 and key = 'systemId' => looks for 'Amarr' CCP systemId
* @param mapId
* @param value
* @param key
* @returns {any}
* @returns {{}|boolean}
*/
let getSystemData = (mapId, value, key = 'id') => {
return getSystemDataFromMapData(Util.getCurrentMapData(mapId), value, key);
};
/**
* get connection data from mapData
* @see getConnectionData
* @param mapData
* @param value
* @param key
* @returns {{}|boolean}
*/
let getConnectionDataFromMapData = (mapData, value, key = 'id') => {
return (Util.getObjVal(mapData, `data.connections`) || [])
.find(connectionData => connectionData[key] === value) || false;
};
/**
* get connection data by mapId + connection data selector
* @param mapId
* @param value
* @param key
* @returns {{}|boolean}
*/
let getConnectionData = (mapId, value, key = 'id') => {
return getConnectionDataFromMapData(Util.getCurrentMapData(mapId), value, key);
};
/**
* get system type information
* @param {number} systemTypeId
@@ -212,14 +237,7 @@ define([
* @returns {string}
*/
let getSystemTypeInfo = (systemTypeId, option) => {
let systemTypeInfo = '';
$.each(Init.systemType, function(prop, data){
if(systemTypeId === data.id){
systemTypeInfo = data[option];
return;
}
});
return systemTypeInfo;
return (Object.values(Init.systemType).find(data => data.id === systemTypeId) || {})[option] || '';
};
/**
@@ -229,11 +247,7 @@ define([
* @returns {string}
*/
let getEffectInfoForSystem = (effect, option) => {
let effectInfo = '';
if( Init.classes.systemEffects.hasOwnProperty(effect) ){
effectInfo = Init.classes.systemEffects[effect][option];
}
return effectInfo;
return Util.getObjVal(Init.classes.systemEffects, `${effect}.${option}`) || '';
};
/**
@@ -478,13 +492,16 @@ define([
let getConnectionDataFromSignatures = (connection, connectionData) => {
let signatureTypeData = {
source: {
ids: [],
names: [],
labels: []
},
target: {
ids: [],
names: [],
labels: []
}
},
hash: false // unique hash key build from all relevant signature for connection
};
if(
@@ -501,41 +518,50 @@ define([
let sourceId = sourceSystem.data('id');
let targetId = targetSystem.data('id');
// in case connection is currently "dragged" between systems, sourceId and/or targetId is undefined
if(!sourceId || !targetId){
return signatureTypeData;
}
let hash = [];
// ... collect overlay/label data from signatures
for(let signatureData of connectionData.signatures){
// ... typeId is required to get a valid name
if(signatureData.typeId > 0){
hash.push(Util.getObjVal(signatureData, 'updated.updated'));
// whether "source" or "target" system is relevant for current connection and current signature...
let tmpSystem = null;
let tmpSystemType = null;
// whether "source" or "target" system is relevant for current connection and current signature...
let tmpSystem = null;
let tmpSystemType = null;
if(signatureData.system.id === sourceId){
// relates to "source" endpoint
tmpSystemType = 'source';
tmpSystem = sourceSystem;
}else if(signatureData.system.id === targetId){
// relates to "target" endpoint
tmpSystemType = 'target';
tmpSystem = targetSystem;
}
if(signatureData.system.id === sourceId){
// relates to "source" endpoint
tmpSystemType = 'source';
tmpSystem = sourceSystem;
}else if(signatureData.system.id === targetId){
// relates to "target" endpoint
tmpSystemType = 'target';
tmpSystem = targetSystem;
}
// ... get endpoint label for source || target system
if(tmpSystem && tmpSystem){
// ... get all available signature type (wormholes) names
let availableSigTypeNames = SystemSignatureModule.getSignatureTypeOptionsBySystem(tmpSystem, 5);
let flattenSigTypeNames = Util.flattenXEditableSelectArray(availableSigTypeNames);
signatureTypeData[tmpSystemType].ids.push(signatureData.id);
signatureTypeData[tmpSystemType].names.push(signatureData.name.toUpperCase());
if(flattenSigTypeNames.hasOwnProperty(signatureData.typeId)){
let label = flattenSigTypeNames[signatureData.typeId];
// shorten label, just take the ingame name
label = label.substr(0, label.indexOf(' '));
signatureTypeData[tmpSystemType].names.push(signatureData.name);
signatureTypeData[tmpSystemType].labels.push(label);
}
// ... typeId is required to get a valid labels
// ... get endpoint label for source || target system
if(signatureData.typeId > 0 && tmpSystem && tmpSystem){
// ... get all available signature type (wormholes) names
let availableSigTypeNames = SystemSignatureModule.getSignatureTypeOptionsBySystem(tmpSystem, 5);
let flattenSigTypeNames = Util.flattenXEditableSelectArray(availableSigTypeNames);
if(flattenSigTypeNames.hasOwnProperty(signatureData.typeId)){
let label = flattenSigTypeNames[signatureData.typeId];
// shorten label, just take the ingame name
label = label.substr(0, label.indexOf(' '));
signatureTypeData[tmpSystemType].labels.push(label);
}
}
}
// ... build unique hash
signatureTypeData.hash = hash.join().hashCode();
}
return signatureTypeData;
@@ -621,64 +647,66 @@ define([
*/
let filterMapByScopes = (map, scopes) => {
if(map){
map.batch(() => {
let mapElement = $(map.getContainer());
let allSystems = mapElement.getSystems();
let allConnections = map.getAllConnections();
map.setSuspendDrawing(true);
if(scopes && scopes.length){
// filter connections -------------------------------------------------------------------------------------
let visibleSystems = [];
let visibleConnections = searchConnectionsByScopeAndType(map, scopes);
let mapElement = $(map.getContainer());
let allSystems = mapElement.getSystems();
let allConnections = map.getAllConnections();
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(scopes && scopes.length){
// filter connections -------------------------------------------------------------------------------------
let visibleSystems = [];
let visibleConnections = searchConnectionsByScopeAndType(map, scopes);
// 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
){
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);
}
for(let connection of allConnections){
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);
}
MapOverlayUtil.getMapOverlay(mapElement, 'info').updateOverlayIcon('filter', 'hide');
}
});
// 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
){
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);
}
for(let connection of allConnections){
setConnectionVisible(connection, true);
}
MapOverlayUtil.getMapOverlay(mapElement, 'info').updateOverlayIcon('filter', 'hide');
}
map.setSuspendDrawing(false, true);
}
};
@@ -792,17 +820,19 @@ define([
*/
let setSystemActive = (map, system) => {
// deselect all selected systems on map
let mapContainer = $( map.getContainer() );
mapContainer.find('.' + config.systemClass).removeClass(config.systemActiveClass);
let mapContainer = map.getContainer();
[...mapContainer.getElementsByClassName(config.systemClass)]
.forEach(systemEl =>
systemEl.classList.remove(config.systemActiveClass)
);
// set current system active
system.addClass(config.systemActiveClass);
// collect all required data from map module to update the info element
// store them global and assessable for each module
// collect all required systemData from map module -> cache
let systemData = system.getSystemData();
systemData.mapId = parseInt(system.attr('data-mapid')) || 0;
Util.setCurrentSystemData(systemData);
Util.setCurrentSystemData(systemData.mapId, systemData);
};
/**
@@ -913,20 +943,22 @@ define([
* @param connections
*/
let setConnectionsActive = (map, connections) => {
map.batch(() => {
// set all inactive
for(let connection of getConnectionsByType(map, 'state_active')){
if(!connections.includes(connection)){
removeConnectionType(connection, 'state_active');
}
}
map.setSuspendDrawing(true);
for(let connection of connections){
if(!connection.hasType('state_active')){
addConnectionType(connection, 'state_active');
}
// set all inactive
for(let connection of getConnectionsByType(map, 'state_active')){
if(!connections.includes(connection)){
removeConnectionType(connection, 'state_active');
}
});
}
for(let connection of connections){
if(!connection.hasType('state_active')){
addConnectionType(connection, 'state_active');
}
}
map.setSuspendDrawing(false, true);
};
/**
@@ -969,21 +1001,38 @@ define([
let toggleConnectionActive = (map, connections) => {
let selectedConnections = [];
let deselectedConnections = [];
map.batch(() => {
for(let connection of connections){
if(connection.hasType('state_active')){
removeConnectionType(connection, 'state_active');
deselectedConnections.push(connection);
}else{
addConnectionType(connection, 'state_active');
selectedConnections.push(connection);
}
map.setSuspendDrawing(true);
for(let connection of connections){
if(connection.hasType('state_active')){
removeConnectionType(connection, 'state_active');
deselectedConnections.push(connection);
}else{
addConnectionType(connection, 'state_active');
selectedConnections.push(connection);
}
});
}
map.setSuspendDrawing(false, true);
updateConnectionInfo(map, selectedConnections, deselectedConnections);
};
/**
* show global map info panels
* @param map
*/
let showMapInfo = map => {
// get parent Tab Content and fire update event
let mapContainer = $(map.getContainer());
getTabContentElementByMapElement(mapContainer).trigger('pf:renderGlobalModules', {
mapId: parseInt(mapContainer.data('id')),
payload: null
});
};
/**
* show system info panels
* @param map
@@ -994,10 +1043,11 @@ define([
// get parent Tab Content and fire update event
let mapContainer = $(map.getContainer());
let mapId = parseInt(mapContainer.data('id')) || 0;
getTabContentElementByMapElement(mapContainer).trigger('pf:renderSystemModules', {
mapId: parseInt(mapContainer.data('id')),
payload: Util.getCurrentSystemData()
mapId: mapId,
payload: Util.getCurrentSystemData(mapId)
});
};
@@ -1238,10 +1288,12 @@ define([
let setUniqueConnectionType = (connection, type, types) => {
type = types.includes(type) ? [type] : [];
connection._jsPlumb.instance.batch(() => {
removeConnectionTypes(connection, types.diff(type));
addConnectionTypes(connection, type);
});
connection._jsPlumb.instance.setSuspendDrawing(true);
removeConnectionTypes(connection, types.diff(type));
addConnectionTypes(connection, type);
connection._jsPlumb.instance.setSuspendDrawing(false, true);
};
/**
@@ -2074,6 +2126,40 @@ define([
return findNonOverlappingDimensions(options, maxResults, findChain);
};
/**
* calculate the x/y coordinates for a new system - relative to current map scroll offset
* @param mapContainer
* @param maxResults
* @returns {[{x: number, y: number}]}
*/
let newSystemPositionsByMapOffset = (mapContainer, maxResults = 1) => {
let scrollPosition = {
x: Math.abs(parseInt(mapContainer.attr('data-scroll-left')) || 0),
y: Math.abs(parseInt(mapContainer.attr('data-scroll-top')) || 0)
};
// space new positions from map top (e.g. used for tooltips)
scrollPosition.y = Math.max(scrollPosition.y, 30);
// default position -> current map section top/left
let positions = [scrollPosition];
// check default position for overlapping
let dimensions = newSystemPositionByCoordinates(mapContainer, {
center: [scrollPosition.x, scrollPosition.y],
minX: scrollPosition.x,
minY: scrollPosition.y
}, maxResults, true);
if(dimensions.length){
positions = dimensions.map(dim => ({
x: parseInt(dim.left) || 0,
y: parseInt(dim.top) || 0
}));
}
return positions;
};
/**
*
* @param mapContainer
@@ -2083,30 +2169,8 @@ define([
if(mapContainer){
let mapId = mapContainer.data('id');
let scrollPosition = {
x: Math.abs(parseInt(mapContainer.attr('data-scroll-left')) || 0),
y: Math.abs(parseInt(mapContainer.attr('data-scroll-top')) || 0)
};
// space new positions from map top (e.g. used for tooltips)
scrollPosition.y = Math.max(scrollPosition.y, 30);
// default position -> current map section top/left -------------------------------------------------------
positions.defaults = [scrollPosition];
// check default position for overlapping -----------------------------------------------------------------
let dimensions = newSystemPositionByCoordinates(mapContainer, {
center: [scrollPosition.x, scrollPosition.y],
minX: scrollPosition.x,
minY: scrollPosition.y
}, 2, true);
if(dimensions.length){
positions.defaults = dimensions.map(dim => ({
x: parseInt(dim.left) || 0,
y: parseInt(dim.top) || 0
}));
}
positions.defaults = newSystemPositionsByMapOffset(mapContainer, 2);
// -> calc possible coordinates for new system that should be used based on current user location ---------
let currentLocationData = Util.getCurrentLocationData();
@@ -2162,6 +2226,8 @@ define([
getInfoForSystem: getInfoForSystem,
getSystemDataFromMapData: getSystemDataFromMapData,
getSystemData: getSystemData,
getConnectionDataFromMapData: getConnectionDataFromMapData,
getConnectionData: getConnectionData,
getSystemTypeInfo: getSystemTypeInfo,
getEffectInfoForSystem: getEffectInfoForSystem,
markAsChanged: markAsChanged,
@@ -2172,6 +2238,7 @@ define([
toggleConnectionType: toggleConnectionType,
toggleConnectionActive: toggleConnectionActive,
setSystemActive: setSystemActive,
showMapInfo: showMapInfo,
showSystemInfo: showSystemInfo,
showConnectionInfo: showConnectionInfo,
showFindRouteDialog: showFindRouteDialog,
@@ -2180,6 +2247,7 @@ define([
getConnectionsByType: getConnectionsByType,
getEndpointsDataByConnection: getEndpointsDataByConnection,
getDataByConnection: getDataByConnection,
getDataByConnections: getDataByConnections,
searchConnectionsBySystems: searchConnectionsBySystems,
searchConnectionsByScopeAndType: searchConnectionsByScopeAndType,
getConnectionInfo: getConnectionInfo,
@@ -2191,7 +2259,6 @@ define([
setConnectionMassStatusType: setConnectionMassStatusType,
setConnectionJumpMassType: setConnectionJumpMassType,
getScopeInfoForConnection: getScopeInfoForConnection,
getDataByConnections: getDataByConnections,
deleteConnections: deleteConnections,
getConnectionDataFromSignatures: getConnectionDataFromSignatures,
getEndpointOverlaySignatureLocation: getEndpointOverlaySignatureLocation,
@@ -2212,6 +2279,7 @@ define([
checkRight: checkRight,
newSystemPositionBySystem: newSystemPositionBySystem,
newSystemPositionByCoordinates: newSystemPositionByCoordinates,
newSystemPositionsByMapOffset: newSystemPositionsByMapOffset,
newSystemPositionsByMap: newSystemPositionsByMap,
getMapDeeplinkUrl: getMapDeeplinkUrl
};

View File

@@ -308,15 +308,14 @@ define([
/**
* clear both main update timeouts
* clear both main update timeouts, and reset values
* -> stop program from working -> shutdown
*/
let clearUpdateTimeouts = () => {
for(let intervalKey in updateTimeouts){
if(updateTimeouts.hasOwnProperty(intervalKey)){
clearTimeout(updateTimeouts[intervalKey]);
}
}
Object.keys(updateTimeouts).forEach(intervalKey => {
clearTimeout(updateTimeouts[intervalKey]);
updateTimeouts[intervalKey] = 0;
});
};
@@ -417,20 +416,20 @@ define([
// IMPORTANT: Get user data for ONE map that is currently visible
// On later releases this can be easy changed to "full update" all maps for a user
let mapIds = [];
let mapId;
let newSystemPositions = null;
let activeMap = Util.getMapModule().getActiveMap();
if(activeMap){
mapIds = [activeMap.data('id')];
mapId = activeMap.data('id');
newSystemPositions = MapUtil.newSystemPositionsByMap(activeMap);
}
let updatedUserData = {
mapIds: mapIds,
mapIds: mapId ? [mapId] : [],
getMapUserData: Util.getSyncType() === 'webSocket' ? 0 : 1,
mapTracking: locationToggle.is(':checked') ? 1 : 0, // location tracking
systemData: Util.getCurrentSystemData()
systemData: Util.getCurrentSystemData(mapId)
};
if(newSystemPositions){

View File

@@ -13,6 +13,7 @@ define([
'module/system_route',
'module/system_intel',
'module/system_killboard',
'module/global_thera',
'module/connection_info',
'app/counter'
], (
@@ -30,6 +31,7 @@ define([
SystemRouteModule,
SystemIntelModule,
SystemKillboardModule,
TheraModule,
ConnectionInfoModule
) => {
'use strict';
@@ -56,6 +58,7 @@ define([
// editable 'settings' popover
editableSettingsClass: 'pf-editable-settings',
editableHeadlineClass: 'pf-editable-headline',
editableToggleClass: 'pf-editable-toggle',
editableToggleItemClass: 'pf-editable-toggle-item',
@@ -65,13 +68,6 @@ define([
let mapTabChangeBlocked = false; // flag for preventing map tab switch
/**
* get all maps for a maps module
* @param mapModule
* @returns {jQuery}
*/
let getMaps = mapModule => $(mapModule).find('.' + Util.config.mapClass);
/**
* get the current active mapElement
* @returns {bool|jQuery}
@@ -90,6 +86,12 @@ define([
* @param tabContentWrapperEl
*/
let setMapTabContentWrapperObserver = tabContentWrapperEl => {
$(tabContentWrapperEl).on('pf:renderGlobalModules', `.${Util.config.mapTabContentClass}`, function(e, data){
getModules()
.then(modules => filterModules(modules, 'global'))
.then(modules => renderModules(modules, e.target, data));
});
$(tabContentWrapperEl).on('pf:renderSystemModules', `.${Util.config.mapTabContentClass}`, function(e, data){
getModules()
.then(modules => filterModules(modules, 'system'))
@@ -114,6 +116,12 @@ define([
.then(modules => removeModules(modules, e.target));
});
$(tabContentWrapperEl).on('pf:updateGlobalModules', `.${Util.config.mapTabContentClass}`, (e, data) => {
getModules()
.then(modules => filterModules(modules, 'global'))
.then(modules => updateModules(modules, e.target, data));
});
$(tabContentWrapperEl).on('pf:updateSystemModules', `.${Util.config.mapTabContentClass}`, (e, data) => {
getModules()
.then(modules => filterModules(modules, true, 'fullDataUpdate'))
@@ -141,6 +149,7 @@ define([
SystemRouteModule,
SystemIntelModule,
SystemKillboardModule,
TheraModule,
ConnectionInfoModule
];
@@ -361,7 +370,7 @@ define([
});
};
removeModule(Module, gridArea, true).then(abc => render(Module, gridArea, defaultPosition, mapId, payload));
removeModule(Module, gridArea, false).then(abc => render(Module, gridArea, defaultPosition, mapId, payload));
};
return new Promise(renderModuleExecutor);
@@ -533,7 +542,7 @@ define([
let updateSystemModulesData = (mapModule, systemData) => {
if(systemData){
// check if current open system is still the requested info system
let currentSystemData = Util.getCurrentSystemData();
let currentSystemData = Util.getCurrentSystemData(systemData.mapId);
if(
currentSystemData &&
@@ -556,6 +565,7 @@ define([
let setTabContentObserver = (tabContent, mapId) => {
let defaultSortableOptions = {
invertSwap: true,
animation: Init.animationSpeed.mapModule,
handle: '.' + config.sortableHandleClass,
draggable: '.' + config.moduleClass,
@@ -926,10 +936,11 @@ define([
let mapId = parseInt(linkEl.dataset.mapId) || 0;
let defaultSystemId = parseInt(linkEl.dataset.defaultSystemId) || 0;
let tabMapData = Util.getCurrentMapData(mapId);
let tabContentEl = document.getElementById(config.mapTabIdPrefix + mapId);
if(tabMapData !== false){
// tabContentEl does not exist in case of error where all map elements got removed
if(tabMapData !== false && tabContentEl){
// load map
let tabContentEl = document.getElementById(config.mapTabIdPrefix + mapId);
let areaMap = tabContentEl.querySelector(`.${Util.getMapTabContentAreaClass('map')}`);
Map.loadMap(areaMap, tabMapData, {showAnimation: true}).then(payload => {
// "wake up" scrollbar for map and get previous state back
@@ -938,6 +949,11 @@ define([
let areaMap = mapElement.closest('.mCustomScrollbar');
$(areaMap).mCustomScrollbar('update');
// show "global" map panels of map was initial loaded
if(payload.isFirstLoad){
MapUtil.showMapInfo(mapConfig.map);
}
// if there is an already an "active" system -> setCurrentSystemData for that again
let activeSystemEl = mapElement.querySelector(`.${MapUtil.config.systemActiveClass}`);
if(activeSystemEl){
@@ -973,9 +989,6 @@ define([
// skip "add button"
if(newMapId > 0){
// delete currentSystemData -> will be set for new map (if there is is an active system)
delete Init.currentSystemData;
let currentTabContentEl = document.getElementById(config.mapTabIdPrefix + oldMapId);
// disable scrollbar for map that will be hidden. "freeze" current state
@@ -991,90 +1004,81 @@ define([
* @param options
* @returns {Promise<any>}
*/
let updateTabData = (tabLinkEl, options) => {
let updateTabData = (tabLinkEl, options) => new Promise(resolve => {
// set "main" data
tabLinkEl.dataset.mapId = options.id;
/**
* update tab promise
* @param resolve
*/
let updateTabExecutor = resolve => {
// set "main" data
tabLinkEl.dataset.mapId = options.id;
// add updated timestamp (not available for "add" tab
if(Util.getObjVal(options, 'updated.updated')){
tabLinkEl.dataset.updated = options.updated.updated;
}
// add updated timestamp (not available for "add" tab
if(Util.getObjVal(options, 'updated.updated')){
tabLinkEl.dataset.updated = options.updated.updated;
// change "tab" link
tabLinkEl.setAttribute('href', `#${config.mapTabIdPrefix}${options.id}`);
// change "map" icon
let mapIconEl = tabLinkEl.querySelector(`.${config.mapTabIconClass}`);
mapIconEl.classList.remove(...mapIconEl.classList);
mapIconEl.classList.add(config.mapTabIconClass, 'fas', 'fa-fw', options.icon);
// change "shared" icon
let mapSharedIconEl = tabLinkEl.querySelector(`.${config.mapTabSharedIconClass}`);
mapSharedIconEl.style.display = 'none';
// check if the map is a "shared" map
if(options.access){
if(
options.access.character.length > 1 ||
options.access.corporation.length > 1 ||
options.access.alliance.length > 1
){
mapSharedIconEl.style.display = 'initial';
}
}
// change "tab" link
tabLinkEl.setAttribute('href', `#${config.mapTabIdPrefix}${options.id}`);
// change map name label
let textEl = tabLinkEl.querySelector(`.${config.mapTabLinkTextClass}`);
textEl.textContent = options.name;
// change "map" icon
let mapIconEl = tabLinkEl.querySelector(`.${config.mapTabIconClass}`);
mapIconEl.classList.remove(...mapIconEl.classList);
mapIconEl.classList.add(config.mapTabIconClass, 'fas', 'fa-fw', options.icon);
// change tabClass
let listEl = tabLinkEl.parentNode;
// change "shared" icon
let mapSharedIconEl = tabLinkEl.querySelector(`.${config.mapTabSharedIconClass}`);
mapSharedIconEl.style.display = 'none';
// new tab classes
let tabClasses = [config.mapTabClass, options.type.classTab];
// check if the map is a "shared" map
if(options.access){
if(
options.access.character.length > 1 ||
options.access.corporation.length > 1 ||
options.access.alliance.length > 1
){
mapSharedIconEl.style.display = 'initial';
}
}
if(options.draggable === false){
tabClasses.push('noSort');
}
// change map name label
let textEl = tabLinkEl.querySelector(`.${config.mapTabLinkTextClass}`);
textEl.textContent = options.name;
// check if tab was "active" before
if(listEl.classList.contains('active')){
tabClasses.push('active');
}
listEl.classList.remove(...listEl.classList);
listEl.classList.add(...tabClasses);
// change tabClass
let listEl = tabLinkEl.parentNode;
// set title for tooltip
if(options.type.name !== undefined){
textEl.setAttribute('title', `${options.type.name} map`);
}
// new tab classes
let tabClasses = [config.mapTabClass, options.type.classTab];
if(options.draggable === false){
tabClasses.push('noSort');
}
// check if tab was "active" before
if(listEl.classList.contains('active')){
tabClasses.push('active');
}
listEl.classList.remove(...listEl.classList);
listEl.classList.add(...tabClasses);
// set title for tooltip
if(options.type.name !== undefined){
textEl.setAttribute('title', `${options.type.name} map`);
}
let mapTooltipOptions = {
placement: 'bottom',
container: 'body',
trigger: 'hover',
delay: 150
};
$(listEl.querySelector('[title]')).tooltip(mapTooltipOptions).tooltip('fixTitle');
resolve({
action: 'update',
data: {
mapId: options.id,
mapName: options.name
}
});
let mapTooltipOptions = {
placement: 'bottom',
container: 'body',
trigger: 'hover',
delay: 150
};
return new Promise(updateTabExecutor);
};
$(listEl.querySelector('[title]')).tooltip(mapTooltipOptions).tooltip('fixTitle');
resolve({
action: 'update',
data: {
mapId: options.id,
mapName: options.name
}
});
});
/**
* add a new tab to tab-map-module end return promise
@@ -1545,10 +1549,14 @@ define([
setMapTabLayout(tabEl, layoutCurrent);
// prepare select options for modules
let sourceOptions = modules.sort((a, b) => a.isPlugin - b.isPlugin).map(Module => ({
let modulePrioCounts = Array(BaseModule.scopeOrder.length).fill(0);
let sourceOptions = modules.sort((a, b) => a.getOrderPrio() - b.getOrderPrio()).map(Module => ({
value: Module.name,
text: `(${Module.scope.substring(0, 3)}) ${Module.label}`,
text: Module.label,
metaData: {
scope: Module.scope,
orderPrio: Module.getOrderPrio(),
prioCount: ++modulePrioCounts[Module.getOrderPrio()],
isPlugin: Module.isPlugin
}
}));
@@ -1628,6 +1636,19 @@ define([
}
}
}, {passive: false});
// add "headlines" to Modules checklist -------------------------------------------------------
anchorEl.childNodes.forEach((gridItem, i) => {
if(sourceOptions[i].metaData.prioCount === 1){
gridItem.classList.add(config.editableHeadlineClass);
gridItem.setAttribute('data-count',
modulePrioCounts[sourceOptions[i].metaData.orderPrio]
);
gridItem.setAttribute('data-headline',
BaseModule.scopeOrder[sourceOptions[i].metaData.orderPrio]
);
}
});
});
settingsLinkEl.on('save', {sourceOptions: sourceOptions}, (e, params) => {
@@ -1644,15 +1665,23 @@ define([
let showModules = filterModules(modules, params.newValue.diff(oldValue), 'name');
removeModules(hideModules, tabContentEl).then(payload => {
let showGlobalModules = showModules.filter(Module => Module.scope === 'global');
let showSystemModules = showModules.filter(Module => Module.scope === 'system');
let showConnectionModules = showModules.filter(Module => Module.scope === 'connection');
if(showGlobalModules.length){
renderModules(showGlobalModules, tabContentEl, {
mapId: activeMapId,
payload: null
});
}
if(
showSystemModules.length &&
Util.getCurrentSystemData()
Util.getCurrentSystemData(activeMapId)
){
renderModules(showSystemModules, tabContentEl, {
mapId: activeMapId,
payload: Util.getCurrentSystemData()
payload: Util.getCurrentSystemData(activeMapId)
});
}
@@ -1706,28 +1735,24 @@ define([
/**
* collect all data (systems/connections) for export/save from each active map in the map module
* if no change detected -> do not attach map data to return array
* @param mapModule
* @param {HTMLElement} mapModule
* @param filter
* @returns {Array}
* @returns {[]}
*/
let getMapModuleDataForUpdate = (mapModule, filter = ['hasId', 'hasChanged']) => {
// get all active map elements for module
let mapElements = getMaps(mapModule);
let data = [];
for(let i = 0; i < mapElements.length; i++){
[...mapModule.getElementsByClassName(Util.config.mapClass)].forEach(mapElement => {
// get all changed (system / connection) data from this map
let mapData = Map.getMapDataForSync($(mapElements[i]), filter);
if(mapData !== false){
if(
mapData.data.systems.length > 0 ||
mapData.data.connections.length > 0
){
data.push(mapData);
}
let mapData = Map.getMapDataForSync(mapElement, filter);
if(
mapData && (
(Util.getObjVal(mapData, 'data.systems') || []).length ||
(Util.getObjVal(mapData, 'data.connections') || []).length
)
){
data.push(mapData);
}
}
});
return data;
};

View File

@@ -1293,47 +1293,35 @@ define([
let mapUserUpdateKey = 'UPDATE_SERVER_USER_DATA';
// Set the name of the hidden property and the change event for visibility
let hidden, visibilityChange;
if(typeof document.hidden !== 'undefined'){ // Opera 12.10 and Firefox 18 and later support
hidden = 'hidden';
let visibilityState, visibilityChange;
if(typeof document.visibilityState !== 'undefined'){ // Opera 12.10 and Firefox 18 and later support
visibilityState = 'visibilityState';
visibilityChange = 'visibilitychange';
}else if(typeof document.mozHidden !== 'undefined'){
hidden = 'mozHidden';
visibilityChange = 'mozvisibilitychange';
}else if(typeof document.msHidden !== 'undefined'){
hidden = 'msHidden';
visibilityChange = 'msvisibilitychange';
}else if(typeof document.webkitHidden !== 'undefined'){
hidden = 'webkitHidden';
visibilityChange = 'webkitvisibilitychange';
}
// function is called if the tab becomes active/inactive
let handleVisibilityChange = () => {
if(document[hidden]){
// tab is invisible
// globally store current visibility status
window.isVisible = false;
Util.getCurrentTriggerDelay( mapUpdateKey, increaseTimer );
Util.getCurrentTriggerDelay( mapUserUpdateKey, increaseTimer );
}else{
if(document[visibilityState] === 'visible'){
// tab is visible
// globally store current visibility status
window.isVisible = true;
Util.getCurrentTriggerDelay( mapUpdateKey, -increaseTimer );
Util.getCurrentTriggerDelay( mapUserUpdateKey, -increaseTimer );
Util.getCurrentTriggerDelay(mapUpdateKey, -increaseTimer);
Util.getCurrentTriggerDelay(mapUserUpdateKey, -increaseTimer);
// stop blinking tab from previous notifications
Util.stopTabBlink();
}else{
// tab is invisible
// globally store current visibility status
window.isVisible = false;
Util.getCurrentTriggerDelay(mapUpdateKey, increaseTimer);
Util.getCurrentTriggerDelay(mapUserUpdateKey, increaseTimer);
}
};
if(
typeof document.addEventListener !== 'undefined' &&
typeof document[hidden] !== 'undefined'
){
if(visibilityState && visibilityChange){
// the current browser supports this feature
// Handle page visibility change

View File

@@ -1,7 +1,8 @@
define([
'PNotify',
'PNotifyDesktop',
'PNotifyButtons',
'PNotifyCallbacks',
'PNotifyDesktop',
'NonBlock'
], (PNotify) => {
'use strict';
@@ -30,6 +31,9 @@ define([
}
};
/**
* default PNotify config
*/
let initDefaultPNotifyConfig = () => {
PNotify.defaults.styling = 'bootstrap3';
PNotify.defaults.icons = 'fontawesome5';
@@ -54,7 +58,6 @@ define([
config.modules = {
Desktop: Object.assign({}, {desktop: true}, options.desktop)
};
startTabBlink(config.title); // make browser tab blink
}
switch(config.type){
@@ -99,60 +102,7 @@ define([
initDefaultPNotifyConfig();
// browser tab blink ==============================================================================================
// initial page title (cached)
let initialPageTitle = document.title;
// global blink timeout cache
let blinkTimer;
/**
* change document.title and make the browsers tab blink
* @param blinkTitle
*/
let startTabBlink = blinkTitle => {
let initBlink = (function(){
// count blinks if tab is currently active
let activeTabBlinkCount = 0;
let blink = (blinkTitle) => {
// number of "blinks" should be limited if tab is currently active
if(window.isVisible){
activeTabBlinkCount++;
}
// toggle page title
document.title = (document.title === blinkTitle) ? initialPageTitle : blinkTitle;
if(activeTabBlinkCount > 10){
stopTabBlink();
}
};
return () => {
if(!blinkTimer){
blinkTimer = setInterval(blink, 1000, blinkTitle);
}
};
}(blinkTitle));
initBlink();
};
/**
* stop blinking document.title
*/
let stopTabBlink = () => {
if(blinkTimer){
clearInterval(blinkTimer);
document.title = initialPageTitle;
blinkTimer = null;
}
};
return {
showNotify: showNotify,
startTabBlink: startTabBlink,
stopTabBlink: stopTabBlink
showNotify: showNotify
};
});

View File

@@ -212,7 +212,7 @@ define([
let systemsElement = $(this);
let systemTable = $('<table>', {
id: Util.getTableId(config.tableId, 'systems', mapData.config.id, ''),
id: Util.getTableId(config.tableId, 'systems', mapData.config.id),
class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ')
});
systemsElement.append(systemTable);
@@ -469,7 +469,7 @@ define([
let confirmationSettings = {
placement: 'left',
title: 'Delete system',
title: '---',
template: Util.getConfirmationTemplate(null, {
size: 'small',
noTitle: true
@@ -535,7 +535,7 @@ define([
let connectionsElement = $(this);
let connectionTable = $('<table>', {
id: Util.getTableId(config.tableId, 'connections', mapData.config.id, ''),
id: Util.getTableId(config.tableId, 'connections', mapData.config.id),
class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ')
});
connectionsElement.append(connectionTable);
@@ -678,7 +678,7 @@ define([
let confirmationSettings = {
placement: 'left',
title: 'Delete connection',
title: '---',
template: Util.getConfirmationTemplate(null, {
size: 'small',
noTitle: true
@@ -686,9 +686,7 @@ define([
onConfirm: function(e, target){
let deleteRowElement = $(target).parents('tr');
// deleteSignatures(row);
let connection = $().getConnectionById(mapData.config.id, rowData.id);
MapUtil.deleteConnections([connection], () => {
// callback function after ajax "delete" success
// remove table row

View File

@@ -90,8 +90,7 @@ define([
let name = parts[0];
let sizeLabel;
if(Util.getObjVal(customOptions, 'showWhSizeLabel')){
let wormholeSizeData = Util.getObjVal(Init, 'wormholes.' + name + '.size');
sizeLabel = Util.getObjVal(wormholeSizeData, 'label') || '';
sizeLabel = Util.getObjVal(Init, `wormholes.${name}.size.label`) || '';
}
let securityClass = Util.getSecurityClassForSystem(getSystemSecurityFromLabel(parts[1]));
@@ -102,19 +101,17 @@ define([
let classes = [securityClass, Util.config.popoverTriggerClass, Util.config.helpDefaultClass];
markup += '<span>' + name + '</span>';
markup += `<span>${name}</span>`;
if(sizeLabel !== undefined){
markup += '<span><kbd>' + sizeLabel + '</kbd></span>';
}else{
markup += '&nbsp;&nbsp;';
markup += `<span><kbd>${sizeLabel}</kbd></span>`;
}
markup += '<i class="fas fa-long-arrow-alt-right txt-color txt-color-grayLight"></i>';
markup += '<span class="' + classes.join(' ') + '" data-name="' + name + '">&nbsp;&nbsp;' + label + '</span>';
markup += `<span class="${classes.join(' ')}" data-name="${name}">&nbsp;&nbsp;${label}</span>`;
if(suffix.length){
markup += '&nbsp;<span>' + suffix + '</span>';
markup += `&nbsp;<span>${suffix}</span>`;
}
}else{
markup += '<span>' + state.text + '</span>';
markup += `<span>${state.text}</span>`;
}
return $(markup);
@@ -203,10 +200,10 @@ define([
}
let securityClass = Util.getSecurityClassForSystem(parts[1]);
markup += '<span class="' + styleClass.join(' ') + '">' + parts[0] + '</span>&nbsp;&nbsp;';
markup += '<span class="' + securityClass + '">' + parts[1] + '</span>';
markup += `<span class="${styleClass.join(' ')}">${parts[0]}</span>`;
markup += `<span class="${securityClass}">&nbsp;&nbsp;${parts[1]}</span>`;
}else{
markup += '<span>' + state.text + '</span>';
markup += `<span>${state.text}</span>`;
}
return $(markup);

View File

@@ -2,9 +2,11 @@ define([
'jquery',
'app/init',
'app/util',
'app/map/util',
'app/lib/cache',
'app/promises/promise.deferred',
'app/promises/promise.queue'
], ($, Init, Util, DeferredPromise, PromiseQueue) => {
], ($, Init, Util, MapUtil, Cache, DeferredPromise, PromiseQueue) => {
'use strict';
/**
@@ -127,6 +129,34 @@ define([
});
}
/**
* label element
* @param text
* @param cls
* @returns {HTMLSpanElement}
*/
newLabelElement(text, cls = []){
let labelEl = document.createElement('span');
labelEl.classList.add('label', 'center-block', ...cls);
labelEl.textContent = text || '';
return labelEl;
}
/**
* control button element
* @param text
* @param cls
* @param iconCls
* @returns {HTMLDivElement}
*/
newControlElement(text, cls = [], iconCls = ['fa-sync']){
let controlEl = document.createElement('div');
controlEl.classList.add(...[BaseModule.Util.config.dynamicAreaClass, this._config.controlAreaClass, ...cls]);
controlEl.insertAdjacentHTML('beforeend', `&nbsp;&nbsp;${text}`);
controlEl.prepend(this.newIconElement(iconCls));
return controlEl;
}
/**
* HTTP request handler for internal (Pathfinder) ajax calls
* @param args
@@ -245,6 +275,188 @@ define([
}
}
/**
* get a unique cache key name for "source"/"target"-name
* @param sourceName
* @param targetName
* @returns {string|boolean}
*/
static getConnectionDataCacheKey(sourceName, targetName){
let key = false;
if(sourceName && targetName){
// names can be "undefined" in case system is currently in drag/drop state
// sort() is important -> ignore direction
key = `con_` + `${ [String(sourceName).toLowerCase(), String(targetName).toLowerCase()].sort() }`.hashCode();
}
return key;
}
/**
* get a connectionsData object that holds all connections for given mapIds (used as cache for route search)
* @param mapIds
* @returns {{}}
*/
static getConnectionsDataFromMaps(mapIds){
let data = {};
for(let mapId of mapIds){
let map = MapUtil.getMapInstance(mapId);
if(map){
let cacheKey = `map_${mapId}`;
let cache = BaseModule.getCache('mapConnections');
let mapConnectionsData = cache.get(cacheKey);
if(!mapConnectionsData){
mapConnectionsData = this.getConnectionsDataFromConnections(mapId, map.getAllConnections());
// update cache
cache.set(cacheKey, mapConnectionsData);
}
Object.assign(data, mapConnectionsData);
}
}
return data;
}
/**
* get a connectionsData object for all connections
* @param mapId
* @param connections
* @returns {{}}
*/
static getConnectionsDataFromConnections(mapId = 0, connections = []){
let data = {};
if(connections.length){
let connectionsData = MapUtil.getDataByConnections(connections);
for(let connectionData of connectionsData){
let connectionDataCacheKey = BaseModule.getConnectionDataCacheKey(connectionData.sourceName, connectionData.targetName);
// skip double connections between same systems
if(connectionDataCacheKey && !Object.keys(data).includes(connectionDataCacheKey)){
data[connectionDataCacheKey] = {
map: {
id: mapId
},
connection: {
id: connectionData.id,
type: connectionData.type,
scope: connectionData.scope,
updated: connectionData.updated
},
source: {
id: connectionData.source,
name: connectionData.sourceName,
alias: connectionData.sourceAlias
},
target: {
id: connectionData.target,
name: connectionData.targetName,
alias: connectionData.targetAlias
}
};
}
}
}
return data;
}
/**
* search for a specific connection by "source"/"target"-name inside connectionsData cache
* @param connectionsData
* @param sourceName
* @param targetName
* @returns {*}
*/
static findConnectionsData(connectionsData, sourceName, targetName){
return this.Util.getObjVal(connectionsData, this.getConnectionDataCacheKey(sourceName, targetName));
}
/**
* get fake connection data (default connection type in case connection was not found on a map)
* @param sourceSystemData
* @param targetSystemData
* @param scope
* @param types
* @returns {{connection: {scope: string, id: number, type: [*]}, source: {name: number, alias: number, id: number}, target: {name: number, alias: number, id: number}}}
*/
static getFakeConnectionData(sourceSystemData, targetSystemData, scope = 'stargate', types = []){
return {
connection: {
id: 0,
scope: scope,
type: types.length ? types : [MapUtil.getDefaultConnectionTypeByScope(scope)],
updated: 0
},
source: {
id: 0,
name: sourceSystemData.system,
alias: sourceSystemData.system
},
target: {
id: 0,
name: targetSystemData.system,
alias: targetSystemData.system
}
};
}
/**
* get fake connection Element
* @param connectionData
* @returns {string}
*/
static getFakeConnectionElement(connectionData){
let mapId = this.Util.getObjVal(connectionData, 'map.id') || 0;
let connectionId = this.Util.getObjVal(connectionData, 'connection.id') || 0;
let scope = this.Util.getObjVal(connectionData, 'connection.scope') || '';
let classes = MapUtil.getConnectionFakeClassesByTypes(this.Util.getObjVal(connectionData, 'connection.type') || []);
let disabled = !mapId || !connectionId;
let connectionElement = '<div data-mapId="' + mapId + '" data-connectionId="' + connectionId + '" ';
connectionElement += (disabled ? 'data-disabled' : '');
connectionElement += ' class="' + classes.join(' ') + '" ';
connectionElement += ' title="' + scope + '" data-placement="bottom"></div>';
return connectionElement;
}
/**
* get static instance of in-memory Cache() store by 'name'
* -> not persistent across page reloads
* -> persistent across module instances (different and same maps)
* @param name
* @returns {Cache}
*/
static getCache(name){
let key = `CACHE-${name}`;
if(!this.Util.getObjVal(this, key)){
let configKey = `cacheConfig.${name}`;
let cacheConfig = this.Util.getObjVal(this, configKey);
if(!cacheConfig){
console.warn('Missing Cache config for %o. Expected at %o. Default config loaded…',
name, `${this.name}.${configKey}`
);
cacheConfig = {};
}else{
// set cache name
cacheConfig.name = name;
}
this[key] = new Cache(cacheConfig);
}
return this[key];
}
static now(){
return new Date().getTime() / 1000;
}
static getOrderPrio(){
return this.isPlugin ?
this.scopeOrder.indexOf('plugin') :
(this.scopeOrder.indexOf(this.scope) !== -1 ?
this.scopeOrder.indexOf(this.scope) :
this.scopeOrder.length - 1
);
}
static newDeferredPromise(){
return new DeferredPromise();
}
@@ -259,6 +471,14 @@ define([
BaseModule.fullDataUpdate = false; // static module requires additional data (e.g. system description,...)
BaseModule.Util = Util; // static access to Pathfinders Util object
BaseModule.scopeOrder = [
'system',
'connection',
'global',
'plugin',
'undefined'
];
BaseModule.handler = [
'render',
'init',
@@ -268,6 +488,14 @@ define([
'onSortableEvent'
];
BaseModule.cacheConfig = {
mapConnections: {
ttl: 5,
maxSize: 600,
debug: false
}
};
BaseModule.defaultConfig = {
position: 1,
className: 'pf-base-module', // class for module
@@ -276,6 +504,7 @@ define([
sortTargetAreas: ['a', 'b', 'c'], // sortable areas where module can be dragged into
headline: 'Base headline', // module headline
bodyClassName: 'pf-module-body', // class for module body [optional: can be used]
controlAreaClass: 'pf-module-control-area', // class for "control" areas
moduleHeadlineIconClass: 'pf-module-icon-button' // class for toolbar icons in the head
};

View File

@@ -78,11 +78,9 @@ define([
*/
newInfoPanelControlEl(mapId){
let connectionEl = this.newConnectionElement(mapId);
let controlEl = document.createElement('div');
controlEl.classList.add(Util.config.dynamicAreaClass, this._config.controlAreaClass);
controlEl.insertAdjacentHTML('beforeend', '<i class="fas fa-fw fa-plus"></i>&nbsp;add connection&nbsp;&nbsp;<kbd>ctrl</kbd>&nbsp;+&nbsp;<kbd>click</kbd>');
connectionEl.append(controlEl);
connectionEl.append(
this.newControlElement('add connection&nbsp;&nbsp;<kbd>ctrl</kbd>&nbsp;+&nbsp;<kbd>click</kbd>', [], ['fa-plus'])
);
return connectionEl;
}
@@ -267,9 +265,12 @@ define([
}, context => {
// hide loading animation
for(let contextData of context.connectionsData){
let tableEls = this.moduleElement.querySelector('#' + this.getConnectionElementId(contextData.id))
.getElementsByTagName('table');
$(tableEls).hideLoadingAnimation();
let connectionEl = this.moduleElement.querySelector('#' + this.getConnectionElementId(contextData.id));
// connectionEl might be removed in meantime ( e.g. module removed)
if(connectionEl){
let tableEls = connectionEl.getElementsByTagName('table');
$(tableEls).hideLoadingAnimation();
}
}
});
}
@@ -351,7 +352,7 @@ define([
let scopeLabel = MapUtil.getScopeInfoForConnection(connectionData.scope, 'label');
let element = document.createElement('div');
element.classList.add(Util.config.dynamicAreaClass, this._config.controlAreaClass);
element.classList.add(BaseModule.Util.config.dynamicAreaClass, this._config.controlAreaClass);
$(element).append(
$('<table>', {
@@ -990,7 +991,7 @@ define([
if(rowData.active){
let confirmationSettings = {
title: 'delete jump log',
title: '---',
template: Util.getConfirmationTemplate(null, {
size: 'small',
noTitle: true
@@ -1251,7 +1252,6 @@ define([
// body
connectionInfoPanelClass: 'pf-connection-info-panel', // class for connection info panels
controlAreaClass: 'pf-module-control-area', // class for "control" areas
// info table
moduleTableClass: 'pf-module-table', // class for module tables

View File

@@ -249,7 +249,7 @@ define([ // dependencies for this module
}
};
DemoModule.isPlugin = true; // module is defined as 'plugin'
DemoModule.isPlugin = true; // module is defined as 'plugin'
DemoModule.scope = 'system'; // module scope controls how module gets updated and what type of data is injected
DemoModule.sortArea = 'a'; // default sortable area
DemoModule.position = 10; // default sort/order position within sortable area

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More