- new "keyboard shortcuts", closed #456

- new "shortcuts" dialog
This commit is contained in:
Exodus4D
2017-03-11 13:05:28 +01:00
parent a8be8c202f
commit 77f1f4c72e
31 changed files with 1975 additions and 746 deletions

442
js/app/key.js Normal file
View File

@@ -0,0 +1,442 @@
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 console.log() information
* @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.
document.body.addEventListener ('DOMNodeRemoved', function(e){
if(typeof e.target.getAttribute === 'function'){
let eventNames = e.target.getAttribute(dataKeyEvents);
if(eventNames){
eventNames.split(',').forEach((event) => {
let index = allEvents[event].elements.indexOf(e.target);
if(index > -1){
// remove element from event list
allEvents[event].elements.splice(index, 1);
}
});
}
}
}, false);
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

@@ -22,10 +22,6 @@ define([
let config = {
zIndexCounter: 110,
newSystemOffset: {
x: 130,
y: 0
},
mapSnapToGrid: false, // "Snap to Grid" feature for drag&drop systems on map (optional)
mapWrapperClass: 'pf-map-wrapper', // wrapper div (scrollable)
@@ -1372,6 +1368,205 @@ define([
}
};
/**
* save a new system and add it to the map
* @param map
* @param requestData
* @param sourceSystem
* @param callback
*/
let saveSystem = function(map, requestData, sourceSystem, callback){
$.ajax({
type: 'POST',
url: Init.path.saveSystem,
data: requestData,
dataType: 'json',
context: {
map: map,
sourceSystem: sourceSystem
}
}).done(function(newSystemData){
Util.showNotify({title: 'New system', text: newSystemData.name, type: 'success'});
// draw new system to map
drawSystem(this.map, newSystemData, this.sourceSystem);
// re/arrange systems (prevent overlapping)
MagnetizerWrapper.setElements(this.map);
if(callback){
callback();
}
}).fail(function( jqXHR, status, error) {
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': saveSystem', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
});
};
/**
* open "new system" dialog and add the system to map
* optional the new system is connected to a "sourceSystem" (if available)
*
* @param map
* @param options
*/
let showNewSystemDialog = function(map, options){
let mapContainer = $(map.getContainer());
// format system status for form select -------------------------------------------------------------
let systemStatus = {};
// "default" selection (id = 0) prevents status from being overwritten
// -> e.g. keep status information if system was just inactive (active = 0)
systemStatus[0] = 'default';
$.each(Init.systemStatus, function(status, statusData){
systemStatus[statusData.id] = statusData.label;
});
// default system status -> first status entry
let defaultSystemStatus = 0;
// get current map data -----------------------------------------------------------------------------
let mapData = mapContainer.getMapDataFromClient({forceData: true});
let mapSystems = mapData.data.systems;
let mapSystemCount = mapSystems.length;
let mapTypeName = mapContainer.data('typeName');
let maxAllowedSystems = Init.mapTypes[mapTypeName].defaultConfig.max_systems;
// show error if system max count reached -----------------------------------------------------------
if(mapSystemCount >= maxAllowedSystems){
Util.showNotify({title: 'Max system count exceeded', text: 'Limit of ' + maxAllowedSystems + ' systems reached', type: 'warning'});
return;
}
// disable systems that are already on it -----------------------------------------------------------
let mapSystemIds = [];
for(let i = 0; i < mapSystems.length; i++ ){
mapSystemIds.push( mapSystems[i].systemId );
}
// dialog data --------------------------------------------------------------------------------------
let data = {
id: config.systemDialogId,
selectClass: config.systemDialogSelectClass
};
// set current position as "default" system to add --------------------------------------------------
let currentCharacterLog = Util.getCurrentCharacterLog();
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;
}
requirejs(['text!templates/dialog/system.html', 'mustache'], function(template, Mustache) {
let content = Mustache.render(template, data);
let systemDialog = bootbox.dialog({
title: 'Add new system',
message: content,
buttons: {
close: {
label: 'cancel',
className: 'btn-default'
},
success: {
label: '<i class="fa fa-fw fa-check"></i> save',
className: 'btn-success',
callback: function (e) {
// get form Values
let form = $('#' + config.systemDialogId).find('form');
let systemDialogData = $(form).getFormValues();
// validate form
form.validator('validate');
// check whether the form is valid
let formValid = form.isValidForm();
if(formValid === false){
// don't close dialog
return false;
}
// calculate new system position ------------------------------------------------
let newPosition = {
x: 0,
y: 0
};
let sourceSystem = null;
// add new position
if(options.sourceSystem !== undefined){
sourceSystem = options.sourceSystem;
// get new position
newPosition = System.calculateNewSystemPosition(sourceSystem);
}else{
// check mouse cursor position (add system to map)
newPosition = {
x: options.position.x,
y: options.position.y
};
}
systemDialogData.position = newPosition;
// ------------------------------------------------------------------------------
let requestData = {
systemData: systemDialogData,
mapData: {
id: mapContainer.data('id')
}
};
saveSystem(map, requestData, sourceSystem, function(){
bootbox.hideAll();
});
return false;
}
}
}
});
// init dialog
systemDialog.on('shown.bs.modal', function(e) {
let modalContent = $('#' + config.systemDialogId);
// init system select live search - some delay until modal transition has finished
let selectElement = modalContent.find('.' + config.systemDialogSelectClass);
selectElement.delay(240).initSystemSelect({
key: 'systemId',
disabledOptions: mapSystemIds
});
});
// init system status select
let modalFields = $('.bootbox .modal-dialog').find('.pf-editable-system-status');
modalFields.editable({
mode: 'inline',
emptytext: 'unknown',
onblur: 'submit',
showbuttons: false,
source: systemStatus,
value: defaultSystemStatus,
inputclass: config.systemDialogSelectClass
});
});
};
/**
* make a system name/alias editable by x-editable
* @param system
@@ -1806,169 +2001,6 @@ define([
return activeOptions;
};
/**
* open "new system" dialog and add the system to map
* optional the new system is connected to a "sourceSystem" (if available)
*
* @param map
* @param options
*/
let showNewSystemDialog = function(map, options){
let mapContainer = $(map.getContainer());
// format system status for form select -------------------------------------------------------------
let systemStatus = {};
// "default" selection (id = 0) prevents status from being overwritten
// -> e.g. keep status information if system was just inactive (active = 0)
systemStatus[0] = 'default';
$.each(Init.systemStatus, function(status, statusData){
systemStatus[statusData.id] = statusData.label;
});
// default system status -> first status entry
let defaultSystemStatus = 0;
// get current map data -----------------------------------------------------------------------------
let mapData = mapContainer.getMapDataFromClient({forceData: true});
let mapSystems = mapData.data.systems;
let mapSystemCount = mapSystems.length;
let mapTypeName = mapContainer.data('typeName');
let maxAllowedSystems = Init.mapTypes[mapTypeName].defaultConfig.max_systems;
// show error if system max count reached -----------------------------------------------------------
if(mapSystemCount >= maxAllowedSystems){
Util.showNotify({title: 'Max system count exceeded', text: 'Limit of ' + maxAllowedSystems + ' systems reached', type: 'warning'});
return;
}
// disable systems that are already on it -----------------------------------------------------------
let mapSystemIds = [];
for(let i = 0; i < mapSystems.length; i++ ){
mapSystemIds.push( mapSystems[i].systemId );
}
// dialog data --------------------------------------------------------------------------------------
let data = {
id: config.systemDialogId,
selectClass: config.systemDialogSelectClass
};
// set current position as "default" system to add --------------------------------------------------
let currentCharacterLog = Util.getCurrentCharacterLog();
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;
}
requirejs(['text!templates/dialog/system.html', 'mustache'], function(template, Mustache) {
let content = Mustache.render(template, data);
let systemDialog = bootbox.dialog({
title: 'Add new system',
message: content,
buttons: {
close: {
label: 'cancel',
className: 'btn-default'
},
success: {
label: '<i class="fa fa-fw fa-check"></i> save',
className: 'btn-success',
callback: function (e) {
// get form Values
let form = $('#' + config.systemDialogId).find('form');
let systemDialogData = $(form).getFormValues();
// validate form
form.validator('validate');
// check whether the form is valid
let formValid = form.isValidForm();
if(formValid === false){
// don't close dialog
return false;
}
// calculate new system position ------------------------------------------------
let newPosition = {
x: 0,
y: 0
};
let sourceSystem = null;
// add new position
if(options.sourceSystem !== undefined){
sourceSystem = options.sourceSystem;
// get new position
newPosition = calculateNewSystemPosition(sourceSystem);
}else{
// check mouse cursor position (add system to map)
newPosition = {
x: options.position.x,
y: options.position.y
};
}
systemDialogData.position = newPosition;
// ------------------------------------------------------------------------------
let requestData = {
systemData: systemDialogData,
mapData: {
id: mapContainer.data('id')
}
};
saveSystem(map, requestData, sourceSystem, function(){
bootbox.hideAll();
});
return false;
}
}
}
});
// init dialog
systemDialog.on('shown.bs.modal', function(e) {
let modalContent = $('#' + config.systemDialogId);
// init system select live search - some delay until modal transition has finished
let selectElement = modalContent.find('.' + config.systemDialogSelectClass);
selectElement.delay(240).initSystemSelect({
key: 'systemId',
disabledOptions: mapSystemIds
});
});
// init system status select
let modalFields = $('.bootbox .modal-dialog').find('.pf-editable-system-status');
modalFields.editable({
mode: 'inline',
emptytext: 'unknown',
onblur: 'submit',
showbuttons: false,
source: systemStatus,
value: defaultSystemStatus,
inputclass: config.systemDialogSelectClass
});
});
};
/**
* set up all actions that can be preformed on a system
* @param map
@@ -2354,6 +2386,28 @@ define([
$(tabContentElement).trigger('pf:drawSystemModules');
};
/**
* select all (selectable) systems on a mapElement
*/
$.fn.selectAllSystems = function(){
return this.each(function(){
let mapElement = $(this);
let map = getMapInstance(mapElement.data('id'));
let allSystems = mapElement.find('.' + config.systemClass + ':not(.' + config.systemSelectedClass + ')');
// filter non-locked systems
allSystems = allSystems.filter(function(i, el){
return ( $(el).data('locked') !== true );
});
allSystems.toggleSelectSystem(map);
Util.showNotify({title: allSystems.length + ' systems selected', type: 'success'});
});
};
/**
* toggle selectable status of a system
*/
@@ -2376,15 +2430,6 @@ define([
});
};
/**
* get all selected (NOT active) systems in a map
* @returns {*}
*/
$.fn.getSelectedSystems = function(){
let mapElement = $(this);
return mapElement.find('.' + config.systemSelectedClass);
};
/**
* toggle log status of a system
* @param poke
@@ -2613,17 +2658,7 @@ define([
showNewSystemDialog(currentMap, {position: position});
break;
case 'select_all':
let allSystems = currentMapElement.find('.' + config.systemClass + ':not(.' + config.systemSelectedClass + ')');
// filter non-locked systems
allSystems = allSystems.filter(function(i, el){
return ( $(el).data('locked') !== true );
});
allSystems.toggleSelectSystem(currentMap);
Util.showNotify({title: allSystems.length + ' systems selected', type: 'success'});
currentMapElement.selectAllSystems();
break;
case 'filter_wh':
case 'filter_stargate':
@@ -2890,65 +2925,6 @@ define([
}
};
/**
* save a new system and add it to the map
* @param map
* @param requestData
* @param sourceSystem
* @param callback
*/
let saveSystem = function(map, requestData, sourceSystem, callback){
$.ajax({
type: 'POST',
url: Init.path.saveSystem,
data: requestData,
dataType: 'json',
context: {
map: map,
sourceSystem: sourceSystem
}
}).done(function(newSystemData){
Util.showNotify({title: 'New system', text: newSystemData.name, type: 'success'});
// draw new system to map
drawSystem(this.map, newSystemData, this.sourceSystem);
// re/arrange systems (prevent overlapping)
MagnetizerWrapper.setElements(this.map);
if(callback){
callback();
}
}).fail(function( jqXHR, status, error) {
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': saveSystem', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
});
};
/**
* calculate the x/y coordinates for a new system - relativ to a source system
* @param sourceSystem
* @returns {{x: *, y: *}}
*/
let calculateNewSystemPosition = function(sourceSystem){
// 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) );
let newPosition = {
x: currentX + config.newSystemOffset.x,
y: currentY + config.newSystemOffset.y
};
return newPosition;
};
/**
* updates all systems on map with current user Data (all users on this map)
* update the Data of the user that is currently viewing the map (if available)
@@ -3244,9 +3220,13 @@ define([
// init custom scrollbars and add overlay
parentElement.initMapScrollbar();
let mapElement = $(mapConfig.map.getContainer());
// set shortcuts
parentElement.find('.' + config.mapWrapperClass).setMapShortcuts();
// show static overlay actions
let mapElement = mapConfig.map.getContainer();
let mapOverlay = $(mapElement).getMapOverlay('info');
let mapOverlay = mapElement.getMapOverlay('info');
mapOverlay.updateOverlayIcon('systemRegion', 'show');
mapOverlay.updateOverlayIcon('connection', 'show');
mapOverlay.updateOverlayIcon('connectionEol', 'show');
@@ -3344,7 +3324,8 @@ define([
return {
getMapInstance: getMapInstance,
clearMapInstance: clearMapInstance,
getDataByConnection: getDataByConnection
getDataByConnection: getDataByConnection,
showNewSystemDialog: showNewSystemDialog
};
});

View File

@@ -13,6 +13,11 @@ define([
'use strict';
let config = {
newSystemOffset: {
x: 130,
y: 0
},
systemActiveClass: 'pf-system-active' // class for an active system in a map
};
@@ -193,8 +198,32 @@ define([
}
};
/**
* calculate the x/y coordinates for a new system - relativ to a source system
* @param sourceSystem
* @returns {{x: *, y: *}}
*/
let calculateNewSystemPosition = function(sourceSystem){
// 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) );
let newPosition = {
x: currentX + config.newSystemOffset.x,
y: currentY + config.newSystemOffset.y
};
return newPosition;
};
return {
deleteSystems: deleteSystems,
removeSystems: removeSystems
removeSystems: removeSystems,
calculateNewSystemPosition: calculateNewSystemPosition
};
});

