diff --git a/app/main/model/pathfinder/characterlogmodel.php b/app/main/model/pathfinder/characterlogmodel.php index 2d8605a3..8796ae93 100644 --- a/app/main/model/pathfinder/characterlogmodel.php +++ b/app/main/model/pathfinder/characterlogmodel.php @@ -272,7 +272,7 @@ class CharacterLogModel extends AbstractPathfinderModel { */ protected function deleteLogsHistory(){ if(is_object($this->characterId)){ - $this->characterId->clearCacheDataWithPrefix(CharacterModel::DATA_CACHE_KEY_HISTORY_LOGS); + $this->characterId->clearCacheDataWithPrefix(CharacterModel::DATA_CACHE_KEY_LOG_HISTORY); } } diff --git a/app/main/model/pathfinder/charactermodel.php b/app/main/model/pathfinder/charactermodel.php index ce87da76..768cf71c 100644 --- a/app/main/model/pathfinder/charactermodel.php +++ b/app/main/model/pathfinder/charactermodel.php @@ -34,17 +34,17 @@ class CharacterModel extends AbstractPathfinderModel { /** * max count of historic character logs */ - const MAX_HISTORY_LOGS_DATA = 3; + const MAX_LOG_HISTORY_DATA = 5; /** * TTL for historic character logs */ - const TTL_HISTORY_LOGS = 60 * 60 * 22; + const TTL_LOG_HISTORY = 60 * 60 * 22; /** * cache key prefix historic character logs */ - const DATA_CACHE_KEY_HISTORY_LOGS = 'HISTORY_LOG'; + const DATA_CACHE_KEY_LOG_HISTORY = 'LOG_HISTORY'; /** * character authorization status @@ -201,56 +201,60 @@ class CharacterModel extends AbstractPathfinderModel { /** * get character data - * @param bool|false $addCharacterLogData - * @return null|object|\stdClass + * @param bool $addLogData + * @param bool $addLogHistoryData + * @return mixed|object|null * @throws \Exception */ - public function getData($addCharacterLogData = false){ - $cacheKeyModifier = ''; - - // check if there is cached data - // -> IMPORTANT: $addCharacterLogData is optional! -> therefore we need 2 cache keys! - if($addCharacterLogData){ - $cacheKeyModifier = self::DATA_CACHE_KEY_LOG; - } - $characterData = $this->getCacheData($cacheKeyModifier); - - if(is_null($characterData)){ + public function getData($addLogData = false, $addLogHistoryData = false){ + // check for cached data + if(is_null($characterData = $this->getCacheData())){ // no cached character data found - $characterData = (object) []; - $characterData->id = $this->_id; - $characterData->name = $this->name; - $characterData->role = $this->roleId->getData(); - $characterData->shared = $this->shared; - $characterData->logLocation = $this->logLocation; - $characterData->selectLocation = $this->selectLocation; - - if($addCharacterLogData){ - if($logModel = $this->getLog()){ - $characterData->log = $logModel->getData(); - } - } + $characterData = (object) []; + $characterData->id = $this->_id; + $characterData->name = $this->name; + $characterData->role = $this->roleId->getData(); + $characterData->shared = $this->shared; + $characterData->logLocation = $this->logLocation; + $characterData->selectLocation = $this->selectLocation; // check for corporation if($corporation = $this->getCorporation()){ - $characterData->corporation = $corporation->getData(); + $characterData->corporation = $corporation->getData(); } // check for alliance if($alliance = $this->getAlliance()){ - $characterData->alliance = $alliance->getData(); + $characterData->alliance = $alliance->getData(); } // max caching time for a system - // the cached date has to be cleared manually on any change - // this includes system, connection,... changes (all dependencies) - $this->updateCacheData($characterData, $cacheKeyModifier); + // cached date has to be cleared manually on any change + // this applies to system, connection,... changes (+ all other dependencies) + $this->updateCacheData($characterData); + } + + if($addLogData){ + if(is_null($logData = $this->getCacheData(self::DATA_CACHE_KEY_LOG))){ + if($logModel = $this->getLog()){ + $logData = $logModel->getData(); + $this->updateCacheData($logData, self::DATA_CACHE_KEY_LOG); + } + } + + if($logData){ + $characterData->log = $logData; + } + } + + if($addLogHistoryData && $characterData->log){ + $characterData->logHistory = $this->getLogsHistory(); } // temp "authStatus" should not be cached if($this->authStatus){ - $characterData->authStatus = $this->authStatus; + $characterData->authStatus = $this->authStatus; } return $characterData; @@ -1041,7 +1045,7 @@ class CharacterModel extends AbstractPathfinderModel { * @return array */ public function getLogsHistory() : array { - if(!is_array($logHistoryData = $this->getCacheData(self::DATA_CACHE_KEY_HISTORY_LOGS))){ + if(!is_array($logHistoryData = $this->getCacheData(self::DATA_CACHE_KEY_LOG_HISTORY))){ $logHistoryData = []; } return $logHistoryData; @@ -1067,9 +1071,9 @@ class CharacterModel extends AbstractPathfinderModel { array_unshift($logHistoryData, $historyEntry); // limit max history data - array_splice($logHistoryData, self::MAX_HISTORY_LOGS_DATA); + array_splice($logHistoryData, self::MAX_LOG_HISTORY_DATA); - $this->updateCacheData($logHistoryData, self::DATA_CACHE_KEY_HISTORY_LOGS, self::TTL_HISTORY_LOGS); + $this->updateCacheData($logHistoryData, self::DATA_CACHE_KEY_LOG_HISTORY, self::TTL_LOG_HISTORY); } } diff --git a/app/main/model/pathfinder/systemmodel.php b/app/main/model/pathfinder/systemmodel.php index 704172c8..df04a2e0 100644 --- a/app/main/model/pathfinder/systemmodel.php +++ b/app/main/model/pathfinder/systemmodel.php @@ -27,17 +27,17 @@ class SystemModel extends AbstractMapTrackingModel { /** * max count of history signature data in cache */ - const MAX_HISTORY_SIGNATURES_DATA = 10; + const MAX_SIGNATURES_HISTORY_DATA = 10; /** * TTL for history signature data */ - const TTL_HISTORY_SIGNATURES = 7200; + const TTL_SIGNATURES_HISTORY = 7200; /** * cache key prefix for getData(); result WITH log data */ - const DATA_CACHE_KEY_HISTORY_SIGNATURES = 'HISTORY_SIGNATURES'; + const DATA_CACHE_KEY_SIGNATURES_HISTORY = 'HISTORY_SIGNATURES'; /** * @var string @@ -805,7 +805,7 @@ class SystemModel extends AbstractMapTrackingModel { * @return array */ public function getSignaturesHistory() : array { - if(!is_array($signaturesHistoryData = $this->getCacheData(self::DATA_CACHE_KEY_HISTORY_SIGNATURES))){ + if(!is_array($signaturesHistoryData = $this->getCacheData(self::DATA_CACHE_KEY_SIGNATURES_HISTORY))){ $signaturesHistoryData = []; } return $signaturesHistoryData; @@ -832,9 +832,9 @@ class SystemModel extends AbstractMapTrackingModel { array_unshift($signaturesHistoryData, $historyEntry); // limit max history data - array_splice($signaturesHistoryData, self::MAX_HISTORY_SIGNATURES_DATA); + array_splice($signaturesHistoryData, self::MAX_SIGNATURES_HISTORY_DATA); - $this->updateCacheData($signaturesHistoryData, self::DATA_CACHE_KEY_HISTORY_SIGNATURES, self::TTL_HISTORY_SIGNATURES); + $this->updateCacheData($signaturesHistoryData, self::DATA_CACHE_KEY_SIGNATURES_HISTORY, self::TTL_SIGNATURES_HISTORY); // clear system cache here // -> Signature model updates should NOT update the system cache on change diff --git a/app/main/model/pathfinder/usermodel.php b/app/main/model/pathfinder/usermodel.php index 81c125e8..382fc87d 100644 --- a/app/main/model/pathfinder/usermodel.php +++ b/app/main/model/pathfinder/usermodel.php @@ -57,7 +57,7 @@ class UserModel extends AbstractPathfinderModel { * @return \stdClass * @throws Exception */ - public function getData(){ + public function getData() : \stdClass { // get public user data for this user $userData = $this->getSimpleData(); @@ -77,7 +77,7 @@ class UserModel extends AbstractPathfinderModel { // get active character with log data $activeCharacter = $this->getActiveCharacter(); - $userData->character = $activeCharacter->getData(true); + $userData->character = $activeCharacter->getData(true, true); return $userData; } @@ -87,7 +87,7 @@ class UserModel extends AbstractPathfinderModel { * - check out getData() for all user data * @return \stdClass */ - public function getSimpleData(){ + public function getSimpleData() : \stdClass{ $userData = (object) []; $userData->id = $this->id; $userData->name = $this->name; @@ -143,7 +143,7 @@ class UserModel extends AbstractPathfinderModel { * checks whether user has a valid email address and pathfinder has a valid SMTP config * @return bool */ - protected function isMailSendEnabled() : bool{ + protected function isMailSendEnabled() : bool { return Config::isValidSMTPConfig($this->getSMTPConfig()); } @@ -151,7 +151,7 @@ class UserModel extends AbstractPathfinderModel { * get SMTP config for this user * @return \stdClass */ - protected function getSMTPConfig() : \stdClass{ + protected function getSMTPConfig() : \stdClass { $config = Config::getSMTPConfig(); $config->to = $this->email; return $config; @@ -164,7 +164,7 @@ class UserModel extends AbstractPathfinderModel { * @return bool * @throws Exception\ValidationException */ - protected function validate_name(string $key, string $val): bool { + protected function validate_name(string $key, string $val) : bool { $valid = true; if( mb_strlen($val) < 3 || @@ -183,7 +183,7 @@ class UserModel extends AbstractPathfinderModel { * @return bool * @throws Exception\ValidationException */ - protected function validate_email(string $key, string $val): bool { + protected function validate_email(string $key, string $val) : bool { $valid = true; if ( !empty($val) && \Audit::instance()->email($val) == false ){ $valid = false; @@ -196,14 +196,14 @@ class UserModel extends AbstractPathfinderModel { * check whether this character has already a user assigned to it * @return bool */ - public function hasUserCharacters(){ + public function hasUserCharacters() : bool { $this->filter('userCharacters', ['active = ?', 1]); return is_object($this->userCharacters); } /** * get current character data from session - * -> if §characterID == 0 -> get first character data (random) + * -> if $characterId == 0 -> get first character data (random) * @param int $characterId * @param bool $objectCheck * @return array @@ -254,7 +254,7 @@ class UserModel extends AbstractPathfinderModel { * @param int $characterId * @return array */ - public function findSessionCharacterData(int $characterId): array { + public function findSessionCharacterData(int $characterId) : array { $data = []; if($characterId){ $sessionCharacters = (array)$this->getF3()->get(User::SESSION_KEY_CHARACTERS); @@ -292,7 +292,7 @@ class UserModel extends AbstractPathfinderModel { * @return null|CharacterModel * @throws Exception */ - public function getActiveCharacter(){ + public function getActiveCharacter() : ?CharacterModel { $activeCharacter = null; $controller = new Controller\Controller(); $currentActiveCharacter = $controller->getCharacter(); @@ -316,7 +316,7 @@ class UserModel extends AbstractPathfinderModel { * get all characters for this user * @return CharacterModel[] */ - public function getCharacters(){ + public function getCharacters() : array { $characters = []; $userCharacters = $this->getUserCharacters(); diff --git a/app/pathfinder.ini b/app/pathfinder.ini index aafe85a8..0d7b7416 100644 --- a/app/pathfinder.ini +++ b/app/pathfinder.ini @@ -255,14 +255,14 @@ DELAY = 5000 ; Requests that exceed the limit are logged as 'warning'. ; Syntax: Integer (milliseconds) ; Default: 200 -EXECUTION_LIMIT = 200 +EXECUTION_LIMIT = 500 [PATHFINDER.TIMER.UPDATE_CLIENT_MAP] ; Execution limit for client side (javascript) map data updates ; Map data updates that exceed the limit are logged as 'warning'. ; Syntax: Integer (milliseconds) ; Default: 50 -EXECUTION_LIMIT = 50 +EXECUTION_LIMIT = 100 [PATHFINDER.TIMER.UPDATE_SERVER_USER_DATA] ; User data update interval (ajax long polling) @@ -275,7 +275,7 @@ DELAY = 5000 ; Requests that exceed the limit are logged as 'warning'. ; Syntax: Integer (milliseconds) ; Default: 500 -EXECUTION_LIMIT = 500 +EXECUTION_LIMIT = 1000 ; update client user data (milliseconds) [PATHFINDER.TIMER.UPDATE_CLIENT_USER_DATA] @@ -283,7 +283,7 @@ EXECUTION_LIMIT = 500 ; User data updates that exceed the limit are logged as 'warning'. ; Syntax: Integer (milliseconds) ; Default: 50 -EXECUTION_LIMIT = 50 +EXECUTION_LIMIT = 100 ; CACHE =========================================================================================== [PATHFINDER.CACHE] diff --git a/js/app.js b/js/app.js index 8bd15050..1cf7c563 100644 --- a/js/app.js +++ b/js/app.js @@ -9,14 +9,16 @@ var jsBaseUrl = document.body.getAttribute('data-js-path'); // requireJs configuration requirejs.config({ - baseUrl: 'js', // path for baseUrl - dynamically set !below! ("build_js" | "js") + baseUrl: 'js', // src root path - 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 + conf: 'app/conf', // path config files + dialog: 'app/ui/dialog', // path dialog files + layout: 'app/ui/layout', // path layout files + module: 'app/ui/module', // path module files + + templates: '../../templates', // path template base dir + img: '../../img', // path image base dir // main views login: './app/login', // initial start "login page" view @@ -45,7 +47,6 @@ requirejs.config({ 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.min', // v1.9.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 @@ -152,9 +153,6 @@ requirejs.config({ hoverIntent: { deps: ['jquery'] }, - fullScreen: { - deps: ['jquery'] - }, select2: { deps: ['jquery', 'mousewheel'], exports: 'Select2' diff --git a/js/app/init.js b/js/app/init.js index b8ff26bc..19fc24a9 100644 --- a/js/app/init.js +++ b/js/app/init.js @@ -2,7 +2,7 @@ * Init */ -define(['jquery'], ($) => { +define([], () => { 'use strict'; diff --git a/js/app/login.js b/js/app/login.js index c544f308..3f4af714 100644 --- a/js/app/login.js +++ b/js/app/login.js @@ -10,9 +10,9 @@ define([ 'blueImpGallery', 'bootbox', 'lazyload', - 'app/ui/header', - 'app/ui/logo', - 'app/ui/demo_map', + 'layout/header_login', + 'layout/logo', + 'layout/demo_map', 'dialog/account_settings', 'dialog/notification', 'dialog/manual', diff --git a/js/app/map/layout.js b/js/app/map/layout.js index a40f95e5..fcf1f051 100644 --- a/js/app/map/layout.js +++ b/js/app/map/layout.js @@ -38,7 +38,7 @@ define(() => { * @returns {*} * @private */ - this._getElementDimension = (element) => { + this._getElementDimension = element => { let dim = null; let left = 0; diff --git a/js/app/map/map.js b/js/app/map/map.js index 8bb1c91e..7c16fff0 100644 --- a/js/app/map/map.js +++ b/js/app/map/map.js @@ -169,13 +169,13 @@ define([ /** * updates a system with current information * @param map + * @param system * @param data * @param currentUserIsHere boolean - if the current user is in this system * @param options */ - $.fn.updateSystemUserData = function(map, data, currentUserIsHere, options){ - let system = $(this); - let systemId = system.attr('id'); + let updateSystemUserData = (map, system, data, currentUserIsHere = false, options = {}) => { + let systemIdAttr = system.attr('id'); let compactView = Util.getObjVal(options, 'compactView'); // find countElement -> minimizedUI @@ -187,16 +187,21 @@ define([ // find expand arrow let systemHeadExpand = system.find('.' + config.systemHeadExpandClass); - let oldCacheKey = system.data('userCache'); - let oldUserCount = system.data('userCount'); - oldUserCount = (oldUserCount !== undefined ? oldUserCount : 0); + let oldCacheKey = system.data('userCacheKey'); + let oldUserCount = system.data('userCount') || 0; + let userWasHere = Boolean(system.data('currentUser')); let userCounter = 0; - system.data('currentUser', false); + system.data('currentUser', currentUserIsHere); - // if current user is in THIS system trigger event - if(currentUserIsHere){ - system.data('currentUser', true); + // auto select system if current user is in THIS system + if( + currentUserIsHere && + !userWasHere && + Boolean(Util.getObjVal(Init, 'character.autoLocationSelect')) && + Boolean(Util.getObjVal(Util.getCurrentUserData(), 'character.selectLocation')) + ){ + Util.triggerMenuAction(map.getContainer(), 'SelectSystem', {systemId: system.data('id'), forceSelect: false}); } // add user information @@ -204,24 +209,28 @@ define([ data && data.user ){ + userCounter = data.user.length; + + // loop all active pilots and build cache-key let cacheArray = []; + for(let tempUserData of data.user){ + cacheArray.push(tempUserData.id + '_' + tempUserData.log.ship.id); + } + + // make sure cacheArray values are sorted for key comparison + let collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'}); + cacheArray.sort(collator.compare); // we need to add "view mode" option to key // -> if view mode change detected -> key no longer valid - cacheArray.push(compactView ? 'compact' : 'default'); + cacheArray.unshift(compactView ? 'compact' : 'default'); - // loop all active pilots and build cache-key - for(let i = 0; i < data.user.length; i++){ - userCounter++; - let tempUserData = data.user[i]; - cacheArray.push(tempUserData.id + '_' + tempUserData.log.ship.id); - } - let cacheKey = cacheArray.join('_'); + let cacheKey = cacheArray.join('_').hashCode(); // check for if cacheKey has changed if(cacheKey !== oldCacheKey){ // set new CacheKey - system.data('userCache', cacheKey); + system.data('userCacheKey', cacheKey); system.data('userCount', userCounter); // remove all content @@ -235,7 +244,7 @@ define([ systemHeadExpand.hide(); system.toggleBody(false, map, {}); - map.revalidate(systemId); + map.revalidate(systemIdAttr); }else{ systemCount.empty(); @@ -277,7 +286,7 @@ define([ } let tooltipOptions = { - systemId: systemId, + systemId: systemIdAttr, highlight: highlight, userCount: userCounter }; @@ -290,14 +299,14 @@ define([ complete: function(system){ // show active user tooltip system.toggleSystemTooltip('show', tooltipOptions); - map.revalidate( systemId ); + map.revalidate(systemIdAttr); } }); } } }else{ // no user data found for this system - system.data('userCache', false); + system.data('userCacheKey', false); system.data('userCount', 0); systemBody.empty(); @@ -311,7 +320,7 @@ define([ systemHeadExpand.hide(); system.toggleBody(false, map, {}); - map.revalidate(systemId); + map.revalidate(systemIdAttr); } } }; @@ -716,11 +725,11 @@ define([ break; case 'map_edit': // open map edit dialog tab - $(document).triggerMenuEvent('ShowMapSettings', {tab: 'edit'}); + Util.triggerMenuAction(document, 'ShowMapSettings', {tab: 'edit'}); break; case 'map_info': // open map info dialog tab - $(document).triggerMenuEvent('ShowMapInfo', {tab: 'information'}); + Util.triggerMenuAction(document, 'ShowMapInfo', {tab: 'information'}); break; } }; @@ -2427,7 +2436,7 @@ define([ } }, onShow: function(){ - $(document).trigger('pf:closeMenu', [{}]); + Util.triggerMenuAction(document, 'Close'); }, onRefresh: function(){ } @@ -2574,90 +2583,89 @@ define([ // catch events =============================================================================================== - // toggle global map option (e.g. "grid snap", "magnetization") - mapContainer.on('pf:menuMapOption', function(e, mapOption){ - let mapElement = $(this); - + /** + * update/toggle global map option (e.g. "grid snap", "magnetization") + * @param mapContainer + * @param data + */ + let updateMapOption = (mapContainer, data) => { // get map menu config options - let data = mapOptions[mapOption.option]; + let mapOption = mapOptions[data.option]; - let promiseStore = MapUtil.getLocaleData('map', mapElement.data('id')); + let promiseStore = MapUtil.getLocaleData('map', mapContainer.data('id')); promiseStore.then(function(dataStore){ let notificationText = 'disabled'; - let button = $('#' + this.data.buttonId); + let button = $('#' + this.mapOption.buttonId); let dataExists = false; if( dataStore && - dataStore[this.mapOption.option] + dataStore[this.data.option] ){ dataExists = true; } - if(dataExists === this.mapOption.toggle){ + if(dataExists === this.data.toggle){ // toggle button class button.removeClass('active'); // toggle map class (e.g. for grid) - if(this.data.class){ - this.mapElement.removeClass(MapUtil.config[this.data.class]); + if(this.mapOption.class){ + this.mapContainer.removeClass(MapUtil.config[this.mapOption.class]); } - // call optional jQuery extension on mapElement - if(this.data.onDisable && !this.mapOption.skipOnDisable){ - this.data.onDisable(this.mapElement); + // call optional jQuery extension on mapContainer + if(this.mapOption.onDisable && !this.data.skipOnDisable){ + this.mapOption.onDisable(this.mapContainer); } // show map overlay info icon - MapOverlayUtil.getMapOverlay(this.mapElement, 'info').updateOverlayIcon(this.mapOption.option, 'hide'); + MapOverlayUtil.getMapOverlay(this.mapContainer, 'info').updateOverlayIcon(this.data.option, 'hide'); // delete map option - MapUtil.deleteLocalData('map', this.mapElement.data('id'), this.mapOption.option); + MapUtil.deleteLocalData('map', this.mapContainer.data('id'), this.data.option); }else{ // toggle button class button.addClass('active'); // toggle map class (e.g. for grid) - if(this.data.class){ - this.mapElement.addClass(MapUtil.config[this.data.class]); + if(this.mapOption.class){ + this.mapContainer.addClass(MapUtil.config[this.mapOption.class]); } - // call optional jQuery extension on mapElement - if(this.data.onEnable && !this.mapOption.skipOnEnable){ - this.data.onEnable(this.mapElement); + // call optional jQuery extension on mapContainer + if(this.mapOption.onEnable && !this.data.skipOnEnable){ + this.mapOption.onEnable(this.mapContainer); } // hide map overlay info icon - MapOverlayUtil.getMapOverlay(this.mapElement, 'info').updateOverlayIcon(this.mapOption.option, 'show'); + MapOverlayUtil.getMapOverlay(this.mapContainer, 'info').updateOverlayIcon(this.data.option, 'show'); // store map option - MapUtil.storeLocalData('map', this.mapElement.data('id'), this.mapOption.option, 1); + MapUtil.storeLocalData('map', this.mapContainer.data('id'), this.data.option, 1); notificationText = 'enabled'; } - if(this.mapOption.toggle){ - Util.showNotify({title: this.data.description, text: notificationText, type: 'info'}); + if(this.data.toggle){ + Util.showNotify({title: this.mapOption.description, text: notificationText, type: 'info'}); } }.bind({ - mapOption: mapOption, data: data, - mapElement: mapElement + mapOption: mapOption, + mapContainer: mapContainer })); - }); + }; - // delete system event - // triggered from "map info" dialog scope - mapContainer.on('pf:deleteSystems', function(e, data){ - System.deleteSystems(map, data.systems, data.callback); - }); - - // triggered from "header" link (if user is active in one of the systems) - mapContainer.on('pf:menuSelectSystem', function(e, data){ - let mapElement = $(this); - let systemId = MapUtil.getSystemId(mapElement.data('id'), data.systemId); - let system = mapElement.find('#' + systemId); + /** + * select system event + * @param mapContainer + * @param data + */ + let selectSystem = (mapContainer, data) => { + let systemId = MapUtil.getSystemId(mapContainer.data('id'), data.systemId); + let system = mapContainer.find('#' + systemId); if(system.length === 1){ // system found on map ... @@ -2674,12 +2682,45 @@ define([ } if(select){ - let mapWrapper = mapElement.closest('.' + config.mapWrapperClass); + let mapWrapper = mapContainer.closest('.' + config.mapWrapperClass); Scrollbar.scrollToSystem(mapWrapper, MapUtil.getSystemPosition(system)); // select system MapUtil.showSystemInfo(map, system); } } + }; + + mapContainer.on('pf:menuAction', (e, action, data) => { + // menuAction events can also be triggered on child nodes + // -> if event is not handled there it bubbles up + // make sure event can be handled by this element + if(e.target === e.currentTarget){ + e.stopPropagation(); + + switch(action){ + case 'MapOption': + // toggle global map option (e.g. "grid snap", "magnetization") + updateMapOption(mapContainer, data); + break; + case 'SelectSystem': + // select system on map (e.g. from modal links) + selectSystem(mapContainer, data); + break; + case 'AddSystem': + System.showNewSystemDialog(map, data, saveSystemCallback); + break; + default: + console.warn('Unknown menuAction %o event name', action); + } + }else{ + console.warn('Unhandled menuAction %o event name. Handled menu events should not bobble up', action); + } + }); + + // delete system event + // triggered from "map info" dialog scope + mapContainer.on('pf:deleteSystems', function(e, data){ + System.deleteSystems(map, data.systems, data.callback); }); // triggered when map lock timer (interval) was cleared @@ -2807,36 +2848,21 @@ define([ let compactView = mapElement.hasClass(MapUtil.config.mapCompactClass); // get current character log data - let characterLogExists = false; - let currentCharacterLog = Util.getCurrentCharacterLog(); + let characterLogSystemId = Util.getObjVal(Util.getCurrentCharacterLog(), 'system.id') || 0; // data for header update let headerUpdateData = { mapId: userData.config.id, userCountInside: 0, // active user on a map userCountOutside: 0, // active user NOT on map - userCountInactive: 0, // inactive users (no location) - currentLocation: { - id: 0, // systemId for current active user - name: false // systemName for current active user - } + userCountInactive: 0 }; - if( - currentCharacterLog && - currentCharacterLog.system - ){ - characterLogExists = true; - headerUpdateData.currentLocation.name = currentCharacterLog.system.name; - } - // check if current user was found on the map let currentUserOnMap = false; // get all systems - let systems = mapElement.find('.' + config.systemClass); - - for(let system of systems){ + for(let system of mapElement.find('.' + config.systemClass)){ system = $(system); let systemId = system.data('systemId'); let tempUserData = null; @@ -2864,20 +2890,15 @@ define([ // the current user can only be in a single system ------------------------------------------------ if( - characterLogExists && - currentCharacterLog.system.id === systemId + !currentUserOnMap && + characterLogSystemId && + characterLogSystemId === systemId ){ - if( !currentUserOnMap ){ - currentUserIsHere = true; - currentUserOnMap = true; - - // set current location data for header update - headerUpdateData.currentLocation.id = system.data('id'); - headerUpdateData.currentLocation.name = currentCharacterLog.system.name; - } + currentUserIsHere = true; + currentUserOnMap = true; } - system.updateSystemUserData(map, tempUserData, currentUserIsHere, {compactView: compactView}); + updateSystemUserData(map, system, tempUserData, currentUserIsHere, {compactView: compactView}); } // users who are not in any map system ---------------------------------------------------------------- diff --git a/js/app/map/system.js b/js/app/map/system.js index c1b901af..0615b7ff 100644 --- a/js/app/map/system.js +++ b/js/app/map/system.js @@ -179,16 +179,22 @@ define([ statusData: statusData }; - // set current position as "default" system to add ------------------------------------------------------------ - let currentCharacterLog = Util.getCurrentCharacterLog(); + // check for pre-selected system ------------------------------------------------------------------------------ + let systemData; + if(options.systemData){ + systemData = options.systemData; + }else{ + // ... check for current active system (characterLog) ----------------------------------------------------- + let currentCharacterLog = Util.getCurrentCharacterLog(); + if(currentCharacterLog !== false){ + // set system from 'characterLog' data as pre-selected system + systemData = Util.getObjVal(currentCharacterLog, 'system'); + } + } - if( - currentCharacterLog !== false && - mapSystemIds.indexOf( currentCharacterLog.system.id ) === -1 - ){ - // current system is NOT already on this map - // set current position as "default" system to add - data.currentSystem = currentCharacterLog.system; + // check if pre-selected system is NOT already on this map + if(mapSystemIds.indexOf(Util.getObjVal(systemData, 'id')) === -1){ + data.currentSystem = systemData; } requirejs(['text!templates/dialog/system.html', 'mustache'], (template, Mustache) => { @@ -235,7 +241,7 @@ define([ // get new position newPosition = calculateNewSystemPosition(sourceSystem); - }else{ + }else if(options.position){ // check mouse cursor position (add system to map) newPosition = { x: options.position.x, @@ -638,7 +644,7 @@ define([ * @param system * @returns {string} */ - let getSystemTooltipPlacement = (system) => { + let getSystemTooltipPlacement = system => { let offsetParent = system.parent().offset(); let offsetSystem = system.offset(); @@ -652,7 +658,7 @@ define([ * @param systems * @param callback function */ - let deleteSystems = (map, systems = [], callback = (systems) => {}) => { + let deleteSystems = (map, systems = [], callback = systems => {}) => { let mapContainer = $( map.getContainer() ); let systemIds = systems.map(system => $(system).data('id')); @@ -726,7 +732,6 @@ define([ let calculateNewSystemPosition = sourceSystem => { let mapContainer = sourceSystem.parent(); let grid = [MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension]; - let x = 0; let y = 0; @@ -756,12 +761,7 @@ define([ y = currentY + config.newSystemOffset.y; } - let newPosition = { - x: x, - y: y - }; - - return newPosition; + return {x: x, y: y}; }; /** @@ -770,7 +770,7 @@ define([ * @param data * @returns {*} */ - let getHeadInfoElement = (data) => { + let getHeadInfoElement = data => { let headInfo = null; let headInfoLeft = []; let headInfoRight = []; diff --git a/js/app/map/util.js b/js/app/map/util.js index e32af826..0b182693 100644 --- a/js/app/map/util.js +++ b/js/app/map/util.js @@ -187,25 +187,28 @@ define([ }; /** - * get system data by mapId and systemid - * @param mapId - * @param systemId - * @returns {boolean} + * get system data from mapData + * @see getSystemData + * @param mapData + * @param value + * @param key + * @returns {any} */ - let getSystemData = (mapId, systemId) => { - let systemData = false; - let mapData = Util.getCurrentMapData(mapId); + let getSystemDataFromMapData = (mapData, value, key = 'id') => { + return mapData ? mapData.data.systems.find(system => system[key] === value) || false : false; + }; - if(mapData){ - for(let j = 0; j < mapData.data.systems.length; j++){ - let systemDataTemp = mapData.data.systems[j]; - if(systemDataTemp.id === systemId){ - systemData = systemDataTemp; - break; - } - } - } - return systemData; + /** + * get system data by mapId system data selector + * -> e.g. value = 2 and key = 'id' + * -> e.g. value = 30002187 and key = 'systemId' => looks for 'Amarr' CCP systemId + * @param mapId + * @param value + * @param key + * @returns {any} + */ + let getSystemData = (mapId, value, key = 'id') => { + return getSystemDataFromMapData(Util.getCurrentMapData(mapId), value, key); }; /** @@ -495,7 +498,7 @@ define([ connectionData && connectionData.signatures // signature data is required... ){ - let SystemSignatures = require('app/ui/module/system_signature'); + let SystemSignatures = require('module/system_signature'); let sourceEndpoint = connection.endpoints[0]; let targetEndpoint = connection.endpoints[1]; @@ -1469,25 +1472,25 @@ define([ }); // init compact system layout --------------------------------------------------------------------- - mapElement.triggerMenuEvent('MapOption', { + Util.triggerMenuAction(mapElement, 'MapOption', { option: 'mapCompact', toggle: false }); // init magnetizer -------------------------------------------------------------------------------- - mapElement.triggerMenuEvent('MapOption', { + Util.triggerMenuAction(mapElement, 'MapOption', { option: 'mapMagnetizer', toggle: false }); // init grid snap --------------------------------------------------------------------------------- - mapElement.triggerMenuEvent('MapOption', { + Util.triggerMenuAction(mapElement, 'MapOption', { option: 'mapSnapToGrid', toggle: false }); // init endpoint overlay -------------------------------------------------------------------------- - mapElement.triggerMenuEvent('MapOption', { + Util.triggerMenuAction(mapElement, 'MapOption', { option: 'mapSignatureOverlays', toggle: false, skipOnEnable: true, // skip callback -> Otherwise it would run 2 times on map create @@ -1946,7 +1949,7 @@ define([ over: function(e){ let staticWormholeElement = $(this); let wormholeName = staticWormholeElement.attr('data-name'); - let wormholeData = Util.getObjVal(Init, 'wormholes.' + wormholeName); + let wormholeData = Util.getObjVal(Init, 'wormholes.' + wormholeName); if(wormholeData){ staticWormholeElement.addWormholeInfoTooltip(wormholeData, options); } @@ -2059,6 +2062,7 @@ define([ getMapIcons: getMapIcons, getInfoForMap: getInfoForMap, getInfoForSystem: getInfoForSystem, + getSystemDataFromMapData: getSystemDataFromMapData, getSystemData: getSystemData, getSystemTypeInfo: getSystemTypeInfo, getEffectInfoForSystem: getEffectInfoForSystem, diff --git a/js/app/mappage.js b/js/app/mappage.js index 2f81d548..8c34ab2e 100644 --- a/js/app/mappage.js +++ b/js/app/mappage.js @@ -6,14 +6,13 @@ 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) => { +], ($, Init, Util, Logging, Page, MapWorker, ModuleMap) => { 'use strict'; @@ -39,7 +38,7 @@ define([ Util.initDefaultEditableConfig(); // load page - Page.loadPageStructure().setGlobalShortcuts(); + Page.loadPageStructure(); // show app information in browser console Util.showVersionInfo(); @@ -417,7 +416,7 @@ define([ data.error.length > 0 ){ // any error in the main trigger functions result in a user log-off - $(document).trigger('pf:menuLogout'); + Util.triggerMenuAction(document, 'Logout'); }else{ $(document).setProgramStatus('online'); @@ -493,13 +492,13 @@ define([ data.error.length > 0 ){ // any error in the main trigger functions result in a user log-off - $(document).trigger('pf:menuLogout'); + Util.triggerMenuAction(document, 'Logout'); }else{ $(document).setProgramStatus('online'); if(data.userData !== undefined){ // store current user data global (cache) - let userData = Util.setCurrentUserData(data.userData); + Util.setCurrentUserData(data.userData); // update system info panels if(data.system){ diff --git a/js/app/module_map.js b/js/app/module_map.js index eab6ee8c..2863bf97 100644 --- a/js/app/module_map.js +++ b/js/app/module_map.js @@ -5,13 +5,13 @@ define([ 'app/map/map', 'app/map/util', 'sortable', - 'app/ui/module/system_info', - 'app/ui/module/system_graph', - 'app/ui/module/system_signature', - 'app/ui/module/system_route', - 'app/ui/module/system_intel', - 'app/ui/module/system_killboard', - 'app/ui/module/connection_info', + 'module/system_info', + 'module/system_graph', + 'module/system_signature', + 'module/system_route', + 'module/system_intel', + 'module/system_killboard', + 'module/connection_info', 'app/counter' ], ( $, @@ -746,7 +746,7 @@ define([ MapUtil.storeLocaleCharacterData('defaultMapId', mapId); }else{ // add new Tab selected - $(document).trigger('pf:menuShowMapSettings', {tab: 'new'}); + Util.triggerMenuAction(document, 'ShowMapSettings', {tab: 'new'}); e.preventDefault(); } }); @@ -1215,7 +1215,7 @@ define([ clearMapModule(mapModule) .then(payload => { // no map data available -> show "new map" dialog - $(document).trigger('pf:menuShowMapSettings', {tab: 'new'}); + Util.triggerMenuAction(document, 'ShowMapSettings', {tab: 'new'}); }) .then(payload => resolve()); }else{ diff --git a/js/app/page.js b/js/app/page.js index 1cb8ac10..8d752acb 100644 --- a/js/app/page.js +++ b/js/app/page.js @@ -12,8 +12,8 @@ define([ 'app/map/contextmenu', 'slidebars', 'text!img/logo.svg!strip', - 'text!templates/modules/header.html', - 'text!templates/modules/footer.html', + 'text!templates/layout/header_map.html', + 'text!templates/layout/footer_map.html', 'dialog/notification', 'dialog/stats', 'dialog/map_info', @@ -36,7 +36,6 @@ define([ pageMenuClass: 'pf-menu', pageMenuLeftClass: 'pf-menu-left', // class for left menu pageMenuRightClass: 'pf-menu-right', // class for right menu - fullScreenClass: 'pf-fullscreen', // class for the "full screen" element // page structure pageClass: 'pf-site', @@ -49,17 +48,17 @@ define([ headUserCharacterClass: 'pf-head-user-character', // class for "user settings" link userCharacterImageClass: 'pf-head-user-character-image', // class for "current user image" - headUserShipClass: 'pf-head-user-ship', // class for "user settings" link - userShipImageClass: 'pf-head-user-ship-image', // class for "current user ship image" - headActiveUserClass: 'pf-head-active-user', // class for "active user" link + headActiveUsersClass: 'pf-head-active-users', // class for "active users" link headProgramStatusClass: 'pf-head-program-status', // class for "program status" notification + headMaxLocationHistoryBreadcrumbs: 3, // max breadcrumb count for character log history + // footer footerLicenceLinkClass: 'pf-footer-licence', // class for "licence" link + footerClockClass: 'pf-footer-clock', // class for EVE-Time clock // menu menuHeadMenuLogoClass: 'pf-head-menu-logo', // class for main menu logo - menuClockClass: 'pf-menu-clock', // class for EVE-Time clock // helper element dynamicElementWrapperId: 'pf-dialog-wrapper', // class for container element that holds hidden "context menus" @@ -69,61 +68,828 @@ define([ systemIntelModuleClass: 'pf-system-intel-module', // module wrapper (intel) }; + let menuBtnTypes = { + 'info': {class: 'list-group-item-info'}, + 'danger': {class: 'list-group-item-danger'}, + 'warning': {class: 'list-group-item-warning'} + }; + let programStatusCounter = 0; // current count down in s until next status change is possible let programStatusInterval = false; // interval timer until next status change is possible + /** + * set an DOM element to fullscreen mode + * @ see https://developer.mozilla.org/docs/Web/API/Fullscreen_API + * @param element + */ + let toggleFullScreen = element => { + if( + document.fullscreenEnabled && + element.requestFullscreen + ){ + let fullScreenButton = $('#' + Util.config.menuButtonFullScreenId); + + if(!document.fullscreenElement){ + element.requestFullscreen().then(() => { + fullScreenButton.toggleClass('active', true); + // close open menu + Util.triggerMenuAction(document, 'Close'); + }); + }else{ + if(document.exitFullscreen){ + document.exitFullscreen().then(() => { + fullScreenButton.toggleClass('active', false); + }); + } + } + } + }; + + /** + * init tooltips in header navbar + * @param element + */ + let initHeaderTooltips = element => { + element.find('[title]').tooltip({ + placement: 'bottom', + delay: { + show: 500, + hide: 0 + } + }); + }; + /** * load main page structure elements and navigation container into body * @returns {*|w.fn.init|jQuery|HTMLElement} */ let loadPageStructure = () => { - let body = $('body'); - body.prepend( - $('