Merge pull request #572 from exodus4d/develop

v1.3.2
This commit is contained in:
Mark Friedrich
2018-01-01 18:22:34 +01:00
committed by GitHub
189 changed files with 60111 additions and 825 deletions

View File

@@ -13,6 +13,7 @@ use Controller\Ccp\Sso;
use Model\CharacterModel;
use Model\CorporationModel;
use lib\Config;
use Model\MapModel;
class Admin extends Controller{
@@ -183,6 +184,23 @@ class Admin extends Controller{
$this->initMembers($f3, $character);
break;
case 'maps':
switch($parts[1]){
case 'active':
$objectId = (int)$parts[2];
$value = (int)$parts[3];
$this->activateMap($character, $objectId, $value);
$f3->reroute('@admin(@*=/' . $parts[0] . ')');
break;
case 'delete':
$objectId = (int)$parts[2];
$this->deleteMap($character, $objectId);
$f3->reroute('@admin(@*=/' . $parts[0] . ')');
break;
}
$this->initMaps($f3, $character);
break;
case 'login':
default:
$f3->set('tplPage', 'login');
@@ -230,7 +248,6 @@ class Admin extends Controller{
*/
protected function banCharacter(CharacterModel $character, $banCharacterId, $value){
$banCharacters = $this->filterValidCharacters($character, $banCharacterId);
foreach($banCharacters as $banCharacter){
$banCharacter->ban($value);
$banCharacter->save();
@@ -248,7 +265,7 @@ class Admin extends Controller{
}
/**
* checks whether a $character has admin access rights for $charcterId
* checks whether a $character has admin access rights for $characterId
* -> must be in same corporation
* @param CharacterModel $character
* @param int $characterId
@@ -270,6 +287,52 @@ class Admin extends Controller{
return $characters;
}
/**
* @param CharacterModel $character
* @param int $mapId
* @param int $value
* @throws \Exception\PathfinderException
*/
protected function activateMap(CharacterModel $character, int $mapId, int $value){
$maps = $this->filterValidMaps($character, $mapId);
foreach($maps as $map){
$map->setActive((bool)$value);
$map->save($character);
}
}
/**
* @param CharacterModel $character
* @param int $mapId
* @throws \Exception\PathfinderException
*/
protected function deleteMap(CharacterModel $character, int $mapId){
$maps = $this->filterValidMaps($character, $mapId);
foreach($maps as $map){
$map->erase();
}
}
/**
* checks whether a $character has admin access rights for $mapId
* @param CharacterModel $character
* @param int $mapId
* @return array
* @throws \Exception\PathfinderException
*/
protected function filterValidMaps(CharacterModel $character, int $mapId) : array {
$maps = [];
if($character->role === 'SUPERADMIN'){
if($filterMaps = MapModel::getAll([$mapId])){
$maps = $filterMaps;
}
}else{
$maps = $character->getCorporation()->getMaps([$mapId], ['addInactive' => true, 'ignoreMapCount' => true]);
}
return $maps;
}
/**
* get log file for "admin" logs
* @param string $type
@@ -287,10 +350,8 @@ class Admin extends Controller{
*/
protected function initMembers(\Base $f3, CharacterModel $character){
$data = (object) [];
if($characterCorporation = $character->getCorporation()){
$corporations = [];
switch($character->role){
case 'SUPERADMIN':
if($accessCorporations = CorporationModel::getAll(['addNPC' => true])){
@@ -304,7 +365,7 @@ class Admin extends Controller{
foreach($corporations as $corporation){
if($characters = $corporation->getCharacters()){
$data->corpMembers[$corporation->name] = $corporation->getCharacters();
$data->corpMembers[$corporation->name] = $characters;
}
}
@@ -317,4 +378,34 @@ class Admin extends Controller{
$f3->set('tplMembers', $data);
}
/**
* init /maps page data
* @param \Base $f3
* @param CharacterModel $character
*/
protected function initMaps(\Base $f3, CharacterModel $character){
$data = (object) [];
if($characterCorporation = $character->getCorporation()){
$corporations = [];
switch($character->role){
case 'SUPERADMIN':
if($accessCorporations = CorporationModel::getAll(['addNPC' => true])){
$corporations = $accessCorporations;
}
break;
case 'CORPORATION':
$corporations[] = $characterCorporation;
break;
}
foreach($corporations as $corporation){
if($maps = $corporation->getMaps([], ['addInactive' => true, 'ignoreMapCount' => true])){
$data->corpMaps[$corporation->name] = $maps;
}
}
}
$f3->set('tplMaps', $data);
}
}

View File

@@ -52,6 +52,14 @@ class GitHub extends Controller\Controller {
$apiResponse = \Web::instance()->request($apiPath, $options );
if($apiResponse['body']){
$return = (object) [];
$return->releasesData = [];
$return->version = (object) [];
$return->version->current = Config::getPathfinderData('version');
$return->version->last = '';
$return->version->delta = null;
$return->version->dev = false;
// request succeeded -> format "Markdown" to "HTML"
// result is JSON formed
$releasesData = (array)json_decode($apiResponse['body']);
@@ -62,7 +70,24 @@ class GitHub extends Controller\Controller {
}
$md = \Markdown::instance();
foreach($releasesData as &$releaseData){
foreach($releasesData as $key => &$releaseData){
// check version ----------------------------------------------------------------------------------
if($key === 0){
$return->version->last = $releaseData->tag_name;
if(version_compare( $return->version->current, $return->version->last, '>')){
$return->version->dev = true;
}
}
if(
!$return->version->dev &&
version_compare( $releaseData->tag_name, $return->version->current, '>=')
){
$return->version->delta = ($key === count($releasesData) - 1) ? '>= ' . $key : $key;
}
// format body ------------------------------------------------------------------------------------
if(isset($releaseData->body)){
$body = $releaseData->body;
@@ -78,7 +103,10 @@ class GitHub extends Controller\Controller {
$releaseData->body = $md->convert( trim($body) );
}
}
$f3->set($cacheKey, $releasesData, $ttl);
$return->releasesData = $releasesData;
$f3->set($cacheKey, $return, $ttl);
}else{
// request failed -> cache failed result (respect API request limit)
$f3->set($cacheKey, false, 60 * 5);

View File

@@ -207,6 +207,11 @@ class Map extends Controller\AccessController {
'status' => (bool)Config::getPathfinderData('slack.status')
];
// Slack integration status -------------------------------------------------------------------------------
$return->discord = [
'status' => (bool)Config::getPathfinderData('discord.status')
];
$f3->set(self::CACHE_KEY_INIT, $return, $expireTimeCache );
}

View File

@@ -431,6 +431,7 @@ class System extends Controller\AccessController {
$rallyData['pokeDesktop'] = $rallyData['pokeDesktop'] === '1';
$rallyData['pokeMail'] = $rallyData['pokeMail'] === '1';
$rallyData['pokeSlack'] = $rallyData['pokeSlack'] === '1';
$rallyData['pokeDiscord'] = $rallyData['pokeDiscord'] === '1';
$rallyData['message'] = trim($rallyData['message']);
$system->sendRallyPoke($rallyData, $activeCharacter);

View File

@@ -806,6 +806,14 @@ class Setup extends Controller {
$label = '<i class="fa fa-fw fa-slack"></i> Rally point poke Slack';
$tooltip = 'If "enabled", map admins can set a Slack channel for rally point pokes.';
break;
case 'send_history_discord_enabled':
$label = '<i class="fa fa-fw fa-microphone"></i> History log Discord';
$tooltip = 'If "enabled", map admins can set a Discord channel were map logs get piped to.';
break;
case 'send_rally_discord_enabled':
$label = '<i class="fa fa-fw fa-microphone"></i> Rally point poke Discord';
$tooltip = 'If "enabled", map admins can set a Discord channel for rally point pokes.';
break;
case 'send_rally_mail_enabled':
$label = '<i class="fa fa-fw fa-envelope"></i> Rally point poke Email';
$tooltip = 'If "enabled", rally point pokes can be send by Email (SMTP config + recipient address required).';
@@ -1159,8 +1167,8 @@ class Setup extends Controller {
$dbVersion = 'unknown';
foreach($dbVersionParts as $dbVersionPart){
// check if this is a valid version number
// hint: MariaDB´s version is always the last valid version number...
if( version_compare( $dbVersionPart, '0.0.1', '>=' ) > 0 ){
// hint: MariaDB´s version is NOT always the last valid version number
if( version_compare( $dbVersionPart, '1', '>' ) > 0 ){
$dbVersion = $dbVersionPart;
}
}

View File

@@ -36,6 +36,8 @@ class Monolog extends \Prefab {
'mail' => 'Monolog\Handler\SwiftMailerHandler',
'slackMap' => 'lib\logging\handler\SlackMapWebhookHandler',
'slackRally' => 'lib\logging\handler\SlackRallyWebhookHandler',
'discordMap' => 'lib\logging\handler\SlackMapWebhookHandler', // use Slack handler for Discord
'discordRally' => 'lib\logging\handler\SlackRallyWebhookHandler', // use Slack handler for Discord
'zmq' => 'lib\logging\handler\ZMQHandler'
];

View File

@@ -250,6 +250,8 @@ abstract class AbstractLog implements LogInterface {
break;
case 'slackMap':
case 'slackRally':
case 'discordMap':
case 'discordRally':
$params = $this->getHandlerParamsSlack($handlerKey);
break;
default:

View File

@@ -19,7 +19,7 @@ abstract class BasicModel extends \DB\Cortex {
/**
* Hive key with DB object
* @var string
* @var string|DB\SQL
*/
protected $db = 'DB_PF';

View File

@@ -1081,6 +1081,11 @@ class CharacterModel extends BasicModel {
return array_merge($characterDataBase, $addData);
}
/**
* get all characters
* @param array $characterIds
* @return \DB\CortexCollection
*/
public static function getAll($characterIds = []){
$query = [
'active = :active AND id IN :characterIds',

View File

@@ -127,24 +127,29 @@ class CorporationModel extends BasicModel {
/**
* get all maps for this corporation
* @return MapModel[]
* @param array $mapIds
* @param array $options
* @return array
* @throws \Exception\PathfinderException
*/
public function getMaps(){
public function getMaps($mapIds = [], $options = []){
$maps = [];
$filter = ['active = ?', 1];
$this->filter('mapCorporations',
['active = ?', 1],
['order' => 'created']
);
if( !empty($mapIds) ){
$filter[0] .= ' AND mapId IN (?)';
$filter[] = $mapIds;
}
$this->filter('mapCorporations', $filter, ['order' => 'created']);
if($this->mapCorporations){
$mapCount = 0;
foreach($this->mapCorporations as $mapCorporation){
if(
$mapCorporation->mapId->isActive() &&
$mapCount < Config::getMapsDefaultConfig('corporation')['max_count']
){
$validActive = !$options['addInactive'] ? $mapCorporation->mapId->isActive() : true;
$validMapCount = !$options['ignoreMapCount'] ? $mapCount < Config::getMapsDefaultConfig('corporation')['max_count'] : true;
if($validActive && $validMapCount){
$maps[] = $mapCorporation->mapId;
$mapCount++;
}

View File

@@ -25,6 +25,7 @@ class MapModel extends AbstractMapTrackingModel {
const DATA_CACHE_KEY_CHARACTER = 'CHARACTERS';
const ERROR_SLACK_CHANNEL = 'Invalid #Slack channel column [%s]';
const ERROR_DISCORD_CHANNEL = 'Invalid #Discord channel column [%s]';
protected $fieldConf = [
'active' => [
@@ -133,6 +134,24 @@ class MapModel extends AbstractMapTrackingModel {
'default' => '',
'activity-log' => true
],
'discordUsername' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => '',
'activity-log' => true
],
'discordWebHookURLRally' => [
'type' => Schema::DT_VARCHAR256,
'nullable' => false,
'default' => '',
'validate' => true
],
'discordWebHookURLHistory' => [
'type' => Schema::DT_VARCHAR256,
'nullable' => false,
'default' => '',
'validate' => true
],
'systems' => [
'has-many' => ['Model\SystemModel', 'mapId']
],
@@ -191,59 +210,65 @@ class MapModel extends AbstractMapTrackingModel {
if(is_null($mapDataAll)){
// no cached map data found
$mapData = (object) [];
$mapData->id = $this->id;
$mapData->name = $this->name;
$mapData->icon = $this->icon;
$mapData->deleteExpiredConnections = $this->deleteExpiredConnections;
$mapData->deleteEolConnections = $this->deleteEolConnections;
$mapData->persistentAliases = $this->persistentAliases;
$mapData = (object) [];
$mapData->id = $this->id;
$mapData->name = $this->name;
$mapData->icon = $this->icon;
$mapData->deleteExpiredConnections = $this->deleteExpiredConnections;
$mapData->deleteEolConnections = $this->deleteEolConnections;
$mapData->persistentAliases = $this->persistentAliases;
// map scope
$mapData->scope = (object) [];
$mapData->scope->id = $this->scopeId->id;
$mapData->scope->name = $this->scopeId->name;
$mapData->scope->label = $this->scopeId->label;
$mapData->scope = (object) [];
$mapData->scope->id = $this->scopeId->id;
$mapData->scope->name = $this->scopeId->name;
$mapData->scope->label = $this->scopeId->label;
// map type
$mapData->type = (object) [];
$mapData->type->id = $this->typeId->id;
$mapData->type->name = $this->typeId->name;
$mapData->type->classTab = $this->typeId->classTab;
$mapData->type = (object) [];
$mapData->type->id = $this->typeId->id;
$mapData->type->name = $this->typeId->name;
$mapData->type->classTab = $this->typeId->classTab;
// map logging
$mapData->logging = (object) [];
$mapData->logging->activity = $this->isActivityLogEnabled();
$mapData->logging->history = $this->isHistoryLogEnabled();
$mapData->logging = (object) [];
$mapData->logging->activity = $this->isActivityLogEnabled();
$mapData->logging->history = $this->isHistoryLogEnabled();
// map Slack logging
$mapData->logging->slackHistory = $this->isSlackChannelEnabled('slackChannelHistory');
$mapData->logging->slackRally = $this->isSlackChannelEnabled('slackChannelRally');
$mapData->logging->slackWebHookURL = $this->slackWebHookURL;
$mapData->logging->slackUsername = $this->slackUsername;
$mapData->logging->slackIcon = $this->slackIcon;
$mapData->logging->slackChannelHistory = $this->slackChannelHistory;
$mapData->logging->slackChannelRally = $this->slackChannelRally;
$mapData->logging->slackHistory = $this->isSlackChannelEnabled('slackChannelHistory');
$mapData->logging->slackRally = $this->isSlackChannelEnabled('slackChannelRally');
$mapData->logging->slackWebHookURL = $this->slackWebHookURL;
$mapData->logging->slackUsername = $this->slackUsername;
$mapData->logging->slackIcon = $this->slackIcon;
$mapData->logging->slackChannelHistory = $this->slackChannelHistory;
$mapData->logging->slackChannelRally = $this->slackChannelRally;
// map Discord logging
$mapData->logging->discordRally = $this->isDiscordChannelEnabled('discordWebHookURLRally');
$mapData->logging->discordUsername = $this->discordUsername;
$mapData->logging->discordWebHookURLRally = $this->discordWebHookURLRally;
$mapData->logging->discordWebHookURLHistory = $this->discordWebHookURLHistory;
// map mail logging
$mapData->logging->mailRally = $this->isMailSendEnabled('RALLY_SET');
$mapData->logging->mailRally = $this->isMailSendEnabled('RALLY_SET');
// map access
$mapData->access = (object) [];
$mapData->access->character = [];
$mapData->access->corporation = [];
$mapData->access->alliance = [];
$mapData->access = (object) [];
$mapData->access->character = [];
$mapData->access->corporation = [];
$mapData->access->alliance = [];
$mapData->created = (object) [];
$mapData->created->created = strtotime($this->created);
$mapData->created = (object) [];
$mapData->created->created = strtotime($this->created);
if(is_object($this->createdCharacterId)){
$mapData->created->character = $this->createdCharacterId->getData();
$mapData->created->character = $this->createdCharacterId->getData();
}
$mapData->updated = (object) [];
$mapData->updated->updated = strtotime($this->updated);
$mapData->updated = (object) [];
$mapData->updated->updated = strtotime($this->updated);
if(is_object($this->updatedCharacterId)){
$mapData->updated->character = $this->updatedCharacterId->getData();
$mapData->updated->character = $this->updatedCharacterId->getData();
}
// get access object data ---------------------------------------------------------------------------------
@@ -315,11 +340,50 @@ class MapModel extends AbstractMapTrackingModel {
* @throws \Exception\ValidationException
*/
protected function validate_slackWebHookURL(string $key, string $val): bool {
return $this->validate_WebHookURL($key, $val, 'slack');
}
/**
* validate Discord History WebHook URL
* @param string $key
* @param string $val
* @return bool
* @throws \Exception\ValidationException
*/
protected function validate_discordWebHookURLHistory(string $key, string $val): bool {
return $this->validate_WebHookURL($key, $val, 'discord');
}
/**
* validate Discord Rally WebHook URL
* @param string $key
* @param string $val
* @return bool
* @throws \Exception\ValidationException
*/
protected function validate_discordWebHookURLRally(string $key, string $val): bool {
return $this->validate_WebHookURL($key, $val, 'discord');
}
/**
* validate Slack/Discord WebHook URL
* @param string $key
* @param string $val
* @param string $type
* @return bool
* @throws \Exception\ValidationException
*/
protected function validate_WebHookURL(string $key, string $val, string $type): bool {
$valid = true;
if( !empty($val) ){
$hosts = [
'slack' => 'hooks.slack.com',
'discord' => 'discordapp.com'
];
if(
!\Audit::instance()->url($val) ||
parse_url($val, PHP_URL_HOST) !== 'hooks.slack.com'
parse_url($val, PHP_URL_HOST) !== $hosts[$type]
){
$valid = false;
$this->throwValidationException($key);
@@ -386,7 +450,6 @@ class MapModel extends AbstractMapTrackingModel {
*/
public function afterEraseEvent($self, $pkeys){
$self->clearCacheData();
$self->logActivity('mapDelete');
$self->deleteLogFile();
}
@@ -873,6 +936,13 @@ class MapModel extends AbstractMapTrackingModel {
$log->addHandlerGroup('slackMap');
}
// send map history to Discord channel ------------------------------------------------------------------------
$discordChannelKey = 'discordWebHookURLHistory';
if($this->isDiscordChannelEnabled($discordChannelKey)){
$log->addHandler('discordMap', null, $this->getDiscordWebHookConfig($discordChannelKey));
$log->addHandlerGroup('discordMap');
}
// update map activity ----------------------------------------------------------------------------------------
$log->logActivity($this->isActivityLogEnabled());
@@ -959,6 +1029,34 @@ class MapModel extends AbstractMapTrackingModel {
return $enabled;
}
/**
* check if "Discord WebHook" is enabled for this map type
* @param string $channel
* @return bool
* @throws PathfinderException
*/
public function isDiscordChannelEnabled(string $channel): bool {
$enabled = false;
// check global Slack status
if((bool)Config::getPathfinderData('discord.status')){
// check global map default config for this channel
switch($channel){
case 'discordWebHookURLHistory': $defaultMapConfigKey = 'send_history_discord_enabled'; break;
case 'discordWebHookURLRally': $defaultMapConfigKey = 'send_rally_discord_enabled'; break;
default: throw new PathfinderException(sprintf(self::ERROR_DISCORD_CHANNEL, $channel));
}
if((bool) Config::getMapsDefaultConfig($this->typeId->name)[$defaultMapConfigKey]){
$config = $this->getDiscordWebHookConfig($channel);
if($config->slackWebHookURL){
$enabled = true;
}
}
}
return $enabled;
}
/**
* check if "E-Mail" Log is enabled for this map
* @param string $type
@@ -1018,6 +1116,20 @@ class MapModel extends AbstractMapTrackingModel {
return $config;
}
/**
* get Config for Discord WebHook cURL calls
* @param string $channel
* @return \stdClass
*/
public function getDiscordWebHookConfig(string $channel = ''): \stdClass {
$config = (object) [];
$config->slackUsername = $this->discordUsername;
if($channel && $this->exists($channel)){
$config->slackWebHookURL = $this->$channel . '/slack';
}
return $config;
}
/**
* get Config for SMTP connection and recipient address
* @param string $type
@@ -1222,9 +1334,12 @@ class MapModel extends AbstractMapTrackingModel {
/**
* @param CharacterModel|null $characterModel
* @return false|ConnectionModel
* @return false|MapModel
*/
public function save(CharacterModel $characterModel = null){
/**
* @var $mapModel MapModel
*/
$mapModel = parent::save($characterModel);
// check if map type has changed and clear access objects
@@ -1241,4 +1356,18 @@ class MapModel extends AbstractMapTrackingModel {
return $mapModel;
}
/**
* get all maps
* @param array $mapIds
* @return \DB\CortexCollection
*/
public static function getAll($mapIds = []){
$query = [
'active = :active AND id IN :mapIds',
':active' => 1,
':mapIds' => $mapIds
];
return (new self())->find($query);
}
}

View File

@@ -565,6 +565,7 @@ class SystemModel extends AbstractMapTrackingModel {
/**
* send rally point poke to various "APIs"
* -> send to a Slack channel
* -> send to a Discord channel
* -> send to an Email
* @param array $rallyData
* @param CharacterModel $characterModel
@@ -585,6 +586,17 @@ class SystemModel extends AbstractMapTrackingModel {
$log->addHandler('slackRally', null, $this->getMap()->getSlackWebHookConfig($slackChannelKey));
}
// Discord poke ---------------------------------------------------------------------------
$discordChannelKey = 'discordWebHookURLRally';
if(
$rallyData['pokeDiscord'] === true &&
$this->getMap()->isDiscordChannelEnabled($discordChannelKey)
){
$isValidLog = true;
$log->addHandler('discordRally', null, $this->getMap()->getDiscordWebHookConfig($discordChannelKey));
}
// Mail poke ------------------------------------------------------------------------------
$mailAddressKey = 'RALLY_SET';
if(

View File

@@ -3,7 +3,7 @@
[PATHFINDER]
NAME = Pathfinder
; installed version (used for CSS/JS cache busting)
VERSION = v1.3.1
VERSION = v1.3.2
; contact information [optional]
CONTACT = https://github.com/exodus4d
; public contact email [optional]
@@ -36,6 +36,11 @@ ALLIANCE =
; Global Slack API status, check PATHFINDER.MAP section for individual control (0=disabled, 1=enabled)
STATUS = 1
; Slack API integration ===========================================================================
[PATHFINDER.DISCORD]
; Global Discord API status, check PATHFINDER.MAP section for individual control (0=disabled, 1=enabled)
STATUS = 1
; View ============================================================================================
[PATHFINDER.VIEW]
; static page templates
@@ -82,28 +87,34 @@ LOG_ACTIVITY_ENABLED = 1
LOG_HISTORY_ENABLED = 1
SEND_HISTORY_SLACK_ENABLED = 0
SEND_RALLY_SLACK_ENABLED = 1
SEND_HISTORY_DISCORD_ENABLED = 0
SEND_RALLY_DISCORD_ENABLED = 1
SEND_RALLY_Mail_ENABLED = 0
[PATHFINDER.MAP.CORPORATION]
LIFETIME = 99999
MAX_COUNT = 3
MAX_SHARED = 3
MAX_COUNT = 5
MAX_SHARED = 4
MAX_SYSTEMS = 100
LOG_ACTIVITY_ENABLED = 1
LOG_HISTORY_ENABLED = 1
SEND_HISTORY_SLACK_ENABLED = 1
SEND_RALLY_SLACK_ENABLED = 1
SEND_HISTORY_DISCORD_ENABLED = 1
SEND_RALLY_DISCORD_ENABLED = 1
SEND_RALLY_Mail_ENABLED = 0
[PATHFINDER.MAP.ALLIANCE]
LIFETIME = 99999
MAX_COUNT = 3
MAX_COUNT = 4
MAX_SHARED = 2
MAX_SYSTEMS = 100
LOG_ACTIVITY_ENABLED = 0
LOG_HISTORY_ENABLED = 1
SEND_HISTORY_SLACK_ENABLED = 1
SEND_RALLY_SLACK_ENABLED = 1
SEND_HISTORY_DISCORD_ENABLED = 1
SEND_RALLY_DISCORD_ENABLED = 1
SEND_RALLY_Mail_ENABLED = 0
; Route search ====================================================================================

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.1"
"exodus4d/pathfinder_esi": "dev-master#v1.2.2"
}
}

View File

@@ -16,7 +16,7 @@ define([
'dialog/account_settings',
'dialog/notification',
'dialog/manual',
'dialog/releases',
'dialog/changelog',
'dialog/credit'
], function($, Init, Util, Render, Gallery, bootbox) {
@@ -81,7 +81,7 @@ define([
*/
let setVersionLinkObserver = function(){
$('.' + config.navigationVersionLinkClass).off('click').on('click', function(e){
$.fn.releasesDialog();
$.fn.changelogsDialog();
});
};

400
js/app/map/layout.js Normal file
View File

@@ -0,0 +1,400 @@
define(() => {
'use strict';
class Position {
constructor(config) {
this._defaultConfig = {
container: null, // parent DOM container element
center: null, // DOM elements that works as center
elementClass: 'pf-system', // class for all elements
defaultGapX: 50,
defaultGapY: 50,
gapX: 50, // leave gap between elements (x-axis)
gapY: 50, // leave gap between elements (y-axis)
loops: 2, // max loops around "center" for search
grid: false, // set to [20, 20] to force grid snapping
newElementWidth: 100, // width for new element
newElementHeight: 22, // height for new element
debug: false, // render debug elements
debugElementClass: 'pf-system-debug' // class for debug elements
};
this._config = Object.assign({}, this._defaultConfig, config);
/**
* convert degree into radial unit
* @param deg
* @returns {number}
* @private
*/
this._degToRad = (deg) => {
return deg * Math.PI / 180;
};
/**
* get element dimension/position of a DOM element
* @param element
* @returns {*}
* @private
*/
this._getElementDimension = (element) => {
let dim = null;
let left = 0;
let top = 0;
let a = 0;
let b = 0;
let width = this._config.newElementWidth;
let height = this._config.newElementHeight;
if(Array.isArray(element)){
// xy coordinates
let point = [
element[0] ? parseInt(element[0], 10) : 0,
element[1] ? parseInt(element[1], 10) : 0
];
if(this._config.grid){
point = this._transformPointToGrid(point);
}
left = point[0];
top = point[1];
a = this._config.gapX ;
b = this._config.gapY ;
}else if(element){
// DOM element
left = element.style.left ? parseInt(element.style.left, 10) : 0;
top = element.style.top ? parseInt(element.style.top, 10) : 0;
a = parseInt((element.offsetWidth / 2).toString(), 10) + this._config.gapX;
b = parseInt((element.offsetHeight / 2).toString(), 10) + this._config.gapY;
width = element.offsetWidth;
height = element.offsetHeight;
}
// add "gap" to a and b in order to have some space around elements
return {
left: left,
top: top,
a: a,
b: b,
width: width,
height: height
};
};
/**
* get x/y coordinate on an eclipse around a 2D area by a given radial angle
* @param dim
* @param angle
* @returns {*}
* @private
*/
this._getEllipseCoordinates = (dim, angle) => {
let coordinates = null;
if(dim){
angle = this._degToRad(angle);
coordinates = {
x: Math.round((dim.a * dim.b) / Math.sqrt(Math.pow(dim.b, 2) + Math.pow(dim.a, 2) * Math.pow(Math.tan(angle), 2) )),
y: Math.round((dim.a * dim.b) / Math.sqrt(Math.pow(dim.a, 2) + Math.pow(dim.b, 2) / Math.pow(Math.tan(angle), 2) ))
};
// invert coordinate based on quadrant ------------------------------------------------------------
if( angle > (Math.PI / 2) && angle < (3 * Math.PI / 2) ){
coordinates.x = coordinates.x * -1;
}
if( angle > Math.PI && angle < (2 * Math.PI) ){
coordinates.y = coordinates.y * -1;
}
}
return coordinates;
};
/**
* get dimensions of all surrounding elements
* @returns {Array}
* @private
*/
this._getAllElementDimensions = () => {
let dimensions = [];
let surroundingElements = this._getContainer().getElementsByClassName(this._config.elementClass);
for(let element of surroundingElements){
dimensions.push(this._getElementDimension(element));
}
return dimensions;
};
/**
* transform a x/y point into a x/y point that snaps to grid
* @param point
* @returns {*}
* @private
*/
this._transformPointToGrid = (point) => {
point[0] = Math.floor(point[0] / this._config.grid[0]) * this._config.grid[0];
point[1] = Math.floor(point[1] / this._config.grid[1]) * this._config.grid[1];
return point;
};
/**
* Transform a x/y coordinate into a 2D element with width/height
* @param centerDimension
* @param coordinate
* @returns {*}
* @private
*/
this._transformCoordinate = (centerDimension, coordinate) => {
let dim = null;
if(centerDimension && coordinate){
let left = 0;
let top = 0;
// calculate left/top based on coordinate system quadrant -----------------------------------------
// -> flip horizontal in Q2 and Q3
if(coordinate.x >= 0 && coordinate.y > 0){
// 1. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x;
top = centerDimension.top + 2 * (centerDimension.b - this._config.gapY) - Math.abs(coordinate.y) - this._config.newElementHeight;
}else if(coordinate.x < 0 && coordinate.y > 0){
// 2. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x - this._config.newElementWidth;
top = centerDimension.top + 2 * (centerDimension.b - this._config.gapY) - Math.abs(coordinate.y) - this._config.newElementHeight;
}else if(coordinate.x < 0 && coordinate.y <= 0){
// 3. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x - this._config.newElementWidth;
top = centerDimension.top + Math.abs(coordinate.y);
}else{
// 4. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x;
top = centerDimension.top + Math.abs(coordinate.y);
}
// center horizontal for x = 0 coordinate (top and bottom element) --------------------------------
if(coordinate.x === 0){
left -= Math.round(this._config.newElementWidth / 2);
}
// transform to grid coordinates (if grid snapping is enabled) ------------------------------------
if(this._config.grid){
let point = this._transformPointToGrid([left, top]);
left = point[0];
top = point[1];
}
dim = {
left: left,
top: top,
width: this._config.newElementWidth,
height: this._config.newElementHeight
};
}
return dim;
};
/**
* calc overlapping percent of two given dimensions
* @param dim1
* @param dim2
* @returns {number}
* @private
*/
this._percentCovered = (dim1, dim2) => {
let percent = 0;
if (
(dim1.left <= dim2.left) &&
(dim1.top <= dim2.top) &&
((dim1.left + dim1.width) >= (dim2.left + dim2.width)) &&
((dim1.top + dim1.height) > (dim2.top + dim2.height))
) {
// The whole thing is covering the whole other thing
percent = 100;
}else{
// Only parts may be covered, calculate percentage
dim1.right = dim1.left + dim1.width;
dim1.bottom = dim1.top + dim1.height;
dim2.right = dim2.left + dim2.width;
dim2.bottom = dim2.top + dim2.height;
let l = Math.max(dim1.left, dim2.left);
let r = Math.min(dim1.right, dim2.right);
let t = Math.max(dim1.top, dim2.top);
let b = Math.min(dim1.bottom, dim2.bottom);
if (b >= t && r >= l) {
percent = (((r - l) * (b - t)) / (dim2.width * dim2.height)) * 100;
}
}
return percent;
};
/**
* checks whether dim1 is partially overlapped by any other element
* @param dim1
* @param dimensionContainer
* @param allDimensions
* @returns {boolean}
* @private
*/
this._isOverlapping = (dim1, dimensionContainer, allDimensions) => {
let isOverlapping = false;
if(dim1){
if(this._percentCovered(dimensionContainer, dim1 ) === 100){
// element is within parent container
for(let dim2 of allDimensions){
let percentCovered = this._percentCovered(dim1, dim2);
if(percentCovered){
isOverlapping = true;
// render debug element
this._showDebugElement(dim1, percentCovered);
break;
}
}
}else{
isOverlapping = true;
this._showDebugElement(dim1, 100);
}
}else{
isOverlapping = true;
}
return isOverlapping;
};
/**
* find all dimensions around a centerDimension that are not overlapped by other elements
* @param maxResults
* @param steps
* @param allDimensions
* @param loops
* @returns {Array}
* @private
*/
this._findDimensions = (maxResults, steps, allDimensions, loops) => {
let dimensions = [];
let start = 0;
let end = 360;
let angle = end / steps;
let dimensionContainer = this._getElementDimension(this._getContainer());
steps = steps || 1;
loops = loops || 1;
if(loops === 1){
// check center element
let centerDimension = this._getElementDimension(this._config.center);
if (!this._isOverlapping(centerDimension, dimensionContainer, allDimensions)) {
dimensions.push({
left: centerDimension.left,
top: centerDimension.top,
width: centerDimension.width,
height: centerDimension.height
});
// render debug element
this._showDebugElement(centerDimension, 0);
maxResults--;
}
}
// increase the "gab" between center element and potential found dimensions...
// ... for each recursive loop call, to get an elliptical cycle beyond
this._config.gapX = this._config.defaultGapX + (loops - 1) * 20;
this._config.gapY = this._config.defaultGapY + (loops - 1) * 20;
let centerDimension = this._getElementDimension(this._config.center);
while(maxResults > 0 && start < end){
// get all potential coordinates on an eclipse around a given "centerElementDimension"
let coordinate = this._getEllipseCoordinates(centerDimension, end);
// transform relative x/y coordinate into a absolute 2D area
let checkDimension = this._transformCoordinate(centerDimension, coordinate);
if (!this._isOverlapping(checkDimension, dimensionContainer, allDimensions)) {
dimensions.push({
left: checkDimension.left,
top: checkDimension.top,
width: checkDimension.width,
height: checkDimension.height
});
// render debug element
this._showDebugElement(checkDimension, 0);
maxResults--;
}
end -= angle;
}
if(maxResults > 0 && loops < this._config.loops){
loops++;
steps *= 2;
dimensions = dimensions.concat(this._findDimensions(maxResults, steps, allDimensions, loops));
}
return dimensions;
};
/**
* get container (parent) element
* @returns {*}
* @private
*/
this._getContainer = () => {
return this._config.container ? this._config.container : document.body;
};
/**
* render debug element into parent container
* -> checks overlapping dimension with other elements
* @param dimension
* @param percentCovered
* @private
*/
this._showDebugElement = (dimension, percentCovered) => {
if(this._config.debug){
let element = document.createElement('div');
element.style.left = dimension.left + 'px';
element.style.top = dimension.top + 'px';
element.style.width = dimension.width + 'px';
element.style.height = dimension.height + 'px';
element.style.backgroundColor = Boolean(percentCovered) ? 'rgba(255,0,0,0.1)' : 'rgba(0,255,0,0.1)';
element.innerHTML = Math.round(percentCovered * 100) / 100 + '%';
element.classList.add(this._config.debugElementClass);
this._getContainer().appendChild(element);
}
};
/**
* hide all debug elements
* @private
*/
this._hideDebugElements = () => {
let debugElements = this._getContainer().getElementsByClassName(this._config.debugElementClass);
while(debugElements.length > 0){
debugElements[0].parentNode.removeChild(debugElements[0]);
}
};
// public functions ---------------------------------------------------------------------------------------
/**
* search for surrounding, non overlapping dimensions
* @param maxResults
* @param steps
* @returns {Array}
*/
this.findNonOverlappingDimensions = (maxResults, steps) => {
this._hideDebugElements();
// element dimensions that exist and should be checked for overlapping
let allDimensions = this._getAllElementDimensions();
return this._findDimensions(maxResults, steps, allDimensions);
};
}
}
return {
Position: Position
};
});

View File

@@ -10,6 +10,7 @@ define([
'bootbox',
'app/map/util',
'app/map/system',
'app/map/layout',
'app/map/magnetizing',
'app/map/scrollbar',
'dragToSelect',
@@ -17,7 +18,7 @@ define([
'app/map/contextmenu',
'app/map/overlay',
'app/map/local'
], function($, Init, Util, Render, bootbox, MapUtil, System, MagnetizerWrapper) {
], function($, Init, Util, Render, bootbox, MapUtil, System, Layout, MagnetizerWrapper) {
'use strict';
@@ -25,7 +26,6 @@ define([
zIndexCounter: 110,
maxActiveConnections: 8,
mapSnapToGrid: false, // "Snap to Grid" feature for drag&drop systems on map (optional)
mapWrapperClass: 'pf-map-wrapper', // wrapper div (scrollable)
mapClass: 'pf-map', // class for all maps
@@ -1569,6 +1569,7 @@ define([
// get new position
newPosition = System.calculateNewSystemPosition(sourceSystem);
}else{
// check mouse cursor position (add system to map)
newPosition = {
@@ -2062,7 +2063,7 @@ define([
let mapContainer = $( map.getContainer() );
let systemHeadExpand = $( system.find('.' + config.systemHeadExpandClass) );
let systemBody = $( system.find('.' + config.systemBodyClass) );
let grid = [MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension];
// map overlay will be set on "drag" start
let mapOverlayTimer = null;
@@ -2083,7 +2084,7 @@ define([
// check if grid-snap is enable -> this enables napping for !CURRENT! Element
if( mapContainer.hasClass(MapUtil.config.mapGridClass) ){
params.drag.params.grid = [MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension];
params.drag.params.grid = grid;
}else{
delete( params.drag.params.grid );
}
@@ -2661,6 +2662,24 @@ define([
switch(action){
case 'add_system':
// add new system dialog
let grid = [MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension];
let positionFinder = new Layout.Position({
container: currentMapElement[0],
center: [position.x, position.y],
loops: 5,
defaultGapX: 10,
defaultGapY: 10,
grid: currentMapElement.hasClass(MapUtil.config.mapGridClass) ? grid : false,
debug: false
});
let dimensions = positionFinder.findNonOverlappingDimensions(1, 8);
if(dimensions.length){
position.x = dimensions[0].left;
position.y = dimensions[0].top;
}
showNewSystemDialog(currentMap, {position: position});
break;
case 'select_all':

View File

@@ -8,8 +8,9 @@ define([
'app/init',
'app/util',
'bootbox',
'app/map/util'
], ($, Init, Util, bootbox, MapUtil) => {
'app/map/util',
'app/map/layout'
], ($, Init, Util, bootbox, MapUtil, Layout) => {
'use strict';
let config = {
@@ -24,6 +25,7 @@ define([
dialogRallyPokeDesktopId: 'pf-rally-dialog-poke-desktop', // id for "desktop" poke checkbox
dialogRallyPokeSlackId: 'pf-rally-dialog-poke-slack', // id for "Slack" poke checkbox
dialogRallyPokeDiscordId: 'pf-rally-dialog-poke-discord', // id for "Discord" poke checkbox
dialogRallyPokeMailId: 'pf-rally-dialog-poke-mail', // id for "mail" poke checkbox
dialogRallyMessageId: 'pf-rally-dialog-message', // id for "message" textarea
@@ -88,11 +90,13 @@ define([
dialogRallyPokeDesktopId: config.dialogRallyPokeDesktopId,
dialogRallyPokeSlackId: config.dialogRallyPokeSlackId,
dialogRallyPokeDiscordId: config.dialogRallyPokeDiscordId,
dialogRallyPokeMailId: config.dialogRallyPokeMailId,
dialogRallyMessageId: config.dialogRallyMessageId ,
desktopRallyEnabled: true,
slackRallyEnabled: Boolean(Util.getObjVal(mapData, 'config.logging.slackRally')),
discordRallyEnabled: Boolean(Util.getObjVal(mapData, 'config.logging.discordRally')),
mailRallyEnabled: Boolean(Util.getObjVal(mapData, 'config.logging.mailRally')),
dialogRallyMessageDefault: config.dialogRallyMessageDefault,
@@ -132,6 +136,8 @@ define([
}
});
rallyDialog.initTooltips();
// after modal is shown ==================================================================================
rallyDialog.on('shown.bs.modal', function(e){
// set event for checkboxes
@@ -293,21 +299,45 @@ define([
/**
* calculate the x/y coordinates for a new system - relativ to a source system
* @param sourceSystem
* @param grid
* @returns {{x: *, y: *}}
*/
let calculateNewSystemPosition = function(sourceSystem){
let mapContainer = sourceSystem.parent();
let grid = [MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension];
// related system is available
let currentX = sourceSystem.css('left');
let currentY = sourceSystem.css('top');
let x = 0;
let y = 0;
// remove "px"
currentX = parseInt( currentX.substring(0, currentX.length - 2) );
currentY = parseInt( currentY.substring(0, currentY.length - 2) );
let positionFinder = new Layout.Position({
container: mapContainer[0],
center: sourceSystem[0],
loops: 4,
grid: mapContainer.hasClass(MapUtil.config.mapGridClass) ? grid : false,
debug: false
});
let dimensions = positionFinder.findNonOverlappingDimensions(1, 16);
if(dimensions.length){
//... empty map space found
x = dimensions[0].left;
y = dimensions[0].top;
}else{
//... fallback
// related system is available
let currentX = sourceSystem.css('left');
let currentY = sourceSystem.css('top');
// remove "px"
currentX = parseInt( currentX.substring(0, currentX.length - 2) );
currentY = parseInt( currentY.substring(0, currentY.length - 2) );
x = currentX + config.newSystemOffset.x;
y = currentY + config.newSystemOffset.y;
}
let newPosition = {
x: currentX + config.newSystemOffset.x,
y: currentY + config.newSystemOffset.y
x: x,
y: y
};
return newPosition;

View File

@@ -62,48 +62,39 @@ define([
* @returns {Array}
*/
let getMapTypes = (filterByUser) => {
let mapTypes = [];
$.each(Init.mapTypes, function(prop, data){
// skip "default" type -> just for 'add' icon
if(data.label.length > 0){
let tempData = data;
tempData.name = prop;
mapTypes.push(tempData);
}
});
let mapTypes = Object.assign({}, Init.mapTypes);
if(filterByUser === true){
let corporationId = Util.getCurrentUserInfo('corporationId');
let allianceId = Util.getCurrentUserInfo('allianceId');
let authorizedMapTypes = [];
// check if character data exists
if(corporationId > 0) {
authorizedMapTypes.push('corporation');
}
if(allianceId > 0){
authorizedMapTypes.push('alliance');
}
let checkMapTypes = ['private', 'corporation', 'alliance'];
// private maps are always allowed
authorizedMapTypes.push('private');
// compare "all" map types with "authorized" types
let tempMapTypes = [];
for(let i = 0; i < mapTypes.length; i++){
for(let j = 0; j < authorizedMapTypes.length; j++){
if(mapTypes[i].name === authorizedMapTypes[j]){
tempMapTypes.push(mapTypes[i]);
break;
for(let i = 0; i < checkMapTypes.length; i++){
let objectId = Util.getCurrentUserInfo(checkMapTypes[i] + 'Id');
if(objectId > 0) {
// check if User could add new map with a mapType
let currentObjectMapData = Util.filterCurrentMapData('config.type.id', Util.getObjVal(mapTypes, checkMapTypes[i] + '.id'));
let maxCountObject = Util.getObjVal(mapTypes, checkMapTypes[i] + '.defaultConfig.max_count');
if(currentObjectMapData.length < maxCountObject){
authorizedMapTypes.push(checkMapTypes[i]);
}
}
}
mapTypes = tempMapTypes;
for(let mapType in mapTypes) {
if(authorizedMapTypes.indexOf(mapType) < 0){
delete( mapTypes[mapType] );
}
}
}
return mapTypes;
// convert to array
let mapTypesFlat = [];
for(let mapType in mapTypes){
mapTypes[mapType].name = mapType;
mapTypesFlat.push(mapTypes[mapType]);
}
return mapTypesFlat;
};
/**

View File

@@ -10,10 +10,10 @@ define([
'app/logging',
'app/page',
'app/map/worker',
'app/module_map',
'app/key',
'app/ui/form_element',
'app/module_map'
], ($, Init, Util, Render, Logging, Page, MapWorker) => {
'app/ui/form_element'
], ($, Init, Util, Render, Logging, Page, MapWorker, ModuleMap) => {
'use strict';
@@ -69,6 +69,7 @@ define([
Init.routes = initData.routes;
Init.url = initData.url;
Init.slack = initData.slack;
Init.discord = initData.discord;
Init.routeSearch = initData.routeSearch;
Init.programMode = initData.programMode;
@@ -111,12 +112,12 @@ define([
switch(MsgWorkerMessage.task()){
case 'mapUpdate':
Util.updateCurrentMapData( MsgWorkerMessage.data() );
mapModule.updateMapModule();
ModuleMap.updateMapModule(mapModule);
break;
case 'mapAccess':
case 'mapDeleted':
Util.deleteCurrentMapData( MsgWorkerMessage.data() );
mapModule.updateMapModule();
ModuleMap.updateMapModule(mapModule);
break;
}
@@ -265,22 +266,24 @@ define([
Util.setCurrentMapData(data.mapData);
// load/update main map module
mapModule.updateMapModule();
ModuleMap.updateMapModule(mapModule).then(() => {
// 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 );
// get the current update delay (this can change if a user is inactive)
let mapUpdateDelay = Util.getCurrentTriggerDelay( logKeyServerMapData, 0 );
// init new trigger
initMapUpdatePing(false);
// init new trigger
initMapUpdatePing(false);
// initial start for the userUpdate trigger
// this should only be called at the first time!
if(updateTimeouts.userUpdate === 0){
// start user update trigger after map loaded
updateTimeouts.userUpdate = setTimeout(() => {
triggerUserUpdatePing();
}, 1000);
}
// initial start for the userUpdate trigger
// this should only be called at the first time!
if(updateTimeouts.userUpdate === 0){
// start user update trigger after map loaded
updateTimeouts.userUpdate = setTimeout(() => {
triggerUserUpdatePing();
}, 1000);
}
});
}
}).fail(handleAjaxErrorResponse);
@@ -288,7 +291,6 @@ define([
// skip this mapUpdate trigger and init next one
initMapUpdatePing(false);
}
};
// ping for user data update =======================================================

File diff suppressed because it is too large Load Diff

View File

@@ -34,16 +34,15 @@ define([
moduleTableClass: 'pf-module-table', // class for module tables
connectionInfoTableLabelSourceClass: 'pf-connection-info-label-source', // class for source label
connectionInfoTableLabelTargetClass: 'pf-connection-info-label-target', // class for target label
connectionInfoTableRowMassLogClass: 'pf-connection-info-row-mass-log', // class for "logged mass" table row
connectionInfoTableRowMassShipClass: 'pf-connection-info-row-mass-ship', // class for "current ship mass" table row
connectionInfoTableCellConnectionClass: 'pf-connection-info-connection', // class for connection "fake" table cell
connectionInfoTableCellMassTotalTooltipClass: 'pf-connection-info-mass-total-tooltip', // class for "mass total tooltip" table cell
connectionInfoTableCellMassTotalClass: 'pf-connection-info-mass-total', // class for "mass total" table cell
connectionInfoTableCellMassLogClass: 'pf-connection-info-mass-log', // class for "mass log" table cell
connectionInfoTableCellMassLogClass: 'pf-connection-info-mass-log', // class for "mass logged" table cell
connectionInfoTableCellMassShipClass: 'pf-connection-info-mass-ship', // class for "current ship mass" table cell
connectionInfoTableCellMassLeftClass: 'pf-connection-info-mass-left', // class for "mass left" table cell
connectionInfoTableTooltipIconClass: 'pf-connection-info-tooltip-icon', // class for "tooltip" icon
connectionInfoTableWarningIconClass: 'pf-connection-info-warning-icon', // class for "warning" icon
// dataTable
connectionInfoTableClass: 'pf-connection-info-table', // class for connection tables
tableCellImageClass: 'pf-table-image-cell', // class for table "image" cells
@@ -126,12 +125,13 @@ define([
let scopeLabel = MapUtil.getScopeInfoForConnection(connectionData.scope, 'label');
// connection type (dummy) classes --------------------------------------------------------
let connectionClasses = ['pf-fake-connection'];
for(let i = 0; i < connectionData.type.length; i++){
connectionClasses.push( MapUtil.getConnectionInfo( connectionData.type[i], 'cssClass') );
}
let massLog = 0;
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'
@@ -196,10 +196,10 @@ define([
text: scopeLabel.charAt(0).toUpperCase() + scopeLabel.slice(1)
}),
$('<td>', {
class: ['text-right'].join(' ')
class: ['text-right', config.connectionInfoTableCellConnectionClass].join(' ')
}).append(
$('<div>', {
class: connectionClasses.join(' ')
class: getConnectionClasses(connectionData.type).join(' ')
})
)
),
@@ -216,12 +216,33 @@ define([
class: ['text-right', 'txt-color', config.connectionInfoTableCellMassTotalClass].join(' ')
})
),
$('<tr>').append(
$('<tr>', {
class: config.connectionInfoTableRowMassLogClass
}).append(
$('<td>', {
class: ['text-right', 'pf-help'].join(' '),
html: '<i class="fa fa-fw fa-question-circle"></i>',
title: 'recorded total jump mass'
}).attr('data-toggle', 'tooltip'),
}).attr('data-toggle', 'tooltip').append(
$('<i>', {
class: [
'fa', 'fa-fw', 'fa-question-circle'
].join(' ')
}),
$('<i>', {
class: [
'fa', 'fa-fw', 'fa-adjust',
'txt-color', 'txt-color-warning',
'hidden'
].join(' ')
}),
$('<i>', {
class: [
'fa', 'fa-fw', 'fa-circle-o',
'txt-color', 'txt-color-danger',
'hidden'
].join(' ')
})
),
$('<td>', {
text: 'Logged mass'
}),
@@ -237,16 +258,13 @@ define([
title: 'current ship mass'
}).attr('data-toggle', 'tooltip').append(
$('<i>', {
class: [
'fa', 'fa-fw', 'fa-question-circle',
config.connectionInfoTableTooltipIconClass
].join(' ')
class: ['fa', 'fa-fw', 'fa-question-circle'].join(' ')
}),
$('<i>', {
class: [
'fa', 'fa-fw', 'fa-exclamation-triangle',
'txt-color', 'txt-color-danger',
'hidden', config.connectionInfoTableWarningIconClass
'hidden'
].join(' ')
})
),
@@ -292,8 +310,8 @@ define([
targetLabelElement.html(MapUtil.getEndpointOverlayContent(targetLabel));
// remove K162
sourceLabel.diff(['K162']);
targetLabel.diff(['K162']);
sourceLabel = sourceLabel.diff(['K162']);
targetLabel = targetLabel.diff(['K162']);
// get static wormhole data by endpoint Labels
let wormholeName = '';
@@ -325,25 +343,33 @@ define([
}).on('pf:calcInfoTable', function(e){
// re-calculate information table from .data() cell values ------------------------
let tableElement = $(this);
let connectionData = tableElement.data('connectionData');
let massChartCell = tableElement.find('[data-percent]');
let wormholeData = tableElement.data('wormholeData');
let shipData = null;
let shipName = '';
let showShip = Boolean(tableElement.data('showShip'));
let massLogRow = tableElement.find('.' + config.connectionInfoTableRowMassLogClass);
let massShipRow = tableElement.find('.' + config.connectionInfoTableRowMassShipClass);
// icons
let massShipTooltipIcon = massShipRow.find('.' + config.connectionInfoTableTooltipIconClass);
let massShipWarningIcon = massShipRow.find('.' + config.connectionInfoTableWarningIconClass);
let massLogTooltipIcon = massLogRow.find('i.fa-question-circle');
let massLogStage2Icon = massLogRow.find('i.fa-adjust');
let massLogStage3Icon = massLogRow.find('i.fa-circle-o');
let massShipTooltipIcon = massShipRow.find('i.fa-question-circle');
let massShipWarningIcon = massShipRow.find('i.fa-exclamation-triangle');
// table cells
let connectionCell = tableElement.find('.' + config.connectionInfoTableCellConnectionClass);
let massTotalCell = tableElement.find('.' + config.connectionInfoTableCellMassTotalClass);
let massLogCell = tableElement.find('.' + config.connectionInfoTableCellMassLogClass);
let massShipCell = tableElement.find('.' + config.connectionInfoTableCellMassShipClass);
let massLeftCell = tableElement.find('.' + config.connectionInfoTableCellMassLeftClass);
let massTotal = null; // initial connection mass
let massLog = massLogCell.data('mass'); // recorded mass
let massReduction = 0; // default reduction (e.g. reduced, crit) in percent
let massLog = massLogCell.data('mass'); // recorded mass
let massLogTotal = massLog; // recorded mass + current ship
let massIndividual = null; // mass mass per jump
let massShip = 0; // current ship
@@ -355,6 +381,36 @@ define([
massIndividual = parseInt(wormholeData.massIndividual);
}
// get connection type (show fake connection div) ---------------------------------
connectionCell.find('div').removeClass().addClass(getConnectionClasses(connectionData.type).join(' '));
// get wormhole status ------------------------------------------------------------
if(connectionData.type.indexOf('wh_critical') !== -1){
massReduction = 90;
massLogTooltipIcon.toggleClass('hidden', true);
massLogStage2Icon.toggleClass('hidden', true);
massLogStage3Icon.toggleClass('hidden', false);
massLogStage3Icon.parent().attr('title', 'stage 3 (critical)').tooltip('fixTitle');
}else if(connectionData.type.indexOf('wh_reduced') !== -1){
massReduction = 50;
massLogTooltipIcon.toggleClass('hidden', true);
massLogStage2Icon.toggleClass('hidden', false);
massLogStage3Icon.toggleClass('hidden', true);
massLogStage3Icon.parent().attr('title', 'stage 2 (reduced)').tooltip('fixTitle');
}else{
massLogTooltipIcon.toggleClass('hidden', false);
massLogStage2Icon.toggleClass('hidden', true);
massLogStage3Icon.toggleClass('hidden', true);
massLogStage3Icon.parent().attr('title', 'recorded total jump mass').tooltip('fixTitle');
}
if(massReduction){
let massLogReduction = massTotal / 100 * massReduction;
if(massLogReduction > massLog){
massLog = massLogTotal = massLogReduction;
}
}
// get current ship data ----------------------------------------------------------
massShipCell.parent().toggle(showShip);
if(showShip){

View File

@@ -0,0 +1,140 @@
/**
* changelog dialog (GitHub API repository information)
*/
define([
'jquery',
'app/init',
'app/util',
'app/render',
'bootbox'
], ($, Init, Util, Render, bootbox) => {
'use strict';
let config = {
changelogDialogClass: 'pf-changelog-dialog', // class for "changelog" dialog
dynamicMessageContainerClass: 'pf-dynamic-message-container', // class for "dynamic" (JS) message container
timelineClass: 'timeline' // class for "timeline"
};
/**
* show version information
* @param changelogDialog
* @param versionData
*/
let showVersion = (changelogDialog, versionData) => {
let type = 'error';
let title = versionData.current;
let text = 'Installed version check failed';
if(versionData.dev){
// developer version
type = 'info';
title = versionData.current + ' (dev)';
text = 'This installation is ahead of current stable version <kbd>' + versionData.last + '</kbd>.';
}else{
// standard version
if(versionData.delta === 0){
// last stable
type = 'success';
title = versionData.current;
text = 'This installation is up2date.';
}else{
// outdated...
type = 'warning';
title = versionData.current;
text = 'This installation is ' + versionData.delta + ' version behind current stable <kbd>' + versionData.last + '</kbd>.';
}
}
changelogDialog.find('.' + config.dynamicMessageContainerClass).showMessage({
dismissible: false,
type: type,
title: title,
text: text
});
};
/**
* load changelog information in dialog
* @param changelogDialog
*/
let loadDialogData = (changelogDialog) => {
// lock dialog
let dialogContent = changelogDialog.find('.modal-content');
dialogContent.showLoadingAnimation();
$.ajax({
type: 'POST',
url: Init.path.gitHubReleases,
dataType: 'json',
context: {
changelogDialog: changelogDialog
}
}).done(function(data){
let changelogDialog = this.changelogDialog;
let versionData = data.version;
let releasesData = data.releasesData;
showVersion(changelogDialog, versionData);
requirejs(['text!templates/ui/timeline_element.html', 'mustache'], function(template, Mustache) {
for(let i = 0; i < releasesData.length; i++){
let releaseData = releasesData[i];
// template vars
let data = {
isFirst: (i === 0),
isOdd: (i % 2 !== 0),
releaseDate: releaseData.published_at.substr(0, 10),
releaseData: releaseData
};
let content = Mustache.render(template, data);
changelogDialog.find('ul.' + config.timelineClass).append(content);
}
changelogDialog.find('.timeline > li').velocity('transition.expandIn', {
stagger: 300,
duration: 240,
//display: 'auto',
complete: function(){}
});
});
}).fail(function( jqXHR, status, error) {
let reason = status + ' ' + jqXHR.status + ': ' + error;
Util.showNotify({title: jqXHR.status + ': login', text: reason, type: 'error'});
}).always(function() {
dialogContent.hideLoadingAnimation();
});
};
/**
* show changelog dialog
*/
$.fn.changelogsDialog = function(){
let content = $('<div>').append(
$('<div>', {
class: config.dynamicMessageContainerClass
}),
$('<ul>', {
class: config.timelineClass
})
);
let changelogDialog = bootbox.dialog({
className: config.changelogDialogClass,
title: 'Changelog',
size: 'large',
message: content
});
// after modal is shown =======================================================================
changelogDialog.on('shown.bs.modal', function(e) {
loadDialogData(changelogDialog);
});
};
});

View File

@@ -8,8 +8,9 @@ define([
'app/util',
'app/render',
'bootbox',
'app/map/util'
], function($, Init, Util, Render, bootbox, MapUtil) {
'app/map/util',
'app/module_map'
], function($, Init, Util, Render, bootbox, MapUtil, ModuleMap) {
'use strict';
let config = {
@@ -33,6 +34,10 @@ define([
slackChannelHistoryId: 'pf-map-dialog-slack-channel-history', // id for Slack channel "history"
slackChannelRallyId: 'pf-map-dialog-slack-channel-rally', // id for Slack channel "rally"
discordUsernameId: 'pf-map-dialog-discord-username', // id for Discord "username"
discordWebHookURLRallyId: 'pf-map-dialog-discord-url-rally', // id for Discord "rally" webHookUrl
discordWebHookURLHistoryId: 'pf-map-dialog-discord-url-history', // id for Discord "history" webHookUrl
characterSelectId: 'pf-map-dialog-character-select', // id for "character" select
corporationSelectId: 'pf-map-dialog-corporation-select', // id for "corporation" select
allianceSelectId: 'pf-map-dialog-alliance-select', // id for "alliance" select
@@ -133,6 +138,14 @@ define([
let slackRallyEnabled = false;
let slackSectionShow = false;
let discordUsername = '';
let discordWebHookURLRally = '';
let discordWebHookURLHistory = '';
let discordEnabled = false;
let discordRallyEnabled = false;
let discordHistoryEnabled = false;
let discordSectionShow = false;
if(mapData !== false){
// set current map information
contentEditMap.find('input[name="id"]').val( mapData.config.id );
@@ -162,6 +175,14 @@ define([
slackRallyEnabled = slackEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_rally_slack_enabled'));
slackSectionShow = (slackEnabled && slackWebHookURL.length > 0);
discordUsername = Util.getObjVal(mapData, 'config.logging.discordUsername');
discordWebHookURLRally = Util.getObjVal(mapData, 'config.logging.discordWebHookURLRally');
discordWebHookURLHistory = Util.getObjVal(mapData, 'config.logging.discordWebHookURLHistory');
discordEnabled = Boolean(Util.getObjVal(Init, 'discord.status'));
discordRallyEnabled = discordEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_rally_discord_enabled'));
discordHistoryEnabled = discordEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_history_discord_enabled'));
discordSectionShow = (discordEnabled && (discordWebHookURLRally.length > 0 || discordWebHookURLHistory.length > 0));
// remove "#" from Slack channels
slackChannelHistory = slackChannelHistory.indexOf('#') === 0 ? slackChannelHistory.substr(1) : slackChannelHistory;
slackChannelRally = slackChannelRally.indexOf('#') === 0 ? slackChannelRally.substr(1) : slackChannelRally;
@@ -221,6 +242,17 @@ define([
slackRallyEnabled: slackRallyEnabled,
slackSectionShow: slackSectionShow,
discordUsernameId: config.discordUsernameId,
discordWebHookURLRallyId: config.discordWebHookURLRallyId,
discordWebHookURLHistoryId: config.discordWebHookURLHistoryId,
discordUsername: discordUsername,
discordWebHookURLRally: discordWebHookURLRally,
discordWebHookURLHistory: discordWebHookURLHistory,
discordEnabled: discordEnabled,
discordRallyEnabled: discordRallyEnabled,
discordHistoryEnabled: discordHistoryEnabled,
discordSectionShow: discordSectionShow,
characterSelectId: config.characterSelectId,
corporationSelectId: config.corporationSelectId,
allianceSelectId: config.allianceSelectId,
@@ -284,7 +316,7 @@ define([
let selectField = $(this);
let selectValues = selectField.val();
if(selectValues.length > 0){
if(selectValues && selectValues.length > 0){
selectField.parents('.form-group').removeClass('has-error');
}else{
selectField.parents('.form-group').addClass('has-error');
@@ -351,7 +383,7 @@ define([
let tabLinkElement = Util.getMapModule().getMapTabElements(responseData.mapData.mapData.id);
if(tabLinkElement.length === 1){
tabLinkElement.updateTabData(responseData.mapData.mapData);
ModuleMap.updateTabData(tabLinkElement, responseData.mapData.mapData);
}
$(mapInfoDialog).modal('hide');

View File

@@ -1,84 +0,0 @@
/**
* releases dialog (GitHub API repository information)
*/
define([
'jquery',
'app/init',
'app/util',
'app/render',
'bootbox'
], function($, Init, Util, Render, bootbox) {
'use strict';
let config = {
releasesDialogClass: 'pf-releases-dialog' // class for "Releases" dialog
};
/**
* load release information in dialog
* @param releasesDialog
*/
let loadDialogData = function(releasesDialog){
// lock dialog
let dialogContent = releasesDialog.find('.modal-content');
dialogContent.showLoadingAnimation();
$.ajax({
type: 'POST',
url: Init.path.gitHubReleases,
// data: updatedMapData,
dataType: 'json'
}).done(function(releasesData){
requirejs(['text!templates/ui/timeline_element.html', 'mustache'], function(template, Mustache) {
for(let i = 0; i < releasesData.length; i++){
let releaseData = releasesData[i];
// template vars
let data = {
isFirst: (i === 0),
isOdd: (i % 2 !== 0),
releaseDate: releaseData.published_at.substr(0, 10),
releaseData: releaseData
};
let content = Mustache.render(template, data);
releasesDialog.find('ul.timeline').append(content);
}
$('.timeline > li').velocity('transition.expandIn', {
stagger: 300,
duration: 240,
//display: 'auto',
complete: function(){}
});
});
}).fail(function( jqXHR, status, error) {
let reason = status + ' ' + jqXHR.status + ': ' + error;
Util.showNotify({title: jqXHR.status + ': login', text: reason, type: 'error'});
}).always(function() {
dialogContent.hideLoadingAnimation();
});
};
/**
* show releases dialog
*/
$.fn.releasesDialog = function(){
let content = '<ul class="timeline"></ul>';
let releasesDialog = bootbox.dialog({
className: config.releasesDialogClass,
title: 'Releases',
size: 'large',
message: content
});
// after modal is shown =======================================================================
releasesDialog.on('shown.bs.modal', function(e) {
loadDialogData(releasesDialog);
});
};
});

View File

@@ -1295,9 +1295,7 @@ define([
* @returns {number}
*/
let getAreaIdBySecurity = function(security){
let areaId = 0;
switch(security){
case 'H':
areaId = 10;
@@ -1723,6 +1721,21 @@ define([
return currentMapData;
};
/**
* @param path
* @param value
* @returns {boolean}
*/
let filterCurrentMapData = (path, value) => {
let currentMapData = getCurrentMapData();
if(currentMapData){
currentMapData = currentMapData.filter((mapData) => {
return (getObjVal(mapData, path) === value);
});
}
return currentMapData;
};
/**
* delete map data by mapId from currentMapData
* @param mapId
@@ -1775,26 +1788,23 @@ define([
* @param option
* @returns {boolean}
*/
let getCurrentUserInfo = function(option){
let getCurrentUserInfo = (option) => {
let currentUserData = getCurrentUserData();
let userInfo = false;
if(currentUserData){
// user data is set -> user data will be set AFTER the main init request!
let characterData = currentUserData.character;
if(characterData){
if(
option === 'allianceId' &&
characterData.alliance
){
if(option === 'privateId'){
userInfo = characterData.id;
}
if(option === 'allianceId' && characterData.alliance){
userInfo = characterData.alliance.id;
}
if(
option === 'corporationId' &&
characterData.corporation
){
if(option === 'corporationId' && characterData.corporation){
userInfo = characterData.corporation.id;
}
}
@@ -2291,6 +2301,7 @@ define([
getCurrentMapUserData: getCurrentMapUserData,
setCurrentMapData: setCurrentMapData,
getCurrentMapData: getCurrentMapData,
filterCurrentMapData: filterCurrentMapData,
getCurrentMapDataIndex: getCurrentMapDataIndex,
updateCurrentMapData: updateCurrentMapData,
deleteCurrentMapData: deleteCurrentMapData,

View File

@@ -269,7 +269,6 @@ $.fn.dragToSelect = function (conf) {
// remove element from "deselected" elements (e.g on add -> remove -> add scenario)
deselectedItems = deselectedItems.not(el);
}else {
if(el.hasClass(config.selectedClass)){
el.removeClass(config.selectedClass);
deselectedItems = deselectedItems.add(el);

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.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

View File

@@ -1,2 +0,0 @@
"use strict";var mainScriptPath=document.body.getAttribute("data-script"),jsBaseUrl=document.body.getAttribute("data-js-path");requirejs.config({baseUrl:"js",paths:{layout:"layout",conf:"app/conf",dialog:"app/ui/dialog",templates:"../../templates",img:"../../img",login:"./app/login",mappage:"./app/mappage",setup:"./app/setup",admin:"./app/admin",notification:"./app/notification",jquery:"lib/jquery-3.1.1.min",bootstrap:"lib/bootstrap.min",text:"lib/requirejs/text",mustache:"lib/mustache.min",localForage:"lib/localforage.min",velocity:"lib/velocity.min",velocityUI:"lib/velocity.ui.min",slidebars:"lib/slidebars",jsPlumb:"lib/dom.jsPlumb-1.7.6",farahey:"lib/farahey-0.5",customScrollbar:"lib/jquery.mCustomScrollbar.min",mousewheel:"lib/jquery.mousewheel.min",xEditable:"lib/bootstrap-editable.min",morris:"lib/morris.min",raphael:"lib/raphael-min",bootbox:"lib/bootbox.min",easyPieChart:"lib/jquery.easypiechart.min",peityInlineChart:"lib/jquery.peity.min",dragToSelect:"lib/jquery.dragToSelect",hoverIntent:"lib/jquery.hoverIntent.minified",fullScreen:"lib/jquery.fullscreen.min",select2:"lib/select2.min",validator:"lib/validator.min",lazylinepainter:"lib/jquery.lazylinepainter-1.5.1.min",blueImpGallery:"lib/blueimp-gallery",blueImpGalleryHelper:"lib/blueimp-helper",blueImpGalleryBootstrap:"lib/bootstrap-image-gallery",bootstrapConfirmation:"lib/bootstrap-confirmation",bootstrapToggle:"lib/bootstrap-toggle.min",lazyload:"lib/jquery.lazyload.min",sortable:"lib/sortable.min",easePack:"lib/EasePack.min",tweenLite:"lib/TweenLite.min","datatables.loader":"./app/datatables.loader","datatables.net":"lib/datatables/DataTables-1.10.12/js/jquery.dataTables.min","datatables.net-buttons":"lib/datatables/Buttons-1.2.1/js/dataTables.buttons.min","datatables.net-buttons-html":"lib/datatables/Buttons-1.2.1/js/buttons.html5.min","datatables.net-responsive":"lib/datatables/Responsive-2.1.0/js/dataTables.responsive.min","datatables.net-select":"lib/datatables/Select-1.2.0/js/dataTables.select.min","datatables.plugins.render.ellipsis":"lib/datatables/plugins/render/ellipsis",pnotify:"lib/pnotify/pnotify","pnotify.buttons":"lib/pnotify/pnotify.buttons","pnotify.confirm":"lib/pnotify/pnotify.confirm","pnotify.nonblock":"lib/pnotify/pnotify.nonblock","pnotify.desktop":"lib/pnotify/pnotify.desktop","pnotify.history":"lib/pnotify/pnotify.history","pnotify.callbacks":"lib/pnotify/pnotify.callbacks","pnotify.reference":"lib/pnotify/pnotify.reference"},shim:{bootstrap:{deps:["jquery"]},farahey:{deps:["jsPlumb"]},velocity:{deps:["jquery"]},velocityUI:{deps:["velocity"]},slidebars:{deps:["jquery"]},customScrollbar:{deps:["jquery","mousewheel"]},"datatables.loader":{deps:["jquery"]},"datatables.net":{deps:["jquery"]},"datatables.net-buttons":{deps:["datatables.net"]},"datatables.net-buttons-html":{deps:["datatables.net-buttons"]},"datatables.net-responsive":{deps:["datatables.net"]},"datatables.net-select":{deps:["datatables.net"]},"datatables.plugins.render.ellipsis":{deps:["datatables.net"]},xEditable:{deps:["bootstrap"]},bootbox:{deps:["jquery","bootstrap"],exports:"bootbox"},morris:{deps:["jquery","raphael"],exports:"Morris",init:function(e,t){window.Raphael=t}},pnotify:{deps:["jquery"]},easyPieChart:{deps:["jquery"]},peityInlineChart:{deps:["jquery"]},dragToSelect:{deps:["jquery"]},hoverIntent:{deps:["jquery"]},fullScreen:{deps:["jquery"]},select2:{deps:["jquery"],exports:"Select2"},validator:{deps:["jquery","bootstrap"]},lazylinepainter:{deps:["jquery","bootstrap"]},blueImpGallery:{deps:["jquery"]},bootstrapConfirmation:{deps:["bootstrap"]},bootstrapToggle:{deps:["jquery"]},lazyload:{deps:["jquery"]}}}),require.config({baseUrl:jsBaseUrl}),requirejs([mainScriptPath]);
//# sourceMappingURL=app.js.map

Binary file not shown.

View File

@@ -1 +0,0 @@
{"version":3,"sources":["app.js"],"names":["mainScriptPath","document","body","getAttribute","jsBaseUrl","requirejs","config","baseUrl","paths","layout","conf","dialog","templates","img","login","mappage","setup","admin","notification","jquery","bootstrap","text","mustache","localForage","velocity","velocityUI","slidebars","jsPlumb","farahey","customScrollbar","mousewheel","xEditable","morris","raphael","bootbox","easyPieChart","peityInlineChart","dragToSelect","hoverIntent","fullScreen","select2","validator","lazylinepainter","blueImpGallery","blueImpGalleryHelper","blueImpGalleryBootstrap","bootstrapConfirmation","bootstrapToggle","lazyload","sortable","easePack","tweenLite","datatables.loader","datatables.net","datatables.net-buttons","datatables.net-buttons-html","datatables.net-responsive","datatables.net-select","datatables.plugins.render.ellipsis","pnotify","pnotify.buttons","pnotify.confirm","pnotify.nonblock","pnotify.desktop","pnotify.history","pnotify.callbacks","pnotify.reference","shim","deps","exports","init","$","Raphael","window","require"],"mappings":"AAAA,aAGA,IAAIA,eAAiBC,SAASC,KAAKC,aAAa,eAI5CC,UAAYH,SAASC,KAAKC,aAAa,gBAG3CE,UAAUC,QACNC,QAAS,KAETC,OACIC,OAAQ,SACRC,KAAM,WACNC,OAAQ,gBACRC,UAAW,kBACXC,IAAK,YAGLC,MAAO,cACPC,QAAS,gBACTC,MAAO,cACPC,MAAO,cACPC,aAAc,qBAEdC,OAAQ,uBACRC,UAAW,oBACXC,KAAM,qBACNC,SAAU,mBACVC,YAAa,sBACbC,SAAU,mBACVC,WAAY,sBACZC,UAAW,gBACXC,QAAS,wBACTC,QAAS,kBACTC,gBAAiB,kCACjBC,WAAY,4BACZC,UAAW,6BACXC,OAAQ,iBACRC,QAAS,kBACTC,QAAS,kBACTC,aAAc,8BACdC,iBAAkB,uBAClBC,aAAc,0BACdC,YAAa,kCACbC,WAAY,4BACZC,QAAS,kBACTC,UAAW,oBACXC,gBAAiB,uCACjBC,eAAgB,sBAChBC,qBAAsB,qBACtBC,wBAAyB,8BACzBC,sBAAuB,6BACvBC,gBAAiB,2BACjBC,SAAU,0BACVC,SAAU,mBAGVC,SAAU,mBACVC,UAAW,oBAGXC,oBAAqB,0BACrBC,iBAAkB,6DAClBC,yBAA0B,yDAC1BC,8BAA+B,oDAC/BC,4BAA6B,+DAC7BC,wBAAyB,uDACzBC,qCAAsC,yCAGtCC,QAAS,sBACTC,kBAAmB,8BACnBC,kBAAmB,8BACnBC,mBAAoB,+BACpBC,kBAAmB,8BACnBC,kBAAmB,8BACnBC,oBAAqB,gCACrBC,oBAAqB,iCAEzBC,MACI/C,WACIgD,MAAO,WAEXxC,SACIwC,MAAO,YAEX5C,UACI4C,MAAO,WAEX3C,YACI2C,MAAO,aAEX1C,WACI0C,MAAO,WAEXvC,iBACIuC,MAAO,SAAU,eAErBhB,qBACIgB,MAAO,WAEXf,kBACIe,MAAO,WAEXd,0BACIc,MAAO,mBAEXb,+BACIa,MAAO,2BAEXZ,6BACIY,MAAO,mBAEXX,yBACIW,MAAO,mBAEXV,sCACIU,MAAO,mBAEXrC,WACIqC,MAAO,cAEXlC,SACIkC,MAAO,SAAU,aACjBC,QAAS,WAEbrC,QACIoC,MAAO,SAAU,WACjBC,QAAS,SACTC,KAAM,SAAUC,EAAGC,GACfC,OAAOD,QAAUA,IAGzBb,SACIS,MAAQ,WAEZjC,cACIiC,MAAQ,WAEZhC,kBACIgC,MAAQ,WAEZ/B,cACI+B,MAAQ,WAEZ9B,aACI8B,MAAQ,WAEZ7B,YACI6B,MAAQ,WAEZ5B,SACI4B,MAAQ,UACRC,QAAS,WAEb5B,WACI2B,MAAQ,SAAU,cAEtB1B,iBACI0B,MAAQ,SAAU,cAEtBzB,gBACIyB,MAAQ,WAEZtB,uBACIsB,MAAQ,cAEZrB,iBACIqB,MAAQ,WAEZpB,UACIoB,MAAQ,cAQpBM,QAAQpE,QACJC,QAASH,YAIbC,WAAYL","file":"app.js","sourceRoot":"/js"}

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

View File

@@ -1,2 +0,0 @@
"use strict";let window={};self.importScripts(self.name);let MsgWorker=window.MsgWorker,socket=null,ports=[],characterPorts=[],initSocket=e=>{let t=new MsgWorker("ws:open");null===socket?((socket=new WebSocket(e)).onopen=(e=>{t.meta({readyState:socket.readyState}),sendToCurrentPort(t)}),socket.onmessage=(e=>{let t=JSON.parse(e.data),r=new MsgWorker("ws:send");r.task(t.task),r.meta({readyState:this.readyState,characterIds:t.characterIds}),r.data(t.load),broadcastPorts(r)}),socket.onclose=(e=>{let t=new MsgWorker("ws:closed");t.meta({readyState:socket.readyState,code:e.code,reason:e.reason,wasClean:e.wasClean}),broadcastPorts(t),socket=null}),socket.onerror=(e=>{let t=new MsgWorker("ws:error");t.meta({readyState:socket.readyState}),sendToCurrentPort(t)})):(t.meta({readyState:socket.readyState}),sendToCurrentPort(t))},sendToCurrentPort=e=>{ports[ports.length-1].postMessage(e)},broadcastPorts=e=>{let t=ports,r=e.meta();r&&r.characterIds&&"undefined"!==r.characterIds&&r.characterIds instanceof Array&&(t=getPortsByCharacterIds(r.characterIds));for(let r=0;r<t.length;r++)t[r].postMessage(e)},addPort=(e,t)=>{(t=parseInt(t))>0?characterPorts.push({characterId:t,port:e}):ports.push(e)},getPortsByCharacterIds=e=>{let t=[];for(let r=0;r<characterPorts.length;r++)for(let a=0;a<e.length;a++)characterPorts[r].characterId===e[a]&&t.push(characterPorts[r].port);return t};self.addEventListener("connect",e=>{let t=e.ports[0];addPort(t),t.addEventListener("message",e=>{let r=e.data;switch(Object.setPrototypeOf(r,MsgWorker.prototype),r.command){case"ws:init":let e=r.data();addPort(t,e.characterId),initSocket(e.uri);break;case"ws:send":let a={task:r.task(),load:r.data()};socket.send(JSON.stringify(a))}},!1),t.start()},!1);
//# sourceMappingURL=map.js.map

View File

@@ -1 +0,0 @@
{"version":3,"sources":["app/worker/map.js"],"names":["window","self","importScripts","name","MsgWorker","socket","ports","characterPorts","initSocket","uri","MsgWorkerOpen","WebSocket","onopen","e","meta","readyState","sendToCurrentPort","onmessage","response","JSON","parse","data","MsgWorkerSend","task","this","characterIds","load","broadcastPorts","onclose","closeEvent","MsgWorkerClosed","code","reason","wasClean","onerror","MsgWorkerError","length","postMessage","sentToPorts","Array","getPortsByCharacterIds","i","addPort","port","characterId","parseInt","push","j","addEventListener","event","MsgWorkerMessage","Object","setPrototypeOf","prototype","command","MsgSocket","send","stringify","start"],"mappings":"AAAA,aAGA,IAAIA,UAGJC,KAAKC,cAAeD,KAAKE,MAEzB,IAAIC,UAAYJ,OAAOI,UACnBC,OAAS,KACTC,SACAC,kBAGAC,WAAcC,IACd,IAAIC,EAAgB,IAAIN,UAAU,WAEpB,OAAXC,SACCA,OAAS,IAAIM,UAAUF,IAGhBG,OAAS,CAACC,IACbH,EAAcI,MACVC,WAAYV,OAAOU,aAGvBC,kBAAkBN,KAItBL,OAAOY,UAAY,CAACJ,IAChB,IAAIK,EAAWC,KAAKC,MAAMP,EAAEQ,MAExBC,EAAgB,IAAIlB,UAAU,WAClCkB,EAAcC,KAAML,EAASK,MAC7BD,EAAcR,MACVC,WAAYS,KAAKT,WACjBU,aAAcP,EAASO,eAE3BH,EAAcD,KAAMH,EAASQ,MAE7BC,eAAeL,KAInBjB,OAAOuB,QAAU,CAACC,IACd,IAAIC,EAAkB,IAAI1B,UAAU,aACpC0B,EAAgBhB,MACZC,WAAYV,OAAOU,WACnBgB,KAAMF,EAAWE,KACjBC,OAAQH,EAAWG,OACnBC,SAAUJ,EAAWI,WAGzBN,eAAeG,GACfzB,OAAS,OAIbA,OAAO6B,QAAU,CAACrB,IACd,IAAIsB,EAAiB,IAAI/B,UAAU,YACnC+B,EAAerB,MACXC,WAAYV,OAAOU,aAGvBC,kBAAkBmB,OAItBzB,EAAcI,MACVC,WAAYV,OAAOU,aAEvBC,kBAAkBN,KAKtBM,kBAAqBU,IACrBpB,MAAMA,MAAM8B,OAAS,GAAGC,YAAYX,IAGpCC,eAAkBD,IAElB,IAAIY,EAAchC,MAGdQ,EAAOY,EAAKZ,OAEZA,GACAA,EAAKW,cACiB,cAAtBX,EAAKW,cACLX,EAAKW,wBAAwBc,QAG7BD,EAAcE,uBAAuB1B,EAAKW,eAG9C,IAAK,IAAIgB,EAAI,EAAGA,EAAIH,EAAYF,OAAQK,IACpCH,EAAYG,GAAGJ,YAAYX,IAK/BgB,QAAU,CAACC,EAAMC,MACjBA,EAAcC,SAASD,IAEN,EACbrC,eAAeuC,MACXF,YAAaA,EACbD,KAAMA,IAGVrC,MAAMwC,KAAKH,IAIfH,uBAA0Bf,IAC1B,IAAInB,KAEJ,IAAI,IAAImC,EAAI,EAAGA,EAAIlC,eAAe6B,OAAQK,IACtC,IAAI,IAAIM,EAAI,EAAGA,EAAItB,EAAaW,OAAQW,IACjCxC,eAAekC,GAAGG,cAAgBnB,EAAasB,IAC9CzC,EAAMwC,KAAKvC,eAAekC,GAAGE,MAKzC,OAAOrC,GAIXL,KAAK+C,iBAAiB,UAAYC,IAC9B,IAAIN,EAAOM,EAAM3C,MAAM,GACvBoC,QAAQC,GAERA,EAAKK,iBAAiB,UAAYnC,IAC9B,IAAIqC,EAAmBrC,EAAEQ,KAGzB,OAFA8B,OAAOC,eAAeF,EAAkB9C,UAAUiD,WAE3CH,EAAiBI,SACpB,IAAK,UACD,IAAIjC,EAAO6B,EAAiB7B,OAE5BqB,QAAQC,EAAMtB,EAAKuB,aACnBpC,WAAWa,EAAKZ,KAChB,MACJ,IAAK,UACD,IAAI8C,GACAhC,KAAM2B,EAAiB3B,OACvBG,KAAMwB,EAAiB7B,QAG3BhB,OAAOmD,KAAKrC,KAAKsC,UAAUF,OAMpC,GAEHZ,EAAKe,UACN","file":"map.js","sourceRoot":"/js"}

View File

@@ -1,2 +0,0 @@
window.MsgWorker=class{constructor(s){this.cmd=s,this.msgTask="",this.msgMeta=null,this.msgBody=null}get command(){return this.cmd}task(s){return s&&(this.msgTask=s),this.msgTask}meta(s){return s&&(this.msgMeta=s),this.msgMeta}data(s){return s&&(this.msgBody=s),this.msgBody}};
//# sourceMappingURL=message.js.map

View File

@@ -1 +0,0 @@
{"version":3,"sources":["app/worker/message.js"],"names":["window","MsgWorker","[object Object]","cmd","this","msgTask","msgMeta","msgBody","command","task","metaData","data"],"mappings":"AAAAA,OAAOC,gBAEHC,YAAYC,GAIRC,KAAKD,IAAMA,EAMXC,KAAKC,QAAU,GAMfD,KAAKE,QAAU,KAMfF,KAAKG,QAAU,KAGnBC,cACI,OAAOJ,KAAKD,IAGhBD,KAAKO,GAKD,OAJGA,IACCL,KAAKC,QAAUI,GAGZL,KAAKC,QAGhBH,KAAKQ,GAKD,OAJGA,IACCN,KAAKE,QAAUI,GAGZN,KAAKE,QAGhBJ,KAAKS,GAKD,OAJGA,IACCP,KAAKG,QAAUI,GAGZP,KAAKG","file":"message.js","sourceRoot":"/js"}

View File

@@ -1,2 +0,0 @@
!function(t){"use strict";"function"==typeof define&&define.amd?define(["jquery","blueImpGallery"],t):t(window.jQuery,window.blueimp.Gallery)}(function(t,o){"use strict";t.extend(o.prototype.options,{useBootstrapModal:!0});var e=o.prototype.close,n=o.prototype.imageFactory,i=o.prototype.videoFactory,r=o.prototype.textFactory;t.extend(o.prototype,{modalFactory:function(o,e,n,i){if(!this.options.useBootstrapModal||n)return i.call(this,o,e,n);var r=this,a=t(this.container).children(".modal").clone().css("display","block").on("click",function(t){t.target!==a[0]&&t.target!==a.children()[0]||(t.preventDefault(),t.stopPropagation(),r.close())}),c=i.call(this,o,function(t){e({type:t.type,target:a[0]}),a.addClass("in")},n);return a.find(".modal-title").text(c.title||String.fromCharCode(160)),a.find(".modal-body").append(c),a[0]},imageFactory:function(t,o,e){return this.modalFactory(t,o,e,n)},videoFactory:function(t,o,e){return this.modalFactory(t,o,e,i)},textFactory:function(t,o,e){return this.modalFactory(t,o,e,r)},close:function(){this.container.find(".modal").removeClass("in"),e.call(this)}})});
//# sourceMappingURL=bootstrap-image-gallery.js.map

View File

@@ -1 +0,0 @@
{"version":3,"sources":["lib/bootstrap-image-gallery.js"],"names":["factory","define","amd","window","jQuery","blueimp","Gallery","$","extend","prototype","options","useBootstrapModal","close","imageFactory","videoFactory","textFactory","modalFactory","obj","callback","factoryInterface","this","call","that","modal","container","children","clone","css","on","event","target","preventDefault","stopPropagation","element","type","addClass","find","text","title","String","fromCharCode","append","removeClass"],"mappings":"CAaE,SAAUA,GACR,aACsB,mBAAXC,QAAyBA,OAAOC,IACvCD,QACI,SACA,kBACDD,GAEHA,EACIG,OAAOC,OACPD,OAAOE,QAAQC,SAV1B,CAaC,SAAUC,EAAGD,GACX,aAEAC,EAAEC,OAAOF,EAAQG,UAAUC,SACvBC,mBAAmB,IAGvB,IAAIC,EAAQN,EAAQG,UAAUG,MAC1BC,EAAeP,EAAQG,UAAUI,aACjCC,EAAeR,EAAQG,UAAUK,aACjCC,EAAcT,EAAQG,UAAUM,YAEpCR,EAAEC,OAAOF,EAAQG,WACbO,aAAc,SAAUC,EAAKC,EAAUC,EAAkBnB,GACrD,IAAKoB,KAAKV,QAAQC,mBAAqBQ,EACnC,OAAOnB,EAAQqB,KAAKD,KAAMH,EAAKC,EAAUC,GAE7C,IAAIG,EAAOF,KAEPG,EADgBhB,EAAEa,KAAKI,WAAWC,SAAS,UACrBC,QAAQC,IAAI,UAAW,SAASC,GAAG,QAAS,SAAUC,GAGxEA,EAAMC,SAAWP,EAAM,IACvBM,EAAMC,SAAWP,EAAME,WAAW,KAClCI,EAAME,iBACNF,EAAMG,kBACNV,EAAKV,WAGTqB,EAAUjC,EAAQqB,KAAKD,KAAMH,EAAK,SAAUY,GAC5CX,GACIgB,KAAML,EAAMK,KACZJ,OAAQP,EAAM,KAElBA,EAAMY,SAAS,OAChBhB,GAIH,OAHAI,EAAMa,KAAK,gBAAgBC,KAAKJ,EAAQK,OAASC,OAAOC,aAAa,MACrEjB,EAAMa,KAAK,eAAeK,OAAOR,GAE1BV,EAAM,IAGjBV,aAAc,SAAUI,EAAKC,EAAUC,GACnC,OAAOC,KAAKJ,aAAaC,EAAKC,EAAUC,EAAkBN,IAG9DC,aAAc,SAAUG,EAAKC,EAAUC,GACnC,OAAOC,KAAKJ,aAAaC,EAAKC,EAAUC,EAAkBL,IAG9DC,YAAa,SAAUE,EAAKC,EAAUC,GAClC,OAAOC,KAAKJ,aAAaC,EAAKC,EAAUC,EAAkBJ,IAG9DH,MAAO,WACHQ,KAAKI,UAAUY,KAAK,UAAUM,YAAY,MAC1C9B,EAAMS,KAAKD","file":"bootstrap-image-gallery.js","sourceRoot":"/js"}

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

View File

@@ -1,2 +0,0 @@
!function(o,i){"function"==typeof define&&define.amd?define("pnotify.buttons",["jquery","pnotify"],i):"object"==typeof exports&&"undefined"!=typeof module?module.exports=i(require("jquery"),require("./pnotify")):i(o.jQuery,o.PNotify)}(this,function(o,i){i.prototype.options.buttons={closer:!0,closer_hover:!0,sticker:!0,sticker_hover:!0,show_on_nonblock:!1,labels:{close:"Close",stick:"Stick",unstick:"Unstick"},classes:{closer:null,pin_up:null,pin_down:null}},i.prototype.modules.buttons={closer:null,sticker:null,init:function(i,s){var n=this;i.elem.on({mouseenter:function(o){!n.options.sticker||i.options.nonblock&&i.options.nonblock.nonblock&&!n.options.show_on_nonblock||n.sticker.trigger("pnotify:buttons:toggleStick").css("visibility","visible"),!n.options.closer||i.options.nonblock&&i.options.nonblock.nonblock&&!n.options.show_on_nonblock||n.closer.css("visibility","visible")},mouseleave:function(o){n.options.sticker_hover&&n.sticker.css("visibility","hidden"),n.options.closer_hover&&n.closer.css("visibility","hidden")}}),this.sticker=o("<div />",{class:"ui-pnotify-sticker","aria-role":"button","aria-pressed":i.options.hide?"false":"true",tabindex:"0",title:i.options.hide?s.labels.stick:s.labels.unstick,css:{cursor:"pointer",visibility:s.sticker_hover?"hidden":"visible"},click:function(){i.options.hide=!i.options.hide,i.options.hide?i.queueRemove():i.cancelRemove(),o(this).trigger("pnotify:buttons:toggleStick")}}).bind("pnotify:buttons:toggleStick",function(){var s=null===n.options.classes.pin_up?i.styles.pin_up:n.options.classes.pin_up,e=null===n.options.classes.pin_down?i.styles.pin_down:n.options.classes.pin_down;o(this).attr("title",i.options.hide?n.options.labels.stick:n.options.labels.unstick).children().attr("class","").addClass(i.options.hide?s:e).attr("aria-pressed",i.options.hide?"false":"true")}).append("<span />").trigger("pnotify:buttons:toggleStick").prependTo(i.container),(!s.sticker||i.options.nonblock&&i.options.nonblock.nonblock&&!s.show_on_nonblock)&&this.sticker.css("display","none"),this.closer=o("<div />",{class:"ui-pnotify-closer","aria-role":"button",tabindex:"0",title:s.labels.close,css:{cursor:"pointer",visibility:s.closer_hover?"hidden":"visible"},click:function(){i.remove(!1),n.sticker.css("visibility","hidden"),n.closer.css("visibility","hidden")}}).append(o("<span />",{class:null===s.classes.closer?i.styles.closer:s.classes.closer})).prependTo(i.container),(!s.closer||i.options.nonblock&&i.options.nonblock.nonblock&&!s.show_on_nonblock)&&this.closer.css("display","none")},update:function(o,i){!i.closer||o.options.nonblock&&o.options.nonblock.nonblock&&!i.show_on_nonblock?this.closer.css("display","none"):i.closer&&this.closer.css("display","block"),!i.sticker||o.options.nonblock&&o.options.nonblock.nonblock&&!i.show_on_nonblock?this.sticker.css("display","none"):i.sticker&&this.sticker.css("display","block"),this.sticker.trigger("pnotify:buttons:toggleStick"),this.closer.find("span").attr("class","").addClass(null===i.classes.closer?o.styles.closer:i.classes.closer),i.sticker_hover?this.sticker.css("visibility","hidden"):o.options.nonblock&&o.options.nonblock.nonblock&&!i.show_on_nonblock||this.sticker.css("visibility","visible"),i.closer_hover?this.closer.css("visibility","hidden"):o.options.nonblock&&o.options.nonblock.nonblock&&!i.show_on_nonblock||this.closer.css("visibility","visible")}},o.extend(i.styling.brighttheme,{closer:"brighttheme-icon-closer",pin_up:"brighttheme-icon-sticker",pin_down:"brighttheme-icon-sticker brighttheme-icon-stuck"}),o.extend(i.styling.jqueryui,{closer:"ui-icon ui-icon-close",pin_up:"ui-icon ui-icon-pin-w",pin_down:"ui-icon ui-icon-pin-s"}),o.extend(i.styling.bootstrap2,{closer:"icon-remove",pin_up:"icon-pause",pin_down:"icon-play"}),o.extend(i.styling.bootstrap3,{closer:"glyphicon glyphicon-remove",pin_up:"glyphicon glyphicon-pause",pin_down:"glyphicon glyphicon-play"}),o.extend(i.styling.fontawesome,{closer:"fa fa-times",pin_up:"fa fa-pause",pin_down:"fa fa-play"})});
//# sourceMappingURL=pnotify.buttons.js.map

View File

@@ -1 +0,0 @@
{"version":3,"sources":["lib/pnotify/pnotify.buttons.js"],"names":["root","factory","define","amd","exports","module","require","jQuery","PNotify","this","$","prototype","options","buttons","closer","closer_hover","sticker","sticker_hover","show_on_nonblock","labels","close","stick","unstick","classes","pin_up","pin_down","modules","init","notice","that","elem","on","mouseenter","e","nonblock","trigger","css","mouseleave","class","aria-role","aria-pressed","hide","tabindex","title","cursor","visibility","click","queueRemove","cancelRemove","bind","styles","attr","children","addClass","append","prependTo","container","remove","update","find","extend","styling","brighttheme","jqueryui","bootstrap2","bootstrap3","fontawesome"],"mappings":"CACC,SAAUA,EAAMC,GACS,mBAAXC,QAAyBA,OAAOC,IAEvCD,OAAO,mBAAoB,SAAU,WAAYD,GACvB,iBAAZG,SAA0C,oBAAXC,OAE7CA,OAAOD,QAAUH,EAAQK,QAAQ,UAAWA,QAAQ,cAGpDL,EAAQD,EAAKO,OAAQP,EAAKQ,SATlC,CAWEC,KAAM,SAASC,EAAGF,GAChBA,EAAQG,UAAUC,QAAQC,SAEtBC,QAAQ,EAERC,cAAc,EAEdC,SAAS,EAETC,eAAe,EAEfC,kBAAkB,EAElBC,QACIC,MAAO,QACPC,MAAO,QACPC,QAAS,WAGbC,SACIT,OAAQ,KACRU,OAAQ,KACRC,SAAU,OAGlBjB,EAAQG,UAAUe,QAAQb,SACtBC,OAAQ,KACRE,QAAS,KAETW,KAAM,SAASC,EAAQhB,GACnB,IAAIiB,EAAOpB,KACXmB,EAAOE,KAAKC,IACRC,WAAc,SAASC,IAEfJ,EAAKjB,QAAQI,SAAcY,EAAOhB,QAAQsB,UAAYN,EAAOhB,QAAQsB,SAASA,WAAaL,EAAKjB,QAAQM,kBACxGW,EAAKb,QAAQmB,QAAQ,+BAA+BC,IAAI,aAAc,YAEtEP,EAAKjB,QAAQE,QAAac,EAAOhB,QAAQsB,UAAYN,EAAOhB,QAAQsB,SAASA,WAAaL,EAAKjB,QAAQM,kBACvGW,EAAKf,OAAOsB,IAAI,aAAc,YAGtCC,WAAc,SAASJ,GAEfJ,EAAKjB,QAAQK,eACbY,EAAKb,QAAQoB,IAAI,aAAc,UAE/BP,EAAKjB,QAAQG,cACbc,EAAKf,OAAOsB,IAAI,aAAc,aAM1C3B,KAAKO,QAAUN,EAAE,WACb4B,MAAS,qBACTC,YAAa,SACbC,eAAgBZ,EAAOhB,QAAQ6B,KAAO,QAAU,OAChDC,SAAY,IACZC,MAASf,EAAOhB,QAAQ6B,KAAO7B,EAAQO,OAAOE,MAAQT,EAAQO,OAAOG,QACrEc,KACIQ,OAAU,UACVC,WAAcjC,EAAQK,cAAgB,SAAW,WAErD6B,MAAS,WACLlB,EAAOhB,QAAQ6B,MAAQb,EAAOhB,QAAQ6B,KAClCb,EAAOhB,QAAQ6B,KACfb,EAAOmB,cAEPnB,EAAOoB,eAEXtC,EAAED,MAAM0B,QAAQ,kCAGvBc,KAAK,8BAA+B,WACjC,IAAIzB,EAAyC,OAAhCK,EAAKjB,QAAQW,QAAQC,OAAkBI,EAAOsB,OAAO1B,OAASK,EAAKjB,QAAQW,QAAQC,OAC5FC,EAA6C,OAAlCI,EAAKjB,QAAQW,QAAQE,SAAoBG,EAAOsB,OAAOzB,SAAWI,EAAKjB,QAAQW,QAAQE,SACtGf,EAAED,MACD0C,KAAK,QAASvB,EAAOhB,QAAQ6B,KAAOZ,EAAKjB,QAAQO,OAAOE,MAAQQ,EAAKjB,QAAQO,OAAOG,SACpF8B,WACAD,KAAK,QAAS,IACdE,SAASzB,EAAOhB,QAAQ6B,KAAOjB,EAASC,GACxC0B,KAAK,eAAgBvB,EAAOhB,QAAQ6B,KAAO,QAAU,UAEzDa,OAAO,YACPnB,QAAQ,+BACRoB,UAAU3B,EAAO4B,aACb5C,EAAQI,SAAYY,EAAOhB,QAAQsB,UAAYN,EAAOhB,QAAQsB,SAASA,WAAatB,EAAQM,mBAC7FT,KAAKO,QAAQoB,IAAI,UAAW,QAIhC3B,KAAKK,OAASJ,EAAE,WACZ4B,MAAS,oBACTC,YAAa,SACbG,SAAY,IACZC,MAAS/B,EAAQO,OAAOC,MACxBgB,KAAQQ,OAAU,UAAWC,WAAcjC,EAAQG,aAAe,SAAW,WAC7E+B,MAAS,WACLlB,EAAO6B,QAAO,GACd5B,EAAKb,QAAQoB,IAAI,aAAc,UAC/BP,EAAKf,OAAOsB,IAAI,aAAc,aAGrCkB,OAAO5C,EAAE,YAAa4B,MAAoC,OAA3B1B,EAAQW,QAAQT,OAAkBc,EAAOsB,OAAOpC,OAASF,EAAQW,QAAQT,UACxGyC,UAAU3B,EAAO4B,aACb5C,EAAQE,QAAWc,EAAOhB,QAAQsB,UAAYN,EAAOhB,QAAQsB,SAASA,WAAatB,EAAQM,mBAC5FT,KAAKK,OAAOsB,IAAI,UAAW,SAGnCsB,OAAQ,SAAS9B,EAAQhB,IAEhBA,EAAQE,QAAWc,EAAOhB,QAAQsB,UAAYN,EAAOhB,QAAQsB,SAASA,WAAatB,EAAQM,iBAC5FT,KAAKK,OAAOsB,IAAI,UAAW,QACpBxB,EAAQE,QACfL,KAAKK,OAAOsB,IAAI,UAAW,UAE1BxB,EAAQI,SAAYY,EAAOhB,QAAQsB,UAAYN,EAAOhB,QAAQsB,SAASA,WAAatB,EAAQM,iBAC7FT,KAAKO,QAAQoB,IAAI,UAAW,QACrBxB,EAAQI,SACfP,KAAKO,QAAQoB,IAAI,UAAW,SAGhC3B,KAAKO,QAAQmB,QAAQ,+BAErB1B,KAAKK,OAAO6C,KAAK,QAAQR,KAAK,QAAS,IAAIE,SAAoC,OAA3BzC,EAAQW,QAAQT,OAAkBc,EAAOsB,OAAOpC,OAASF,EAAQW,QAAQT,QAEzHF,EAAQK,cACRR,KAAKO,QAAQoB,IAAI,aAAc,UACtBR,EAAOhB,QAAQsB,UAAYN,EAAOhB,QAAQsB,SAASA,WAAatB,EAAQM,kBACjFT,KAAKO,QAAQoB,IAAI,aAAc,WAE/BxB,EAAQG,aACRN,KAAKK,OAAOsB,IAAI,aAAc,UACrBR,EAAOhB,QAAQsB,UAAYN,EAAOhB,QAAQsB,SAASA,WAAatB,EAAQM,kBACjFT,KAAKK,OAAOsB,IAAI,aAAc,aAI1C1B,EAAEkD,OAAOpD,EAAQqD,QAAQC,aACrBhD,OAAQ,0BACRU,OAAQ,2BACRC,SAAU,oDAEdf,EAAEkD,OAAOpD,EAAQqD,QAAQE,UACrBjD,OAAQ,wBACRU,OAAQ,wBACRC,SAAU,0BAEdf,EAAEkD,OAAOpD,EAAQqD,QAAQG,YACrBlD,OAAQ,cACRU,OAAQ,aACRC,SAAU,cAEdf,EAAEkD,OAAOpD,EAAQqD,QAAQI,YACrBnD,OAAQ,6BACRU,OAAQ,4BACRC,SAAU,6BAEdf,EAAEkD,OAAOpD,EAAQqD,QAAQK,aACrBpD,OAAQ,cACRU,OAAQ,cACRC,SAAU","file":"pnotify.buttons.js","sourceRoot":"/js"}

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

188
public/js/v1.3.2/app.js Normal file
View File

@@ -0,0 +1,188 @@
'use strict';
// main script path
var mainScriptPath = document.body.getAttribute('data-script');
// js baseURL. Depends on the environment.
// e.g. use raw files (develop) or build files (production)
var jsBaseUrl = document.body.getAttribute('data-js-path');
// requireJs configuration
requirejs.config({
baseUrl: 'js', // path for baseUrl - dynamically set !below! ("build_js" | "js")
paths: {
layout: 'layout',
conf: 'app/conf', // path for "config" files dir
dialog: 'app/ui/dialog', // path for "dialog" files dir
templates: '../../templates', // template dir
img: '../../img', // images dir
// main views
login: './app/login', // initial start "login page" view
mappage: './app/mappage', // initial start "map page" view
setup: './app/setup', // initial start "setup page" view
admin: './app/admin', // initial start "admin page" view
notification: './app/notification', // "notification" view
jquery: 'lib/jquery-3.1.1.min', // v3.1.1 jQuery
bootstrap: 'lib/bootstrap.min', // v3.3.0 Bootstrap js code - http://getbootstrap.com/javascript
text: 'lib/requirejs/text', // v2.0.12 A RequireJS/AMD loader plugin for loading text resources.
mustache: 'lib/mustache.min', // v1.0.0 Javascript template engine - http://mustache.github.io
localForage: 'lib/localforage.min', // v1.4.2 localStorage library - https://mozilla.github.io/localForage
velocity: 'lib/velocity.min', // v1.4.1 animation engine - http://julian.com/research/velocity
velocityUI: 'lib/velocity.ui.min', // v5.2.0 plugin for velocity - http://julian.com/research/velocity/#uiPack
slidebars: 'lib/slidebars', // v0.10 Slidebars - side menu plugin http://plugins.adchsm.me/slidebars
jsPlumb: 'lib/dom.jsPlumb-1.7.6', // v1.7.6 jsPlumb (Vanilla)- main map draw plugin https://jsplumbtoolkit.com
farahey: 'lib/farahey-0.5', // v0.5 jsPlumb "magnetizing" extension - https://github.com/jsplumb/farahey
customScrollbar: 'lib/jquery.mCustomScrollbar.min', // v3.1.3 Custom scroll bars - http://manos.malihu.gr
mousewheel: 'lib/jquery.mousewheel.min', // v3.1.13 Mousewheel - https://github.com/jquery/jquery-mousewheel
xEditable: 'lib/bootstrap-editable.min', // v1.5.1 X-editable - in placed editing
morris: 'lib/morris.min', // v0.5.1 Morris.js - graphs and charts
raphael: 'lib/raphael-min', // v2.1.2 Raphaël - required for morris (dependency)
bootbox: 'lib/bootbox.min', // v4.4.0 Bootbox.js - custom dialogs - http://bootboxjs.com
easyPieChart: 'lib/jquery.easypiechart.min', // v2.1.6 Easy Pie Chart - HTML 5 pie charts - http://rendro.github.io/easy-pie-chart
peityInlineChart: 'lib/jquery.peity.min', // v3.2.1 Inline Chart - http://benpickles.github.io/peity/
dragToSelect: 'lib/jquery.dragToSelect', // v1.1 Drag to Select - http://andreaslagerkvist.com/jquery/drag-to-select
hoverIntent: 'lib/jquery.hoverIntent.minified', // v1.8.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html
fullScreen: 'lib/jquery.fullscreen.min', // v0.6.0 Full screen mode - https://github.com/private-face/jquery.fullscreen
select2: 'lib/select2.min', // v4.0.3 Drop Down customization - https://select2.github.io
validator: 'lib/validator.min', // v0.10.1 Validator for Bootstrap 3 - https://github.com/1000hz/bootstrap-validator
lazylinepainter: 'lib/jquery.lazylinepainter-1.5.1.min', // v1.5.1 SVG line animation plugin - http://lazylinepainter.info
blueImpGallery: 'lib/blueimp-gallery', // v2.21.3 Image Gallery - https://github.com/blueimp/Gallery
blueImpGalleryHelper: 'lib/blueimp-helper', // helper function for Blue Imp Gallery
blueImpGalleryBootstrap: 'lib/bootstrap-image-gallery', // v3.4.2 Bootstrap extension for Blue Imp Gallery - https://blueimp.github.io/Bootstrap-Image-Gallery
bootstrapConfirmation: 'lib/bootstrap-confirmation', // v1.0.5 Bootstrap extension for inline confirm dialog - https://github.com/tavicu/bs-confirmation
bootstrapToggle: 'lib/bootstrap-toggle.min', // v2.2.0 Bootstrap Toggle (Checkbox) - http://www.bootstraptoggle.com
lazyload: 'lib/jquery.lazyload.min', // v1.9.5 LazyLoader images - http://www.appelsiini.net/projects/lazyload
sortable: 'lib/sortable.min', // v1.6.0 Sortable - drag&drop reorder - https://github.com/rubaxa/Sortable
// header animation
easePack: 'lib/EasePack.min',
tweenLite: 'lib/TweenLite.min',
// datatables // v1.10.12 DataTables - https://datatables.net
'datatables.loader': './app/datatables.loader',
'datatables.net': 'lib/datatables/DataTables-1.10.12/js/jquery.dataTables.min',
'datatables.net-buttons': 'lib/datatables/Buttons-1.2.1/js/dataTables.buttons.min',
'datatables.net-buttons-html': 'lib/datatables/Buttons-1.2.1/js/buttons.html5.min',
'datatables.net-responsive': 'lib/datatables/Responsive-2.1.0/js/dataTables.responsive.min',
'datatables.net-select': 'lib/datatables/Select-1.2.0/js/dataTables.select.min',
'datatables.plugins.render.ellipsis': 'lib/datatables/plugins/render/ellipsis',
// notification plugin
pnotify: 'lib/pnotify/pnotify', // v3.0.0 PNotify - notification core file - https://sciactive.com/pnotify/
'pnotify.buttons': 'lib/pnotify/pnotify.buttons', // PNotify - buttons notification extension
'pnotify.confirm': 'lib/pnotify/pnotify.confirm', // PNotify - confirmation notification extension
'pnotify.nonblock': 'lib/pnotify/pnotify.nonblock', // PNotify - notification non-block extension (hover effect)
'pnotify.desktop': 'lib/pnotify/pnotify.desktop', // PNotify - desktop push notification extension
'pnotify.history': 'lib/pnotify/pnotify.history', // PNotify - history push notification history extension
'pnotify.callbacks': 'lib/pnotify/pnotify.callbacks', // PNotify - callbacks push notification extension
'pnotify.reference': 'lib/pnotify/pnotify.reference' // PNotify - reference push notification extension
},
shim: {
bootstrap: {
deps: ['jquery']
},
farahey: {
deps: ['jsPlumb']
},
velocity: {
deps: ['jquery']
},
velocityUI: {
deps: ['velocity']
},
slidebars: {
deps: ['jquery']
},
customScrollbar: {
deps: ['jquery', 'mousewheel']
},
'datatables.loader': {
deps: ['jquery']
},
'datatables.net': {
deps: ['jquery']
},
'datatables.net-buttons': {
deps: ['datatables.net']
},
'datatables.net-buttons-html': {
deps: ['datatables.net-buttons']
},
'datatables.net-responsive': {
deps: ['datatables.net']
},
'datatables.net-select': {
deps: ['datatables.net']
},
'datatables.plugins.render.ellipsis': {
deps: ['datatables.net']
},
xEditable: {
deps: ['bootstrap']
},
bootbox: {
deps: ['jquery', 'bootstrap'],
exports: 'bootbox'
},
morris: {
deps: ['jquery', 'raphael'],
exports: 'Morris',
init: function ($, Raphael) {
window.Raphael = Raphael;
}
},
pnotify: {
deps : ['jquery']
},
easyPieChart: {
deps : ['jquery']
},
peityInlineChart: {
deps : ['jquery']
},
dragToSelect: {
deps : ['jquery']
},
hoverIntent: {
deps : ['jquery']
},
fullScreen: {
deps : ['jquery']
},
select2: {
deps : ['jquery'],
exports: 'Select2'
},
validator: {
deps : ['jquery', 'bootstrap']
},
lazylinepainter: {
deps : ['jquery', 'bootstrap']
},
blueImpGallery: {
deps : ['jquery']
},
bootstrapConfirmation: {
deps : ['bootstrap']
},
bootstrapToggle: {
deps : ['jquery']
},
lazyload: {
deps : ['jquery']
}
}
});
// switch baseUrl to js "build_js" in production environment
// this has no effect for js build process!
// check build.js for build configuration
require.config({
baseUrl: jsBaseUrl
});
// load the main app module -> initial app start
requirejs( [mainScriptPath] );

View File

@@ -0,0 +1,45 @@
/**
* Main "admin" page
*/
define([
'jquery',
'app/init',
'app/util',
'datatables.loader'
], function($, Init, Util) {
'use strict';
let config = {
splashOverlayClass: 'pf-splash' // class for "splash" overlay
};
/**
* main init "admin" page
*/
$(function(){
// set Dialog default config
Util.initDefaultBootboxConfig();
// hide splash loading animation
$('.' + config.splashOverlayClass).hideSplashOverlay();
let systemsDataTable = $('.dataTable').dataTable( {
pageLength: 100,
paging: true,
ordering: true,
autoWidth: false,
hover: false,
language: {
emptyTable: 'No members',
zeroRecords: 'No members found',
lengthMenu: 'Show _MENU_ members',
info: 'Showing _START_ to _END_ of _TOTAL_ members'
}
});
});
});

View File

@@ -0,0 +1,420 @@
/**
* Created by exodus4d on 06.07.2015.
* static signature types
*
* (*) marked fields are in-game verified and
* proofed, signature names (copy & paste from scanning window)
*/
define(['jquery'], function($) {
'use strict';
// signature sources
// http://de.sistersprobe.wikia.com/wiki/EVE_Sister_Core_Scanner_Probe_Wiki
// NullSec Relic sites, which can also spawn in C1, C2, C3 wormholes
let nullSecRelicSites = {
10: 'Ruined Angel Crystal Quarry',
11: 'Ruined Angel Monument Site',
12: 'Ruined Angel Science Outpost',
13: 'Ruined Angel Temple Site',
14: 'Ruined Blood Raider Crystal Quarry',
15: 'Ruined Blood Raider Monument Site',
16: 'Ruined Blood Raider Science Outpost',
17: 'Ruined Blood Raider Temple Site',
18: 'Ruined Guristas Crystal Quarry',
19: 'Ruined Guristas Monument Site',
20: 'Ruined Guristas Science Outpost',
21: 'Ruined Guristas Temple Site',
22: 'Ruined Sansha Crystal Quarry',
23: 'Ruined Sansha Monument Site',
24: 'Ruined Sansha Science Outpost',
25: 'Ruined Sansha Temple Site',
26: 'Ruined Serpentis Crystal Quarry',
27: 'Ruined Serpentis Monument Site',
28: 'Ruined Serpentis Science Outpost',
29: 'Ruined Serpentis Temple Site'
};
// NulSec Data sites, which can also spawn in C1, C2, C3 wormholes
let nullSecDataSites = {
10: 'Abandoned Research Complex DA005',
11: 'Abandoned Research Complex DA015',
12: 'Abandoned Research Complex DC007',
13: 'Abandoned Research Complex DC021',
14: 'Abandoned Research Complex DC035',
15: 'Abandoned Research Complex DG003',
16: 'Central Angel Command Center',
17: 'Central Angel Data Mining Site',
18: 'Central Angel Sparking Transmitter',
19: 'Central Angel Survey Site',
20: 'Central Blood Raider Command Center',
21: 'Central Blood Raider Data Mining Site',
22: 'Central Blood Raider Sparking Transmitter',
23: 'Central Blood Raider Survey Site',
24: 'Central Guristas Command Center',
25: 'Central Guristas Data Mining Center',
26: 'Central Guristas Sparking Transmitter',
27: 'Central Guristas Survey Site',
28: 'Central Sansha Command Center',
29: 'Central Sansha Data Mining Site',
30: 'Central Sansha Sparking Transmitter',
31: 'Central Sansha Survey Site',
32: 'Central Serpentis Command Center',
33: 'Central Serpentis Data Mining Site',
34: 'Central Serpentis Sparking Transmitter',
35: 'Central Serpentis Survey Site'
};
// signature types
let signatureTypes = {
1: { // system type (wh)
1: { // C1 (area id)
1: { // Combat
1: 'Perimeter Ambush Point',
2: 'Perimeter Camp',
3: 'Phase Catalyst Node',
4: 'The Line'
},
2: $.extend({}, nullSecRelicSites, { // Relic
1: 'Forgotten Perimeter Coronation Platform', //*
2: 'Forgotten Perimeter Power Array' //*
}),
3: $.extend({}, nullSecDataSites, { // Data
1: 'Unsecured Perimeter Amplifier', //*
2: 'Unsecured Perimeter Information Center' //*
}),
4: { // Gas
1: 'Barren Perimeter Reservoir', //*
2: 'Token Perimeter Reservoir', //*
3: 'Minor Perimeter Reservoir', //*
4: 'Sizeable Perimeter Reservoir', //*
5: 'Ordinary Perimeter Reservoir' //*
},
5: { // Wormhole
1: 'H121 - C1',
2: 'C125 - C2',
3: 'O883 - C3',
4: 'M609 - C4',
5: 'L614 - C5',
6: 'S804 - C6',
7: 'F135 - Thera'
},
6: { // ORE
1: 'Ordinary Perimeter Deposit', //*
2: 'Common Perimeter Deposit', //*
3: 'Unexceptional Frontier Deposit', //*
4: 'Average Frontier Deposit', //*
5: 'Isolated Core Deposit', //*
6: 'Uncommon Core Deposit' //*
},
7: { // Ghost
}
},
2: { // C2
1: { // Combat
1: 'Perimeter Checkpoint',
2: 'Perimeter Hangar',
3: 'The Ruins of Enclave Cohort 27',
4: 'Sleeper Data Sanctuary'
},
2: $.extend({}, nullSecRelicSites, { // Relic
1: 'Forgotten Perimeter Gateway', //*
2: 'Forgotten Perimeter Habitation Coils' //*
}),
3: $.extend({}, nullSecDataSites, { // Data
1: 'Unsecured Perimeter Comms Relay', //*
2: 'Unsecured Perimeter Transponder Farm' //*
}),
4: { // Gas
1: 'Barren Perimeter Reservoir', //*
2: 'Token Perimeter Reservoir', //*
3: 'Minor Perimeter Reservoir', //*
4: 'Sizeable Perimeter Reservoir', //*
5: 'Ordinary Perimeter Reservoir' //*
},
5: { // Wormhole
1: 'Z647 - C1',
2: 'D382 - C2',
3: 'O477 - C3',
4: 'Y683 - C4',
5: 'N062 - C5',
6: 'R474 - C6',
7: 'F135 - Thera'
},
6: { // ORE
1: 'Ordinary Perimeter Deposit', //*
2: 'Common Perimeter Deposit', //*
3: 'Unexceptional Frontier Deposit', //*
4: 'Average Frontier Deposit', //*
5: 'Isolated Core Deposit', //*
6: 'Uncommon Core Deposit' //*
},
7: { // Ghost
}
},
3: { // C3
1: { // Combat
1: 'Fortification Frontier Stronghold',
2: 'Outpost Frontier Stronghold',
3: 'Solar Cell',
4: 'The Oruze Construct'
},
2: $.extend({}, nullSecRelicSites, { // Relic
1: 'Forgotten Frontier Quarantine Outpost', //*
2: 'Forgotten Frontier Recursive Depot' //*
}),
3: $.extend({}, nullSecDataSites, { // Data
1: 'Unsecured Frontier Database', //*
2: 'Unsecured Frontier Receiver' //*
}),
4: { // Gas
1: 'Barren Perimeter Reservoir', //*
2: 'Token Perimeter Reservoir', //*
3: 'Minor Perimeter Reservoir', //*
4: 'Sizeable Perimeter Reservoir', //*
5: 'Ordinary Perimeter Reservoir', //*
6: 'Bountiful Frontier Reservoir', //*
7: 'Vast Frontier Reservoir' //*
},
5: { // Wormhole
1: 'V301 - C1',
2: 'I182 - C2',
3: 'N968 - C3',
4: 'T405 - C4',
5: 'N770 - C5',
6: 'A982 - C6',
7: 'F135 - Thera'
},
6: { // ORE
1: 'Ordinary Perimeter Deposit', //*
2: 'Common Perimeter Deposit', //*
3: 'Unexceptional Frontier Deposit', //*
4: 'Average Frontier Deposit', //*
5: 'Infrequent Core Deposit', //*
6: 'Unusual Core Deposit' //*
},
7: { // Ghost
}
},
4: { // C4
1: { // Combat
1: 'Frontier Barracks',
2: 'Frontier Command Post',
3: 'Integrated Terminus',
4: 'Sleeper Information Sanctum'
},
2: { // Relic
1: 'Forgotten Frontier Conversion Module',
2: 'Forgotten Frontier Evacuation Center'
},
3: { // Data
1: 'Unsecured Frontier Digital Nexus',
2: 'Unsecured Frontier Trinary Hub'
},
4: { // Gas
1: 'Barren Perimeter Reservoir', //*
2: 'Token Perimeter Reservoir', //*
3: 'Minor Perimeter Reservoir', //*
4: 'Sizeable Perimeter Reservoir', //*
5: 'Ordinary Perimeter Reservoir', //*
6: 'Vast Frontier Reservoir', //*
7: 'Bountiful Frontier Reservoir' //*
},
5: { // Wormhole
// no *wandering* w-space -> k-space wormholes
// all holes are statics or K162
},
6: { // ORE
1: 'Ordinary Perimeter Deposit', //*
2: 'Common Perimeter Deposit', //*
3: 'Unexceptional Frontier Deposit', //*
4: 'Average Frontier Deposit', //*
5: 'Unusual Core Deposit', //*
6: 'Infrequent Core Deposit' //*
},
7: { // Ghost
}
},
5: { // C5
1: { // Combat
1: 'Core Garrison', //*
2: 'Core Stronghold', //*
3: 'Oruze Osobnyk', //*
4: 'Quarantine Area'
},
2: { // Relic
1: 'Forgotten Core Data Field',
2: 'Forgotten Core Information Pen'
},
3: { // Data
1: 'Unsecured Frontier Enclave Relay',
2: 'Unsecured Frontier Server Bank'
},
4: { // Gas
1: 'Barren Perimeter Reservoir', //*
2: 'Minor Perimeter Reservoir', //*
3: 'Ordinary Perimeter Reservoir', //*
4: 'Sizeable Perimeter Reservoir', //*
5: 'Token Perimeter Reservoir', //*
6: 'Bountiful Frontier Reservoir', //*
7: 'Vast Frontier Reservoir', //*
8: 'Instrumental Core Reservoir', //*
9: 'Vital Core Reservoir' //*
},
5: { // Wormhole
1: 'D792 - HS',
2: 'C140 - LS',
3: 'Z142 - 0.0',
4: 'F135 - Thera'
},
6: { // ORE
1: 'Average Frontier Deposit', //*
2: 'Unexceptional Frontier Deposit', //*
3: 'Uncommon Core Deposit', //*
4: 'Ordinary Perimeter Deposit', //*
5: 'Common Perimeter Deposit', //*
6: 'Exceptional Core Deposit', //*
7: 'Infrequent Core Deposit', //*
8: 'Unusual Core Deposit', //*
9: 'Rarified Core Deposit', //*
10: 'Isolated Core Deposit' //*
},
7: { // Ghost
}
},
6: { // C6
1: { // Combat
1: 'Core Citadel', //*
2: 'Core Bastion', //*
3: 'Strange Energy Readings', //*
4: 'The Mirror' //*
},
2: { // Relic
1: 'Forgotten Core Assembly Hall', //*
2: 'Forgotten Core Circuitry Disassembler' //*
},
3: { // Data
1: 'Unsecured Core Backup Array', //*
2: 'Unsecured Core Emergence' //*
},
4: { // Gas
1: 'Barren Perimeter Reservoir', //*
2: 'Minor Perimeter Reservoir', //*
3: 'Ordinary Perimeter Reservoir', //*
4: 'Sizeable Perimeter Reservoir', //*
5: 'Token Perimeter Reservoir', //*
6: 'Bountiful Frontier Reservoir', //*
7: 'Vast Frontier Reservoir', //*
8: 'Instrumental Core Reservoir', //*
9: 'Vital Core Reservoir' //*
},
5: { // Wormhole
1: 'D792 - HS',
2: 'C391 - LS',
3: 'C248 - 0.0',
4: 'F135 - Thera'
},
6: { // ORE
1: 'Ordinary Perimeter Deposit', //*
2: 'Common Perimeter Deposit', //*
3: 'Unexceptional Frontier Deposit', //*
4: 'Average Frontier Deposit', //*
5: 'Rarified Core Deposit' //*
},
7: { // Ghost
1: 'Superior Blood Raider Covert Research Facility' //*
}
},
13: { // Shattered Wormholes
5: { // Wormhole (some of them are static)
1: 'P060 - C1',
2: 'Z647 - C1',
3: 'D382 - C2',
4: 'L005 - C2',
5: 'N766 - C2',
6: 'C247 - C3',
7: 'M267 - C3',
8: 'O477 - C3',
9: 'X877 - C4',
10: 'Y683 - C4',
11: 'H296 - C5',
12: 'H900 - C5',
13: 'H296 - C5',
14: 'N062 - C5', // ??
15: 'V911 - C5',
16: 'U574 - C6',
17: 'V753 - C6',
18: 'W237 - C6',
19: 'B274 - HS',
20: 'D792 - HS',
21: 'D845 - HS',
22: 'N110 - HS',
23: 'A239 - LS',
24: 'C391 - LS',
25: 'J244 - LS',
26: 'U201 - LS', // ??
27: 'U210 - LS',
28: 'C248 - 0.0',
29: 'E545 - 0.0',
30: 'K346 - 0.0',
31: 'Z060 - 0.0'
}
}
}, // system type (k-space)
2: {
10: { // High Sec
5: { // Wormhole
1: 'Z971 - C1',
2: 'R943 - C2',
3: 'X702 - C3',
// no C4
4: 'M555 - C5',
5: 'B041 - C6',
6: 'A641 - HS',
7: 'R051 - LS',
8: 'V283 - 0.0',
9: 'T458 - Thera'
}
},
11: { // Low Sec
5: { // Wormhole
1: 'Z971 - C1',
2: 'R943 - C2',
3: 'X702 - C3',
// no C4
4: 'N432 - C5',
5: 'U319 - C6',
6: 'B449 - HS',
7: 'N944 - LS',
8: 'S199 - 0.0',
9: 'M164 - Thera'
}
},
12: { // 0.0
5: { // Wormhole
1: 'Z971 - C1',
2: 'R943 - C2',
3: 'X702 - C3',
// no C4
4: 'N432 - C5',
5: 'U319 - C6',
6: 'B449 - HS',
7: 'N944 - LS',
8: 'S199 - 0.0',
9: 'L031 - Thera'
}
}
}
};
return signatureTypes;
});

View File

@@ -0,0 +1,733 @@
/**
* Created by exodus4d on 06.07.2015.
* static system effects
*/
define([], function() {
'use strict';
// system effects
let systemEffects = {
wh: {
magnetar: {
1: [
{
effect: 'Damage',
value: '+30%'
},{
effect: 'Missile explosion radius',
value: '+15%'
},{
effect: 'Drone Tracking',
value: '-15%'
},{
effect: 'Targeting Range',
value: '-15%'
},{
effect: 'Tracking Speed',
value: '-15%'
},{
effect: 'Target Painter Strength',
value: '-15%'
}
],
2: [
{
effect: 'Damage',
value: '+44%'
},{
effect: 'Missile explosion radius',
value: '+22%'
},{
effect: 'Drone Tracking',
value: '-22%'
},{
effect: 'Targeting Range',
value: '-22%'
},{
effect: 'Tracking Speed',
value: '-22%'
},{
effect: 'Target Painter Strength',
value: '-22%'
}
],
3: [
{
effect: 'Damage',
value: '+58%'
},{
effect: 'Missile explosion radius',
value: '+29%'
},{
effect: 'Drone Tracking',
value: '-29%'
},{
effect: 'Targeting Range',
value: '-29%'
},{
effect: 'Tracking Speed',
value: '-29%'
},{
effect: 'Target Painter Strength',
value: '-29%'
}
],
4: [
{
effect: 'Damage',
value: '+72%'
},{
effect: 'Missile explosion radius',
value: '+36%'
},{
effect: 'Drone Tracking',
value: '-36%'
},{
effect: 'Targeting Range',
value: '-36%'
},{
effect: 'Tracking Speed',
value: '-36%'
},{
effect: 'Target Painter Strength',
value: '-36%'
}
],
5: [
{
effect: 'Damage',
value: '+86%'
},{
effect: 'Missile explosion radius',
value: '+43%'
},{
effect: 'Drone Tracking',
value: '-43%'
},{
effect: 'Targeting Range',
value: '-43%'
},{
effect: 'Tracking Speed',
value: '-43%'
},{
effect: 'Target Painter Strength',
value: '-43%'
}
],
6: [
{
effect: 'Damage',
value: '+100%'
},{
effect: 'Missile explosion radius',
value: '+50%'
},{
effect: 'Drone Tracking',
value: '-50%'
},{
effect: 'Targeting Range',
value: '-50%'
},{
effect: 'Tracking Speed',
value: '-50%'
},{
effect: 'Target Painter Strength',
value: '-50%'
}
]
},
redGiant: {
1: [
{
effect: 'Heat Damage',
value: '+15%'
},{
effect: 'Overload Bonus',
value: '+30%'
},{
effect: 'Smart Bomb Range',
value: '+30%'
},{
effect: 'Smart Bomb Damage',
value: '+30%'
},{
effect: 'Bomb Damage',
value: '+30%'
}
],
2: [
{
effect: 'Heat Damage',
value: '+22%'
},{
effect: 'Overload Bonus',
value: '+44%'
},{
effect: 'Smart Bomb Range',
value: '+44%'
},{
effect: 'Smart Bomb Damage',
value: '+44%'
},{
effect: 'Bomb Damage',
value: '+44%'
}
],
3: [
{
effect: 'Heat Damage',
value: '+29%'
},{
effect: 'Overload Bonus',
value: '+58%'
},{
effect: 'Smart Bomb Range',
value: '+58%'
},{
effect: 'Smart Bomb Damage',
value: '+58%'
},{
effect: 'Bomb Damage',
value: '+58%'
}
],
4: [
{
effect: 'Heat Damage',
value: '+36%'
},{
effect: 'Overload Bonus',
value: '+72%'
},{
effect: 'Smart Bomb Range',
value: '+72%'
},{
effect: 'Smart Bomb Damage',
value: '+72%'
},{
effect: 'Bomb Damage',
value: '+72%'
}
],
5: [
{
effect: 'Heat Damage',
value: '+43%'
},{
effect: 'Overload Bonus',
value: '+86%'
},{
effect: 'Smart Bomb Range',
value: '+86%'
},{
effect: 'Smart Bomb Damage',
value: '+86%'
},{
effect: 'Bomb Damage',
value: '+86%'
}
],
6: [
{
effect: 'Heat Damage',
value: '+50%'
},{
effect: 'Overload Bonus',
value: '+100%'
},{
effect: 'Smart Bomb Range',
value: '+100%'
},{
effect: 'Smart Bomb Damage',
value: '+100%'
},{
effect: 'Bomb Damage',
value: '+100%'
}
]
},
pulsar: {
1: [
{
effect: 'Shield HP',
value: '+30%'
},{
effect: 'Armor Resists',
value: '-15%'
},{
effect: 'Capacitor recharge',
value: '-15%'
},{
effect: 'Signature',
value: '+30%'
},{
effect: 'NOS / Neut Drain Amount',
value: '+30%'
}
],
2: [
{
effect: 'Shield HP',
value: '+44%'
},{
effect: 'Armor Resists',
value: '-22%'
},{
effect: 'Capacitor recharge',
value: '-22%'
},{
effect: 'Signature',
value: '+44%'
},{
effect: 'NOS / Neut Drain Amount',
value: '+44%'
}
],
3: [
{
effect: 'Shield HP',
value: '+58%'
},{
effect: 'Armor Resists',
value: '-29%'
},{
effect: 'Capacitor recharge',
value: '-29%'
},{
effect: 'Signature',
value: '+58%'
},{
effect: 'NOS / Neut Drain Amount',
value: '+58%'
}
],
4: [
{
effect: 'Shield HP',
value: '+72%'
},{
effect: 'Armor Resists',
value: '-36%'
},{
effect: 'Capacitor recharge',
value: '-36%'
},{
effect: 'Signature',
value: '+72%'
},{
effect: 'NOS / Neut Drain Amount',
value: '+72%'
}
],
5: [
{
effect: 'Shield HP',
value: '+86%'
},{
effect: 'Armor Resists',
value: '-43%'
},{
effect: 'Capacitor recharge',
value: '-43%'
},{
effect: 'Signature',
value: '+86%'
},{
effect: 'NOS / Neut Drain Amount',
value: '+86%'
}
],
6: [
{
effect: 'Shield HP',
value: '+100%'
},{
effect: 'Armor Resists',
value: '-50%'
},{
effect: 'Capacitor recharge',
value: '-50%'
},{
effect: 'Signature',
value: '+100%'
},{
effect: 'NOS / Neut Drain Amount',
value: '+100%'
}
]
},
wolfRayet: {
1: [
{
effect: 'Armor HP',
value: '+30%'
},{
effect: 'Shield Resist',
value: '-15%'
},{
effect: 'Small Weapon Damage',
value: '+60%'
},{
effect: 'Signature Size',
value: '-15%'
}
],
2: [
{
effect: 'Armor HP',
value: '+44%'
},{
effect: 'Shield Resist',
value: '-22%'
},{
effect: 'Small Weapon Damage',
value: '+88%'
},{
effect: 'Signature Size',
value: '-22%'
}
],
3: [
{
effect: 'Armor HP',
value: '+58%'
},{
effect: 'Shield Resist',
value: '-29%'
},{
effect: 'Small Weapon Damage',
value: '+116%'
},{
effect: 'Signature Size',
value: '-29%'
}
],
4: [
{
effect: 'Armor HP',
value: '+72%'
},{
effect: 'Shield Resist',
value: '-36%'
},{
effect: 'Small Weapon Damage',
value: '+144%'
},{
effect: 'Signature Size',
value: '-36%'
}
],
5: [
{
effect: 'Armor HP',
value: '+86%'
},{
effect: 'Shield Resist',
value: '-43%'
},{
effect: 'Small Weapon Damage',
value: '+172%'
},{
effect: 'Signature Size',
value: '-43%'
}
],
6: [
{
effect: 'Armor HP',
value: '+100%'
},{
effect: 'Shield Resist',
value: '-50%'
},{
effect: 'Small Weapon Damage',
value: '+200%'
},{
effect: 'Signature Size',
value: '-50%'
}
]
},
cataclysmic: {
1: [
{
effect: 'Local armor repair amount',
value: '-15%'
},{
effect: 'Local shield boost amount',
value: '-15%'
},{
effect: 'Shield transfer amount',
value: '+30%'
},{
effect: 'Remote repair amount',
value: '+30%'
},{
effect: 'Capacitor capacity',
value: '+30%'
},{
effect: 'Capacitor recharge time',
value: '+15%'
},{
effect: 'Remote Capacitor Transmitter amount',
value: '-15%'
}
],
2: [
{
effect: 'Local armor repair amount',
value: '-22%'
},{
effect: 'Local shield boost amount',
value: '-22%'
},{
effect: 'Shield transfer amount',
value: '+44%'
},{
effect: 'Remote repair amount',
value: '+44%'
},{
effect: 'Capacitor capacity',
value: '+44%'
},{
effect: 'Capacitor recharge time',
value: '+22%'
},{
effect: 'Remote Capacitor Transmitter amount',
value: '-22%'
}
],
3: [
{
effect: 'Local armor repair amount',
value: '-29%'
},{
effect: 'Local shield boost amount',
value: '-29%'
},{
effect: 'Shield transfer amount',
value: '+58%'
},{
effect: 'Remote repair amount',
value: '+58%'
},{
effect: 'Capacitor capacity',
value: '+58%'
},{
effect: 'Capacitor recharge time',
value: '+29%'
},{
effect: 'Remote Capacitor Transmitter amount',
value: '-29%'
}
],
4: [
{
effect: 'Local armor repair amount',
value: '-36%'
},{
effect: 'Local shield boost amount',
value: '-36%'
},{
effect: 'Shield transfer amount',
value: '+72%'
},{
effect: 'Remote repair amount',
value: '+72%'
},{
effect: 'Capacitor capacity',
value: '+72%'
},{
effect: 'Capacitor recharge time',
value: '+36%'
},{
effect: 'Remote Capacitor Transmitter amount',
value: '-36%'
}
],
5: [
{
effect: 'Local armor repair amount',
value: '-43%'
},{
effect: 'Local shield boost amount',
value: '-43%'
},{
effect: 'Shield transfer amount',
value: '+86%'
},{
effect: 'Remote repair amount',
value: '+86%'
},{
effect: 'Capacitor capacity',
value: '+86%'
},{
effect: 'Capacitor recharge time',
value: '+43%'
},{
effect: 'Remote Capacitor Transmitter amount',
value: '-43%'
}
],
6: [
{
effect: 'Local armor repair amount',
value: '-50%'
},{
effect: 'Local shield boost amount',
value: '-50%'
},{
effect: 'Shield transfer amount',
value: '+100%'
},{
effect: 'Remote repair amount',
value: '+100%'
},{
effect: 'Capacitor capacity',
value: '+100%'
},{
effect: 'Capacitor recharge time',
value: '+50%'
},{
effect: 'Remote Capacitor Transmitter amount',
value: '-50%'
}
]
},
blackHole: {
1: [
{
effect: 'Missile velocity',
value: '+15%'
},{
effect: 'Missile Explosion Velocity',
value: '+30%'
},{
effect: 'Ship velocity',
value: '+30%'
},{
effect: 'Stasis Webifier Strength',
value: '-15%'
},{
effect: 'Inertia',
value: '+15%'
},{
effect: 'Targeting range',
value: '+30%'
}
],
2: [
{
effect: 'Missile velocity',
value: '+22%'
},{
effect: 'Missile Explosion Velocity',
value: '+44%'
},{
effect: 'Ship velocity',
value: '+44%'
},{
effect: 'Stasis Webifier Strength',
value: '-22%'
},{
effect: 'Inertia',
value: '+22%'
},{
effect: 'Targeting range',
value: '+44%'
}
],
3: [
{
effect: 'Missile velocity',
value: '+29%'
},{
effect: 'Missile Explosion Velocity',
value: '+58%'
},{
effect: 'Ship velocity',
value: '+58%'
},{
effect: 'Stasis Webifier Strength',
value: '-29%'
},{
effect: 'Inertia',
value: '+29%'
},{
effect: 'Targeting range',
value: '+58%'
}
],
4: [
{
effect: 'Missile velocity',
value: '+36%'
},{
effect: 'Missile Explosion Velocity',
value: '+72%'
},{
effect: 'Ship velocity',
value: '+72%'
},{
effect: 'Stasis Webifier Strength',
value: '-36%'
},{
effect: 'Inertia',
value: '+36%'
},{
effect: 'Targeting range',
value: '+72%'
}
],
5: [
{
effect: 'Missile velocity',
value: '+43%'
},{
effect: 'Missile Explosion Velocity',
value: '+86%'
},{
effect: 'Ship velocity',
value: '+86%'
},{
effect: 'Stasis Webifier Strength',
value: '-43%'
},{
effect: 'Inertia',
value: '+43%'
},{
effect: 'Targeting range',
value: '+86%'
}
],
6: [
{
effect: 'Missile velocity',
value: '+50%'
},{
effect: 'Missile Explosion Velocity',
value: '+100%'
},{
effect: 'Ship velocity',
value: '+100%'
},{
effect: 'Stasis Webifier Strength',
value: '-50%'
},{
effect: 'Inertia',
value: '+50%'
},{
effect: 'Targeting range',
value: '+100%'
}
]
}
}
};
return systemEffects;
});

View File

@@ -0,0 +1,118 @@
define([
'jquery',
'app/init',
'app/util'
], function($, Init, Util) {
'use strict';
let config = {
counterDigitSmallClass: 'pf-digit-counter-small',
counterDigitLargeClass: 'pf-digit-counter-large'
};
/**
* update element with time information
* @param element
* @param tempDate
* @param round
*/
let updateDateDiff = function(element, tempDate, round){
let diff = Util.getTimeDiffParts(tempDate, new Date());
let days = diff.days;
let hrs = diff.hours;
let min = diff.min;
let leftSec = diff.sec;
let parts = [];
if(
round === 'd' &&
days >= 1
){
parts.push('<span class="' + config.counterDigitLargeClass + '">' + '&gt;&nbsp;1d' + '</span>');
}else{
if(
days > 0 ||
parts.length > 0
){
parts.push('<span class="' + config.counterDigitLargeClass + '">' + days + 'd' + '</span>');
}
if(
hrs > 0 ||
parts.length > 0
){
parts.push('<span class="' + config.counterDigitSmallClass + '">' + hrs + 'h' + '</span>');
}
if(
min > 0 ||
parts.length > 0
){
parts.push('<span class="' + config.counterDigitSmallClass + '">' + min + 'm' + '</span>');
}
if(
leftSec >= 0 ||
parts.length > 0
){
parts.push('<span class="' + config.counterDigitSmallClass + '">' + leftSec + 's' + '</span>');
}
}
element.html(parts.join(' '));
};
/**
* destroy all active counter recursive
*/
$.fn.destroyTimestampCounter = function(){
return this.each(function(){
let element = $(this);
element.find('[data-counter="init"]').each(function(){
let interval = $(this).data('interval');
if(interval){
clearInterval(interval);
element.removeAttr('data-counter')
.removeData('interval')
.removeClass('stopCounter');
}
});
});
};
/**
* init a live counter based on a unix timestamp
* @param round string e.g. 'd' => round days
*/
$.fn.initTimestampCounter = function(round){
return this.each(function(){
let element = $(this);
let timestamp = parseInt( element.text() );
// do not init twice
if(timestamp > 0){
// mark as init
element.attr('data-counter', 'init');
let date = new Date( timestamp * 1000);
updateDateDiff(element, date, round);
// show element (if invisible) after first update
element.css({'visibility': 'initial'});
let refreshIntervalId = window.setInterval(function(){
// update element with current time
if( !element.hasClass('stopCounter')){
updateDateDiff(element, date, round);
}else{
clearInterval( element.data('interval') );
}
}, 500);
element.data('interval', refreshIntervalId);
}
});
};
});

View File

@@ -0,0 +1,11 @@
define([
'datatables.net',
'datatables.net-buttons',
'datatables.net-buttons-html',
'datatables.net-responsive',
'datatables.net-select'
], (a, b) => {
'use strict';
// all Datatables stuff is available...
});

View File

@@ -0,0 +1,453 @@
/**
* Init
*/
define(['jquery'], function($) {
'use strict';
let Config = {
path: {
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
// access API
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
// 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
// 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
// connection API
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
// route API
searchRoute: 'api/route/search', // ajax URL - search system routes
// stats API
getStatisticsData: 'api/statistic/getData', // ajax URL - get statistics data (activity log)
// GitHub API
gitHubReleases: 'api/github/releases' // ajax URL - get release info from GitHub
},
breakpoints: [
{ name: 'desktop', width: Infinity },
{ name: 'tablet', width: 1200 },
{ name: 'fablet', width: 780 },
{ name: 'phone', width: 480 }
],
animationSpeed: {
splashOverlay: 300, // "splash" loading overlay
headerLink: 100, // links in head bar
mapOverlay: 200, // show/hide duration for map overlays
mapOverlayLocal: 180, // show/hide duration for map "local" overlay
mapMoveSystem: 180, // system position has changed animation
mapDeleteSystem: 200, // remove system from map
mapModule: 200, // show/hide of an map module
dialogEvents: 180 // dialog events /slide/show/...
},
syncStatus: {
type: 'ajax',
webSocket: {
status: 'closed',
class: 'txt-color-danger',
timestamp: undefined
},
sharedWorker: {
status: 'offline', // SharedWorker status
class: 'txt-color-danger',
timestamp: undefined
},
ajax: {
status: 'enabled',
class: 'txt-color-success',
timestamp: undefined
}
},
performanceLogging: {
keyServerMapData: 'UPDATE_SERVER_MAP', // ajax request update map data
keyClientMapData: 'UPDATE_CLIENT_MAP', // update client map data
keyServerUserData: 'UPDATE_SERVER_USER_DATA', // ajax request update map user data
keyClientUserData: 'UPDATE_CLIENT_USER_DATA', // update client map user data
},
mapIcons: [ // map tab-icons
{
class: 'fa-desktop',
label: 'desktop',
unicode: '&#xf108;'
},{
class: 'fa-bookmark',
label: 'bookmark',
unicode: '&#xf02e;'
},{
class: 'fa-cube',
label: 'cube',
unicode: '&#xf1b2;'
},{
class: 'fa-plane',
label: 'plane',
unicode: '&#xf072;'
},{
class: 'fa-globe',
label: 'globe',
unicode: '&#xf0ac;'
},{
class: 'fa-rocket',
label: 'rocket',
unicode: '&#xf135;'
},{
class: 'fa-life-ring',
label: 'life ring',
unicode: '&#xf1cd;'
},{
class: 'fa-heart',
label: 'heart',
unicode: '&#xf004;'
}
],
classes: {
// log types
logTypes: {
info: {
class: 'pf-log-info',
label: 'info'
},
warning: {
class: 'pf-log-warning',
label: 'warning'
},
error: {
class: 'pf-log-error',
label: 'error'
}
},
// system effects
systemEffects: {
effect: {
class: 'pf-system-effect',
name: 'no effect'
},
magnetar: {
class: 'pf-system-effect-magnetar',
name: 'magnetar'
},
redGiant: {
class: 'pf-system-effect-redgiant',
name: 'red giant'
},
pulsar: {
class: 'pf-system-effect-pulsar',
name: 'pulsar'
},
wolfRayet: {
class: 'pf-system-effect-wolfrayet',
name: 'wolf rayet'
},
cataclysmic: {
class: 'pf-system-effect-cataclysmic',
name: 'cataclysmic'
},
blackHole: {
class: 'pf-system-effect-blackhole',
name: 'black hole'
}
},
// system security
systemSecurity: {
security: {
class: 'pf-system-sec'
},
'SH': {
class: 'pf-system-sec-unknown'
},
'H': {
class: 'pf-system-sec-highSec'
},
'L': {
class: 'pf-system-sec-lowSec'
},
'0.0': {
class: 'pf-system-sec-nullSec'
},
'C6': {
class: 'pf-system-sec-high'
},
'C5': {
class: 'pf-system-sec-high'
},
'C4': {
class: 'pf-system-sec-mid'
},
'C3': {
class: 'pf-system-sec-mid'
},
'C2': {
class: 'pf-system-sec-low'
},
'C1': {
class: 'pf-system-sec-low'
}
},
// true sec
trueSec: {
'0.0': {
class: 'pf-system-security-0-0'
},
'0.1': {
class: 'pf-system-security-0-1'
},
'0.2': {
class: 'pf-system-security-0-2'
},
'0.3': {
class: 'pf-system-security-0-3'
},
'0.4': {
class: 'pf-system-security-0-4'
},
'0.5': {
class: 'pf-system-security-0-5'
},
'0.6': {
class: 'pf-system-security-0-6'
},
'0.7': {
class: 'pf-system-security-0-7'
},
'0.8': {
class: 'pf-system-security-0-8'
},
'0.9': {
class: 'pf-system-security-0-9'
},
'1.0': {
class: 'pf-system-security-1-0'
}
},
// system info
systemInfo: {
rally: {
class: 'pf-system-info-rally',
label: 'rally point'
}
},
// easy-pie-charts
pieChart: {
class: 'pf-pie-chart', // class for all pie charts
pieChartMapCounterClass: 'pf-pie-chart-map-timer' // class for timer chart
}
},
// map scopes
defaultMapScope: 'wh', // default scope for connection
// map connection types
connectionTypes: {
jumpbridge: {
cssClass: 'pf-map-connection-jumpbridge',
paintStyle: {
dashstyle: '4 2 1 2'
}
},
stargate: {
cssClass: 'pf-map-connection-stargate',
paintStyle: {
dashstyle: '0' // solid line
}
},
wh_eol: {
cssClass: 'pf-map-connection-wh-eol',
paintStyle: {
dashstyle: '0' // solid line
}
},
wh_fresh: {
cssClass: 'pf-map-connection-wh-fresh',
paintStyle: {
dashstyle: '0' // solid line
}
},
wh_reduced: {
cssClass: 'pf-map-connection-wh-reduced',
paintStyle: {
dashstyle: '0' // solid line
}
},
wh_critical: {
cssClass: 'pf-map-connection-wh-critical',
paintStyle: {
dashstyle: '0' // solid line
}
},
frigate: {
cssClass: 'pf-map-connection-frig',
paintStyle: {
dashstyle: '0.99'
},
overlays:[
['Label',
{
label: 'frig',
cssClass: ['pf-map-connection-overlay', 'frig'].join(' '),
location: 0.6
}]
]
},
preserve_mass: {
cssClass: 'pf-map-connection-preserve-mass',
overlays:[
['Label',
{
label: '<i class="fa fa-fw fa-warning"></i>&nbsp;save mass',
cssClass: ['pf-map-connection-overlay', 'mass'].join(' '),
location: 0.6
}]
]
},
active: {
cssClass: 'pf-map-connection-active'
}
},
// signature groups
signatureGroups: {
1: {
name: '(combat site|kampfgebiet|site de combat)', //*
label: 'Combat'
},
2: {
name: '(relic site|reliktgebiet|site de reliques)', //*
label: 'Relic'
},
3: {
name: '(data site|datengebiet|site de données)',
label: 'Data'
},
4: {
name: '(gas site|gasgebiet|site de collecte de gaz)',
label: 'Gas'
},
5: {
name: '(wormhole|wurmloch|trou de ver)',
label: 'Wormhole'
},
6: {
name: '(ore site|mineraliengebiet|site de minerai)',
label: 'Ore'
},
7: {
name: '(ghost)',
label: 'Ghost'
}
},
// frigate wormholes
frigateWormholes: {
1: { // C1
1: 'E004 - C1',
2: 'L005 - C2',
3: 'Z006 - C3',
4: 'M001 - C4',
5: 'C008 - C5',
6: 'G008 - C6',
7: 'Q003 - 0.0',
8: 'A009 - (shattered)'
},
2: { // C2
1: 'E004 - C1',
2: 'L005 - C2',
3: 'Z006 - C3',
4: 'M001 - C4',
5: 'C008 - C5',
6: 'G008 - C6',
7: 'Q003 - 0.0',
8: 'A009 - (shattered)'
},
3: { // C3
1: 'E004 - C1',
2: 'L005 - C2',
3: 'Z006 - C3',
4: 'M001 - C4',
5: 'C008 - C5',
6: 'G008 - C6',
7: 'Q003 - 0.0',
8: 'A009 - (shattered)'
},
4: { // C4
1: 'E004 - C1',
2: 'L005 - C2',
3: 'Z006 - C3',
4: 'M001 - C4',
5: 'C008 - C5',
6: 'G008 - C6',
7: 'Q003 - 0.0',
8: 'A009 - (shattered)'
},
5: { // C5
1: 'E004 - C1',
2: 'L005 - C2',
3: 'Z006 - C3',
4: 'M001 - C4',
5: 'C008 - C5',
6: 'G008 - C6',
7: 'Q003 - 0.0',
8: 'A009 - (shattered)'
},
6: { // C6
1: 'E004 - C1',
2: 'L005 - C2',
3: 'Z006 - C3',
4: 'M001 - C4',
5: 'C008 - C5',
6: 'G008 - C6',
7: 'Q003 - 0.0',
8: 'A009 - (shattered)'
},
13: { // Shattered Wormholes (some of them are static)
1: 'E004 - C1',
2: 'L005 - C2',
3: 'Z006 - C3',
4: 'M001 - C4',
5: 'C008 - C5',
6: 'G008 - C6',
7: 'Q003 - 0.0',
8: 'A009 - (shattered)'
}
},
// incoming wormholes
incomingWormholes: {
1: 'K162 - C1/2/3 (unknown)',
2: 'K162 - C4/5 (dangerous)',
3: 'K162 - C6 (deadly)',
4: 'K162 - HS',
5: 'K162 - LS',
6: 'K162 - 0.0',
7: 'K162 - Thera'
}
};
return Config;
});

451
public/js/v1.3.2/app/key.js Normal file
View File

@@ -0,0 +1,451 @@
define([
'jquery'
], function($) {
'use strict';
let allCombo = {
// global -------------------------------------------------------------------------------------------
tabReload: {
group: 'global',
label: 'Close open dialog',
keyNames: ['ESC']
},
// signature ----------------------------------------------------------------------------------------
signatureSelect: {
group: 'signatures',
label: 'Select multiple rows',
keyNames: ['CONTROL', 'CLICK']
}
};
let allEvents = {
// global -------------------------------------------------------------------------------------------
tabReload: {
group: 'global',
label: 'Reload tab',
keyNames: ['CONTROL', 'R']
},
signaturePaste: {
group: 'global',
label: 'Paste signatures from clipboard',
keyNames: ['CONTROL', 'V'],
alias: 'paste'
},
// map ----------------------------------------------------------------------------------------------
mapSystemAdd: {
group: 'map',
label: 'Add new system',
keyNames: ['CONTROL', 'S']
},
mapSystemsSelect: {
group: 'map',
label: 'Select all systems',
keyNames: ['CONTROL', 'A']
},
mapSystemsDelete: {
group: 'map',
label: 'Delete selected systems',
keyNames: ['CONTROL', 'D']
}
};
let groups = {
global: {
label: 'Global'
},
map: {
label: 'Map'
},
signatures: {
label: 'Signature'
}
};
/**
* enables some debug output in console
* @type {boolean}
*/
let debug = false;
/**
* check interval for "new" active keys
* @type {number}
*/
let keyWatchPeriod = 100;
/**
* DOM data key for an element that lists all active events (comma separated)
* @type {string}
*/
let dataKeyEvents = 'key-events';
/**
* DOM data key prefix whether domElement that holds the trigger needs to be "focused"
* @type {string}
*/
let dataKeyFocusPrefix = 'key-focus-';
/**
* DOM data key that holds the callback function for that element
* @type {string}
*/
let dataKeyCallbackPrefix = 'key-callback-';
/**
* check if module is initiated
*/
let isInit = false;
/**
* global key map holds all active (hold down) keys
* @type {{}}
*/
let map = {};
/**
* show debug information in console
* @param msg
* @param element
*/
let debugWatchKey = (msg, element) => {
if(debug){
console.info(msg, element);
}
};
/**
* get all active (hold down) keys at this moment
* @returns {Array}
*/
let getActiveKeys = () => {
return Object.keys(map);
};
/**
* callback function that compares two arrays
* @param element
* @param index
* @param array
*/
let compareKeyLists = function(element, index, array) {
return this.find(x => x === element);
};
/**
* get event names that COULD lead to a "full" event (not all keys pressed yet)
* @param keyList
* @returns {Array}
*/
let checkEventNames = (keyList) => {
let incompleteEvents = [];
for(let event in allEvents){
// check if "some" or "all" keys are pressed for en event
if( keyList.every(compareKeyLists, allEvents[event].keyNames) ){
incompleteEvents.push(event);
}
}
return incompleteEvents;
};
/**
* get all event names
* @returns {Array}
*/
let getAllEventNames = () => {
let eventNames = [];
for(let event in allEvents){
eventNames.push(event);
}
return eventNames;
};
/**
* get all event names that matches a given keyList
* @param keyList
* @param checkEvents
* @returns {Array}
*/
let getMatchingEventNames = (keyList, checkEvents) => {
checkEvents = checkEvents || getAllEventNames();
let events = [];
for(let event of checkEvents){
// check if both key arrays are equal
if(
allEvents[event].keyNames.every(compareKeyLists, keyList) &&
keyList.every(compareKeyLists, allEvents[event].keyNames)
){
events.push(event);
}
}
return events;
};
/**
* init global keyWatch interval and check for event trigger (hotKey combinations)
*/
let init = () => {
if( !isInit ){
// key watch loop -------------------------------------------------------------------------------
let prevActiveKeys = [];
/**
*
* @param e
* @returns {number} 0: no keys hold, 1: invalid match, 2: partial match, 3: match, 4: alias match, 5: event(s) fired
*/
let checkForEvents = (e) => {
let status = 0;
// get all pressed keys
let activeKeys = getActiveKeys();
debugWatchKey('activeKeys', activeKeys);
// check if "active" keys has changes since last loop
if(activeKeys.length){
// check for "incomplete" events (not all keys pressed yet)
let incompleteEvents = checkEventNames(activeKeys);
if(incompleteEvents.length){
// "some" event keys pressed OR "all" keys pressed
status = 2;
// check if key combo matches a registered (valid) event
let events = getMatchingEventNames(activeKeys, incompleteEvents);
if(events.length){
status = 3;
// check events if there are attached elements to it
events.forEach((event) => {
// skip events that has an alias and should not be triggered by key combo
if( !allEvents[event].alias ){
if(allEvents[event].elements){
// search for callback functions attached to each element
allEvents[event].elements.forEach((domElement) => {
let domElementObj = $(domElement);
// check if event on this element requires active "focus"
let optFocus = domElementObj.data(dataKeyFocusPrefix + event);
if( !(
optFocus &&
document.activeElement !== domElement
)
){
// execute callback if valid
let callback = domElementObj.data(dataKeyCallbackPrefix + event);
if(typeof callback === 'function'){
status = 5;
callback.call(domElement, domElement, e);
}
}
});
}
}else{
status = 4;
}
});
}
}else{
// invalid combo
status = 1;
}
}
// store current keys for next loop check
prevActiveKeys = activeKeys;
return status;
};
// set key-events -------------------------------------------------------------------------------
let evKeyDown = (e) => {
// exclude some HTML Tags from watcher
if(
e.target.tagName !== 'INPUT' &&
e.target.tagName !== 'TEXTAREA'
){
let key = e.key.toUpperCase();
map[key] = true;
// check for any shortcut combo that triggers an event
let status = checkForEvents(e);
if(
status === 2 ||
status === 3 ||
status === 5
){
// prevent SOME browser default actions -> we want 'Pathfinder' shortcuts :)
e.preventDefault();
}
}
};
let evKeyUp = (e) => {
let key = e.key.toUpperCase();
if(map.hasOwnProperty(key)){
delete map[key];
}
};
let container = $('body');
container.on('keydown', evKeyDown);
container.on('keyup', evKeyUp);
// global dom remove listener -------------------------------------------------------------------
// -> check whether the removed element had an event listener active and removes them.
new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if(mutation.type === 'childList'){
for (let i = 0; i < mutation.removedNodes.length; i++){
let removedNode = mutation.removedNodes[i];
if(typeof removedNode.getAttribute === 'function'){
let eventNames = removedNode.getAttribute(dataKeyEvents);
if(eventNames){
let events = eventNames.split(',');
for(let j = 0; i < events.length; j++){
let event = events[j];
let index = allEvents[event].elements.indexOf(removedNode);
if(index > -1){
// remove element from event list
allEvents[event].elements.splice(index, 1);
}
}
}
}
}
}
});
}).observe(document.body, { childList: true, subtree: true });
isInit = true;
}
};
/**
* add a new "shortCut" combination (event) to a DOM element
* @param event
* @param callback
* @param options
*/
$.fn.watchKey = function(event, callback, options){
// default options for keyWatcher on elements
let defaultOptions = {
focus: false, // element must be focused (active)
bubbling: true // elements deeper (children) in the DOM can bubble the event up
};
let customOptions = $.extend(true, {}, defaultOptions, options );
return this.each((i, domElement) => {
let element = $(domElement);
// init global key events
init();
// check if event is "valid" (exists) and is not already set for this element
let validEvent = false;
if(allEvents[event].elements){
if(allEvents[event].elements.indexOf(domElement) === -1){
validEvent = true;
}else{
console.warn('Event "' + event + '" already set');
}
}else{
validEvent = true;
allEvents[event].elements = [];
}
if(validEvent){
// store callback options to dom element
if(customOptions.focus){
let dataAttr = dataKeyFocusPrefix + event;
element.data(dataAttr, true);
// check if DOM element has "tabindex" attr -> required to manually set focus() to it
if(!domElement.hasAttribute('tabindex')){
domElement.setAttribute('tabindex', 0);
}
// element requires a "focus" listener
element.off('click.focusKeyWatcher').on('click.focusKeyWatcher', function(e){
if(
e.target === this ||
customOptions.bubbling
){
this.focus();
debugWatchKey('focus set:', this);
}
});
}
// check if is key combo has a native JS event that should be used instead
if(allEvents[event].alias){
element.on(allEvents[event].alias, callback);
}else{
// store callback function to dom element
let dataAttr = dataKeyCallbackPrefix + event;
element.data(dataAttr, callback);
}
// add eventName to dom element as attribute ----------------------------------------------------
let currentEventNames = element.attr(dataKeyEvents) ? element.attr(dataKeyEvents).split(',') : [];
currentEventNames.push(event);
element.attr(dataKeyEvents, currentEventNames.join(','));
// store domElement to event (global)
allEvents[event].elements.push(domElement);
debugWatchKey('new event "' + event + '" registered', domElement);
}
});
};
/**
* get a array with all available shortcut groups and their events
* @returns {Array}
*/
let getGroupedShortcuts = () => {
let result = $.extend(true, {}, groups);
// add combos and events to groups
let allEntries = [allCombo, allEvents];
for(let i = 0; i < allEntries.length; i++){
for(let event in allEntries[i]){
let data = allEntries[i][event];
//format keyNames for UI
let keyNames = data.keyNames.map( (key) => {
if(key === 'CONTROL'){
key = 'ctrl';
}
return key;
});
let newEventData = {
label: data.label,
keyNames: keyNames
};
if( result[data.group].events ){
result[data.group].events.push(newEventData);
}else{
result[data.group].events = [newEventData];
}
}
}
// convert obj into array
result = Object.values(result);
return result;
};
return {
getGroupedShortcuts: getGroupedShortcuts
};
});

View File

@@ -0,0 +1,530 @@
/**
* logging
*/
define([
'jquery',
'app/init',
'app/util',
'bootbox'
], function($, Init, Util, bootbox) {
'use strict';
let logData = []; // cache object for all log entries
let logDataTable = null; // "Datatables" Object
// Morris charts data
let maxGraphDataCount = 30; // max date entries for a graph
let chartData = {}; // chart Data object for all Morris Log graphs
let config = {
taskDialogId: 'pf-task-dialog', // id for map "task manager" dialog
dialogDynamicAreaClass: 'pf-dynamic-area', // class for dynamic areas
timestampCounterClass: 'pf-timestamp-counter', // class for "timestamp" counter
taskDialogStatusAreaClass: 'pf-task-dialog-status', // class for "status" dynamic area
taskDialogLogTableAreaClass: 'pf-task-dialog-table', // class for "log table" dynamic area
logGraphClass: 'pf-log-graph', // class for all log Morris graphs
tableToolsClass: 'pf-table-tools' // class for table tools
};
/**
* get log time string
* @returns {string}
*/
let getLogTime = function(){
let serverTime = Util.getServerTime();
let logTime = serverTime.toLocaleTimeString('en-US', { hour12: false });
return logTime;
};
/**
* updated "sync status" dynamic dialog area
*/
let updateSyncStatus = function(){
// check if task manager dialog is open
let logDialog = $('#' + config.taskDialogId);
if(logDialog.length){
// dialog is open
requirejs(['text!templates/modules/sync_status.html', 'mustache'], function(templateSyncStatus, Mustache) {
let data = {
timestampCounterClass: config.timestampCounterClass,
syncStatus: Init.syncStatus,
isWebSocket: () => {
return (Util.getSyncType() === 'webSocket');
},
isAjax: () => {
return (Util.getSyncType() === 'ajax');
}
};
let syncStatusElement = $( Mustache.render(templateSyncStatus, data ) );
logDialog.find('.' + config.taskDialogStatusAreaClass).html( syncStatusElement );
logDialog.find('.' + config.timestampCounterClass).initTimestampCounter();
syncStatusElement.initTooltips({
placement: 'right'
});
});
}
};
/**
* shows the logging dialog
*/
let showDialog = function(){
// dialog content
requirejs(['text!templates/dialog/task_manager.html', 'mustache', 'datatables.loader'], function(templateTaskManagerDialog, Mustache) {
let data = {
id: config.taskDialogId,
dialogDynamicAreaClass: config.dialogDynamicAreaClass,
taskDialogStatusAreaClass: config.taskDialogStatusAreaClass,
taskDialogLogTableAreaClass: config.taskDialogLogTableAreaClass,
tableActionBarClass: config.tableToolsClass
};
let contentTaskManager = $( Mustache.render(templateTaskManagerDialog, data) );
let rowElementGraphs = contentTaskManager.find('.row');
let taskDialogLogTableAreaElement = contentTaskManager.find('.' + config.taskDialogLogTableAreaClass);
let logTable = $('<table>', {
class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ')
});
taskDialogLogTableAreaElement.append(logTable);
// init log table
logDataTable = logTable.DataTable({
paging: true,
ordering: true,
order: [ 1, 'desc' ],
autoWidth: false,
hover: false,
pageLength: 10,
lengthMenu: [[5, 10, 25, 50, 100, -1], [5, 10, 25, 50, 100, 'All']],
data: logData, // load cached logs (if available)
language: {
emptyTable: 'No entries',
zeroRecords: 'No entries found',
lengthMenu: 'Show _MENU_ entries',
info: 'Showing _START_ to _END_ of _TOTAL_ entries'
},
columnDefs: [
{
targets: 0,
title: '<i class="fa fa-lg fa-tag"></i>',
width: '18px',
searchable: false,
class: ['text-center'].join(' '),
data: 'status'
},{
targets: 1,
title: '<i class="fa fa-lg fa-fw fa-clock-o"></i>&nbsp;&nbsp;',
width: '50px',
searchable: true,
class: 'text-right',
data: 'time'
},{
targets: 2,
title: '<i class="fa fa-lg fa-fw fa-history"></i>&nbsp;&nbsp;',
width: '35px',
searchable: false,
class: 'text-right',
sType: 'html',
data: 'duration'
},{
targets: 3,
title: 'description',
searchable: true,
data: 'description'
},{
targets: 4,
title: 'type',
width: '40px',
searchable: true,
class: ['text-center'].join(' '),
data: 'type'
},{
targets: 5,
title: 'Prozess-ID&nbsp;&nbsp;&nbsp;',
width: '80px',
searchable: false,
class: 'text-right',
data: 'key'
}
]
});
// open dialog
let logDialog = bootbox.dialog({
title: 'Task-Manager',
message: contentTaskManager,
size: 'large',
buttons: {
close: {
label: 'close',
className: 'btn-default'
}
}
});
// modal dialog is shown
logDialog.on('shown.bs.modal', function(e) {
updateSyncStatus();
// show Morris graphs ----------------------------------------------------------
// function for chart label formation
let labelYFormat = function(y){
return Math.round(y) + 'ms';
};
for(let key in chartData) {
if(chartData.hasOwnProperty(key)) {
// create a chart for each key
let colElementGraph = $('<div>', {
class: ['col-md-6'].join(' ')
});
// graph element
let graphElement = $('<div>', {
class: config.logGraphClass
});
let graphArea = $('<div>', {
class: config.dialogDynamicAreaClass
}).append( graphElement );
let headline = $('<h4>', {
text: key
}).prepend(
$('<span>', {
class: ['txt-color', 'txt-color-grayLight'].join(' '),
text: 'Prozess-ID: '
})
);
// show update ping between function calls
let updateElement = $('<small>', {
class: ['txt-color', 'txt-color-blue', 'pull-right'].join(' ')
});
headline.append(updateElement).append('<br>');
// show average execution time
let averageElement = $('<small>', {
class: 'pull-right'
});
headline.append(averageElement);
colElementGraph.append( headline );
colElementGraph.append( graphArea );
graphArea.showLoadingAnimation();
rowElementGraphs.append( colElementGraph );
// cache DOM Elements that will be updated frequently
chartData[key].averageElement = averageElement;
chartData[key].updateElement = updateElement;
chartData[key].graph = Morris.Area({
element: graphElement,
data: [],
xkey: 'x',
ykeys: ['y'],
labels: [key],
units: 'ms',
parseTime: false,
ymin: 0,
yLabelFormat: labelYFormat,
padding: 10,
hideHover: true,
pointSize: 3,
lineColors: ['#375959'],
pointFillColors: ['#477372'],
pointStrokeColors: ['#313335'],
lineWidth: 2,
grid: false,
gridStrokeWidth: 0.3,
gridTextSize: 9,
gridTextFamily: 'Oxygen Bold',
gridTextColor: '#63676a',
behaveLikeLine: true,
goals: [],
goalLineColors: ['#66c84f'],
smooth: false,
fillOpacity: 0.3,
resize: true
});
updateLogGraph(key);
graphArea.hideLoadingAnimation();
}
}
// ------------------------------------------------------------------------------
// add dataTable buttons (extension)
let buttons = new $.fn.dataTable.Buttons( logDataTable, {
buttons: [
{
extend: 'copy',
className: 'btn btn-sm btn-default',
text: '<i class="fa fa-fw fa-clipboard"></i> copy'
},{
extend: 'csv',
className: 'btn btn-sm btn-default',
text: '<i class="fa fa-fw fa-download"></i> csv'
}
]
} );
logDataTable.buttons().container().appendTo( $(this).find('.' + config.tableToolsClass));
});
// modal dialog is closed
logDialog.on('hidden.bs.modal', function(e) {
// clear memory -> destroy all charts
for (let key in chartData) {
if (chartData.hasOwnProperty(key)) {
chartData[key].graph = null;
}
}
});
// modal dialog before hide
logDialog.on('hide.bs.modal', function(e) {
// destroy logTable
logDataTable.destroy(true);
logDataTable= null;
// remove event -> prevent calling this multiple times
$(this).off('hide.bs.modal');
});
});
};
/**
* updates the log graph for a log key
* @param key
* @param duration (if undefined -> just update graph with current data)
*/
let updateLogGraph = function(key, duration){
// check if graph data already exist
if( !(chartData.hasOwnProperty(key))){
chartData[key] = {};
chartData[key].data = [];
chartData[key].graph = null;
chartData[key].averageElement = null;
chartData[key].updateElement = null;
}
// add new value
if(duration !== undefined){
chartData[key].data.unshift(duration);
}
if(chartData[key].data.length > maxGraphDataCount){
chartData[key].data = chartData[key].data.slice(0, maxGraphDataCount);
}
function getGraphData(data) {
let tempChartData = {
data: [],
dataSum: 0,
average: 0
};
for(let x = 0; x < maxGraphDataCount; x++){
let value = 0;
if(data[x]){
value = data[x];
tempChartData.dataSum = Number( (tempChartData.dataSum + value).toFixed(2) );
}
tempChartData.data.push({
x: x,
y: value
});
}
// calculate average
tempChartData.average = Number( ( tempChartData.dataSum / data.length ).toFixed(2) );
return tempChartData;
}
let tempChartData = getGraphData(chartData[key].data);
// add new data to graph (Morris chart) - if is already initialized
if(chartData[key].graph !== null){
let avgElement = chartData[key].averageElement;
let updateElement = chartData[key].updateElement;
let delay = Util.getCurrentTriggerDelay( key, 0 );
if(delay){
updateElement[0].textContent = ' delay: ' + delay + 'ms ';
}
// set/change average line
chartData[key].graph.options.goals = [tempChartData.average];
// change avg. display
avgElement[0].textContent = 'Avg. ' + tempChartData.average + 'ms';
let avgStatus = getLogStatusByDuration(key, tempChartData.average);
let avgStatusClass = Util.getLogInfo( avgStatus, 'class' );
//change avg. display class
if( !avgElement.hasClass(avgStatusClass) ){
// avg status changed!
avgElement.removeClass().addClass('pull-right txt-color ' + avgStatusClass);
// change goals line color
if(avgStatus === 'warning'){
chartData[key].graph.options.goalLineColors = ['#e28a0d'];
$(document).setProgramStatus('slow connection');
}else{
chartData[key].graph.options.goalLineColors = ['#5cb85c'];
}
}
// set new data and redraw
chartData[key].graph.setData( tempChartData.data );
}
return tempChartData.data;
};
/**
* get the log "status" by log duration (ms).
* If duration > warning limit -> show as warning
* @param logKey
* @param logDuration
* @returns {string}
*/
let getLogStatusByDuration = function(logKey, logDuration){
let logStatus = 'info';
if( logDuration > Init.timer[logKey].EXECUTION_LIMIT ){
logStatus = 'warning';
}
return logStatus;
};
/**
* get the css class for a specific log type
* @param logType
* @returns {string}
*/
let getLogTypeIconClass = function(logType){
let logIconClass = '';
switch(logType){
case 'client':
logIconClass = 'fa-user';
break;
case 'server':
logIconClass = 'fa-download';
break;
}
return logIconClass;
};
/**
* init logging -> set global log events
*/
let init = function(){
let maxEntries = 150;
$(window).on('pf:syncStatus', function(){
updateSyncStatus();
});
// set global logging listener
$(window).on('pf:log', function(e, logKey, options){
// check required logging information
if(
options &&
options.duration &&
options.description
){
let logDescription = options.description;
let logDuration = options.duration;
let logType = options.type;
// check log status by duration
let logStatus = getLogStatusByDuration(logKey, logDuration);
let statusClass = Util.getLogInfo( logStatus, 'class' );
let typeIconClass = getLogTypeIconClass(logType);
// update graph data
updateLogGraph(logKey, logDuration);
let logRowData = {
status: '<i class="fa fa-fw fa-circle txt-color ' + statusClass + '"></i>',
time: getLogTime(),
duration: '<span class="txt-color ' + statusClass + '">' + logDuration + '<small>ms</small></span>',
description: logDescription,
type: '<i class="fa ' + typeIconClass + '"></i>',
key: logKey
};
if(logDataTable){
// add row if dataTable is initialized before new log
logDataTable.row.add( logRowData ).draw(false);
}else{
// add row data to cache
logData.push(logRowData);
}
}
// delete old log entries from table ---------------------------------
let rowCount = logData.length;
if( rowCount >= maxEntries ){
if(logDataTable){
logDataTable.rows(0, {order:'index'}).remove().draw(false);
}else{
logData.shift();
}
}
// cache logs in order to keep previous logs in table after reopening the dialog
if(logDataTable){
logData = logDataTable.rows({order:'index'}).data();
}
});
};
return {
init: init,
getLogTime: getLogTime,
showDialog: showDialog
};
});

View File

@@ -0,0 +1,826 @@
/**
* Main loginPage application
*/
define([
'jquery',
'app/init',
'app/util',
'app/render',
'blueImpGallery',
'bootbox',
'lazyload',
'app/ui/header',
'app/ui/logo',
'app/ui/demo_map',
'dialog/account_settings',
'dialog/notification',
'dialog/manual',
'dialog/changelog',
'dialog/credit'
], function($, Init, Util, Render, Gallery, bootbox) {
'use strict';
let config = {
splashOverlayClass: 'pf-splash', // class for "splash" overlay
// header
headerId: 'pf-landing-top', // id for header
headerContainerId: 'pf-header-container', // id for header container
logoContainerId: 'pf-logo-container', // id for main header logo container
headHeaderMapId: 'pf-header-map', // id for header image (svg animation)
// map bg
headMapBgId: 'pf-header-map-bg', // id for header background container
mapNeocomId: 'pf-map-neocom', // id for map "neocom" image
mapBrowserId: 'pf-map-browser', // id for "browser" image
mapBgImageId: 'pf-map-bg-image', // id for "background" map image
// navigation
navigationElementId: 'pf-navbar', // id for navbar element
navigationLinkManualClass: 'pf-navbar-manual', // class for "manual" trigger link
navigationLinkLicenseClass: 'pf-navbar-license', // class for "license" trigger link
navigationVersionLinkClass: 'pf-navbar-version-info', // class for "version information"
// cookie hint
cookieHintId: 'pf-cookie-hint', // id for "cookie hint" element
// login
ssoButtonClass: 'pf-sso-login-button', // class for SSO login button
// character select
characterSelectionClass: 'pf-character-selection', // class for character panel wrapper
characterRowAnimateClass: 'pf-character-row-animate', // class for character panel row during animation
characterImageWrapperClass: 'pf-character-image-wrapper', // class for image wrapper (animated)
characterImageInfoClass: 'pf-character-info', // class for character info layer (visible on hover)
dynamicMessageContainerClass: 'pf-dynamic-message-container', // class for "dynamic" (JS) message container
// gallery
galleryId: 'pf-gallery', // id for gallery container
galleryThumbImageClass: 'pf-landing-image-preview', // class for gallery thumb images
galleryThumbContainerId: 'pf-landing-gallery-thumb-container', // id for gallery thumb images
galleryCarouselId: 'pf-landing-gallery-carousel', // id for "carousel" element
// notification panel
notificationPanelId: 'pf-notification-panel', // id for "notification panel" (e.g. last update information)
// sticky panel
stickyPanelClass: 'pf-landing-sticky-panel', // class for sticky panels
stickyPanelServerId: 'pf-landing-server-panel', // id for EVE Online server status panel
stickyPanelAdminId: 'pf-landing-admin-panel', // id for admin login panel
// animation
animateElementClass: 'pf-animate-on-visible', // class for elements that will be animated to show
defaultAcceptCookieExpire: 365 // default expire for "accept coolies" cookie
};
/**
* set link observer for "version info" dialog
*/
let setVersionLinkObserver = function(){
$('.' + config.navigationVersionLinkClass).off('click').on('click', function(e){
$.fn.changelogsDialog();
});
};
/**
* move panel out of "cookie" accept hint
* @param direction
*/
let moveAdminPanel = (direction) => {
let adminPanel = $('#' + config.stickyPanelAdminId);
adminPanel.css({bottom: ((direction === 'up') ? '+' : '-') + '=35px'});
};
/**
* set page observer
*/
let setPageObserver = function(){
let cookieHintElement = $('#' + config.cookieHintId);
// cookie hint --------------------------------------------------------
cookieHintElement.on('show.bs.collapse', function () {
// move admin panel upwards (prevents overlapping with cookie notice)
moveAdminPanel('up');
});
cookieHintElement.on('hidden.bs.collapse', function () {
moveAdminPanel('down');
});
if(Util.getCookie('cookie') !== '1'){
// hint not excepted
cookieHintElement.collapse('show');
// show Cookie accept hint on SSO login button
let confirmationSettings = {
container: 'body',
placement: 'bottom',
btnOkClass: 'btn btn-sm btn-default',
btnOkLabel: 'dismiss',
btnOkIcon: 'fa fa-fw fa-sign-in',
title: 'Accept cookies',
btnCancelClass: 'btn btn-sm btn-success',
btnCancelLabel: 'accept',
btnCancelIcon: 'fa fa-fw fa-check',
onCancel: function(e, target){
// "Accept cookies"
Util.setCookie('cookie', 1, config.defaultAcceptCookieExpire, 'd');
// set "default" href
let href = $(target).data('bs.confirmation').getHref();
$(e.target).attr('href', href);
},
onConfirm : function(e, target){
// "NO cookies" => trigger "default" href link action
},
href: function(target){
return $(target).attr('href');
}
};
$('.' + config.ssoButtonClass).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();
$.fn.showMapManual();
});
// license ------------------------------------------------------------
$('.' + config.navigationLinkLicenseClass).on('click', function(e){
e.preventDefault();
$.fn.showCreditsDialog(false, true);
});
// releases -----------------------------------------------------------
setVersionLinkObserver();
// tooltips -----------------------------------------------------------
let mapTooltipOptions = {
toggle: 'tooltip',
delay: 150
};
let tooltipElements = $('[title]').not('.slide img');
tooltipElements.tooltip(mapTooltipOptions);
// initial show some tooltips
tooltipElements.filter('[data-show="1"]').tooltip('show');
};
/**
* init image carousel
*/
let initCarousel = function(){
// check if carousel exists
if($('#' + config.galleryCarouselId).length === 0){
return;
}
// extent "blueimp" gallery for a textFactory method to show HTML templates
Gallery.prototype.textFactory = function (obj, callback) {
let newSlideContent = $('<div>')
.addClass('text-content')
.attr('imgTitle', obj.title);
let moduleConfig = {
name: obj.href, // template name
position: newSlideContent,
functions: {
after: function(){
// element inserted -> load complete
callback({
type: 'complete',
target: newSlideContent[0]
});
}
}
};
// render HTML file (template)
let moduleData = {
id: config.headHeaderMapId,
bgId: config.headMapBgId,
neocomId: config.mapNeocomId,
browserId: config.mapBrowserId,
mapBgImageId: config.mapBgImageId
};
Render.showModule(moduleConfig, moduleData);
return newSlideContent[0];
};
// initialize carousel ------------------------------------------------
let carousel = new Gallery([
{
imgTitle: 'Browser',
href: 'ui/map',
type: 'text/html'
},
{
href: 'public/img/landing/responsive.jpg',
imgTitle: 'Responsive layout',
type: 'image/jpg',
thumbnail: ''
},
{
href: 'public/img/landing/pathfinder_1.jpg',
imgTitle: 'Map view',
type: 'image/jpg',
thumbnail: ''
},
{
href: 'public/img/landing/pathfinder_3.jpg',
imgTitle: 'Map information',
type: 'image/jpg',
thumbnail: ''
},
{
href: 'public/img/landing/pathfinder_2.jpg',
imgTitle: 'System information',
type: 'image/jpg',
thumbnail: ''
}
], {
container: '#' + config.galleryCarouselId,
carousel: true,
startSlideshow: false,
titleProperty: 'imgTitle',
transitionSpeed: 600,
slideshowInterval: 5000,
onopened: function () {
// Callback function executed when the Gallery has been initialized
// and the initialization transition has been completed.
// -> show "demo" map
// set title for first slide
$( this.options.container ).find( this.options.titleElement).text('Browser view');
$('#' + config.headHeaderMapId).drawDemoMap(function(){
// zoom map SVGs
$('#' + config.headHeaderMapId + ' svg').velocity({
scaleX: 0.66,
scaleY: 0.66
}, {
duration: 360
});
// position map container
$('#' + config.headHeaderMapId).velocity({
marginTop: '130px',
marginLeft: '-50px'
}, {
duration: 360,
complete: function(){
// show browser
$('#' + config.mapBrowserId).velocity('transition.slideUpBigIn', {
duration: 360,
complete: function(){
// show neocom
$('#' + config.mapNeocomId).velocity('transition.slideLeftIn', {
duration: 180
});
// show background
$('#' + config.mapBgImageId).velocity('transition.shrinkIn', {
duration: 360
});
// when map is shown -> start carousel looping
carousel.play();
}
});
}
});
});
}
});
};
/**
* get all thumbnail elements
* @returns {*|jQuery|HTMLElement}
*/
let getThumbnailElements = function(){
return $('a[data-gallery="#' + config.galleryId + '"]').not('.disabled');
};
/**
* init gallery for thumbnail elements
* @param newElements
*/
let initGallery = function(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() {
$(newElements).each(function() {
let borderless = false;
let galleryElement = $('#' + config.galleryId);
galleryElement.data('useBootstrapModal', !borderless);
galleryElement.toggleClass('blueimp-gallery-controls', borderless);
$(this).on('click', function(e){
e.preventDefault();
e = e || window.event;
let target = e.target || e.srcElement;
let link = target.src ? target.parentNode : target;
let options = {
index: link,
event: e,
container: '#' + config.galleryId,
titleProperty: 'description'
};
new Gallery(allThumbLinks, options);
});
});
});
}
};
/**
* init "YouTube" video preview
*/
let initYoutube = function(){
$('.youtube').each(function() {
// Based on the YouTube ID, we can easily find the thumbnail image
$(this).css('background-image', 'url(//i.ytimg.com/vi/' + this.id + '/sddefault.jpg)');
// Overlay the Play icon to make it look like a video player
$(this).append($('<div/>', {'class': 'play'}));
$(document).delegate('#' + this.id, 'click', function() {
// Create an iFrame with autoplay set to true
let iFrameUrl = '//www.youtube.com/embed/' + this.id + '?autoplay=1&autohide=1';
if ( $(this).data('params') ){
iFrameUrl += '&'+$(this).data('params');
}
// The height and width of the iFrame should be the same as parent
let iFrame = $('<iframe/>', {
frameborder: '0',
src: iFrameUrl,
width: $(this).width(),
height: $(this).height(),
class: 'pricing-big'
});
// Replace the YouTube thumbnail with YouTube HTML5 Player
$(this).replaceWith(iFrame);
});
});
};
/**
* init scrollSpy for navigation bar
*/
let initScrollSpy = function(){
// init scrollspy
// show elements that are currently in the viewport
let showVisibleElements = function(){
// find all elements that should be animated
let visibleElements = $('.' + config.animateElementClass).isInViewport();
$(visibleElements).removeClass( config.animateElementClass );
$(visibleElements).velocity('transition.flipXIn', {
duration: 600,
stagger: 60,
delay: 500,
complete: function(element){
// show "fade" modules (e.g. ribbons)
$(element).find('.fade').addClass('in');
// init gallery for "now" visible elements
let newGalleryElements = $(element).filter('[data-gallery="#' + config.galleryId + '"]');
initGallery(newGalleryElements);
},
visibility: 'visible'
});
};
$( window ).scroll(function() {
// check for new visible elements
showVisibleElements();
});
// initial check for visible elements
showVisibleElements();
// event listener for navigation links
$('.page-scroll').on('click', function(){
// get element to scroll
let anchorTag = $(this).attr('data-anchor');
// scroll to container
$(anchorTag).velocity('scroll', {
duration: 300,
easing: 'swing'
});
});
};
/**
* get current EVE-Online server status
* -> show "server panel"
*/
let initServerStatus = function(){
$.ajax({
type: 'POST',
url: Init.path.getServerStatus,
dataType: 'json'
}).done(function(responseData, textStatus, request){
if(responseData.hasOwnProperty('status')){
let data = responseData.status;
data.stickyPanelServerId = config.stickyPanelServerId;
data.stickyPanelClass = config.stickyPanelClass;
let statusClass = '';
switch(data.serviceStatus.toLowerCase()){
case 'online': statusClass = 'txt-color-green'; break;
case 'vip': statusClass = 'txt-color-orange'; break;
case 'offline': statusClass = 'txt-color-redDarker'; break;
}
data.serviceStatus = {
eve: data.serviceStatus,
style: statusClass
};
requirejs(['text!templates/ui/server_panel.html', 'mustache'], function(template, Mustache) {
let content = Mustache.render(template, data);
$('#' + config.headerId).prepend(content);
$('#' + config.stickyPanelServerId).velocity('transition.slideLeftBigIn', {
duration: 240
});
});
}
}).fail(handleAjaxErrorResponse);
};
/**
* show "notification panel" to user
* -> checks if panel not already shown
*/
let initNotificationPanel = function(){
let storageKey = 'notification_panel';
let currentVersion = Util.getVersion();
let showNotificationPanel = function(){
let data = {
version: Util.getVersion()
};
requirejs(['text!templates/ui/notice.html', 'mustache'], function(template, Mustache) {
let content = Mustache.render(template, data);
let notificationPanel = $('#' + config.notificationPanelId);
notificationPanel.html(content);
notificationPanel.velocity('transition.slideUpIn', {
duration: 300,
complete: function(){
setVersionLinkObserver();
// mark panel as "shown"
Util.getLocalStorage().setItem(storageKey, currentVersion);
}
});
});
};
Util.getLocalStorage().getItem(storageKey).then(function(data){
// check if panel was shown before
if(data){
if(data !== this.version){
// show current panel
showNotificationPanel();
}
}else{
// show current panel
showNotificationPanel();
}
}.bind({
version: currentVersion
}));
};
/**
* load character data from cookie information
* -> all validation is done server side!
*/
let initCharacterSelect = function(){
/**
* init panel animation for an element
* @param imageWrapperElement
*/
let initCharacterAnimation = function(imageWrapperElement){
imageWrapperElement.velocity('stop').velocity('transition.flipBounceXIn', {
display: 'inline-block',
drag: true,
duration: 500
});
// Hover effect for character info layer
imageWrapperElement.hoverIntent(function(e){
let characterInfoElement = $(this).find('.' + config.characterImageInfoClass);
characterInfoElement.velocity('finish').velocity({
width: ['100%', [ 400, 15 ] ]
},{
easing: 'easeOutSine'
});
}, function(e){
let characterInfoElement = $(this).find('.' + config.characterImageInfoClass);
characterInfoElement.velocity('finish').velocity({
width: 0
},{
duration: 150,
easing: 'easeOutSine'
});
});
};
// --------------------------------------------------------------------
/**
* update all character panels -> set CSS class (e.g. after some panels were added/removed,..)
*/
let updateCharacterPanels = function(){
let characterRows = $('.' + config.characterSelectionClass + ' .pf-dynamic-area').parent();
let rowClassIdentifier = ((12 / characterRows.length ) <= 3) ? 3 : (12 / characterRows.length);
$(characterRows).removeClass().addClass('col-sm-' + rowClassIdentifier);
};
// --------------------------------------------------------------------
let removeCharacterPanel = function(panelElement){
$(panelElement).velocity('transition.expandOut', {
duration: 250,
complete: function () {
// lock row for CSS animations while removing...
$(this).parent().addClass(config.characterRowAnimateClass);
$(this).parent().velocity({
width: 0
},{
easing: 'ease',
duration: 300,
complete: function(){
$(this).remove();
// reset column CSS classes for all existing panels
updateCharacterPanels();
}
});
}
});
};
// --------------------------------------------------------------------
let getCharacterAuthLabel = (authStatus) => {
let label = '';
switch(authStatus){
case 'UNKNOWN':
label = 'ERROR';
break;
case 'CORPORATION':
case 'ALLIANCE':
label = 'INVALID';
break;
default:
label = authStatus;
break;
}
return label;
};
// --------------------------------------------------------------------
// request character data for each character panel
requirejs(['text!templates/ui/character_panel.html', 'mustache'], function(template, Mustache){
$('.' + config.characterSelectionClass + ' .pf-dynamic-area').each(function(){
let characterElement = $(this);
characterElement.showLoadingAnimation();
let requestData = {
cookie: characterElement.data('cookie')
};
$.ajax({
type: 'POST',
url: Init.path.getCookieCharacterData,
data: requestData,
dataType: 'json',
context: {
cookieName: requestData.cookie,
characterElement: characterElement,
browserTabId: Util.getBrowserTabId()
}
}).done(function(responseData, textStatus, request){
this.characterElement.hideLoadingAnimation();
if(
responseData.error &&
responseData.error.length > 0
){
$('.' + config.dynamicMessageContainerClass).showMessage({
dismissible: false,
type: responseData.error[0].type,
title: 'Character verification failed',
text: responseData.error[0].message
});
}
if(responseData.hasOwnProperty('character')){
let data = {
link: this.characterElement.data('href'),
cookieName: this.cookieName,
browserTabId: this.browserTabId,
character: responseData.character,
authLabel: getCharacterAuthLabel(responseData.character.authStatus),
authOK: responseData.character.authStatus === 'OK',
hasActiveSession: responseData.character.hasActiveSession === true
};
let content = Mustache.render(template, data);
this.characterElement.html(content);
// lock character selection on click (prevent click spamming)
this.characterElement.find('a').on('click', function(){
$('.' + config.splashOverlayClass).showSplashOverlay();
});
// show character panel (animation settings)
initCharacterAnimation(this.characterElement.find('.' + config.characterImageWrapperClass));
}else{
// character data not available -> remove panel
removeCharacterPanel(this.characterElement);
}
}).fail(function( jqXHR, status, error) {
let characterElement = this.characterElement;
characterElement.hideLoadingAnimation();
// character data not available -> remove panel
removeCharacterPanel(this.characterElement);
});
});
});
};
/**
* default ajax error handler
* -> show user notifications
* @param jqXHR
* @param status
* @param error
*/
let handleAjaxErrorResponse = function(jqXHR, status, error){
let type = status;
let title = 'Status ' + jqXHR.status + ': ' + error;
let message = '';
if(jqXHR.responseText){
let errorObj = $.parseJSON(jqXHR.responseText);
if(
errorObj.error &&
errorObj.error.length > 0
){
for(let i = 0; i < errorObj.error.length; i++){
let errorData = errorObj.error[i];
type = errorData.type;
title = 'Status ' + errorData.code + ': ' + errorData.status;
message = errorData.message;
Util.showNotify({title: title, text: message, type: type});
}
}
}else{
Util.showNotify({title: title, text: message, type: type});
}
};
/**
* main init "landing" page
*/
$(function(){
// clear sessionStorage
Util.clearSessionStorage();
// set default AJAX config
Util.ajaxSetup();
// set Dialog default config
Util.initDefaultBootboxConfig();
// show app information in browser console
Util.showVersionInfo();
// show log off message
let isLogOut = location.search.split('logout')[1];
if(isLogOut !== undefined){
// show logout dialog
let options = {
buttons: {
close: {
label: 'close',
className: ['btn-default'].join(' ')
}
},
content: {
icon: 'fa-sign-out',
class: 'txt-color-warning',
title: 'Logout',
headline: 'Logout',
text: [
'For security reasons, you were logged out automatically',
'Please log in again'
]
}
};
$.fn.showNotificationDialog(options);
// change url (remove logout parameter)
if (history.pushState) {
history.pushState({}, '', location.protocol + '//' + location.host + location.pathname);
}
}
// "Lock" default link action (=> open in new tab)!
// -> until "gallery" is initialized (=> wait animation complete!)
getThumbnailElements().on('click', function(e){
e.preventDefault();
});
// init "lazy loading" for images
$('.' + config.galleryThumbImageClass).lazyload({
threshold : 300
});
// hide splash loading animation
$('.' + config.splashOverlayClass).hideSplashOverlay();
// init server status information
initServerStatus();
// init notification panel
initNotificationPanel();
// init character select
initCharacterSelect();
// init page observer
setPageObserver();
// init carousel
initCarousel();
// init scrollspy
// -> after "Carousel"! required for correct "viewport" calculation (Gallery)!
initScrollSpy();
// init youtube videos
initYoutube();
// draw header logo
$('#' + config.logoContainerId).drawLogo(function(){
// init header animation
$('#' + config.headerContainerId).initHeader(function(){
});
}, false);
});
});

View File

@@ -0,0 +1,132 @@
/**
* context menu
*/
define([
'jquery'
], function($) {
'use strict';
$.fn.contextMenu = function (settings) {
// animation
let animationInType = 'transition.flipXIn';
let animationInDuration = 150;
let animationOutType = 'transition.flipXOut';
let animationOutDuration = 150;
return this.each(function () {
// Open context menu
$(this).off('pf:openContextMenu').on('pf:openContextMenu', function (e, originalEvent, component, hiddenOptions, activeOptions) {
// hide all other open context menus
$('#pf-dialog-wrapper > .dropdown-menu').hide();
let contextMenu = $(settings.menuSelector);
let menuLiElements = contextMenu.find('li');
// show all menu entries
menuLiElements.show();
// disable specific menu entries
for(let i = 0; i < hiddenOptions.length; i++){
contextMenu.find('li[data-action="' + hiddenOptions[i] + '"]').hide();
}
// deactivate all menu entries
menuLiElements.removeClass('active');
//set active specific menu entries
for(let j = 0; j < activeOptions.length; j++){
contextMenu.find('li[data-action="' + activeOptions[j] + '"]').addClass('active');
}
//open menu
contextMenu.css({
position: 'absolute',
left: getLeftLocation(originalEvent),
top: getTopLocation(originalEvent)
}).velocity(animationInType, {
duration: animationInDuration,
complete: function(){
let posX = 0;
let posY = 0;
if(
originalEvent.offsetX &&
originalEvent.offsetY
){
// Chrome
posX = originalEvent.offsetX;
posY = originalEvent.offsetY ;
}else if(originalEvent.originalEvent){
// Firefox -> #415
posX = originalEvent.originalEvent.layerX;
posY = originalEvent.originalEvent.layerY ;
}
let position = {
x: posX,
y: posY
};
$(this).off('click').one('click', {component: component, position: position}, function (e) {
// hide contextmenu
$(this).hide();
let params = {
selectedMenu: $(e.target),
component: e.data.component,
position: e.data.position
};
settings.menuSelected.call(this, params);
return false;
});
}
});
//make sure menu closes on any click
$(document).one('click.closeContextmenu', function () {
$('.dropdown-menu[role="menu"]').velocity(animationOutType, {
duration: animationOutDuration
});
});
return false;
});
});
function getLeftLocation(e) {
let mouseWidth = e.pageX;
let pageWidth = $(window).width();
let menuWidth = $(settings.menuSelector).width();
// opening menu would pass the side of the page
if (mouseWidth + menuWidth > pageWidth &&
menuWidth < mouseWidth) {
return mouseWidth - menuWidth;
}
return mouseWidth;
}
function getTopLocation(e) {
let mouseHeight = e.pageY;
let pageHeight = $(window).height();
let menuHeight = $(settings.menuSelector).height();
// opening menu would pass the bottom of the page
if (mouseHeight + menuHeight > pageHeight &&
menuHeight < mouseHeight) {
return mouseHeight - menuHeight;
}
return mouseHeight;
}
};
});

View File

@@ -0,0 +1,400 @@
define(() => {
'use strict';
class Position {
constructor(config) {
this._defaultConfig = {
container: null, // parent DOM container element
center: null, // DOM elements that works as center
elementClass: 'pf-system', // class for all elements
defaultGapX: 50,
defaultGapY: 50,
gapX: 50, // leave gap between elements (x-axis)
gapY: 50, // leave gap between elements (y-axis)
loops: 2, // max loops around "center" for search
grid: false, // set to [20, 20] to force grid snapping
newElementWidth: 100, // width for new element
newElementHeight: 22, // height for new element
debug: false, // render debug elements
debugElementClass: 'pf-system-debug' // class for debug elements
};
this._config = Object.assign({}, this._defaultConfig, config);
/**
* convert degree into radial unit
* @param deg
* @returns {number}
* @private
*/
this._degToRad = (deg) => {
return deg * Math.PI / 180;
};
/**
* get element dimension/position of a DOM element
* @param element
* @returns {*}
* @private
*/
this._getElementDimension = (element) => {
let dim = null;
let left = 0;
let top = 0;
let a = 0;
let b = 0;
let width = this._config.newElementWidth;
let height = this._config.newElementHeight;
if(Array.isArray(element)){
// xy coordinates
let point = [
element[0] ? parseInt(element[0], 10) : 0,
element[1] ? parseInt(element[1], 10) : 0
];
if(this._config.grid){
point = this._transformPointToGrid(point);
}
left = point[0];
top = point[1];
a = this._config.gapX ;
b = this._config.gapY ;
}else if(element){
// DOM element
left = element.style.left ? parseInt(element.style.left, 10) : 0;
top = element.style.top ? parseInt(element.style.top, 10) : 0;
a = parseInt((element.offsetWidth / 2).toString(), 10) + this._config.gapX;
b = parseInt((element.offsetHeight / 2).toString(), 10) + this._config.gapY;
width = element.offsetWidth;
height = element.offsetHeight;
}
// add "gap" to a and b in order to have some space around elements
return {
left: left,
top: top,
a: a,
b: b,
width: width,
height: height
};
};
/**
* get x/y coordinate on an eclipse around a 2D area by a given radial angle
* @param dim
* @param angle
* @returns {*}
* @private
*/
this._getEllipseCoordinates = (dim, angle) => {
let coordinates = null;
if(dim){
angle = this._degToRad(angle);
coordinates = {
x: Math.round((dim.a * dim.b) / Math.sqrt(Math.pow(dim.b, 2) + Math.pow(dim.a, 2) * Math.pow(Math.tan(angle), 2) )),
y: Math.round((dim.a * dim.b) / Math.sqrt(Math.pow(dim.a, 2) + Math.pow(dim.b, 2) / Math.pow(Math.tan(angle), 2) ))
};
// invert coordinate based on quadrant ------------------------------------------------------------
if( angle > (Math.PI / 2) && angle < (3 * Math.PI / 2) ){
coordinates.x = coordinates.x * -1;
}
if( angle > Math.PI && angle < (2 * Math.PI) ){
coordinates.y = coordinates.y * -1;
}
}
return coordinates;
};
/**
* get dimensions of all surrounding elements
* @returns {Array}
* @private
*/
this._getAllElementDimensions = () => {
let dimensions = [];
let surroundingElements = this._getContainer().getElementsByClassName(this._config.elementClass);
for(let element of surroundingElements){
dimensions.push(this._getElementDimension(element));
}
return dimensions;
};
/**
* transform a x/y point into a x/y point that snaps to grid
* @param point
* @returns {*}
* @private
*/
this._transformPointToGrid = (point) => {
point[0] = Math.floor(point[0] / this._config.grid[0]) * this._config.grid[0];
point[1] = Math.floor(point[1] / this._config.grid[1]) * this._config.grid[1];
return point;
};
/**
* Transform a x/y coordinate into a 2D element with width/height
* @param centerDimension
* @param coordinate
* @returns {*}
* @private
*/
this._transformCoordinate = (centerDimension, coordinate) => {
let dim = null;
if(centerDimension && coordinate){
let left = 0;
let top = 0;
// calculate left/top based on coordinate system quadrant -----------------------------------------
// -> flip horizontal in Q2 and Q3
if(coordinate.x >= 0 && coordinate.y > 0){
// 1. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x;
top = centerDimension.top + 2 * (centerDimension.b - this._config.gapY) - Math.abs(coordinate.y) - this._config.newElementHeight;
}else if(coordinate.x < 0 && coordinate.y > 0){
// 2. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x - this._config.newElementWidth;
top = centerDimension.top + 2 * (centerDimension.b - this._config.gapY) - Math.abs(coordinate.y) - this._config.newElementHeight;
}else if(coordinate.x < 0 && coordinate.y <= 0){
// 3. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x - this._config.newElementWidth;
top = centerDimension.top + Math.abs(coordinate.y);
}else{
// 4. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x;
top = centerDimension.top + Math.abs(coordinate.y);
}
// center horizontal for x = 0 coordinate (top and bottom element) --------------------------------
if(coordinate.x === 0){
left -= Math.round(this._config.newElementWidth / 2);
}
// transform to grid coordinates (if grid snapping is enabled) ------------------------------------
if(this._config.grid){
let point = this._transformPointToGrid([left, top]);
left = point[0];
top = point[1];
}
dim = {
left: left,
top: top,
width: this._config.newElementWidth,
height: this._config.newElementHeight
};
}
return dim;
};
/**
* calc overlapping percent of two given dimensions
* @param dim1
* @param dim2
* @returns {number}
* @private
*/
this._percentCovered = (dim1, dim2) => {
let percent = 0;
if (
(dim1.left <= dim2.left) &&
(dim1.top <= dim2.top) &&
((dim1.left + dim1.width) >= (dim2.left + dim2.width)) &&
((dim1.top + dim1.height) > (dim2.top + dim2.height))
) {
// The whole thing is covering the whole other thing
percent = 100;
}else{
// Only parts may be covered, calculate percentage
dim1.right = dim1.left + dim1.width;
dim1.bottom = dim1.top + dim1.height;
dim2.right = dim2.left + dim2.width;
dim2.bottom = dim2.top + dim2.height;
let l = Math.max(dim1.left, dim2.left);
let r = Math.min(dim1.right, dim2.right);
let t = Math.max(dim1.top, dim2.top);
let b = Math.min(dim1.bottom, dim2.bottom);
if (b >= t && r >= l) {
percent = (((r - l) * (b - t)) / (dim2.width * dim2.height)) * 100;
}
}
return percent;
};
/**
* checks whether dim1 is partially overlapped by any other element
* @param dim1
* @param dimensionContainer
* @param allDimensions
* @returns {boolean}
* @private
*/
this._isOverlapping = (dim1, dimensionContainer, allDimensions) => {
let isOverlapping = false;
if(dim1){
if(this._percentCovered(dimensionContainer, dim1 ) === 100){
// element is within parent container
for(let dim2 of allDimensions){
let percentCovered = this._percentCovered(dim1, dim2);
if(percentCovered){
isOverlapping = true;
// render debug element
this._showDebugElement(dim1, percentCovered);
break;
}
}
}else{
isOverlapping = true;
this._showDebugElement(dim1, 100);
}
}else{
isOverlapping = true;
}
return isOverlapping;
};
/**
* find all dimensions around a centerDimension that are not overlapped by other elements
* @param maxResults
* @param steps
* @param allDimensions
* @param loops
* @returns {Array}
* @private
*/
this._findDimensions = (maxResults, steps, allDimensions, loops) => {
let dimensions = [];
let start = 0;
let end = 360;
let angle = end / steps;
let dimensionContainer = this._getElementDimension(this._getContainer());
steps = steps || 1;
loops = loops || 1;
if(loops === 1){
// check center element
let centerDimension = this._getElementDimension(this._config.center);
if (!this._isOverlapping(centerDimension, dimensionContainer, allDimensions)) {
dimensions.push({
left: centerDimension.left,
top: centerDimension.top,
width: centerDimension.width,
height: centerDimension.height
});
// render debug element
this._showDebugElement(centerDimension, 0);
maxResults--;
}
}
// increase the "gab" between center element and potential found dimensions...
// ... for each recursive loop call, to get an elliptical cycle beyond
this._config.gapX = this._config.defaultGapX + (loops - 1) * 20;
this._config.gapY = this._config.defaultGapY + (loops - 1) * 20;
let centerDimension = this._getElementDimension(this._config.center);
while(maxResults > 0 && start < end){
// get all potential coordinates on an eclipse around a given "centerElementDimension"
let coordinate = this._getEllipseCoordinates(centerDimension, end);
// transform relative x/y coordinate into a absolute 2D area
let checkDimension = this._transformCoordinate(centerDimension, coordinate);
if (!this._isOverlapping(checkDimension, dimensionContainer, allDimensions)) {
dimensions.push({
left: checkDimension.left,
top: checkDimension.top,
width: checkDimension.width,
height: checkDimension.height
});
// render debug element
this._showDebugElement(checkDimension, 0);
maxResults--;
}
end -= angle;
}
if(maxResults > 0 && loops < this._config.loops){
loops++;
steps *= 2;
dimensions = dimensions.concat(this._findDimensions(maxResults, steps, allDimensions, loops));
}
return dimensions;
};
/**
* get container (parent) element
* @returns {*}
* @private
*/
this._getContainer = () => {
return this._config.container ? this._config.container : document.body;
};
/**
* render debug element into parent container
* -> checks overlapping dimension with other elements
* @param dimension
* @param percentCovered
* @private
*/
this._showDebugElement = (dimension, percentCovered) => {
if(this._config.debug){
let element = document.createElement('div');
element.style.left = dimension.left + 'px';
element.style.top = dimension.top + 'px';
element.style.width = dimension.width + 'px';
element.style.height = dimension.height + 'px';
element.style.backgroundColor = Boolean(percentCovered) ? 'rgba(255,0,0,0.1)' : 'rgba(0,255,0,0.1)';
element.innerHTML = Math.round(percentCovered * 100) / 100 + '%';
element.classList.add(this._config.debugElementClass);
this._getContainer().appendChild(element);
}
};
/**
* hide all debug elements
* @private
*/
this._hideDebugElements = () => {
let debugElements = this._getContainer().getElementsByClassName(this._config.debugElementClass);
while(debugElements.length > 0){
debugElements[0].parentNode.removeChild(debugElements[0]);
}
};
// public functions ---------------------------------------------------------------------------------------
/**
* search for surrounding, non overlapping dimensions
* @param maxResults
* @param steps
* @returns {Array}
*/
this.findNonOverlappingDimensions = (maxResults, steps) => {
this._hideDebugElements();
// element dimensions that exist and should be checked for overlapping
let allDimensions = this._getAllElementDimensions();
return this._findDimensions(maxResults, steps, allDimensions);
};
}
}
return {
Position: Position
};
});

View File

@@ -0,0 +1,595 @@
/**
* map overlay functions for "Nearby" table
* Created by Exodus on 13.04.2017.
*/
define([
'jquery',
'app/init',
'app/util',
'app/map/util'
], function($, Init, Util, MapUtil) {
'use strict';
let config = {
overlayClass: 'pf-map-overlay', // class for all map overlays
overlayLocalClass: 'pf-map-overlay-local', // class for "local" overlay
// left section
overlayLocalContentClass: 'pf-map-overlay-local-content', // class for left area - content
overlayLocalHeadlineClass: 'pf-map-overlay-headline', // class for headline
overlayLocalTableClass: 'pf-local-table', // class for local tables
// right section
overlayLocalTriggerClass: 'pf-map-overlay-local-trigger', // class for open/close trigger icon
overlayLocalOpenClass: 'pf-map-overlay-local-open', // class for open status
overlayLocalMainClass: 'pf-map-overlay-local-main', // class for right area (always visible)
overlayLocalUsersClass: 'pf-map-overlay-local-users', // class for active user count
overlayLocalJumpsClass: 'pf-map-overlay-local-jumps', // class for jump distance for table results
// dataTable
tableCellImageClass: 'pf-table-image-cell', // class for table "image" cells
tableCellActionClass: 'pf-table-action-cell', // class for table "action" cells
tableCellActionIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content)
// toolbar
toolbarClass: 'pf-map-overlay-toolbar', // class for toolbar - content
toolbarIconClass: 'pf-map-overlay-toolbar-icon', // class for toolbar icon
toolbarCheckboxClass: 'pf-map-overlay-toolbar-checkbox' // class for toolbar checkbox
};
/**
* checks whether overlay is currently open or not
* @param overlay
* @returns {*}
*/
let isOpen = (overlay) => {
return overlay.hasClass(config.overlayLocalOpenClass);
};
/**
* open overlay -> animation
* @param overlay
*/
let openOverlay = (overlay) => {
if( !isOpen(overlay) ){
let overlayMain = overlay.find('.' + config.overlayLocalMainClass);
overlayMain.find('.' + config.overlayLocalTriggerClass).addClass('right');
overlay.addClass(config.overlayLocalOpenClass);
overlay.velocity({
width: '350px'
},{
duration: Init.animationSpeed.mapOverlayLocal,
easing: 'easeOut'
});
}
};
/**
* close overlay -> animation
* @param overlay
*/
let closeOverlay = (overlay) => {
if( isOpen(overlay) ){
let overlayMain = overlay.find('.' + config.overlayLocalMainClass);
overlayMain.find('.' + config.overlayLocalTriggerClass).removeClass('right');
overlay.removeClass(config.overlayLocalOpenClass);
overlay.velocity({
width: '32px'
},{
duration: Init.animationSpeed.mapOverlayLocal,
easing: 'easeOut'
});
}
};
/**
* sets overlay observer
* @param overlay
* @param mapId
*/
let setOverlayObserver = (overlay, mapId) => {
let overlayMain = overlay.find('.' + config.overlayLocalMainClass);
overlayMain.on('click', function(){
let overlayMain = $(this).parent('.' + config.overlayLocalClass);
let isOpenStatus = isOpen(overlayMain);
// store current state in indexDB (client)
MapUtil.storeLocalData('map', mapId, 'showLocal', !isOpenStatus );
// trigger open/close
if( isOpenStatus ){
closeOverlay(overlay);
}else{
openOverlay(overlay);
}
});
overlayMain.initTooltips({
container: 'body',
placement: 'bottom'
});
};
/**
* filter DataTable rows by column data and return rowIds
* @param table
* @param data
* @param values
* @param checkExistence
*/
let filterRows = (table, data = 'id', values = [], checkExistence = true) => {
return table.rows().eq(0).filter( function (rowIdx) {
let rowExists = values.indexOf( table.row(rowIdx ).data()[data] ) !== -1;
if( !checkExistence ){
rowExists = !rowExists;
}
return rowExists;
});
};
/**
* Update the "headline" within the Overlay
* @param overlay
* @param systemData
* @param characterAll
* @param characterLocal
*/
let updateLocaleHeadline = (overlay, systemData, characterAll = 0, characterLocal = 0) => {
let headlineElement = overlay.find('.' + config.overlayLocalHeadlineClass);
let userCountElement = overlay.find('.' + config.overlayLocalUsersClass);
let secClassBase = Util.getSecurityClassForSystem('security');
let secClass = Util.getSecurityClassForSystem(systemData.security);
let childElements = headlineElement.children('span');
childElements.eq(1).removeClass().addClass(
[secClassBase, secClass].join(' ')
).text(systemData.security);
childElements.eq(2).text(systemData.alias ? systemData.alias : systemData.name);
// update userCount for "near by" count -------------------------------------------------------------------
if( characterAll > 0){
userCountElement.toggleClass( 'txt-color-green', true).toggleClass( 'txt-color-red', false);
}else{
userCountElement.toggleClass( 'txt-color-green', false).toggleClass( 'txt-color-red', true);
}
userCountElement.text(characterAll);
// update userCount in current system ---------------------------------------------------------------------
if( characterLocal > 0){
childElements.eq(3).toggleClass( 'txt-color-green', true).toggleClass( 'txt-color-red', false);
}else{
childElements.eq(3).toggleClass( 'txt-color-green', false).toggleClass( 'txt-color-red', true);
}
childElements.eq(3).text(characterLocal);
};
/**
* updates all changed table rows
* @param systemData
* @param userData
*/
$.fn.updateLocalTable = function(systemData, userData){
return this.each(function(){
let overlay = $(this);
let tableElement = overlay.find('.' + config.overlayLocalTableClass);
let localTable = tableElement.DataTable();
let mapId = systemData.mapId;
let characterAllIds = [];
let characterLocalIds = [];
// system is on map (just for security check)
for(let jumps in userData) {
if( userData.hasOwnProperty(jumps) ){
jumps = parseInt(jumps);
for(let j = 0; j < userData[jumps].length; j++){
// add jump distance
userData[jumps][j].jumps = jumps;
let rowData = userData[jumps][j];
// check for existing rows
let indexes = filterRows(localTable, 'id', [rowData.id]);
if(indexes.length > 0){
// row exists -> update
let changedRow = localTable.row( parseInt(indexes[0]) );
let changedRowElement = changedRow.nodes().to$();
// remove tooltips
changedRowElement.find('[title]').tooltip('hide').tooltip('destroy');
// update data
changedRow.data(rowData);
}else{
// new row
localTable.row.add(rowData);
}
if(jumps === 0){
characterLocalIds.push(rowData.id);
}
characterAllIds.push(rowData.id);
}
}
}
// remove rows that no longer exists ----------------------------------------------------------------------
let indexesRemove = filterRows(localTable, 'id', characterAllIds, false);
localTable.rows(indexesRemove).remove();
localTable.draw();
// update system relevant data in overlay -----------------------------------------------------------------
updateLocaleHeadline(overlay, systemData, characterAllIds.length, characterLocalIds.length);
// open Overlay -------------------------------------------------------------------------------------------
if( !isOpen(overlay) ){
let promiseStore = MapUtil.getLocaleData('map', mapId);
promiseStore.then(function(dataStore) {
if(
dataStore &&
dataStore.showLocal
){
openOverlay(overlay);
}
});
}
});
};
/**
* Access a nested JSON object by "dot.notation" syntax
* @param obj
* @param selector
* @returns {*}
*/
let getDescendantProp = (obj, selector) => {
return selector.split('.').reduce(function(a, b) {
return a[b];
}, obj);
};
/**
* init tooltip for a "DataTables" Cell
* @param api
* @param cell
* @param titleSelector
*/
let initCellTooltip = (api, cell, titleSelector = '') => {
$(cell).hover( function(e){
let rowIdx = api.cell(cell).index().row;
let rowData = api.row(rowIdx).data();
$(this).tooltip({
container: 'body',
title: String( getDescendantProp(rowData, titleSelector) ),
placement: 'left',
delay: 100
}).tooltip('show');
}, function(e){
$(this).tooltip('hide');
});
};
/**
* init all map local overlay on a "parent" element
* @returns {*}
*/
$.fn.initLocalOverlay = function(mapId){
let parentElements = $(this);
require(['datatables.loader'], () => {
parentElements.each(function(){
let parentElement = $(this);
let overlay = $('<div>', {
class: [config.overlayClass, config.overlayLocalClass].join(' ')
});
let content = $('<div>', {
class: [ 'text-right', config.overlayLocalContentClass].join(' ')
});
// crate new route table
let table = $('<table>', {
class: ['compact', 'order-column', config.overlayLocalTableClass].join(' ')
});
let overlayMain = $('<div>', {
text: '',
class: config.overlayLocalMainClass
}).append(
$('<i>', {
class: ['fa', 'fa-chevron-down', 'fa-fw', 'pf-animate-rotate', config.overlayLocalTriggerClass].join(' ')
}),
$('<span>', {
class: ['badge', 'txt-color', 'txt-color-red', config.overlayLocalUsersClass].join(' '),
text: 0
}),
$('<div>', {
class: config.overlayLocalJumpsClass
}).append(
$('<span>', {
class: ['badge', 'txt-color', 'txt-color-grayLight'].join(' '),
text: MapUtil.config.defaultLocalJumpRadius
}).attr('title', 'jumps')
)
);
let headline = $('<div>', {
class: config.overlayLocalHeadlineClass
}).append(
$('<span>', {
html: 'Nearby&nbsp;&nbsp;&nbsp;',
class: 'pull-left'
}),
$('<span>'),
$('<span>'),
$('<span>', {
class: ['badge', ' txt-color', 'txt-color-red'].join(' '),
text: 0
})
);
content.append(headline);
content.append(table);
// toolbar not used for now
// content.append(initToolbar());
overlay.append(overlayMain);
overlay.append(content);
// set observer
setOverlayObserver(overlay, mapId);
parentElement.append(overlay);
// init local table ---------------------------------------------------------------------------------------
table.on('draw.dt', function(e, settings){
// init table tooltips
$(this).find('td').initTooltips({
container: 'body',
placement: 'left'
});
// hide pagination in case of only one page
let paginationElement = overlay.find('.dataTables_paginate');
let pageElements = paginationElement.find('span .paginate_button');
if(pageElements.length <= 1){
paginationElement.hide();
}else{
paginationElement.show();
}
});
// table init complete
table.on( 'init.dt', function (){
// init table head tooltips
$(this).initTooltips({
container: 'body',
placement: 'top'
});
});
let localTable = table.DataTable( {
pageLength: 13, // hint: if pagination visible => we need space to show it
paging: true,
lengthChange: false,
ordering: true,
order: [ 0, 'asc' ],
info: false,
searching: false,
hover: false,
autoWidth: false,
rowId: function(rowData) {
return 'pf-local-row_' + rowData.id; // characterId
},
language: {
emptyTable: '<span>You&nbsp;are&nbsp;alone</span>'
},
columnDefs: [
{
targets: 0,
orderable: true,
title: '<span title="jumps" data-toggle="tooltip">&nbsp;</span>',
width: '1px',
className: ['pf-help-default', 'text-center'].join(' '),
data: 'jumps',
render: {
_: function(data, type, row, meta){
let value = data;
if(type === 'display'){
if(value === 0){
value = '<i class="fa fa-map-marker"></i>';
}
}
return value;
}
},
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
let api = this.DataTable();
initCellTooltip(api, cell, 'log.system.name');
}
},{
targets: 1,
orderable: false,
title: '',
width: '26px',
className: ['pf-help-default', 'text-center', config.tableCellImageClass].join(' '),
data: 'log.ship',
render: {
_: function(data, type, row, meta){
let value = data.typeName;
if(type === 'display'){
value = '<img src="' + Init.url.ccpImageServer + '/Render/' + data.typeId + '_32.png"/>';
}
return value;
}
},
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
let api = this.DataTable();
initCellTooltip(api, cell, 'log.ship.typeName');
}
}, {
targets: 2,
orderable: true,
title: 'ship&nbsp;name',
width: '80px',
data: 'log.ship',
render: {
_: function(data, type, row, meta){
let value = data.name;
if(type === 'display'){
value = '<div class="' + MapUtil.config.tableCellEllipsisClass + ' ' + MapUtil.config.tableCellEllipsis80Class + '">' + data.name + '</div>';
}
return value;
},
sort: 'name'
}
},{
targets: 3,
orderable: true,
title: 'pilot',
data: 'name',
render: {
_: function(data, type, row, meta){
let value = data;
if(type === 'display'){
value = '<div class="' + MapUtil.config.tableCellEllipsisClass + ' ' + MapUtil.config.tableCellEllipsis90Class + '">' + data + '</div>';
}
return value;
}
}
},{
targets: 4,
orderable: false,
title: '',
width: '10px',
className: ['pf-help-default'].join(' '),
data: 'log',
render: {
_: function(data, type, row, meta){
let value = '';
if(type === 'display'){
if(data.station && data.station.id > 0){
value = '<i class="fa fa-home"></i>';
}else if(data.structure && data.structure.id > 0){
value = '<i class="fa fa-industry"></i>';
}
}
return value;
}
},
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
let selector = '';
if(cellData.station && cellData.station.id > 0){
selector = 'log.station.name';
}else if(cellData.structure && cellData.structure.id > 0){
selector = 'log.structure.name';
}
let api = this.DataTable();
initCellTooltip(api, cell, selector);
}
},{
targets: 5,
orderable: false,
title: '<i title="open ingame" data-toggle="tooltip" class="fa fa-id-card text-right"></i>',
width: '10px',
className: [config.tableCellActionClass].join(' '),
data: 'id',
render: {
_: function(data, type, row, meta){
let value = data;
if(type === 'display'){
value = '<i class="fa fa-id-card ' + config.tableCellActionIconClass + '"></i>';
}
return value;
}
},
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
// open character information window (ingame)
$(cell).on('click', { tableApi: this.DataTable(), cellData: cellData }, function(e){
let cellData = e.data.tableApi.cell(this).data();
Util.openIngameWindow(e.data.cellData);
});
}
}
]
});
});
});
};
let initToolbar = () => {
let getCheckbox = (options) => {
return $('<div>', {
class: [config.toolbarCheckboxClass, 'checkbox'].join(' ')
}).append(
$('<input>', {
type: 'checkbox',
id: options.id,
name: options.name,
value: options.value,
checked: 'checked'
}),
$('<label>',{
'for': options.id,
html: options.label
})
);
};
let toolbar = $('<div>', {
class: [config.toolbarClass].join(' ')
}).append(
$('<i>', {
class: ['fa', 'fa-fw', 'fa-lg', 'fa-filter', config.toolbarIconClass, 'pull-left'].join(' ')
}),
getCheckbox({
id: 'test',
name: 'filter_character_active',
value: 1,
checked: true,
label: 'active'
})
);
return toolbar;
};
/**
* Clear Overlay and "Reset"
* @param mapId
*/
$.fn.clearLocalTable = function(mapId){
return this.each(function(){
let overlay = $(this);
// update locale overlay headline -------------------------------------------------------------------------
updateLocaleHeadline(overlay, {
name: 'unknown',
security: ''
});
// clear all table rows -----------------------------------------------------------------------------------
let tableElement = overlay.find('.' + config.overlayLocalTableClass);
let localTable = tableElement.DataTable();
localTable.rows().remove().draw();
});
};
});

View File

@@ -0,0 +1,180 @@
/**
* Map "magnetizing" feature
* jsPlumb extension used: http://morrisonpitt.com/farahey/
*/
define([
'jquery',
'app/map/util',
'farahey'
], function($, MapUtil) {
'use strict';
/**
* Cached current "Magnetizer" object
* @type {Magnetizer}
*/
let m8 = null;
/**
* init a jsPlumb (map) Element for "magnetised" function.
* this is optional and prevents systems from being overlapped
*/
$.fn.initMagnetizer = function(){
let mapContainer = this;
let systems = mapContainer.getSystems();
/**
* helper function
* get current system offset
* @param system
* @returns {{left, top}}
* @private
*/
let _offset = function(system) {
let _ = function(p) {
let v = system.style[p];
return parseInt(v.substring(0, v.length - 2));
};
return {
left:_('left'),
top:_('top')
};
};
/**
* helper function
* set new system offset
* @param system
* @param o
* @private
*/
let _setOffset = function(system, o) {
let markAsUpdated = false;
// new position must be within parent container
// no negative offset!
if(
o.left >= 0 &&
o.left <= 2300
){
markAsUpdated = true;
system.style.left = o.left + 'px';
}
if(
o.top >= 0 &&
o.top <= 498
){
markAsUpdated = true;
system.style.top = o.top + 'px';
}
if(markAsUpdated === true){
$(system).markAsChanged();
}
};
/**
* helper function
* exclude current dragged element(s) from position update
* @param id
* @returns {boolean}
* @private
*/
let _dragFilter = function(id) {
return !$('#' + id).is('.jsPlumb_dragged, .pf-system-locked');
};
let gridConstrain = function(gridX, gridY) {
return function(id, current, delta) {
if( mapContainer.hasClass(MapUtil.config.mapGridClass) ){
// active grid
return {
left:(gridX * Math.floor( (current[0] + delta.left) / gridX )) - current[0],
top:(gridY * Math.floor( (current[1] + delta.top) / gridY )) - current[1]
};
}else{
// no grid
return delta;
}
};
};
// main init for "magnetize" feature ------------------------------------------------------
m8 = new Magnetizer({
container: mapContainer,
getContainerPosition: function(c) {
return c.offset();
},
getPosition:_offset,
getSize: function(system) {
return [ $(system).outerWidth(), $(system).outerHeight() ];
},
getId : function(system) {
return $(system).attr('id');
},
setPosition:_setOffset,
elements: systems,
filter: _dragFilter,
padding: [6, 6],
constrain: gridConstrain(MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension)
});
};
$.fn.destroyMagnetizer = function(){
let mapContainer = this;
// remove cached "magnetizer" instance
m8 = null;
};
/**
* update system positions for "all" systems that are effected by drag&drop
* @param map
* @param e
*/
let executeAtEvent = function(map, e){
if(m8 !== null && e ){
m8.executeAtEvent(e);
map.repaintEverything();
}
};
/**
* rearrange all systems of a map
* needs "magnetization" to be active
* @param map
*/
let executeAtCenter = function(map){
if(m8 !== null){
m8.executeAtCenter();
map.repaintEverything();
}
};
/**
* set/update elements for "magnetization"
* -> (e.g. new systems was added)
* @param map
*/
let setElements = function(map){
if(m8 !== null){
let mapContainer = $(map.getContainer());
let systems = mapContainer.getSystems();
m8.setElements(systems);
// re-arrange systems
executeAtCenter(map);
}
};
return {
executeAtCenter: executeAtCenter,
executeAtEvent: executeAtEvent,
setElements: setElements
};
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,716 @@
/**
* map overlay functions
*/
define([
'jquery',
'app/init',
'app/util',
'app/map/util'
], function($, Init, Util, MapUtil) {
'use strict';
let config = {
logTimerCount: 3, // map log timer in seconds
// map
mapWrapperClass: 'pf-map-wrapper', // wrapper div (scrollable)
// map overlay positions
mapOverlayClass: 'pf-map-overlay', // class for all map overlays
mapOverlayTimerClass: 'pf-map-overlay-timer', // class for map overlay timer e.g. map timer
mapOverlayInfoClass: 'pf-map-overlay-info', // class for map overlay info e.g. map info
overlayLocalClass: 'pf-map-overlay-local', // class for map overlay "local" table
// system
systemHeadClass: 'pf-system-head', // class for system head
// overlay IDs
connectionOverlayId: 'pf-map-connection-overlay', // connection "normal size" ID (jsPlumb)
connectionOverlayEolId: 'pf-map-connection-eol-overlay', // connection EOL overlay ID (jsPlumb)
connectionOverlayArrowId: 'pf-map-connection-arrow-overlay', // connection Arrows overlay ID (jsPlumb)
connectionOverlaySmallId: 'pf-map-connection-small-overlay', // connection "smaller" overlay ID (jsPlumb)
// overlay classes
connectionOverlayClass: 'pf-map-connection-overlay', // class for "normal size" overlay
connectionArrowOverlayClass: 'pf-map-connection-arrow-overlay', // class for "connection arrow" overlay
connectionDiamondOverlayClass: 'pf-map-connection-diamond-overlay', // class for "connection diamond" overlay
connectionOverlaySmallClass: 'pf-map-connection-small-overlay' // class for "smaller" overlays
};
/**
* get MapObject (jsPlumb) from mapElement
* @param mapElement
* @returns {*}
*/
let getMapObjectFromMapElement = (mapElement) => {
let Map = require('app/map/map');
return Map.getMapInstance( mapElement.data('id') );
};
/**
* get map object (jsPlumb) from iconElement
* @param overlayIcon
* @returns {*}
*/
let getMapObjectFromOverlayIcon = (overlayIcon) => {
let mapElement = Util.getMapElementFromOverlay(overlayIcon);
return getMapObjectFromMapElement( mapElement );
};
/**
* add overlays to connections (signature based data)
* @param connections
* @param connectionsData
*/
let addConnectionsOverlay = (connections, connectionsData) => {
let SystemSignatures = require('app/ui/system_signature');
/**
* add label to endpoint
* @param endpoint
* @param label
*/
let addEndpointOverlay = (endpoint, label) => {
endpoint.addOverlay([
'Label',
{
label: MapUtil.getEndpointOverlayContent(label),
id: config.connectionOverlaySmallId,
cssClass: config.connectionOverlaySmallClass,
location: [ 0.9, 0.9 ]
}
]);
};
// loop through all map connections (get from DOM)
for(let connection of connections) {
let connectionId = connection.getParameter('connectionId');
let sourceEndpoint = connection.endpoints[0];
let targetEndpoint = connection.endpoints[1];
let sourceSystem = $(sourceEndpoint.element);
let targetSystem = $(targetEndpoint.element);
let sourceId = sourceSystem.data('id');
let targetId = targetSystem.data('id');
let signatureTypeNames = {
sourceLabels: [],
targetLabels: []
};
// ... find matching connectionData (from Ajax)
for(let connectionData of connectionsData){
if(connectionData.id === connectionId){
signatureTypeNames = MapUtil.getConnectionDataFromSignatures(connection, connectionData);
// ... connection matched -> continue with next one
break;
}
}
let sourceLabel = signatureTypeNames.sourceLabels;
let targetLabel = signatureTypeNames.targetLabels;
// add endpoint overlays ------------------------------------------------------
addEndpointOverlay(sourceEndpoint, sourceLabel);
addEndpointOverlay(targetEndpoint, targetLabel);
// add arrow (connection) overlay that points from "XXX" => "K162" ------------
let overlayType = 'Diamond'; // not specified
let arrowDirection = 1;
if(
(sourceLabel.indexOf('K162') !== -1 && targetLabel.indexOf('K162') !== -1) ||
(sourceLabel.length === 0 && targetLabel.length === 0) ||
(
sourceLabel.length > 0 && targetLabel.length > 0 &&
sourceLabel.indexOf('K162') === -1 && targetLabel.indexOf('K162') === -1
)
){
// unknown direction
overlayType = 'Diamond'; // not specified
arrowDirection = 1;
}else if(
(sourceLabel.indexOf('K162') !== -1) ||
(sourceLabel.length === 0 && targetLabel.indexOf('K162') === -1)
){
// convert default arrow direction
overlayType = 'Arrow';
arrowDirection = -1;
}else{
// default arrow direction is fine
overlayType = 'Arrow';
arrowDirection = 1;
}
connection.addOverlay([
overlayType,
{
width: 12,
length: 15,
location: 0.5,
foldback: 0.85,
direction: arrowDirection,
id: config.connectionOverlayArrowId,
cssClass: (overlayType === 'Arrow') ? config.connectionArrowOverlayClass : config.connectionDiamondOverlayClass
}
]);
}
};
/**
* remove overviews from a Tooltip
* @param endpoint
* @param i
*/
let removeEndpointOverlay = (endpoint, i) => {
endpoint.removeOverlays(config.connectionOverlaySmallId);
};
/**
* format json object with "time parts" into string
* @param parts
* @returns {string}
*/
let formatTimeParts = (parts) => {
let label = '';
if(parts.days){
label += parts.days + 'd ';
}
label += ('00' + parts.hours).slice(-2);
label += ':' + ('00' + parts.min).slice(-2);
return label;
};
/**
* hide default icon and replace it with "loading" icon
* @param iconElement
*/
let showLoading = (iconElement) => {
iconElement = $(iconElement);
let dataName = 'default-icon';
let defaultIconClass = iconElement.data(dataName);
// get default icon class
if( !defaultIconClass ){
// index 0 == 'fa-fw', index 1 == IconName
defaultIconClass = $(iconElement).attr('class').match(/\bfa-\S*/g)[1];
iconElement.data(dataName, defaultIconClass);
}
iconElement.toggleClass( defaultIconClass + ' fa-refresh fa-spin' );
};
/**
* hide "loading" icon and replace with default icon
* @param iconElement
*/
let hideLoading = (iconElement) => {
iconElement = $(iconElement);
let dataName = 'default-icon';
let defaultIconClass = iconElement.data(dataName);
iconElement.toggleClass( defaultIconClass + ' fa-refresh fa-spin' );
};
/**
* git signature data that is linked to a connection for a mapId
* @param mapElement
* @param connections
* @param callback
*/
let getConnectionSignatureData = (mapElement, connections, callback) => {
let mapOverlay = $(mapElement).getMapOverlay('info');
let overlayConnectionIcon = mapOverlay.find('.pf-map-overlay-endpoint');
showLoading(overlayConnectionIcon);
let requestData = {
mapId: mapElement.data('id'),
addData : ['signatures'],
filterData : ['signatures']
};
$.ajax({
type: 'POST',
url: Init.path.getMapConnectionData,
data: requestData,
dataType: 'json',
context: {
mapElement: mapElement,
connections: connections,
overlayConnectionIcon: overlayConnectionIcon
}
}).done(function(connectionsData){
// hide all connection before add them (refresh)
this.mapElement.hideEndpointOverlays();
// ... add overlays
callback(this.connections, connectionsData);
}).always(function() {
hideLoading(this.overlayConnectionIcon);
});
};
/**
* showEndpointOverlays
* -> used by "refresh" overlays (hover) AND/OR initial menu trigger
*/
$.fn.showEndpointOverlays = function(){
let mapElement = $(this);
let map = getMapObjectFromMapElement(mapElement);
let MapUtil = require('app/map/util');
let connections = MapUtil.searchConnectionsByScopeAndType(map, 'wh');
// get connection signature information ---------------------------------------
getConnectionSignatureData(mapElement, connections, addConnectionsOverlay);
};
/**
* hideEndpointOverlays
* -> see showEndpointOverlays()
*/
$.fn.hideEndpointOverlays = function(){
let map = getMapObjectFromMapElement($(this));
let MapUtil = require('app/map/util');
let connections = MapUtil.searchConnectionsByScopeAndType(map, 'wh');
for (let connection of connections){
connection.removeOverlays(config.connectionOverlayArrowId);
connection.endpoints.forEach(removeEndpointOverlay);
}
};
/**
* Overlay options (all available map options shown in overlay)
* "active": (active || hover) indicated whether an icon/option
* is marked as "active".
* "active": Makes icon active when visible
* "hover": Make icon active on hover
*/
let options = {
filter: {
title: 'active filter',
trigger: 'active',
class: 'pf-map-overlay-filter',
iconClass: ['fa', 'fa-fw', 'fa-filter']
},
mapSnapToGrid: {
title: 'active grid',
trigger: 'active',
class: 'pf-map-overlay-grid',
iconClass: ['glyphicon', 'glyphicon-th']
},
mapMagnetizer: {
title: 'active magnetizer',
trigger: 'active',
class: 'pf-map-overlay-magnetizer',
iconClass: ['fa', 'fa-fw', 'fa-magnet']
},
systemRegion: {
title: 'show regions',
trigger: 'hover',
class: 'pf-map-overlay-region',
iconClass: ['fa', 'fa-fw', 'fa-tags'],
hoverIntent: {
over: function(e){
let mapElement = Util.getMapElementFromOverlay(this);
mapElement.find('.' + config.systemHeadClass).each(function(){
let system = $(this);
// init tooltip if not already exists
if ( !system.data('bs.tooltip') ){
system.tooltip({
container: mapElement,
placement: 'right',
title: function(){
return $(this).parent().data('region');
},
trigger: 'manual'
});
}
system.tooltip('show');
});
},
out: function(e){
let mapElement = Util.getMapElementFromOverlay(this);
mapElement.find('.' + config.systemHeadClass).tooltip('hide');
}
}
},
mapEndpoint: {
title: 'refresh signature overlays',
trigger: 'refresh',
class: 'pf-map-overlay-endpoint',
iconClass: ['fa', 'fa-fw', 'fa-link'],
hoverIntent: {
over: function(e){
let mapElement = Util.getMapElementFromOverlay(this);
mapElement.showEndpointOverlays();
},
out: function(e){
// just "refresh" on hover
}
}
},
connection: {
title: 'WH data',
trigger: 'hover',
class: 'pf-map-overlay-connection',
iconClass: ['fa', 'fa-fw', 'fa-fighter-jet'],
hoverIntent: {
over: function(e){
let map = getMapObjectFromOverlayIcon(this);
let MapUtil = require('app/map/util');
let connections = MapUtil.searchConnectionsByScopeAndType(map, 'wh');
let serverDate = Util.getServerTime();
// show connection overlays ---------------------------------------------------
for (let connection of connections) {
let createdTimestamp = connection.getParameter('created');
let updatedTimestamp = connection.getParameter('updated');
let createdDate = Util.convertTimestampToServerTime(createdTimestamp);
let updatedDate = Util.convertTimestampToServerTime(updatedTimestamp);
let createdDiff = Util.getTimeDiffParts(createdDate, serverDate);
let updatedDiff = Util.getTimeDiffParts(updatedDate, serverDate);
// format overlay label
let labels = [
'<i class="fa fa-fw fa-plus-square"></i>&nbsp;' + formatTimeParts(createdDiff),
'<i class="fa fa-fw fa-pencil-square"></i>&nbsp;' + formatTimeParts(updatedDiff)
];
// add label overlay ------------------------------------------------------
connection.addOverlay([
'Label',
{
label: labels.join('<br>'),
id: config.connectionOverlayId,
cssClass: config.connectionOverlaySmallClass,
location: 0.35
}
]);
}
},
out: function(e){
let map = getMapObjectFromOverlayIcon(this);
let MapUtil = require('app/map/util');
let connections = MapUtil.searchConnectionsByScopeAndType(map, 'wh');
for (let connection of connections){
connection.removeOverlays(config.connectionOverlayId);
}
}
}
},
connectionEol: {
title: 'EOL timer',
trigger: 'hover',
class: 'pf-map-overlay-connection-eol',
iconClass: ['fa', 'fa-fw', 'fa-clock-o'],
hoverIntent: {
over: function(e){
let map = getMapObjectFromOverlayIcon(this);
let MapUtil = require('app/map/util');
let connections = MapUtil.searchConnectionsByScopeAndType(map, 'wh', ['wh_eol']);
let serverDate = Util.getServerTime();
for (let connection of connections) {
let eolTimestamp = connection.getParameter('eolUpdated');
let eolDate = Util.convertTimestampToServerTime(eolTimestamp);
let diff = Util.getTimeDiffParts(eolDate, serverDate);
connection.addOverlay([
'Label',
{
label: '<i class="fa fa-fw fa-clock-o"></i>&nbsp;' + formatTimeParts(diff),
id: config.connectionOverlayEolId,
cssClass: [config.connectionOverlayClass, 'eol'].join(' '),
location: 0.25
}
]);
}
},
out: function(e){
let map = getMapObjectFromOverlayIcon(this);
let MapUtil = require('app/map/util');
let connections = MapUtil.searchConnectionsByScopeAndType(map, 'wh', ['wh_eol']);
for (let connection of connections) {
connection.removeOverlay(config.connectionOverlayEolId);
}
}
}
}
};
/**
* get map overlay element by type e.g. timer/counter, info - overlay
* @param overlayType
* @returns {*}
*/
$.fn.getMapOverlay = function(overlayType){
let mapWrapperElement = $(this).parents('.' + config.mapWrapperClass);
let mapOverlay = null;
switch(overlayType){
case 'timer':
mapOverlay = mapWrapperElement.find('.' + config.mapOverlayTimerClass);
break;
case 'info':
mapOverlay = mapWrapperElement.find('.' + config.mapOverlayInfoClass);
break;
case 'local':
mapOverlay = mapWrapperElement.find('.' + config.overlayLocalClass);
break;
}
return mapOverlay;
};
/**
* draws the map update counter to the map overlay timer
* @param percent
* @param value
* @returns {*}
*/
$.fn.setMapUpdateCounter = function(percent, value){
let mapOverlayTimer = $(this);
// check if counter already exists
let counterChart = mapOverlayTimer.getMapCounter();
if(counterChart.length === 0){
// create new counter
counterChart = $('<div>', {
class: [Init.classes.pieChart.class, Init.classes.pieChart.pieChartMapCounterClass].join(' ')
}).attr('data-percent', percent).append(
$('<span>', {
text: value
})
);
mapOverlayTimer.append(counterChart);
// init counter
counterChart.initMapUpdateCounter();
// set tooltip
mapOverlayTimer.attr('data-placement', 'left');
mapOverlayTimer.attr('title', 'update counter');
mapOverlayTimer.tooltip();
}
return counterChart;
};
/**
* get the map counter chart from overlay
* @returns {JQuery|*|T|{}|jQuery}
*/
$.fn.getMapCounter = function(){
return $(this).find('.' + Init.classes.pieChart.pieChartMapCounterClass);
};
$.fn.getMapOverlayInterval = function(){
return $(this).getMapOverlay('timer').getMapCounter().data('interval');
};
/**
* start the map update counter or reset
*/
$.fn.startMapUpdateCounter = function(){
let mapOverlayTimer = $(this);
let counterChart = mapOverlayTimer.getMapCounter();
let maxSeconds = config.logTimerCount;
let counterChartLabel = counterChart.find('span');
let percentPerCount = 100 / maxSeconds;
// update counter
let updateChart = function(tempSeconds){
let pieChart = counterChart.data('easyPieChart');
if(pieChart !== undefined){
counterChart.data('easyPieChart').update( percentPerCount * tempSeconds);
}
counterChartLabel.text(tempSeconds);
};
// main timer function is called on any counter update
let timer = function(mapUpdateCounter){
// decrease timer
let currentSeconds = counterChart.data('currentSeconds');
currentSeconds--;
counterChart.data('currentSeconds', currentSeconds);
if(currentSeconds >= 0){
// update counter
updateChart(currentSeconds);
}else{
// hide counter and reset
clearInterval(mapUpdateCounter);
mapOverlayTimer.velocity('transition.whirlOut', {
duration: Init.animationSpeed.mapOverlay,
complete: function(){
counterChart.data('interval', false);
Util.getMapElementFromOverlay(mapOverlayTimer).trigger('pf:unlocked');
}
});
}
};
// get current seconds (in case the timer is already running)
let currentSeconds = counterChart.data('currentSeconds');
// start values for timer and chart
counterChart.data('currentSeconds', maxSeconds);
updateChart(maxSeconds);
if(
currentSeconds === undefined ||
currentSeconds < 0
){
// start timer
let mapUpdateCounter = setInterval(() => {
timer(mapUpdateCounter);
}, 1000);
// store counter interval
counterChart.data('interval', mapUpdateCounter);
// show overlay
if(mapOverlayTimer.is(':hidden')){
mapOverlayTimer.velocity('stop').velocity('transition.whirlIn', { duration: Init.animationSpeed.mapOverlay });
}
}
};
/**
* update (show/hide) a overlay icon in the "info"-overlay
* show/hide the overlay itself is no icons are visible
* @param option
* @param viewType
*/
$.fn.updateOverlayIcon = function(option, viewType){
let mapOverlayInfo = $(this);
let showOverlay = false;
let mapOverlayIconClass = options[option].class;
// look for the overlay icon that should be updated
let iconElement = mapOverlayInfo.find('.' + mapOverlayIconClass);
if(iconElement){
if(viewType === 'show'){
showOverlay = true;
// check "trigger" and mark as "active"
if(
options[option].trigger === 'active' ||
options[option].trigger === 'refresh'
){
iconElement.addClass('active');
}
// check if icon is not already visible
// -> prevents unnecessary "show" animation
if( !iconElement.data('visible') ){
// display animation for icon
iconElement.velocity({
opacity: [0.8, 0],
scale: [1, 0],
width: ['30px', 0],
marginLeft: ['3px', 0]
},{
duration: 240,
easing: 'easeInOutQuad'
});
iconElement.data('visible', true);
}
}else if(viewType === 'hide'){
iconElement.removeClass('active').velocity('reverse');
iconElement.data('visible', false);
// check if there is any visible icon remaining
let visibleIcons = mapOverlayInfo.find('i:visible');
if(visibleIcons.length > 0){
showOverlay = true;
}
}
}
// show the entire overlay if there is at least one active icon
if(
showOverlay === true &&
mapOverlayInfo.is(':hidden')
){
// show overlay
mapOverlayInfo.velocity('stop').velocity('transition.whirlIn', { duration: Init.animationSpeed.mapOverlay });
}else if(
showOverlay === false &&
mapOverlayInfo.is(':visible')
){
// hide overlay
mapOverlayInfo.velocity('stop').velocity('transition.whirlOut', { duration: Init.animationSpeed.mapOverlay });
}
};
/**
* init all map overlays on a "parent" element
* @returns {*}
*/
$.fn.initMapOverlays = function(){
return this.each(function(){
let parentElement = $(this);
let mapOverlayTimer = $('<div>', {
class: [config.mapOverlayClass, config.mapOverlayTimerClass].join(' ')
});
parentElement.append(mapOverlayTimer);
// ------------------------------------------------------------------------------------
// add map overlay info. after scrollbar is initialized
let mapOverlayInfo = $('<div>', {
class: [config.mapOverlayClass, config.mapOverlayInfoClass].join(' ')
});
// add all overlay elements
for (let prop in options) {
if(options.hasOwnProperty(prop)){
let icon = $('<i>', {
class: options[prop].iconClass.concat( ['pull-right', options[prop].class] ).join(' ')
}).attr('title', options[prop].title).tooltip({
placement: 'bottom',
container: 'body',
delay: 150
});
// add "hover" action for some icons
if(
options[prop].trigger === 'hover' ||
options[prop].trigger === 'refresh'
){
icon.hoverIntent(options[prop].hoverIntent);
}
mapOverlayInfo.append(icon);
}
}
parentElement.append(mapOverlayInfo);
// reset map update timer
mapOverlayTimer.setMapUpdateCounter(100, config.logTimerCount);
});
};
});

View File

@@ -0,0 +1,75 @@
/**
* Created by Exodus on 26.06.2016.
*/
define([
'jquery',
'app/init',
'app/util'
], function($, Init, Util) {
'use strict';
/**
* init map scrollbar
* @param config
* @returns {*}
*/
$.fn.initCustomScrollbar = function(config){
// default config -------------------------------------------------------------------------
let defaultConfig = {
axis: 'x',
theme: 'light-thick',
scrollInertia: 300,
autoExpandScrollbar: false,
scrollButtons:{
scrollAmount: 30,
enable: true
},
callbacks:{
onTotalScrollOffset: 0,
onTotalScrollBackOffset: 0,
alwaysTriggerOffsets: true
},
advanced: {
updateOnBrowserResize: true,
updateOnContentResize: true,
autoExpandHorizontalScroll: true,
autoScrollOnFocus: 'div'
},
mouseWheel:{
enable: false, // scroll weel currently disabled
scrollAmount: 'auto',
axis: 'x',
preventDefault: true
},
scrollbarPosition: 'inside',
autoDraggerLength: true
//autoHideScrollbar: false
};
// init -----------------------------------------------------------------------------------
config = $.extend(true, {}, defaultConfig, config);
return this.each(function(){
let scrollableElement = $(this);
// prevent multiple initialization
scrollableElement.mCustomScrollbar('destroy');
// init custom scrollbars
scrollableElement.mCustomScrollbar(config);
});
};
/**
* scroll to a specific position in the map
* demo: http://manos.malihu.gr/repository/custom-scrollbar/demo/examples/scrollTo_demo.html
* @returns {*} // string or id
*/
$.fn.scrollToX = function(position){
return this.each(function(){
$(this).mCustomScrollbar('scrollTo', position);
});
};
});

View File

@@ -0,0 +1,351 @@
/**
* map system functions
*/
define([
'jquery',
'app/init',
'app/util',
'bootbox',
'app/map/util',
'app/map/layout'
], ($, Init, Util, bootbox, MapUtil, Layout) => {
'use strict';
let config = {
newSystemOffset: {
x: 130,
y: 0
},
systemActiveClass: 'pf-system-active', // class for an active system on a map
dialogRallyId: 'pf-rally-dialog', // id for "Rally point" dialog
dialogRallyPokeDesktopId: 'pf-rally-dialog-poke-desktop', // id for "desktop" poke checkbox
dialogRallyPokeSlackId: 'pf-rally-dialog-poke-slack', // id for "Slack" poke checkbox
dialogRallyPokeDiscordId: 'pf-rally-dialog-poke-discord', // id for "Discord" poke checkbox
dialogRallyPokeMailId: 'pf-rally-dialog-poke-mail', // id for "mail" poke checkbox
dialogRallyMessageId: 'pf-rally-dialog-message', // id for "message" textarea
dialogRallyMessageDefault: '' +
'I need some help!\n\n' +
'- Potential PvP options around\n' +
'- DPS and Logistic ships needed'
};
/**
* show "set rally point" dialog for system
* @param system
*/
$.fn.showRallyPointDialog = (system) => {
let mapData = Util.getCurrentMapData(system.data('mapid'));
requirejs(['text!templates/dialog/system_rally.html', 'mustache'], function(template, Mustache) {
let setCheckboxObserver = (checkboxes) => {
checkboxes.each(function(){
$(this).on('change', function(){
// check all others
let allUnchecked = true;
checkboxes.each(function(){
if(this.checked){
allUnchecked = false;
}
});
let textareaElement = $('#' + config.dialogRallyMessageId);
if(allUnchecked){
textareaElement.prop('disabled', true);
}else{
textareaElement.prop('disabled', false);
}
});
});
};
let sendPoke = (requestData, context) => {
// lock dialog
let dialogContent = context.rallyDialog.find('.modal-content');
dialogContent.showLoadingAnimation();
$.ajax({
type: 'POST',
url: Init.path.pokeRally,
data: requestData,
dataType: 'json',
context: context
}).done(function(data){
}).fail(function( jqXHR, status, error) {
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': sendPoke', text: reason, type: 'warning'});
}).always(function(){
this.rallyDialog.find('.modal-content').hideLoadingAnimation();
});
};
let data = {
id: config.dialogRallyId,
dialogRallyPokeDesktopId: config.dialogRallyPokeDesktopId,
dialogRallyPokeSlackId: config.dialogRallyPokeSlackId,
dialogRallyPokeDiscordId: config.dialogRallyPokeDiscordId,
dialogRallyPokeMailId: config.dialogRallyPokeMailId,
dialogRallyMessageId: config.dialogRallyMessageId ,
desktopRallyEnabled: true,
slackRallyEnabled: Boolean(Util.getObjVal(mapData, 'config.logging.slackRally')),
discordRallyEnabled: Boolean(Util.getObjVal(mapData, 'config.logging.discordRally')),
mailRallyEnabled: Boolean(Util.getObjVal(mapData, 'config.logging.mailRally')),
dialogRallyMessageDefault: config.dialogRallyMessageDefault,
systemId: system.data('id')
};
let content = Mustache.render(template, data);
let rallyDialog = bootbox.dialog({
message: content,
title: 'Set rally point in "' + system.getSystemInfo( ['alias'] ) + '"',
buttons: {
close: {
label: 'cancel',
className: 'btn-default'
},
success: {
label: '<i class="fa fa-fw fa-volume-up"></i> set rally point',
className: 'btn-success',
callback: function() {
let form = $('#' + config.dialogRallyId).find('form');
// get form data
let formData = form.getFormValues();
// update map
system.setSystemRally(1, {
poke: Boolean(formData.pokeDesktop)
});
system.markAsChanged();
// send poke data to server
sendPoke(formData, {
rallyDialog: this
});
}
}
}
});
rallyDialog.initTooltips();
// after modal is shown ==================================================================================
rallyDialog.on('shown.bs.modal', function(e){
// set event for checkboxes
setCheckboxObserver(rallyDialog.find(':checkbox'));
});
});
};
/**
* shows delete dialog for systems that should be deleted
* @param map
* @param systems
* @returns {*}
*/
$.fn.showDeleteSystemDialog = (map, systems = []) => {
let mapContainer = $( map.getContainer() );
let validDeleteSystems = [];
let activeCharacters = 0;
// check if systems belong to map -> security check
for (let system of systems) {
let systemElement = $(system);
if(
systemElement.data('mapid') === mapContainer.data('id') &&
!systemElement.data('locked')
){
// system belongs to map -> valid system
validDeleteSystems.push(system);
activeCharacters += (systemElement.data('userCount') ? parseInt( systemElement.data('userCount') ) : 0);
}
}
if(validDeleteSystems.length){
let msg = '';
if(validDeleteSystems.length === 1){
let deleteSystem = $(validDeleteSystems[0]);
let systemName = deleteSystem.data('name');
let systemAlias = deleteSystem.getSystemInfo( ['alias'] );
let systemNameStr = (systemName === systemAlias) ? '"' + systemName + '"' : '"' + systemAlias + '" (' + systemName + ')';
systemNameStr = '<span class="txt-color txt-color-warning">' + systemNameStr + '</span>';
msg = 'Delete system ' + systemNameStr + ' and all its connections?';
}else{
msg = 'Delete ' + validDeleteSystems.length + ' selected systems and their connections?';
}
// add warning for active characters
if(activeCharacters > 0){
msg += ' <span class="txt-color txt-color-warning">Warning: ' + activeCharacters + ' active characters</span>';
}
let systemDeleteDialog = bootbox.confirm(msg, result => {
if(result){
deleteSystems(map, validDeleteSystems, (deletedSystems) => {
// callback function after deleted -> close dialog
systemDeleteDialog.modal('hide');
// check whether all systems were deleted properly
if(deletedSystems.length !== validDeleteSystems.length){
let notDeletedCount = validDeleteSystems.length - deletedSystems.length;
Util.showNotify({
title: 'Failed to delete systems',
text: '(' + notDeletedCount + '/' + validDeleteSystems.length + ') systems could not be deleted',
type: 'warning'}
);
}else if(deletedSystems.length === 1){
Util.showNotify({title: 'System deleted', text: $(deletedSystems[0]).data('name'), type: 'success'});
}else{
Util.showNotify({title: systems.length + ' systems deleted', type: 'success'});
}
});
}
});
}else{
Util.showNotify({title: 'No systems selected', type: 'warning'});
}
return this;
};
/**
* delete system(s) with all their connections
* (ajax call) remove system from DB
* @param map
* @param systems
* @param callback function
*/
let deleteSystems = (map, systems = [], callback = (systems) => {}) => {
let mapContainer = $( map.getContainer() );
$.ajax({
type: 'POST',
url: Init.path.deleteSystem,
data: {
mapId: mapContainer.data('id'),
systemIds: systems.map( system => $(system).data('id') )
},
dataType: 'json',
context: {
map: map,
systems: systems
}
}).done(function(data){
// check if all systems were deleted that should get deleted
let deletedSystems = this.systems.filter(
function(system){
return this.indexOf( $(system).data('id') ) !== -1;
}, data.deletedSystemIds
);
// remove systems from map
removeSystems(this.map, deletedSystems);
callback(deletedSystems);
}).fail(function(jqXHR, status, error) {
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': deleteSystem', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
});
};
/**
* remove system(s) from map (no backend requests)
* @param map
* @param systems
*/
let removeSystems = (map, systems) => {
let removeSystemCallbak = function(deleteSystem){
map.remove(deleteSystem);
};
for (let system of systems){
system = $(system);
// check if system is "active"
if( system.hasClass(config.systemActiveClass) ){
// get parent Tab Content and fire clear modules event
let tabContentElement = MapUtil.getTabContentElementByMapElement( system );
$(tabContentElement).trigger('pf:removeSystemModules');
}
// remove endpoints and their connections
// do not fire a "connectionDetached" event
map.detachAllConnections(system, {fireEvent: false});
// hide tooltip
system.toggleSystemTooltip('destroy', {});
// remove system
system.velocity('transition.whirlOut', {
duration: Init.animationSpeed.mapDeleteSystem,
complete: removeSystemCallbak
});
}
};
/**
* calculate the x/y coordinates for a new system - relativ to a source system
* @param sourceSystem
* @param grid
* @returns {{x: *, y: *}}
*/
let calculateNewSystemPosition = function(sourceSystem){
let mapContainer = sourceSystem.parent();
let grid = [MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension];
let x = 0;
let y = 0;
let positionFinder = new Layout.Position({
container: mapContainer[0],
center: sourceSystem[0],
loops: 4,
grid: mapContainer.hasClass(MapUtil.config.mapGridClass) ? grid : false,
debug: false
});
let dimensions = positionFinder.findNonOverlappingDimensions(1, 16);
if(dimensions.length){
//... empty map space found
x = dimensions[0].left;
y = dimensions[0].top;
}else{
//... fallback
// related system is available
let currentX = sourceSystem.css('left');
let currentY = sourceSystem.css('top');
// remove "px"
currentX = parseInt( currentX.substring(0, currentX.length - 2) );
currentY = parseInt( currentY.substring(0, currentY.length - 2) );
x = currentX + config.newSystemOffset.x;
y = currentY + config.newSystemOffset.y;
}
let newPosition = {
x: x,
y: y
};
return newPosition;
};
return {
deleteSystems: deleteSystems,
removeSystems: removeSystems,
calculateNewSystemPosition: calculateNewSystemPosition
};
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,127 @@
/**
* SharedWorker config for map
*/
define([
'app/util'
], function(Util) {
'use strict';
let config = {
};
let sharedWorker = null;
let MsgWorker = null;
let characterId = null;
/**
* get WebSocket URL for SharedWorker script
* @returns {string}
*/
let getWebSocketURL = () => {
let domain = location.host;
let workerProtocol = (window.location.protocol === 'https:') ? 'wss:' : 'ws:';
return workerProtocol + '//' + domain + '/ws/map/update';
};
/**
* get SharedWorker Script path
* @returns {string}
*/
let getWorkerScript = () => {
return '/public/js/' + Util.getVersion() + '/app/worker/map.js';
};
/**
* get path to message object
* @returns {string}
*/
let getMessageWorkerObjectPath = () => {
return '/public/js/' + Util.getVersion() + '/app/worker/message.js';
};
/**
* init (connect) WebSocket within SharedWorker
*/
let initSocket = () => {
let MsgWorkerInit = new MsgWorker('ws:init');
MsgWorkerInit.data({
uri: getWebSocketURL(),
characterId: characterId,
});
sharedWorker.port.postMessage(MsgWorkerInit);
};
/**
* init (start/connect) to "SharedWorker"
* -> set worker events
*/
let init = (config) => {
// set characterId that is connected with this SharedWorker PORT
characterId = parseInt(config.characterId);
// get message Class for App <=> SharedWorker MessageEvent communication
requirejs([getMessageWorkerObjectPath()], () => {
MsgWorker = window.MsgWorker;
// start/connect to "SharedWorker"
sharedWorker = new SharedWorker( getWorkerScript(), getMessageWorkerObjectPath() );
sharedWorker.port.addEventListener('message', (e) => {
let MsgWorkerMessage = e.data;
Object.setPrototypeOf(MsgWorkerMessage, MsgWorker.prototype);
switch(MsgWorkerMessage.command){
case 'ws:open':
config.callbacks.onOpen(MsgWorkerMessage);
break;
case 'ws:send':
config.callbacks.onGet(MsgWorkerMessage);
break;
case 'ws:closed':
config.callbacks.onClosed(MsgWorkerMessage);
break;
case 'ws:error':
config.callbacks.onError(MsgWorkerMessage);
break;
}
}, false);
sharedWorker.onerror = (e) => {
// could not connect to SharedWorker script -> send error back
let MsgWorkerError = new MsgWorker('sw:error');
MsgWorkerError.meta({
reason: 'Could not connect to SharedWorker: ' + getWorkerScript()
});
config.callbacks.onError(MsgWorkerError);
};
sharedWorker.port.start();
// SharedWorker initialized
let MsgWorkerInit = new MsgWorker('sw:init');
config.callbacks.onInit(MsgWorkerInit);
// startWebSocket
initSocket();
});
};
let send = (task, data) => {
let MsgWorkerSend = new MsgWorker('ws:send');
MsgWorkerSend.task(task);
MsgWorkerSend.data(data);
sharedWorker.port.postMessage(MsgWorkerSend);
};
return {
getWebSocketURL: getWebSocketURL,
init: init,
send: send
};
});

View File

@@ -0,0 +1,408 @@
/**
* Main map application
*/
define([
'jquery',
'app/init',
'app/util',
'app/render',
'app/logging',
'app/page',
'app/map/worker',
'app/module_map',
'app/key',
'app/ui/form_element'
], ($, Init, Util, Render, Logging, Page, MapWorker, ModuleMap) => {
'use strict';
/**
* main init "map" page
*/
$(() => {
Util.initPrototypes();
// clear sessionStorage
//Util.clearSessionStorage();
// set default AJAX config
Util.ajaxSetup();
// set default dialog config
Util.initDefaultBootboxConfig();
// load page
// load info (maintenance) info panel (if scheduled)
$('body').loadPageStructure().setGlobalShortcuts();
// show app information in browser console
Util.showVersionInfo();
// init logging
Logging.init();
let mapModule = $('#' + Util.config.mapModuleId);
// map init load static data =======================================================
$.getJSON( Init.path.initMap, (initData) => {
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
});
}
}
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;
// 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();
}
}).fail(( jqXHR, status, error) => {
let reason = status + ' ' + jqXHR.status + ': ' + error;
$(document).trigger('pf:shutdown', {status: jqXHR.status, reason: reason});
});
/**
* request all map access data (tokens) -> required wor WebSocket subscription
*/
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());
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());
}
}
});
}
});
};
getMapAccessData();
/**
* main function for init all map relevant trigger calls
*/
$.fn.initMapModule = function(){
let mapModule = $(this);
// 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 ========================================================
/**
* @param forceUpdateMapData // force request to be send
*/
let triggerMapUpdatePing = (forceUpdateMapData) => {
// check each interval if map module is still available
let check = $('#' + mapModule.attr('id')).length;
if(check === 0){
// program crash stop any update
return;
}
// get updated map data
let updatedMapData = {
mapData: mapModule.getMapModuleDataForUpdate(),
getUserData: ( Util.getCurrentUserData() ) ? 0 : 1
};
// check if mapUpdate trigger should be send
// -> if "syncType" === "ajax" -> send always
// -> if "syncType" === "webSocket" -> send initial AND on map changes
if(
forceUpdateMapData ||
Util.getSyncType() === 'ajax' ||
(
Util.getSyncType() === 'webSocket' &&
updatedMapData.mapData.length
)
){
// start log
Util.timeStart(logKeyServerMapData);
// store updatedMapData
$.ajax({
type: 'POST',
url: Init.path.updateMapData,
data: updatedMapData,
dataType: 'json'
}).done((data) => {
// log request time
let duration = Util.timeStop(logKeyServerMapData);
Util.log(logKeyServerMapData, {duration: duration, type: 'server', description: 'request map data'});
Util.setSyncStatus('ajax:get');
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)
Util.setCurrentUserData(data.userData);
}
// map data found
Util.setCurrentMapData(data.mapData);
// load/update main map module
ModuleMap.updateMapModule(mapModule).then(() => {
// 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 );
// init new trigger
initMapUpdatePing(false);
// initial start for the userUpdate trigger
// this should only be called at the first time!
if(updateTimeouts.userUpdate === 0){
// start user update trigger after map loaded
updateTimeouts.userUpdate = setTimeout(() => {
triggerUserUpdatePing();
}, 1000);
}
});
}
}).fail(handleAjaxErrorResponse);
}else{
// skip this mapUpdate trigger and init next one
initMapUpdatePing(false);
}
};
// ping for user data update =======================================================
let triggerUserUpdatePing = () => {
// IMPORTANT: Get user data for ONE map that is currently visible
// On later releases this can be easy changed to "full update" all maps for a user
//
let mapIds = [];
let activeMap = Util.getMapModule().getActiveMap();
if(activeMap){
mapIds = [ activeMap.data('id') ];
}
let updatedUserData = {
mapIds: mapIds,
systemData: Util.getCurrentSystemData(),
characterMapData: {
mapTracking: (locationToggle.is(':checked') ? 1 : 0) // location tracking
}
};
Util.timeStart(logKeyServerUserData);
$.ajax({
type: 'POST',
url: Init.path.updateUserData,
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){
// 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);
}
}
}).fail(handleAjaxErrorResponse);
};
/**
* clear both main update timeouts
* -> stop program from working -> shutdown
*/
let clearUpdateTimeouts = () => {
for(let intervalKey in updateTimeouts) {
if(updateTimeouts.hasOwnProperty(intervalKey)){
clearTimeout( updateTimeouts[intervalKey] );
}
}
};
// initial start of the map update function
triggerMapUpdatePing(true);
// Send map update request on tab close/reload, in order to save map changes that
// haven´t been saved through default update trigger
window.addEventListener('beforeunload', function(e) {
// save unsaved map changes ...
triggerMapUpdatePing();
// check if character should be switched on reload or current character should be loaded afterwards
let characterSwitch = Boolean( $('body').data('characterSwitch') );
if(!characterSwitch){
let characterId = Util.getCurrentCharacterId();
if(characterId){
Util.setCookie('old_char_id', characterId, 3, 's');
}
}
// IMPORTANT, return false in order to not "abort" ajax request in background!
return false;
});
};
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,190 @@
define([
'jquery',
'app/init',
'pnotify',
//'pnotify.buttons',
//'pnotify.confirm',
'pnotify.nonblock',
'pnotify.desktop',
//'pnotify.history',
'pnotify.callbacks'
], function($, Init, PNotify) {
'use strict';
let config = {
title: '',
text: '',
type: '', // 'info', 'success', error, 'warning'
icon: false,
styling: 'fontawesome', // 'fontawesome', 'bootstrap3', 'jqueryui'
animate_speed: 'fast', // animation speed for notifications moving up/down
hide: true, // close after few seconds
delay: 5000, // visible time for notification in browser
mouse_reset: true, // Reset the hide timer if the mouse moves over the notice.
shadow: true,
addclass: 'stack-bottomright', // class for display, must changed on stack different stacks
width: '250px',
// nonblock extension parameter (click through notifications)
nonblock: {
nonblock: true, // change for enable
nonblock_opacity: 0.9
},
// desktop extension "Web Notifications"
desktop: {
desktop: false, // change for enable
icon: Init.path.img + 'notifications/logo.png' // default image for desktop notifications
}
};
// initial page title (cached)
let initialPageTitle = document.title;
// global blink timeout cache
let blinkTimer;
// stack container for all notifications
let stack = {
bottomRight: {
stack: {
dir1: 'up',
dir2: 'left',
firstpos1: 30,
firstpos2: 10,
spacing1: 5,
spacing2: 5,
push: 'bottom'
},
addclass: 'stack-bottomright',
width: '250px',
},
barBottom: {
stack: {
dir1: 'up',
dir2: 'right',
// context: $('body'),
spacing1: 0,
spacing2: 0
},
addclass: 'stack-bar-bottom',
width: '70%',
}
};
/**
* show a notification in browser and/or "Web Notifications" in OS
* @param customConfig
* @param settings
*/
let showNotify = function(customConfig, settings){
customConfig = $.extend(true, {}, config, customConfig );
// desktop notification
if(
settings &&
settings.desktop === true
){
// ask for Web Notifications permission
PNotify.desktop.permission();
customConfig.delay = 10000;
customConfig.desktop.desktop = true;
// make browser tab blink
startTabBlink(customConfig.title);
}
// set notification stack
if(
settings &&
settings.stack
){
customConfig.stack = stack[settings.stack].stack;
customConfig.addclass = stack[settings.stack].addclass;
customConfig.width = stack[settings.stack].width;
}else{
customConfig.stack = stack.bottomRight.stack;
customConfig.addclass = stack.bottomRight.addclass;
}
switch(customConfig.type){
case 'info':
customConfig.icon = 'fa fa-info fa-fw fa-lg';
break;
case 'success':
customConfig.icon = 'fa fa-check fa-fw fa-lg';
break;
case 'warning':
customConfig.icon = 'fa fa-exclamation-triangle fa-fw fa-lg';
break;
case 'error':
customConfig.icon = 'fa fa-close fa-fw fa-lg';
break;
case 'lock':
customConfig.icon = 'fa fa-lock fa-fw fa-lg';
customConfig.type = 'success';
break;
case 'unlock':
customConfig.icon = 'fa fa-unlock fa-fw fa-lg';
customConfig.type = 'info';
break;
default:
customConfig.icon = false;
}
new PNotify(customConfig);
};
/**
* change document.title and make the browsers tab blink
* @param blinkTitle
*/
let startTabBlink = function(blinkTitle){
let initBlink = (function(blinkTitle){
// count blinks if tab is currently active
let activeTabBlinkCount = 0;
let blink = function(){
// number of "blinks" should be limited if tab is currently active
if(window.isVisible){
activeTabBlinkCount++;
}
// toggle page title
document.title = (document.title === blinkTitle) ? initialPageTitle : blinkTitle;
if(activeTabBlinkCount > 10){
stopTabBlink();
}
};
return function () {
if (!blinkTimer) {
blinkTimer = setInterval(blink, 1000);
}
};
}( blinkTitle ));
initBlink();
};
/**
* stop blinking document.title
*/
let stopTabBlink = function(){
if(blinkTimer){
clearInterval(blinkTimer);
document.title = initialPageTitle;
blinkTimer = null;
}
};
return {
showNotify: showNotify,
startTabBlink: startTabBlink,
stopTabBlink: stopTabBlink
};
});

1228
public/js/v1.3.2/app/page.js Normal file

File diff suppressed because it is too large Load Diff

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