View File

@@ -5,8 +5,9 @@
define([
'jquery',
'app/init',
'app/util'
], function($, Init, Util) {
'app/util',
'bootbox'
], function($, Init, Util, bootbox) {
'use strict';
let config = {
@@ -17,9 +18,12 @@ define([
mapLocalStoragePrefix: 'map_', // prefix for map data local storage key
mapTabContentClass: 'pf-map-tab-content', // Tab-Content element (parent element)
mapClass: 'pf-map', // class for all maps
mapGridClass: 'pf-grid-small', // class for map grid snapping
systemIdPrefix: 'pf-system-', // id prefix for a system
systemClass: 'pf-system', // class for all systems
mapGridClass: 'pf-grid-small' // class for map grid snapping
systemSelectedClass: 'pf-system-selected' // class for selected systems in a map
};
// map menu options
@@ -198,6 +202,15 @@ define([
return this.find('.' + config.systemClass);
};
/**
* get all selected (NOT active) systems in a map
* @returns {*}
*/
$.fn.getSelectedSystems = function(){
let mapElement = $(this);
return mapElement.find('.' + config.systemSelectedClass);
};
/**
* search connections by systems
* @param {Object} map - jsPlumb
@@ -577,6 +590,40 @@ define([
});
};
/**
* set map "shortcut" events
*/
$.fn.setMapShortcuts = function(){
return this.each((i, mapWrapper) => {
mapWrapper = $(mapWrapper);
let mapElement = mapWrapper.findMapElement();
// dynamic require Map module -> otherwise there is a require(), loop
let Map = require('app/map/map');
let map = Map.getMapInstance( mapElement.data('id'));
mapWrapper.watchKey('mapSystemAdd', (mapWrapper) => {
console.log('mapSystemAdd');
Map.showNewSystemDialog(map, {position: {x: 0, y: 0}});
},{focus: true});
mapWrapper.watchKey('mapSystemsSelect', (mapWrapper) => {
mapElement.selectAllSystems();
},{focus: true});
mapWrapper.watchKey('mapSystemsDelete', (mapWrapper) => {
console.log('mapSystemsDelete');
let selectedSystems = mapElement.getSelectedSystems();
$.fn.showDeleteSystemDialog(map, selectedSystems);
},{focus: true});
});
};
$.fn.findMapElement = function(){
return $(this).find('.' + config.mapClass);
};
/**
* get systemId string (selector
* @param mapId

View File

@@ -10,6 +10,7 @@ define([
'app/logging',
'app/page',
'app/map/worker',
'app/key',
'app/ui/form_element',
'app/module_map'
], ($, Init, Util, Render, Logging, Page, MapWorker) => {
@@ -30,7 +31,7 @@ define([
// load page
// load info (maintenance) info panel (if scheduled)
$('body').loadPageStructure();
$('body').loadPageStructure().setGlobalShortcuts();
// show app information in browser console
Util.showVersionInfo();

View File

@@ -16,6 +16,7 @@ define([
'dialog/map_info',
'dialog/account_settings',
'dialog/manual',
'dialog/shortcuts',
'dialog/map_settings',
'dialog/system_effects',
'dialog/jump_info',
@@ -62,7 +63,10 @@ define([
menuHeadMenuLogoClass: 'pf-head-menu-logo', // class for main menu logo
// helper element
dynamicElementWrapperId: 'pf-dialog-wrapper'
dynamicElementWrapperId: 'pf-dialog-wrapper',
// system signature module
systemSigModuleClass: 'pf-sig-table-module', // module wrapper (signatures)
};
let programStatusCounter = 0; // current count down in s until next status change is possible
@@ -74,49 +78,83 @@ define([
* @returns {*|jQuery|HTMLElement}
*/
$.fn.loadPageStructure = function(){
let body = $(this);
return this.each((i, body) => {
body = $(body);
// menu left
body.prepend(
$('<div>', {
class: [config.pageSlidebarClass, config.pageSlidebarLeftClass, 'sb-style-push', 'sb-width-custom'].join(' ')
}).attr('data-sb-width', config.pageSlideLeftWidth)
);
// menu right
body.prepend(
$('<div>', {
class: [config.pageSlidebarClass, config.pageSlidebarRightClass, 'sb-style-push', 'sb-width-custom'].join(' ')
}).attr('data-sb-width', config.pageSlideRightWidth)
);
// main page
body.prepend(
$('<div>', {
id: config.pageId,
class: config.pageClass
}).append(
Util.getMapModule()
).append(
// menu left
body.prepend(
$('<div>', {
id: config.dynamicElementWrapperId
})
)
);
class: [config.pageSlidebarClass, config.pageSlidebarLeftClass, 'sb-style-push', 'sb-width-custom'].join(' ')
}).attr('data-sb-width', config.pageSlideLeftWidth)
);
// load header / footer
$('.' + config.pageClass).loadHeader().loadFooter();
// menu right
body.prepend(
$('<div>', {
class: [config.pageSlidebarClass, config.pageSlidebarRightClass, 'sb-style-push', 'sb-width-custom'].join(' ')
}).attr('data-sb-width', config.pageSlideRightWidth)
);
// load left menu
$('.' + config.pageSlidebarLeftClass).loadLeftMenu();
// main page
body.prepend(
$('<div>', {
id: config.pageId,
class: config.pageClass
}).append(
Util.getMapModule()
).append(
$('<div>', {
id: config.dynamicElementWrapperId
})
)
);
// load right menu
$('.' + config.pageSlidebarRightClass).loadRightMenu();
// load header / footer
$('.' + config.pageClass).loadHeader().loadFooter();
// set document observer for global events
setDocumentObserver();
// load left menu
$('.' + config.pageSlidebarLeftClass).loadLeftMenu();
return body;
// load right menu
$('.' + config.pageSlidebarRightClass).loadRightMenu();
// set document observer for global events
setDocumentObserver();
});
};
$.fn.setGlobalShortcuts = function(){
return this.each((i, body) => {
body = $(body);
console.log('setGlobalShortcuts');
body.watchKey('tabReload', (body) => {
location.reload();
});
body.watchKey('signaturePaste', (e) => {
let moduleElement = $('.' + config.systemSigModuleClass);
// check if there is a signature module active (system clicked)
if(moduleElement.length){
e = e.originalEvent;
let targetElement = $(e.target);
// do not read clipboard if pasting into form elements
if(
targetElement.prop('tagName').toLowerCase() !== 'input' &&
targetElement.prop('tagName').toLowerCase() !== 'textarea' || (
targetElement.is('input[type="search"]') // Datatables "search" field bubbles `paste.DT` event :(
)
){
let clipboard = (e.originalEvent || e).clipboardData.getData('text/plain');
moduleElement.trigger('pf:updateSystemSignatureModuleByClipboard', [clipboard]);
}
}
});
});
};
/**
@@ -355,6 +393,22 @@ define([
).on('click', function(){
$(document).triggerMenuEvent('Manual');
})
).append(
$('<a>', {
class: 'list-group-item list-group-item-info',
href: '#'
}).html('&nbsp;&nbsp;Shortcuts').prepend(
$('<i>',{
class: 'fa fa-keyboard-o fa-fw'
})
).append(
$('<span>',{
class: 'badge bg-color bg-color-gray txt-color txt-color-warning',
text: 'beta'
})
).on('click', function(){
$(document).triggerMenuEvent('Shortcuts');
})
).append(
$('<a>', {
class: 'list-group-item list-group-item-info',
@@ -586,6 +640,18 @@ define([
return false;
});
$(document).on('pf:menuShowTaskManager', function(e, data){
// show log dialog
Logging.showDialog();
return false;
});
$(document).on('pf:menuShortcuts', function(e, data){
// show shortcuts dialog
$.fn.showShortcutsDialog();
return false;
});
$(document).on('pf:menuShowSettingsDialog', function(e){
// show character select dialog
$.fn.showSettingsDialog();
@@ -626,12 +692,6 @@ define([
return false;
});
$(document).on('pf:menuShowTaskManager', function(e, data){
// show log dialog
Logging.showDialog();
return false;
});
$(document).on('pf:menuLogout', function(e, data){
let clearCookies = false;

View File

@@ -9,9 +9,10 @@ define([
'app/render',
'bootbox',
], function($, Init, Util, Render, bootbox) {
'use strict';
var config = {
let config = {
// jump info dialog
jumpInfoDialogClass: 'pf-jump-info-dialog' // class for jump info dialog
};
@@ -22,12 +23,10 @@ define([
$.fn.showJumpInfoDialog = function(){
requirejs(['text!templates/dialog/jump_info.html', 'mustache'], function(template, Mustache) {
let data = {};
let content = Mustache.render(template, data);
var data = {};
var content = Mustache.render(template, data);
var signatureReaderDialog = bootbox.dialog({
let signatureReaderDialog = bootbox.dialog({
className: config.jumpInfoDialogClass,
title: 'Wormhole jump information',
message: content

View File

@@ -12,7 +12,7 @@ define([
'use strict';
var config = {
let config = {
// global dialog
dialogNavigationClass: 'pf-dialog-navigation-list', // class for dialog navigation bar
dialogNavigationListItemClass: 'pf-dialog-navigation-list-item', // class for map manual li main navigation elements
@@ -28,7 +28,7 @@ define([
requirejs(['text!templates/dialog/map_manual.html', 'mustache'], function(template, Mustache) {
var data = {
let data = {
dialogNavigationClass: config.dialogNavigationClass,
dialogNavLiClass: config.dialogNavigationListItemClass,
scrollspyId: config.mapManualScrollspyId,
@@ -36,10 +36,10 @@ define([
mapCounterClass : Init.classes.pieChart.pieChartMapCounterClass
};
var content = Mustache.render(template, data);
let content = Mustache.render(template, data);
// show dialog
var mapManualDialog = bootbox.dialog({
let mapManualDialog = bootbox.dialog({
title: 'Manual',
message: content,
size: 'large',
@@ -56,15 +56,15 @@ define([
});
// modal offset top
var modalOffsetTop = 200;
let modalOffsetTop = 200;
// disable on scroll event
var disableOnScrollEvent = false;
let disableOnScrollEvent = false;
// scroll breakpoints
var scrolLBreakpointElements = null;
let scrolLBreakpointElements = null;
// scroll navigation links
var scrollNavLiElements = null;
let scrollNavLiElements = null;
mapManualDialog.on('shown.bs.modal', function(e) {
// modal on open
@@ -72,13 +72,13 @@ define([
scrollNavLiElements = $('.' + config.dialogNavigationListItemClass);
});
var scrollspyElement = $('#' + config.mapManualScrollspyId);
let scrollspyElement = $('#' + config.mapManualScrollspyId);
var whileScrolling = function(){
let whileScrolling = function(){
if(disableOnScrollEvent === false){
for(var i = 0; i < scrolLBreakpointElements.length; i++){
var offset = $(scrolLBreakpointElements[i]).offset().top;
for(let i = 0; i < scrolLBreakpointElements.length; i++){
let offset = $(scrolLBreakpointElements[i]).offset().top;
if( (offset - modalOffsetTop) > 0){
@@ -116,11 +116,11 @@ define([
scrollspyElement.find('.' + data.mapCounterClass).initMapUpdateCounter();
// set navigation button observer
var mainNavigationLinks = $('.' + config.dialogNavigationClass).find('a');
let mainNavigationLinks = $('.' + config.dialogNavigationClass).find('a');
// text anchor links
var subNavigationLinks = scrollspyElement.find('a[data-target]');
let subNavigationLinks = scrollspyElement.find('a[data-target]');
var navigationLinks = mainNavigationLinks.add(subNavigationLinks);
let navigationLinks = mainNavigationLinks.add(subNavigationLinks);
navigationLinks.on('click', function(e){
e.preventDefault();
@@ -130,7 +130,7 @@ define([
// scroll to anchor
scrollspyElement.mCustomScrollbar('scrollTo', $(this).attr('data-target'));
var mainNavigationLiElement = $(this).parent('.' + config.dialogNavigationListItemClass);
let mainNavigationLiElement = $(this).parent('.' + config.dialogNavigationListItemClass);
whileScrolling();

View File

@@ -12,7 +12,7 @@ define([
'use strict';
var config = {
let config = {
// shutdown dialog
notificationDialogId: 'pf-notification-dialog', // id for "notification" dialog
@@ -23,8 +23,8 @@ define([
* show/animate dialog page content
* @param dialog
*/
var showPageContent = function(dialog){
var headlineElement = dialog.find('h1');
let showPageContent = function(dialog){
let headlineElement = dialog.find('h1');
headlineElement.delay(300).velocity('transition.shrinkIn', {
duration: 500
@@ -45,7 +45,7 @@ define([
$.fn.showNotificationDialog = function(dialogData){
// check if there is already a notification dialog open
var notificationDialogClassDialoges = $('.' + config.notificationDialogClass);
let notificationDialogClassDialoges = $('.' + config.notificationDialogClass);
if(notificationDialogClassDialoges.length === 0){
@@ -54,15 +54,15 @@ define([
requirejs(['text!templates/dialog/notification.html', 'mustache'], function(template, Mustache) {
var data = {
let data = {
id: config.notificationDialogId,
content: dialogData.content
};
var content = Mustache.render(template, data);
let content = Mustache.render(template, data);
// show dialog
var shutdownDialog = bootbox.dialog({
let shutdownDialog = bootbox.dialog({
title: dialogData.content.title,
message: content,
className: config.notificationDialogClass,
@@ -72,7 +72,7 @@ define([
shutdownDialog.on('shown.bs.modal', function(e) {
// remove close button
var dialog = $(this);
let dialog = $(this);
dialog.find('.bootbox-close-button').remove();
dialog.find('button').blur();

View File

@@ -0,0 +1,50 @@
/**
* shortcuts dialog
*/
define([
'jquery',
'app/init',
'app/util',
'app/render',
'bootbox',
'app/key',
], function($, Init, Util, Render, bootbox, Key) {
'use strict';
let config = {
// map dialog
shortcutsDialogId: 'pf-shortcuts-dialog', // id for shortcuts dialog
};
/**
* shows the map manual modal dialog
*/
$.fn.showShortcutsDialog = function(){
requirejs(['text!templates/dialog/shortcuts.html', 'mustache'], function(template, Mustache){
let data = {
id: config.shortcutsDialogId,
shortcuts: Key.getGroupedShortcuts()
};
let content = Mustache.render(template, data);
// show dialog
let shortcutsDialog = bootbox.dialog({
title: 'Keyboard Shortcuts',
message: content,
size: 'large',
buttons: {
success: {
label: 'close',
className: 'btn-default'
}
},
show: true
});
});
};
});

View File

@@ -13,12 +13,12 @@ define([
], function($, Init, Util, Render, bootbox, MapUtil) {
'use strict';
var config = {
let config = {
// system effect dialog
systemEffectDialogWrapperClass: 'pf-system-effect-dialog-wrapper' // class for system effect dialog
};
var cache = {
let cache = {
systemEffectDialog: false // system effect info dialog
};
@@ -30,31 +30,31 @@ define([
// cache table structure
if(!cache.systemEffectDialog){
var dialogWrapperElement = $('<div>', {
let dialogWrapperElement = $('<div>', {
class: config.systemEffectDialogWrapperClass
});
var systemEffectData = Util.getSystemEffectData();
let systemEffectData = Util.getSystemEffectData();
$.each( systemEffectData.wh, function( effectName, effectData ) {
var table = $('<table>', {
let table = $('<table>', {
class: ['table', 'table-condensed'].join(' ')
});
var tbody = $('<tbody>');
var thead = $('<thead>');
let tbody = $('<tbody>');
let thead = $('<thead>');
var rows = [];
let rows = [];
// get formatted system effect name
var systemEffectName = MapUtil.getEffectInfoForSystem(effectName, 'name');
var systemEffectClass = MapUtil.getEffectInfoForSystem(effectName, 'class');
let systemEffectName = MapUtil.getEffectInfoForSystem(effectName, 'name');
let systemEffectClass = MapUtil.getEffectInfoForSystem(effectName, 'class');
$.each( effectData, function( areaId, areaData ) {
var systemType = 'C' + areaId;
var securityClass = Util.getSecurityClassForSystem( systemType );
let systemType = 'C' + areaId;
let securityClass = Util.getSecurityClassForSystem( systemType );
if(areaId === '1'){
rows.push( $('<tr>') );

View File

@@ -15,7 +15,7 @@ define([
* init a select element as "select2" for map selection
*/
$.fn.initMapSelect = function(){
var selectElement = $(this);
let selectElement = $(this);
$.when(
selectElement.select2({
@@ -31,9 +31,9 @@ define([
* @param options
*/
$.fn.initSystemSelect = function(options){
var selectElement = $(this);
let selectElement = $(this);
var config = {
let config = {
maxSelectionLength: 1
};
options = $.extend({}, config, options);
@@ -46,12 +46,12 @@ define([
}
// show effect info just for wormholes
var hideEffectClass = '';
let hideEffectClass = '';
if(data.effect === ''){
hideEffectClass = 'hide';
}
var markup = '<div class="clearfix">';
let markup = '<div class="clearfix">';
markup += '<div class="col-sm-5 pf-select-item-anchor">' + data.text + '</div>';
markup += '<div class="col-sm-2 text-right ' + data.effectClass + '">';
markup += '<i class="fa fa-fw fa-square ' + hideEffectClass + '"></i>';
@@ -83,12 +83,12 @@ define([
results: data.map( function(item){
// "systemId" or "name"
var id = item[options.key];
var disabled = false;
var trueSec = parseFloat(item.trueSec);
var secClass = Util.getSecurityClassForSystem(item.security);
var trueSecClass = Util.getTrueSecClassForSystem( trueSec );
var effectClass = MapUtil.getEffectInfoForSystem(item.effect, 'class');
let id = item[options.key];
let disabled = false;
let trueSec = parseFloat(item.trueSec);
let secClass = Util.getSecurityClassForSystem(item.security);
let trueSecClass = Util.getTrueSecClassForSystem( trueSec );
let effectClass = MapUtil.getEffectInfoForSystem(item.effect, 'class');
// check if system is dialed
if(
@@ -126,7 +126,7 @@ define([
error: function (jqXHR, status, error) {
if( !Util.isXHRAborted(jqXHR) ){
var reason = status + ' ' + jqXHR.status + ': ' + error;
let reason = status + ' ' + jqXHR.status + ': ' + error;
Util.showNotify({title: 'System select warning', text: reason + ' deleted', type: 'warning'});
}
@@ -161,7 +161,7 @@ define([
return this.each(function(){
var selectElement = $(this);
let selectElement = $(this);
// format result data
function formatResultData (data) {
@@ -172,7 +172,7 @@ define([
// check if an option is already selected
// do not show the same result twice
var currentValues = selectElement.val();
let currentValues = selectElement.val();
if(
currentValues &&
@@ -181,8 +181,8 @@ define([
return ;
}
var imagePath = '';
var previewContent = '';
let imagePath = '';
let previewContent = '';
switch(options.type){
case 'character':
@@ -199,7 +199,7 @@ define([
break;
}
var markup = '<div class="clearfix">';
let markup = '<div class="clearfix">';
markup += '<div class="col-sm-2">' + previewContent + '</div>';
markup += '<div class="col-sm-10">' + data.text + '</div></div>';
@@ -213,7 +213,7 @@ define([
return data.text;
}
var markup = '<div class="clearfix">';
let markup = '<div class="clearfix">';
markup += '<div class="col-sm-10">' + data.text + '</div></div>';
return markup;
@@ -248,7 +248,7 @@ define([
error: function (jqXHR, status, error) {
if( !Util.isXHRAborted(jqXHR) ){
var reason = status + ' ' + jqXHR.status + ': ' + error;
let reason = status + ' ' + jqXHR.status + ': ' + error;
Util.showNotify({title: 'Access select warning', text: reason + ' deleted', type: 'warning'});
}

View File

@@ -2206,20 +2206,8 @@ define([
});
// event listener for global "paste" signatures into the page -------------------------------------------------
$('body').off('paste').on('paste', function(e){
let targetElement = $(e.target);
// do not read clipboard if pasting into form elements
if(
targetElement.prop('tagName').toLowerCase() !== 'input' &&
targetElement.prop('tagName').toLowerCase() !== 'textarea' || (
targetElement.is('input[type="search"]') // Datatables "search" field bubbles `paste.DT` event :(
)
){
let clipboard = (e.originalEvent || e).clipboardData.getData('text/plain');
moduleElement.updateSignatureTableByClipboard(systemData, clipboard, {});
}
moduleElement.on('pf:updateSystemSignatureModuleByClipboard', function(e, clipboard){
$(this).updateSignatureTableByClipboard(systemData, clipboard, {});
});
};

File diff suppressed because one or more lines are too long

442
public/js/v1.2.1/app/key.js Normal file
View File

@@ -0,0 +1,442 @@
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 console.log() information
* @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.
document.body.addEventListener ('DOMNodeRemoved', function(e){
if(typeof e.target.getAttribute === 'function'){
let eventNames = e.target.getAttribute(dataKeyEvents);
if(eventNames){
eventNames.split(',').forEach((event) => {
let index = allEvents[event].elements.indexOf(e.target);
if(index > -1){
// remove element from event list
allEvents[event].elements.splice(index, 1);
}
});
}
}
}, false);
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

@@ -22,10 +22,6 @@ define([
let config = {
zIndexCounter: 110,
newSystemOffset: {
x: 130,
y: 0
},
mapSnapToGrid: false, // "Snap to Grid" feature for drag&drop systems on map (optional)
mapWrapperClass: 'pf-map-wrapper', // wrapper div (scrollable)
@@ -1372,6 +1368,205 @@ define([
}
};
/**
* save a new system and add it to the map
* @param map
* @param requestData
* @param sourceSystem
* @param callback
*/
let saveSystem = function(map, requestData, sourceSystem, callback){
$.ajax({
type: 'POST',
url: Init.path.saveSystem,
data: requestData,
dataType: 'json',
context: {
map: map,
sourceSystem: sourceSystem
}
}).done(function(newSystemData){
Util.showNotify({title: 'New system', text: newSystemData.name, type: 'success'});
// draw new system to map
drawSystem(this.map, newSystemData, this.sourceSystem);
// re/arrange systems (prevent overlapping)
MagnetizerWrapper.setElements(this.map);
if(callback){
callback();
}
}).fail(function( jqXHR, status, error) {
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': saveSystem', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
});
};
/**
* open "new system" dialog and add the system to map
* optional the new system is connected to a "sourceSystem" (if available)
*
* @param map
* @param options
*/
let showNewSystemDialog = function(map, options){
let mapContainer = $(map.getContainer());
// format system status for form select -------------------------------------------------------------
let systemStatus = {};
// "default" selection (id = 0) prevents status from being overwritten
// -> e.g. keep status information if system was just inactive (active = 0)
systemStatus[0] = 'default';
$.each(Init.systemStatus, function(status, statusData){
systemStatus[statusData.id] = statusData.label;
});
// default system status -> first status entry
let defaultSystemStatus = 0;
// get current map data -----------------------------------------------------------------------------
let mapData = mapContainer.getMapDataFromClient({forceData: true});
let mapSystems = mapData.data.systems;
let mapSystemCount = mapSystems.length;
let mapTypeName = mapContainer.data('typeName');
let maxAllowedSystems = Init.mapTypes[mapTypeName].defaultConfig.max_systems;
// show error if system max count reached -----------------------------------------------------------
if(mapSystemCount >= maxAllowedSystems){
Util.showNotify({title: 'Max system count exceeded', text: 'Limit of ' + maxAllowedSystems + ' systems reached', type: 'warning'});
return;
}
// disable systems that are already on it -----------------------------------------------------------
let mapSystemIds = [];
for(let i = 0; i < mapSystems.length; i++ ){
mapSystemIds.push( mapSystems[i].systemId );
}
// dialog data --------------------------------------------------------------------------------------
let data = {
id: config.systemDialogId,
selectClass: config.systemDialogSelectClass
};
// set current position as "default" system to add --------------------------------------------------
let currentCharacterLog = Util.getCurrentCharacterLog();
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;
}
requirejs(['text!templates/dialog/system.html', 'mustache'], function(template, Mustache) {
let content = Mustache.render(template, data);
let systemDialog = bootbox.dialog({
title: 'Add new system',
message: content,
buttons: {
close: {
label: 'cancel',
className: 'btn-default'
},
success: {
label: '<i class="fa fa-fw fa-check"></i> save',
className: 'btn-success',
callback: function (e) {
// get form Values
let form = $('#' + config.systemDialogId).find('form');
let systemDialogData = $(form).getFormValues();
// validate form
form.validator('validate');
// check whether the form is valid
let formValid = form.isValidForm();
if(formValid === false){
// don't close dialog
return false;
}
// calculate new system position ------------------------------------------------
let newPosition = {
x: 0,
y: 0
};
let sourceSystem = null;
// add new position
if(options.sourceSystem !== undefined){
sourceSystem = options.sourceSystem;
// get new position
newPosition = System.calculateNewSystemPosition(sourceSystem);
}else{
// check mouse cursor position (add system to map)
newPosition = {
x: options.position.x,
y: options.position.y
};
}
systemDialogData.position = newPosition;
// ------------------------------------------------------------------------------
let requestData = {
systemData: systemDialogData,
mapData: {
id: mapContainer.data('id')
}
};
saveSystem(map, requestData, sourceSystem, function(){
bootbox.hideAll();
});
return false;
}
}
}
});
// init dialog
systemDialog.on('shown.bs.modal', function(e) {
let modalContent = $('#' + config.systemDialogId);
// init system select live search - some delay until modal transition has finished
let selectElement = modalContent.find('.' + config.systemDialogSelectClass);
selectElement.delay(240).initSystemSelect({
key: 'systemId',
disabledOptions: mapSystemIds
});
});
// init system status select
let modalFields = $('.bootbox .modal-dialog').find('.pf-editable-system-status');
modalFields.editable({
mode: 'inline',
emptytext: 'unknown',
onblur: 'submit',
showbuttons: false,
source: systemStatus,
value: defaultSystemStatus,
inputclass: config.systemDialogSelectClass
});
});
};
/**
* make a system name/alias editable by x-editable
* @param system
@@ -1806,169 +2001,6 @@ define([
return activeOptions;
};
/**
* open "new system" dialog and add the system to map
* optional the new system is connected to a "sourceSystem" (if available)
*
* @param map
* @param options
*/
let showNewSystemDialog = function(map, options){
let mapContainer = $(map.getContainer());
// format system status for form select -------------------------------------------------------------
let systemStatus = {};
// "default" selection (id = 0) prevents status from being overwritten
// -> e.g. keep status information if system was just inactive (active = 0)
systemStatus[0] = 'default';
$.each(Init.systemStatus, function(status, statusData){
systemStatus[statusData.id] = statusData.label;
});
// default system status -> first status entry
let defaultSystemStatus = 0;
// get current map data -----------------------------------------------------------------------------
let mapData = mapContainer.getMapDataFromClient({forceData: true});
let mapSystems = mapData.data.systems;
let mapSystemCount = mapSystems.length;
let mapTypeName = mapContainer.data('typeName');
let maxAllowedSystems = Init.mapTypes[mapTypeName].defaultConfig.max_systems;
// show error if system max count reached -----------------------------------------------------------
if(mapSystemCount >= maxAllowedSystems){
Util.showNotify({title: 'Max system count exceeded', text: 'Limit of ' + maxAllowedSystems + ' systems reached', type: 'warning'});
return;
}
// disable systems that are already on it -----------------------------------------------------------
let mapSystemIds = [];
for(let i = 0; i < mapSystems.length; i++ ){
mapSystemIds.push( mapSystems[i].systemId );
}
// dialog data --------------------------------------------------------------------------------------
let data = {
id: config.systemDialogId,
selectClass: config.systemDialogSelectClass
};
// set current position as "default" system to add --------------------------------------------------
let currentCharacterLog = Util.getCurrentCharacterLog();
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;
}
requirejs(['text!templates/dialog/system.html', 'mustache'], function(template, Mustache) {
let content = Mustache.render(template, data);
let systemDialog = bootbox.dialog({
title: 'Add new system',
message: content,
buttons: {
close: {
label: 'cancel',
className: 'btn-default'
},
success: {
label: '<i class="fa fa-fw fa-check"></i> save',
className: 'btn-success',
callback: function (e) {
// get form Values
let form = $('#' + config.systemDialogId).find('form');
let systemDialogData = $(form).getFormValues();
// validate form
form.validator('validate');
// check whether the form is valid
let formValid = form.isValidForm();
if(formValid === false){
// don't close dialog
return false;
}
// calculate new system position ------------------------------------------------
let newPosition = {
x: 0,
y: 0
};
let sourceSystem = null;
// add new position
if(options.sourceSystem !== undefined){
sourceSystem = options.sourceSystem;
// get new position
newPosition = calculateNewSystemPosition(sourceSystem);
}else{
// check mouse cursor position (add system to map)
newPosition = {
x: options.position.x,
y: options.position.y
};
}
systemDialogData.position = newPosition;
// ------------------------------------------------------------------------------
let requestData = {
systemData: systemDialogData,
mapData: {
id: mapContainer.data('id')
}
};
saveSystem(map, requestData, sourceSystem, function(){
bootbox.hideAll();
});
return false;
}
}
}
});
// init dialog
systemDialog.on('shown.bs.modal', function(e) {
let modalContent = $('#' + config.systemDialogId);
// init system select live search - some delay until modal transition has finished
let selectElement = modalContent.find('.' + config.systemDialogSelectClass);
selectElement.delay(240).initSystemSelect({
key: 'systemId',
disabledOptions: mapSystemIds
});
});
// init system status select
let modalFields = $('.bootbox .modal-dialog').find('.pf-editable-system-status');
modalFields.editable({
mode: 'inline',
emptytext: 'unknown',
onblur: 'submit',
showbuttons: false,
source: systemStatus,
value: defaultSystemStatus,
inputclass: config.systemDialogSelectClass
});
});
};
/**
* set up all actions that can be preformed on a system
* @param map
@@ -2354,6 +2386,28 @@ define([
$(tabContentElement).trigger('pf:drawSystemModules');
};
/**
* select all (selectable) systems on a mapElement
*/
$.fn.selectAllSystems = function(){
return this.each(function(){
let mapElement = $(this);
let map = getMapInstance(mapElement.data('id'));
let allSystems = mapElement.find('.' + config.systemClass + ':not(.' + config.systemSelectedClass + ')');
// filter non-locked systems
allSystems = allSystems.filter(function(i, el){
return ( $(el).data('locked') !== true );
});
allSystems.toggleSelectSystem(map);
Util.showNotify({title: allSystems.length + ' systems selected', type: 'success'});
});
};
/**
* toggle selectable status of a system
*/
@@ -2376,15 +2430,6 @@ define([
});
};
/**
* get all selected (NOT active) systems in a map
* @returns {*}
*/
$.fn.getSelectedSystems = function(){
let mapElement = $(this);
return mapElement.find('.' + config.systemSelectedClass);
};
/**
* toggle log status of a system
* @param poke
@@ -2613,17 +2658,7 @@ define([
showNewSystemDialog(currentMap, {position: position});
break;
case 'select_all':
let allSystems = currentMapElement.find('.' + config.systemClass + ':not(.' + config.systemSelectedClass + ')');
// filter non-locked systems
allSystems = allSystems.filter(function(i, el){
return ( $(el).data('locked') !== true );
});
allSystems.toggleSelectSystem(currentMap);
Util.showNotify({title: allSystems.length + ' systems selected', type: 'success'});
currentMapElement.selectAllSystems();
break;
case 'filter_wh':
case 'filter_stargate':
@@ -2890,65 +2925,6 @@ define([
}
};
/**
* save a new system and add it to the map
* @param map
* @param requestData
* @param sourceSystem
* @param callback
*/
let saveSystem = function(map, requestData, sourceSystem, callback){
$.ajax({
type: 'POST',
url: Init.path.saveSystem,
data: requestData,
dataType: 'json',
context: {
map: map,
sourceSystem: sourceSystem
}
}).done(function(newSystemData){
Util.showNotify({title: 'New system', text: newSystemData.name, type: 'success'});
// draw new system to map
drawSystem(this.map, newSystemData, this.sourceSystem);
// re/arrange systems (prevent overlapping)
MagnetizerWrapper.setElements(this.map);
if(callback){
callback();
}
}).fail(function( jqXHR, status, error) {
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': saveSystem', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
});
};
/**
* calculate the x/y coordinates for a new system - relativ to a source system
* @param sourceSystem
* @returns {{x: *, y: *}}
*/
let calculateNewSystemPosition = function(sourceSystem){
// 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) );
let newPosition = {
x: currentX + config.newSystemOffset.x,
y: currentY + config.newSystemOffset.y
};
return newPosition;
};
/**
* updates all systems on map with current user Data (all users on this map)
* update the Data of the user that is currently viewing the map (if available)
@@ -3244,9 +3220,13 @@ define([
// init custom scrollbars and add overlay
parentElement.initMapScrollbar();
let mapElement = $(mapConfig.map.getContainer());
// set shortcuts
parentElement.find('.' + config.mapWrapperClass).setMapShortcuts();
// show static overlay actions
let mapElement = mapConfig.map.getContainer();
let mapOverlay = $(mapElement).getMapOverlay('info');
let mapOverlay = mapElement.getMapOverlay('info');
mapOverlay.updateOverlayIcon('systemRegion', 'show');
mapOverlay.updateOverlayIcon('connection', 'show');
mapOverlay.updateOverlayIcon('connectionEol', 'show');
@@ -3344,7 +3324,8 @@ define([
return {
getMapInstance: getMapInstance,
clearMapInstance: clearMapInstance,
getDataByConnection: getDataByConnection
getDataByConnection: getDataByConnection,
showNewSystemDialog: showNewSystemDialog
};
});

View File

@@ -13,6 +13,11 @@ define([
'use strict';
let config = {
newSystemOffset: {
x: 130,
y: 0
},
systemActiveClass: 'pf-system-active' // class for an active system in a map
};
@@ -193,8 +198,32 @@ define([
}
};
/**
* calculate the x/y coordinates for a new system - relativ to a source system
* @param sourceSystem
* @returns {{x: *, y: *}}
*/
let calculateNewSystemPosition = function(sourceSystem){
// 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) );
let newPosition = {
x: currentX + config.newSystemOffset.x,
y: currentY + config.newSystemOffset.y
};
return newPosition;
};
return {
deleteSystems: deleteSystems,
removeSystems: removeSystems
removeSystems: removeSystems,
calculateNewSystemPosition: calculateNewSystemPosition
};
});

View File

@@ -5,8 +5,9 @@
define([
'jquery',
'app/init',
'app/util'
], function($, Init, Util) {
'app/util',
'bootbox'
], function($, Init, Util, bootbox) {
'use strict';
let config = {
@@ -17,9 +18,12 @@ define([
mapLocalStoragePrefix: 'map_', // prefix for map data local storage key
mapTabContentClass: 'pf-map-tab-content', // Tab-Content element (parent element)
mapClass: 'pf-map', // class for all maps
mapGridClass: 'pf-grid-small', // class for map grid snapping
systemIdPrefix: 'pf-system-', // id prefix for a system
systemClass: 'pf-system', // class for all systems
mapGridClass: 'pf-grid-small' // class for map grid snapping
systemSelectedClass: 'pf-system-selected' // class for selected systems in a map
};
// map menu options
@@ -198,6 +202,15 @@ define([
return this.find('.' + config.systemClass);
};
/**
* get all selected (NOT active) systems in a map
* @returns {*}
*/
$.fn.getSelectedSystems = function(){
let mapElement = $(this);
return mapElement.find('.' + config.systemSelectedClass);
};
/**
* search connections by systems
* @param {Object} map - jsPlumb
@@ -577,6 +590,40 @@ define([
});
};
/**
* set map "shortcut" events
*/
$.fn.setMapShortcuts = function(){
return this.each((i, mapWrapper) => {
mapWrapper = $(mapWrapper);
let mapElement = mapWrapper.findMapElement();
// dynamic require Map module -> otherwise there is a require(), loop
let Map = require('app/map/map');
let map = Map.getMapInstance( mapElement.data('id'));
mapWrapper.watchKey('mapSystemAdd', (mapWrapper) => {
console.log('mapSystemAdd');
Map.showNewSystemDialog(map, {position: {x: 0, y: 0}});
},{focus: true});
mapWrapper.watchKey('mapSystemsSelect', (mapWrapper) => {
mapElement.selectAllSystems();
},{focus: true});
mapWrapper.watchKey('mapSystemsDelete', (mapWrapper) => {
console.log('mapSystemsDelete');
let selectedSystems = mapElement.getSelectedSystems();
$.fn.showDeleteSystemDialog(map, selectedSystems);
},{focus: true});
});
};
$.fn.findMapElement = function(){
return $(this).find('.' + config.mapClass);
};
/**
* get systemId string (selector
* @param mapId

View File

@@ -10,6 +10,7 @@ define([
'app/logging',
'app/page',
'app/map/worker',
'app/key',
'app/ui/form_element',
'app/module_map'
], ($, Init, Util, Render, Logging, Page, MapWorker) => {
@@ -30,7 +31,7 @@ define([
// load page
// load info (maintenance) info panel (if scheduled)
$('body').loadPageStructure();
$('body').loadPageStructure().setGlobalShortcuts();
// show app information in browser console
Util.showVersionInfo();

View File

@@ -16,6 +16,7 @@ define([
'dialog/map_info',
'dialog/account_settings',
'dialog/manual',
'dialog/shortcuts',
'dialog/map_settings',
'dialog/system_effects',
'dialog/jump_info',
@@ -62,7 +63,10 @@ define([
menuHeadMenuLogoClass: 'pf-head-menu-logo', // class for main menu logo
// helper element
dynamicElementWrapperId: 'pf-dialog-wrapper'
dynamicElementWrapperId: 'pf-dialog-wrapper',
// system signature module
systemSigModuleClass: 'pf-sig-table-module', // module wrapper (signatures)
};
let programStatusCounter = 0; // current count down in s until next status change is possible
@@ -74,49 +78,83 @@ define([
* @returns {*|jQuery|HTMLElement}
*/
$.fn.loadPageStructure = function(){
let body = $(this);
return this.each((i, body) => {
body = $(body);
// menu left
body.prepend(
$('<div>', {
class: [config.pageSlidebarClass, config.pageSlidebarLeftClass, 'sb-style-push', 'sb-width-custom'].join(' ')
}).attr('data-sb-width', config.pageSlideLeftWidth)
);
// menu right
body.prepend(
$('<div>', {
class: [config.pageSlidebarClass, config.pageSlidebarRightClass, 'sb-style-push', 'sb-width-custom'].join(' ')
}).attr('data-sb-width', config.pageSlideRightWidth)
);
// main page
body.prepend(
$('<div>', {
id: config.pageId,
class: config.pageClass
}).append(
Util.getMapModule()
).append(
// menu left
body.prepend(
$('<div>', {
id: config.dynamicElementWrapperId
})
)
);
class: [config.pageSlidebarClass, config.pageSlidebarLeftClass, 'sb-style-push', 'sb-width-custom'].join(' ')
}).attr('data-sb-width', config.pageSlideLeftWidth)
);
// load header / footer
$('.' + config.pageClass).loadHeader().loadFooter();
// menu right
body.prepend(
$('<div>', {
class: [config.pageSlidebarClass, config.pageSlidebarRightClass, 'sb-style-push', 'sb-width-custom'].join(' ')
}).attr('data-sb-width', config.pageSlideRightWidth)
);
// load left menu
$('.' + config.pageSlidebarLeftClass).loadLeftMenu();
// main page
body.prepend(
$('<div>', {
id: config.pageId,
class: config.pageClass
}).append(
Util.getMapModule()
).append(
$('<div>', {
id: config.dynamicElementWrapperId
})
)
);
// load right menu
$('.' + config.pageSlidebarRightClass).loadRightMenu();
// load header / footer
$('.' + config.pageClass).loadHeader().loadFooter();
// set document observer for global events
setDocumentObserver();
// load left menu
$('.' + config.pageSlidebarLeftClass).loadLeftMenu();
return body;
// load right menu
$('.' + config.pageSlidebarRightClass).loadRightMenu();
// set document observer for global events
setDocumentObserver();
});
};
$.fn.setGlobalShortcuts = function(){
return this.each((i, body) => {
body = $(body);
console.log('setGlobalShortcuts');
body.watchKey('tabReload', (body) => {
location.reload();
});
body.watchKey('signaturePaste', (e) => {
let moduleElement = $('.' + config.systemSigModuleClass);
// check if there is a signature module active (system clicked)
if(moduleElement.length){
e = e.originalEvent;
let targetElement = $(e.target);
// do not read clipboard if pasting into form elements
if(
targetElement.prop('tagName').toLowerCase() !== 'input' &&
targetElement.prop('tagName').toLowerCase() !== 'textarea' || (
targetElement.is('input[type="search"]') // Datatables "search" field bubbles `paste.DT` event :(
)
){
let clipboard = (e.originalEvent || e).clipboardData.getData('text/plain');
moduleElement.trigger('pf:updateSystemSignatureModuleByClipboard', [clipboard]);
}
}
});
});
};
/**
@@ -355,6 +393,22 @@ define([
).on('click', function(){
$(document).triggerMenuEvent('Manual');
})
).append(
$('<a>', {
class: 'list-group-item list-group-item-info',
href: '#'
}).html('&nbsp;&nbsp;Shortcuts').prepend(
$('<i>',{
class: 'fa fa-keyboard-o fa-fw'
})
).append(
$('<span>',{
class: 'badge bg-color bg-color-gray txt-color txt-color-warning',
text: 'beta'
})
).on('click', function(){
$(document).triggerMenuEvent('Shortcuts');
})
).append(
$('<a>', {
class: 'list-group-item list-group-item-info',
@@ -586,6 +640,18 @@ define([
return false;
});
$(document).on('pf:menuShowTaskManager', function(e, data){
// show log dialog
Logging.showDialog();
return false;
});
$(document).on('pf:menuShortcuts', function(e, data){
// show shortcuts dialog
$.fn.showShortcutsDialog();
return false;
});
$(document).on('pf:menuShowSettingsDialog', function(e){
// show character select dialog
$.fn.showSettingsDialog();
@@ -626,12 +692,6 @@ define([
return false;
});
$(document).on('pf:menuShowTaskManager', function(e, data){
// show log dialog
Logging.showDialog();
return false;
});
$(document).on('pf:menuLogout', function(e, data){
let clearCookies = false;

View File

@@ -9,9 +9,10 @@ define([
'app/render',
'bootbox',
], function($, Init, Util, Render, bootbox) {
'use strict';
var config = {
let config = {
// jump info dialog
jumpInfoDialogClass: 'pf-jump-info-dialog' // class for jump info dialog
};
@@ -22,12 +23,10 @@ define([
$.fn.showJumpInfoDialog = function(){
requirejs(['text!templates/dialog/jump_info.html', 'mustache'], function(template, Mustache) {
let data = {};
let content = Mustache.render(template, data);
var data = {};
var content = Mustache.render(template, data);
var signatureReaderDialog = bootbox.dialog({
let signatureReaderDialog = bootbox.dialog({
className: config.jumpInfoDialogClass,
title: 'Wormhole jump information',
message: content

View File

@@ -12,7 +12,7 @@ define([
'use strict';
var config = {
let config = {
// global dialog
dialogNavigationClass: 'pf-dialog-navigation-list', // class for dialog navigation bar
dialogNavigationListItemClass: 'pf-dialog-navigation-list-item', // class for map manual li main navigation elements
@@ -28,7 +28,7 @@ define([
requirejs(['text!templates/dialog/map_manual.html', 'mustache'], function(template, Mustache) {
var data = {
let data = {
dialogNavigationClass: config.dialogNavigationClass,
dialogNavLiClass: config.dialogNavigationListItemClass,
scrollspyId: config.mapManualScrollspyId,
@@ -36,10 +36,10 @@ define([
mapCounterClass : Init.classes.pieChart.pieChartMapCounterClass
};
var content = Mustache.render(template, data);
let content = Mustache.render(template, data);
// show dialog
var mapManualDialog = bootbox.dialog({
let mapManualDialog = bootbox.dialog({
title: 'Manual',
message: content,
size: 'large',
@@ -56,15 +56,15 @@ define([
});
// modal offset top
var modalOffsetTop = 200;
let modalOffsetTop = 200;
// disable on scroll event
var disableOnScrollEvent = false;
let disableOnScrollEvent = false;
// scroll breakpoints
var scrolLBreakpointElements = null;
let scrolLBreakpointElements = null;
// scroll navigation links
var scrollNavLiElements = null;
let scrollNavLiElements = null;
mapManualDialog.on('shown.bs.modal', function(e) {
// modal on open
@@ -72,13 +72,13 @@ define([
scrollNavLiElements = $('.' + config.dialogNavigationListItemClass);
});
var scrollspyElement = $('#' + config.mapManualScrollspyId);
let scrollspyElement = $('#' + config.mapManualScrollspyId);
var whileScrolling = function(){
let whileScrolling = function(){
if(disableOnScrollEvent === false){
for(var i = 0; i < scrolLBreakpointElements.length; i++){
var offset = $(scrolLBreakpointElements[i]).offset().top;
for(let i = 0; i < scrolLBreakpointElements.length; i++){
let offset = $(scrolLBreakpointElements[i]).offset().top;
if( (offset - modalOffsetTop) > 0){
@@ -116,11 +116,11 @@ define([
scrollspyElement.find('.' + data.mapCounterClass).initMapUpdateCounter();
// set navigation button observer
var mainNavigationLinks = $('.' + config.dialogNavigationClass).find('a');
let mainNavigationLinks = $('.' + config.dialogNavigationClass).find('a');
// text anchor links
var subNavigationLinks = scrollspyElement.find('a[data-target]');
let subNavigationLinks = scrollspyElement.find('a[data-target]');
var navigationLinks = mainNavigationLinks.add(subNavigationLinks);
let navigationLinks = mainNavigationLinks.add(subNavigationLinks);
navigationLinks.on('click', function(e){
e.preventDefault();
@@ -130,7 +130,7 @@ define([
// scroll to anchor
scrollspyElement.mCustomScrollbar('scrollTo', $(this).attr('data-target'));
var mainNavigationLiElement = $(this).parent('.' + config.dialogNavigationListItemClass);
let mainNavigationLiElement = $(this).parent('.' + config.dialogNavigationListItemClass);
whileScrolling();

View File

@@ -12,7 +12,7 @@ define([
'use strict';
var config = {
let config = {
// shutdown dialog
notificationDialogId: 'pf-notification-dialog', // id for "notification" dialog
@@ -23,8 +23,8 @@ define([
* show/animate dialog page content
* @param dialog
*/
var showPageContent = function(dialog){
var headlineElement = dialog.find('h1');
let showPageContent = function(dialog){
let headlineElement = dialog.find('h1');
headlineElement.delay(300).velocity('transition.shrinkIn', {
duration: 500
@@ -45,7 +45,7 @@ define([
$.fn.showNotificationDialog = function(dialogData){
// check if there is already a notification dialog open
var notificationDialogClassDialoges = $('.' + config.notificationDialogClass);
let notificationDialogClassDialoges = $('.' + config.notificationDialogClass);
if(notificationDialogClassDialoges.length === 0){
@@ -54,15 +54,15 @@ define([
requirejs(['text!templates/dialog/notification.html', 'mustache'], function(template, Mustache) {
var data = {
let data = {
id: config.notificationDialogId,
content: dialogData.content
};
var content = Mustache.render(template, data);
let content = Mustache.render(template, data);
// show dialog
var shutdownDialog = bootbox.dialog({
let shutdownDialog = bootbox.dialog({
title: dialogData.content.title,
message: content,
className: config.notificationDialogClass,
@@ -72,7 +72,7 @@ define([
shutdownDialog.on('shown.bs.modal', function(e) {
// remove close button
var dialog = $(this);
let dialog = $(this);
dialog.find('.bootbox-close-button').remove();
dialog.find('button').blur();

View File

@@ -0,0 +1,50 @@
/**
* shortcuts dialog
*/
define([
'jquery',
'app/init',
'app/util',
'app/render',
'bootbox',
'app/key',
], function($, Init, Util, Render, bootbox, Key) {
'use strict';
let config = {
// map dialog
shortcutsDialogId: 'pf-shortcuts-dialog', // id for shortcuts dialog
};
/**
* shows the map manual modal dialog
*/
$.fn.showShortcutsDialog = function(){
requirejs(['text!templates/dialog/shortcuts.html', 'mustache'], function(template, Mustache){
let data = {
id: config.shortcutsDialogId,
shortcuts: Key.getGroupedShortcuts()
};
let content = Mustache.render(template, data);
// show dialog
let shortcutsDialog = bootbox.dialog({
title: 'Keyboard Shortcuts',
message: content,
size: 'large',
buttons: {
success: {
label: 'close',
className: 'btn-default'
}
},
show: true
});
});
};
});

View File

@@ -13,12 +13,12 @@ define([
], function($, Init, Util, Render, bootbox, MapUtil) {
'use strict';
var config = {
let config = {
// system effect dialog
systemEffectDialogWrapperClass: 'pf-system-effect-dialog-wrapper' // class for system effect dialog
};
var cache = {
let cache = {
systemEffectDialog: false // system effect info dialog
};
@@ -30,31 +30,31 @@ define([
// cache table structure
if(!cache.systemEffectDialog){
var dialogWrapperElement = $('<div>', {
let dialogWrapperElement = $('<div>', {
class: config.systemEffectDialogWrapperClass
});
var systemEffectData = Util.getSystemEffectData();
let systemEffectData = Util.getSystemEffectData();
$.each( systemEffectData.wh, function( effectName, effectData ) {
var table = $('<table>', {
let table = $('<table>', {
class: ['table', 'table-condensed'].join(' ')
});
var tbody = $('<tbody>');
var thead = $('<thead>');
let tbody = $('<tbody>');
let thead = $('<thead>');
var rows = [];
let rows = [];
// get formatted system effect name
var systemEffectName = MapUtil.getEffectInfoForSystem(effectName, 'name');
var systemEffectClass = MapUtil.getEffectInfoForSystem(effectName, 'class');
let systemEffectName = MapUtil.getEffectInfoForSystem(effectName, 'name');
let systemEffectClass = MapUtil.getEffectInfoForSystem(effectName, 'class');
$.each( effectData, function( areaId, areaData ) {
var systemType = 'C' + areaId;
var securityClass = Util.getSecurityClassForSystem( systemType );
let systemType = 'C' + areaId;
let securityClass = Util.getSecurityClassForSystem( systemType );
if(areaId === '1'){
rows.push( $('<tr>') );

View File

@@ -15,7 +15,7 @@ define([
* init a select element as "select2" for map selection
*/
$.fn.initMapSelect = function(){
var selectElement = $(this);
let selectElement = $(this);
$.when(
selectElement.select2({
@@ -31,9 +31,9 @@ define([
* @param options
*/
$.fn.initSystemSelect = function(options){
var selectElement = $(this);
let selectElement = $(this);
var config = {
let config = {
maxSelectionLength: 1
};
options = $.extend({}, config, options);
@@ -46,12 +46,12 @@ define([
}
// show effect info just for wormholes
var hideEffectClass = '';
let hideEffectClass = '';
if(data.effect === ''){
hideEffectClass = 'hide';
}
var markup = '<div class="clearfix">';
let markup = '<div class="clearfix">';
markup += '<div class="col-sm-5 pf-select-item-anchor">' + data.text + '</div>';
markup += '<div class="col-sm-2 text-right ' + data.effectClass + '">';
markup += '<i class="fa fa-fw fa-square ' + hideEffectClass + '"></i>';
@@ -83,12 +83,12 @@ define([
results: data.map( function(item){
// "systemId" or "name"
var id = item[options.key];
var disabled = false;
var trueSec = parseFloat(item.trueSec);
var secClass = Util.getSecurityClassForSystem(item.security);
var trueSecClass = Util.getTrueSecClassForSystem( trueSec );
var effectClass = MapUtil.getEffectInfoForSystem(item.effect, 'class');
let id = item[options.key];
let disabled = false;
let trueSec = parseFloat(item.trueSec);
let secClass = Util.getSecurityClassForSystem(item.security);
let trueSecClass = Util.getTrueSecClassForSystem( trueSec );
let effectClass = MapUtil.getEffectInfoForSystem(item.effect, 'class');
// check if system is dialed
if(
@@ -126,7 +126,7 @@ define([
error: function (jqXHR, status, error) {
if( !Util.isXHRAborted(jqXHR) ){
var reason = status + ' ' + jqXHR.status + ': ' + error;
let reason = status + ' ' + jqXHR.status + ': ' + error;
Util.showNotify({title: 'System select warning', text: reason + ' deleted', type: 'warning'});
}
@@ -161,7 +161,7 @@ define([
return this.each(function(){
var selectElement = $(this);
let selectElement = $(this);
// format result data
function formatResultData (data) {
@@ -172,7 +172,7 @@ define([
// check if an option is already selected
// do not show the same result twice
var currentValues = selectElement.val();
let currentValues = selectElement.val();
if(
currentValues &&
@@ -181,8 +181,8 @@ define([
return ;
}
var imagePath = '';
var previewContent = '';
let imagePath = '';
let previewContent = '';
switch(options.type){
case 'character':
@@ -199,7 +199,7 @@ define([
break;
}
var markup = '<div class="clearfix">';
let markup = '<div class="clearfix">';
markup += '<div class="col-sm-2">' + previewContent + '</div>';
markup += '<div class="col-sm-10">' + data.text + '</div></div>';
@@ -213,7 +213,7 @@ define([
return data.text;
}
var markup = '<div class="clearfix">';
let markup = '<div class="clearfix">';
markup += '<div class="col-sm-10">' + data.text + '</div></div>';
return markup;
@@ -248,7 +248,7 @@ define([
error: function (jqXHR, status, error) {
if( !Util.isXHRAborted(jqXHR) ){
var reason = status + ' ' + jqXHR.status + ': ' + error;
let reason = status + ' ' + jqXHR.status + ': ' + error;
Util.showNotify({title: 'Access select warning', text: reason + ' deleted', type: 'warning'});
}

View File

@@ -2206,20 +2206,8 @@ define([
});
// event listener for global "paste" signatures into the page -------------------------------------------------
$('body').off('paste').on('paste', function(e){
let targetElement = $(e.target);
// do not read clipboard if pasting into form elements
if(
targetElement.prop('tagName').toLowerCase() !== 'input' &&
targetElement.prop('tagName').toLowerCase() !== 'textarea' || (
targetElement.is('input[type="search"]') // Datatables "search" field bubbles `paste.DT` event :(
)
){
let clipboard = (e.originalEvent || e).clipboardData.getData('text/plain');
moduleElement.updateSignatureTableByClipboard(systemData, clipboard, {});
}
moduleElement.on('pf:updateSystemSignatureModuleByClipboard', function(e, clipboard){
$(this).updateSignatureTableByClipboard(systemData, clipboard, {});
});
};

View File

@@ -0,0 +1,22 @@
<div id="{{id}}" class="row">
{{#shortcuts}}
<div class="col-sm-6">
<h4>{{label}}</h4>
<table class="table table-condensed">
<tbody>
{{#events}}
<tr>
<td>{{label}}</td>
<td class="text-right">
{{#keyNames}}
<kbd>{{.}}</kbd>&nbsp;<i class="fa fa-fw fa-plus"></i>&nbsp;
{{/keyNames}}
</td>
</tr>
{{/events}}
</tbody>
</table>
</div>
{{/shortcuts}}
</div>

View File

@@ -111,7 +111,6 @@
<th class="bg-color bg-color-grayDarker"></th>
<th class="bg-color bg-color-tealDarker txt-color txt-color-orange"></th>
</tr>
</tr>
</tfoot>
</table>
</div>

View File

@@ -121,6 +121,15 @@
}
}
// shortcuts dialog ===========================================================
#pf-shortcuts-dialog{
td kbd:last-of-type{
& + i {
display: none
}
}
}
// map manual dialog ==========================================================
#pf-manual-scrollspy{
position: relative;

View File

@@ -56,6 +56,11 @@ $mapWidth: 2500px ;
style: solid;
color: $gray-dark;
}
&:focus{
border: 1px solid $gray;
}
}
}