From 4385c4a3d01d556919f1f0d8ce658a44c5f85417 Mon Sep 17 00:00:00 2001 From: Mark Friedrich Date: Thu, 6 Sep 2018 19:12:41 +0200 Subject: [PATCH] - new "deep links" added, custom URLs for e.g. system rally poke notifications, closed #682 --- .../handler/SlackRallyWebhookHandler.php | 10 ++ app/main/model/mapmodel.php | 22 ++++- app/main/model/systemmodel.php | 2 + js/app/datatables.loader.js | 2 - js/app/map/system.js | 7 +- js/app/map/util.js | 6 +- js/app/module_map.js | 80 ++++++++++----- js/app/ui/dialog/map_info.js | 95 +----------------- js/app/ui/module/system_info.js | 32 ++++-- js/app/util.js | 98 ++++++++++++++++++- public/js/v1.4.1/app/datatables.loader.js | 2 - public/js/v1.4.1/app/map/system.js | 7 +- public/js/v1.4.1/app/map/util.js | 6 +- public/js/v1.4.1/app/module_map.js | 76 +++++++++----- public/js/v1.4.1/app/ui/dialog/map_info.js | 95 +----------------- public/js/v1.4.1/app/ui/module/system_info.js | 32 ++++-- public/js/v1.4.1/app/util.js | 98 ++++++++++++++++++- public/templates/dialog/system_rally.html | 7 +- public/templates/modules/system_info.html | 23 ++--- 19 files changed, 412 insertions(+), 288 deletions(-) diff --git a/app/main/lib/logging/handler/SlackRallyWebhookHandler.php b/app/main/lib/logging/handler/SlackRallyWebhookHandler.php index 1a53f29f..71850c06 100644 --- a/app/main/lib/logging/handler/SlackRallyWebhookHandler.php +++ b/app/main/lib/logging/handler/SlackRallyWebhookHandler.php @@ -98,10 +98,20 @@ class SlackRallyWebhookHandler extends AbstractSlackWebhookHandler { $attachment['fields'][] = $this->generateAttachmentField('TrueSec', $objectData['objTrueSec']); } + if(!empty($objectData['objCountPlanets'])){ + // System planet count + $attachment['fields'][] = $this->generateAttachmentField('Planets', $objectData['objCountPlanets']); + } + if(!empty($objectData['objDescription'])){ // System trueSec $attachment['fields'][] = $this->generateAttachmentField('System description', '```' . $objectData['objDescription'] . '```', false, false); } + + if(!empty($objectData['objUrl'])){ + // System deeeplink + $attachment['fields'][] = $this->generateAttachmentField('', $objectData['objUrl'] , false, false); + } } } diff --git a/app/main/model/mapmodel.php b/app/main/model/mapmodel.php index af2a0f57..e7ed18bf 100644 --- a/app/main/model/mapmodel.php +++ b/app/main/model/mapmodel.php @@ -1210,7 +1210,7 @@ class MapModel extends AbstractMapTrackingModel { * checks whether this map is private map * @return bool */ - public function isPrivate(){ + public function isPrivate() : bool { return ($this->typeId->id === 2); } @@ -1218,7 +1218,7 @@ class MapModel extends AbstractMapTrackingModel { * checks whether this map is corporation map * @return bool */ - public function isCorporation(){ + public function isCorporation() : bool { return ($this->typeId->id === 3); } @@ -1226,7 +1226,7 @@ class MapModel extends AbstractMapTrackingModel { * checks whether this map is alliance map * @return bool */ - public function isAlliance(){ + public function isAlliance() : bool { return ($this->typeId->id === 4); } @@ -1242,6 +1242,22 @@ class MapModel extends AbstractMapTrackingModel { return $scope; } + /** + * get deeplink url for map + * -> optional return link for map + system + * @param int $systemId + * @return string + */ + public function getDeeplinkUrl(int $systemId = 0) : string { + $url = ''; + if( !$this->dry() ){ + $param = rawurlencode(base64_encode($this->_id)); + $param .= $systemId ? '_' . rawurlencode(base64_encode($systemId)) : ''; + $url = $this->getF3()->get('SCHEME') . '://' . $this->getF3()->get('HOST') . $this->getF3()->alias('map', ['*' => '/' . $param]); + } + return $url; + } + /** * get log file data * @param int $offset diff --git a/app/main/model/systemmodel.php b/app/main/model/systemmodel.php index 20bc6b64..7704d5a3 100644 --- a/app/main/model/systemmodel.php +++ b/app/main/model/systemmodel.php @@ -738,12 +738,14 @@ class SystemModel extends AbstractMapTrackingModel { ]; if($fullData){ + $objectData['objUrl'] = $this->getMap()->getDeeplinkUrl($this->_id); $objectData['objAlias'] = $this->alias; $objectData['objRegion'] = $this->region; $objectData['objIsWormhole'] = $this->isWormhole(); $objectData['objEffect'] = $this->effect; $objectData['objSecurity'] = $this->security; $objectData['objTrueSec'] = $this->trueSec; + $objectData['objCountPlanets'] = count((array)$this->planets); $objectData['objDescription'] = $this->description; } diff --git a/js/app/datatables.loader.js b/js/app/datatables.loader.js index 79873ffb..7076767a 100644 --- a/js/app/datatables.loader.js +++ b/js/app/datatables.loader.js @@ -12,8 +12,6 @@ define([ // all Datatables stuff is available... let initDefaultDatatablesConfig = () => { - - $.extend(true, $.fn.dataTable.defaults, { pageLength: -1, pagingType: 'simple_numbers', diff --git a/js/app/map/system.js b/js/app/map/system.js index 17cb8585..732096ff 100644 --- a/js/app/map/system.js +++ b/js/app/map/system.js @@ -48,7 +48,9 @@ define([ * @param system */ $.fn.showRallyPointDialog = (system) => { - let mapData = Util.getCurrentMapData(system.data('mapid')); + let mapId = system.data('mapid'); + let systemId = system.data('id'); + let mapData = Util.getCurrentMapData(mapId); requirejs(['text!templates/dialog/system_rally.html', 'mustache'], function(template, Mustache) { @@ -108,7 +110,8 @@ define([ mailRallyEnabled: Boolean(Util.getObjVal(mapData, 'config.logging.mailRally')), dialogRallyMessageDefault: config.dialogRallyMessageDefault, - systemId: system.data('id') + systemUrl: MapUtil.getMapDeeplinkUrl(mapId, systemId), + systemId: systemId }; let content = Mustache.render(template, data); diff --git a/js/app/map/util.js b/js/app/map/util.js index 11de7c16..dc5bae08 100644 --- a/js/app/map/util.js +++ b/js/app/map/util.js @@ -570,6 +570,7 @@ define([ /** * set system visibility e.g. or filtered systems * @param system + * @param map * @param visible */ let setSystemVisible = (system, map, visible) => { @@ -1585,12 +1586,13 @@ define([ /** * get a unique map url for deeplinking * @param mapId + * @param systemId * @returns {string} */ - let getMapDeeplinkUrl = (mapId) => { + let getMapDeeplinkUrl = (mapId, systemId) => { let url = location.protocol + '//' + location.host + '/map'; url += mapId ? '/' + encodeURIComponent(window.btoa(mapId)) : ''; - + url += systemId ? '_' + encodeURIComponent(window.btoa(systemId)) : ''; return url; }; diff --git a/js/app/module_map.js b/js/app/module_map.js index f74fc9ca..c6718754 100644 --- a/js/app/module_map.js +++ b/js/app/module_map.js @@ -246,7 +246,7 @@ define([ */ let showPanel = (parentElement, Module, mapId, data) => { let moduleElement = Module.getModule(parentElement, mapId, data); - if (moduleElement) { + if(moduleElement) { // store Module object to DOM element for further access moduleElement.data('module', Module); moduleElement.data('data', data); @@ -265,12 +265,12 @@ define([ // check for stored module order in indexDB (client) ---------------------------------------------- let key = 'modules_cell_' + this.parentElement.attr('data-position'); - if ( + if( dataStore && dataStore[key] - ) { + ){ let positionIndex = dataStore[key].indexOf(Module.config.moduleName); - if (positionIndex !== -1) { + if(positionIndex !== -1) { // first index (0) => is position 1 defaultPosition = positionIndex + 1; } @@ -284,13 +284,13 @@ define([ // insert at correct position --------------------------------------------------------------------- let prevModuleElement = this.parentElement.find('.' + config.moduleClass + ':nth-child(' + position + ')'); - if (prevModuleElement.length) { + if(prevModuleElement.length) { this.moduleElement.insertAfter(prevModuleElement); } else { this.parentElement.prepend(this.moduleElement); } - if (typeof Module.beforeShow === 'function') { + if(typeof Module.beforeShow === 'function') { Module.beforeShow(this.moduleElement, moduleElement.data('data')); } @@ -304,7 +304,7 @@ define([ complete: function (moduleElement) { moduleElement = $(moduleElement); let Module = $(moduleElement).data('module'); - if (typeof Module.initModule === 'function') { + if(typeof Module.initModule === 'function') { Module.initModule(moduleElement, mapId, moduleElement.data('data')); } @@ -328,7 +328,7 @@ define([ // check if module already exists let moduleElement = parentElement.find('.' + Module.config.moduleTypeClass); - if (moduleElement.length > 0) { + if(moduleElement.length > 0) { removeModule(moduleElement, Module, () => { showPanel(parentElement, Module, mapId, data); }, true); @@ -714,7 +714,7 @@ define([ if(mapTabChangeBlocked === false){ let tabLinkElement = $(this); - let mapId = tabLinkElement.data('map-id'); + let mapId = tabLinkElement.data('mapId'); // ignore "add" tab. no need for map change if(mapId > 0){ @@ -741,7 +741,7 @@ define([ // tab switch -------------------------------------------------------------------------------------------- $(tabListElement).on('show.bs.tab', 'a', function(e){ - let mapId = $(e.target).data('map-id'); + let mapId = $(e.target).data('mapId'); if(mapId > 0){ // save mapId as new "default" (local storage) @@ -755,7 +755,9 @@ define([ $(tabListElement).on('shown.bs.tab', 'a', function(e){ // load new map right after tab-change - let mapId = $(e.target).data('map-id'); + let tabLinkElement = $(e.target); + let mapId = tabLinkElement.data('mapId'); + let defaultSystemId = tabLinkElement.data('defaultSystemId'); let tabMapData = Util.getCurrentMapData(mapId); if(tabMapData !== false){ @@ -773,10 +775,19 @@ define([ let activeSystem = mapElement.find('.' + MapUtil.config.systemActiveClass + ':first'); if(activeSystem.length){ MapUtil.setSystemActive(mapConfig.map, activeSystem); + }else if(defaultSystemId){ + // currently no system "active" check if there is a default system set for this mapTab + // -> e.g. from URL link + let systemId = MapUtil.getSystemId(mapConfig.config.id, defaultSystemId); + let system = mapElement.find('#' + systemId); + if(system.length){ + // system exists on map -> make active and show panels + MapUtil.showSystemInfo(mapConfig.map, system); + } } // change url to unique map URL - if (history.pushState) { + if(history.pushState) { let mapUrl = MapUtil.getMapDeeplinkUrl(mapConfig.config.id); history.pushState({}, '', mapUrl); } @@ -788,8 +799,8 @@ define([ }); $(tabListElement).on('hide.bs.tab', 'a', function(e){ - let newMapId = $(e.relatedTarget).data('map-id'); - let oldMapId = $(e.target).data('map-id'); + let newMapId = $(e.relatedTarget).data('mapId'); + let oldMapId = $(e.target).data('mapId'); // skip "add button" if(newMapId > 0){ @@ -823,7 +834,7 @@ define([ */ let updateTabExecutor = (resolve, reject) => { // set "main" data - tabElement.data('map-id', options.id); + tabElement.data('mapId', options.id); // add updated timestamp (not available for "add" tab if(Util.getObjVal(options, 'updated.updated')){ @@ -988,7 +999,7 @@ define([ let key = 'maps_list_' + tabBar.attr('data-position'); if(dataStore && dataStore[key]){ let positionIndex = dataStore[key].indexOf(this.options.id); - if (positionIndex !== -1) { + if(positionIndex !== -1) { // first index (0) => is position 1 defaultPosition = positionIndex + 1; } @@ -1000,7 +1011,7 @@ define([ // insert at correct position ------------------------------------------------------------------------- let prevListElement = tabBar.find('li' + '' + ':nth-child(' + position + ')'); - if (prevListElement.length) { + if(prevListElement.length) { tabListElement.insertAfter(prevListElement); } else { tabBar.prepend(tabListElement); @@ -1085,7 +1096,7 @@ define([ let tabElements = mapModule.getMapTabElements(); for(let i = 0; i < tabElements.length; i++){ let tabElement = $(tabElements[i]); - let mapId = tabElement.data('map-id'); + let mapId = tabElement.data('mapId'); if(mapId > 0){ promiseDeleteTab.push(deleteTab(tabMapElement, mapId)); } @@ -1104,6 +1115,23 @@ define([ return parts.pop() || parts.pop(); }; + /** + * extract data from map url + * @returns {Array} + */ + let getMapDataFromUrl = () => { + let data = []; + let lastURLSegment = getLastUrlSegment(); + if(lastURLSegment.length){ + try{ + data = lastURLSegment.split('_').map(part => parseInt(atob(decodeURIComponent(part))) || 0); + }catch(e){ + // data could not be extracted from URL -> ignore + } + } + return data; + }; + /** * set "default" map tab * -> default mapId might be available in local storage @@ -1128,17 +1156,15 @@ define([ let activeTabLinkElement = false; // check for existing mapId URL identifier ------------------------------------------------------------ - let lastURLSegment = getLastUrlSegment(); - - let defaultMapId = 0; - try{ - defaultMapId = parseInt(atob(decodeURIComponent(lastURLSegment))); - }catch(e){ - // defaultMapID could not be extracted from URL -> ignore - } + let urlData = getMapDataFromUrl(); + let defaultMapId = urlData[0] || 0; + let defaultSystemId = urlData[1] || 0; if(defaultMapId){ activeTabLinkElement = getActiveTabLinkElement(defaultMapId); + if(defaultSystemId && activeTabLinkElement.length){ + activeTabLinkElement.data('defaultSystemId', defaultSystemId); + } } // ... else check for existing cached default mapId --------------------------------------------------- @@ -1218,7 +1244,7 @@ define([ // check whether a tab/map is still active for(let i = 0; i < tabElements.length; i++){ let tabElement = $(tabElements[i]); - let mapId = tabElement.data('map-id'); + let mapId = tabElement.data('mapId'); if(mapId > 0){ let tabMapData = Util.getCurrentMapData(mapId); diff --git a/js/app/ui/dialog/map_info.js b/js/app/ui/dialog/map_info.js index ad19d814..773ac7c2 100644 --- a/js/app/ui/dialog/map_info.js +++ b/js/app/ui/dialog/map_info.js @@ -92,99 +92,6 @@ define([ return 'unknown'; }; - /** - * write clipboard text - * @param text - * @returns {Promise} - */ - let copyToClipboard = (text) => { - - let copyToClipboardExecutor = (resolve, reject) => { - let payload = { - action: 'copyToClipboard', - data: false - }; - - if (navigator.clipboard) { - // get current permission status - navigator.permissions.query({ - name: 'clipboard-write' - }).then(permissionStatus => { - // will be 'granted', 'denied' or 'prompt' - if( - permissionStatus.state === 'granted' || - permissionStatus.state === 'prompt' - ){ - navigator.clipboard.writeText(text) - .then(() => { - payload.data = true; - resolve(payload); }) - .catch(err => { - let errorMsg = 'Failed to write clipboard content'; - console.error(errorMsg, err); - Util.showNotify({title: 'Clipboard API', text: errorMsg, type: 'error'}); - resolve(payload); - }); - }else{ - Util.showNotify({title: 'Clipboard API', text: 'You denied write access', type: 'warning'}); - resolve(payload); - } - }); - } else { - console.warn('Clipboard API not supported by your browser'); - resolve(payload); - } - }; - - return new Promise(copyToClipboardExecutor); - }; - - /** - * read clipboard text - * @returns {Promise} - */ - let readFromClipboard = () => { - - let readFromClipboardExecutor = (resolve, reject) => { - let payload = { - action: 'readFromClipboard', - data: false - }; - - if (navigator.clipboard) { - // get current permission status - navigator.permissions.query({ - name: 'clipboard-read' - }).then(permissionStatus => { - // will be 'granted', 'denied' or 'prompt' - if( - permissionStatus.state === 'granted' || - permissionStatus.state === 'prompt' - ){ - navigator.clipboard.readText() - .then(text => { - payload.data = text; - resolve(payload); }) - .catch(err => { - let errorMsg = 'Failed to read clipboard content'; - console.error(errorMsg, err); - Util.showNotify({title: 'Clipboard API', text: errorMsg, type: 'error'}); - resolve(payload); - }); - }else{ - Util.showNotify({title: 'Clipboard API', text: 'You denied read access', type: 'warning'}); - resolve(payload); - } - }); - } else { - console.warn('Clipboard API not supported by your browser'); - resolve(payload); - } - }; - - return new Promise(readFromClipboardExecutor); - }; - /** * loads the map info data into an element * @param mapData @@ -289,7 +196,7 @@ define([ mapElement.find('.' + config.textActionIconCopyClass).on('click', function(){ let mapUrl = $(this).find('span').text().trim(); - copyToClipboard(mapUrl).then(payload => { + Util.copyToClipboard(mapUrl).then(payload => { if(payload.data){ Util.showNotify({title: 'Copied to clipbaord', text: mapUrl, type: 'success'}); } diff --git a/js/app/ui/module/system_info.js b/js/app/ui/module/system_info.js index 841e4d7c..8f2ae19d 100644 --- a/js/app/ui/module/system_info.js +++ b/js/app/ui/module/system_info.js @@ -19,10 +19,14 @@ define([ // system info module moduleTypeClass: 'pf-system-info-module', // class for this module + // headline toolbar + moduleHeadlineIconClass: 'pf-module-icon-button', // class for toolbar icons in the head + // breadcrumb constellationLinkClass: 'pf-system-info-constellation', // class for "constellation" name regionLinkClass: 'pf-system-info-region', // class for "region" name typeLinkClass: 'pf-system-info-type', // class for "type" name + urlLinkClass: 'pf-system-info-url', // class for "url" copy link // info table systemInfoTableClass: 'pf-module-table', // class for system info table @@ -185,13 +189,10 @@ define([ functions: { after: function(conf){ let tempModuleElement = conf.position; - // lock "description" field until first update tempModuleElement.find('.' + config.descriptionArea).showLoadingAnimation(); - // "add description" button let descriptionButton = tempModuleElement.find('.' + config.addDescriptionButtonClass); - // description textarea element let descriptionTextareaElement = tempModuleElement.find('.' + config.descriptionTextareaElementClass); @@ -302,13 +303,16 @@ define([ // init tooltips ---------------------------------------------------------------------------------- let tooltipElements = tempModuleElement.find('[data-toggle="tooltip"]'); - tooltipElements.tooltip(); + tooltipElements.tooltip({ + container: 'body', + placement: 'top' + }); // init system effect popover --------------------------------------------------------------------- - $(moduleElement).find('.' + config.systemInfoEffectClass).addSystemEffectTooltip(systemData.security, systemData.effect); + tempModuleElement.find('.' + config.systemInfoEffectClass).addSystemEffectTooltip(systemData.security, systemData.effect); // init planets popover --------------------------------------------------------------------------- - $(moduleElement).find('.' + config.systemInfoPlanetsClass).addSystemPlanetsTooltip(systemData.planets); + tempModuleElement.find('.' + config.systemInfoPlanetsClass).addSystemPlanetsTooltip(systemData.planets); // init static wormhole information --------------------------------------------------------------- for(let staticData of staticsData){ @@ -316,6 +320,16 @@ define([ staticRowElement.addWormholeInfoTooltip(staticData); } + // copy system deeplink URL ----------------------------------------------------------------------- + tempModuleElement.find('.' + config.urlLinkClass).on('click', function(){ + let mapUrl = $(this).attr('data-url'); + Util.copyToClipboard(mapUrl).then(payload => { + if(payload.data){ + Util.showNotify({title: 'Copied to clipbaord', text: mapUrl, type: 'success'}); + } + }); + }); + // constellation popover -------------------------------------------------------------------------- tempModuleElement.find('a.popup-ajax').popover({ html: true, @@ -351,6 +365,7 @@ define([ let moduleData = { system: systemData, static: staticsData, + moduleHeadlineIconClass: config.moduleHeadlineIconClass, tableClass: config.systemInfoTableClass, nameInfoClass: config.systemInfoNameClass, effectInfoClass: config.systemInfoEffectClass, @@ -359,6 +374,7 @@ define([ statusInfoClass: config.systemInfoStatusLabelClass, popoverTriggerClass: Util.config.popoverTriggerClass, + systemUrl: MapUtil.getMapDeeplinkUrl(mapId, systemData.id), systemTypeName: MapUtil.getSystemTypeInfo(systemData.type.id, 'name'), systemIsWormhole: MapUtil.getSystemTypeInfo(systemData.type.id, 'name') === 'w-space', systemStatusId: systemData.status.id, @@ -389,8 +405,8 @@ define([ systemConstellationLinkClass: config.constellationLinkClass, systemRegionLinkClass: config.regionLinkClass, - systemTypeLinkClass: config.typeLinkClass - + systemTypeLinkClass: config.typeLinkClass, + systemUrlLinkClass: config.urlLinkClass }; Render.showModule(moduleConfig, moduleData); diff --git a/js/app/util.js b/js/app/util.js index bf7b7242..1a5065c2 100644 --- a/js/app/util.js +++ b/js/app/util.js @@ -1560,7 +1560,7 @@ define([ if(mapId){ // search for a specific tab element mapTabElements = mapTabElements.filter(function(i, el){ - return ( $(el).data('map-id') === mapId ); + return ( $(el).data('mapId') === mapId ); }); } @@ -2398,6 +2398,100 @@ define([ }); }; + + /** + * write clipboard text + * @param text + * @returns {Promise} + */ + let copyToClipboard = (text) => { + + let copyToClipboardExecutor = (resolve, reject) => { + let payload = { + action: 'copyToClipboard', + data: false + }; + + if (navigator.clipboard) { + // get current permission status + navigator.permissions.query({ + name: 'clipboard-write' + }).then(permissionStatus => { + // will be 'granted', 'denied' or 'prompt' + if( + permissionStatus.state === 'granted' || + permissionStatus.state === 'prompt' + ){ + navigator.clipboard.writeText(text) + .then(() => { + payload.data = true; + resolve(payload); }) + .catch(err => { + let errorMsg = 'Failed to write clipboard content'; + console.error(errorMsg, err); + showNotify({title: 'Clipboard API', text: errorMsg, type: 'error'}); + resolve(payload); + }); + }else{ + showNotify({title: 'Clipboard API', text: 'You denied write access', type: 'warning'}); + resolve(payload); + } + }); + } else { + console.warn('Clipboard API not supported by your browser'); + resolve(payload); + } + }; + + return new Promise(copyToClipboardExecutor); + }; + + /** + * read clipboard text + * @returns {Promise} + */ + let readFromClipboard = () => { + + let readFromClipboardExecutor = (resolve, reject) => { + let payload = { + action: 'readFromClipboard', + data: false + }; + + if (navigator.clipboard) { + // get current permission status + navigator.permissions.query({ + name: 'clipboard-read' + }).then(permissionStatus => { + // will be 'granted', 'denied' or 'prompt' + if( + permissionStatus.state === 'granted' || + permissionStatus.state === 'prompt' + ){ + navigator.clipboard.readText() + .then(text => { + payload.data = text; + resolve(payload); }) + .catch(err => { + let errorMsg = 'Failed to read clipboard content'; + console.error(errorMsg, err); + showNotify({title: 'Clipboard API', text: errorMsg, type: 'error'}); + resolve(payload); + }); + }else{ + showNotify({title: 'Clipboard API', text: 'You denied read access', type: 'warning'}); + resolve(payload); + } + }); + } else { + console.warn('Clipboard API not supported by your browser'); + resolve(payload); + } + }; + + return new Promise(readFromClipboardExecutor); + }; + /** * set currentSystemData as "global" variable * @param systemData @@ -2786,6 +2880,8 @@ define([ getNearBySystemData: getNearBySystemData, getNearByCharacterData: getNearByCharacterData, setDestination: setDestination, + copyToClipboard: copyToClipboard, + readFromClipboard: readFromClipboard, convertDateToUTC: convertDateToUTC, convertDateToString: convertDateToString, getOpenDialogs: getOpenDialogs, diff --git a/public/js/v1.4.1/app/datatables.loader.js b/public/js/v1.4.1/app/datatables.loader.js index 79873ffb..7076767a 100644 --- a/public/js/v1.4.1/app/datatables.loader.js +++ b/public/js/v1.4.1/app/datatables.loader.js @@ -12,8 +12,6 @@ define([ // all Datatables stuff is available... let initDefaultDatatablesConfig = () => { - - $.extend(true, $.fn.dataTable.defaults, { pageLength: -1, pagingType: 'simple_numbers', diff --git a/public/js/v1.4.1/app/map/system.js b/public/js/v1.4.1/app/map/system.js index 17cb8585..732096ff 100644 --- a/public/js/v1.4.1/app/map/system.js +++ b/public/js/v1.4.1/app/map/system.js @@ -48,7 +48,9 @@ define([ * @param system */ $.fn.showRallyPointDialog = (system) => { - let mapData = Util.getCurrentMapData(system.data('mapid')); + let mapId = system.data('mapid'); + let systemId = system.data('id'); + let mapData = Util.getCurrentMapData(mapId); requirejs(['text!templates/dialog/system_rally.html', 'mustache'], function(template, Mustache) { @@ -108,7 +110,8 @@ define([ mailRallyEnabled: Boolean(Util.getObjVal(mapData, 'config.logging.mailRally')), dialogRallyMessageDefault: config.dialogRallyMessageDefault, - systemId: system.data('id') + systemUrl: MapUtil.getMapDeeplinkUrl(mapId, systemId), + systemId: systemId }; let content = Mustache.render(template, data); diff --git a/public/js/v1.4.1/app/map/util.js b/public/js/v1.4.1/app/map/util.js index 11de7c16..dc5bae08 100644 --- a/public/js/v1.4.1/app/map/util.js +++ b/public/js/v1.4.1/app/map/util.js @@ -570,6 +570,7 @@ define([ /** * set system visibility e.g. or filtered systems * @param system + * @param map * @param visible */ let setSystemVisible = (system, map, visible) => { @@ -1585,12 +1586,13 @@ define([ /** * get a unique map url for deeplinking * @param mapId + * @param systemId * @returns {string} */ - let getMapDeeplinkUrl = (mapId) => { + let getMapDeeplinkUrl = (mapId, systemId) => { let url = location.protocol + '//' + location.host + '/map'; url += mapId ? '/' + encodeURIComponent(window.btoa(mapId)) : ''; - + url += systemId ? '_' + encodeURIComponent(window.btoa(systemId)) : ''; return url; }; diff --git a/public/js/v1.4.1/app/module_map.js b/public/js/v1.4.1/app/module_map.js index f74fc9ca..83c2a39a 100644 --- a/public/js/v1.4.1/app/module_map.js +++ b/public/js/v1.4.1/app/module_map.js @@ -246,7 +246,7 @@ define([ */ let showPanel = (parentElement, Module, mapId, data) => { let moduleElement = Module.getModule(parentElement, mapId, data); - if (moduleElement) { + if(moduleElement) { // store Module object to DOM element for further access moduleElement.data('module', Module); moduleElement.data('data', data); @@ -265,12 +265,12 @@ define([ // check for stored module order in indexDB (client) ---------------------------------------------- let key = 'modules_cell_' + this.parentElement.attr('data-position'); - if ( + if( dataStore && dataStore[key] - ) { + ){ let positionIndex = dataStore[key].indexOf(Module.config.moduleName); - if (positionIndex !== -1) { + if(positionIndex !== -1) { // first index (0) => is position 1 defaultPosition = positionIndex + 1; } @@ -284,13 +284,13 @@ define([ // insert at correct position --------------------------------------------------------------------- let prevModuleElement = this.parentElement.find('.' + config.moduleClass + ':nth-child(' + position + ')'); - if (prevModuleElement.length) { + if(prevModuleElement.length) { this.moduleElement.insertAfter(prevModuleElement); } else { this.parentElement.prepend(this.moduleElement); } - if (typeof Module.beforeShow === 'function') { + if(typeof Module.beforeShow === 'function') { Module.beforeShow(this.moduleElement, moduleElement.data('data')); } @@ -304,7 +304,7 @@ define([ complete: function (moduleElement) { moduleElement = $(moduleElement); let Module = $(moduleElement).data('module'); - if (typeof Module.initModule === 'function') { + if(typeof Module.initModule === 'function') { Module.initModule(moduleElement, mapId, moduleElement.data('data')); } @@ -328,7 +328,7 @@ define([ // check if module already exists let moduleElement = parentElement.find('.' + Module.config.moduleTypeClass); - if (moduleElement.length > 0) { + if(moduleElement.length > 0) { removeModule(moduleElement, Module, () => { showPanel(parentElement, Module, mapId, data); }, true); @@ -714,7 +714,7 @@ define([ if(mapTabChangeBlocked === false){ let tabLinkElement = $(this); - let mapId = tabLinkElement.data('map-id'); + let mapId = tabLinkElement.data('mapId'); // ignore "add" tab. no need for map change if(mapId > 0){ @@ -741,7 +741,7 @@ define([ // tab switch -------------------------------------------------------------------------------------------- $(tabListElement).on('show.bs.tab', 'a', function(e){ - let mapId = $(e.target).data('map-id'); + let mapId = $(e.target).data('mapId'); if(mapId > 0){ // save mapId as new "default" (local storage) @@ -755,7 +755,9 @@ define([ $(tabListElement).on('shown.bs.tab', 'a', function(e){ // load new map right after tab-change - let mapId = $(e.target).data('map-id'); + let tabLinkElement = $(e.target); + let mapId = tabLinkElement.data('mapId'); + let defaultSystemId = tabLinkElement.data('defaultSystemId'); let tabMapData = Util.getCurrentMapData(mapId); if(tabMapData !== false){ @@ -773,10 +775,19 @@ define([ let activeSystem = mapElement.find('.' + MapUtil.config.systemActiveClass + ':first'); if(activeSystem.length){ MapUtil.setSystemActive(mapConfig.map, activeSystem); + }else if(defaultSystemId){ + // currently no system "active" check if there is a default system set for this mapTab + // -> e.g. from URL link + let systemId = MapUtil.getSystemId(mapConfig.config.id, defaultSystemId); + let system = mapElement.find('#' + systemId); + if(system.length){ + // system exists on map -> make active and show panels + MapUtil.showSystemInfo(mapConfig.map, system); + } } // change url to unique map URL - if (history.pushState) { + if(history.pushState) { let mapUrl = MapUtil.getMapDeeplinkUrl(mapConfig.config.id); history.pushState({}, '', mapUrl); } @@ -788,8 +799,8 @@ define([ }); $(tabListElement).on('hide.bs.tab', 'a', function(e){ - let newMapId = $(e.relatedTarget).data('map-id'); - let oldMapId = $(e.target).data('map-id'); + let newMapId = $(e.relatedTarget).data('mapId'); + let oldMapId = $(e.target).data('mapId'); // skip "add button" if(newMapId > 0){ @@ -823,7 +834,7 @@ define([ */ let updateTabExecutor = (resolve, reject) => { // set "main" data - tabElement.data('map-id', options.id); + tabElement.data('mapId', options.id); // add updated timestamp (not available for "add" tab if(Util.getObjVal(options, 'updated.updated')){ @@ -988,7 +999,7 @@ define([ let key = 'maps_list_' + tabBar.attr('data-position'); if(dataStore && dataStore[key]){ let positionIndex = dataStore[key].indexOf(this.options.id); - if (positionIndex !== -1) { + if(positionIndex !== -1) { // first index (0) => is position 1 defaultPosition = positionIndex + 1; } @@ -1000,7 +1011,7 @@ define([ // insert at correct position ------------------------------------------------------------------------- let prevListElement = tabBar.find('li' + '' + ':nth-child(' + position + ')'); - if (prevListElement.length) { + if(prevListElement.length) { tabListElement.insertAfter(prevListElement); } else { tabBar.prepend(tabListElement); @@ -1085,7 +1096,7 @@ define([ let tabElements = mapModule.getMapTabElements(); for(let i = 0; i < tabElements.length; i++){ let tabElement = $(tabElements[i]); - let mapId = tabElement.data('map-id'); + let mapId = tabElement.data('mapId'); if(mapId > 0){ promiseDeleteTab.push(deleteTab(tabMapElement, mapId)); } @@ -1104,6 +1115,19 @@ define([ return parts.pop() || parts.pop(); }; + let getMapDataFromUrl = () => { + let data = []; + let lastURLSegment = getLastUrlSegment(); + if(lastURLSegment.length){ + try{ + data = lastURLSegment.split('_').map(part => parseInt(atob(decodeURIComponent(part))) || 0); + }catch(e){ + // data could not be extracted from URL -> ignore + } + } + return data; + }; + /** * set "default" map tab * -> default mapId might be available in local storage @@ -1128,17 +1152,15 @@ define([ let activeTabLinkElement = false; // check for existing mapId URL identifier ------------------------------------------------------------ - let lastURLSegment = getLastUrlSegment(); - - let defaultMapId = 0; - try{ - defaultMapId = parseInt(atob(decodeURIComponent(lastURLSegment))); - }catch(e){ - // defaultMapID could not be extracted from URL -> ignore - } + let urlData = getMapDataFromUrl(); + let defaultMapId = urlData[0] || 0; + let defaultSystemId = urlData[1] || 0; if(defaultMapId){ activeTabLinkElement = getActiveTabLinkElement(defaultMapId); + if(defaultSystemId && activeTabLinkElement.length){ + activeTabLinkElement.data('defaultSystemId', defaultSystemId); + } } // ... else check for existing cached default mapId --------------------------------------------------- @@ -1218,7 +1240,7 @@ define([ // check whether a tab/map is still active for(let i = 0; i < tabElements.length; i++){ let tabElement = $(tabElements[i]); - let mapId = tabElement.data('map-id'); + let mapId = tabElement.data('mapId'); if(mapId > 0){ let tabMapData = Util.getCurrentMapData(mapId); diff --git a/public/js/v1.4.1/app/ui/dialog/map_info.js b/public/js/v1.4.1/app/ui/dialog/map_info.js index ad19d814..773ac7c2 100644 --- a/public/js/v1.4.1/app/ui/dialog/map_info.js +++ b/public/js/v1.4.1/app/ui/dialog/map_info.js @@ -92,99 +92,6 @@ define([ return 'unknown'; }; - /** - * write clipboard text - * @param text - * @returns {Promise} - */ - let copyToClipboard = (text) => { - - let copyToClipboardExecutor = (resolve, reject) => { - let payload = { - action: 'copyToClipboard', - data: false - }; - - if (navigator.clipboard) { - // get current permission status - navigator.permissions.query({ - name: 'clipboard-write' - }).then(permissionStatus => { - // will be 'granted', 'denied' or 'prompt' - if( - permissionStatus.state === 'granted' || - permissionStatus.state === 'prompt' - ){ - navigator.clipboard.writeText(text) - .then(() => { - payload.data = true; - resolve(payload); }) - .catch(err => { - let errorMsg = 'Failed to write clipboard content'; - console.error(errorMsg, err); - Util.showNotify({title: 'Clipboard API', text: errorMsg, type: 'error'}); - resolve(payload); - }); - }else{ - Util.showNotify({title: 'Clipboard API', text: 'You denied write access', type: 'warning'}); - resolve(payload); - } - }); - } else { - console.warn('Clipboard API not supported by your browser'); - resolve(payload); - } - }; - - return new Promise(copyToClipboardExecutor); - }; - - /** - * read clipboard text - * @returns {Promise} - */ - let readFromClipboard = () => { - - let readFromClipboardExecutor = (resolve, reject) => { - let payload = { - action: 'readFromClipboard', - data: false - }; - - if (navigator.clipboard) { - // get current permission status - navigator.permissions.query({ - name: 'clipboard-read' - }).then(permissionStatus => { - // will be 'granted', 'denied' or 'prompt' - if( - permissionStatus.state === 'granted' || - permissionStatus.state === 'prompt' - ){ - navigator.clipboard.readText() - .then(text => { - payload.data = text; - resolve(payload); }) - .catch(err => { - let errorMsg = 'Failed to read clipboard content'; - console.error(errorMsg, err); - Util.showNotify({title: 'Clipboard API', text: errorMsg, type: 'error'}); - resolve(payload); - }); - }else{ - Util.showNotify({title: 'Clipboard API', text: 'You denied read access', type: 'warning'}); - resolve(payload); - } - }); - } else { - console.warn('Clipboard API not supported by your browser'); - resolve(payload); - } - }; - - return new Promise(readFromClipboardExecutor); - }; - /** * loads the map info data into an element * @param mapData @@ -289,7 +196,7 @@ define([ mapElement.find('.' + config.textActionIconCopyClass).on('click', function(){ let mapUrl = $(this).find('span').text().trim(); - copyToClipboard(mapUrl).then(payload => { + Util.copyToClipboard(mapUrl).then(payload => { if(payload.data){ Util.showNotify({title: 'Copied to clipbaord', text: mapUrl, type: 'success'}); } diff --git a/public/js/v1.4.1/app/ui/module/system_info.js b/public/js/v1.4.1/app/ui/module/system_info.js index 841e4d7c..8f2ae19d 100644 --- a/public/js/v1.4.1/app/ui/module/system_info.js +++ b/public/js/v1.4.1/app/ui/module/system_info.js @@ -19,10 +19,14 @@ define([ // system info module moduleTypeClass: 'pf-system-info-module', // class for this module + // headline toolbar + moduleHeadlineIconClass: 'pf-module-icon-button', // class for toolbar icons in the head + // breadcrumb constellationLinkClass: 'pf-system-info-constellation', // class for "constellation" name regionLinkClass: 'pf-system-info-region', // class for "region" name typeLinkClass: 'pf-system-info-type', // class for "type" name + urlLinkClass: 'pf-system-info-url', // class for "url" copy link // info table systemInfoTableClass: 'pf-module-table', // class for system info table @@ -185,13 +189,10 @@ define([ functions: { after: function(conf){ let tempModuleElement = conf.position; - // lock "description" field until first update tempModuleElement.find('.' + config.descriptionArea).showLoadingAnimation(); - // "add description" button let descriptionButton = tempModuleElement.find('.' + config.addDescriptionButtonClass); - // description textarea element let descriptionTextareaElement = tempModuleElement.find('.' + config.descriptionTextareaElementClass); @@ -302,13 +303,16 @@ define([ // init tooltips ---------------------------------------------------------------------------------- let tooltipElements = tempModuleElement.find('[data-toggle="tooltip"]'); - tooltipElements.tooltip(); + tooltipElements.tooltip({ + container: 'body', + placement: 'top' + }); // init system effect popover --------------------------------------------------------------------- - $(moduleElement).find('.' + config.systemInfoEffectClass).addSystemEffectTooltip(systemData.security, systemData.effect); + tempModuleElement.find('.' + config.systemInfoEffectClass).addSystemEffectTooltip(systemData.security, systemData.effect); // init planets popover --------------------------------------------------------------------------- - $(moduleElement).find('.' + config.systemInfoPlanetsClass).addSystemPlanetsTooltip(systemData.planets); + tempModuleElement.find('.' + config.systemInfoPlanetsClass).addSystemPlanetsTooltip(systemData.planets); // init static wormhole information --------------------------------------------------------------- for(let staticData of staticsData){ @@ -316,6 +320,16 @@ define([ staticRowElement.addWormholeInfoTooltip(staticData); } + // copy system deeplink URL ----------------------------------------------------------------------- + tempModuleElement.find('.' + config.urlLinkClass).on('click', function(){ + let mapUrl = $(this).attr('data-url'); + Util.copyToClipboard(mapUrl).then(payload => { + if(payload.data){ + Util.showNotify({title: 'Copied to clipbaord', text: mapUrl, type: 'success'}); + } + }); + }); + // constellation popover -------------------------------------------------------------------------- tempModuleElement.find('a.popup-ajax').popover({ html: true, @@ -351,6 +365,7 @@ define([ let moduleData = { system: systemData, static: staticsData, + moduleHeadlineIconClass: config.moduleHeadlineIconClass, tableClass: config.systemInfoTableClass, nameInfoClass: config.systemInfoNameClass, effectInfoClass: config.systemInfoEffectClass, @@ -359,6 +374,7 @@ define([ statusInfoClass: config.systemInfoStatusLabelClass, popoverTriggerClass: Util.config.popoverTriggerClass, + systemUrl: MapUtil.getMapDeeplinkUrl(mapId, systemData.id), systemTypeName: MapUtil.getSystemTypeInfo(systemData.type.id, 'name'), systemIsWormhole: MapUtil.getSystemTypeInfo(systemData.type.id, 'name') === 'w-space', systemStatusId: systemData.status.id, @@ -389,8 +405,8 @@ define([ systemConstellationLinkClass: config.constellationLinkClass, systemRegionLinkClass: config.regionLinkClass, - systemTypeLinkClass: config.typeLinkClass - + systemTypeLinkClass: config.typeLinkClass, + systemUrlLinkClass: config.urlLinkClass }; Render.showModule(moduleConfig, moduleData); diff --git a/public/js/v1.4.1/app/util.js b/public/js/v1.4.1/app/util.js index bf7b7242..1a5065c2 100644 --- a/public/js/v1.4.1/app/util.js +++ b/public/js/v1.4.1/app/util.js @@ -1560,7 +1560,7 @@ define([ if(mapId){ // search for a specific tab element mapTabElements = mapTabElements.filter(function(i, el){ - return ( $(el).data('map-id') === mapId ); + return ( $(el).data('mapId') === mapId ); }); } @@ -2398,6 +2398,100 @@ define([ }); }; + + /** + * write clipboard text + * @param text + * @returns {Promise} + */ + let copyToClipboard = (text) => { + + let copyToClipboardExecutor = (resolve, reject) => { + let payload = { + action: 'copyToClipboard', + data: false + }; + + if (navigator.clipboard) { + // get current permission status + navigator.permissions.query({ + name: 'clipboard-write' + }).then(permissionStatus => { + // will be 'granted', 'denied' or 'prompt' + if( + permissionStatus.state === 'granted' || + permissionStatus.state === 'prompt' + ){ + navigator.clipboard.writeText(text) + .then(() => { + payload.data = true; + resolve(payload); }) + .catch(err => { + let errorMsg = 'Failed to write clipboard content'; + console.error(errorMsg, err); + showNotify({title: 'Clipboard API', text: errorMsg, type: 'error'}); + resolve(payload); + }); + }else{ + showNotify({title: 'Clipboard API', text: 'You denied write access', type: 'warning'}); + resolve(payload); + } + }); + } else { + console.warn('Clipboard API not supported by your browser'); + resolve(payload); + } + }; + + return new Promise(copyToClipboardExecutor); + }; + + /** + * read clipboard text + * @returns {Promise} + */ + let readFromClipboard = () => { + + let readFromClipboardExecutor = (resolve, reject) => { + let payload = { + action: 'readFromClipboard', + data: false + }; + + if (navigator.clipboard) { + // get current permission status + navigator.permissions.query({ + name: 'clipboard-read' + }).then(permissionStatus => { + // will be 'granted', 'denied' or 'prompt' + if( + permissionStatus.state === 'granted' || + permissionStatus.state === 'prompt' + ){ + navigator.clipboard.readText() + .then(text => { + payload.data = text; + resolve(payload); }) + .catch(err => { + let errorMsg = 'Failed to read clipboard content'; + console.error(errorMsg, err); + showNotify({title: 'Clipboard API', text: errorMsg, type: 'error'}); + resolve(payload); + }); + }else{ + showNotify({title: 'Clipboard API', text: 'You denied read access', type: 'warning'}); + resolve(payload); + } + }); + } else { + console.warn('Clipboard API not supported by your browser'); + resolve(payload); + } + }; + + return new Promise(readFromClipboardExecutor); + }; + /** * set currentSystemData as "global" variable * @param systemData @@ -2786,6 +2880,8 @@ define([ getNearBySystemData: getNearBySystemData, getNearByCharacterData: getNearByCharacterData, setDestination: setDestination, + copyToClipboard: copyToClipboard, + readFromClipboard: readFromClipboard, convertDateToUTC: convertDateToUTC, convertDateToString: convertDateToString, getOpenDialogs: getOpenDialogs, diff --git a/public/templates/dialog/system_rally.html b/public/templates/dialog/system_rally.html index 45f00b30..db15a8bc 100644 --- a/public/templates/dialog/system_rally.html +++ b/public/templates/dialog/system_rally.html @@ -32,11 +32,14 @@
-
+
-
+
+ Attach deeplink: + {{systemUrl}} +
diff --git a/public/templates/modules/system_info.html b/public/templates/modules/system_info.html index 387fb72a..3cdb3bef 100644 --- a/public/templates/modules/system_info.html +++ b/public/templates/modules/system_info.html @@ -1,5 +1,5 @@
-
+
{{#system.alias}} {{system.alias}} {{/system.alias}} @@ -10,25 +10,26 @@
-
{{systemTypeName}} 
-
{{system.region.name}} 
+
{{systemTypeName}} 
+
{{system.region.name}} 
{{system.constellation.name}} 
- {{system.name}} + {{system.name}} + {{#system.locked}} - + {{/system.locked}}
{{#systemIsWormhole}} - + {{/systemIsWormhole}} {{^systemIsWormhole}} - + {{/systemIsWormhole}} {{#systemIsWormhole}} - + {{/systemIsWormhole}}
@@ -37,7 +38,7 @@ {{! info text ================================================================================================ }}
- +
@@ -48,7 +49,7 @@ {{! info table ================================================================================================ }}
- + {{systemStatusLabel}} @@ -60,7 +61,7 @@ Name {{#system.shattered}} -   +   {{/system.shattered}} {{system.name}}