Merge pull request #610 from exodus4d/develop

v1.3.4
This commit is contained in:
Mark Friedrich
2018-04-12 21:07:21 +02:00
committed by GitHub
127 changed files with 2217 additions and 1181 deletions

View File

@@ -13,96 +13,96 @@
* | | < | <| -__|-- __|
* |__|__|__||__|__|_____|_____|
*
* Copyright (c) 2016 by ikkez
* Copyright (c) 2012-2018 by ikkez
* Christian Knuth <ikkez0n3@gmail.com>
* https://github.com/ikkez/F3-Sugar/
*
* @package DB
* @version 2.2.1
* @date 25.04.2017
* @version 2.2.2
* @date 06.03.2018
**/
namespace DB\SQL;
use DB\SQL;
class Schema {
class Schema extends DB_Utils {
use DB_Utils;
public
$dataTypes = array(
'BOOLEAN' => array('mysql' => 'tinyint(1)',
'sqlite2?|pgsql' => 'BOOLEAN',
'mssql|sybase|dblib|odbc|sqlsrv' => 'bit',
'ibm' => 'numeric(1,0)',
'sqlite2?|pgsql' => 'BOOLEAN',
'mssql|sybase|dblib|odbc|sqlsrv' => 'bit',
'ibm' => 'numeric(1,0)',
),
'INT1' => array('mysql' => 'tinyint(4)',
'sqlite2?' => 'integer(4)',
'mssql|sybase|dblib|odbc|sqlsrv' => 'tinyint',
'pgsql|ibm' => 'smallint',
'sqlite2?' => 'integer(4)',
'mssql|sybase|dblib|odbc|sqlsrv' => 'tinyint',
'pgsql|ibm' => 'smallint',
),
'INT2' => array('mysql' => 'smallint(6)',
'sqlite2?' => 'integer(6)',
'pgsql|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'smallint',
'sqlite2?' => 'integer(6)',
'pgsql|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'smallint',
),
'INT4' => array('sqlite2?' => 'integer(11)',
'pgsql|imb' => 'integer',
'mysql' => 'int(11)',
'mssql|dblib|sybase|odbc|sqlsrv' => 'int',
'pgsql|imb' => 'integer',
'mysql' => 'int(11)',
'mssql|dblib|sybase|odbc|sqlsrv' => 'int',
),
'INT8' => array('sqlite2?' => 'integer(20)',
'pgsql|mssql|sybase|dblib|odbc|sqlsrv|imb' => 'bigint',
'mysql' => 'bigint(20)',
'pgsql|mssql|sybase|dblib|odbc|sqlsrv|imb' => 'bigint',
'mysql' => 'bigint(20)',
),
'FLOAT' => array('mysql|sqlite2?' => 'FLOAT',
'pgsql' => 'double precision',
'mssql|sybase|dblib|odbc|sqlsrv' => 'float',
'imb' => 'decfloat'
'pgsql' => 'double precision',
'mssql|sybase|dblib|odbc|sqlsrv' => 'float',
'imb' => 'decfloat'
),
'DOUBLE' => array('mysql|ibm' => 'decimal(18,6)',
'sqlite2?' => 'decimal(15,6)', // max 15-digit on sqlite
'pgsql' => 'numeric(18,6)',
'mssql|dblib|sybase|odbc|sqlsrv' => 'decimal(18,6)',
'sqlite2?' => 'decimal(15,6)', // max 15-digit on sqlite
'pgsql' => 'numeric(18,6)',
'mssql|dblib|sybase|odbc|sqlsrv' => 'decimal(18,6)',
),
'VARCHAR128' => array('mysql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(128)',
'pgsql' => 'character varying(128)',
'pgsql' => 'character varying(128)',
),
'VARCHAR256' => array('mysql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(255)',
'pgsql' => 'character varying(255)',
'pgsql' => 'character varying(255)',
),
'VARCHAR512' => array('mysql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(512)',
'pgsql' => 'character varying(512)',
'pgsql' => 'character varying(512)',
),
'TEXT' => array('mysql|sqlite2?|pgsql|mssql' => 'text',
'sybase|dblib|odbc|sqlsrv' => 'nvarchar(max)',
'ibm' => 'BLOB SUB_TYPE TEXT',
'sybase|dblib|odbc|sqlsrv' => 'nvarchar(max)',
'ibm' => 'BLOB SUB_TYPE TEXT',
),
'LONGTEXT' => array('mysql' => 'LONGTEXT',
'sqlite2?|pgsql|mssql' => 'text',
'sybase|dblib|odbc|sqlsrv' => 'nvarchar(max)',
'ibm' => 'CLOB(2000000000)',
'sqlite2?|pgsql|mssql' => 'text',
'sybase|dblib|odbc|sqlsrv' => 'nvarchar(max)',
'ibm' => 'CLOB(2000000000)',
),
'DATE' => array('mysql|sqlite2?|pgsql|mssql|sybase|dblib|odbc|sqlsrv|ibm' => 'date',
),
'DATETIME' => array('pgsql' => 'timestamp without time zone',
'mysql|sqlite2?|mssql|sybase|dblib|odbc|sqlsrv' => 'datetime',
'ibm' => 'timestamp',
'mysql|sqlite2?|mssql|sybase|dblib|odbc|sqlsrv' => 'datetime',
'ibm' => 'timestamp',
),
'TIMESTAMP' => array('mysql|ibm' => 'timestamp',
'pgsql|odbc' => 'timestamp without time zone',
'sqlite2?|mssql|sybase|dblib|sqlsrv'=>'DATETIME',
'pgsql|odbc' => 'timestamp without time zone',
'sqlite2?|mssql|sybase|dblib|sqlsrv'=>'DATETIME',
),
'BLOB' => array('mysql|odbc|sqlite2?|ibm' => 'blob',
'pgsql' => 'bytea',
'mssql|sybase|dblib' => 'image',
'sqlsrv' => 'varbinary(max)',
'pgsql' => 'bytea',
'mssql|sybase|dblib' => 'image',
'sqlsrv' => 'varbinary(max)',
),
),
$defaultTypes = array(
'CUR_STAMP' => array('mysql' => 'CURRENT_TIMESTAMP',
'mssql|sybase|dblib|odbc|sqlsrv' => 'getdate()',
'pgsql' => 'LOCALTIMESTAMP(0)',
'sqlite2?' => "(datetime('now','localtime'))",
'mssql|sybase|dblib|odbc|sqlsrv' => 'getdate()',
'pgsql' => 'LOCALTIMESTAMP(0)',
'sqlite2?' => "(datetime('now','localtime'))",
),
);
@@ -112,9 +112,6 @@ class Schema extends DB_Utils {
public static
$strict = FALSE;
/** @var \Base */
protected $fw;
const
// DataTypes and Aliases
DT_BOOL = 'BOOLEAN',
@@ -147,8 +144,7 @@ class Schema extends DB_Utils {
public function __construct(\DB\SQL $db)
{
$this->fw = \Base::instance();
parent::__construct($db);
$this->db = $db;
}
/**
@@ -304,7 +300,9 @@ class Schema extends DB_Utils {
}
}
abstract class TableBuilder extends DB_Utils {
abstract class TableBuilder {
use DB_Utils;
protected $columns, $pkeys, $queries, $increments, $rebuild_cmd, $suppress;
public $name;
@@ -318,7 +316,6 @@ abstract class TableBuilder extends DB_Utils {
/**
* @param string $name
* @param Schema $schema
* @return \DB\SQL\TableBuilder
*/
public function __construct($name, Schema $schema)
{
@@ -328,7 +325,7 @@ abstract class TableBuilder extends DB_Utils {
$this->queries = array();
$this->pkeys = array('id');
$this->increments = 'id';
parent::__construct($schema->db);
$this->db = $schema->db;
}
/**
@@ -549,6 +546,8 @@ class TableModifier extends TableBuilder {
/**
* generate SQL queries for altering the table and execute it if $exec is true,
* otherwise return the generated query string
* @param bool $exec
* @return array|FALSE
*/
public function build($exec = TRUE)
{
@@ -763,9 +762,9 @@ class TableModifier extends TableBuilder {
else
foreach ($schema as $name => &$cols) {
$default = ($cols['default'] === '') ? null : $cols['default'];
if (!is_null($default) && (
(is_int(strpos($curdef=$this->findQuery($this->schema->defaultTypes['CUR_STAMP']),
$default)) || is_int(strpos($default,$curdef)))
if (!is_null($default) && ((is_int(strpos($curdef=strtolower(
$this->findQuery($this->schema->defaultTypes['CUR_STAMP'])),
strtolower($default))) || is_int(strpos(strtolower($default),$curdef)))
|| $default == "('now'::text)::timestamp(0) without time zone"))
{
$default = 'CUR_STAMP';
@@ -874,7 +873,6 @@ class TableModifier extends TableBuilder {
* @param string $name
* @param string|Column $datatype
* @param bool $force
* @return bool
*/
public function updateColumn($name, $datatype, $force = false)
{
@@ -1031,7 +1029,9 @@ class TableModifier extends TableBuilder {
* Class Column
* @package DB\SQL
*/
class Column extends DB_Utils {
class Column {
use DB_Utils;
public $name, $type, $nullable, $default, $after, $index, $unique, $passThrough, $pkey;
protected $table, $schema, $type_val;
@@ -1056,7 +1056,7 @@ class Column extends DB_Utils {
$this->table = $table;
$this->schema = $table->schema;
parent::__construct($this->schema->db);
$this->db = $this->schema->db;
}
/**
@@ -1281,22 +1281,16 @@ class Column extends DB_Utils {
constant('\PDO::PARAM_'.strtoupper($parts[0])) : \PDO::PARAM_STR;
return ($this->default === NULL ? 'NULL' :
$this->db->quote(htmlspecialchars($this->default, ENT_QUOTES,
$this->f3->get('ENCODING')), $pdo_type));
\Base::instance()->get('ENCODING')), $pdo_type));
}
}
}
class DB_Utils {
trait DB_Utils {
/** @var \DB\SQL */
protected $db;
/** @var \BASE */
protected $f3;
const
TEXT_ENGINE_NOT_SUPPORTED = 'DB Engine `%s` is not supported for this action.';
public $db;
/**
* parse command array and return backend specific query
@@ -1308,11 +1302,6 @@ class DB_Utils {
foreach ($cmd as $backend => $val)
if (preg_match('/'.$backend.'/', $this->db->driver()))
return $val;
trigger_error(sprintf(self::TEXT_ENGINE_NOT_SUPPORTED, $this->db->driver()),E_USER_ERROR);
}
public function __construct(SQL $db) {
$this->db = $db;
$this->f3 = \Base::instance();
trigger_error(sprintf('DB Engine `%s` is not supported for this action.', $this->db->driver()),E_USER_ERROR);
}
}

View File

@@ -10,9 +10,9 @@ namespace Controller;
use Controller\Ccp\Sso;
use lib\Config;
use Model\CharacterModel;
use Model\CorporationModel;
use lib\Config;
use Model\MapModel;
use Model\RoleModel;
@@ -421,6 +421,13 @@ class Admin extends Controller{
}
$f3->set('tplMaps', $data);
if( !isset($data->corpMaps) ){
$f3->set('tplNotification', $this->getNotificationObject('No maps found',
'Only corporation maps could get loaded' ,
'info'
));
}
}
/**

View File

@@ -56,24 +56,12 @@ class Connection extends Controller\AccessController {
$connection = Model\BasicModel::getNew('ConnectionModel');
$connection->getById( (int)$connectionData['id'] );
// search if systems are neighbors
$routeController = new Route();
$routeController->initJumpData();
$route = $routeController->findRoute($connectionData['sourceName'], $connectionData['targetName'], 1);
if($route['routePossible'] == true){
// systems are next to each other
$connectionData['scope'] = 'stargate';
$connectionData['type'] = ['stargate'];
}elseif($connectionData['scope'] == 'stargate'){
// connection scope changed -> this can not be a stargate
$connectionData['scope'] = 'wh';
$connectionData['type'] = ['wh_fresh'];
}
$connectionData['mapId'] = $map;
$connection->setData($connectionData);
// change the default type for the new connection
$connection->setDefaultTypeData();
if($connection->save($activeCharacter)){
$return->connectionData = $connection->getData();

View File

@@ -24,39 +24,17 @@ class Map extends Controller\AccessController {
// cache keys
const CACHE_KEY_INIT = 'CACHED_INIT';
const CACHE_KEY_MAP_DATA = 'CACHED.MAP_DATA.%s';
const CACHE_KEY_USER_DATA = 'CACHED.USER_DATA.%s_%s';
const CACHE_KEY_USER_DATA = 'CACHED.USER_DATA.%s';
const CACHE_KEY_HISTORY = 'CACHED_MAP_HISTORY_%s';
/**
* get map data cache key
* @param Model\CharacterModel $character
* @return string
*/
protected function getMapDataCacheKey(Model\CharacterModel $character): string {
return sprintf(self::CACHE_KEY_MAP_DATA, 'CHAR_' . $character->_id);
}
/**
* clear map data caching
* -> cache timings are short, this will just increase update performance on map changes
* @param Model\CharacterModel $character
*/
protected function clearMapDataCache(Model\CharacterModel $character){
$cacheKey = $this->getMapDataCacheKey($character);
if($this->getF3()->exists($cacheKey)){
$this->getF3()->clear($cacheKey);
}
}
/**
* get user data cache key
* @param int $mapId
* @param int $systemId
* @return string
*/
protected function getUserDataCacheKey($mapId, $systemId = 0): string {
return sprintf(self::CACHE_KEY_USER_DATA, 'MAP_' . $mapId, 'SYS_' . $systemId);
protected function getUserDataCacheKey(int $mapId): string {
return sprintf(self::CACHE_KEY_USER_DATA, 'MAP_' . $mapId);
}
/**
@@ -74,7 +52,7 @@ class Map extends Controller\AccessController {
* @throws Exception
* @throws Exception\PathfinderException
*/
public function init(\Base $f3){
public function initData(\Base $f3){
// expire time in seconds
$expireTimeCache = 60 * 60;
$expireTimeSQL = 60 * 60 * 12;
@@ -652,11 +630,18 @@ class Map extends Controller\AccessController {
$return = (object) [];
$activeCharacter = $this->getCharacter();
$characterData = $activeCharacter->getData(true);
$maps = $activeCharacter->getMaps();
// some character data is not required (in WebSocket) -> unset() and keep return small
if(isset($characterData->corporation->rights)){
unset($characterData->corporation->rights);
}
$return->data = [
'id' => $activeCharacter->_id,
'token' => bin2hex(random_bytes(16)), // token for character access
'characterData' => $characterData,
'mapData' => []
];
@@ -684,23 +669,20 @@ class Map extends Controller\AccessController {
* @throws Exception\PathfinderException
*/
public function updateData(\Base $f3){
$mapData = (array)$f3->get('POST.mapData');
$userDataRequired = (bool)$f3->get('POST.getUserData');
$postData = (array)$f3->get('POST');
$mapData = (array)$postData['mapData'];
$userDataRequired = (bool)$postData['getUserData'];
$return = (object) [];
$return->error = [];
$activeCharacter = $this->getCharacter();
$cacheKey = $this->getMapDataCacheKey($activeCharacter);
// get current map data
$maps = $activeCharacter->getMaps();
// if there is any system/connection change data submitted -> save new data
if(
!empty($mapData) ||
!$f3->exists($cacheKey, $return)
){
$return = (object) [];
$return->error = [];
// get current map data ===================================================================================
$maps = $activeCharacter->getMaps();
if( !empty($maps) && !empty($mapData) ){
// loop all submitted map data that should be saved
// -> currently there will only be ONE map data change submitted -> single loop
@@ -807,20 +789,14 @@ class Map extends Controller\AccessController {
}
}
}
// format map Data for return
$return->mapData = $this->getFormattedMapsData($maps);
// cache time(s) per user should be equal or less than this function is called
// prevent request flooding
$responseTTL = (int)Config::getPathfinderData('timer.update_server_map.delay') / 1000;
$f3->set($cacheKey, $return, $responseTTL);
}
// format map Data for return
$return->mapData = $this->getFormattedMapsData($maps);
// if userData is requested -> add it as well
// -> Only first trigger call should request this data!
if($userDataRequired){
if($userDataRequired) {
$return->userData = $activeCharacter->getUser()->getData();
}
@@ -851,61 +827,51 @@ class Map extends Controller\AccessController {
* @throws Exception\PathfinderException
*/
public function updateUserData(\Base $f3){
$return = (object) [];
$postData = (array)$f3->get('POST');
$mapIds = (array)$postData['mapIds'];
$getMapUserData = (bool)$postData['getMapUserData'];
$mapTracking = (bool)$postData['mapTracking'];
$systemData = (array)$postData['systemData'];
$activeCharacter = $this->getCharacter(0);
$postData = $f3->get('POST');
if( !empty($mapIds = (array)$postData['mapIds']) ){
$return = (object)[];
// update current location
// -> suppress temporary timeout errors
$activeCharacter = $activeCharacter->updateLog(['suppressHTTPErrors' => true]);
if( !empty($mapIds) ){
// IMPORTANT for now -> just update a single map (save performance)
$mapId = (int)reset($mapIds);
// get map and check map access
$map = $activeCharacter->getMap( (int)$mapId);
if( !is_null($map) ){
$characterMapData = (array)$postData['characterMapData'];
// check if data for specific system is requested
$systemData = (array)$postData['systemData'];
// if data is requested extend the cache key in order to get new data
$requestSystemData = (object) [];
$requestSystemData->mapId = isset($systemData['mapId']) ? (int) $systemData['mapId'] : 0;
$requestSystemData->systemId = isset($systemData['systemData']['id']) ? (int) $systemData['systemData']['id'] : 0;
// update current location
// -> suppress temporary timeout errors
$activeCharacter = $activeCharacter->updateLog(['suppressHTTPErrors' => true]);
if( !is_null($map = $activeCharacter->getMap($mapId)) ){
// check character log (current system) and manipulate map (e.g. add new system)
if( (bool)$characterMapData['mapTracking'] ){
if($mapTracking){
$map = $this->updateMapData($activeCharacter, $map);
}
// get from cache
// this should happen if a user has multiple program instances running
// with the same main char
$cacheKey = $this->getUserDataCacheKey($mapId, $requestSystemData->systemId);
if( !$f3->exists($cacheKey, $return) ){
$return = (object) [];
$return->mapUserData[] = $map->getUserData();
// mapUserData ----------------------------------------------------------------------------------------
if($getMapUserData){
$cacheKey = $this->getUserDataCacheKey($mapId);
if( !$f3->exists($cacheKey, $mapUserData) ){
$mapUserData = $map->getUserData();
// request signature data for a system if user has map access!
if( $mapId === $requestSystemData->mapId ){
$system = $map->getSystemById( $requestSystemData->systemId );
if( !is_null($system) ){
// data for currently selected system
$return->system = $system->getData();
$return->system->signatures = $system->getSignaturesData();
}
// cache time (seconds) should be equal or less than request trigger time
// prevent request flooding
$responseTTL = (int)Config::getPathfinderData('timer.update_server_user_data.delay') / 1000;
$f3->set($cacheKey, $mapUserData, $responseTTL);
}
$return->mapUserData[] = $mapUserData;
}
// cache time (seconds) should be equal or less than request trigger time
// prevent request flooding
$responseTTL = (int)Config::getPathfinderData('timer.update_server_user_data.delay') / 1000;
// cache response
$f3->set($cacheKey, $return, $responseTTL);
// systemData -----------------------------------------------------------------------------------------
if(
$mapId === (int)$systemData['mapId'] &&
!is_null($system = $map->getSystemById((int)$systemData['systemData']['id']))
){
// data for currently selected system
$return->system = $system->getData();
$return->system->signatures = $system->getSignaturesData();
}
}
}
@@ -1042,8 +1008,7 @@ class Map extends Controller\AccessController {
// check distance between systems (in jumps)
// -> if > 1 it is !very likely! a wormhole
$routeController = new Route();
$routeController->initJumpData();
$route = $routeController->findRoute($sourceSystem->name, $targetSystem->name, 1);
$route = $routeController->searchRoute($sourceSystem->systemId, $targetSystem->systemId, 1);
if( !$route['routePossible'] ){
$addSourceSystem = true;
@@ -1121,7 +1086,6 @@ class Map extends Controller\AccessController {
}
if($mapDataChanged){
$this->clearMapDataCache($character);
$this->broadcastMapData($map);
}

View File

@@ -19,6 +19,8 @@ use Model;
*/
class Route extends Controller\AccessController {
const ROUTE_SEARCH_DEPTH_DEFAULT = 1;
/**
* cache time for static jump data (e.g. K-Space stargates)
* @var int
@@ -44,27 +46,32 @@ class Route extends Controller\AccessController {
private $jumpArray = [];
/**
* array withh systemName => systemId matching
* array with systemName => systemId matching
* @var array
*/
private $idArray = [];
/**
* set jump data for route search
* -> this function is required for route search! (Don´t forget)
* @param array $mapIds
* @param array $filterData
* @param array $keepSystems
* template for routeData payload
* @var array
*/
public function initJumpData($mapIds = [], $filterData = [], $keepSystems = []){
// add static data (e.g. K-Space stargates,..)
$this->setStaticJumpData();
private $defaultRouteData = [
'routePossible' => false,
'routeJumps' => 0,
'maxDepth' => self::ROUTE_SEARCH_DEPTH_DEFAULT,
'depthSearched' => 0,
'searchType' => '',
'route' => [],
'error' => ''
];
// add map specific data
$this->setDynamicJumpData($mapIds, $filterData);
// filter jump data (e.g. remove some systems (0.0, LS)
$this->filterJumpData($filterData, $keepSystems);
/**
* reset all jump data
*/
protected function resetJumpData(){
$this->nameArray = [];
$this->jumpArray = [];
$this->idArray = [];
}
/**
@@ -74,31 +81,11 @@ class Route extends Controller\AccessController {
* -> this data is equal for EACH route search (does not depend on map data)
*/
private function setStaticJumpData(){
$cacheKey = 'staticJumpData';
$query = "SELECT * FROM system_neighbour";
$rows = $this->getDB()->exec($query, null, $this->staticJumpDataCacheTime);
$f3 = $this->getF3();
$cacheKeyNamedArray = $cacheKey . '.nameArray';
$cacheKeyJumpArray = $cacheKey . '.jumpArray';
$cacheKeyIdArray = $cacheKey . '.idArray';
if(
!$f3->exists($cacheKeyNamedArray, $this->nameArray) ||
!$f3->exists($cacheKeyJumpArray, $this->jumpArray) ||
!$f3->exists($cacheKeyIdArray, $this->idArray)
){
// nothing cached
$query = "SELECT * FROM system_neighbour";
$rows = $this->getDB()->exec($query, null, $this->staticJumpDataCacheTime);
if(count($rows) > 0){
$this->updateJumpData($rows);
// static data should be cached
$f3->set($cacheKeyNamedArray, $this->nameArray, $this->staticJumpDataCacheTime);
$f3->set($cacheKeyJumpArray, $this->jumpArray, $this->staticJumpDataCacheTime);
$f3->set($cacheKeyIdArray, $this->idArray, $this->staticJumpDataCacheTime);
}
if(count($rows) > 0){
$this->updateJumpData($rows);
}
}
@@ -267,12 +254,12 @@ class Route extends Controller\AccessController {
}
/**
* filter systems (remove some systems) e.g. WH,LS,0.0 for "safer search"
* filter systems (remove some systems) e.g. WH,LS,0.0 for "secure search"
* @param array $filterData
* @param array $keepSystems
*/
private function filterJumpData($filterData = [], $keepSystems = []){
if($filterData['safer']){
if($filterData['flag'] == 'secure'){
// remove all systems (TrueSec < 0.5) from search arrays
$this->jumpArray = array_filter($this->jumpArray, function($jumpData) use($keepSystems) {
@@ -302,9 +289,7 @@ class Route extends Controller\AccessController {
* @return null
*/
private function getSystemInfoBySystemId($systemId, $option){
$info = null;
switch($option){
case 'systemName':
$info = $this->nameArray[ $systemId ][0];
@@ -395,65 +380,98 @@ class Route extends Controller\AccessController {
}
/**
* find a route between two systems (system names)
* $searchDepth for recursive route search (5000 would be best but slow)
* -> in reality there are no routes > 100 jumps between systems
* @param $systemFrom
* @param $systemTo
* @param int $searchDepth
* get formatted jump node data
* @param $systemName
* @return array
*/
public function findRoute($systemFrom, $systemTo, $searchDepth = 7000){
$routeData = [
'routePossible' => false,
'routeJumps' => 0,
'maxDepth' => $searchDepth,
'depthSearched' => 0,
'route' => []
protected function getJumpNodeData($systemName) : array {
return [
'system' => $systemName,
'security' => $this->getSystemInfoBySystemId($this->idArray[$systemName], 'trueSec')
];
}
if(
!empty($systemFrom) &&
!empty($systemTo)
){
/**
* search root between two systemIds
* -> function searches over ESI API, as fallback a custom search algorithm is used (no ESI)
* @param int $systemFromId
* @param int $systemToId
* @param int $searchDepth
* @param array $mapIds
* @param array $filterData
* @return array
* @throws \Exception\PathfinderException
*/
public function searchRoute(int $systemFromId, int $systemToId, $searchDepth = 0, array $mapIds = [], array $filterData = []) : array {
// search root by ESI API
$routeData = $this->searchRouteESI($systemFromId, $systemToId, $searchDepth, $mapIds, $filterData);
$from = strtoupper( $systemFrom );
$to = strtoupper( $systemTo );
if( !empty($routeData['error']) ){
// ESI route search has errors -> fallback to custom search implementation
$routeData = $this->searchRouteCustom($systemFromId, $systemToId, $searchDepth, $mapIds, $filterData);
}
return $routeData;
}
/**
* uses a custom search algorithm to fine a route
* @param int $systemFromId
* @param int $systemToId
* @param int $searchDepth
* @param array $mapIds
* @param array $filterData
* @return array
* @throws \Exception\PathfinderException
*/
public function searchRouteCustom(int $systemFromId, int $systemToId, $searchDepth = 0, array $mapIds = [], array $filterData = []) : array {
// reset all previous set jump data
$this->resetJumpData();
$searchDepth = $searchDepth ? $searchDepth : Config::getPathfinderData('route.search_depth');
$routeData = $this->defaultRouteData;
$routeData['maxDepth'] = $searchDepth;
$routeData['searchType'] = 'custom';
if($systemFromId && $systemToId){
// prepare search data ------------------------------------------------------------------------------------
// add static data (e.g. K-Space stargates,..)
$this->setStaticJumpData();
// add map specific data
$this->setDynamicJumpData($mapIds, $filterData);
// filter jump data (e.g. remove some systems (0.0, LS)
// --> don´t filter some systems (e.g. systemFrom, systemTo) even if they are are WH,LS,0.0
$this->filterJumpData($filterData, [$systemFromId, $systemToId]);
$systemFrom = $this->getSystemInfoBySystemId($systemFromId, 'systemName');
$systemTo = $this->getSystemInfoBySystemId($systemToId, 'systemName');
// search route -------------------------------------------------------------------------------------------
// jump counter
$jumpNum = 0;
$depthSearched = 0;
if( isset($this->jumpArray[$from]) ){
if( isset($this->jumpArray[$systemFrom]) ){
// check if the system we are looking for is a direct neighbour
foreach( $this->jumpArray[$from] as $n ) {
if ($n == $to) {
foreach( $this->jumpArray[$systemFrom] as $n ) {
if ($n == $systemTo) {
$jumpNum = 2;
$jumpNode = [
'system' => $n,
'security' => $this->getSystemInfoBySystemId($this->idArray[$n], 'trueSec')
];
$routeData['route'][] = $jumpNode;
$routeData['route'][] = $this->getJumpNodeData($n);
break;
}
}
// system is not a direct neighbour -> search recursive its neighbours
if ($jumpNum == 0) {
$searchResult = $this->graph_find_path( $this->jumpArray, $from, $to, $searchDepth );
$searchResult = $this->graph_find_path( $this->jumpArray, $systemFrom, $systemTo, $searchDepth );
$depthSearched = $searchResult['depth'];
foreach( $searchResult['path'] as $systemName ) {
if ($jumpNum > 0) {
$jumpNode = [
'system' => $systemName,
'security' => $this->getSystemInfoBySystemId($this->idArray[$systemName], 'trueSec')
];
$routeData['route'][] = $jumpNode;
$routeData['route'][] = $this->getJumpNodeData($systemName);
}
$jumpNum++;
}
@@ -462,14 +480,8 @@ class Route extends Controller\AccessController {
if ($jumpNum > 0) {
// route found
$routeData['routePossible'] = true;
$jumpNode = [
'system' => $from,
'security' => $this->getSystemInfoBySystemId($this->idArray[$from], 'trueSec')
];
// insert "from" system on top
array_unshift($routeData['route'], $jumpNode);
array_unshift($routeData['route'], $this->getJumpNodeData($systemFrom));
} else {
// route not found
$routeData['routePossible'] = false;
@@ -484,6 +496,102 @@ class Route extends Controller\AccessController {
return $routeData;
}
/**
* uses ESI route search endpoint to fine a route
* @param int $systemFromId
* @param int $systemToId
* @param int $searchDepth
* @param array $mapIds
* @param array $filterData
* @return array
* @throws \Exception\PathfinderException
*/
public function searchRouteESI(int $systemFromId, int $systemToId, int $searchDepth = 0, array $mapIds = [], array $filterData = []) : array {
// reset all previous set jump data
$this->resetJumpData();
$searchDepth = $searchDepth ? $searchDepth : Config::getPathfinderData('route.search_depth');
$routeData = $this->defaultRouteData;
$routeData['maxDepth'] = $searchDepth;
$routeData['searchType'] = 'esi';
if($systemFromId && $systemToId){
// prepare search data ------------------------------------------------------------------------------------
// add map specific data
$this->setDynamicJumpData($mapIds, $filterData);
// filter jump data (e.g. remove some systems (0.0, LS)
// --> don´t filter some systems (e.g. systemFrom, systemTo) even if they are are WH,LS,0.0
$this->filterJumpData($filterData, [$systemFromId, $systemToId]);
$connections = [];
foreach($this->jumpArray as $systemSourceName => $jumpData){
$count = count($jumpData);
if($count > 1){
// ... should always > 1
$systemSourceId = (int)$this->idArray[$systemSourceName];
// loop all connections for current source system
foreach($jumpData as $systemTargetName) {
// skip last entry
if(--$count <= 0){
break;
}
$systemTargetId = (int)$this->idArray[$systemTargetName];
// systemIds exist and wer not removed before in filterJumpData()
if($systemSourceId && $systemTargetId){
$connections[] = [$systemSourceId, $systemTargetId];
}
}
}
}
// search route -------------------------------------------------------------------------------------------
$options = [
'flag' => $filterData['flag'],
'connections' => $connections
];
$result = $this->getF3()->ccpClient->getRouteData($systemFromId, $systemToId, $options);
// format result ------------------------------------------------------------------------------------------
// jump counter
$jumpNum = 0;
$depthSearched = 0;
if( !empty($result['error']) ){
$routeData['error'] = $result['error'];
}elseif( !empty($result['route']) ){
$jumpNum = count($result['route']) - 1;
// check max search depth
if($jumpNum <= $routeData['maxDepth']){
$depthSearched = $jumpNum;
$routeData['routePossible'] = true;
// Now (after search) we have to "add" static jump data information
$this->setStaticJumpData();
foreach($result['route'] as $systemId){
$systemName = $this->getSystemInfoBySystemId($systemId, 'systemName');
$routeData['route'][] = $this->getJumpNodeData($systemName);
}
}else{
$depthSearched = $routeData['maxDepth'];
}
}
// route jumps
$routeData['routeJumps'] = $jumpNum;
$routeData['depthSearched'] = $depthSearched;
}
return $routeData;
}
/**
* get key for route cache
* @param $mapIds
@@ -543,7 +651,9 @@ class Route extends Controller\AccessController {
// check map access (filter requested mapIDs and format) ----------------------------------------------
array_walk($mapData, function(&$item, &$key, $data){
/**
* @var Model\MapModel $data[0]
*/
if( isset($data[1][$key]) ){
// character has map access -> do not check again
$item = $data[1][$key];
@@ -577,7 +687,7 @@ class Route extends Controller\AccessController {
'wormholesCritical' => (bool) $routeData['wormholesCritical'],
'wormholesFrigate' => (bool) $routeData['wormholesFrigate'],
'wormholesEOL' => (bool) $routeData['wormholesEOL'],
'safer' => (bool) $routeData['safer']
'flag' => $routeData['flag']
];
$returnRoutData = [
@@ -611,16 +721,8 @@ class Route extends Controller\AccessController {
// get data from cache
$returnRoutData = $cachedData;
}else{
// max search depth for search
$searchDepth = Config::getPathfinderData('route.search_depth');
$foundRoutData = $this->searchRoute($systemFromId, $systemToId, 0, $mapIds, $filterData);
// set jump data for following route search
// --> don´t filter some systems (e.g. systemFrom, systemTo) even if they are are WH,LS,0.0
$keepSystems = [$systemFromId, $systemToId];
$this->initJumpData($mapIds, $filterData, $keepSystems);
// no cached route data found
$foundRoutData = $this->findRoute($systemFrom, $systemTo, $searchDepth);
$returnRoutData = array_merge($returnRoutData, $foundRoutData);
// cache if route was found

View File

@@ -25,6 +25,7 @@ class Controller {
const ERROR_SESSION_SUSPECT = 'id: [%45s], ip: [%45s], User-Agent: [%s]';
const ERROR_TEMP_CHARACTER_ID = 'Invalid temp characterId: %s';
const NOTIFICATION_TYPES = ['danger', 'warning', 'info', 'success'];
/**
* @var \Base
*/
@@ -184,6 +185,7 @@ class Controller {
* set/update logged in cookie by character model
* -> store validation data in DB
* @param Model\CharacterModel $character
* @throws \Exception
* @throws \Exception\PathfinderException
*/
protected function setLoginCookie(Model\CharacterModel $character){
@@ -541,6 +543,20 @@ class Controller {
return $object;
}
/**
* @param string $title
* @param string $message
* @param string $type
* @return \stdClass
*/
protected function getNotificationObject(string $title, $message = '', $type = 'danger') : \stdClass {
$notification = (object) [];
$notification->type = in_array($type, self::NOTIFICATION_TYPES) ? $type : 'danger';
$notification->title = $title;
$notification->message = $message;
return $notification;
}
/**
* get a program URL by alias
* -> if no $alias given -> get "default" route (index.php)
@@ -716,11 +732,11 @@ class Controller {
* @return array
*/
static function getScopesByAuthType($authType = ''){
$scopes = (array)self::getEnvironmentData('CCP_ESI_SCOPES');
$scopes = array_filter((array)self::getEnvironmentData('CCP_ESI_SCOPES'));
switch($authType){
case 'admin':
$scopesAdmin = (array)self::getEnvironmentData('CCP_ESI_SCOPES_ADMIN');
$scopesAdmin = array_filter((array)self::getEnvironmentData('CCP_ESI_SCOPES_ADMIN'));
$scopes = array_merge($scopes, $scopesAdmin);
break;
}

View File

@@ -9,6 +9,7 @@
namespace lib;
use controller\LogController;
use Exception;
class Config extends \Prefab {
@@ -378,7 +379,6 @@ class Config extends \Prefab {
}
/**
* get PATHFINDER config data
* @param string $key
* @return mixed
* @throws Exception\PathfinderException
@@ -386,8 +386,12 @@ class Config extends \Prefab {
static function getPathfinderData($key = ''){
$hiveKey = self::HIVE_KEY_PATHFINDER . ($key ? '.' . strtoupper($key) : '');
if( !\Base::instance()->exists($hiveKey, $data) ){
throw new Exception\PathfinderException(sprintf(self::ERROR_CONF_PATHFINDER, $hiveKey));
try{
if( !\Base::instance()->exists($hiveKey, $data) ){
throw new Exception\PathfinderException(sprintf(self::ERROR_CONF_PATHFINDER, $hiveKey));
}
}catch (Exception\PathfinderException $e){
LogController::getLogger('ERROR')->write($e->getMessage());
}
return $data;

View File

@@ -222,6 +222,9 @@ class CharacterLogModel extends BasicModel {
if($this->characterId){
// characterId relation could be deleted by cron therefore check again first...
$this->characterId->clearCacheDataWithPrefix(CharacterModel::DATA_CACHE_KEY_LOG);
// broadcast updated character data (with changed log data)
$this->characterId->broadcastCharacterUpdate();
}
}

View File

@@ -13,6 +13,7 @@ use Controller\Api\User as User;
use DB\SQL\Schema;
use lib\Util;
use lib\Config;
use lib\Socket;
use Model\Universe;
class CharacterModel extends BasicModel {
@@ -904,6 +905,15 @@ class CharacterModel extends BasicModel {
return $this;
}
/**
* broadcast characterData
* @throws \ZMQSocketException
*/
public function broadcastCharacterUpdate(){
$characterData = $this->getData(true);
(new Socket( Config::getSocketUri() ))->sendData('characterUpdate', $characterData);
}
/**
* update character data from CCPs ESI API
* @return array (some status messages)

View File

@@ -179,8 +179,7 @@ class ConnectionModel extends AbstractMapTrackingModel {
is_object($this->target)
){
$routeController = new Route();
$routeController->initJumpData();
$route = $routeController->findRoute($this->source->name, $this->target->name, 1);
$route = $routeController->searchRoute($this->source->systemId, $this->target->systemId, 1);
if($route['routePossible']){
$this->scope = 'stargate';

View File

@@ -844,8 +844,9 @@ class MapModel extends AbstractMapTrackingModel {
/**
* get data for ALL characters with map access
* -> The result of this function is cached!
* @param array $options filter options
* @return \stdClass
* @param array $options
* @return array|null|\stdClass
* @throws \Exception
*/
public function getCharactersData($options = []){
// check if there is cached data

View File

@@ -3,7 +3,7 @@
[PATHFINDER]
NAME = Pathfinder
; installed version (used for CSS/JS cache busting)
VERSION = v1.3.3
VERSION = v1.3.4
; contact information [optional]
CONTACT = https://github.com/exodus4d
; public contact email [optional]
@@ -123,15 +123,15 @@ SEND_RALLY_Mail_ENABLED = 0
; Route search ====================================================================================
[PATHFINDER.ROUTE]
; max recursive search depth for routes (default: 7000)
; max recursive search depth for routes (default: 9000)
; decrease it on performance problems
SEARCH_DEPTH = 9000
; default count of routes that will be checked (initial) when a system is selected (default: 2)
SEARCH_DEFAULT_COUNT = 2
; max count of routes that can be selected in "route settings" dialog (default: 4)
MAX_DEFAULT_COUNT = 4
; max count of routes that will be checked (MAX_COUNT + custom routes ) (default: 6)
LIMIT = 6
; default count of routes that will be checked (initial) when a system is selected (default: 4)
SEARCH_DEFAULT_COUNT = 4
; max count of routes that can be selected in "route settings" dialog (default: 6)
MAX_DEFAULT_COUNT = 6
; max count of routes that will be checked (MAX_COUNT + custom routes ) (default: 8)
LIMIT = 8
; Email notifications =============================================================================
; Requires SMTP configuration (see environment.ini)
@@ -206,6 +206,14 @@ CACHE = 5
; file folder for 'history' logs (e.g. map history) (default: history/)
LOG = history/
; ADMIN ===========================================================================================
; "SUPER" admins and additional "CORPORATION" admins can be added here
;[PATHFINDER.ROLES]
;CHARACTER.0.ID = 123456789
;CHARACTER.0.ROLE = SUPER
;CHARACTER.1.ID = 1122334455
;CHARACTER.1.ROLE = CORPORATION
; API =============================================================================================
[PATHFINDER.API]
CCP_IMAGE_SERVER = https://image.eveonline.com

View File

@@ -9,7 +9,7 @@ GET @login: / [sync] = Controller\AppContro
; CCP SSO redirect
GET @sso: /sso/@action [sync] = Controller\Ccp\Sso->@action
; map page
GET @map: /map [sync] = Controller\MapController->init
GET @map: /map* [sync] = Controller\MapController->init
; admin panel
GET @admin: /admin* [sync] = Controller\Admin->dispatch
@@ -17,3 +17,4 @@ GET @admin: /admin* [sync] = Controller\Admin->di
GET|POST /api/@controller/@action [ajax] = Controller\Api\@controller->@action, 0, 512
GET|POST /api/@controller/@action/@arg1 [ajax] = Controller\Api\@controller->@action, 0, 512
GET|POST /api/@controller/@action/@arg1/@arg2 [ajax] = Controller\Api\@controller->@action, 0, 512

View File

@@ -27,6 +27,6 @@
"monolog/monolog": "1.*",
"websoftwares/monolog-zmq-handler": "0.2.*",
"swiftmailer/swiftmailer": "^6.0",
"exodus4d/pathfinder_esi": "dev-master#v1.2.2"
"exodus4d/pathfinder_esi": "dev-master#v1.2.3"
}
}

View File

@@ -7,8 +7,8 @@ require 'compass/import-once/activate'
http_path = "/"
# project_path = ''
css_dir = 'public/css'
sass_dir = 'sass'
# css_dir = 'public/css'
# sass_dir = 'sass'
images_dir = 'public/img'
generated_images_path = 'img_cache'
# javascripts_dir = 'javascripts'

View File

@@ -5,7 +5,6 @@ let fs = require('fs');
let ini = require('ini');
let gulp = require('gulp');
let gutil = require('gulp-util');
let requirejsOptimize = require('gulp-requirejs-optimize');
let filter = require('gulp-filter');
let gulpif = require('gulp-if');
@@ -19,20 +18,23 @@ let compass = require('gulp-compass');
let cleanCSS = require('gulp-clean-css');
let bytediff = require('gulp-bytediff');
let debug = require('gulp-debug');
let notifier = require('node-notifier');
// -- Helper & NPM modules ----------------------------------------------------
let flatten = require('flat');
let padEnd = require('lodash.padend');
let merge = require('lodash.merge');
let minimist = require('minimist');
let slash = require('slash');
let fileExtension = require('file-extension');
let log = require('fancy-log');
let colors = require('ansi-colors');
let stylish = require('jshint-stylish');
let Table = require('terminal-table');
let prettyBytes = require('pretty-bytes');
let del = require('promised-del');
let minify = composer(uglifyjs, console);
let chalk = gutil.colors;
// == Settings ========================================================================================================
@@ -55,8 +57,9 @@ let PATH = {
}
};
// Pathfinder config file
let pathfinderConfigFile = './app/pathfinder.ini';
// Pathfinder config files
let pathfinderConfigFileApp = './app/pathfinder.ini';
let pathfinderConfigFileConf = './conf/pathfinder.ini';
// CLI box size in characters
let cliBoxLength = 80;
@@ -96,36 +99,6 @@ let uglifyJsOptions = {
// Sourcemaps options
// https://www.npmjs.com/package/gulp-sourcemaps
// -- Plugin options ----------------------------------------------------------
let gZipOptions = {
append: false, // disables default append ext .gz
extension: 'gz', // use "custom" ext: .gz
threshold: '1kb', // min size required to compress a file
deleteMode: PATH.JS.DIST_BUILD, // replace *.gz files if size < 'threhold'
gzipOptions: {
level: 9 // zlib.Gzip compression level [0-9]
},
skipGrowingFiles: true // use orig. files in case of *.gz size > orig. size
};
let brotliOptions = {
extension: 'br', // use "custom" ext: .br
mode: 1, // compression mode for UTF-8 formatted text
quality: 11, // quality [1 worst - 11 best]
skipLarger: true // use orig. files in case of *.br size > orig. size
};
let compassOptions = {
config_file: './config.rb',
css: 'public/css',
sass: 'sass',
time: true, // show execution time
sourcemap: true
};
let compressionExt = [gZipOptions.extension, brotliOptions.extension];
// -- Error output ------------------------------------------------------------
/**
@@ -136,14 +109,14 @@ let compressionExt = [gZipOptions.extension, brotliOptions.extension];
let printError = (title, example) => {
let cliLineLength = (cliBoxLength - 8);
gutil.log('').log(chalk.red( '= ERROR ' + '=' . repeat(cliLineLength)));
gutil.log(chalk.red(title));
log('').log(colors.red( '= ERROR ' + '=' . repeat(cliLineLength)));
log(colors.red(title));
if(example){
gutil.log(`
${chalk.gray(example)}
log(`
${colors.gray(example)}
`);
}
gutil.log(chalk.red('='.repeat(cliBoxLength))).log('');
log(colors.red('='.repeat(cliBoxLength))).log('');
};
// == Settings ========================================================================================================
@@ -151,19 +124,23 @@ let printError = (title, example) => {
// parse pathfinder.ini config file for relevant data
let tagVersion;
try{
let pathfinderIni = ini.parse(fs.readFileSync(pathfinderConfigFile, 'utf-8'));
let pathfinderAppIni = ini.parse(fs.readFileSync(pathfinderConfigFileApp, 'utf-8'));
let pathfinderConfIni = fs.existsSync(pathfinderConfigFileConf) ?
ini.parse(fs.readFileSync(pathfinderConfigFileConf, 'utf-8')) : {};
let pathfinderIni = merge(pathfinderAppIni, pathfinderConfIni);
try{
tagVersion = pathfinderIni.PATHFINDER.VERSION;
}catch(err){
printError(
err.message,
'Missing "PATHFINDER.VERSION" in "' + pathfinderConfigFile + '"');
'Missing "PATHFINDER.VERSION" in "' + pathfinderConfigFileApp + '"');
process.exit(1);
}
}catch(err){
printError(
err.message,
'Check read permissions for "' + pathfinderConfigFile + '"');
'Check read permissions for "' + pathfinderConfigFileApp + '"');
process.exit(1);
}
@@ -188,6 +165,36 @@ let CONF = {
DEBUG: false
};
// -- Plugin options ----------------------------------------------------------
let gZipOptions = {
append: false, // disables default append ext .gz
extension: 'gz', // use "custom" ext: .gz
threshold: '1kb', // min size required to compress a file
deleteMode: PATH.JS.DIST_BUILD, // replace *.gz files if size < 'threshold'
gzipOptions: {
level: 9 // zlib.Gzip compression level [0-9]
},
skipGrowingFiles: true // use orig. files in case of *.gz size > orig. size
};
let brotliOptions = {
extension: 'br', // use "custom" ext: .br
mode: 1, // compression mode for UTF-8 formatted text
quality: 11, // quality [1 worst - 11 best]
skipLarger: true // use orig. files in case of *.br size > orig. size
};
let compassOptions = {
config_file: './config.rb',
css: 'public/css/' + CONF.TAG, // #VERSION# will be replaced with version tag
sass: 'sass',
time: true, // show execution time
sourcemap: true
};
let compressionExt = [gZipOptions.extension, brotliOptions.extension];
// == Helper methods ==================================================================================================
/**
@@ -263,33 +270,33 @@ let mergeConf = (confUser, confDefault) => {
*/
let printHelp = () => {
let cliLineLength = (cliBoxLength - 7);
gutil.log('')
.log(chalk.cyan( '= HELP ' + '='.repeat(cliLineLength)))
log('')
.log(colors.cyan( '= HELP ' + '='.repeat(cliLineLength)))
.log(`
${chalk.cyan('documentation:')} ${chalk.gray('https://github.com/exodus4d/pathfinder/wiki/GulpJs')}
${colors.cyan('documentation:')} ${colors.gray('https://github.com/exodus4d/pathfinder/wiki/GulpJs')}
${chalk.cyan('usage:')} ${chalk.gray('$ npm run gulp [task] -- [--options] ...')}
${colors.cyan('usage:')} ${colors.gray('$ npm run gulp [task] -- [--options] ...')}
${chalk.cyan('tasks:')}
${chalk.gray('help')} This view
${chalk.gray('default')} Development environment. Working with row src files and file watcher, default:
${chalk.gray('')} ${chalk.gray('--jsUglify=false --jsSourcemaps=false --cssSourcemaps=false --jsGzip=false --cssGzip=false --jsBrotli=false --cssBrotli=false')}
${chalk.gray('production')} Production build. Concat and uglify static resources, default:
${chalk.gray('')} ${chalk.gray('--jsUglify=true --jsSourcemaps=true --cssSourcemaps=true --jsGzip=true --cssGzip=true --jsBrotli=true --cssBrotli=true')}
${colors.cyan('tasks:')}
${colors.gray('help')} This view
${colors.gray('default')} Development environment. Working with row src files and file watcher, default:
${colors.gray('')} ${colors.gray('--jsUglify=false --jsSourcemaps=false --cssSourcemaps=false --jsGzip=false --cssGzip=false --jsBrotli=false --cssBrotli=false')}
${colors.gray('production')} Production build. Concat and uglify static resources, default:
${colors.gray('')} ${colors.gray('--jsUglify=true --jsSourcemaps=true --cssSourcemaps=true --jsGzip=true --cssGzip=true --jsBrotli=true --cssBrotli=true')}
${chalk.cyan('options:')}
${chalk.gray('--tag')} Set build version. ${chalk.gray('default: --tag="v1.2.4" -> dest path: public/js/v1.2.4')}
${chalk.gray('--jsUglify')} Set js uglification. ${chalk.gray('(true || false)')}
${chalk.gray('--jsSourcemaps')} Set js sourcemaps generation. ${chalk.gray('(true || false)')}
${chalk.gray('--jsGzip')} Set js "gzip" compression mode. ${chalk.gray('(true || false)')}
${chalk.gray('--jsBrotli')} Set js "brotli" compression mode. ${chalk.gray('(true || false)')}
${colors.cyan('options:')}
${colors.gray('--tag')} Set build version. ${colors.gray('default: --tag="v1.2.4" -> dest path: public/js/v1.2.4')}
${colors.gray('--jsUglify')} Set js uglification. ${colors.gray('(true || false)')}
${colors.gray('--jsSourcemaps')} Set js sourcemaps generation. ${colors.gray('(true || false)')}
${colors.gray('--jsGzip')} Set js "gzip" compression mode. ${colors.gray('(true || false)')}
${colors.gray('--jsBrotli')} Set js "brotli" compression mode. ${colors.gray('(true || false)')}
${chalk.gray('--cssSourcemaps')} Set CSS sourcemaps generation. ${chalk.gray('(true || false)')}
${chalk.gray('--cssGzip')} Set CSS "gzip" compression mode. ${chalk.gray('(true || false)')}
${chalk.gray('--cssBrotli')} Set CSS "brotli" compression mode. ${chalk.gray('(true || false)')}
${chalk.gray('--debug')} Set debug mode (more output). ${chalk.gray('(true || false)')}
${colors.gray('--cssSourcemaps')} Set CSS sourcemaps generation. ${colors.gray('(true || false)')}
${colors.gray('--cssGzip')} Set CSS "gzip" compression mode. ${colors.gray('(true || false)')}
${colors.gray('--cssBrotli')} Set CSS "brotli" compression mode. ${colors.gray('(true || false)')}
${colors.gray('--debug')} Set debug mode (more output). ${colors.gray('(true || false)')}
`)
.log(chalk.cyan('='.repeat(cliBoxLength)))
.log(colors.cyan('='.repeat(cliBoxLength)))
.log('');
};
@@ -484,7 +491,7 @@ gulp.task('task:cleanJsBuild', () => del([PATH.JS.DIST_BUILD]));
/**
* clean CSS build dir
*/
gulp.task('task:cleanCssBuild', () => del([PATH.ASSETS.DIST + '/css']));
gulp.task('task:cleanCssBuild', () => del([PATH.ASSETS.DIST + '/css/' + CONF.TAG]));
/**
* clean JS destination (final) dir
@@ -535,11 +542,11 @@ gulp.task('task:concatJS', () => {
};
}))
.pipe(bytediff.start())
.pipe(gulpif(CONF.JS.UGLIFY, minify(uglifyJsOptions).on('warnings', gutil.log)))
.pipe(gulpif(CONF.JS.UGLIFY, minify(uglifyJsOptions).on('warnings', log)))
.pipe(gulpif(CONF.JS.SOURCEMAPS, sourcemaps.write('.', {includeContent: false, sourceRoot: '/js'}))) // prod (minify)
.pipe(bytediff.stop(data => {
trackFile(data, {src: 'startSize', src_percent: 'percent', uglify: 'endSize'});
return chalk.green('Build concat file "' + data.fileName + '"');
return colors.green('Build concat file "' + data.fileName + '"');
}))
.pipe(gulp.dest(PATH.JS.DIST_BUILD));
});
@@ -559,7 +566,7 @@ gulp.task('task:diffJS', () => {
.pipe(gulpif(CONF.JS.SOURCEMAPS, sourcemaps.write('.', {includeContent: false, sourceRoot: '/js'})))
.pipe(bytediff.stop(data => {
trackFile(data, {src: 'startSize', src_percent: 'percent', uglify: 'endSize'});
return chalk.green('Build file "' + data.fileName + '"');
return colors.green('Build file "' + data.fileName + '"');
}))
.pipe(gulp.dest(PATH.JS.DIST_BUILD, {overwrite: false}));
});
@@ -595,9 +602,9 @@ let gzipAssets = (config, taskName) => {
.pipe(bytediff.stop(data => {
trackFile(data, {gzipFile: 'fileName', gzip: 'endSize'});
if(fileExtension(data.fileName) === gZipOptions.extension){
return chalk.green('Gzip generate "' + data.fileName + '"');
return colors.green('Gzip generate "' + data.fileName + '"');
}else{
return chalk.gray('Gzip skip "' + data.fileName + '". Size < ' + gZipOptions.threshold + ' (threehold)');
return colors.gray('Gzip skip "' + data.fileName + '". Size < ' + gZipOptions.threshold + ' (threehold)');
}
}))
.pipe(gulp.dest(PATH.ASSETS.DIST));
@@ -618,9 +625,9 @@ let brotliAssets = (config, taskName) => {
.pipe(bytediff.stop(data => {
trackFile(data, {brotliFile: 'fileName', brotli: 'endSize'});
if(fileExtension(data.fileName) === brotliOptions.extension){
return chalk.green('Brotli generate "' + data.fileName + '"');
return colors.green('Brotli generate "' + data.fileName + '"');
}else{
return chalk.gray('Brotli skip "' + data.fileName + '"');
return colors.gray('Brotli skip "' + data.fileName + '"');
}
}))
.pipe(gulp.dest(PATH.ASSETS.DIST));
@@ -664,9 +671,9 @@ gulp.task('task:sass', () => {
.pipe(bytediff.start())
.pipe(bytediff.stop(data => {
trackFile(data, {src: 'startSize', src_percent: 'percent', uglify: 'endSize'});
return chalk.green('Build CSS file "' + data.fileName + '"');
return colors.green('Build CSS file "' + data.fileName + '"');
}))
.pipe(gulp.dest(PATH.ASSETS.DIST + '/css'));
.pipe(gulp.dest(PATH.ASSETS.DIST + '/css/' + CONF.TAG));
});
/**
@@ -678,7 +685,7 @@ gulp.task('task:cleanCss', () => {
compatibility: '*',
level: 2
}))
.pipe(gulp.dest(PATH.ASSETS.DIST +'/css'));
.pipe(gulp.dest(PATH.ASSETS.DIST +'/css/' + CONF.TAG));
});
// == Helper tasks ====================================================================================================
@@ -703,13 +710,13 @@ gulp.task('task:printJsSummary', done => {
* print task configuration (e.g. CLI parameters)
*/
gulp.task('task:printConfig', done => {
let error = chalk.red;
let success = chalk.green;
let error = colors.red;
let success = colors.green;
let columnLength = Math.round(cliBoxLength / 2);
let cliLineLength = cliBoxLength - 9;
gutil.log(chalk.gray( '= CONFIG ' + '='.repeat(cliLineLength)));
log(colors.gray( '= CONFIG ' + '='.repeat(cliLineLength)));
let configFlat = flatten(CONF);
for (let key in configFlat) {
@@ -717,13 +724,13 @@ gulp.task('task:printConfig', done => {
let value = configFlat[key];
// format value
value = padEnd((typeof value === 'undefined') ? 'undefined': value, columnLength);
gutil.log(
chalk.gray.yellow(padEnd(key, columnLength)),
log(
colors.yellow(padEnd(key, columnLength)),
configFlat[key] ? success(value) : error(value)
);
}
}
gutil.log(chalk.reset.gray('='.repeat(cliBoxLength)));
log(colors.gray('='.repeat(cliBoxLength)));
done();
});
@@ -733,7 +740,7 @@ gulp.task('task:printConfig', done => {
gulp.task('task:checkConfig', done => {
if(!CONF.TAG){
printError(
'Missing TAG version. Add param ' + chalk.cyan('--tag'),
'Missing TAG version. Add param ' + colors.cyan('--tag'),
'$ npm run gulp default -- --tag="v1.2.4"');
process.exit(0);
}
@@ -802,7 +809,7 @@ gulp.task('task:updateJsDest', gulp.series(
'task:gzipJsAssets',
'task:brotliJsAssets',
'task:renameJsDest',
'task:printJsSummary',
// 'task:printJsSummary',
'task:cleanJsBuild'
)
);
@@ -826,6 +833,36 @@ gulp.task('task:buildCss', gulp.series(
)
);
// == Notification tasks ==============================================================================================
/**
* JS Build done notification
*/
gulp.task('task:notifyJsDone', done => {
notifier.notify({
title: 'Done JS build',
message: 'JS build task finished',
icon: PATH.ASSETS.DIST + '/img/logo.png',
wait: false
});
done();
}
);
/**
* CSS Build done notification
*/
gulp.task('task:notifyCssDone', done => {
notifier.notify({
title: 'Done CSS build',
message: 'CSS build task finished',
icon: PATH.ASSETS.DIST + '/img/logo.png',
wait: false
});
done();
}
);
// == Watcher tasks ===================================================================================================
/**
@@ -836,7 +873,8 @@ gulp.task(
gulp.series(
'task:hintJS',
'task:diffJS',
'task:updateJsDest'
'task:updateJsDest',
'task:notifyJsDone'
)
);
@@ -850,18 +888,30 @@ gulp.task(
// 'task:cleanCss',
'task:gzipCssAssets',
'task:brotliCssAssets',
'task:printJsSummary'
// 'task:printJsSummary',
'task:notifyCssDone'
)
);
/**
* watch files for changes
*/
gulp.task('task:setWatcher', () => {
gulp.watch(PATH.JS.SRC, gulp.series('task:watchJsSrc'));
gulp.watch(PATH.CSS.SRC, gulp.series('task:watchCss'));
gulp.task('task:setWatcherJs', function() {
return gulp.watch(PATH.JS.SRC, gulp.series('task:watchJsSrc', 'task:printJsSummary'));
});
gulp.task('task:setWatcherCss', function() {
return gulp.watch(PATH.CSS.SRC, gulp.series('task:watchCss', 'task:printJsSummary'));
});
gulp.task('task:setWatcher',
gulp.parallel(
'task:setWatcherJs',
'task:setWatcherCss'
)
);
// == Default/Main tasks ==============================================================================================
gulp.task(
@@ -883,9 +933,10 @@ gulp.task(
gulp.series(
'task:cleanCssBuild',
'task:watchCss'
),
'task:setWatcher'
)
)
),
'task:printJsSummary',
'task:setWatcher'
)
);
@@ -902,7 +953,8 @@ gulp.task(
'task:cleanCssBuild',
'task:watchCss'
)
)
),
'task:printJsSummary'
)
);

View File

@@ -8,51 +8,51 @@ define(['jquery'], ($) => {
let Config = {
path: {
img: 'public/img/', // path for images
img: '/public/img/', // path for images
// user API
getCaptcha: 'api/user/getCaptcha', // ajax URL - get captcha image
getServerStatus: 'api/user/getEveServerStatus', // ajax URL - get EVE-Online server status
getCookieCharacterData: 'api/user/getCookieCharacter', // ajax URL - get character data from cookie
logIn: 'api/user/logIn', // ajax URL - login
logout: 'api/user/logout', // ajax URL - logout
deleteLog: 'api/user/deleteLog', // ajax URL - delete character log
openIngameWindow: 'api/user/openIngameWindow', // ajax URL - open inGame Window
saveUserConfig: 'api/user/saveAccount', // ajax URL - saves/update user account
deleteAccount: 'api/user/deleteAccount', // ajax URL - delete Account data
getCaptcha: '/api/user/getCaptcha', // ajax URL - get captcha image
getServerStatus: '/api/user/getEveServerStatus', // ajax URL - get EVE-Online server status
getCookieCharacterData: '/api/user/getCookieCharacter', // ajax URL - get character data from cookie
logIn: '/api/user/logIn', // ajax URL - login
logout: '/api/user/logout', // ajax URL - logout
deleteLog: '/api/user/deleteLog', // ajax URL - delete character log
openIngameWindow: '/api/user/openIngameWindow', // ajax URL - open inGame Window
saveUserConfig: '/api/user/saveAccount', // ajax URL - saves/update user account
deleteAccount: '/api/user/deleteAccount', // ajax URL - delete Account data
// access API
searchAccess: 'api/access/search', // ajax URL - search user/corporation/ally by name
searchAccess: '/api/access/search', // ajax URL - search user/corporation/ally by name
// main config/map ping API
initMap: 'api/map/init', // ajax URL - get static data
getAccessData: 'api/map/getAccessData', // ajax URL - get map access tokens (WebSocket)
updateMapData: 'api/map/updateData', // ajax URL - main map update trigger
updateUserData: 'api/map/updateUserData', // ajax URL - main map user data trigger
initData: '/api/map/initData', // ajax URL - get static configuration data
getAccessData: '/api/map/getAccessData', // ajax URL - get map access tokens (WebSocket)
updateMapData: '/api/map/updateData', // ajax URL - main map update trigger
updateUserData: '/api/map/updateUserData', // ajax URL - main map user data trigger
// map API
saveMap: 'api/map/save', // ajax URL - save/update map
deleteMap: 'api/map/delete', // ajax URL - delete map
importMap: 'api/map/import', // ajax URL - import map
getMapConnectionData: 'api/map/getConnectionData', // ajax URL - get connection data
getMapLogData: 'api/map/getLogData', // ajax URL - get logs data
saveMap: '/api/map/save', // ajax URL - save/update map
deleteMap: '/api/map/delete', // ajax URL - delete map
importMap: '/api/map/import', // ajax URL - import map
getMapConnectionData: '/api/map/getConnectionData', // ajax URL - get connection data
getMapLogData: '/api/map/getLogData', // ajax URL - get logs data
// system API
searchSystem: 'api/system/search', // ajax URL - search system by name
saveSystem: 'api/system/save', // ajax URL - saves system to map
deleteSystem: 'api/system/delete', // ajax URL - delete system from map
getSystemGraphData: 'api/system/graphData', // ajax URL - get all system graph data
getConstellationData: 'api/system/constellationData', // ajax URL - get system constellation data
setDestination: 'api/system/setDestination', // ajax URL - set destination
pokeRally: 'api/system/pokeRally', // ajax URL - send rally point pokes
searchSystem: '/api/system/search', // ajax URL - search system by name
saveSystem: '/api/system/save', // ajax URL - saves system to map
deleteSystem: '/api/system/delete', // ajax URL - delete system from map
getSystemGraphData: '/api/system/graphData', // ajax URL - get all system graph data
getConstellationData: '/api/system/constellationData', // ajax URL - get system constellation data
setDestination: '/api/system/setDestination', // ajax URL - set destination
pokeRally: '/api/system/pokeRally', // ajax URL - send rally point pokes
// connection API
saveConnection: 'api/connection/save', // ajax URL - save new connection to map
deleteConnection: 'api/connection/delete', // ajax URL - delete connection from map
saveConnection: '/api/connection/save', // ajax URL - save new connection to map
deleteConnection: '/api/connection/delete', // ajax URL - delete connection from map
// signature API
getSignatures: 'api/signature/getAll', // ajax URL - get all signature data for system
saveSignatureData: 'api/signature/save', // ajax URL - save signature data for system
deleteSignatureData: 'api/signature/delete', // ajax URL - delete signature data for system
getSignatures: '/api/signature/getAll', // ajax URL - get all signature data for system
saveSignatureData: '/api/signature/save', // ajax URL - save signature data for system
deleteSignatureData: '/api/signature/delete', // ajax URL - delete signature data for system
// route API
searchRoute: 'api/route/search', // ajax URL - search system routes
searchRoute: '/api/route/search', // ajax URL - search system routes
// stats API
getStatisticsData: 'api/statistic/getData', // ajax URL - get statistics data (activity log)
getStatisticsData: '/api/statistic/getData', // ajax URL - get statistics data (activity log)
// GitHub API
gitHubReleases: 'api/github/releases' // ajax URL - get release info from GitHub
gitHubReleases: '/api/github/releases' // ajax URL - get release info from GitHub
},
breakpoints: [
{ name: 'desktop', width: Infinity },

View File

@@ -119,21 +119,21 @@ define([
columnDefs: [
{
targets: 0,
title: '<i class="fas fa-lg fa-tag"></i>',
title: '<i class="fas fa-tag"></i>',
width: '18px',
searchable: false,
class: ['text-center'].join(' '),
data: 'status'
},{
targets: 1,
title: '<i class="far fa-lg fa-fw fa-clock"></i>&nbsp;&nbsp;',
title: '<i class="far fa-fw fa-clock"></i>&nbsp;&nbsp;',
width: '50px',
searchable: true,
class: 'text-right',
data: 'time'
},{
targets: 2,
title: '<i class="fas fa-lg fa-fw fa-history"></i>&nbsp;&nbsp;',
title: '<i class="fas fa-fw fa-history"></i>&nbsp;&nbsp;',
width: '35px',
searchable: false,
class: 'text-right',

View File

@@ -18,7 +18,7 @@ define([
'dialog/manual',
'dialog/changelog',
'dialog/credit'
], function($, Init, Util, Render, Gallery, bootbox) {
], ($, Init, Util, Render, Gallery, bootbox) => {
'use strict';
@@ -79,7 +79,7 @@ define([
/**
* set link observer for "version info" dialog
*/
let setVersionLinkObserver = function(){
let setVersionLinkObserver = () => {
$('.' + config.navigationVersionLinkClass).off('click').on('click', function(e){
$.fn.changelogsDialog();
});
@@ -94,13 +94,24 @@ define([
adminPanel.css({bottom: ((direction === 'up') ? '+' : '-') + '=35px'});
};
let setAcceptCookie = () => {
Util.setCookie('cookie', 1, config.defaultAcceptCookieExpire, 'd');
};
/**
* set page observer
*/
let setPageObserver = function(){
let setPageObserver = () => {
let ssoButtonElement = $('.' + config.ssoButtonClass);
let cookieHintElement = $('#' + config.cookieHintId);
// cookie hint --------------------------------------------------------
cookieHintElement.find('.btn-success').on('click', function(){
setAcceptCookie();
// confirmation no longer needed on SSO login button
ssoButtonElement.confirmation('destroy');
});
cookieHintElement.on('show.bs.collapse', function () {
// move admin panel upwards (prevents overlapping with cookie notice)
moveAdminPanel('up');
@@ -127,7 +138,7 @@ define([
btnCancelIcon: 'fas fa-fw fa-check',
onCancel: function(e, target){
// "Accept cookies"
Util.setCookie('cookie', 1, config.defaultAcceptCookieExpire, 'd');
setAcceptCookie();
// set "default" href
let href = $(target).data('bs.confirmation').getHref();
@@ -141,13 +152,9 @@ define([
}
};
$('.' + config.ssoButtonClass).confirmation(confirmationSettings);
ssoButtonElement.confirmation(confirmationSettings);
}
cookieHintElement.find('.btn-success').on('click', function(){
Util.setCookie('cookie', 1, config.defaultAcceptCookieExpire, 'd');
});
// manual -------------------------------------------------------------
$('.' + config.navigationLinkManualClass).on('click', function(e){
e.preventDefault();
@@ -179,7 +186,7 @@ define([
/**
* init image carousel
*/
let initCarousel = function(){
let initCarousel = () => {
// check if carousel exists
if($('#' + config.galleryCarouselId).length === 0){
@@ -313,7 +320,7 @@ define([
* get all thumbnail elements
* @returns {*|jQuery|HTMLElement}
*/
let getThumbnailElements = function(){
let getThumbnailElements = () => {
return $('a[data-gallery="#' + config.galleryId + '"]').not('.disabled');
};
@@ -321,14 +328,14 @@ define([
* init gallery for thumbnail elements
* @param newElements
*/
let initGallery = function(newElements){
let initGallery = (newElements) => {
if( newElements.length > 0){
// We have to add ALL thumbnail elements to the gallery!
// -> even those wthat are invisible (not lazyLoaded) now!
// -> This is required for "swipe" through all images
let allThumbLinks = getThumbnailElements();
requirejs(['blueImpGalleryBootstrap'], function() {
requirejs(['blueImpGalleryBootstrap'], () => {
$(newElements).each(function() {
let borderless = false;
@@ -360,7 +367,7 @@ define([
/**
* init "YouTube" video preview
*/
let initYoutube = function(){
let initYoutube = () => {
$('.youtube').each(function() {
// Based on the YouTube ID, we can easily find the thumbnail image
@@ -395,11 +402,11 @@ define([
/**
* init scrollSpy for navigation bar
*/
let initScrollSpy = function(){
let initScrollSpy = () => {
// init scrollspy
// show elements that are currently in the viewport
let showVisibleElements = function(){
let showVisibleElements = () => {
// find all elements that should be animated
let visibleElements = $('.' + config.animateElementClass).isInViewport();
@@ -421,7 +428,7 @@ define([
});
};
$( window ).scroll(function() {
$( window ).scroll(() => {
// check for new visible elements
showVisibleElements();
});
@@ -446,7 +453,7 @@ define([
* get current EVE-Online server status
* -> show "server panel"
*/
let initServerStatus = function(){
let initServerStatus = () => {
$.ajax({
type: 'POST',
url: Init.path.getServerStatus,
@@ -485,16 +492,16 @@ define([
* show "notification panel" to user
* -> checks if panel not already shown
*/
let initNotificationPanel = function(){
let initNotificationPanel = () => {
let storageKey = 'notification_panel';
let currentVersion = Util.getVersion();
let showNotificationPanel = function(){
let showNotificationPanel = () => {
let data = {
version: Util.getVersion()
};
requirejs(['text!templates/ui/notice.html', 'mustache'], function(template, Mustache) {
requirejs(['text!templates/ui/notice.html', 'mustache'], (template, Mustache) => {
let content = Mustache.render(template, data);
let notificationPanel = $('#' + config.notificationPanelId);
@@ -704,7 +711,7 @@ define([
* @param status
* @param error
*/
let handleAjaxErrorResponse = function(jqXHR, status, error){
let handleAjaxErrorResponse = (jqXHR, status, error) => {
let type = status;
let title = 'Status ' + jqXHR.status + ': ' + error;
@@ -734,7 +741,7 @@ define([
/**
* main init "landing" page
*/
$(function(){
$(() => {
// clear sessionStorage
Util.clearSessionStorage();
@@ -816,9 +823,9 @@ define([
initYoutube();
// draw header logo
$('#' + config.logoContainerId).drawLogo(function(){
$('#' + config.logoContainerId).drawLogo(() => {
// init header animation
$('#' + config.headerContainerId).initHeader(function(){
$('#' + config.headerContainerId).initHeader(() => {
});
}, false);

View File

@@ -64,9 +64,6 @@ define([
systemSec: 'pf-system-sec'
};
// active jsPlumb instances currently running
let activeInstances = {};
// active connections per map (cache object)
let connectionCache = {};
@@ -947,35 +944,70 @@ define([
*/
let setMapWrapperObserver = (mapWrapper, mapConfig) => {
/**
* save current map dimension to local storage
* @param entry
*/
let saveMapSize = (entry) => {
let width = '';
let height = '';
if(entry.constructor.name === 'HTMLDivElement'){
width = entry.style.width;
height = entry.style.height;
}else if (entry.constructor.name === 'ResizeObserverEntry'){
width = entry.target.style.width;
height = entry.target.style.height;
}
width = parseInt(width.substring(0, width.length - 2)) || 0;
height = parseInt(height.substring(0, height.length - 2)) || 0;
let promiseStore = MapUtil.getLocaleData('map', mapConfig.config.id );
promiseStore.then((data) => {
let storeData = true;
if (
data && data.style &&
data.style.width === width &&
data.style.height === height
) {
// no style changes
storeData = false;
}
if (storeData) {
MapUtil.storeLocalData('map', mapConfig.config.id, 'style', {
width: width,
height: height
});
}
});
};
// map resize observer ----------------------------------------------------------------------------------------
if(window.ResizeObserver) {
// ResizeObserver() supported
let resizeTimer;
let wrapperResize = new ResizeObserver(entries => { // jshint ignore:line
/**
* save current map dimension to local storage
* @param entry
*/
let saveMapSize = (entry) => {
return setTimeout(() => {
let width = entry.target.style.width;
let height = entry.target.style.height;
width = parseInt( width.substring(0, width.length - 2) ) || 0;
height = parseInt( height.substring(0, height.length - 2) ) || 0;
MapUtil.storeLocalData('map', mapConfig.config.id, 'style', {
width: width,
height: height
});
}, 100);
let checkMapSize = (entry) => {
return setTimeout(saveMapSize, 100, entry);
};
for (let entry of entries){
// use timeout to "throttle" save actions
clearTimeout(resizeTimer);
resizeTimer = saveMapSize(entry);
resizeTimer = checkMapSize(entry);
}
});
wrapperResize.observe(mapWrapper[0]);
}else if(requestAnimationFrame){
// ResizeObserver() not supported
let checkMapSize = (entry) => {
saveMapSize(entry);
return setTimeout(checkMapSize, 500, entry);
};
checkMapSize(mapWrapper[0]);
}
};
@@ -2602,7 +2634,7 @@ define([
*/
let getMapInstance = function(mapId){
if(typeof activeInstances[mapId] !== 'object'){
if( !MapUtil.existsMapInstance(mapId) ){
// create new instance
jsPlumb.Defaults.LogEnabled = true;
@@ -2712,10 +2744,10 @@ define([
return (targetEndpoint.connections.length === 0);
});
activeInstances[mapId] = newJsPlumbInstance;
MapUtil.setMapInstance(mapId, newJsPlumbInstance);
}
return activeInstances[mapId];
return MapUtil.getMapInstance(mapId);
};
/**
@@ -3112,7 +3144,9 @@ define([
// data for header update
let headerUpdateData = {
mapId: userData.config.id,
userCount: 0 // active user on a map
userCountInside: 0, // active user on a map
userCountOutside: 0, // active user NOT on map
userCountInactive: 0 // inactive users (no location)
};
if(
@@ -3152,7 +3186,7 @@ define([
tempUserData = systemData;
// add "user count" to "total map user count"
headerUpdateData.userCount += tempUserData.user.length;
headerUpdateData.userCountInside += tempUserData.user.length;
// remove system from "search" array -> speed up loop
userData.data.systems.splice(j, 1);
@@ -3177,6 +3211,16 @@ define([
system.updateSystemUserData(map, tempUserData, currentUserIsHere);
}
// users who are not in any map system --------------------------------------------------------------------
for(let i = 0; i < userData.data.systems.length; i++){
// users without location are grouped in systemid: 0
if(userData.data.systems[i].id){
headerUpdateData.userCountOutside += userData.data.systems[i].user.length;
}else{
headerUpdateData.userCountInactive += userData.data.systems[i].user.length;
}
}
// trigger document event -> update header
$(document).trigger('pf:updateHeaderMapData', headerUpdateData);
}
@@ -3339,16 +3383,6 @@ define([
return systemData;
};
/**
* removes a map instance from local cache
* @param mapId
*/
let clearMapInstance = (mapId) => {
if(typeof activeInstances[mapId] === 'object'){
delete activeInstances[mapId];
}
};
/**
* init map options
* @param mapConfig
@@ -3513,7 +3547,6 @@ define([
return {
getMapInstance: getMapInstance,
clearMapInstance: clearMapInstance,
loadMap: loadMap,
showNewSystemDialog: showNewSystemDialog
};

View File

@@ -55,6 +55,48 @@ define([
}
};
// active jsPlumb instances currently running =====================================================================
let activeInstances = {};
/**
* set mapInstance
* @param mapId
* @param map
*/
let setMapInstance = (mapId, map) => {
activeInstances[mapId] = map;
};
/**
* get mapInstance
* @param mapId
* @returns {*}
*/
let getMapInstance = (mapId) => {
return activeInstances[mapId];
};
/**
* check for mapInstance is set
* @param mapId
* @returns {boolean}
*/
let existsMapInstance = (mapId) => {
return typeof activeInstances[mapId] === 'object';
};
/**
* removes a map instance
* @param mapId
*/
let clearMapInstance = (mapId) => {
if(existsMapInstance(mapId)){
delete activeInstances[mapId];
}
};
// ================================================================================================================
/**
* get all available map Types
* optional they can be filtered by current access level of a user
@@ -162,6 +204,28 @@ define([
return systemInfo;
};
/**
* get system data by mapId and systemid
* @param mapId
* @param systemId
* @returns {boolean}
*/
let getSystemData = (mapId, systemId) => {
let systemData = false;
let mapData = Util.getCurrentMapData(mapId);
if(mapData){
for(let j = 0; j < mapData.data.systems.length; j++){
let systemDataTemp = mapData.data.systems[j];
if(systemDataTemp.id === systemId){
systemData = systemDataTemp;
break;
}
}
}
return systemData;
};
/**
* get system type information
* @param {number} systemTypeId
@@ -582,6 +646,19 @@ define([
return connectionInfo;
};
/**
* get CSS classes for connection types
* @param types
* @returns {string[]}
*/
let getConnectionFakeClassesByTypes = (types) => {
let connectionClasses = ['pf-fake-connection'];
for(let i = 0; i < types.length; i++){
connectionClasses.push(getConnectionInfo( types[i], 'cssClass'));
}
return connectionClasses;
};
/**
* get all direct connections between two given systems
* @param map
@@ -1039,15 +1116,32 @@ define([
return hasAccess;
};
/**
* get a unique map url for deeplinking
* @param mapId
* @returns {string}
*/
let getMapDeeplinkUrl = (mapId) => {
let url = location.protocol + '//' + location.host + '/map';
url += mapId ? '/' + encodeURIComponent(window.btoa(mapId)) : '';
return url;
};
return {
config: config,
mapOptions: mapOptions,
setMapInstance: setMapInstance,
getMapInstance: getMapInstance,
existsMapInstance: existsMapInstance,
clearMapInstance: clearMapInstance,
getMapTypes: getMapTypes,
getMapScopes: getMapScopes,
getScopeInfoForMap: getScopeInfoForMap,
getMapIcons: getMapIcons,
getInfoForMap: getInfoForMap,
getInfoForSystem: getInfoForSystem,
getSystemData: getSystemData,
getSystemTypeInfo: getSystemTypeInfo,
getEffectInfoForSystem: getEffectInfoForSystem,
toggleSelectSystem: toggleSelectSystem,
@@ -1059,6 +1153,7 @@ define([
searchConnectionsBySystems: searchConnectionsBySystems,
searchConnectionsByScopeAndType: searchConnectionsByScopeAndType,
getConnectionInfo: getConnectionInfo,
getConnectionFakeClassesByTypes: getConnectionFakeClassesByTypes,
checkForConnection: checkForConnection,
getDefaultConnectionTypeByScope: getDefaultConnectionTypeByScope,
setConnectionWHStatus: setConnectionWHStatus,
@@ -1073,6 +1168,7 @@ define([
storeLocalData: storeLocalData,
deleteLocalData: deleteLocalData,
getSystemId: getSystemId,
checkRight: checkRight
checkRight: checkRight,
getMapDeeplinkUrl: getMapDeeplinkUrl
};
});

View File

@@ -44,164 +44,292 @@ define([
let mapModule = $('#' + Util.config.mapModuleId);
// map init load static data =======================================================
$.getJSON( Init.path.initMap, (initData) => {
// main update intervals/trigger (heartbeat)
let updateTimeouts = {
mapUpdate: 0,
userUpdate: 0
};
if( initData.error.length > 0 ){
for(let i = 0; i < initData.error.length; i++){
Util.showNotify({
title: initData.error[i].title,
text: initData.error[i].message,
type: initData.error[i].type
});
/**
* clear both main update timeouts
* -> stop program from working -> shutdown
*/
let clearUpdateTimeouts = () => {
for(let intervalKey in updateTimeouts) {
if(updateTimeouts.hasOwnProperty(intervalKey)){
clearTimeout(updateTimeouts[intervalKey]);
}
}
};
Init.timer = initData.timer;
Init.mapTypes = initData.mapTypes;
Init.mapScopes = initData.mapScopes;
Init.connectionScopes = initData.connectionScopes;
Init.systemStatus = initData.systemStatus;
Init.systemType = initData.systemType;
Init.wormholes = initData.wormholes;
Init.characterStatus = initData.characterStatus;
Init.routes = initData.routes;
Init.url = initData.url;
Init.slack = initData.slack;
Init.discord = initData.discord;
Init.routeSearch = initData.routeSearch;
Init.programMode = initData.programMode;
/**
* Ajax error response handler function for main-ping functions
* @param jqXHR
* @param status
* @param error
*/
let handleAjaxErrorResponse = (jqXHR, status, error) => {
// clear both main update request trigger timer
clearUpdateTimeouts();
// init tab change observer, Once the timers are available
Page.initTabChangeObserver();
let reason = status + ' ' + jqXHR.status + ': ' + error;
let errorData = [];
// init map module
mapModule.initMapModule();
// load info (maintenance) info panel (if scheduled)
if(Init.programMode.maintenance){
$('body').showGlobalInfoPanel();
if(jqXHR.responseJSON){
// handle JSON
let errorObj = jqXHR.responseJSON;
if(
errorObj.error &&
errorObj.error.length > 0
){
errorData = errorObj.error;
}
}else{
// handle HTML
errorData.push({
type: 'error',
message: 'Please restart and reload this page'
});
}
}).fail(( jqXHR, status, error) => {
let reason = status + ' ' + jqXHR.status + ': ' + error;
console.error(' ↪ %s Error response: %o', jqXHR.url, errorData);
$(document).trigger('pf:shutdown', {status: jqXHR.status, reason: reason, error: errorData});
};
$(document).trigger('pf:shutdown', {status: jqXHR.status, reason: reason});
});
// map init functions =========================================================================================
/**
* get static init data and store response
* @returns {Promise<any>}
*/
let initData = () => {
let initDataExecutor = (resolve, reject) => {
$.getJSON(Init.path.initData).done(response => {
if( response.error.length > 0 ){
for(let i = 0; i < response.error.length; i++){
Util.showNotify({
title: response.error[i].title,
text: response.error[i].message,
type: response.error[i].type
});
}
}
Init.timer = response.timer;
Init.mapTypes = response.mapTypes;
Init.mapScopes = response.mapScopes;
Init.connectionScopes = response.connectionScopes;
Init.systemStatus = response.systemStatus;
Init.systemType = response.systemType;
Init.wormholes = response.wormholes;
Init.characterStatus = response.characterStatus;
Init.routes = response.routes;
Init.url = response.url;
Init.slack = response.slack;
Init.discord = response.discord;
Init.routeSearch = response.routeSearch;
Init.programMode = response.programMode;
resolve({
action: 'initData',
data: false
});
}).fail((jqXHR, status, error) => {
reject({
action: 'shutdown',
data: {
jqXHR: jqXHR,
status: status,
error: error
}
});
});
};
return new Promise(initDataExecutor);
};
/**
* get mapAccess Data for WebSocket subscription
* @returns {Promise<any>}
*/
let getMapAccessData = () => {
let getMapAccessDataExecutor = (resolve, reject) => {
$.getJSON(Init.path.getAccessData).done(response => {
resolve({
action: 'mapAccessData',
data: response
});
}).fail((jqXHR, status, error) => {
reject({
action: 'shutdown',
data: {
jqXHR: jqXHR,
status: status,
error: error
}
});
});
};
return new Promise(getMapAccessDataExecutor);
};
/**
* init main mapModule
* -> initData() needs to be resolved first!
* @param payload
* @returns {Promise<any>}
*/
let initMapModule = (payload) => {
let initMapModuleExecutor = (resolve, reject) => {
// init tab change observer, Once the timers are available
Page.initTabChangeObserver();
// init map module
mapModule.initMapModule() ;
// load info (maintenance) info panel (if scheduled)
if(Init.programMode.maintenance){
$('body').showGlobalInfoPanel();
}
resolve({
action: 'initMapModule',
data: false
});
};
return new Promise(initMapModuleExecutor);
};
/**
* request all map access data (tokens) -> required wor WebSocket subscription
* -> initData() needs to be resolved first!
* @param payloadMapAccessData
* @returns {Promise<any>}
*/
let getMapAccessData = () => {
$.getJSON( Init.path.getAccessData, ( response ) => {
if(response.status === 'OK'){
// init SharedWorker for maps
MapWorker.init({
characterId: response.data.id,
callbacks: {
onInit: (MsgWorkerMessage) => {
Util.setSyncStatus(MsgWorkerMessage.command);
},
onOpen: (MsgWorkerMessage) => {
Util.setSyncStatus(MsgWorkerMessage.command, MsgWorkerMessage.meta());
let initMapWorker = (payloadMapAccessData) => {
MapWorker.send( 'subscribe', response.data);
},
onGet: (MsgWorkerMessage) => {
switch(MsgWorkerMessage.task()){
case 'mapUpdate':
Util.updateCurrentMapData( MsgWorkerMessage.data() );
ModuleMap.updateMapModule(mapModule);
break;
case 'mapAccess':
case 'mapDeleted':
Util.deleteCurrentMapData( MsgWorkerMessage.data() );
ModuleMap.updateMapModule(mapModule);
break;
}
Util.setSyncStatus('ws:get');
},
onClosed: (MsgWorkerMessage) => {
Util.setSyncStatus(MsgWorkerMessage.command, MsgWorkerMessage.meta());
},
onError: (MsgWorkerMessage) => {
Util.setSyncStatus(MsgWorkerMessage.command, MsgWorkerMessage.meta());
}
let initMapWorkerExecutor = (resolve, reject) => {
let getPayload = (command) => {
return {
action: 'initMapWorker',
data: {
syncStatus: Init.syncStatus.type,
command: command
}
});
};
};
let validMapAccessData = false;
if(payloadMapAccessData && payloadMapAccessData.action === 'mapAccessData'){
let response = payloadMapAccessData.data;
if(response.status === 'OK'){
validMapAccessData = true;
// init SharedWorker for maps
MapWorker.init({
characterId: response.data.id,
callbacks: {
onInit: (MsgWorkerMessage) => {
Util.setSyncStatus(MsgWorkerMessage.command);
},
onOpen: (MsgWorkerMessage) => {
Util.setSyncStatus(MsgWorkerMessage.command, MsgWorkerMessage.meta());
MapWorker.send( 'subscribe', response.data);
resolve(getPayload(MsgWorkerMessage.command));
},
onGet: (MsgWorkerMessage) => {
switch(MsgWorkerMessage.task()){
case 'mapUpdate':
Util.updateCurrentMapData( MsgWorkerMessage.data() );
ModuleMap.updateMapModule(mapModule);
break;
case 'mapAccess':
case 'mapDeleted':
Util.deleteCurrentMapData( MsgWorkerMessage.data() );
ModuleMap.updateMapModule(mapModule);
break;
case 'mapSubscriptions':
Util.updateCurrentMapUserData(MsgWorkerMessage.data());
ModuleMap.updateActiveMapUserData(mapModule);
break;
}
Util.setSyncStatus('ws:get');
},
onClosed: (MsgWorkerMessage) => {
Util.setSyncStatus(MsgWorkerMessage.command, MsgWorkerMessage.meta());
reject(getPayload(MsgWorkerMessage.command));
},
onError: (MsgWorkerMessage) => {
Util.setSyncStatus(MsgWorkerMessage.command, MsgWorkerMessage.meta());
reject(getPayload(MsgWorkerMessage.command));
}
}
});
}
}
});
if( !validMapAccessData ){
reject(getPayload('Invalid mapAccessData'));
}
};
return new Promise(initMapWorkerExecutor);
};
getMapAccessData();
// run all init functions for mainModule and WebSocket configuration async
Promise.all([initData(), getMapAccessData()])
.then(payload => Promise.all([initMapModule(payload[0]), initMapWorker(payload[1])]))
.then(payload => {
// mapModule initialized and WebSocket configuration working
console.info('%s() complete! command: "%s"; syncStatus: "%s"',
payload[1].action,
payload[1].data.command,
payload[1].data.syncStatus
);
})
.catch(payload => {
switch(payload.action){
case 'shutdown':
// ajax error
handleAjaxErrorResponse(payload.data.jqXHR, payload.data.status, payload.data.error);
break;
case 'initMapWorker':
// WebSocket not working -> no error here -> fallback to Ajax
console.warn('%s() rejects Promise. command: "%s"; syncStatus: "%s", payload: %o',
payload.action,
payload.data.command,
payload.data.syncStatus,
payload.data
);
break;
default:
console.error('Unhandled error thrown while initialization: %o ', payload);
}
});
/**
* main function for init all map relevant trigger calls
*/
$.fn.initMapModule = function(){
let mapModule = $(this);
// log keys ------------------------------------------------------------------------
// log keys -----------------------------------------------------------------------------------------------
let logKeyServerMapData = Init.performanceLogging.keyServerMapData;
let logKeyServerUserData = Init.performanceLogging.keyServerUserData;
// main update intervals/trigger (heartbeat)
let updateTimeouts = {
mapUpdate: 0,
userUpdate: 0
};
let locationToggle = $('#' + Util.config.headMapTrackingId);
/**
* Ajax error response handler function for main-ping functions
* @param jqXHR
* @param status
* @param error
*/
let handleAjaxErrorResponse = (jqXHR, status, error) => {
// clear both main update request trigger timer
clearUpdateTimeouts();
let reason = status + ' ' + jqXHR.status + ': ' + error;
let errorData = [];
if(jqXHR.responseJSON){
// handle JSON
let errorObj = jqXHR.responseJSON;
if(
errorObj.error &&
errorObj.error.length > 0
){
errorData = errorObj.error;
}
}else{
// handle HTML
errorData.push({
type: 'error',
message: 'Please restart and reload this page'
});
}
$(document).trigger('pf:shutdown', {status: jqXHR.status, reason: reason, error: errorData});
};
/**
* init (schedule) next MapUpdate Ping
*/
let initMapUpdatePing = (forceUpdateMapData) => {
// get the current update delay (this can change if a user is inactive)
let delay = Util.getCurrentTriggerDelay( logKeyServerMapData, 0 );
updateTimeouts.mapUpdate = setTimeout((forceUpdateMapData) => {
triggerMapUpdatePing(forceUpdateMapData);
}, delay, forceUpdateMapData);
};
// ping for main map update ========================================================
// ping for main map update ===============================================================================
/**
* @param forceUpdateMapData // force request to be send
*/
@@ -218,7 +346,7 @@ define([
// get updated map data
let updatedMapData = {
mapData: mapModule.getMapModuleDataForUpdate(),
getUserData: ( Util.getCurrentUserData() ) ? 0 : 1
getUserData: Util.getCurrentUserData() ? 0 : 1
};
// check if mapUpdate trigger should be send
@@ -267,7 +395,7 @@ define([
// load/update main map module
ModuleMap.updateMapModule(mapModule).then(() => {
// map update done, init new trigger
// map update done, init new trigger
// get the current update delay (this can change if a user is inactive)
let mapUpdateDelay = Util.getCurrentTriggerDelay( logKeyServerMapData, 0 );
@@ -293,7 +421,7 @@ define([
}
};
// ping for user data update =======================================================
// ping for user data update ==============================================================================
let triggerUserUpdatePing = () => {
// IMPORTANT: Get user data for ONE map that is currently visible
@@ -307,10 +435,9 @@ define([
let updatedUserData = {
mapIds: mapIds,
systemData: Util.getCurrentSystemData(),
characterMapData: {
mapTracking: (locationToggle.is(':checked') ? 1 : 0) // location tracking
}
getMapUserData: Util.getSyncType() === 'webSocket' ? 0 : 1,
mapTracking: locationToggle.is(':checked') ? 1 : 0, // location tracking
systemData: Util.getCurrentSystemData()
};
Util.timeStart(logKeyServerUserData);
@@ -321,62 +448,66 @@ define([
data: updatedUserData,
dataType: 'json'
}).done((data) => {
// log request time
let duration = Util.timeStop(logKeyServerUserData);
Util.log(logKeyServerUserData, {duration: duration, type: 'server', description:'request user data'});
if(data.error.length > 0){
if(
data.error &&
data.error.length > 0
){
// any error in the main trigger functions result in a user log-off
$(document).trigger('pf:menuLogout');
}else{
$(document).setProgramStatus('online');
if(data.userData !== undefined){
// store current user data global (cache)
let userData = Util.setCurrentUserData(data.userData);
// store current map user data (cache)
if(data.mapUserData !== undefined){
Util.setCurrentMapUserData(data.mapUserData);
}
// active character data found
mapModule.updateMapModuleData();
// update system info panels
if(data.system){
mapModule.updateSystemModuleData(data.system);
}
// get the current update delay (this can change if a user is inactive)
let mapUserUpdateDelay = Util.getCurrentTriggerDelay( logKeyServerUserData, 0 );
// init new trigger
updateTimeouts.userUpdate = setTimeout(() => {
triggerUserUpdatePing();
}, mapUserUpdateDelay);
// store current map user data (cache)
if(data.mapUserData !== undefined){
Util.setCurrentMapUserData(data.mapUserData);
}
// update map module character data
ModuleMap.updateActiveMapUserData(mapModule).then(() => {
// map module update done, init new trigger
initMapUserUpdatePing();
});
}
}
}).fail(handleAjaxErrorResponse);
};
/**
* init (schedule) next MapUpdate Ping
*/
let initMapUpdatePing = (forceUpdateMapData) => {
// get the current update delay (this can change if a user is inactive)
let delay = Util.getCurrentTriggerDelay(logKeyServerMapData, 0);
updateTimeouts.mapUpdate = setTimeout((forceUpdateMapData) => {
triggerMapUpdatePing(forceUpdateMapData);
}, delay, forceUpdateMapData);
};
/**
* clear both main update timeouts
* -> stop program from working -> shutdown
* init (schedule) next MapUserUpdate Ping
*/
let clearUpdateTimeouts = () => {
for(let intervalKey in updateTimeouts) {
let initMapUserUpdatePing = () => {
// get the current update delay (this can change if a user is inactive)
let delay = Util.getCurrentTriggerDelay(logKeyServerUserData, 0);
if(updateTimeouts.hasOwnProperty(intervalKey)){
clearTimeout( updateTimeouts[intervalKey] );
}
}
updateTimeouts.userUpdate = setTimeout(() => {
triggerUserUpdatePing();
}, delay);
};
// initial start of the map update function

View File

@@ -298,39 +298,55 @@ define([
};
/**
* updates only visible/active map module
* @returns {boolean}
* updates current visible/active mapElement in mapModuleElement with user data
* @param mapModuleElement
* @returns {Promise<any>}
*/
$.fn.updateMapModuleData = function(){
let mapModule = $(this);
let updateActiveMapUserData = (mapModuleElement) => {
let updateActiveMapModuleExecutor = (resolve, reject) => {
// get all active map elements for module
let mapElement = mapModuleElement.getActiveMap();
updateMapUserData(mapElement).then(payload => resolve());
};
return new Promise(updateActiveMapModuleExecutor);
};
/**
* updates mapElement with user data
* update
* @param mapElement
* @returns {Promise<any>}
*/
let updateMapUserData = (mapElement) => {
// performance logging (time measurement)
let logKeyClientUserData = Init.performanceLogging.keyClientUserData;
Util.timeStart(logKeyClientUserData);
// get all active map elements for module
let mapElement = mapModule.getActiveMap();
let updateMapUserDataExecutor = (resolve, reject) => {
if(mapElement !== false){
let mapId = mapElement.data('id');
let currentMapUserData = Util.getCurrentMapUserData(mapId);
if(mapElement !== false){
let mapId = mapElement.data('id');
if(currentMapUserData){
// trigger "update local" for this map => async
mapElement.trigger('pf:updateLocal', currentMapUserData);
let currentMapUserData = Util.getCurrentMapUserData(mapId);
if(currentMapUserData){
// trigger "update local" for this map => async
mapElement.trigger('pf:updateLocal', currentMapUserData);
// update map with current user data
mapElement.updateUserData(currentMapUserData);
// update map with current user data
mapElement.updateUserData(currentMapUserData);
}
}
}
// log client user data update time
let duration = Util.timeStop(logKeyClientUserData);
Util.log(logKeyClientUserData, {duration: duration, type: 'client', description:'update users'});
resolve();
};
return true;
return new Promise(updateMapUserDataExecutor).then(payload => {
// log client map update time
let duration = Util.timeStop(logKeyClientUserData);
Util.log(logKeyClientUserData, {duration: duration, type: 'client', description: 'update users'});
});
};
/**
@@ -628,6 +644,15 @@ define([
let mapElement = $(mapConfig.map.getContainer());
let mapWrapperElement = mapElement.closest('.mCustomScrollbar');
mapWrapperElement.mCustomScrollbar('update');
// change url to unique map URL
if (history.pushState) {
let mapUrl = MapUtil.getMapDeeplinkUrl(mapConfig.config.id);
history.pushState({}, '', mapUrl);
}
// update map user data (do not wait until next update is triggered)
updateMapUserData(mapElement);
});
}
});
@@ -899,7 +924,7 @@ define([
contentElement.remove();
// remove map instance from local cache
Map.clearMapInstance(mapId);
MapUtil.clearMapInstance(mapId);
}
resolve({
@@ -937,6 +962,15 @@ define([
return Promise.all(promiseDeleteTab);
};
/**
* get last URL segment e.g. https://pathfinder/map/test -> test
* @returns {string | undefined}
*/
let getLastUrlSegment = () => {
let parts = window.location.pathname.split('/');
return parts.pop() || parts.pop();
};
/**
* set "default" map tab
* -> default mapId might be available in local storage
@@ -946,6 +980,10 @@ define([
*/
let showDefaultTab = (tabMapElement, currentUserData) => {
let getActiveTabLinkElement = (mapId) => {
return tabMapElement.find('.' + config.mapTabClass + '[data-mapid="' + mapId + '"] > a');
};
/**
* show default tab promise
* @param resolve
@@ -956,13 +994,31 @@ define([
promiseStore.then((data) => {
let activeTabLinkElement = false;
if(data && data.defaultMapId){
// make specific map tab active
activeTabLinkElement = tabMapElement.find('.' + config.mapTabClass + '[data-mapid="' + data.defaultMapId + '"] > a');
// check for existing mapId URL identifier ------------------------------------------------------------
let lastURLSegment = getLastUrlSegment();
let defaultMapId = 0;
try{
defaultMapId = parseInt(atob(decodeURIComponent(lastURLSegment)));
}catch(e){
// defaultMapID could not be extracted from URL -> ignore
}
if(defaultMapId){
activeTabLinkElement = getActiveTabLinkElement(defaultMapId);
}
// ... else check for existing cached default mapId ---------------------------------------------------
if(
(!activeTabLinkElement || !activeTabLinkElement.length) &&
data && data.defaultMapId
){
// make specific map tab active
activeTabLinkElement = getActiveTabLinkElement(data.defaultMapId);
}
// ... else make first map tab active (default) -------------------------------------------------------
if(!activeTabLinkElement || !activeTabLinkElement.length){
// make first map tab active (default)
activeTabLinkElement = tabMapElement.find('.' + config.mapTabClass + ':not(.pull-right):first > a');
}
@@ -1152,6 +1208,7 @@ define([
return {
updateTabData: updateTabData,
updateMapModule: updateMapModule
updateMapModule: updateMapModule,
updateActiveMapUserData: updateActiveMapUserData
};
});

View File

@@ -63,6 +63,7 @@ define([
// menu
menuHeadMenuLogoClass: 'pf-head-menu-logo', // class for main menu logo
menuClockClass: 'pf-menu-clock', // class for EVE-Time clock
// helper element
dynamicElementWrapperId: 'pf-dialog-wrapper',
@@ -290,6 +291,10 @@ define([
).on('click', function(){
$(document).triggerMenuEvent('Logout', {clearCookies: 1});
})
).append(
$('<div>', {
class: config.menuClockClass
})
)
);
@@ -477,7 +482,7 @@ define([
});
// active pilots
$('.' + config.headActiveUserClass).find('a').on('click', function(){
$('.' + config.headActiveUserClass).on('click', function(){
$(document).triggerMenuEvent('ShowMapInfo', {tab: 'activity'});
});
@@ -713,7 +718,9 @@ define([
documentElement.on('pf:updateHeaderMapData', function(e, data){
let activeMap = Util.getMapModule().getActiveMap();
let userCount = 0;
let userCountInside = 0;
let userCountOutside = 0;
let userCountInactive = 0;
let currentLocationData = {};
// show active user just for the current active map
@@ -721,10 +728,12 @@ define([
activeMap &&
activeMap.data('id') === data.mapId
){
userCount = data.userCount;
userCountInside = data.userCountInside;
userCountOutside = data.userCountOutside;
userCountInactive = data.userCountInactive;
currentLocationData = data;
}
updateHeaderActiveUserCount(userCount);
updateHeaderActiveUserCount(userCountInside, userCountOutside, userCountInactive);
updateHeaderCurrentLocation(currentLocationData);
});
@@ -787,6 +796,29 @@ define([
return false;
});
initEveClock();
};
/**
* init clock element with current EVE time
*/
let initEveClock = () => {
let clockElement = $('.' + config.menuClockClass);
let checkTime = (i) => {
return (i < 10) ? '0' + i : i;
};
let startTime = () => {
let date = Util.getServerTime();
let h = date.getHours();
let m = checkTime(date.getMinutes());
clockElement.text(h + ':' + m);
let t = setTimeout(startTime, 500);
};
startTime();
};
/**
@@ -953,23 +985,35 @@ define([
/**
* update the "active user" badge in header
* @param userCount
* @param userCountInside
* @param userCountOutside
* @param userCountInactive
*/
let updateHeaderActiveUserCount = function(userCount){
let updateHeaderActiveUserCount = (userCountInside, userCountOutside, userCountInactive) => {
let activeUserElement = $('.' + config.headActiveUserClass);
let badge = activeUserElement.find('.badge');
if(badge.data('userCount') !== userCount){
badge.data('userCount', userCount);
let updateCount = (badge, count) => {
let changed = false;
if(badge.data('userCount') !== count){
changed = true;
badge.data('userCount', count);
badge.text(count);
badge.text(userCount);
badge.toggleClass('txt-color-greenLight', (userCount > 0) );
badge.toggleClass('txt-color-red', (userCount === 0) );
if(! activeUserElement.is(':visible')){
activeUserElement.velocity('fadeIn', {duration: Init.animationSpeed.headerLink});
badge.toggleClass(badge.attr('data-on'), (count > 0) );
badge.toggleClass(badge.attr('data-off'), (count === 0) );
}
return changed;
};
let changedInside = updateCount(activeUserElement.find('.badge[data-type="inside"]'), userCountInside);
let changedOutside = updateCount(activeUserElement.find('.badge[data-type="outside"]'), userCountOutside);
let changedInactive = updateCount(activeUserElement.find('.badge[data-type="inactive"]'), userCountInactive);
if(
(changedInactive || changedOutside || changedInactive) &&
!activeUserElement.is(':visible')
){
activeUserElement.velocity('fadeIn', {duration: Init.animationSpeed.headerLink});
}
};

View File

@@ -124,15 +124,6 @@ define([
// connection scope -----------------------------------------------------------------------
let scopeLabel = MapUtil.getScopeInfoForConnection(connectionData.scope, 'label');
// connection type (dummy) classes --------------------------------------------------------
let getConnectionClasses = (types) => {
let connectionClasses = ['pf-fake-connection'];
for(let i = 0; i < types.length; i++){
connectionClasses.push( MapUtil.getConnectionInfo( types[i], 'cssClass') );
}
return connectionClasses;
};
let element = $('<div>', {
class: 'pf-dynamic-area'
}).append(
@@ -199,7 +190,7 @@ define([
class: ['text-right', config.connectionInfoTableCellConnectionClass].join(' ')
}).append(
$('<div>', {
class: getConnectionClasses(connectionData.type).join(' ')
class: MapUtil.getConnectionFakeClassesByTypes(connectionData.type).join(' ')
})
)
),
@@ -382,7 +373,7 @@ define([
}
// get connection type (show fake connection div) ---------------------------------
connectionCell.find('div').removeClass().addClass(getConnectionClasses(connectionData.type).join(' '));
connectionCell.find('div').removeClass().addClass(MapUtil.getConnectionFakeClassesByTypes(connectionData.type).join(' '));
// get wormhole status ------------------------------------------------------------
if(connectionData.type.indexOf('wh_critical') !== -1){

View File

@@ -67,6 +67,7 @@ define([
lengthMenu: 'Show _MENU_ wormholes',
info: 'Showing _START_ to _END_ of _TOTAL_ wormholes'
},
columnDefs: []
});
$(this).find('.' + config.wormholeInfoJumpTableClass).DataTable({
@@ -83,6 +84,7 @@ define([
lengthMenu: 'Show _MENU_ wormholes',
info: 'Showing _START_ to _END_ of _TOTAL_ wormholes'
},
columnDefs: []
});
});

View File

@@ -41,6 +41,10 @@ define([
tableCellCounterClass: 'pf-table-counter-cell', // class for table "counter" cells
tableCellEllipsisClass: 'pf-table-cell-ellipses-auto', // class for table "ellipsis" cells
tableCellActionIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content)
tableCellUnknownDataClass: 'pf-table-unknown-cell', // class for table "unknown" cells
textActionIconClass: 'pf-module-icon-button', // class for text action
textActionIconCopyClass: 'pf-module-icon-button-copy', // class for text action "copy"
loadingOptions: { // config for loading overlay
icon: {
@@ -79,6 +83,107 @@ define([
return icon.length ? '<i class="fas fa-fw ' + icon + ' ' + config.tableCellActionIconClass + '" title="' + type + '" data-toggle="tooltip"></i>' : '';
};
/**
* get label for "unknown" label
* @returns {string}
*/
let getLabelForUnknownData = () => {
return '<span class="' + config.tableCellUnknownDataClass + '">unknown</span>';
};
/**
* write clipboard text
* @param text
* @returns {Promise<any>}
*/
let copyToClipboard = (text) => {
let copyToClipboardExecutor = (resolve, reject) => {
let payload = {
action: 'copyToClipboard',
data: false
};
if (navigator.clipboard) {
// get current permission status
navigator.permissions.query({
name: 'clipboard-write'
}).then(permissionStatus => {
// will be 'granted', 'denied' or 'prompt'
if(
permissionStatus.state === 'granted' ||
permissionStatus.state === 'prompt'
){
navigator.clipboard.writeText(text)
.then(() => {
payload.data = true;
resolve(payload); })
.catch(err => {
let errorMsg = 'Failed to write clipboard content';
console.error(errorMsg, err);
Util.showNotify({title: 'Clipboard API', text: errorMsg, type: 'error'});
resolve(payload);
});
}else{
Util.showNotify({title: 'Clipboard API', text: 'You denied write access', type: 'warning'});
resolve(payload);
}
});
} else {
console.warn('Clipboard API not supported by your browser');
resolve(payload);
}
};
return new Promise(copyToClipboardExecutor);
};
/**
* read clipboard text
* @returns {Promise<any>}
*/
let readFromClipboard = () => {
let readFromClipboardExecutor = (resolve, reject) => {
let payload = {
action: 'readFromClipboard',
data: false
};
if (navigator.clipboard) {
// get current permission status
navigator.permissions.query({
name: 'clipboard-read'
}).then(permissionStatus => {
// will be 'granted', 'denied' or 'prompt'
if(
permissionStatus.state === 'granted' ||
permissionStatus.state === 'prompt'
){
navigator.clipboard.readText()
.then(text => {
payload.data = text;
resolve(payload); })
.catch(err => {
let errorMsg = 'Failed to read clipboard content';
console.error(errorMsg, err);
Util.showNotify({title: 'Clipboard API', text: errorMsg, type: 'error'});
resolve(payload);
});
}else{
Util.showNotify({title: 'Clipboard API', text: 'You denied read access', type: 'warning'});
resolve(payload);
}
});
} else {
console.warn('Clipboard API not supported by your browser');
resolve(payload);
}
};
return new Promise(readFromClipboardExecutor);
};
/**
* loads the map info data into an element
* @param mapData
@@ -115,58 +220,82 @@ define([
class: 'dl-horizontal',
css: {'float': 'left'}
}).append(
$('<dt>').text( 'Icon' )
$('<dt>').text('Icon')
).append(
$('<dd>').append(
$('<i>', {
class: ['fas', 'fa-fw', mapData.config.icon].join(' ')
})
)
).append(
$('<dt>').text('Name')
).append(
$('<dd>').text(mapData.config.name)
).append(
$('<dt>').text('Type')
).append(
$('<dd>', {
class: mapType.class
}).text(mapType.name)
).append(
$('<dt>').text('Link')
).append(
$('<dd>', {
class: [config.textActionIconClass, config.textActionIconCopyClass].join(' ')
}).append(
$('<span>', {
title: 'copy to clipboard',
}).text(MapUtil.getMapDeeplinkUrl(mapData.config.id) + ' ')
).append(
$('<dd>').append(
$('<i>', {
class: ['fas', 'fa-fw', mapData.config.icon].join(' ')
})
)
).append(
$('<dt>').text( 'Name' )
).append(
$('<dd>').text( mapData.config.name )
).append(
$('<dt>').text( 'Type' )
).append(
$('<dd>', {
class: mapType.class
}).text( mapType.name )
);
$('<i>', {
class: ['fas', 'fa-fw', 'fa-copy'].join(' ')
})
)
);
mapElement.append(dlElementLeft);
let dlElementRight = $('<dl>', {
class: 'dl-horizontal',
css: {'float': 'right'}
}).append(
$('<dt>').text( 'Systems' )
).append(
$('<dd>', {
class: ['txt-color', maxSystemsClass].join(' ')
}).text( countSystems + ' / ' + mapType.defaultConfig.max_systems )
).append(
$('<dt>').text( 'Connections' )
).append(
$('<dd>').text( countConnections )
).append(
$('<dt>').text( 'Lifetime' )
).append(
$('<dd>', {
class: config.mapInfoLifetimeCounterClass,
text: mapData.config.created
})
).append(
$('<dt>').text( 'Created' )
).append(
$('<dd>').text(Util.getObjVal(mapDataOrigin, 'config.created.character.name'))
);
}).append(
$('<dt>').text('Systems')
).append(
$('<dd>', {
class: ['txt-color', maxSystemsClass].join(' ')
}).text(countSystems + ' / ' + mapType.defaultConfig.max_systems)
).append(
$('<dt>').text('Connections')
).append(
$('<dd>').text(countConnections)
).append(
$('<dt>').text('Lifetime')
).append(
$('<dd>', {
class: config.mapInfoLifetimeCounterClass,
text: mapData.config.created
})
).append(
$('<dt>').text('Created')
).append(
$('<dd>').text(Util.getObjVal(mapDataOrigin, 'config.created.character.name'))
);
mapElement.append(dlElementRight);
// init map lifetime counter
$('.' + config.mapInfoLifetimeCounterClass).initTimestampCounter();
mapElement.find('.' + config.textActionIconCopyClass).on('click', function(){
let mapUrl = $(this).find('span').text().trim();
copyToClipboard(mapUrl).then(payload => {
if(payload.data){
Util.showNotify({title: 'Copied to clipbaord', text: mapUrl, type: 'success'});
}
});
});
mapElement.initTooltips();
mapElement.hideLoadingAnimation();
};
@@ -387,7 +516,7 @@ define([
title: 'region',
data: 'region'
},{
title: '<i class="far fa-square fa-lg" title="system&nbsp;status" data-toggle="tooltip"></i>',
title: '<i class="far fa-square" title="system&nbsp;status" data-toggle="tooltip"></i>',
width: '12px',
searchable: false,
data: 'status',
@@ -396,7 +525,7 @@ define([
sort: 'status_sort'
}
},{
title: '<i class="fas fa-square fa-lg" title="system&nbsp;effect" data-toggle="tooltip"></i>',
title: '<i class="fas fa-square" title="system&nbsp;effect" data-toggle="tooltip"></i>',
width: '12px',
className: 'text-center',
searchable: false,
@@ -410,7 +539,7 @@ define([
width: '30px',
data: 'static'
},{
title: '<i class="fas fa-map-marker-alt fa-lg" title="your&nbsp;position" data-toggle="tooltip"></i>',
title: '<i class="fas fa-map-marker-alt" title="your&nbsp;position" data-toggle="tooltip"></i>',
width: '8px',
searchable: false,
data: 'position',
@@ -419,13 +548,13 @@ define([
sort: 'position_sort'
}
},{
title: '<i class="fas fa-plane fa-lg" title="active&nbsp;pilots" data-toggle="tooltip"></i>',
title: '<i class="fas fa-plane" title="active&nbsp;pilots" data-toggle="tooltip"></i>',
width: '12px',
className: 'text-center',
searchable: false,
data: 'userCount'
},{
title: '<i class="fas fa-lock fa-lg" title="system&nbsp;locked" data-toggle="tooltip"></i>',
title: '<i class="fas fa-lock" title="system&nbsp;locked" data-toggle="tooltip"></i>',
width: '10px',
searchable: false,
data: 'locked',
@@ -538,13 +667,8 @@ define([
};
// connection
let connectionClasses = [];
for(let k = 0; k < tempConnectionData.type.length; k++){
connectionClasses.push( MapUtil.getConnectionInfo( tempConnectionData.type[k], 'cssClass') );
}
let connectionClasses = MapUtil.getConnectionFakeClassesByTypes(tempConnectionData.type);
connectionClasses = connectionClasses.join(' ');
tempConData.connection = '<div class="pf-fake-connection ' + connectionClasses + '"></div>';
tempConData.target = {
@@ -734,10 +858,11 @@ define([
searchable: false,
className: ['pf-help-default', 'text-center', config.tableCellImageClass].join(' '),
data: 'log.ship',
defaultContent: '',
render: {
_: function(data, type, row, meta){
let value = data;
if(type === 'display'){
if(data && type === 'display'){
value = '<img src="' + Init.url.ccpImageServer + '/Render/' + value.typeId + '_32.png" title="' + value.typeName + '" data-toggle="tooltip" />';
}
return value;
@@ -750,12 +875,17 @@ define([
orderable: true,
searchable: true,
data: 'log.ship',
defaultContent: getLabelForUnknownData(),
render: {
_: function(data, type, row){
let value = data.name;
if(type === 'display'){
value = '<div class="' + MapUtil.config.tableCellEllipsisClass + ' ' + MapUtil.config.tableCellEllipsis100Class + '">' + data.name + '</div>';
let value = data;
if(data){
value = data.name;
if(type === 'display'){
value = '<div class="' + MapUtil.config.tableCellEllipsisClass + ' ' + MapUtil.config.tableCellEllipsis100Class + '">' + data.name + '</div>';
}
}
return value;
}
}
@@ -845,9 +975,15 @@ define([
orderable: true,
searchable: true,
data: 'log.system',
defaultContent: getLabelForUnknownData(),
render: {
_: 'name',
sort: 'name'
_: function(data, type, row, meta){
let value = data;
if(data && type === 'display'){
value = data.name;
}
return value;
}
}
},{
targets: 7,
@@ -856,13 +992,18 @@ define([
searchable: true,
className: [config.tableCellActionClass].join(' '),
data: 'log',
defaultContent: getLabelForUnknownData(),
render: {
_: function (data, type, row, meta) {
let value = '';
if(data.station && data.station.id > 0){
value = data.station.name + '&nbsp;' + getIconForDockedStatus('station');
}else if(data.structure && data.structure.id > 0){
value = data.structure.name + '&nbsp;' + getIconForDockedStatus('structure');
let value = data;
if(data){
if(data.station && data.station.id > 0){
value = data.station.name + '&nbsp;' + getIconForDockedStatus('station');
}else if(data.structure && data.structure.id > 0){
value = data.structure.name + '&nbsp;' + getIconForDockedStatus('structure');
}else{
value = '';
}
}
return value;
}
@@ -997,7 +1138,7 @@ define([
},{
targets: 1,
name: 'timestamp',
title: '<i class="far fa-lg fa-fw fa-clock"></i>',
title: '<i class="far fa-fw fa-clock"></i>',
width: 100,
className: ['text-right'].join(' '),
data: 'datetime.date',
@@ -1090,7 +1231,7 @@ define([
data: 'context.data.formatted'
},{
targets: 8,
title: '<i class="fas fa-lg fa-code text-right"></i>',
title: '<i class="fas fa-code text-right"></i>',
width: 12,
className: [config.tableCellActionClass].join(' '),
data: 'context.data',

View File

@@ -62,7 +62,11 @@ define([
// render function for numeric columns
let renderNumericColumn = function(data, type, row, meta){
return data.toLocaleString();
let value = data;
if(type === 'display'){
value = data.toLocaleString();
}
return value;
};
// get table element
@@ -348,7 +352,7 @@ define([
});
$(sumColumnIndexes).each(function(index, value){
$( api.column( value ).footer() ).text( renderNumericColumn(pageTotalColumns[index]) );
$( api.column( value ).footer() ).text( renderNumericColumn(pageTotalColumns[index], 'display') );
});
},
data: [] // will be added dynamic

View File

@@ -18,7 +18,7 @@ define([
moduleHeadClass: 'pf-module-head', // class for module header
moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler
routeCacheTTL: 10, // route cache timer (client) in seconds
routeCacheTTL: 5, // route cache timer (client) in seconds
// system route module
moduleTypeClass: 'pf-system-route-module', // class for this module
@@ -38,12 +38,64 @@ define([
systemInfoRoutesTableClass: 'pf-system-route-table', // class for route tables
mapSelectId: 'pf-route-dialog-map-select', // id for "map" select
dataTableActionCellClass: 'pf-table-action-cell' // class for "action" cells
dataTableActionCellClass: 'pf-table-action-cell', // class for "action" cells
dataTableRouteCellClass: 'pf-table-route-cell', // class for "route" cells
dataTableJumpCellClass: 'pf-table-jump-cell' // class for "route jump" cells
};
// cache for system routes
let cache = {
systemRoutes: {} // jump information between solar systems
systemRoutes: {}, // jump information between solar systems
mapConnections: {} // connection data read from UI
};
/**
* set cache data
* @param cacheType
* @param cacheKey
* @param data
*/
let setCacheData = (cacheType, cacheKey, data) => {
cache[cacheType][cacheKey] = {
data: data,
updated: Util.getServerTime().getTime() / 1000
};
};
/**
* get cache data
* @param cacheType
* @param cacheKey
* @returns {*}
*/
let 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
) <= config.routeCacheTTL
){
cachedData = cache[cacheType][cacheKey].data;
}
return cachedData;
};
let getRouteDataCacheKey = (mapIds, sourceName, targetName) => {
return [mapIds.join('_'), sourceName.toLowerCase(), targetName.toLowerCase()].join('###');
};
/**
* get a unique cache key name for "source"/"target"-name
* @param sourceName
* @param targetName
* @returns {string}
*/
let getConnectionDataCacheKey = (sourceName, targetName) => {
return [sourceName.toLowerCase(), targetName.toLowerCase()].sort().join('###');
};
/**
@@ -59,22 +111,12 @@ define([
// format routeData
let rowData = formatRouteData(routeData);
if(rowData.route){
let cacheKey = routeData.systemFromData.name.toLowerCase() +
'_' + routeData.systemToData.name.toLowerCase();
// update route cache
cache.systemRoutes[cacheKey] = {
data: rowData,
updated: Util.getServerTime().getTime() / 1000
};
let cacheKey = getRouteDataCacheKey(rowData.mapIds, routeData.systemFromData.name, routeData.systemToData.name);
setCacheData('systemRoutes', cacheKey, rowData);
let rowElement = addRow(context, rowData);
rowElement.initTooltips({
container: 'body'
});
addRow(context, rowData);
}
}
@@ -116,10 +158,13 @@ define([
if(row.length > 0){
rowElement = row.nodes().to$();
if(animationStatus !== null){
rowElement.data('animationStatus', animationStatus);
}
rowElement.initTooltips({
container: 'body'
});
}
return rowElement;
@@ -186,7 +231,8 @@ define([
wormholesCritical: (rowData.hasOwnProperty('wormholesCritical')) ? rowData.wormholesCritical | 0 : 1,
wormholesFrigate: (rowData.hasOwnProperty('wormholesFrigate')) ? rowData.wormholesFrigate | 0 : 1,
wormholesEOL: (rowData.hasOwnProperty('wormholesEOL')) ? rowData.wormholesEOL | 0 : 1,
safer: (rowData.hasOwnProperty('safer')) ? rowData.safer.value | 0 : 0
connections: (rowData.hasOwnProperty('connections')) ? rowData.connections.value | 0 : 0,
flag: (rowData.hasOwnProperty('flag')) ? rowData.flag.value : 'shortest'
};
};
@@ -320,34 +366,30 @@ define([
* @param routesTable
* @param systemsTo
*/
let drawRouteTable = function(mapId, moduleElement, systemFromData, routesTable, systemsTo){
let drawRouteTable = (mapId, moduleElement, systemFromData, routesTable, systemsTo) => {
let requestRouteData = [];
let currentTimestamp = Util.getServerTime().getTime();
// Skip some routes from search
// -> this should help to throttle requests (heavy CPU load for route calculation)
let defaultRoutesCount = Init.routeSearch.defaultCount;
let rowElements = [];
for(let i = 0; i < systemsTo.length; i++){
let systemToData = systemsTo[i];
if(systemFromData.name !== systemToData.name){
let cacheKey = 'route_' + mapId + '_' + systemFromData.name.toUpperCase() + '_' + systemToData.name.toUpperCase();
if(
cache.systemRoutes.hasOwnProperty(cacheKey) &&
Math.round(
( currentTimestamp - (new Date( cache.systemRoutes[cacheKey].updated * 1000).getTime())) / 1000
) <= config.routeCacheTTL
){
// check for cached rowData
let cacheKey = getRouteDataCacheKey([mapId], systemFromData.name, systemToData.name);
let rowData = getCacheData('systemRoutes', cacheKey);
if(rowData){
// route data is cached (client side)
let context = {
dataTable: routesTable
};
addRow(context, cache.systemRoutes[cacheKey].data);
rowElements.push( addRow(context, rowData) );
}else{
// get route data
// get route data -> ajax
let searchData = {
mapIds: [mapId],
systemFromData: systemFromData,
@@ -360,6 +402,11 @@ define([
}
}
// rows added from cache -> redraw() table
if(rowElements.length){
routesTable.draw();
}
// check if routes data is not cached and is requested
if(requestRouteData.length > 0){
let contextData = {
@@ -382,7 +429,7 @@ define([
* @param systemFromData
* @param routesTable
*/
let showSettingsDialog = function(dialogData, moduleElement, systemFromData, routesTable){
let showSettingsDialog = (dialogData, moduleElement, systemFromData, routesTable) => {
let promiseStore = MapUtil.getLocaleData('map', dialogData.mapId);
promiseStore.then(function(dataStore) {
@@ -405,7 +452,7 @@ define([
maxSelectionLength: maxSelectionLength
};
requirejs(['text!templates/dialog/route_settings.html', 'mustache'], function(template, Mustache) {
requirejs(['text!templates/dialog/route_settings.html', 'mustache'], (template, Mustache) => {
let content = Mustache.render(template, data);
let settingsDialog = bootbox.dialog({
@@ -461,7 +508,7 @@ define([
* @param {Array} data
* @returns {Array}
*/
let formSystemSelectData = function(data){
let formSystemSelectData = (data) => {
let formattedData = [];
for(let i = 0; i < data.length; i++){
let tmpData = data[i];
@@ -479,7 +526,7 @@ define([
* set event observer for route finder dialog
* @param routeDialog
*/
let setDialogObserver = function(routeDialog){
let setDialogObserver = (routeDialog) => {
let wormholeCheckbox = routeDialog.find('input[type="checkbox"][name="wormholes"]');
let wormholeReducedCheckbox = routeDialog.find('input[type="checkbox"][name="wormholesReduced"]');
let wormholeCriticalCheckbox = routeDialog.find('input[type="checkbox"][name="wormholesCritical"]');
@@ -528,19 +575,137 @@ define([
onWormholeCheckboxChange();
};
/**
* get a connectionsData object that holds all connections for given mapIds (used as cache for route search)
* @param mapIds
* @returns {{}}
*/
let getConnectionsDataFromMaps = (mapIds) => {
let connectionsData = {};
for(let mapId of mapIds) {
let map = MapUtil.getMapInstance(mapId);
if(map){
let cacheKey = 'map_' + mapId;
let mapConnectionsData = 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 = 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
setCacheData('mapConnections', cacheKey, mapConnectionsData);
}
if(connectionsData !== null){
connectionsData = Object.assign({}, mapConnectionsData, connectionsData);
}
}
}
return connectionsData;
};
/**
* search for a specific connection by "source"/"target"-name inside connectionsData cache
* @param connectionsData
* @param sourceName
* @param targetName
* @returns {{}}
*/
let findConnectionsData = (connectionsData, sourceName, targetName) => {
let connectionDataCacheKey = getConnectionDataCacheKey(sourceName, targetName);
return connectionsData.hasOwnProperty(connectionDataCacheKey) ?
connectionsData[connectionDataCacheKey] : {};
};
/**
* get stargate connection data (default connection type in case connection was not found on a map)
* @param sourceRouteNodeData
* @param targetRouteNodeData
* @returns {{connection: {id: number, type: string[], scope: string}, source: {id: number, name, alias}, target: {id: number, name, alias}}}
*/
let getStargateConnectionData = (sourceRouteNodeData, targetRouteNodeData) => {
return {
connection: {
id: 0,
type: ['stargate'],
scope: 'stargate'
},
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}
*/
let 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;
};
/**
* format route data from API request into dataTable row format
* @param routeData
* @returns {{}}
*/
let formatRouteData = function(routeData){
let formatRouteData = (routeData) => {
/**
* get status icon for route
* @param status
* @returns {string}
*/
let getStatusIcon= function(status){
let getStatusIcon= (status) => {
let color = 'txt-color-danger';
let title = 'route not found';
switch(status){
@@ -563,10 +728,11 @@ define([
// 2: not searched
let routeStatus = routeData.skipSearch ? 2 : 0;
// button class for "safer" routes
let saferButtonClass = routeData.safer ? 'txt-color-success' : '';
// button class for flag (e.g. "secure" routes)
let flagButtonClass = routeData.flag === 'secure' ? 'txt-color-success' : '';
let saferButton = '<i class="fas ' + ['fa-shield-alt', 'txt-color', saferButtonClass].join(' ') + '"></i>';
let connectionButton = '<i class="fas ' + ['fa-link', 'txt-color'].join(' ') + '"></i>';
let flagButton = '<i class="fas ' + ['fa-shield-alt', 'txt-color', flagButtonClass].join(' ') + '"></i>';
let reloadButton = '<i class="fas ' + ['fa-sync'].join(' ') + '"></i>';
let searchButton = '<i class="fas ' + ['fa-search-plus '].join(' ') + '"></i>';
let deleteButton = '<i class="fas ' + ['fa-times', 'txt-color', 'txt-color-redDarker'].join(' ') + '"></i>';
@@ -583,7 +749,10 @@ define([
value: '',
formatted: ''
},
route: routeStatus === 2 ? 'search now' : 'not found',
route: {
value: routeStatus === 2 ? 'search now' : 'not found',
data: routeData.route
},
stargates: routeData.stargates,
jumpbridges: routeData.jumpbridges,
wormholes: routeData.wormholes,
@@ -591,9 +760,13 @@ define([
wormholesCritical: routeData.wormholesCritical,
wormholesFrigate: routeData.wormholesFrigate,
wormholesEOL: routeData.wormholesEOL,
safer: {
value: routeData.safer,
button: saferButton
connections: {
value: 0,
button: connectionButton
},
flag: {
value: routeData.flag,
button: flagButton
},
reload: {
button: routeData.skipSearch ? searchButton : reloadButton
@@ -613,15 +786,28 @@ define([
routeStatus = 1;
// add route Data
let jumpData = [];
let routeJumpElements = [];
let avgSecTemp = 0;
let connectionsData = getConnectionsDataFromMaps(routeData.mapIds);
let prevRouteNodeData = null;
// loop all systems on this route
for(let i = 0; i < routeData.route.length; i++){
let routeNodeData = routeData.route[i];
// format system name
let systemName = routeNodeData.system;
// fake connection elements between systems -----------------------------------------------------------
if(prevRouteNodeData){
let connectionData = findConnectionsData(connectionsData, prevRouteNodeData.system, systemName);
if(!connectionData.hasOwnProperty('connection')){
connectionData = getStargateConnectionData(prevRouteNodeData, routeNodeData);
}
let connectionElement = getFakeConnectionElement(connectionData);
routeJumpElements.push( connectionElement );
}
// system elements ------------------------------------------------------------------------------------
let systemSec = Number(routeNodeData.security).toFixed(1).toString();
let tempSystemSec = systemSec;
@@ -640,12 +826,14 @@ define([
let system = '<i class="' + icon + ' ' + systemSecClass + '" ';
system += 'data-toggle="tooltip" data-placement="bottom" data-container="body" ';
system += 'title="' + systemName + ' [' + systemSec + '] "></i>';
jumpData.push( system );
routeJumpElements.push( system );
// "source" system is not relevant for average security
if(i > 0){
avgSecTemp += Number(routeNodeData.security);
}
prevRouteNodeData = routeNodeData;
}
let avgSec = ( avgSecTemp / (routeData.route.length - 1)).toFixed(2);
@@ -666,7 +854,8 @@ define([
value: avgSec,
formatted: '<span class="' + avgSecClass + '">' + avgSec + '</span>'
};
tableRowData.route = jumpData.join(' ');
tableRowData.route.value = routeJumpElements.join(' ');
}
// route status data ----------------------------------------------------------------------
@@ -682,7 +871,7 @@ define([
* get module element
* @returns {*}
*/
let getModule = function(){
let getModule = () => {
// create new module container
let moduleElement = $('<div>').append(
$('<div>', {
@@ -694,14 +883,14 @@ define([
$('<h5>', {
class: 'pull-right'
}).append(
$('<i>', {
class: ['fas', 'fa-fw', 'fa-search', config.moduleHeadlineIconClass, config.moduleHeadlineIconSearchClass].join(' '),
title: 'find&nbsp;route'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
$('<i>', {
class: ['fas', 'fa-fw', 'fa-sliders-h', config.moduleHeadlineIconClass, config.moduleHeadlineIconSettingsClass].join(' '),
title: 'settings'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
$('<i>', {
class: ['fas', 'fa-fw', 'fa-search', config.moduleHeadlineIconClass, config.moduleHeadlineIconSearchClass].join(' '),
title: 'find&nbsp;route'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
$('<i>', {
class: ['fas', 'fa-fw', 'fa-sync', config.moduleHeadlineIconClass, config.moduleHeadlineIconRefreshClass].join(' '),
title: 'refresh&nbsp;all'
@@ -737,7 +926,7 @@ define([
targets: 0,
orderable: true,
title: '',
width: '10px',
width: 2,
class: ['text-center'].join(' '),
data: 'status',
render: {
@@ -764,7 +953,7 @@ define([
targets: 2,
orderable: true,
title: '<span title="jumps" data-toggle="tooltip"><i class="fas fa-arrows-alt-h"></i>&nbsp;&nbsp;</span>',
width: '18px',
width: 18,
class: 'text-right',
data: 'jumps',
render: {
@@ -775,7 +964,7 @@ define([
targets: 3,
orderable: true,
title: '<span title="average security" data-toggle="tooltip">&#216;&nbsp;&nbsp;</span>',
width: '15px',
width: 15,
class: 'text-right',
data: 'avgTrueSec',
render: {
@@ -786,19 +975,49 @@ define([
targets: 4,
orderable: false,
title: 'route',
data: 'route'
class: [config.dataTableRouteCellClass].join(' '),
data: 'route',
render: {
_: 'value'
}
},{
targets: 5,
title: '<i title="search safer route (HS)" data-toggle="tooltip" class="fas fa-shield-alt text-right"></i>',
title: '<i title="toggle connections" data-toggle="tooltip" class="fas fa-link text-right"></i>',
orderable: false,
searchable: false,
width: '10px',
width: 10,
class: ['text-center', config.dataTableActionCellClass].join(' '),
data: 'safer',
data: 'connections',
render: {
_: 'button'
},
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
createdCell: function(cell, cellData, rowData, rowIndex, colIndex) {
let tempTableApi = this.api();
$(cell).on('click', function(e) {
let routeCellElement = tempTableApi.cell( rowIndex, 4 ).nodes().to$();
if(routeCellElement.hasClass(config.dataTableJumpCellClass)){
routeCellElement.toggleClass(config.dataTableJumpCellClass, false);
$(this).find('i').toggleClass('txt-color-orange', false);
}else{
routeCellElement.toggleClass(config.dataTableJumpCellClass, true);
$(this).find('i').toggleClass('txt-color-orange', true);
}
});
}
},{
targets: 6,
title: '<i title="search safer route (HS)" data-toggle="tooltip" class="fas fa-shield-alt text-right"></i>',
orderable: false,
searchable: false,
width: 10,
class: ['text-center', config.dataTableActionCellClass].join(' '),
data: 'flag',
render: {
_: 'button'
},
createdCell: function(cell, cellData, rowData, rowIndex, colIndex) {
let tempTableApi = this.api();
$(cell).on('click', function(e) {
@@ -809,7 +1028,7 @@ define([
// overwrite some params
routeData.skipSearch = 0;
routeData.safer = 1 - routeData.safer; // toggle
routeData.flag = routeData.flag === 'shortest' ? 'secure' : 'shortest'; // toggle
let context = {
moduleElement: moduleElement,
@@ -824,11 +1043,11 @@ define([
});
}
},{
targets: 6,
targets: 7,
title: '',
orderable: false,
searchable: false,
width: '10px',
width: 10,
class: ['text-center', config.dataTableActionCellClass].join(' '),
data: 'reload',
render: {
@@ -859,11 +1078,11 @@ define([
});
}
},{
targets: 7,
targets: 8,
title: '',
orderable: false,
searchable: false,
width: '10px',
width: 10,
class: ['text-center', config.dataTableActionCellClass].join(' '),
data: 'clear',
render: {
@@ -908,6 +1127,20 @@ define([
}
},
initComplete: function(settings, json){
// click on "fake connection" -------------------------------------------------------------------------
$(this).on('click', '.pf-fake-connection', function(){
let fakeConnectionElement = $(this);
let mapId = fakeConnectionElement.attr('data-mapId');
let connectionId = fakeConnectionElement.attr('data-connectionId');
let connection = $().getConnectionById(mapId, connectionId);
if(connection){
let map = connection._jsPlumb.instance;
MapUtil.showConnectionInfo(map, [connection]);
}
});
},
data: [] // will be added dynamic
});
@@ -990,7 +1223,7 @@ define([
* @param mapId
* @param systemData
*/
let initModule = function(moduleElement, mapId, systemData){
let initModule = (moduleElement, mapId, systemData) => {
let systemFromData = {
name: systemData.name,

View File

@@ -225,37 +225,39 @@ define([
let getTableData = function(tableApi){
let tableData = [];
tableApi.rows().eq(0).each(function(idx){
let row = tableApi.row(idx);
// default row data
let defaultRowData = row.data();
let rowElement = row.nodes().to$();
if(tableApi){
tableApi.rows().eq(0).each(function(idx){
let row = tableApi.row(idx);
// default row data
let defaultRowData = row.data();
let rowElement = row.nodes().to$();
if(defaultRowData.id > 0){
// get all editable fields per row
let editableFields = rowElement.find('.editable');
if(defaultRowData.id > 0){
// get all editable fields per row
let editableFields = rowElement.find('.editable');
if(editableFields.length > 0){
let values = $(editableFields).editable('getValue');
if(editableFields.length > 0){
let values = $(editableFields).editable('getValue');
if(values.name){
// convert to lower for better compare options
values.name = values.name.toLowerCase();
if(values.name){
// convert to lower for better compare options
values.name = values.name.toLowerCase();
// add pk for this row
values.id = defaultRowData.id;
// add pk for this row
values.id = defaultRowData.id;
// add updated for this row
values.updated = defaultRowData.updated;
// add updated for this row
values.updated = defaultRowData.updated;
// add row index
values.index = idx;
// add row index
values.index = idx;
tableData.push( values );
tableData.push( values );
}
}
}
}
});
});
}
return tableData;
};
@@ -1500,6 +1502,7 @@ define([
let map = Map.getMapInstance( mapId );
let systemId = MapUtil.getSystemId(mapId, systemData.id);
let systemConnections = MapUtil.searchConnectionsBySystems(map, [systemId], 'wh');
let newSelectOptions = [];
let connectionOptions = [];
for(let i = 0; i < systemConnections.length; i++){
@@ -1510,25 +1513,35 @@ define([
// check whether "source" or "target" system is relevant for this connection
// -> hint "source" === 'target' --> loop
if(systemData.id !== connectionData.target){
// take target...
connectionOptions.push({
value: connectionData.id,
text: connectionData.targetAlias
});
let targetSystemData = MapUtil.getSystemData(mapId, connectionData.target);
if(targetSystemData){
// take target...
connectionOptions.push({
value: connectionData.id,
text: connectionData.targetAlias + ' - ' + targetSystemData.security
});
}
}else if(systemData.id !== connectionData.source){
// take source...
connectionOptions.push({
value: connectionData.id,
text: connectionData.sourceAlias
});
let sourceSystemData = MapUtil.getSystemData(mapId, connectionData.source);
if(sourceSystemData){
// take source...
connectionOptions.push({
value: connectionData.id,
text: connectionData.sourceAlias + ' - ' + sourceSystemData.security
});
}
}
}
}
// add empty entry
connectionOptions.unshift({ value: null, text: ''});
if(connectionOptions.length > 0){
newSelectOptions.push({ text: 'System', children: connectionOptions});
}
return connectionOptions;
// add empty entry
newSelectOptions.unshift({ value: null, text: ''});
return newSelectOptions;
};
/**
@@ -1621,7 +1634,7 @@ define([
if(newSelectOptionsCount > 0){
if(groupId === 5){
// "wormhole" selected => multiple <optgroup> available
newSelectOptions.push({ text: 'Wandering WHs', children: fixSelectOptions});
newSelectOptions.push({ text: 'Wandering WH', children: fixSelectOptions});
}else{
newSelectOptions = fixSelectOptions;
}
@@ -1645,7 +1658,7 @@ define([
}
if(frigateWHData.length > 0){
newSelectOptions.push({ text: 'Frigate WHs', children: frigateWHData});
newSelectOptions.push({ text: 'Frigate WH', children: frigateWHData});
}
// add possible incoming holes
@@ -1661,7 +1674,7 @@ define([
}
if(incomingWHData.length > 0){
newSelectOptions.push({ text: 'Incoming WHs', children: incomingWHData});
newSelectOptions.push({ text: 'Incoming WH', children: incomingWHData});
}
}else{
// groups without "children" (optgroup) should be sorted by "value"
@@ -1686,7 +1699,7 @@ define([
}
if(staticWHData.length > 0){
newSelectOptions.unshift({ text: 'Static WHs', children: staticWHData});
newSelectOptions.unshift({ text: 'Static WH', children: staticWHData});
}
}
}

View File

@@ -72,9 +72,9 @@ define([
let localStorage; // cache for "localForage" singleton
/*
* ===========================================================================================================
* ===============================================================================================================
* Global jQuery plugins for some common and frequently used functions
* ==========================================================================================================
* ==============================================================================================================
*/
/**
@@ -820,9 +820,9 @@ define([
};
/*
* ===========================================================================================================
* ===============================================================================================================
* Util functions that are global available for all modules
* ==========================================================================================================
* ==============================================================================================================
*/
/**
@@ -1149,11 +1149,29 @@ define([
return logInfo;
};
/**
* set currentUserData as "global" variable
* this function should be called continuously after data change
* to keep the data always up2data
* @param userData
*/
let setCurrentUserData = (userData) => {
Init.currentUserData = userData;
// check if function is available
// this is not the case in "login" page
if( $.fn.updateHeaderUserData ){
$.fn.updateHeaderUserData();
}
return getCurrentUserData();
};
/**
* get currentUserData from "global" variable
* @returns {*}
*/
let getCurrentUserData = function(){
let getCurrentUserData = () => {
return Init.currentUserData;
};
@@ -1196,15 +1214,18 @@ define([
/**
* set default jQuery AJAX configuration
*/
let ajaxSetup = function(){
let ajaxSetup = () => {
$.ajaxSetup({
beforeSend: function(xhr, settings) {
beforeSend: function(jqXHR, settings) {
// store request URL for later use (e.g. in error messages)
jqXHR.url = location.protocol + '//' + location.host + settings.url;
// Add custom application headers on "same origin" requests only!
// -> Otherwise a "preflight" request is made, which will "probably" fail
if(settings.crossDomain === false){
// add current character data to ANY XHR request (HTTP HEADER)
// -> This helps to identify multiple characters on multiple browser tabs
xhr.setRequestHeader('Pf-Character', getCurrentCharacterId());
jqXHR.setRequestHeader('Pf-Character', getCurrentCharacterId());
}
}
});
@@ -1702,13 +1723,34 @@ define([
return signatureTypeId;
};
/**
* get array key that points to map data catching mapId
* @param data
* @param mapId
* @returns {boolean}
*/
let getDataIndexByMapId = (data, mapId) => {
let index = false;
if( Array.isArray(data) && mapId === parseInt(mapId, 10) ){
for(let i = 0; i < data.length; i++){
if(data[i].config.id === mapId){
index = i;
break;
}
}
}
return index;
};
// CurrentMapUserData =============================================================================================
/**
* set currentMapUserData as "global" variable (count of active pilots)
* this function should be called continuously after data change
* to keep the data always up2data
* @param mapUserData
*/
let setCurrentMapUserData = function(mapUserData){
let setCurrentMapUserData = (mapUserData) => {
Init.currentMapUserData = mapUserData;
return getCurrentMapUserData();
@@ -1719,23 +1761,26 @@ define([
* @param mapId
* @returns {boolean}
*/
let getCurrentMapUserData = function(mapId){
let getCurrentMapUserData = (mapId) => {
let currentMapUserData = false;
if(
mapId === parseInt(mapId, 10) &&
Init.currentMapUserData
){
// search for a specific map
for(let i = 0; i < Init.currentMapUserData.length; i++){
if(Init.currentMapUserData[i].config.id === mapId){
currentMapUserData = Init.currentMapUserData[i];
break;
if(Init.currentMapUserData){
if(mapId === parseInt(mapId, 10)){
// search for a specific map
for(let i = 0; i < Init.currentMapUserData.length; i++){
if(
Init.currentMapUserData[i].config &&
Init.currentMapUserData[i].config.id === mapId
){
currentMapUserData = Init.currentMapUserData[i];
break;
}
}
}else{
// get data for all maps
currentMapUserData = Init.currentMapUserData;
}
}else{
// get data for all maps
currentMapUserData = Init.currentMapUserData;
}
if(currentMapUserData !== false){
@@ -1746,60 +1791,54 @@ define([
return currentMapUserData;
};
/**
* get mapDataUser array index by mapId
* @param mapId
* @returns {boolean|int}
*/
let getCurrentMapUserDataIndex = (mapId) => {
return getDataIndexByMapId(Init.currentMapUserData, mapId);
};
/**
* update cached mapUserData for a single map
* @param mapUserData
*/
let updateCurrentMapUserData = (mapUserData) => {
let mapUserDataIndex = getCurrentMapUserDataIndex( mapUserData.config.id );
if( !Array.isArray(Init.currentMapUserData) ){
Init.currentMapUserData = [];
}
if(mapUserDataIndex !== false){
Init.currentMapUserData[mapUserDataIndex] = mapUserData;
}else{
// new map data
Init.currentMapUserData.push(mapUserData);
}
};
// CurrentMapData =================================================================================================
/**
* set currentMapData as "global" variable
* this function should be called continuously after data change
* to keep the data always up2data
* @param mapData
*/
let setCurrentMapData = function(mapData){
let setCurrentMapData = (mapData) => {
Init.currentMapData = mapData;
return getCurrentMapData();
};
/**
* get mapData array index by mapId
* @param mapId
* @returns {boolean|int}
*/
let getCurrentMapDataIndex = function(mapId){
let mapDataIndex = false;
if( mapId === parseInt(mapId, 10) ){
for(let i = 0; i < Init.currentMapData.length; i++){
if(Init.currentMapData[i].config.id === mapId){
mapDataIndex = i;
break;
}
}
}
return mapDataIndex;
};
/**
* update cached mapData for a single map
* @param mapData
*/
let updateCurrentMapData = function(mapData){
let mapDataIndex = getCurrentMapDataIndex( mapData.config.id );
if(mapDataIndex !== false){
Init.currentMapData[mapDataIndex].config = mapData.config;
Init.currentMapData[mapDataIndex].data = mapData.data;
}else{
// new map data
Init.currentMapData.push(mapData);
}
};
/**
* get currentMapData from "global" variable for a specific map or all maps
* @param mapId
* @returns {boolean}
*/
let getCurrentMapData = function(mapId){
let getCurrentMapData = (mapId) => {
let currentMapData = false;
if( mapId === parseInt(mapId, 10) ){
@@ -1818,6 +1857,31 @@ define([
return currentMapData;
};
/**
* get mapData array index by mapId
* @param mapId
* @returns {boolean|int}
*/
let getCurrentMapDataIndex = (mapId) => {
return getDataIndexByMapId(Init.currentMapData, mapId);
};
/**
* update cached mapData for a single map
* @param mapData
*/
let updateCurrentMapData = (mapData) => {
let mapDataIndex = getCurrentMapDataIndex( mapData.config.id );
if(mapDataIndex !== false){
Init.currentMapData[mapDataIndex].config = mapData.config;
Init.currentMapData[mapDataIndex].data = mapData.data;
}else{
// new map data
Init.currentMapData.push(mapData);
}
};
/**
* @param path
* @param value
@@ -1843,24 +1907,6 @@ define([
});
};
/**
* set currentUserData as "global" variable
* this function should be called continuously after data change
* to keep the data always up2data
* @param userData
*/
let setCurrentUserData = function(userData){
Init.currentUserData = userData;
// check if function is available
// this is not the case in "login" page
if( $.fn.updateHeaderUserData ){
$.fn.updateHeaderUserData();
}
return getCurrentUserData();
};
/**
* get the current log data for the current user character
* @returns {boolean}
@@ -2397,10 +2443,10 @@ define([
getAreaIdBySecurity: getAreaIdBySecurity,
setCurrentMapUserData: setCurrentMapUserData,
getCurrentMapUserData: getCurrentMapUserData,
updateCurrentMapUserData: updateCurrentMapUserData,
setCurrentMapData: setCurrentMapData,
getCurrentMapData: getCurrentMapData,
filterCurrentMapData: filterCurrentMapData,
getCurrentMapDataIndex: getCurrentMapDataIndex,
updateCurrentMapData: updateCurrentMapData,
deleteCurrentMapData: deleteCurrentMapData,
setCurrentUserData: setCurrentUserData,

View File

@@ -1,5 +1,5 @@
/*!
* Bootstrap Confirmation v1.0.5
* Bootstrap Confirmation v1.0.7
* https://github.com/tavicu/bs-confirmation
*/
+function ($) {
@@ -15,62 +15,47 @@
this.init('confirmation', element, options);
$(element).on('show.bs.confirmation', function(e) {
that.options.onShow(e, this);
$(this).addClass('open');
var options = that.options;
var all = options.all_selector;
if(options.singleton)
{
$(all).not(that.$element).each(function()
{
if( $(this).hasClass('open') )
{
$(this).confirmation('hide');
}
});
}
});
$(element).on('hide.bs.confirmation', function(e) {
that.options.onHide(e, this);
$(this).removeClass('open');
});
$(element).on('shown.bs.confirmation', function(e) {
var options = that.options;
var all = options.all_selector;
if(that.isPopout()) {
if(!event_body) {
event_body = $('body').on('click', function (e) {
if(that.$element.is(e.target)) return;
if(that.$element.has(e.target).length) return;
if($('.popover').has(e.target).length) return;
that.hide();
that.inState.click = false;
$('body').unbind(e);
event_body = false;
return;
});
}
}
});
if(options.selector) {
if (options.selector) {
$(element).on('click.bs.confirmation', options.selector, function(e) {
e.preventDefault();
});
} else {
$(element).on('click.bs.confirmation', function(e) {
$(element).on('show.bs.confirmation', function(event) {
that.runCallback(that.options.onShow, event, that.$element);
that.$element.addClass('open');
if (that.options.singleton) {
$(that.options.all_selector).not(that.$element).each(function() {
if ($(this).hasClass('open')) {
$(this).confirmation('hide');
}
});
}
}).on('hide.bs.confirmation', function(event) {
that.runCallback(that.options.onHide, event, that.$element);
that.$element.removeClass('open');
}).on('shown.bs.confirmation', function(e) {
if (!that.isPopout() && !event_body) {
return;
}
event_body = $('body').on('click', function (e) {
if (that.$element.is(e.target)) return;
if (that.$element.has(e.target).length) return;
if ($('.popover').has(e.target).length) return;
that.hide();
that.inState.click = false;
$('body').unbind(e);
event_body = false;
return;
});
}).on('click.bs.confirmation', function(e) {
e.preventDefault();
});
}
@@ -78,30 +63,30 @@
if (!$.fn.popover || !$.fn.tooltip) throw new Error('Confirmation requires popover.js and tooltip.js');
Confirmation.VERSION = '1.0.5'
Confirmation.VERSION = '1.0.7'
Confirmation.DEFAULTS = $.extend({}, $.fn.popover.Constructor.DEFAULTS, {
placement : 'right',
title : 'Are you sure?',
btnOkClass : 'btn btn-sm btn-danger',
btnOkLabel : 'Delete',
btnOkIcon : 'glyphicon glyphicon-ok',
btnCancelClass : 'btn btn-sm btn-default',
btnCancelLabel : 'Cancel',
btnCancelIcon : 'glyphicon glyphicon-remove',
href : '#',
target : '_self',
singleton : true,
popout : true,
onShow : function(event, element){},
onHide : function(event, element){},
onConfirm : function(event, element){},
onCancel : function(event, element){},
template : '<div class="popover"><div class="arrow"></div>'
placement : 'right',
title : 'Are you sure?',
btnOkClass : 'btn btn-sm btn-danger',
btnOkLabel : 'Delete',
btnOkIcon : 'glyphicon glyphicon-ok',
btnCancelClass : 'btn btn-sm btn-default',
btnCancelLabel : 'Cancel',
btnCancelIcon : 'glyphicon glyphicon-remove',
href : '#',
target : '_self',
singleton : true,
popout : true,
onShow : function(event, element) {},
onHide : function(event, element) {},
onConfirm : function(event, element) {},
onCancel : function(event, element) {},
template : '<div class="popover"><div class="arrow"></div>'
+ '<h3 class="popover-title"></h3>'
+ '<div class="popover-content">'
+ '<a data-apply="confirmation">Yes</a>'
+ '<a data-dismiss="confirmation">No</a>'
+ ' <a data-apply="confirmation">Yes</a>'
+ ' <a data-dismiss="confirmation">No</a>'
+ '</div>'
+ '</div>'
});
@@ -131,24 +116,34 @@
.attr('href', this.getHref())
.attr('target', this.getTarget())
.off('click').on('click', function(event) {
options.onConfirm(event, that.$element);
that.runCallback(that.options.onConfirm, event, that.$element);
// If the button is a submit one
if (that.$element.attr('type') == 'submit')
that.$element.closest('form:first').submit();
if (that.$element.attr('type') == 'submit') {
var form = that.$element.closest('form');
var novalidate = form.attr('novalidate') !== undefined;
if (novalidate || form[0].checkValidity()) {
form.submit();
}
}
that.hide();
that.inState.click = false;
that.$element.trigger($.Event('confirm.bs.confirmation'));
});
$btnCancel.addClass(this.getBtnCancelClass())
.html(this.getBtnCancelLabel())
.prepend($('<i></i>').addClass(this.getBtnCancelIcon()), " ")
.off('click').on('click', function(event){
options.onCancel(event, that.$element);
.off('click').on('click', function(event) {
that.runCallback(that.options.onCancel, event, that.$element);
that.hide();
that.inState.click = false;
that.$element.trigger($.Event('cancel.bs.confirmation'));
});
$tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title);
@@ -161,73 +156,71 @@
}
Confirmation.prototype.getBtnOkClass = function () {
var $e = this.$element;
var o = this.options;
return $e.attr('data-btnOkClass') || (typeof o.btnOkClass == 'function' ? o.btnOkClass.call(this, $e[0]) : o.btnOkClass);
return this.$element.data('btnOkClass') ||
(typeof this.options.btnOkClass == 'function' ? this.options.btnOkClass.call(this, this.$element) : this.options.btnOkClass);
}
Confirmation.prototype.getBtnOkLabel = function () {
var $e = this.$element;
var o = this.options;
return $e.attr('data-btnOkLabel') || (typeof o.btnOkLabel == 'function' ? o.btnOkLabel.call(this, $e[0]) : o.btnOkLabel);
return this.$element.data('btnOkLabel') ||
(typeof this.options.btnOkLabel == 'function' ? this.options.btnOkLabel.call(this, this.$element) : this.options.btnOkLabel);
}
Confirmation.prototype.getBtnOkIcon = function () {
var $e = this.$element;
var o = this.options;
return $e.attr('data-btnOkIcon') || (typeof o.btnOkIcon == 'function' ? o.btnOkIcon.call(this, $e[0]) : o.btnOkIcon);
return this.$element.data('btnOkIcon') ||
(typeof this.options.btnOkIcon == 'function' ? this.options.btnOkIcon.call(this, this.$element) : this.options.btnOkIcon);
}
Confirmation.prototype.getBtnCancelClass = function () {
var $e = this.$element;
var o = this.options;
return $e.attr('data-btnCancelClass') || (typeof o.btnCancelClass == 'function' ? o.btnCancelClass.call(this, $e[0]) : o.btnCancelClass);
return this.$element.data('btnCancelClass') ||
(typeof this.options.btnCancelClass == 'function' ? this.options.btnCancelClass.call(this, this.$element) : this.options.btnCancelClass);
}
Confirmation.prototype.getBtnCancelLabel = function () {
var $e = this.$element;
var o = this.options;
return $e.attr('data-btnCancelLabel') || (typeof o.btnCancelLabel == 'function' ? o.btnCancelLabel.call(this, $e[0]) : o.btnCancelLabel);
return this.$element.data('btnCancelLabel') ||
(typeof this.options.btnCancelLabel == 'function' ? this.options.btnCancelLabel.call(this, this.$element) : this.options.btnCancelLabel);
}
Confirmation.prototype.getBtnCancelIcon = function () {
var $e = this.$element;
var o = this.options;
return this.$element.data('btnCancelIcon') ||
(typeof this.options.btnCancelIcon == 'function' ? this.options.btnCancelIcon.call(this, this.$element) : this.options.btnCancelIcon);
}
return $e.attr('data-btnCancelIcon') || (typeof o.btnCancelIcon == 'function' ? o.btnCancelIcon.call(this, $e[0]) : o.btnCancelIcon);
Confirmation.prototype.getTitle = function () {
return this.$element.data('confirmation-title') ||
this.$element.data('title') ||
this.$element.attr('title') ||
(typeof this.options.title == 'function' ? this.options.title.call(this, this.$element) : this.options.title);
}
Confirmation.prototype.getHref = function () {
var $e = this.$element;
var o = this.options;
return $e.attr('data-href') || (typeof o.href == 'function' ? o.href.call(this, $e[0]) : o.href);
return this.$element.data('href') ||
this.$element.attr('href') ||
(typeof this.options.href == 'function' ? this.options.href.call(this, this.$element) : this.options.href);
}
Confirmation.prototype.getTarget = function () {
var $e = this.$element;
var o = this.options;
return $e.attr('data-target') || (typeof o.target == 'function' ? o.target.call(this, $e[0]) : o.target);
return this.$element.data('target') ||
this.$element.attr('target') ||
(typeof this.options.target == 'function' ? this.options.target.call(this, this.$element) : this.options.target);
}
Confirmation.prototype.isPopout = function () {
var popout;
var $e = this.$element;
var o = this.options;
var popout = this.$element.data('popout') ||
(typeof this.options.popout == 'function' ? this.options.popout.call(this, this.$element) : this.options.popout);
popout = $e.attr('data-popout') || (typeof o.popout == 'function' ? o.popout.call(this, $e[0]) : o.popout);
if(popout == 'false') popout = false;
if (popout == 'false') popout = false;
return popout
}
Confirmation.prototype.runCallback = function (callback, event, element) {
if (typeof callback == 'function') {
callback.call(this, event, element);
} else if (typeof callback == 'string') {
eval(callback);
}
}
// CONFIRMATION PLUGIN DEFINITION
// =========================

View File

@@ -9,12 +9,11 @@
"dependencies": {
},
"devDependencies": {
"gulp": "gulpjs/gulp#4.0",
"gulp-util": "3.0.x",
"gulp-requirejs-optimize": "1.2.x",
"gulp-filter": "5.0.x",
"gulp": "next",
"gulp-requirejs-optimize": "1.3.x",
"gulp-filter": "5.1.x",
"gulp-if": "2.0.x",
"gulp-jshint": "2.0.x",
"gulp-jshint": "2.1.x",
"gulp-sourcemaps": "2.6.x",
"gulp-gzip": "1.x.x",
"gulp-brotli": "1.2.x",
@@ -24,6 +23,7 @@
"gulp-clean-css": "3.x.x",
"gulp-bytediff": "1.0.x",
"gulp-debug": "^3.1.x",
"node-notifier": "5.2.x",
"uglify-es": "^3.0.x",
"jshint": "^2.9.x",
"jshint-stylish": "^2.x.x",
@@ -32,8 +32,10 @@
"promised-del": "1.0.x",
"flat": "2.0.x",
"lodash.padend": "4.6.x",
"slash": "1.0.x",
"file-extension": "3.1.x"
"slash": "2.x.x",
"file-extension": "3.1.x",
"fancy-log": "1.3.x",
"ansi-colors": "1.x.x"
},
"scripts": {
"gulp": "gulp"

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

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 one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

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 one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

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 one or more lines are too long

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 one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

BIN
public/js/v1.3.4/app.js.br Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -0,0 +1 @@
9<01>Ĵ9<C4B4>F<EFBFBD>0~y<><18>|uK<75>v0<76>Iw<49><77>.<2E>x<14><><EFBFBD><EFBFBD>(O<><4F><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><12><>zʘL0<4C><05><><EFBFBD><EFBFBD>ů<EFBFBD>r<16>_D<5F>? <1<><31>P(<28>LFj<46><1C><><EFBFBD>H<EFBFBD><48><EFBFBD><EFBFBD><EFBFBD>26hL<68>3<EFBFBD><33>- <0C>0 +<2B> ;<3B><0F>#<23>dMFj=<3D><><EFBFBD><EFBFBD><EFBFBD>`+s$<24>P=.a<>veS<65>r<EFBFBD>z<EFBFBD><7A><EFBFBD>>

Binary file not shown.

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