diff --git a/js/app/ui/form_element.js b/js/app/ui/form_element.js index 54c82ba9..e37cf9b8 100644 --- a/js/app/ui/form_element.js +++ b/js/app/ui/form_element.js @@ -461,7 +461,7 @@ define([ * @param term search term */ function sortResultData (data, term){ - let levenshtein = function (a,b){ + let levenshtein = (a,b) => { let matrix = new Array(a.length+1); for(let i = 0; i < matrix.length; i++){ matrix[i] = new Array(b.length+1).fill(0); @@ -480,7 +480,7 @@ define([ matrix[ai][bi] = Math.min( matrix[ai-1][bi]+1, matrix[ai][bi-1]+1, - matrix[ai-1][bi-1]+(a[ai-1] == b[bi-1] ? 0 : 1) + matrix[ai-1][bi-1]+(a[ai-1] === b[bi-1] ? 0 : 1) ); } } @@ -488,7 +488,7 @@ define([ return matrix[a.length][b.length]; }; - data.sort(function(a,b){ + data.sort((a,b) => { let levA = levenshtein(term, a.name.toLowerCase()); let levB = levenshtein(term, b.name.toLowerCase()); return levA === levB ? 0 : (levA > levB ? 1 : -1); @@ -531,6 +531,7 @@ define([ for(let category in result){ // skip custom functions in case result = [] (array functions) if(result.hasOwnProperty(category)){ + // sort results (optional) sortResultData(result[category], page.term); data.results.push({ text: category, diff --git a/js/app/ui/module/system_signature.js b/js/app/ui/module/system_signature.js index b7fb2f8f..f3979aed 100644 --- a/js/app/ui/module/system_signature.js +++ b/js/app/ui/module/system_signature.js @@ -1220,7 +1220,7 @@ define([ searchable: true, title: 'id', type: 'string', - width: 15, + width: 12, class: [config.tableCellFocusClass, config.sigTableEditSigNameInput].join(' '), data: 'name', createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ diff --git a/public/js/v1.4.1/app/ui/connection_info.js b/public/js/v1.4.1/app/ui/connection_info.js deleted file mode 100644 index 6c5999ff..00000000 --- a/public/js/v1.4.1/app/ui/connection_info.js +++ /dev/null @@ -1,1018 +0,0 @@ -/** - * connection info module - */ - -define([ - 'jquery', - 'app/init', - 'app/util', - 'app/map/util' -], ($, Init, Util, MapUtil) => { - 'use strict'; - - let config = { - // module info - modulePosition: 1, - moduleName: 'connectionInfo', - moduleHeadClass: 'pf-module-head', // class for module header - moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler - - headUserShipClass: 'pf-head-user-ship', // class for "user settings" link - - // connection info module - moduleTypeClass: 'pf-connection-info-module', // class for this module - - // headline toolbar - moduleHeadlineIconClass: 'pf-module-icon-button', // class for toolbar icons in the head - moduleHeadlineIconRefreshClass: 'pf-module-icon-button-refresh', // class for "refresh" icon - moduleHeadlineIconCurrentMassClass: 'pf-module-icon-button-mass', // class for "current ship mass" toggle icon - - connectionInfoPanelClass: 'pf-connection-info-panel', // class for connection info panels - connectionInfoPanelId: 'pf-connection-info-panel-', // id prefix for connection info panels - - // info table - moduleTableClass: 'pf-module-table', // class for module tables - connectionInfoTableLabelSourceClass: 'pf-connection-info-label-source', // class for source label - connectionInfoTableLabelTargetClass: 'pf-connection-info-label-target', // class for target label - connectionInfoTableRowMassLogClass: 'pf-connection-info-row-mass-log', // class for "logged mass" table row - connectionInfoTableRowMassShipClass: 'pf-connection-info-row-mass-ship', // class for "current ship mass" table row - connectionInfoTableCellConnectionClass: 'pf-connection-info-connection', // class for connection "fake" table cell - connectionInfoTableCellMassTotalTooltipClass: 'pf-connection-info-mass-total-tooltip', // class for "mass total tooltip" table cell - connectionInfoTableCellMassTotalClass: 'pf-connection-info-mass-total', // class for "mass total" table cell - connectionInfoTableCellMassLogClass: 'pf-connection-info-mass-log', // class for "mass logged" table cell - connectionInfoTableCellMassShipClass: 'pf-connection-info-mass-ship', // class for "current ship mass" table cell - connectionInfoTableCellMassLeftClass: 'pf-connection-info-mass-left', // class for "mass left" table cell - - // dataTable - connectionInfoTableClass: 'pf-connection-info-table', // class for connection tables - tableCellImageClass: 'pf-table-image-cell', // class for table "image" cells - tableCellCounterClass: 'pf-table-counter-cell', // class for table "counter" cells - - // config - showShip: true // default for "show current ship mass" toggle - }; - - /** - * get module toolbar element - * @returns {*|jQuery|HTMLElement|void} - */ - let getHeadlineToolbar = () => { - let headlineToolbar = $('
', { - class: 'pull-right' - }).append( - $('', { - class: ['fas', 'fa-fw', 'fa-male', - config.showShip ? 'active' : '' , - config.moduleHeadlineIconClass, - config.moduleHeadlineIconCurrentMassClass].join(' '), - title: 'toggle current ship mass' - }).attr('data-html', 'true').attr('data-toggle', 'tooltip'), - $('', { - class: ['fas', 'fa-fw', 'fa-sync', - config.moduleHeadlineIconClass, - config.moduleHeadlineIconRefreshClass].join(' '), - title: 'refresh all' - }).attr('data-html', 'true').attr('data-toggle', 'tooltip') - ); - - headlineToolbar.find('[data-toggle="tooltip"]').tooltip({ - container: 'body' - }); - - return headlineToolbar; - }; - - /** - * get new connection element - * @param mapId - * @param connectionId - * @returns {jQuery} - */ - let getConnectionElement = (mapId, connectionId) => { - let connectionElement = $('
', { - id: getConnectionElementId(connectionId), - class: ['col-xs-12', 'col-sm-4', 'col-lg-3' , config.connectionInfoPanelClass].join(' ') - }).data({ - mapId: mapId, - connectionId: connectionId - }); - - return connectionElement; - }; - - /** - * get info control panel element - * @param mapId - * @returns {void|jQuery|*} - */ - let getInfoPanelControl = (mapId) => { - let connectionElement = getConnectionElement(mapId, 0).append($('
', { - class: 'pf-dynamic-area', - html: ' add connection  ctrl + click' - })); - - return connectionElement; - }; - - /** - * get connection information element - * @param connectionData - * @returns {void|*|jQuery|HTMLElement} - */ - let getInformationElement = (connectionData) => { - - // connection scope ----------------------------------------------------------------------- - let scopeLabel = MapUtil.getScopeInfoForConnection(connectionData.scope, 'label'); - - let element = $('
', { - class: 'pf-dynamic-area' - }).append( - $('', { - class: ['table', 'table-condensed', 'pf-table-fixed', config.moduleTableClass].join(' ') - }).data('showShip', config.showShip).append( - $('').append( - $('').append( - $('').append( - $('').append( - $('').append( - $('', { - class: config.connectionInfoTableRowMassLogClass - }).append( - $('', { - class: config.connectionInfoTableRowMassShipClass - }).append( - $('').append( - $('
', { - class: ['pf-table-cell-20', 'text-right', Util.config.helpClass, 'pf-pie-chart'].join(' ') - }).attr('data-toggle', 'tooltip').attr('data-percent', '-100').easyPieChart({ - barColor: (percent) => { - let color = '#e28a0d'; - if((percent * -1) >= 100){ - color = '#a52521'; - } - return color; - }, - overrideOptions: 'signed', - trackColor: '#5cb85c', - size: 14, - scaleColor: false, - lineWidth: 2, - lineCap: 'butt', - animate: false - }), - $('', { - class: ['text-right'].join(' ') - }).attr('colspan', 2).append( - $('', { - class: 'pf-link', - html: connectionData.sourceAlias + '  ' - }).on('click', function(){ - Util.getMapModule().getActiveMap().triggerMenuEvent('SelectSystem', {systemId: connectionData.source }); - }), - $('', { - class: [config.connectionInfoTableLabelSourceClass].join(' ') - }), - $('', { - class: 'fas fa-fw fa-angle-double-right' - }), - $('', { - class: [config.connectionInfoTableLabelTargetClass].join(' ') - }), - $('', { - class: 'pf-link', - html: '  ' + connectionData.targetAlias - }).on('click', function(){ - Util.getMapModule().getActiveMap().triggerMenuEvent('SelectSystem', {systemId: connectionData.target }); - }) - ) - ) - ), - $('
', { - class: ['text-right', Util.config.helpClass, config.connectionInfoTableCellMassTotalTooltipClass].join(' '), - html: '' - }), - $('', { - text: scopeLabel.charAt(0).toUpperCase() + scopeLabel.slice(1) - }), - $('', { - class: ['text-right', config.connectionInfoTableCellConnectionClass].join(' ') - }).append( - $('
', { - class: MapUtil.getConnectionFakeClassesByTypes(connectionData.type).join(' ') - }) - ) - ), - $('
', { - class: ['text-right', Util.config.helpClass].join(' '), - html: '', - title: 'initial mass. From signature table' - }).attr('data-toggle', 'tooltip'), - $('', { - text: 'Total mass' - }), - $('', { - class: ['text-right', 'txt-color', config.connectionInfoTableCellMassTotalClass].join(' ') - }) - ), - $('
', { - class: ['text-right', Util.config.helpClass].join(' '), - title: 'recorded total jump mass' - }).attr('data-toggle', 'tooltip').append( - $('', { - class: [ - 'fas', 'fa-fw', 'fa-question-circle' - ].join(' ') - }), - $('', { - class: [ - 'fas', 'fa-fw', 'fa-adjust', - 'txt-color', 'txt-color-warning', - 'hidden' - ].join(' ') - }), - $('', { - class: [ - 'far', 'fa-fw', 'fa-circle', - 'txt-color', 'txt-color-danger', - 'hidden' - ].join(' ') - }) - ), - $('', { - text: 'Logged mass' - }), - $('', { - class: ['text-right', config.connectionInfoTableCellMassLogClass].join(' ') - }) - ), - $('
', { - class: ['text-right', Util.config.helpClass].join(' '), - title: 'current ship mass' - }).attr('data-toggle', 'tooltip').append( - $('', { - class: ['fas', 'fa-fw', 'fa-question-circle'].join(' ') - }), - $('', { - class: [ - 'fas', 'fa-fw', 'fa-exclamation-triangle', - 'txt-color', 'txt-color-danger', - 'hidden' - ].join(' ') - }) - ), - $('', { - class: ['pf-table-cell-ellipses-auto'].join(' '), - text: 'Ship mass' - }), - $('', { - class: ['text-right', 'txt-color', config.connectionInfoTableCellMassShipClass].join(' ') - }) - ), - $('
', { - class: ['text-right', Util.config.helpClass].join(' '), - html: '', - title: 'max. mass left' - }).attr('data-toggle', 'tooltip'), - $('', { - text: 'Mass left' - }), - $('', { - class: ['text-right', 'txt-color', config.connectionInfoTableCellMassLeftClass].join(' ') - }) - ) - ) - ).on('pf:updateInfoTable', function(e, data){ - // update information table ------------------------------------------------------- - let tableElement = $(this); - let connectionData = tableElement.data('connectionData'); - if(connectionData){ - if(connectionData.scope === 'wh'){ - // update signature information ------------------------------------------- - let sourceLabelElement = tableElement.find('.' + config.connectionInfoTableLabelSourceClass); - let targetLabelElement = tableElement.find('.' + config.connectionInfoTableLabelTargetClass); - - // get related jsPlumb connection - let connection = $().getConnectionById(data.mapId, data.connectionId); - let signatureTypeNames = MapUtil.getConnectionDataFromSignatures(connection, connectionData); - - let sourceLabel = signatureTypeNames.sourceLabels; - let targetLabel = signatureTypeNames.targetLabels; - sourceLabelElement.html(MapUtil.getEndpointOverlayContent(sourceLabel)); - targetLabelElement.html(MapUtil.getEndpointOverlayContent(targetLabel)); - - // remove K162 - sourceLabel = sourceLabel.diff(['K162']); - targetLabel = targetLabel.diff(['K162']); - - // get static wormhole data by endpoint Labels - let wormholeName = ''; - let wormholeData = null; - if(sourceLabel.length === 1 && targetLabel.length === 0){ - wormholeName = sourceLabel[0]; - }else if(sourceLabel.length === 0 && targetLabel.length === 1){ - wormholeName = targetLabel[0]; - } - - if( - wormholeName && - Init.wormholes.hasOwnProperty(wormholeName) - ){ - wormholeData = Object.assign({}, Init.wormholes[wormholeName]); - wormholeData.class = Util.getSecurityClassForSystem(wormholeData.security); - - // init wormhole tooltip ---------------------------------------------- - let massTotalTooltipCell = tableElement.find('.' + config.connectionInfoTableCellMassTotalTooltipClass); - massTotalTooltipCell.addWormholeInfoTooltip(wormholeData); - } - - // all required data is set -> re-calculate rows - tableElement.data('wormholeData', wormholeData); - tableElement.trigger('pf:calcInfoTable'); - } - - } - }).on('pf:calcInfoTable', function(e){ - // re-calculate information table from .data() cell values ------------------------ - let tableElement = $(this); - let connectionData = tableElement.data('connectionData'); - let massChartCell = tableElement.find('[data-percent]'); - - let wormholeData = tableElement.data('wormholeData'); - let shipData = null; - let shipName = ''; - let showShip = Boolean(tableElement.data('showShip')); - let massLogRow = tableElement.find('.' + config.connectionInfoTableRowMassLogClass); - let massShipRow = tableElement.find('.' + config.connectionInfoTableRowMassShipClass); - - // icons - let massLogTooltipIcon = massLogRow.find('i.fa-question-circle'); - let massLogStage2Icon = massLogRow.find('i.fa-adjust'); - let massLogStage3Icon = massLogRow.find('i.fa-circle'); - - let massShipTooltipIcon = massShipRow.find('i.fa-question-circle'); - let massShipWarningIcon = massShipRow.find('i.fa-exclamation-triangle'); - - // table cells - let connectionCell = tableElement.find('.' + config.connectionInfoTableCellConnectionClass); - let massTotalCell = tableElement.find('.' + config.connectionInfoTableCellMassTotalClass); - let massLogCell = tableElement.find('.' + config.connectionInfoTableCellMassLogClass); - let massShipCell = tableElement.find('.' + config.connectionInfoTableCellMassShipClass); - let massLeftCell = tableElement.find('.' + config.connectionInfoTableCellMassLeftClass); - let massTotal = null; // initial connection mass - let massReduction = 0; // default reduction (e.g. reduced, crit) in percent - let massLog = massLogCell.data('mass'); // recorded mass - let massLogTotal = massLog; // recorded mass + current ship - let massIndividual = null; // mass mass per jump - let massShip = 0; // current ship - let massIndividualError = false; - - // get wormhole data from signature binding --------------------------------------- - if(wormholeData){ - massTotal = parseInt(wormholeData.massTotal); - massIndividual = parseInt(wormholeData.massIndividual); - } - - // get connection type (show fake connection div) --------------------------------- - connectionCell.find('div').removeClass().addClass(MapUtil.getConnectionFakeClassesByTypes(connectionData.type).join(' ')); - - // get wormhole status ------------------------------------------------------------ - if(connectionData.type.indexOf('wh_critical') !== -1){ - massReduction = 90; - massLogTooltipIcon.toggleClass('hidden', true); - massLogStage2Icon.toggleClass('hidden', true); - massLogStage3Icon.toggleClass('hidden', false); - massLogStage3Icon.parent().attr('title', 'stage 3 (critical)').tooltip('fixTitle'); - }else if(connectionData.type.indexOf('wh_reduced') !== -1){ - massReduction = 50; - massLogTooltipIcon.toggleClass('hidden', true); - massLogStage2Icon.toggleClass('hidden', false); - massLogStage3Icon.toggleClass('hidden', true); - massLogStage3Icon.parent().attr('title', 'stage 2 (reduced)').tooltip('fixTitle'); - }else{ - massLogTooltipIcon.toggleClass('hidden', false); - massLogStage2Icon.toggleClass('hidden', true); - massLogStage3Icon.toggleClass('hidden', true); - massLogStage3Icon.parent().attr('title', 'recorded total jump mass').tooltip('fixTitle'); - } - - if(massReduction){ - let massLogReduction = massTotal / 100 * massReduction; - if(massLogReduction > massLog){ - massLog = massLogTotal = massLogReduction; - } - } - - // get current ship data ---------------------------------------------------------- - massShipCell.parent().toggle(showShip); - if(showShip){ - shipData = $('.' + config.headUserShipClass).data('shipData'); - if(shipData){ - if(shipData.mass){ - massShip = parseInt(shipData.mass); - - // check individual mass jump - if(massIndividual){ - massIndividualError = massShip > massIndividual; - } - } - if(shipData.typeId && shipData.typeName){ - shipName = shipData.typeName; - } - } - } - - // update ship mass and "individual mass" cells ---------------------------------- - massShipTooltipIcon.toggleClass('hidden', massIndividualError); - massShipWarningIcon.toggleClass('hidden', !massIndividualError); - let shipMassTooltip = 'current ship mass ' + (shipName ? '"' + shipName + '"' : ''); - if(massIndividualError){ - shipMassTooltip = '"' + shipName + '" exceeds max jump mass for this connection: ' + Util.formatMassValue(massIndividual); - }else{ - // current ship mass check is OK -> add to massLogTotal - massLogTotal += massShip; - } - massShipTooltipIcon.parent().attr('title', shipMassTooltip).tooltip('fixTitle'); - - // current ship mass -------------------------------------------------------------- - massShipCell.html( function(){ - let cell = $(this); - let value = ' '; - let error = false; - let textLineThrough = false; - if(massShip > 0){ - value += Util.formatMassValue(massShip); - if(massIndividualError){ - error = textLineThrough = true; - value = '  ' + value; - }else{ - value = '-' + value; - } - }else{ - error = true; - value = 'undefined'; - } - - // change cell style - cell.toggleClass('txt-color-red', error) - .toggleClass('txt-color-warning', !error) - .toggleClass('pf-font-line-through', textLineThrough); - - return value; - }); - - // calculate mass left ------------------------------------------------------------ - let massLeft = massTotal - massLogTotal; - massLeft = (massLeft < 0) ? 0 : massLeft; - let massPercentLog = (massTotal > 0) ? Math.floor((100 / massTotal) * massLogTotal) : 0; - - // update easyPieChart and tooltip ------------------------------------------------ - let massPercentLeft = (100 - massPercentLog <= 0) ? 0 : '< ' + (100 - massPercentLog); - massChartCell.data('easyPieChart').enableAnimation().update(massPercentLog * -1); - massChartCell.attr('title', massPercentLeft + '% mass left').tooltip('fixTitle'); - - // update mass cells -------------------------------------------------------------- - massTotalCell.html(massTotal > 0 ? Util.formatMassValue(massTotal) : 'undefined') - .toggleClass('txt-color-red', massTotal <= 0); - massLogCell.html('- ' + Util.formatMassValue(massLog)); - massLeftCell.html( - massLeft > 0 ? - '~ ' + Util.formatMassValue(massLeft) : - (massLeft === 0 && massTotal) ? - 'will collapse' : 'undefined') - .toggleClass('txt-color-red', massLeft <= 0) - .toggleClass('txt-color-success', massLeft > 0); - }) - ); - - element.find('[data-toggle="tooltip"]').tooltip({ - container: 'body' - }); - - return element; - }; - - /** - * get HTML id by connectionId - * @param connectionId - * @returns {string} - */ - let getConnectionElementId = (connectionId) => { - return config.connectionInfoPanelId + connectionId; - }; - - /** - * get all visible connection panel elements - * @param moduleElement - * @returns {*|T|{}} - */ - let getConnectionElements = (moduleElement) => { - return moduleElement.find('.' + config.connectionInfoPanelClass).not('#' + getConnectionElementId(0)); - }; - - /** - * request connection log data - * @param requestData - * @param context - * @param callback - */ - let requestConnectionLogData = (requestData, context, callback) => { - // show loading animation - for(let connectionId of requestData.connectionIds){ - context.moduleElement.find('#' + getConnectionElementId(connectionId) + ' table').showLoadingAnimation(); - } - - $.ajax({ - type: 'POST', - url: Init.path.getMapConnectionData, - data: requestData, - dataType: 'json', - context: context - }).done(function(connectionsData){ - // enrich connectionData with "logs" data (if available) and other "missing" data - for(let i = 0; i < this.connectionsData.length; i++){ - for(let connectionData of connectionsData) { - if(this.connectionsData[i].id === connectionData.id){ - // copy some missing data - this.connectionsData[i].created = connectionData.created; - // check for mass logs and copy data - if(connectionData.logs && connectionData.logs.length){ - this.connectionsData[i].logs = connectionData.logs; - } - // check for signatures and copy data - if(connectionData.signatures && connectionData.signatures.length){ - this.connectionsData[i].signatures = connectionData.signatures; - } - break; - } - } - } - - callback(this.moduleElement, this.connectionsData); - }).always(function(){ - // hide loading animation - for(let contextData of this.connectionsData){ - context.moduleElement.find('#' + getConnectionElementId(contextData.id) + ' table').hideLoadingAnimation(); - } - }); - }; - - /** - * @see requestConnectionLogData - * @param moduleElement - * @param mapId - * @param connectionsData - */ - let getConnectionsLogData = (moduleElement, mapId, connectionsData) => { - let connectionIds = []; - for(let connectionData of connectionsData) { - connectionIds.push(connectionData.id); - } - - let requestData = { - mapId: mapId, - connectionIds: connectionIds, - addData : ['signatures', 'logs'], - // filterData : ['logs'] // do not exclude connections with NO "logs" -> sig data will be used as well - }; - - let contextData = { - moduleElement: moduleElement, - connectionsData: connectionsData - }; - - requestConnectionLogData(requestData, contextData, addConnectionsData); - }; - - /** - * replace/insert dataTables log data - * @param moduleElement - * @param connectionsData - */ - let addConnectionsData = (moduleElement, connectionsData) => { - - let getRowIndexesByData = (dataTable, colName, value) => { - return dataTable.rows().eq(0).filter((rowIdx) => { - return (dataTable.cell(rowIdx, colName + ':name' ).data() === value); - }); - }; - - for(let connectionData of connectionsData) { - // find related dom element for current connection - let connectionElement = moduleElement.find('#' + getConnectionElementId(connectionData.id)); - if(connectionElement.length){ - // attach connectionData to connection information for later use ------------------ - let connectionInfoElement = connectionElement.find('.' + config.moduleTableClass); - connectionInfoElement.data('connectionData', connectionData); - - // update dataTable --------------------------------------------------------------- - let dataTable = connectionElement.find('.dataTable').dataTable().api(); - - if(connectionData.logs && connectionData.logs.length > 0){ - for(let i = 0; i < connectionData.logs.length; i++){ - let rowData = connectionData.logs[i]; - let row = null; - let animationStatus = null; - let indexes = getRowIndexesByData(dataTable, 'index', rowData.id); - if(indexes.length === 0){ - // row not found -> add new row - row = dataTable.row.add( rowData ); - animationStatus = 'added'; - } - /* else{ - // we DON´t expect changes -> no row update) - // update row with FIRST index - //row = dataTable.row( parseInt(indexes[0]) ); - // update row data - //row.data(connectionData.logs[i]); - //animationStatus = 'changed'; - } */ - - if( - animationStatus !== null && - row.length > 0 - ){ - row.nodes().to$().data('animationStatus', animationStatus); - } - } - }else{ - // clear table or leave empty - dataTable.clear(); - } - - // redraw dataTable - dataTable.draw(false); - } - } - }; - - /** - * - * @param moduleElement - * @param mapId - * @param connectionData - */ - let updateConnectionPanel = (moduleElement, mapId, connectionData) => { - let rowElement = moduleElement.find('.row'); - let connectionElement = rowElement.find('#' + getConnectionElementId(connectionData.id)); - - if( !connectionElement.length ){ - connectionElement = getConnectionElement(mapId, connectionData.id); - connectionElement.append(getInformationElement(connectionData)); - - let table = $('', { - class: ['compact', 'stripe', 'order-column', 'row-border', 'nowrap', config.connectionInfoTableClass].join(' ') - }).append(''); - connectionElement.append(table); - - // init empty table - let logTable = table.DataTable({ - pageLength: 8, - paging: true, - pagingType: 'simple', - lengthChange: false, - ordering: true, - order: [[ 4, 'desc' ]], - info: true, - searching: false, - hover: false, - autoWidth: false, - // rowId: 'systemTo', - language: { - emptyTable: 'No jumps recorded', - info: '_START_ to _END_ of _MAX_', - infoEmpty: '' - }, - columnDefs: [ - { - targets: 0, - name: 'index', - title: '', - orderable: false, - searchable: false, - width: 20, - class: 'text-center', - data: 'id' - },{ - targets: 1, - title: '', - width: 26, - orderable: false, - className: [Util.config.helpDefaultClass, 'text-center', config.tableCellImageClass].join(' '), - data: 'ship', - render: { - _: function(data, type, row){ - let value = data.typeId; - if(type === 'display'){ - value = ''; - } - return value; - } - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - $(cell).find('img').tooltip(); - } - },{ - targets: 2, - title: '', - width: 26, - orderable: false, - className: [Util.config.helpDefaultClass, 'text-center', config.tableCellImageClass].join(' '), - data: 'created.character', - render: { - _: function(data, type, row){ - let value = data.name; - if(type === 'display'){ - value = ''; - } - return value; - } - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - $(cell).find('img').tooltip(); - } - },{ - targets: 3, - title: 'mass', - className: ['text-right'].join(' ') , - data: 'ship.mass', - render: { - _: function(data, type, row){ - let value = data; - if(type === 'display'){ - value = Util.formatMassValue(value); - } - return value; - } - } - },{ - targets: 4, - title: 'log', - width: 55, - className: ['text-right', config.tableCellCounterClass].join(' '), - data: 'created.created', - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - $(cell).initTimestampCounter('d'); - } - } - ], - drawCallback: function(settings){ - let animationRows = this.api().rows().nodes().to$().filter(function(a,b ) { - return ( - $(this).data('animationStatus') || - $(this).data('animationTimer') - ); - }); - - for(let i = 0; i < animationRows.length; i++){ - $(animationRows[i]).pulseTableRow($(animationRows[i]).data('animationStatus')); - $(animationRows[i]).removeData('animationStatus'); - } - - }, - footerCallback: function ( row, data, start, end, display ) { - - let api = this.api(); - let sumColumnIndexes = [3]; - - // column data for "sum" columns over this page - let pageTotalColumns = api - .columns( sumColumnIndexes, { page: 'all'} ) - .data(); - - // sum columns for "total" sum - pageTotalColumns.each((colData, index) => { - pageTotalColumns[index] = colData.reduce((a, b) => { - return parseInt(a) + parseInt(b); - }, 0); - }); - - $(sumColumnIndexes).each((index, value) => { - $( api.column( value ).footer() ).text( Util.formatMassValue(pageTotalColumns[index]) ); - - // save mass for further reCalculation of "info" table - connectionElement.find('.' + config.connectionInfoTableCellMassLogClass).data('mass', pageTotalColumns[index]); - }); - - // calculate "info" table ----------------------------------------------------- - connectionElement.find('.' + config.moduleTableClass).trigger('pf:updateInfoTable', connectionElement.data()); - } - }); - - // find position to insert - connectionElement.insertBefore(rowElement.find('#' + getConnectionElementId(0))); - - logTable.on('order.dt search.dt', function(){ - let pageInfo = logTable.page.info(); - logTable.column(0, {search:'applied', order:'applied'}).nodes().each((cell, i) => { - let content = (pageInfo.recordsTotal - i) + '.  '; - $(cell).html(content); - }); - }); - - logTable.on('destroy.dt', function(){ - $(this).destroyTimestampCounter(); - }); - } - }; - - /** - * remove connection Panel from moduleElement - * @param connectionElement - */ - let removeConnectionPanel = (connectionElement) => { - connectionElement = $(connectionElement); - if(connectionElement.length){ - // destroy dataTable (and remove table from DOM) - let logTable = connectionElement.find('.' + config.connectionInfoTableClass); - logTable.dataTable().api().destroy(true); - // remove belonging connectionElement - connectionElement.remove(); - } - }; - - /** - * get connections from ModuleElement - * -> validate with current map data - * @param moduleElement - * @param mapId - * @returns {{connectionsDataUpdate: Array, connectionsDataRemove: Array}} - */ - let getConnectionsDataFromModule = (moduleElement, mapId) => { - let activeMap = Util.getMapModule().getActiveMap(); - let mapData = activeMap.getMapDataFromClient({forceData: true}); - let connectionsData = { - connectionsDataUpdate: [], - connectionsDataRemove: [], - }; - - if(mapData !== false){ - getConnectionElements(moduleElement).each((i, connectionElement) => { - let removeConnectionPanel = true; - let connectionData = {id: $(connectionElement).data('connectionId') }; - - let connection = $().getConnectionById(mapId, connectionData.id); - if(connection){ - let connectionDataTemp = MapUtil.getDataByConnection(connection); - if(connectionDataTemp.id > 0){ - // connection still on map - OK - removeConnectionPanel = false; - connectionData = connectionDataTemp; - } - } - - if(removeConnectionPanel){ - connectionsData.connectionsDataRemove.push(connectionData); - }else{ - connectionsData.connectionsDataUpdate.push(connectionData); - } - }); - } - - return connectionsData; - }; - - /** - * update/init multiple connection panels at once - * @param moduleElement - * @param mapId - * @param connectionsDataUpdate - * @param connectionsDataRemove - */ - let updateConnectionPanels = (moduleElement, mapId, connectionsDataUpdate, connectionsDataRemove) => { - for(let connectionData of connectionsDataRemove){ - let connectionElement = moduleElement.find('#' + getConnectionElementId(connectionData.id)); - removeConnectionPanel(connectionElement); - } - - for(let connectionData of connectionsDataUpdate){ - updateConnectionPanel(moduleElement, mapId, connectionData); - } - - // request connectionsLogData for each updated connection - if(connectionsDataUpdate.length){ - getConnectionsLogData(moduleElement, mapId, connectionsDataUpdate); - } - - // remove module if no connection panel left - // --> all connection deselected on map - let connectionElements = getConnectionElements(moduleElement); - if(connectionElements.length === 0){ - MapUtil.getTabContentElementByMapElement(moduleElement).trigger('pf:removeConnectionModules'); - } - - // hide "control" panel when multiple connection - moduleElement.find('#' + getConnectionElementId(0)).toggle(connectionElements.length < 2); - }; - - /** - * set module observer - * @param moduleElement - * @param mapId - */ - let setModuleObserver = (moduleElement, mapId) => { - $(document).off('pf:updateConnectionInfoModule').on('pf:updateConnectionInfoModule', function(e, data){ - updateConnectionPanels( - moduleElement, - data.mapId, - MapUtil.getDataByConnections(data.connectionsUpdate), - MapUtil.getDataByConnections(data.connectionsRemove) - ); - }); - - $(document).off('pf:activeShip').on('pf:activeShip', function(e){ - moduleElement.find('.' + config.connectionInfoPanelClass).each((i, connectionElement) => { - $(connectionElement).find('.' + config.moduleTableClass).each((i, tableElement) => { - $(tableElement).trigger('pf:calcInfoTable'); - }); - }); - }); - - // init toggle active ship ---------------------------------------------------------------- - moduleElement.find('.' + config.moduleHeadlineIconCurrentMassClass).on('click', function(e){ - let currentMassIcon = $(this).toggleClass('active'); - moduleElement.find('.' + config.connectionInfoPanelClass).each((i, connectionElement) => { - $(connectionElement).find('.' + config.moduleTableClass).each((i, tableElement) => { - $(tableElement).data('showShip', currentMassIcon.hasClass('active')).trigger('pf:calcInfoTable'); - }); - }); - }); - - // init refresh connections --------------------------------------------------------------- - moduleElement.find('.' + config.moduleHeadlineIconRefreshClass).on('click', function(e){ - refreshConnectionPanels(moduleElement, mapId); - }); - }; - - /** - * refresh all connection panels in a module - * @param moduleElement - * @param mapId - */ - let refreshConnectionPanels = (moduleElement, mapId) => { - let connectionsData = getConnectionsDataFromModule(moduleElement, mapId); - updateConnectionPanels(moduleElement, mapId, connectionsData.connectionsDataUpdate, connectionsData.connectionsDataRemove); - }; - - /** - * before module destroy callback - * @param moduleElement - */ - let beforeDestroy = (moduleElement) => { - getConnectionElements(moduleElement).each((i, connectionElement) => { - removeConnectionPanel(connectionElement); - }); - }; - - /** - * init callback - * @param moduleElement - * @param mapId - * @param connectionData - */ - let initModule = (moduleElement, mapId, connectionData) => { - setModuleObserver(moduleElement, mapId); - }; - - /** - * get module element - * @param parentElement - * @param mapId - * @param connections - * @returns {*|jQuery|HTMLElement} - */ - let getModule = (parentElement, mapId, connections) => { - // create new module container - let moduleElement = $('
').append( - $('
', { - class: config.moduleHeadClass - }).append( - $('
', { - class: config.moduleHandlerClass - }), - $('
', { - text: 'Connection' - }), - getHeadlineToolbar() - ) - ); - - let rowElement = $('
', { - class: 'row' - }); - - moduleElement.append(rowElement); - - rowElement.append(getInfoPanelControl(mapId)); - - updateConnectionPanels(moduleElement, mapId, MapUtil.getDataByConnections(connections), []); - - return moduleElement; - }; - - return { - config: config, - getModule: getModule, - initModule: initModule, - beforeDestroy: beforeDestroy - }; -}); \ No newline at end of file diff --git a/public/js/v1.4.1/app/ui/form_element.js b/public/js/v1.4.1/app/ui/form_element.js index 54c82ba9..e37cf9b8 100644 --- a/public/js/v1.4.1/app/ui/form_element.js +++ b/public/js/v1.4.1/app/ui/form_element.js @@ -461,7 +461,7 @@ define([ * @param term search term */ function sortResultData (data, term){ - let levenshtein = function (a,b){ + let levenshtein = (a,b) => { let matrix = new Array(a.length+1); for(let i = 0; i < matrix.length; i++){ matrix[i] = new Array(b.length+1).fill(0); @@ -480,7 +480,7 @@ define([ matrix[ai][bi] = Math.min( matrix[ai-1][bi]+1, matrix[ai][bi-1]+1, - matrix[ai-1][bi-1]+(a[ai-1] == b[bi-1] ? 0 : 1) + matrix[ai-1][bi-1]+(a[ai-1] === b[bi-1] ? 0 : 1) ); } } @@ -488,7 +488,7 @@ define([ return matrix[a.length][b.length]; }; - data.sort(function(a,b){ + data.sort((a,b) => { let levA = levenshtein(term, a.name.toLowerCase()); let levB = levenshtein(term, b.name.toLowerCase()); return levA === levB ? 0 : (levA > levB ? 1 : -1); @@ -531,6 +531,7 @@ define([ for(let category in result){ // skip custom functions in case result = [] (array functions) if(result.hasOwnProperty(category)){ + // sort results (optional) sortResultData(result[category], page.term); data.results.push({ text: category, diff --git a/public/js/v1.4.1/app/ui/module/system_signature.js b/public/js/v1.4.1/app/ui/module/system_signature.js index b7fb2f8f..f3979aed 100644 --- a/public/js/v1.4.1/app/ui/module/system_signature.js +++ b/public/js/v1.4.1/app/ui/module/system_signature.js @@ -1220,7 +1220,7 @@ define([ searchable: true, title: 'id', type: 'string', - width: 15, + width: 12, class: [config.tableCellFocusClass, config.sigTableEditSigNameInput].join(' '), data: 'name', createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ diff --git a/public/js/v1.4.1/app/ui/module/system_signature_new.js b/public/js/v1.4.1/app/ui/module/system_signature_new.js deleted file mode 100644 index ef192dd4..00000000 --- a/public/js/v1.4.1/app/ui/module/system_signature_new.js +++ /dev/null @@ -1,2461 +0,0 @@ -/** - * System signature module - */ - -define([ - 'jquery', - 'app/init', - 'app/util', - 'bootbox', - 'app/counter', - 'app/map/map', - 'app/map/util', - 'app/ui/form_element' -], ($, Init, Util, bootbox, Counter, Map, MapUtil, FormElement) => { - 'use strict'; - - let config = { - // module info - modulePosition: 4, - moduleName: 'systemSignature', - moduleHeadClass: 'pf-module-head', // class for module header - moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler - - // system signature module - moduleTypeClass: 'pf-system-signature-module', // class for this module - - // headline toolbar - moduleHeadlineIconClass: 'pf-module-icon-button', // class for toolbar icons in the head - moduleHeadlineIconAddClass: 'pf-module-icon-button-add', // class for "add signature" icon - moduleHeadlineIconReaderClass: 'pf-module-icon-button-reader', // class for "signature reader" icon - moduleHeadlineIconLazyClass: 'pf-module-icon-button-lazy', // class for "lazy delete" toggle icon - moduleHeadlineProgressBarClass: 'pf-system-progress-scanned', // class for signature progress bar - - // tables - tableToolsActionClass: 'pf-table-tools-action', // class for "new signature" table (hidden) - - // table toolbar - sigTableClearButtonClass: 'pf-sig-table-clear-button', // class for "clear" signatures button - - // signature table - sigTableId: 'pf-sig-table-', // Table id prefix - sigTableClass: 'pf-sig-table', // Table class for all Signature Tables - sigTablePrimaryClass: 'pf-sig-table-primary', // class for primary sig table - sigTableSecondaryClass: 'pf-sig-table-secondary', // class for secondary sig table - sigTableRowIdPrefix: 'pf-sig-row_', // id prefix for table rows - - sigTableEditSigNameInput: 'pf-sig-table-edit-name-input', // class for editable fields (sig name) - - tableCellConnectionClass: 'pf-table-connection-cell', // class for "connection" cells - tableCellFocusClass: 'pf-table-focus-cell', // class for "tab-able" cells. enable focus() - tableCellCounterClass: 'pf-table-counter-cell', // class for "counter" cells - tableCellActionClass: 'pf-table-action-cell', // class for "action" cells - - // xEditable - editableNameInputClass: 'pf-editable-name', // class for "name" input - editableDescriptionInputClass: 'pf-editable-description', // class for "description" textarea - editableUnknownInputClass: 'pf-editable-unknown', // class for input fields (e.g. checkboxes) with "unknown" status - - signatureGroupsLabels: Util.getSignatureGroupOptions('label'), - signatureGroupsNames: Util.getSignatureGroupOptions('name') - }; - - let lockedTables = {}; // locked tables (e.g. disable cops&paste, disable table update) - - let sigNameCache = {}; // cache signature names - - let validSignatureNames = [ // allowed signature type/names - 'Cosmic Anomaly', - 'Cosmic Signature', - 'Kosmische Anomalie', - 'Kosmische Signatur', - 'Anomalie cosmique', - 'Signature cosmique', - 'Космическая аномалия', // == "Cosmic Anomaly" - 'Источники сигналов' // == "Cosmic Signature" - ]; - - let emptySignatureData = { - id: 0, - name: '', - groupId: 0, - typeId: 0 - }; - - let editableDefaults = { // xEditable default options for signature fields - url: Init.path.saveSignatureData, - dataType: 'json', - container: 'body', - highlight: false, // i use a custom implementation. xEditable uses inline styles for bg color animation -> does not work properly on datatables "sort" cols - error: function(jqXHR, newValue){ - let reason = ''; - let status = 'Error'; - if(jqXHR.statusText){ - reason = jqXHR.statusText; - }else if(jqXHR.name){ - // validation error new sig (new row data save function) - reason = jqXHR.name; - // re-open "name" fields (its a collection of fields but we need "id" field) - jqXHR.name.field.$element.editable('show'); - }else{ - reason = jqXHR.responseJSON.text; - status = jqXHR.status; - } - - Util.showNotify({title: status + ': save signature', text: reason, type: 'error'}); - $(document).setProgramStatus('problem'); - return reason; - } - }; - - /** - * get custom "metaData" from dataTables API - * @param tableApi - * @returns {*} - */ - let getTableMetaData = tableApi => { - let data = null; - if(tableApi){ - data = tableApi.init().pfMeta; - } - return data; - }; - - /** - * lock signature tableApi and lockType - * @param tableApi - * @param lockType - */ - let lockTable = (tableApi, lockType = 'update') => { - let metaData = getTableMetaData(tableApi); - if(metaData.systemId){ - if( !lockedTables.hasOwnProperty(metaData.systemId) ){ - lockedTables[metaData.systemId] = {}; - } - lockedTables[metaData.systemId][lockType] = true; - }else{ - console.warn('metaData.systemId required in lockTable()', metaData.systemId); - } - }; - - /** - * check whether a signature tableApi is locked by lockType - * @param tableApi - * @param lockType - * @returns {boolean} - */ - let isLockedTable = (tableApi, lockType = 'update') => { - let locked = false; - if(tableApi){ - let metaData = getTableMetaData(tableApi); - if(metaData.systemId){ - if( - lockedTables.hasOwnProperty(metaData.systemId) && - lockedTables[metaData.systemId].hasOwnProperty(lockType) - ){ - locked = true; - } - }else{ - console.warn('metaData.systemId required in isLockedTable()', metaData.systemId); - } - } - - return locked; - }; - - /** - * unlock signature tableApi and lockType - * @param tableApi - * @param lockType - */ - let unlockTable = (tableApi, lockType = 'update') => { - if(tableApi){ - let metaData = getTableMetaData(tableApi); - if(isLockedTable(tableApi, lockType)){ - delete lockedTables[metaData.systemId][lockType]; - } - if( - lockedTables.hasOwnProperty(metaData.systemId) && - !Object.getOwnPropertyNames(lockedTables[metaData.systemId]).length - ){ - delete lockedTables[metaData.systemId]; - } - } - }; - - /** - * get dataTable id - * @param mapId - * @param systemId - * @param tableType - * @returns {string} - */ - let getTableId = (mapId, systemId, tableType) => config.sigTableId + [mapId, systemId, tableType].join('-'); - - /** - * get a dataTableApi instance from global cache - * @param mapId - * @param systemId - * @param tableType - * @returns {*} - */ - let getDataTableInstance = (mapId, systemId, tableType) => { - let instance = null; - let table = $.fn.dataTable.tables({ visible: false, api: true }).table('#' + getTableId(mapId, systemId, tableType)); - if(table.node()){ - instance = table; - } - return instance; - }; - - /** - * Update/set tooltip for an element - * @param element - * @param title - */ - let updateTooltip = (element, title) => { - $(element).attr('data-container', 'body') - .attr('title', title.toUpperCase()) - .tooltip('fixTitle').tooltip('setContent'); - }; - - /** - * sum up all options in nested (or not nested) object of objects - * -> e.g. - * { - * first: { - * count = [4, 2, 1] - * test = { ... } - * }, - * second: { - * count = [12, 13] - * test = { ... } - * } - * } - * -> getOptionsCount('count', obj) => 5; - * @param key - * @param obj - * @returns {number} - */ - let getOptionsCount = (key, obj) => { - let sum = 0; - for(let entry of obj){ - if(entry.hasOwnProperty(key)){ - sum += entry[key].length; - }else{ - sum++; - } - } - return sum; - }; - - /** - * get possible frig holes that could spawn in a system - * filtered by "systemTypeId" - * @param systemTypeId - * @returns {{}} - */ - let getFrigateHolesBySystem = systemTypeId => { - let signatureNames = {}; - if(Init.frigateWormholes[systemTypeId]){ - signatureNames = Init.frigateWormholes[systemTypeId]; - } - return signatureNames; - }; - - /** - * get all signature types that can exist for a given system - * -> result is partially cached - * @param systemData - * @param systemTypeId - * @param areaId - * @param groupId - * @returns {Array} - */ - let getAllSignatureNames = (systemData, systemTypeId, areaId, groupId) => { - systemTypeId = parseInt(systemTypeId || 0); - areaId = parseInt(areaId || 0); - groupId = parseInt(groupId || 0); - let newSelectOptions = []; - let newSelectOptionsCount = 0; - - if(!systemTypeId || !areaId || !groupId){ - return newSelectOptions; - } - - let cacheKey = [systemTypeId, areaId, groupId].join('_'); - - // check for cached signature names - if(sigNameCache.hasOwnProperty( cacheKey )){ - // cached signatures do not include static WHs! - // -> ".slice(0)" creates copy - newSelectOptions = sigNameCache[cacheKey].slice(0); - newSelectOptionsCount = getOptionsCount('children', newSelectOptions); - }else{ - // get new Options ---------- - // get all possible "static" signature names by the selected groupId - let tempSelectOptions = Util.getAllSignatureNames(systemTypeId, areaId, groupId); - - // format options into array with objects advantages: keep order, add more options (whs), use optgroup - if(tempSelectOptions){ - let fixSelectOptions = []; - for(let key in tempSelectOptions){ - if ( - key > 0 && - tempSelectOptions.hasOwnProperty(key) - ){ - newSelectOptionsCount++; - fixSelectOptions.push({value: newSelectOptionsCount, text: tempSelectOptions[key]}); - } - } - - if(newSelectOptionsCount > 0){ - if(groupId === 5){ - // "wormhole" selected => multiple available - newSelectOptions.push({ text: 'Wandering', children: fixSelectOptions}); - }else{ - newSelectOptions = fixSelectOptions; - } - } - } - - // wormhole (cached signatures) - if( groupId === 5 ){ - - // add possible frigate holes - let frigateHoles = getFrigateHolesBySystem(areaId); - let frigateWHData = []; - for(let frigKey in frigateHoles){ - if ( - frigKey > 0 && - frigateHoles.hasOwnProperty(frigKey) - ){ - newSelectOptionsCount++; - frigateWHData.push( {value: newSelectOptionsCount, text: frigateHoles[frigKey]} ); - } - } - - if(frigateWHData.length > 0){ - newSelectOptions.push({ text: 'Frigate', children: frigateWHData}); - } - - // add possible incoming holes - let incomingWHData = []; - for(let incomingKey in Init.incomingWormholes){ - if ( - incomingKey > 0 && - Init.incomingWormholes.hasOwnProperty(incomingKey) - ){ - newSelectOptionsCount++; - incomingWHData.push( {value: newSelectOptionsCount, text: Init.incomingWormholes[incomingKey]} ); - } - } - - if(incomingWHData.length > 0){ - newSelectOptions.push({ text: 'Incoming', children: incomingWHData}); - } - }else{ - // groups without "children" (optgroup) should be sorted by "value" - // this is completely optional and not necessary! - newSelectOptions = newSelectOptions.sortBy('text'); - } - - // update cache (clone array) -> further manipulation to this array, should not be cached - sigNameCache[cacheKey] = newSelectOptions.slice(0); - } - - // static wormholes (DO NOT CACHE) (not all C2 WHs have the same statics,... - if( groupId === 5 ){ - // add static WH(s) for this system - if(systemData.statics){ - let staticWHData = []; - for(let wormholeName of systemData.statics){ - let wormholeData = Object.assign({}, Init.wormholes[wormholeName]); - let staticWHName = wormholeData.name + ' - ' + wormholeData.security; - - newSelectOptionsCount++; - staticWHData.push( {value: newSelectOptionsCount, text: staticWHName} ); - } - - if(staticWHData.length > 0){ - newSelectOptions.unshift({ text: 'Static', children: staticWHData}); - } - } - } - - return newSelectOptions; - }; - - /** - * get all signature types that can exist for a system (jQuery obj) - * @param systemElement - * @param groupId - * @returns {Array} - */ - let getAllSignatureNamesBySystem = (systemElement, groupId) => { - let systemTypeId = systemElement.data('typeId'); - let areaId = Util.getAreaIdBySecurity(systemElement.data('security')); - let systemData = {statics: systemElement.data('statics')}; - return getAllSignatureNames(systemData, systemTypeId, areaId, groupId); - }; - - /** - * get all connection select options - * @param mapId - * @param systemData - * @returns {Array} - */ - let getSignatureConnectionOptions = (mapId, systemData) => { - let map = Map.getMapInstance( mapId ); - let systemId = MapUtil.getSystemId(mapId, systemData.id); - let systemConnections = MapUtil.searchConnectionsBySystems(map, [systemId], 'wh'); - let newSelectOptions = []; - let connectionOptions = []; - - for(let systemConnection of systemConnections){ - let connectionData = MapUtil.getDataByConnection(systemConnection); - - // connectionId is required (must be stored) - if(connectionData.id){ - // check whether "source" or "target" system is relevant for this connection - // -> hint "source" === 'target' --> loop - if(systemData.id !== connectionData.target){ - let targetSystemData = MapUtil.getSystemData(mapId, connectionData.target); - if(targetSystemData){ - // take target... - connectionOptions.push({ - value: connectionData.id, - text: connectionData.targetAlias + ' - ' + targetSystemData.security - }); - } - }else if(systemData.id !== connectionData.source){ - let sourceSystemData = MapUtil.getSystemData(mapId, connectionData.source); - if(sourceSystemData){ - // take source... - connectionOptions.push({ - value: connectionData.id, - text: connectionData.sourceAlias + ' - ' + sourceSystemData.security - }); - } - } - } - } - - if(connectionOptions.length > 0){ - newSelectOptions.push({ text: 'System', children: connectionOptions}); - } - - return newSelectOptions; - }; - - /** - * show/hides a table
rowElement - * @param rowElement - */ - let toggleTableRow = rowElement => { - - let toggleTableRowExecutor = (resolve, reject) => { - let cellElements = rowElement.children('td'); - let duration = 350; - // wrap each
into a container (for better animation performance) - // slideUp new wrapper divs - if(rowElement.is(':visible')){ - // hide row - - // stop sig counter by adding a stopClass to each , remove padding - cellElements.addClass('stopCounter') - .velocity({ - paddingTop: [0, '4px'], - paddingBottom: [0, '4px'], - opacity: [0, 1] - },{ - duration: duration, - easing: 'linear' - }).wrapInner('
') - .children() - .css({ - 'willChange': 'height' - }).velocity('slideUp', { - duration: duration, - easing: 'linear', - complete: function(animationElements){ - // remove wrapper - $(animationElements).children().unwrap(); - - resolve({ - action: 'rowHidden', - row: rowElement - }); - } - }); - }else{ - // show row - - // remove padding on "hidden" cells for smother animation - cellElements.css({ - 'padding-top': 0, - 'padding-bottom': 0, - 'willChange': 'padding-top, padding-top, height' - }); - - // add hidden wrapper for ea - cellElements.wrapInner($('
').hide()); - - // show row for padding animation - rowElement.show(); - - cellElements.velocity({ - paddingTop: ['4px', 0], - paddingBottom: ['4px', 0] - },{ - duration: duration, - queue: false, - complete: function(){ - // animate
wrapper - cellElements.children() - .css({ - 'willChange': 'height' - }).velocity('slideDown', { - duration: duration, - complete: function(animationElements){ - // remove wrapper - for(let i = 0; i < animationElements.length; i++){ - let currentWrapper = $(animationElements[i]); - if(currentWrapper.children().length > 0){ - currentWrapper.children().unwrap(); - }else{ - currentWrapper.parent().html( currentWrapper.html() ); - } - } - - resolve({ - action: 'rowShown', - row: rowElement - }); - } - }); - } - }); - } - }; - - return new Promise(toggleTableRowExecutor); - }; - - /** - * update scanned signatures progress bar - * @param tableApi - * @param options - */ - let updateScannedSignaturesBar = (tableApi, options) => { - let tableElement = tableApi.table().node(); - let moduleElement = $(tableElement).parents('.' + config.moduleTypeClass); - let progressBar = moduleElement.find('.progress-bar'); - let progressBarLabel = moduleElement.find('.progress-label-right'); - - let percent = 0; - let progressBarType = ''; - let columnGroupData = tableApi.column('group:name').data(); - let sigCount = columnGroupData.length; - let sigIncompleteCount = columnGroupData.filter((value, index) => !value).length; - - if(sigCount){ - percent = 100 - Math.round( 100 / sigCount * sigIncompleteCount ); - } - - if(percent < 30){ - progressBarType = 'progress-bar-danger' ; - }else if(percent < 100){ - progressBarType = 'progress-bar-warning'; - }else{ - progressBarType = 'progress-bar-success'; - } - - progressBarLabel.text(percent + '%'); - progressBar.removeClass().addClass('progress-bar').addClass(progressBarType); - progressBar.attr('aria-valuenow', percent); - progressBar.css({width: percent + '%'}); - - // show notifications - if(options.showNotice !== false){ - let notification = (sigCount - sigIncompleteCount) + ' / ' + sigCount + ' (' + percent + '%) signatures scanned'; - - if(percent < 100){ - Util.showNotify({title: 'Unscanned signatures', text: notification, type: 'info'}); - }else{ - Util.showNotify({title: 'System is scanned', text: notification, type: 'success'}); - } - } - }; - - /** - * open "signature reader" dialog for signature table - * @param systemData - */ - $.fn.showSignatureReaderDialog = function(systemData){ - let moduleElement = $(this); - - requirejs(['text!templates/dialog/signature_reader.html', 'mustache'], (template, Mustache) => { - let signatureReaderDialog = bootbox.dialog({ - title: 'Signature reader', - message: Mustache.render(template, {}), - buttons: { - close: { - label: 'cancel', - className: 'btn-default' - }, - success: { - label: ' update signatures', - className: 'btn-success', - callback: function (){ - let form = this.find('form'); - let formData = form.getFormValues(); - let signatureOptions = { - deleteOld: (formData.deleteOld) ? 1 : 0 - }; - updateSignatureTableByClipboard(moduleElement, systemData, formData.clipboard, signatureOptions); - } - } - } - }); - - // dialog shown event - signatureReaderDialog.on('shown.bs.modal', function(e){ - signatureReaderDialog.initTooltips(); - - // set focus on sig-input textarea - signatureReaderDialog.find('textarea').focus(); - }); - }); - }; - - /** - * parses a copy&paste string from ingame scanning window - * @param systemData - * @param clipboard - * @returns {Array} - */ - let parseSignatureString = (systemData, clipboard) => { - let signatureData = []; - - if(clipboard.length){ - let signatureRows = clipboard.split(/\r\n|\r|\n/g); - let signatureGroupOptions = config.signatureGroupsNames; - let invalidSignatures = 0; - - for(let i = 0; i < signatureRows.length; i++){ - let rowData = signatureRows[i].split(/\t/g); - if(rowData.length === 6){ - // check if sig Type = anomaly or combat site - if(validSignatureNames.indexOf( rowData[1] ) !== -1){ - - let sigGroup = $.trim(rowData[2]).toLowerCase(); - let sigDescription = $.trim(rowData[3]); - let sigGroupId = 0; - let typeId = 0; - - // get groupId by groupName - for(let groupOption of signatureGroupOptions){ - let reg = new RegExp(groupOption.text, 'i'); - if(reg.test(sigGroup)){ - sigGroupId = groupOption.value; - break; - } - } - - // wormhole type cant be extracted from signature string -> skip function call - if(sigGroupId !== 5){ - // try to get "typeId" by description string - typeId = Util.getSignatureTypeIdByName(systemData, sigGroupId, sigDescription); - - // set signature name as "description" if signature matching failed - sigDescription = (typeId === 0) ? sigDescription : ''; - }else{ - sigDescription = ''; - } - - // map array values to signature Object - let signatureObj = { - systemId: systemData.id, - name: $.trim( rowData[0] ).toLowerCase(), - groupId: sigGroupId, - typeId: typeId, - description: sigDescription - }; - - signatureData.push(signatureObj); - }else{ - invalidSignatures++; - } - } - } - - if(invalidSignatures > 0){ - let notification = invalidSignatures + ' / ' + signatureRows.length + ' signatures invalid'; - Util.showNotify({title: 'Invalid signature(s)', text: notification, type: 'warning'}); - } - } - - return signatureData; - }; - - /** - * updates the signature table with all signatures pasted into the "signature reader" dialog - * -> Hint: copy&paste signature data (without any open dialog) will add signatures as well - * @param tableApi - * @param systemData - * @param clipboard data stream - * @param options - */ - let updateSignatureTableByClipboard = (tableApi, systemData, clipboard, options) => { - if(isLockedTable(tableApi, 'clipboard')) return; - - let saveSignatureData = signatureData => { - // lock update function until request is finished - lockTable(tableApi); - - // lock copy during request (prevent spamming (ctrl + c ) - lockTable(tableApi, 'clipboard'); - - let requestData = { - signatures: signatureData, - deleteOld: (options.deleteOld) ? 1 : 0, - systemId: parseInt(systemData.id) - }; - - $.ajax({ - type: 'POST', - url: Init.path.saveSignatureData, - data: requestData, - dataType: 'json', - context: { - tableApi: tableApi - } - }).done(function(responseData){ - // unlock table for update - unlockTable(this.tableApi); - // updates table with new/updated signature information - updateSignatureTable(this.tableApi, responseData.signatures, false); - }).fail(function( jqXHR, status, error){ - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': Update signatures', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }).always(function(){ - unlockTable(this.tableApi); - unlockTable(this.tableApi, 'clipboard'); - }); - }; - - // parse input stream - let signatureData = parseSignatureString(systemData, clipboard); - if(signatureData.length > 0){ - // valid signature data parsed - - // check if signatures will be added to a system where character is currently in - // if user is not in any system -> id === undefined -> no "confirmation required - let currentLocationData = Util.getCurrentLocationData(); - if( - currentLocationData.id && - currentLocationData.id !== systemData.id - ){ - - let systemNameStr = (systemData.name === systemData.alias) ? '"' + systemData.name + '"' : '"' + systemData.alias + '" (' + systemData.name + ')'; - systemNameStr = '' + systemNameStr + ''; - - let msg = 'Update signatures in ' + systemNameStr + ' ? This not your current location, "' + currentLocationData.name + '" !'; - bootbox.confirm(msg, function(result){ - if(result){ - saveSignatureData(signatureData); - } - }); - }else{ - // current system selected -> no "confirmation" required - saveSignatureData(signatureData); - } - } - }; - - /** - * deletes signature rows from signature table - * @param tableApi - * @param rows - */ - let deleteSignatures = (tableApi, rows) => { - // get unique id array from rows -> in case there are 2 rows with same id -> you never know - let signatureIds = [...new Set(rows.data().toArray().map(rowData => rowData.id))]; - - let requestData = { - signatureIds: signatureIds - }; - - $.ajax({ - type: 'POST', - url: Init.path.deleteSignatureData, - data: requestData, - dataType: 'json', - context: { - tableApi: tableApi - } - }).done(function(responseData){ - // promises for all delete rows - let promisesToggleRow = []; - // get deleted rows -> match with response data - let rows = this.tableApi.rows((idx, rowData, node) => responseData.deletedSignatureIds.includes(rowData.id)); - // toggle hide animation for rows one by one... - rows.every(function(rowIdx, tableLoop, rowLoop){ - let row = this; - let rowElement = row.nodes().to$(); - - rowElement.pulseBackgroundColor('deleted'); - - promisesToggleRow.push(toggleTableRow(rowElement)); - }); - - // ... all hide animations done ... - Promise.all(promisesToggleRow).then(payloads => { - // ... get deleted (hide animation done) and delete them - this.tableApi.rows(payloads.map(payload => payload.row)).remove().draw(); - - // update signature bar - updateScannedSignaturesBar(this.tableApi, {showNotice: false}); - - // update connection conflicts - checkConnectionConflicts(); - - let notificationOptions = { - type: 'success' - }; - if(payloads.length === 1){ - notificationOptions.title = 'Signature deleted'; - }else{ - notificationOptions.title = payloads.length + ' Signatures deleted '; - } - Util.showNotify(notificationOptions); - }); - }).fail(function( jqXHR, status, error){ - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': Delete signature', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }); - }; - - /** - * updates a single cell with new data (e.g. "updated" cell) - * @param tableApi - * @param rowIndex - * @param columnSelector - * @param data - */ - let updateSignatureCell = (tableApi, rowIndex, columnSelector, data) => { - tableApi.cell(rowIndex, columnSelector).data(data); - }; - - /** - * check connectionIds for conflicts (multiple signatures -> same connection) - * -> show "conflict" icon next to select - */ - let checkConnectionConflicts = () => { - setTimeout(() => { - let connectionSelects = $('.' + config.tableCellConnectionClass + '.editable'); - let connectionIds = []; - let duplicateConnectionIds = []; - let groupedSelects = []; - - connectionSelects.each(function(){ - let select = $(this); - let value = parseInt(select.editable('getValue', true) )|| 0; - - if( - connectionIds.indexOf(value) > -1 && - duplicateConnectionIds.indexOf(value) === -1 - ){ - // duplicate found - duplicateConnectionIds.push(value); - } - - if(groupedSelects[value] !== undefined){ - groupedSelects[value].push(select[0]); - }else{ - groupedSelects[value] = [select[0]]; - } - - connectionIds.push(value); - }); - - // update "conflict" icon next to select label for connectionIds - connectionSelects.each(function(){ - let select = $(this); - let value = parseInt(select.editable('getValue', true) )|| 0; - let conflictIcon = select.find('.fa-exclamation-triangle'); - if( - duplicateConnectionIds.indexOf(value) > -1 && - groupedSelects[value].indexOf(select[0]) > -1 - ){ - conflictIcon.removeClass('hide'); - }else{ - conflictIcon.addClass('hide'); - } - }); - }, 200); - }; - - /** - * get group label by groupId - * @param groupId - * @returns {string} - */ - let getGroupLabelById = (groupId) => { - let options = config.signatureGroupsLabels.filter(option => option.value === groupId); - return options.length ? options[0].text : ''; - }; - - /** - * helper function - get cell by columnSelector from same row as cell - * @param tableApi - * @param cell - * @param columnSelector - * @returns {*} - */ - let getNeighboringCell = (tableApi, cell, columnSelector) => { - return tableApi.cell(tableApi.row(cell).index(), columnSelector); - }; - - /** - * get next cell by columnSelector - * @param tableApi - * @param cell - * @param columnSelectors - * @returns {*} - */ - let searchNextCell = (tableApi, cell, columnSelectors) => { - if(columnSelectors.length){ - // copy selectors -> .shift() modifies the orig array, important! - columnSelectors = columnSelectors.slice(0); - let nextCell = getNeighboringCell(tableApi, cell, columnSelectors.shift()); - let nextCellElement = nextCell.nodes().to$(); - if( nextCellElement.data('editable') ){ - // cell is xEditable field -> skip "disabled" OR check value - let nextCellValue = nextCellElement.editable('getValue', true); - if( - [0, null].includes(nextCellValue) && - !nextCellElement.data('editable').options.disabled - ){ - // xEditable value is empty - return nextCell; - }else{ - // search next cell - return searchNextCell(tableApi, cell, columnSelectors); - } - }else if( nextCell.index().column === tableApi.column(-1).index() ){ - // NO xEditable cell BUT last column (=> action cell) -> OK - return nextCell; - }else{ - console.error('No cell found for activation!'); - } - }else{ - // return origin cell - return tableApi.cell(cell); - } - }; - - /** - * make cell active -> focus() + show xEditable - * @param cell - */ - let activateCell = (cell) => { - let cellElement = cell.nodes().to$(); - // NO xEditable - cellElement.focus(); - - if( cellElement.data('editable') ){ - // cell is xEditable field -> show xEditable form - cellElement.editable('show'); - } - }; - - /** - * search neighboring cell (same row) and set "active" -> show editable - * @param tableApi - * @param cell - * @param columnSelectors - */ - let activateNextCell = (tableApi, cell, columnSelectors) => { - let nextCell = searchNextCell(tableApi, cell, columnSelectors); - activateCell(nextCell); - let test; - }; - - /** - * helper function - set 'save' observer for xEditable cell - * -> show "neighboring" xEditable field - * @param tableApi - * @param cell - * @param columnSelectorsAjax - used for Ajax save (edit signature) - * @param columnSelectorsDry - used for dry save (new signature) - */ - let editableOnSave = (tableApi, cell, columnSelectorsAjax = [], columnSelectorsDry = []) => { - $(cell).on('save', function(e, params){ - if(params.response){ - // send by Ajax - activateNextCell(tableApi, cell, columnSelectorsAjax); - }else{ - // dry save - no request - activateNextCell(tableApi, cell, columnSelectorsDry); - } - }); - }; - - /** - * helper function - set 'hidden' observer for xEditable cell - * -> set focus() on xEditable field - * @param tableApi - * @param cell - */ - let editableOnHidden = (tableApi, cell) => { - $(cell).on('hidden', function(e, reason){ - // re-focus element on close (keyboard navigation) - // 'save' event handles default focus (e.g. open new xEditable) - // 'hide' handles all the rest (experimental) - if(reason !== 'save'){ - this.focus(); - } - }); - }; - - /** - * helper function - set 'shown' observer for xEditable type cell - * -> enable Select2 for xEditable form - * @param cell - */ - let editableGroupOnShown = cell => { - $(cell).on('shown', function(e, editable){ - let inputField = editable.input.$input; - inputField.addClass('pf-select2').initSignatureGroupSelect(); - }); - }; - - /** - * helper function - set 'save' observer for xEditable group cell - * -> update scanned signature bar - * @param tableApi - * @param cell - */ - let editableGroupOnSave = (tableApi, cell) => { - $(cell).on('save', function(e, params){ - if(params.response){ - // send by Ajax - updateScannedSignaturesBar(tableApi, {showNotice: true}); - } - }); - }; - - /** - * helper function - set 'init' observer for xEditable type cell - * -> disable xEditable field if no options available - * @param cell - */ - let editableTypeOnInit = cell => { - $(cell).on('init', function(e, editable){ - if(!editable.options.source().length){ - editableDisable($(this)); - } - }); - }; - - /** - * helper function - set 'shown' observer for xEditable type cell - * -> enable Select2 for xEditable form - * @param cell - */ - let editableTypeOnShown = cell => { - $(cell).on('shown', function(e, editable){ - // destroy possible open popovers (e.g. wormhole types) - $(this).destroyPopover(true); - - let inputField = editable.input.$input; - let hasOptGroups = inputField.has('optgroup').length > 0; - inputField.addClass('pf-select2').initSignatureTypeSelect({}, hasOptGroups); - }); - }; - - /** - * helper function - set 'shown' observer for xEditable description cell - * -> change height for "new signature" table wrapper - * @param cell - */ - let editableDescriptionOnShown = cell => { - $(cell).on('shown', function(e, editable){ - $(this).parents('.' + config.tableToolsActionClass).css( 'height', '+=35px' ); - }); - }; - - /** - * helper function - set 'hidden' observer for xEditable description cell - * -> change height for "new signature" table wrapper - * @param cell - */ - let editableDescriptionOnHidden = cell => { - $(cell).on('hidden', function(e, editable){ - $(this).parents('.' + config.tableToolsActionClass).css( 'height', '-=35px' ); - }); - }; - - /** - * helper function - set 'init' observer for xEditable connection cell - * -> set focus() on xEditable field - * @param cell - */ - let editableConnectionOnInit = cell => { - $(cell).on('init', function(e, editable){ - if(editable.value > 0){ - // empty connection selects ON INIT don´t make a difference for conflicts - checkConnectionConflicts(); - } - }); - }; - - /** - * helper function - set 'shown' observer for xEditable connection cell - * -> enable Select2 for xEditable form - * @param cell - */ - let editableConnectionOnShown = cell => { - $(cell).on('shown', function(e, editable){ - let inputField = editable.input.$input; - inputField.addClass('pf-select2').initSignatureConnectionSelect(); - }); - }; - - /** - * helper function - set 'save' observer for xEditable connection cell - * -> check connection conflicts - * @param cell - */ - let editableConnectionOnSave = cell => { - $(cell).on('save', function(e, params){ - checkConnectionConflicts(); - }); - }; - - /** - * enable xEditable element - * @param element - */ - let editableEnable = element => { - element.editable('enable'); - // (re)-enable focus on element by tabbing, xEditable removes "tabindex" on 'disable' - element.attr('tabindex', 0); - }; - - /** - * disable xEditable element - * @param element - */ - let editableDisable = element => { - element.editable('disable'); - // xEditable sets 'tabindex = -1' - }; - - /** - * get dataTables default options for signature tables - * @param mapId - * @param systemData - * @returns {{}} - */ - let getSignatureDataTableDefaults = (mapId, systemData) => { - - /** - * add map/system specific data for each editable field in the sig-table - * @param params - * @returns {*} - */ - let modifyFieldParamsOnSend = params => { - params.systemId = systemData.id; - return params; - }; - - let dataTableDefaults = { - pfMeta: { - 'mapId': mapId, - 'systemId': systemData.id - }, - order: [1, 'asc'], - rowId: rowData => config.sigTableRowIdPrefix + rowData.id, - language: { - emptyTable: 'No signatures added', - info: 'Showing _START_ to _END_ of _TOTAL_ signatures', - infoEmpty: 'Showing 0 to 0 of 0 signatures', - infoFiltered: '( from _MAX_ total)', - lengthMenu: 'Show _MENU_', - zeroRecords: 'No signatures recorded' - }, - columnDefs: [ - { - targets: 0, - name: 'status', - orderable: true, - searchable: false, - title: '', - width: 2, - class: ['text-center'].join(' '), - data: 'updated', - type: 'html', - render: { - _: (cellData, type, rowData, meta) => { - let value = ''; - if(cellData && cellData.character){ - value = Util.getStatusInfoForCharacter(cellData.character, 'class'); - } - - if(type === 'display'){ - value = ''; - } - return value; - } - } - },{ - targets: 1, - name: 'id', - orderable: true, - searchable: true, - title: 'id', - type: 'string', - width: 15, - class: [config.tableCellFocusClass, config.sigTableEditSigNameInput].join(' '), - data: 'name', - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let tableApi = this.api(); - - updateTooltip(cell, cellData); - - editableOnSave(tableApi, cell, [], ['group:name', 'type:name', 'action:name']); - editableOnHidden(tableApi, cell); - - $(cell).editable($.extend({ - mode: 'popup', - type: 'text', - title: 'signature id', - name: 'name', - pk: rowData.id || null, - emptytext: '? ? ?', - value: cellData, - inputclass: config.editableNameInputClass, - display: function(value){ - // change display value to first 3 letters - $(this).text($.trim( value.substr(0, 3) ).toLowerCase()); - }, - validate: function(value){ - let msg = false; - if($.trim(value).length < 3){ - msg = 'Id is less than min of "3"'; - }else if($.trim(value).length > 10){ - msg = 'Id is more than max of "10"'; - } - - if(msg){ - return {newValue: value, msg: msg, field: this}; - } - }, - params: modifyFieldParamsOnSend, - success: function(response, newValue){ - tableApi.cell(cell).data(newValue); - - $(this).pulseBackgroundColor('changed'); - updateTooltip(cell, newValue); - - if(response){ - let newRowData = response.signatures[0]; - updateSignatureCell(tableApi, rowIndex, 'status:name', newRowData.updated); - updateSignatureCell(tableApi, rowIndex, 'updated:name', newRowData.updated.updated); - } - tableApi.draw(); - } - }, editableDefaults)); - } - },{ - targets: 2, - name: 'group', - orderable: true, - searchable: true, - title: 'group', - type: 'string', // required for sort/filter because initial data type is numeric - width: 40, - class: [config.tableCellFocusClass].join(' '), - data: 'groupId', - render: { - sort: getGroupLabelById, - filter: getGroupLabelById - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let tableApi = this.api(); - - editableOnSave(tableApi, cell, ['type:name'], ['type:name', 'action:name']); - editableOnHidden(tableApi, cell); - editableGroupOnShown(cell); - editableGroupOnSave(tableApi, cell); - - $(cell).editable($.extend({ - mode: 'popup', - type: 'select', - title: 'group', - name: 'groupId', - pk: rowData.id || null, - emptytext: 'unknown', - onblur: 'submit', - showbuttons: false, - value: cellData, - prepend: [{value: 0, text: ''}], - params: modifyFieldParamsOnSend, - source: config.signatureGroupsLabels, - display: function(value, sourceData){ - let selected = $.fn.editableutils.itemsByValue(value, sourceData); - if(selected.length && selected[0].value > 0){ - $(this).html(selected[0].text); - }else{ - $(this).empty(); - } - }, - validate: function(value){ - // convert string to int -> important for further processing - // -> on submit record (new signature) validate() is called and no error should be returned - // value should already be integer - if( !Number.isInteger(value) ){ - return {newValue: parseInt(value) || 0, msg: null}; - } - }, - success: function(response, newValue){ - tableApi.cell(cell).data(newValue); - - $(this).pulseBackgroundColor('changed'); - - if(response){ - let newRowData = response.signatures[0]; - updateSignatureCell(tableApi, rowIndex, 'status:name', newRowData.updated); - updateSignatureCell(tableApi, rowIndex, 'updated:name', newRowData.updated.updated); - } - tableApi.draw(); - - // find related "type" select (same row) and change options - let signatureTypeCell = getNeighboringCell(tableApi, cell, 'type:name'); - let signatureTypeField = signatureTypeCell.nodes().to$(); - - let typeOptions = getAllSignatureNames( - systemData, - systemData.type.id, - Util.getAreaIdBySecurity(systemData.security), - newValue - ); - signatureTypeField.editable('option', 'source', typeOptions); - - if( - newValue > 0 && - typeOptions.length > 0 - ){ - editableEnable(signatureTypeField); - }else{ - editableDisable(signatureTypeField); - } - signatureTypeCell.data(0); - signatureTypeField.editable('setValue', 0); - - - // find "connection" select (same row) and change "enabled" flag - let signatureConnectionCell = getNeighboringCell(tableApi, cell, 'connection:name'); - let signatureConnectionField = signatureConnectionCell.nodes().to$(); - - if(newValue === 5){ - // wormhole - editableEnable(signatureConnectionField); - }else{ - checkConnectionConflicts(); - editableDisable(signatureConnectionField); - } - signatureConnectionCell.data(0); - signatureConnectionField.editable('setValue', 0); - } - }, editableDefaults)); - } - },{ - targets: 3, - name: 'type', - orderable: false, - searchable: false, - title: 'type', - type: 'string', // required for sort/filter because initial data type is numeric - width: 180, - class: [config.tableCellFocusClass].join(' '), - data: 'typeId', - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let tableApi = this.api(); - - editableOnSave(tableApi, cell, ['connection:name'], ['action:name']); - editableOnHidden(tableApi, cell); - editableTypeOnInit(cell); - editableTypeOnShown(cell); - - $(cell).editable($.extend({ - mode: 'popup', - type: 'select', - title: 'type', - name: 'typeId', - pk: rowData.id || null, - emptytext: 'unknown', - onblur: 'submit', - showbuttons: false, - disabled: rowData.groupId <= 0, // initial disabled if groupId not set - value: cellData, - prepend: [{value: 0, text: ''}], - params: modifyFieldParamsOnSend, - source: function(){ - // get current row data (important!) - // -> "rowData" param is not current state, values are "on createCell()" state - let rowData = tableApi.row($(cell).parents('tr')).data(); - - let typeOptions = getAllSignatureNames( - systemData, - systemData.type.id, - Util.getAreaIdBySecurity(systemData.security), - rowData.groupId - ); - return typeOptions; - }, - display: function(value, sourceData){ - let selected = $.fn.editableutils.itemsByValue(value, sourceData); - if(selected.length && selected[0].value > 0){ - $(this).html(FormElement.formatSignatureTypeSelectionData({text: selected[0].text})); - }else{ - $(this).empty(); - } - }, - validate: function(value){ - // convert string to int -> important for further processing - // -> on submit record (new signature) validate() is called and no error should be returned - // value should already be integer - if( !Number.isInteger(value) ){ - return {newValue: parseInt(value) || 0, msg: null}; - } - }, - success: function(response, newValue){ - tableApi.cell(cell).data(newValue); - - $(this).pulseBackgroundColor('changed'); - - if(response){ - let newRowData = response.signatures[0]; - updateSignatureCell(tableApi, rowIndex, 'status:name', newRowData.updated); - updateSignatureCell(tableApi, rowIndex, 'updated:name', newRowData.updated.updated); - } - tableApi.draw(); - } - }, editableDefaults)); - } - },{ - targets: 4, - name: 'description', - orderable: false, - searchable: false, - title: 'description', - class: [config.tableCellFocusClass, config.tableCellActionClass].join(' '), - type: 'html', - data: 'description', - defaultContent: '', - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let tableApi = this.api(); - - editableOnSave(tableApi, cell, [], ['action:name']); - editableOnHidden(tableApi, cell); - editableDescriptionOnShown(cell); - editableDescriptionOnHidden(cell); - - $(cell).editable($.extend({ - mode: 'inline', - type: 'textarea', - title: 'description', - name: 'description', - pk: rowData.id || null, - emptytext: '', - onblur: 'submit', - showbuttons: false, - inputclass: config.editableDescriptionInputClass, - emptyclass: config.moduleHeadlineIconClass, - params: modifyFieldParamsOnSend, - success: function(response, newValue){ - tableApi.cell(cell).data(newValue); - - $(this).pulseBackgroundColor('changed'); - - if(response){ - let newRowData = response.signatures[0]; - updateSignatureCell(tableApi, rowIndex, 'status:name', newRowData.updated); - updateSignatureCell(tableApi, rowIndex, 'updated:name', newRowData.updated.updated); - } - tableApi.draw(); - } - }, editableDefaults)); - } - },{ - targets: 5, - name: 'connection', - orderable: false, - searchable: false, - title: 'leads to', - type: 'string', // required for sort/filter because initial data type is numeric - className: [config.tableCellFocusClass, config.tableCellConnectionClass].join(' '), - width: 80, - data: 'connection.id', - defaultContent: 0, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let tableApi = this.api(); - - editableOnSave(tableApi, cell, [], ['action:name']); - editableOnHidden(tableApi, cell); - editableConnectionOnInit(cell); - editableConnectionOnShown(cell); - editableConnectionOnSave(cell); - - $(cell).editable($.extend({ - mode: 'popup', - type: 'select', - title: 'system', - name: 'connectionId', - pk: rowData.id || null, - emptytext: 'unknown', - onblur: 'submit', - showbuttons: false, - disabled: rowData.groupId !== 5, // initial disabled if NON wh - value: cellData, - prepend: [{value: 0, text: ''}], - params: modifyFieldParamsOnSend, - source: function(){ - let activeMap = Util.getMapModule().getActiveMap(); - let mapId = activeMap.data('id'); - let connectionOptions = getSignatureConnectionOptions(mapId, systemData); - return connectionOptions; - }, - display: function(value, sourceData){ - let selected = $.fn.editableutils.itemsByValue(value, sourceData); - if(selected.length && selected[0].value > 0){ - let errorIcon = ' '; - $(this).html(FormElement.formatSignatureConnectionSelectionData({text: selected[0].text})).prepend(errorIcon); - }else{ - $(this).empty() ; - } - }, - validate: function(value, b, c){ - // convert string to int -> important for further processing - // -> on submit record (new signature) validate() is called and no error should be returned - // value should already be integer - if( !Number.isInteger(value) ){ - return {newValue: parseInt(value) || 0, msg: null}; - } - }, - success: function(response, newValue){ - tableApi.cell(cell).data(newValue); - - $(this).pulseBackgroundColor('changed'); - - if(response){ - let newRowData = response.signatures[0]; - updateSignatureCell(tableApi, rowIndex, 'status:name', newRowData.updated); - updateSignatureCell(tableApi, rowIndex, 'updated:name', newRowData.updated.updated); - } - tableApi.draw(); - } - }, editableDefaults)); - } - },{ - targets: 6, - name: 'created', - title: 'created', - searchable: false, - width: 80, - className: ['text-right', config.tableCellCounterClass, 'min-screen-d'].join(' '), - data: 'created.created', - defaultContent: '' - },{ - targets: 7, - name: 'updated', - title: 'updated', - searchable: false, - width: 80, - className: ['text-right', config.tableCellCounterClass, 'min-screen-d'].join(' '), - data: 'updated.updated', - defaultContent: '', - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - // highlight cell - let diff = Math.floor((new Date()).getTime()) - cellData * 1000; - - // age > 1 day - if( diff > 86400000){ - $(cell).addClass('txt-color txt-color-warning'); - } - } - },{ - targets: 8, - name: 'info', - title: '', - orderable: false, - searchable: false, - width: 10, - class: ['text-center', Util.config.helpClass , Util.config.popoverTriggerClass].join(' '), - data: 'created.created', - defaultContent: '', - render: { - display: (cellData, type, rowData, meta) => { - if(cellData){ - return ''; - } - } - } - },{ - targets: 9, - name: 'action', - title: '', - orderable: false, - searchable: false, - width: 10, - class: ['text-center', config.tableCellFocusClass, config.tableCellActionClass].join(' '), - data: null, - render: { - display: (cellData, type, rowData, meta) => { - let val = ''; - if(rowData.id){ - val = ''; - } - return val; - } - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let tableApi = this.api(); - - if(rowData.id){ - // delete signature ----------------------------------------------------------------------- - let confirmationSettings = { - container: 'body', - placement: 'left', - btnCancelClass: 'btn btn-sm btn-default', - btnCancelLabel: 'cancel', - btnCancelIcon: 'fas fa-fw fa-ban', - title: 'delete signature', - btnOkClass: 'btn btn-sm btn-danger', - btnOkLabel: 'delete', - btnOkIcon: 'fas fa-fw fa-times', - onConfirm: function(e, target){ - // top scroll to top - e.preventDefault(); - - let deleteRowElement = $(target).parents('tr'); - let row = tableApi.rows(deleteRowElement); - deleteSignatures(tableApi, row); - } - }; - - $(cell).confirmation(confirmationSettings); - }else{ - // add new signature ---------------------------------------------------------------------- - $(cell).on('click', {tableApi: tableApi, rowIndex: rowIndex}, function(e){ - e.stopPropagation(); - e.preventDefault(); - - let secondaryTableApi = e.data.tableApi; - let metaData = getTableMetaData(secondaryTableApi); - let primaryTableApi = getDataTableInstance(metaData.mapId, metaData.systemId, 'primary'); - - let formFields = secondaryTableApi.row(e.data.rowIndex).nodes().to$().find('.editable'); - - // the "hide" makes sure to take care about open editable fields (e.g. description) - // otherwise, changes would not be submitted in this field (not necessary) - formFields.editable('hide'); - - // submit all xEditable fields - formFields.editable('submit', { - url: Init.path.saveSignatureData, - ajaxOptions: { - dataType: 'json', //assuming json response - beforeSend: function(xhr, settings){ - lockTable(primaryTableApi); - }, - context: { - primaryTableApi: primaryTableApi, - secondaryTableApi: secondaryTableApi, - } - }, - data: { - systemId: metaData.systemId, // additional data to submit - pk: 0 // new data no primary key - }, - error: editableDefaults.error, // user default xEditable error function - success: function(data, editableConfig){ - let context = editableConfig.ajaxOptions.context; - let primaryTableApi = context.primaryTableApi; - let secondaryTableApi = context.secondaryTableApi; - - unlockTable(primaryTableApi); - - let signatureData = data.signatures[0]; - let row = addSignatureRow(primaryTableApi, signatureData); - if(row){ - primaryTableApi.draw(); - // highlight - row.nodes().to$().pulseBackgroundColor('added'); - - // prepare "add signature" table for new entry -> reset ------------------- - secondaryTableApi.clear().row.add($.extend(true, {}, emptySignatureData)).draw(); - - Util.showNotify({ - title: 'Signature added', - text: 'Name: ' + signatureData.name, - type: 'success' - }); - - // update signature bar - updateScannedSignaturesBar(primaryTableApi, {showNotice: true}); - } - } - }); - }); - } - } - } - ], - createdRow: function(row, data, dataIndex){ - // enable tabbing for interactive cells - let focusCells = $(row).find('.' + config.tableCellFocusClass + ':not(.editable-disabled)').attr('tabindex', 0); - // enable "return" key -> click() - focusCells.on('keydown', function(e){ - e.stopPropagation(); - if(e.which === 13){ - $(this).trigger('click'); - } - }); - } - }; - - return dataTableDefaults; - }; - - /** - * draw signature table toolbar (add signature button, scan progress bar - * @param moduleElement - * @param mapId - * @param systemData - */ - let drawSignatureTableNew = (moduleElement, mapId, systemData) => { - let secondaryTableContainer = $('
', { - class: config.tableToolsActionClass - }); - - // create "empty table for new signature - let table = $('', { - id: getTableId(mapId, systemData.id, 'secondary'), - class: ['stripe', 'row-border', 'compact', 'nowrap', config.sigTableClass, config.sigTableSecondaryClass].join(' ') - }); - - secondaryTableContainer.append(table); - - moduleElement.find('.' + config.moduleHeadClass).after(secondaryTableContainer); - - let dataTableOptions = { - paging: false, - info: false, - searching: false, - tabIndex: -1, - data: [$.extend(true, {}, emptySignatureData)] - }; - - $.extend(true, dataTableOptions, getSignatureDataTableDefaults(mapId, systemData)); - - let tableApi = table.DataTable(dataTableOptions); - - // "Responsive" dataTables plugin did not load automatic (because table is invisible onInit) - // -> manually start "Responsive" extension -> see default dataTable setting for config e.g. breakpoints - new $.fn.dataTable.Responsive(tableApi); - }; - - /** - * filter table "group" column - * @param tableApi - * @param newValue - * @param sourceOptions - */ - let searchGroupColumn = (tableApi, newValue, sourceOptions) => { - let column = tableApi.column('group:name'); - let pattern = ''; - - if(newValue.length <= sourceOptions.length){ - // all options selected + "prepend" option - let selected = $.fn.editableutils.itemsByValue(newValue, sourceOptions); - - pattern = selected.map(option => option.value !== 0 ? $.fn.dataTable.util.escapeRegex(option.text) : '^$').join('|'); - } - column.search(pattern, true, false).draw(); - }; - - /** - * init table filter button "group" column - * @param tableApi - */ - let initGroupFilterButton = tableApi => { - let characterId = Util.getCurrentCharacterId(); - - let promiseStore = MapUtil.getLocaleData('character', Util.getCurrentCharacterId()); - promiseStore.then(data => { - let filterButton = tableApi.button('tableTools', 'filterGroup:name').node(); - let prependOptions = [{value: 0, text: 'unknown'}]; - let sourceOptions = config.signatureGroupsLabels; - let selectedValues = []; - - if(data && data.filterSignatureGroups && data.filterSignatureGroups.length){ - // select local stored values - selectedValues = data.filterSignatureGroups; - }else{ - // no default group filter options -> show all - selectedValues = sourceOptions.map(option => option.value); - selectedValues.unshift(0); - } - - filterButton.editable({ - mode: 'popup', - container: 'body', - type: 'checklist', - showbuttons: false, - onblur: 'submit', - highlight: false, - title: 'filter groups', - value: selectedValues, - prepend: prependOptions, - source: sourceOptions, - inputclass: config.editableUnknownInputClass, - display: function(value, sourceData){ - // update filter button label - let html = 'group'; - let allSelected = value.length >= sourceData.length; - if( !allSelected ){ - html += ' (' + value.length + ')'; - } - $(this).toggleClass('active', !allSelected).html(html); - }, - validate: function(value){ - // convert string to int -> important for further processing - return {newValue: value.map(num => parseInt(num)), msg: null}; - } - }); - - let allOptions = prependOptions.concat(sourceOptions); - - filterButton.on('save', {tableApi: tableApi, sourceOptions: allOptions}, function(e, params){ - // store values local -> IndexDB - MapUtil.storeLocaleCharacterData('filterSignatureGroups', params.newValue); - - searchGroupColumn(e.data.tableApi, params.newValue, e.data.sourceOptions); - }); - - // set initial search string -> even if table ist currently empty - searchGroupColumn(tableApi, selectedValues, allOptions); - }); - }; - - /** - * draw empty signature table - * @param moduleElement - * @param mapId - * @param systemData - */ - let drawSignatureTable = (moduleElement, mapId, systemData) => { - let table = $('
', { - id: getTableId(mapId, systemData.id, 'primary'), - class: ['display', 'compact', 'nowrap', config.sigTableClass, config.sigTablePrimaryClass].join(' ') - }); - - moduleElement.append(table); - - let dataTableOptions = { - tabIndex: -1, - dom: '<"row"<"col-xs-3"l><"col-xs-5"B><"col-xs-4"f>>' + - '<"row"<"col-xs-12"tr>>' + - '<"row"<"col-xs-5"i><"col-xs-7"p>>', - buttons: { - name: 'tableTools', - buttons: [ - { - name: 'filterGroup', - className: config.moduleHeadlineIconClass, - text: '' // set by js (xEditable) - }, - { - name: 'selectAll', - className: config.moduleHeadlineIconClass, - text: 'select all', - action: function(e, tableApi, node, conf){ - let allRows = tableApi.rows(); - let selectedRows = getSelectedRows(tableApi); - let allRowElements = allRows.nodes().to$(); - - if(allRows.data().length === selectedRows.data().length){ - allRowElements.removeClass('selected'); - }else{ - allRowElements.addClass('selected'); - } - - // check delete button - checkDeleteSignaturesButton(tableApi); - } - }, - { - name: 'delete', - className: [config.moduleHeadlineIconClass, config.sigTableClearButtonClass].join(' '), - text: 'delete (0)', - action: function(e, tableApi, node, conf){ - let selectedRows = getSelectedRows(tableApi); - bootbox.confirm('Delete ' + selectedRows.data().length + ' signature?', function(result){ - if(result){ - deleteSignatures(tableApi, selectedRows); - } - }); - } - } - ] - }, - initComplete: function (settings, json){ - let tableApi = this.api(); - - initGroupFilterButton(tableApi); - - // init update counter for timestamp columns - // mark as init - this.attr('data-counter', 'init'); - let refreshIntervalId = window.setInterval(() => { - tableApi.cells(null, ['created:name', 'updated:name']).every(function(rowIndex, colIndex, tableLoopCount, cellLoopCount){ - let cell = this; - let node = cell.node(); - let data = cell.data(); - if(data && Number.isInteger(data) && !node.classList.contains('stopCounter')){ - // timestamp expected int > 0 - let date = new Date(data * 1000); - Counter.updateDateDiff( cell.nodes().to$(), date); - } - }); - }, 500); - this.data('interval', refreshIntervalId); - } - }; - - $.extend(true, dataTableOptions, getSignatureDataTableDefaults(mapId, systemData)); - - let tableApi = table.DataTable(dataTableOptions); - - // "Responsive" dataTables plugin did not load automatic (because table is invisible onInit) - // -> manually start "Responsive" extension -> see default dataTable setting for config e.g. breakpoints - new $.fn.dataTable.Responsive(tableApi); - - // lock table until module is fully rendered - lockTable(tableApi); - }; - - /** - * open xEditable input field in "new Signature" table - * @param moduleElement - */ - let focusNewSignatureEditableField = moduleElement => { - moduleElement.find('.' + config.sigTableSecondaryClass) - .find('td.' + config.sigTableEditSigNameInput).editable('show'); - }; - - /** - * get all selected rows of a table - * @param tableApi - * @returns {*} - */ - let getSelectedRows = tableApi => { - return tableApi.rows('.selected'); - }; - - /** - * check the "delete signature" button. show/hide the button if a signature is selected - * @param tableApi - */ - let checkDeleteSignaturesButton = tableApi => { - let selectedRows = getSelectedRows(tableApi); - let selectedRowCount = selectedRows.data().length; - let clearButton = tableApi.button('tableTools', 'delete:name').node(); - - if(selectedRowCount > 0){ - let allRows = tableApi.rows(); - let rowCount = allRows.data().length; - - let countText = selectedRowCount; - if(selectedRowCount >= rowCount){ - countText = 'all'; - } - clearButton.find('i+span').text(countText); - - // update clear signatures button text - clearButton.velocity('stop'); - - if( clearButton.is(':hidden') ){ - // show button - clearButton.velocity('transition.expandIn', { - duration: 100 - }); - }else{ - // highlight button - clearButton.velocity('callout.pulse', { - duration: 200 - }); - } - }else{ - // hide button - clearButton.velocity('transition.expandOut', { - duration: 100 - }); - } - }; - - /** - * set module observer and look for relevant signature data to update - * @param moduleElement - * @param mapId - * @param systemData - */ - let setModuleObserver = (moduleElement, mapId, systemData) => { - let primaryTable = moduleElement.find('.' + config.sigTablePrimaryClass); - let primaryTableApi = getDataTableInstance(mapId, systemData.id, 'primary'); - - // add signature toggle --------------------------------------------------------------------------------------- - let toggleAddSignature = (show = 'auto') => { - let button = moduleElement.find('.' + config.moduleHeadlineIconAddClass); - let toolsElement = moduleElement.find('.' + config.tableToolsActionClass); - button.toggleClass('active', show === 'auto' ? undefined : show); - - if(toolsElement.is(':visible') && (!show || show === 'auto')){ - // hide container - toolsElement.velocity('stop').velocity({ - opacity: [0, 1], - height: [0, '70px'] - },{ - duration: 150, - display: 'none' - }); - }else if(!toolsElement.is(':visible') && (show || show === 'auto')){ - // show container - toolsElement.velocity('stop').velocity({ - opacity: [1, 0], - height: ['70px', 0] - },{ - duration: 150, - display: 'block', - complete: function(){ - focusNewSignatureEditableField(moduleElement); - } - }); - }else if(toolsElement.is(':visible') && show){ - // still visible -> no animation - focusNewSignatureEditableField(moduleElement); - } - }; - - moduleElement.find('.' + config.moduleHeadlineIconAddClass).on('click', function(e){ - toggleAddSignature('auto'); - }); - - moduleElement.on('pf:showSystemSignatureModuleAddNew', function(e){ - toggleAddSignature(true); - }); - - // signature reader dialog ------------------------------------------------------------------------------------ - moduleElement.find('.' + config.moduleHeadlineIconReaderClass).on('click', function(e){ - moduleElement.showSignatureReaderDialog(systemData); - }); - - // "lazy update" toggle --------------------------------------------------------------------------------------- - moduleElement.find('.' + config.moduleHeadlineIconLazyClass).on('click', function(e){ - let button = $(this); - button.toggleClass('active'); - }); - - // set multi row select --------------------------------------------------------------------------------------- - primaryTable.on('mousedown', 'td', {tableApi: primaryTableApi}, function(e){ - if(e.ctrlKey){ - e.preventDefault(); - e.stopPropagation(); - // xEditable field should not open -> on 'click' - // -> therefore disable "pointer-events" on "td" for some ms -> 'click' event is not triggered - $(this).css('pointer-events', 'none'); - $(e.target.parentNode).toggleClass('selected'); - - // check delete button - checkDeleteSignaturesButton(e.data.tableApi); - - setTimeout(() => { - $(this).css('pointer-events', 'auto'); - }, 250); - } - }); - - // draw event for signature table ----------------------------------------------------------------------------- - primaryTableApi.on('draw.dt', function(e, settings){ - // check delete button - let tableApi = $(this).dataTable().api(); - checkDeleteSignaturesButton(tableApi); - }); - - // event listener for global "paste" signatures into the page ------------------------------------------------- - moduleElement.on('pf:updateSystemSignatureModuleByClipboard', {tableApi: primaryTableApi}, function(e, clipboard){ - let signatureOptions = { - deleteOld: moduleElement.find('.' + config.moduleHeadlineIconLazyClass).hasClass('active') ? 1 : 0 - }; - updateSignatureTableByClipboard(e.data.tableApi, systemData, clipboard, signatureOptions); - }); - - // signature column - "type" popover -------------------------------------------------------------------------- - moduleElement.find('.' + config.sigTableClass).hoverIntent({ - over: function(e){ - let staticWormholeElement = $(this); - let wormholeName = staticWormholeElement.attr('data-name'); - let wormholeData = Util.getObjVal(Init, 'wormholes.' + wormholeName); - if(wormholeData){ - staticWormholeElement.addWormholeInfoTooltip(wormholeData, { - trigger: 'manual', - placement: 'top', - show: true - }); - } - }, - out: function(e){ - $(this).destroyPopover(); - }, - selector: '.editable-click:not(.editable-open) span[class^="pf-system-sec-"]' - }); - - // signature column - "info" popover -------------------------------------------------------------------------- - moduleElement.find('.' + config.sigTablePrimaryClass).hoverIntent({ - over: function(e){ - let cellElement = $(this); - let rowData = primaryTableApi.row(cellElement.parents('tr')).data(); - cellElement.addCharacterInfoTooltip(rowData, { - trigger: 'manual', - placement: 'top', - show: true - }); - }, - out: function(e){ - $(this).destroyPopover(); - }, - selector: 'td.' + Util.config.helpClass - }); - }; - - /** - * add new row to signature table - * @param tableApi - * @param signatureData - * @returns {*} - */ - let addSignatureRow = (tableApi, signatureData) => { - let row = null; - if(tableApi){ - row = tableApi.row.add(signatureData); - } - return row; - }; - - /** - * update signature table with new signatures - * -> add/update/delete rows - * @param tableApi - * @param signaturesDataOrig - * @param deleteOutdatedSignatures - */ - let updateSignatureTable = (tableApi, signaturesDataOrig, deleteOutdatedSignatures = false) => { - if(isLockedTable(tableApi)) return; - - // disable tableApi until update finished; - lockTable(tableApi); - - // clone signature array because of further manipulation - let signaturesData = $.extend([], signaturesDataOrig); - - let rowIdsExist = []; - - let promisesAdded = []; - let promisesChanged = []; - let promisesDeleted = []; - - let allRows = tableApi.rows(); - let updateEmptyTable = !allRows.any(); - - let rowUpdate = function(rowIndex, colIndex, tableLoopCount, cellLoopCount){ - let cell = this; - let node = cell.nodes().to$(); - if(node.data('editable')){ - // xEditable is active -> should always be active! - // set new value even if no change -> e.g. render selected Ids as text labels - let oldValue = node.editable('getValue', true); - node.editable('setValue', cell.data()); - - if(oldValue !== cell.data()){ - // highlight cell on data change - node.pulseBackgroundColor('changed'); - } - }else if(node.hasClass(config.tableCellCounterClass)){ - // "updated" timestamp always changed - node.pulseBackgroundColor('changed'); - } - }; - - // update signatures ------------------------------------------------------------------------------------------ - allRows.every(function(rowIdx, tableLoop, rowLoop){ - let row = this; - let rowData = row.data(); - let rowElement = row.nodes().to$(); - - for(let i = 0; i < signaturesData.length; i++){ - if(signaturesData[i].id === rowData.id){ - let rowId = row.id(true); - - // check if row was updated - if(signaturesData[i].updated.updated > rowData.updated.updated){ - // set new row data -> draw() is executed after all changes made - row.data(signaturesData[i]); - - // bind new signature dataTable data() -> to xEditable inputs - row.cells(row.id(true), ['id:name', 'group:name', 'type:name', 'description:name', 'connection:name', 'updated:name']) - .every(rowUpdate); - - promisesChanged.push(new Promise((resolve, reject) => { - resolve({action: 'changed', rowId: rowId}); - })); - } - - rowIdsExist.push(rowId); - - // remove signature data -> all left signatures will be added - signaturesData.splice(i, 1); - i--; - } - } - }); - - // delete signatures ------------------------------------------------------------------------------------------ - if(deleteOutdatedSignatures){ - let rows = tableApi.rows((rowIdx, rowData, node) => !rowIdsExist.includes('#' + config.sigTableRowIdPrefix + rowData.id)); - rows.every(function(rowIdx, tableLoop, rowLoop){ - let row = this; - let rowId = row.id(true); - let rowElement = row.nodes().to$(); - - // hide open editable fields on the row before removing them - rowElement.find('.editable').editable('destroy'); - - // destroy possible open popovers (e.g. wormhole types, update popover) - rowElement.destroyPopover(true); - - rowElement.pulseBackgroundColor('deleted'); - - promisesDeleted.push(new Promise((resolve, reject) => { - toggleTableRow(rowElement).then(payload => resolve({action: 'deleted', rowIdx: rowId})); - })); - }).remove(); - } - - // add new signatures ----------------------------------------------------------------------------------------- - for(let signatureData of signaturesData){ - let row = addSignatureRow(tableApi, signatureData); - let rowId = row.id(true); - let rowElement = row.nodes().to$(); - rowElement.pulseBackgroundColor('added'); - - promisesAdded.push(new Promise((resolve, reject) => { - resolve({action: 'added', rowId: rowId}); - })); - } - - // done ------------------------------------------------------------------------------------------------------- - Promise.all(promisesAdded.concat(promisesChanged, promisesDeleted)).then(payloads => { - if(payloads.length){ - // table data changed -> draw() table changes - tableApi.draw(); - - if(!updateEmptyTable){ - // no notifications if table was empty just progressbar notification is needed - // sum payloads by "action" - let notificationCounter = payloads.reduce((acc, payload) => { - if(!acc[payload.action]){ - acc[payload.action] = 0; - } - acc[payload.action]++; - return acc; - }, {}); - - let notification = ''; - if(notificationCounter.added > 0){ - notification += notificationCounter.added + ' added
'; - } - if(notificationCounter.changed > 0){ - notification += notificationCounter.changed + ' updated
'; - } - if(notificationCounter.deleted > 0){ - notification += notificationCounter.deleted + ' deleted
'; - } - if(notification.length){ - Util.showNotify({title: 'Signatures updated', text: notification, type: 'success'}); - } - } - - updateScannedSignaturesBar(tableApi, {showNotice: true}); - } - - // unlock table - unlockTable(tableApi); - }); - }; - - /** - * update trigger function for this module - * compare data and update module - * @param moduleElement - * @param systemData - */ - let updateModule = (moduleElement, systemData) => { - - if(systemData.signatures){ - let mapId = moduleElement.data('mapId'); - let systemId = moduleElement.data('systemId'); - let tableApi = getDataTableInstance(mapId, systemId, 'primary'); - updateSignatureTable(tableApi, systemData.signatures, true); - } - - moduleElement.hideLoadingAnimation(); - }; - - /** - * init callback - * @param moduleElement - * @param mapId - * @param systemData - */ - let initModule = (moduleElement, mapId, systemData) => { - let tableApi = getDataTableInstance(mapId, systemData.id, 'primary'); - unlockTable(tableApi); - }; - - /** - * get module toolbar element - * @returns {jQuery} - */ - let getHeadlineToolbar = () => { - let headlineToolbar = $('
', { - class: 'pull-right' - }).append( - $('', { - class: 'progress-label-right', - text: '0%' - }), - $('', { - class: ['fas', 'fa-fw', 'fa-plus', config.moduleHeadlineIconClass, config.moduleHeadlineIconAddClass].join(' '), - title: 'add' - }).attr('data-toggle', 'tooltip'), - $('', { - class: ['fas', 'fa-fw', 'fa-paste', config.moduleHeadlineIconClass, config.moduleHeadlineIconReaderClass].join(' '), - title: 'signature reader' - }).attr('data-toggle', 'tooltip'), - $('', { - class: ['fas', 'fa-fw', 'fa-exchange-alt', config.moduleHeadlineIconClass, config.moduleHeadlineIconLazyClass].join(' '), - title: 'lazy \'delete\' signatures' - }).attr('data-toggle', 'tooltip') - ); - - headlineToolbar.find('[data-toggle="tooltip"]').tooltip({ - container: 'body' - }); - - return headlineToolbar; - }; - - /** - * get module element - * @param parentElement - * @param mapId - * @param systemData - * @returns {jQuery} - */ - let getModule = (parentElement, mapId, systemData) => { - let moduleElement = $('
').append( - $('
', { - class: config.moduleHeadClass - }).append( - $('
', { - class: config.moduleHandlerClass - }), - $('
', { - text: 'Signatures' - }), - getHeadlineToolbar() - ) - ); - - // scanned signatures progress bar ---------------------------------------------------------------------------- - requirejs(['text!templates/form/progress.html', 'mustache'], (template, Mustache) => { - let data = { - label: true, - wrapperClass: config.moduleHeadlineProgressBarClass, - class: ['progress-bar-success'].join(' '), - percent: 0 - }; - moduleElement.find('.' + config.moduleHeadClass).append(Mustache.render(template, data)); - }); - - moduleElement.data('mapId', mapId); - moduleElement.data('systemId', systemData.id); - - moduleElement.showLoadingAnimation(); - - // draw "new signature" add table - drawSignatureTableNew(moduleElement, mapId, systemData); - - // draw signature table - drawSignatureTable(moduleElement, mapId, systemData); - - // set module observer - setModuleObserver(moduleElement, mapId, systemData); - - return moduleElement; - }; - - /** - * before module hide callback - * @param moduleElement - */ - let beforeHide = moduleElement => { - // disable update - let mapId = moduleElement.data('mapId'); - let systemId = moduleElement.data('systemId'); - let tableApi = getDataTableInstance(mapId, systemId, 'primary'); - lockTable(tableApi); - }; - - /** - * before module destroy callback - * @param moduleElement - */ - let beforeDestroy = moduleElement => { - // Destroying the data tables throws - // -> safety remove all dataTables - let mapId = moduleElement.data('mapId'); - let systemId = moduleElement.data('systemId'); - let primaryTableApi = getDataTableInstance(mapId, systemId, 'primary'); - let secondaryTableApi = getDataTableInstance(mapId, systemId, 'secondary'); - primaryTableApi.destroy(); - secondaryTableApi.destroy(); - }; - - return { - config: config, - getModule: getModule, - initModule: initModule, - updateModule: updateModule, - beforeHide: beforeHide, - beforeDestroy: beforeDestroy, - getAllSignatureNamesBySystem: getAllSignatureNamesBySystem - }; -}); \ No newline at end of file diff --git a/public/js/v1.4.1/app/ui/system_graph.js b/public/js/v1.4.1/app/ui/system_graph.js deleted file mode 100644 index c18bb203..00000000 --- a/public/js/v1.4.1/app/ui/system_graph.js +++ /dev/null @@ -1,248 +0,0 @@ -/** - * System graph module - */ - -define([ - 'jquery', - 'app/init', - 'app/util', - 'morris' -], ($, Init, Util, Morris) => { - 'use strict'; - - let config = { - // module info - modulePosition: 3, - moduleName: 'systemGraph', - moduleHeadClass: 'pf-module-head', // class for module header - moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler - - // system graph module - moduleTypeClass: 'pf-system-graph-module', // class for this module - systemGraphClass: 'pf-system-graph', // class for each graph - - // system graph labels - systemGraphs: { - jumps: { - headline: 'Jumps', - units: 'jumps', - ykeys: ['y'], - labels: ['jumps'], - lineColors: ['#375959'], - pointFillColors: ['#477372'] - }, - shipKills: { - headline: 'Ship/POD Kills', - units: 'kills', - ykeys: ['y', 'z'], - labels: ['Ship kills', 'POD kills'], - lineColors: ['#375959', '#477372'], - pointFillColors: ['#477372', '#568a89'] - }, - factionKills: { - headline: 'NPC Kills', - units: 'kills', - ykeys: ['y'], - labels: ['kills'], - lineColors: ['#375959'], - pointFillColors: ['#477372'] - } - } - }; - - /** - * get info for a given graph key - * @param graphKey - * @param option - * @returns {string} - */ - let getInfoForGraph = function(graphKey, option){ - let info = ''; - if(config.systemGraphs.hasOwnProperty(graphKey)){ - info = config.systemGraphs[graphKey][option]; - } - - return info; - }; - - /** - * init Morris Graph - * @param graphElement - * @param graphKey - * @param graphData - * @param eventLine - */ - let initGraph = function(graphElement, graphKey, graphData, eventLine){ - if(graphData.length > 0){ - let labelYFormat = function(y){ - return Math.round(y); - }; - - let graphConfig = { - element: graphElement, - data: graphData, - xkey: 'x', - ykeys: getInfoForGraph(graphKey, 'ykeys'), - labels: getInfoForGraph(graphKey, 'labels'), - parseTime: false, - ymin: 0, - yLabelFormat: labelYFormat, - padding: 10, - hideHover: true, - pointSize: 3, - lineColors: getInfoForGraph(graphKey, 'lineColors'), - pointFillColors: getInfoForGraph(graphKey, 'pointFillColors'), - pointStrokeColors: ['#141413'], - lineWidth: 2, - grid: true, - gridStrokeWidth: 0.3, - gridTextSize: 9, - gridTextFamily: 'Oxygen Bold', - gridTextColor: '#63676a', - behaveLikeLine: false, - goals: [], - goalLineColors: ['#5cb85c'], - smooth: true, - fillOpacity: 0.2, - resize: true, - redraw: true, - eventStrokeWidth: 2, - eventLineColors: ['#5CB85C'] - }; - - if(eventLine >= 0){ - graphConfig.events = [eventLine]; - } - - Morris.Area(graphConfig); - } - }; - - /** - * request graphs data - * @param requestData - * @param context - * @param callback - */ - let requestGraphData = (requestData, context, callback) => { - // show loading animation - context.moduleElement.find('.' + config.systemGraphClass).showLoadingAnimation(); - - $.ajax({ - type: 'POST', - url: Init.path.getSystemGraphData, - data: requestData, - dataType: 'json', - context: context - }).done(function(systemGraphsData){ - callback(this, systemGraphsData); - }).fail(function( jqXHR, status, error) { - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': System graph data', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - this.moduleElement.hide(); - }).always(function(){ - // hide loading animation - context.moduleElement.find('.' + config.systemGraphClass).hideLoadingAnimation(); - }); - }; - - /** - * update graph elements with data - * @param context - * @param systemGraphsData - */ - let addGraphData = (context, systemGraphsData) => { - - // calculate time offset until system created ----------------------------------------------------------------- - let serverData = Util.getServerTime(); - let timestampNow = Math.floor(serverData.getTime() / 1000); - let timeSinceUpdate = timestampNow - context.systemData.updated.updated; - - let timeInHours = Math.floor(timeSinceUpdate / 3600); - let timeInMinutes = Math.floor((timeSinceUpdate % 3600) / 60); - let timeInMinutesPercent = ( timeInMinutes / 60 ).toFixed(2); - let eventLine = timeInHours + timeInMinutesPercent; - - // graph is from right to left -> convert event line - eventLine = 23 - eventLine; - - // update graph data ------------------------------------------------------------------------------------------ - for (let [systemId, graphsData] of Object.entries(systemGraphsData)){ - for (let [graphKey, graphData] of Object.entries(graphsData)){ - let graphElement = context.moduleElement.find('[data-graph="' + graphKey + '"]'); - initGraph(graphElement, graphKey, graphData, eventLine); - } - } - }; - - /** - * @see requestGraphData - * @param moduleElement - * @param mapId - * @param systemData - */ - let updateGraphPanel = (moduleElement, mapId, systemData) => { - let requestData = { - systemIds: [systemData.systemId] - }; - - let contextData = { - moduleElement: moduleElement, - systemData: systemData - }; - - requestGraphData(requestData, contextData, addGraphData); - }; - - /** - * get module element - * @param parentElement - * @param mapId - * @param systemData - * @returns {*} - */ - let getModule = (parentElement, mapId, systemData) => { - // graph data is available for k-space systems - let moduleElement = null; - if(systemData.type.id === 2){ - moduleElement = $('
'); - let rowElement = $('
', { - class: 'row' - }); - - for (let [graphKey, graphConfig] of Object.entries(config.systemGraphs)){ - rowElement.append( - $('
', { - class: ['col-xs-12', 'col-sm-6', 'col-md-4'].join(' ') - }).append( - $('
', { - class: config.moduleHeadClass - }).append( - $('
', { - class: config.moduleHandlerClass - }), - $('
', { - text: getInfoForGraph(graphKey, 'headline') - }) - ), - $('
', { - class: config.systemGraphClass - }).attr('data-graph', graphKey) - ) - ); - } - moduleElement.append(rowElement); - - updateGraphPanel(moduleElement, mapId, systemData); - } - - return moduleElement; - }; - - return { - config: config, - getModule: getModule - }; - -}); diff --git a/public/js/v1.4.1/app/ui/system_info.js b/public/js/v1.4.1/app/ui/system_info.js deleted file mode 100644 index 9a74e619..00000000 --- a/public/js/v1.4.1/app/ui/system_info.js +++ /dev/null @@ -1,421 +0,0 @@ -/** - * System info module - */ - -define([ - 'jquery', - 'app/init', - 'app/util', - 'app/render', - 'app/map/util' -], ($, Init, Util, Render, MapUtil) => { - 'use strict'; - - let config = { - // module info - modulePosition: 2, - moduleName: 'systemInfo', - - // system info module - moduleTypeClass: 'pf-system-info-module', // class for this module - - // 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 - - // info table - systemInfoTableClass: 'pf-module-table', // class for system info table - systemInfoNameClass: 'pf-system-info-name', // class for "name" information element - systemInfoEffectClass: 'pf-system-info-effect', // class for "effect" information element - systemInfoPlanetsClass: 'pf-system-info-planets', // class for "planets" information element - systemInfoStatusLabelClass: 'pf-system-info-status-label', // class for "status" information element - systemInfoStatusAttributeName: 'data-status', // attribute name for status label - systemInfoWormholeClass: 'pf-system-info-wormhole-', // class prefix for static wormhole element - - // description field - descriptionArea: 'pf-system-info-description-area', // class for "description" area - addDescriptionButtonClass: 'pf-system-info-description-button', // class for "add description" button - moduleElementToolbarClass: 'pf-table-tools', // class for "module toolbar" element - tableToolsActionClass: 'pf-table-tools-action', // class for "edit" action - - descriptionTextareaElementClass: 'pf-system-info-description', // class for "description" textarea element (xEditable) - descriptionTextareaCharCounter: 'pf-form-field-char-count', // class for "character counter" element for form field - - // fonts - fontTriglivianClass: 'pf-triglivian' // class for "Triglivian" names (e.g. Abyssal systems) - }; - - // disable Module update temporary (in case e.g. textarea is currently active) - let disableModuleUpdate = false; - - // animation speed values - let animationSpeedToolbarAction = 200; - - // max character length for system description - let maxDescriptionLength = 512; - - /** - * shows the tool action element by animation - * @param toolsActionElement - */ - let showToolsActionElement = (toolsActionElement) => { - toolsActionElement.velocity('stop').velocity({ - opacity: 1, - height: '100%' - },{ - duration: animationSpeedToolbarAction, - display: 'block', - visibility: 'visible' - }); - }; - - /** - * hides the tool action element by animation - * @param toolsActionElement - */ - let hideToolsActionElement = (toolsActionElement) => { - toolsActionElement.velocity('stop').velocity('reverse', { - display: 'none', - visibility: 'hidden' - }); - }; - - /** - * update trigger function for this module - * compare data and update module - * @param moduleElement - * @param systemData - */ - let updateModule = (moduleElement, systemData) => { - let systemId = moduleElement.data('id'); - - if(systemId === systemData.id){ - // update system status ----------------------------------------------------------------------------------- - let systemStatusLabelElement = moduleElement.find('.' + config.systemInfoStatusLabelClass); - let systemStatusId = parseInt( systemStatusLabelElement.attr( config.systemInfoStatusAttributeName ) ); - - if(systemStatusId !== systemData.status.id){ - // status changed - let currentStatusClass = Util.getStatusInfoForSystem(systemStatusId, 'class'); - let newStatusClass = Util.getStatusInfoForSystem(systemData.status.id, 'class'); - let newStatusLabel = Util.getStatusInfoForSystem(systemData.status.id, 'label'); - systemStatusLabelElement.removeClass(currentStatusClass).addClass(newStatusClass).text(newStatusLabel); - - // set new status attribute - systemStatusLabelElement.attr( config.systemInfoStatusAttributeName, systemData.status.id); - } - - // update description textarea ---------------------------------------------------------------------------- - let descriptionTextareaElement = moduleElement.find('.' + config.descriptionTextareaElementClass); - let description = descriptionTextareaElement.editable('getValue', true); - - if( - !disableModuleUpdate && // don´t update if field is active - description !== systemData.description - ){ - // description changed - let descriptionButton = moduleElement.find('.' + config.addDescriptionButtonClass); - - // set new value - descriptionTextareaElement.editable('setValue', systemData.description); - - let actionElement = descriptionButton.siblings('.' + config.tableToolsActionClass); - - if(systemData.description.length === 0){ - // show/activate description field - // show button if value is empty - descriptionButton.show(); - hideToolsActionElement(actionElement); - }else{ - // hide/disable description field - // hide tool button - descriptionButton.hide(); - showToolsActionElement(actionElement); - } - } - - // created/updated tooltip -------------------------------------------------------------------------------- - let nameRowElement = moduleElement.find('.' + config.systemInfoNameClass); - - let tooltipData = { - created: systemData.created, - updated: systemData.updated - }; - - nameRowElement.addCharacterInfoTooltip( tooltipData ); - } - - moduleElement.find('.' + config.descriptionArea).hideLoadingAnimation(); - }; - - /** - * get module element - * @param parentElement - * @param mapId - * @param systemData - */ - let getModule = (parentElement, mapId, systemData) => { - - // create new module container - let moduleElement = $('
'); - - // store systemId -> module can be updated with the correct data - moduleElement.data('id', systemData.id); - - // system "static" wh data - let staticsData = []; - if( - systemData.statics && - systemData.statics.length > 0 - ){ - for(let wormholeName of systemData.statics){ - let wormholeData = Object.assign({}, Init.wormholes[wormholeName]); - wormholeData.class = Util.getSecurityClassForSystem(wormholeData.security); - staticsData.push(wormholeData); - } - } - - let effectName = MapUtil.getEffectInfoForSystem(systemData.effect, 'name'); - let effectClass = MapUtil.getEffectInfoForSystem(systemData.effect, 'class'); - - // systemInfo template config - let moduleConfig = { - name: 'modules/system_info', - position: moduleElement, - link: 'append', - 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); - - // init description textarea - descriptionTextareaElement.editable({ - url: Init.path.saveSystem, - dataType: 'json', - pk: systemData.id, - type: 'textarea', - mode: 'inline', - emptytext: '', - onblur: 'cancel', - showbuttons: true, - value: '', // value is set by trigger function updateModule() - rows: 5, - name: 'description', - inputclass: config.descriptionTextareaElementClass, - tpl: '', - params: function(params){ - params.mapData = { - id: mapId - }; - - params.systemData = {}; - params.systemData.id = params.pk; - params.systemData[params.name] = params.value; - - // clear unnecessary data - delete params.pk; - delete params.name; - delete params.value; - - return params; - }, - validate: function(value){ - if(value.length > 0 && $.trim(value).length === 0) { - return {newValue: ''}; - } - }, - success: function(response, newValue){ - Util.showNotify({title: 'System updated', text: 'Name: ' + response.name, type: 'success'}); - }, - error: function(jqXHR, newValue){ - let reason = ''; - let status = ''; - if(jqXHR.name){ - // save error new sig (mass save) - reason = jqXHR.name; - status = 'Error'; - }else{ - reason = jqXHR.responseJSON.text; - status = jqXHR.status; - } - - Util.showNotify({title: status + ': save system information', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - return reason; - } - }); - - // on xEditable open ------------------------------------------------------------------------------ - descriptionTextareaElement.on('shown', function(e, editable){ - let textarea = editable.input.$input; - - // disable module update until description field is open - disableModuleUpdate = true; - - // create character counter - let charCounter = $('', { - class: [config.descriptionTextareaCharCounter, 'txt-color', 'text-right'].join(' ') - }); - textarea.parent().next().append(charCounter); - - // update character counter - Util.updateCounter(textarea, charCounter, maxDescriptionLength); - - textarea.on('keyup', function(){ - Util.updateCounter($(this), charCounter, maxDescriptionLength); - }); - }); - - // on xEditable close ----------------------------------------------------------------------------- - descriptionTextareaElement.on('hidden', function(e){ - let value = $(this).editable('getValue', true); - if(value.length === 0){ - // show button if value is empty - hideToolsActionElement(descriptionButton.siblings('.' + config.tableToolsActionClass)); - descriptionButton.show(); - } - - // enable module update - disableModuleUpdate = false; - }); - - // enable xEditable field on Button click --------------------------------------------------------- - descriptionButton.on('click', function(e){ - e.stopPropagation(); - let descriptionButton = $(this); - - // hide tool buttons - descriptionButton.hide(); - - // show field *before* showing the element - descriptionTextareaElement.editable('show'); - - showToolsActionElement(descriptionButton.siblings('.' + config.tableToolsActionClass)); - }); - - // init tooltips ---------------------------------------------------------------------------------- - let tooltipElements = tempModuleElement.find('[data-toggle="tooltip"]'); - tooltipElements.tooltip(); - - // init system effect popover --------------------------------------------------------------------- - $(moduleElement).find('.' + config.systemInfoEffectClass).addSystemEffectTooltip(systemData.security, systemData.effect); - - // init planets popover --------------------------------------------------------------------------- - $(moduleElement).find('.' + config.systemInfoPlanetsClass).addSystemPlanetsTooltip(systemData.planets); - - // init static wormhole information --------------------------------------------------------------- - for(let staticData of staticsData){ - let staticRowElement = tempModuleElement.find('.' + config.systemInfoWormholeClass + staticData.name); - staticRowElement.addWormholeInfoTooltip(staticData); - } - - // constellation popover -------------------------------------------------------------------------- - tempModuleElement.find('a.popup-ajax').popover({ - html: true, - trigger: 'hover', - placement: 'top', - delay: 200, - container: 'body', - content: function(){ - return details_in_popup(this); - } - }); - - function details_in_popup(popoverElement){ - popoverElement = $(popoverElement); - let popover = popoverElement.data('bs.popover'); - - $.ajax({ - url: popoverElement.data('url'), - success: function(data){ - let systemEffectTable = Util.getSystemsInfoTable( data.systemsData ); - popover.options.content = systemEffectTable; - // reopen popover (new content size) - popover.show(); - } - }); - return 'Loading...'; - } - - } - } - }; - - let moduleData = { - system: systemData, - static: staticsData, - tableClass: config.systemInfoTableClass, - nameInfoClass: config.systemInfoNameClass, - effectInfoClass: config.systemInfoEffectClass, - planetsInfoClass: config.systemInfoPlanetsClass, - wormholePrefixClass: config.systemInfoWormholeClass, - statusInfoClass: config.systemInfoStatusLabelClass, - - systemTypeName: MapUtil.getSystemTypeInfo(systemData.type.id, 'name'), - systemIsWormhole: MapUtil.getSystemTypeInfo(systemData.type.id, 'name') === 'w-space', - systemStatusId: systemData.status.id, - systemStatusClass: Util.getStatusInfoForSystem(systemData.status.id, 'class'), - systemStatusLabel: Util.getStatusInfoForSystem(systemData.status.id, 'label'), - securityClass: Util.getSecurityClassForSystem( systemData.security ), - trueSec: systemData.trueSec.toFixed(1), - trueSecClass: Util.getTrueSecClassForSystem( systemData.trueSec ), - effectName: effectName, - effectClass: effectClass, - moduleToolbarClass: config.moduleElementToolbarClass, - descriptionButtonClass: config.addDescriptionButtonClass, - tableToolsActionClass: config.tableToolsActionClass, - descriptionTextareaClass: config.descriptionTextareaElementClass, - systemNameClass: () => { - return (val, render) => { - return render(val) === 'A' ? config.fontTriglivianClass : ''; - }; - }, - formatUrl: () => { - return (val, render) => render(val).replace(/ /g, '_'); - }, - planetCount: systemData.planets ? systemData.planets.length : 0, - - shatteredClass: Util.getSecurityClassForSystem('SH'), - - ajaxConstellationInfoUrl: Init.path.getConstellationData, - - systemConstellationLinkClass: config.constellationLinkClass, - systemRegionLinkClass: config.regionLinkClass, - systemTypeLinkClass: config.typeLinkClass - - }; - - Render.showModule(moduleConfig, moduleData); - - return moduleElement; - }; - - /** - * efore module destroy callback - * @param moduleElement - */ - let beforeDestroy = (moduleElement) => { - // remove xEditable description textarea - let descriptionTextareaElement = moduleElement.find('.' + config.descriptionTextareaElementClass); - descriptionTextareaElement.editable('destroy'); - }; - - return { - config: config, - getModule: getModule, - updateModule: updateModule, - beforeDestroy: beforeDestroy - }; -}); - - - diff --git a/public/js/v1.4.1/app/ui/system_intel.js b/public/js/v1.4.1/app/ui/system_intel.js deleted file mode 100644 index 91ee7846..00000000 --- a/public/js/v1.4.1/app/ui/system_intel.js +++ /dev/null @@ -1,827 +0,0 @@ -/** - * system route module - */ - -define([ - 'jquery', - 'app/init', - 'app/util', - 'bootbox' -], ($, Init, Util, bootbox) => { - 'use strict'; - - let config = { - // module info - modulePosition: 1, - moduleName: 'systemIntel', - moduleHeadClass: 'pf-module-head', // class for module header - moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler - moduleTypeClass: 'pf-system-intel-module', // class for this module - - // headline toolbar - moduleHeadlineIconClass: 'pf-module-icon-button', // class for toolbar icons in the head - moduleHeadlineIconAddClass: 'pf-module-icon-button-add', // class for "add structure" icon - moduleHeadlineIconReaderClass: 'pf-module-icon-button-reader', // class for "dScan reader" icon - moduleHeadlineIconRefreshClass: 'pf-module-icon-button-refresh', // class for "refresh" icon - - // system intel module - systemStructuresTableClass: 'pf-system-structure-table', // class for route tables - - // structure dialog - structureDialogId: 'pf-structure-dialog', // id for "structure" dialog - statusSelectId: 'pf-structure-dialog-status-select', // id for "status" select - typeSelectId: 'pf-structure-dialog-type-select', // id for "type" select - corporationSelectId: 'pf-structure-dialog-corporation-select', // id for "corporation" select - descriptionTextareaId: 'pf-structure-dialog-description-textarea', // id for "description" textarea - descriptionTextareaCharCounter: 'pf-form-field-char-count', // class for "character counter" element for form field - - // dataTable - tableRowIdPrefix: 'pf-structure-row_', // id prefix for table rows - tableCellImageClass: 'pf-table-image-smaller-cell', // class for table "image" cells - tableCellCounterClass: 'pf-table-counter-cell', // class for table "counter" cells - tableCellEllipsisClass: 'pf-table-cell-ellipses-auto', // class for table "ellipsis" cells - dataTableActionCellClass: 'pf-table-action-cell' // class for "action" cells - }; - - let maxDescriptionLength = 512; - - /** - * get status icon for structure - * @param statusData - * @returns {string} - */ - let getStatusData = (statusData) => { - return ''; - }; - - /** - * get
DOM id by id - * @param tableApi - * @param id - * @returns {*} - */ - let getRowId = (tableApi, id) => { - return tableApi.rows().ids().toArray().find(rowId => rowId === config.tableRowIdPrefix + id); - }; - - /** - * callback -> add structure rows from responseData - * @param context - * @param responseData - */ - let callbackAddStructureRows = (context, responseData) => { - let systemData = Util.getObjVal(responseData, 'system'); - callbackUpdateStructureRows(context, systemData); - }; - - /** - * callback -> add structure rows from systemData - * @param context - * @param systemData - */ - let callbackUpdateStructureRows = (context, systemData) => { - let touchedRows = []; - let hadData = context.tableApi.rows().any(); - let notificationCounter = { - added: 0, - changed: 0, - deleted: 0 - }; - - if(systemData){ - let corporations = Util.getObjVal(systemData, 'structures'); - if(corporations) { - for (let [corporationId, corporationData] of Object.entries(corporations)){ - if(corporationData.structures && corporationData.structures.length){ - for(let structureData of corporationData.structures){ - let rowId = getRowId(context.tableApi, structureData.id); - - // add corporation data - structureData.corporation = { - id: corporationData.id, - name: corporationData.name - }; - - if(rowId){ - // update row - let api = context.tableApi.row('#' + rowId); - let rowData = api.data(); - - // check for update - if(rowData.updated.updated !== structureData.updated.updated){ - // row data changed -> update - api.data(structureData); - api.nodes().to$().data('animationStatus', 'changed').destroyTimestampCounter(); - notificationCounter.changed++; - } - - touchedRows.push(api.id()); - }else{ - // insert new row - let api = context.tableApi.row.add(structureData); - api.nodes().to$().data('animationStatus', 'added'); - notificationCounter.added++; - - touchedRows.push(api.id()); - } - } - } - } - } - } - - if(context.removeMissing){ - let api = context.tableApi.rows((idx, data, node) => !touchedRows.includes(node.id)); - notificationCounter.deleted += api.ids().count(); - api.remove(); - } - - context.tableApi.draw(); - - // show notification ------------------------------------------------------------------------------------------ - let notification = ''; - notification += notificationCounter.added > 0 ? notificationCounter.added + ' added
' : ''; - notification += notificationCounter.changed > 0 ? notificationCounter.changed + ' changed
' : ''; - notification += notificationCounter.deleted > 0 ? notificationCounter.deleted + ' deleted
' : ''; - - if(hadData && notification.length){ - Util.showNotify({title: 'Structures updated', text: notification, type: 'success'}); - } - }; - - /** - * callback -> delete structure rows - * @param context - * @param responseData - */ - let callbackDeleteStructures = (context, responseData) => { - let structureIds = Util.getObjVal(responseData, 'deletedStructureIds'); - let deletedCounter = 0; - if(structureIds && structureIds.length){ - for(let structureId of structureIds){ - let rowId = getRowId(context.tableApi, structureId); - if(rowId){ - context.tableApi.row('#' + rowId).remove(); - deletedCounter++; - } - } - } - - if(deletedCounter){ - context.tableApi.draw(); - Util.showNotify({title: 'Structure deleted', text: deletedCounter + ' deleted', type: 'success'}); - } - }; - - /** - * send ajax request - * @param url - * @param requestData - * @param context - * @param callback - */ - let sendRequest = (url, requestData, context, callback) => { - context.moduleElement.showLoadingAnimation(); - - $.ajax({ - url: url, - type: 'POST', - dataType: 'json', - data: requestData, - context: context - }).done(function(data){ - callback(this, data); - }).fail(function( jqXHR, status, error) { - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': System intel data', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }).always(function(){ - // hide loading animation - this.moduleElement.hideLoadingAnimation(); - }); - }; - - /** - * requests system data - * @param requestData - * @param context - * @param callback - */ - let getStructureData = (requestData, context, callback) => { - sendRequest(Init.path.getSystemData, requestData, context, callback); - }; - - /** - * save structure data - * @param requestData - * @param context - * @param callback - */ - let saveStructureData = (requestData, context, callback) => { - sendRequest(Init.path.saveStructureData, requestData, context, callback); - }; - - /** - * delete structure - * @param requestData - * @param context - * @param callback - */ - let deleteStructure = (requestData, context, callback) => { - sendRequest(Init.path.deleteStructureData, requestData, context, callback); - }; - - /** - * show structure dialog - * @param moduleElement - * @param tableApi - * @param systemId - * @param structureData - */ - let showStructureDialog = (moduleElement, tableApi, systemId, structureData) => { - let structureStatusData = Util.getObjVal(Init, 'structureStatus'); - let structureTypeData = Util.getObjVal(Init, 'structureStatus'); - - let statusData = Object.keys(structureStatusData).map((k) => { - let data = structureStatusData[k]; - data.selected = data.id === Util.getObjVal(structureData, 'status.id'); - return data; - }); - - let data = { - id: config.structureDialogId, - structureData: structureData, - structureStatus: statusData, - statusSelectId: config.statusSelectId, - typeSelectId: config.typeSelectId, - corporationSelectId: config.corporationSelectId, - descriptionTextareaId: config.descriptionTextareaId, - descriptionTextareaCharCounter: config.descriptionTextareaCharCounter, - maxDescriptionLength: maxDescriptionLength - }; - - requirejs(['text!templates/dialog/structure.html', 'mustache'], (template, Mustache) => { - let content = Mustache.render(template, data); - - let structureDialog = bootbox.dialog({ - title: 'Structure', - message: content, - show: false, - buttons: { - close: { - label: 'cancel', - className: 'btn-default' - }, - success: { - label: ' save', - className: 'btn-success', - callback: function (){ - let form = this.find('form'); - - // validate form - form.validator('validate'); - - // check whether the form is valid - let formValid = form.isValidForm(); - - if(formValid){ - // get form data - let formData = form.getFormValues(); - formData.id = Util.getObjVal(structureData, 'id') | 0; - formData.structureId = Util.getObjVal(formData, 'structureId') | 0; - formData.corporationId = Util.getObjVal(formData, 'corporationId') | 0; - formData.systemId = systemId | 0; - - saveStructureData({ - structures: [formData] - }, { - moduleElement: moduleElement, - tableApi: tableApi - }, callbackUpdateStructureRows); - }else{ - return false; - } - } - } - } - }); - - structureDialog.on('show.bs.modal', function(e) { - let modalContent = $('#' + config.structureDialogId); - - // init type select live search - let selectElementType = modalContent.find('#' + config.typeSelectId); - selectElementType.initUniverseTypeSelect({ - categoryIds: [65], - maxSelectionLength: 1, - selected: [Util.getObjVal(structureData, 'structure.id')] - }); - - // init corporation select live search - let selectElementCorporation = modalContent.find('#' + config.corporationSelectId); - selectElementCorporation.initUniverseSearch({ - categoryNames: ['corporation'], - maxSelectionLength: 1 - }); - - // init status select2 - modalContent.find('#' + config.statusSelectId).initStatusSelect({ - data: statusData - }); - - // init character counter - let textarea = modalContent.find('#' + config.descriptionTextareaId); - let charCounter = modalContent.find('.' + config.descriptionTextareaCharCounter); - Util.updateCounter(textarea, charCounter, maxDescriptionLength); - - textarea.on('keyup', function(){ - Util.updateCounter($(this), charCounter, maxDescriptionLength); - }); - - // set form validator (after select2 init finish) - modalContent.find('form').initFormValidation(); - }); - - // show dialog - structureDialog.modal('show'); - }); - }; - - /** - * show D-Scan reader dialog - * @param moduleElement - * @param tableApi - * @param systemData - */ - let showDscanReaderDialog = (moduleElement, tableApi, systemData) => { - requirejs(['text!templates/dialog/dscan_reader.html', 'mustache'], (template, Mustache) => { - let structureDialog = bootbox.dialog({ - title: 'D-Scan reader', - message: Mustache.render(template, {}), - show: true, - buttons: { - close: { - label: 'cancel', - className: 'btn-default' - }, - success: { - label: ' update intel', - className: 'btn-success', - callback: function (){ - let form = this.find('form'); - let formData = form.getFormValues(); - - updateStructureTableByClipboard(systemData, formData.clipboard, { - moduleElement: moduleElement, - tableApi: tableApi - }); - } - } - } - }); - - // dialog shown event - structureDialog.on('shown.bs.modal', function(e) { - // set focus on textarea - structureDialog.find('textarea').focus(); - }); - }); - }; - - /** - * get module element - * @param parentElement - * @param mapId - * @param systemData - * @returns {jQuery} - */ - let getModule = (parentElement, mapId, systemData) => { - let corporationId = Util.getCurrentUserInfo('corporationId'); - - // create new module container - let moduleElement = $('
').append( - $('
', { - class: config.moduleHeadClass - }).append( - $('
', { - class: config.moduleHandlerClass - }), - $('
', { - class: 'pull-right' - }).append( - $('', { - class: ['fas', 'fa-fw', 'fa-plus', config.moduleHeadlineIconClass, config.moduleHeadlineIconAddClass].join(' '), - title: 'add' - }).attr('data-html', 'true').attr('data-toggle', 'tooltip'), - $('', { - class: ['fas', 'fa-fw', 'fa-paste', config.moduleHeadlineIconClass, config.moduleHeadlineIconReaderClass].join(' '), - title: 'D-Scan reader' - }).attr('data-html', 'true').attr('data-toggle', 'tooltip'), - $('', { - class: ['fas', 'fa-fw', 'fa-sync', config.moduleHeadlineIconClass, config.moduleHeadlineIconRefreshClass].join(' '), - title: 'refresh all' - }).attr('data-html', 'true').attr('data-toggle', 'tooltip') - ), - $('
', { - text: 'Intel' - }) - ) - ); - - let table = $('
', { - class: ['compact', 'stripe', 'order-column', 'row-border', 'pf-table-fixed', config.systemStructuresTableClass].join(' ') - }); - moduleElement.append(table); - - let structureTable = table.DataTable({ - paging: false, - lengthChange: false, - ordering: true, - order: [[ 10, 'desc' ], [ 0, 'asc' ]], - info: false, - searching: false, - hover: false, - autoWidth: false, - rowId: rowData => config.tableRowIdPrefix + rowData.id, - language: { - emptyTable: 'No structures recorded', - info: '_START_ to _END_ of _MAX_', - infoEmpty: '' - }, - rowGroup: { - enable: true, - dataSrc: 'systemId' - }, - columnDefs: [ - { - targets: 0, - title: '', - width: 2, - class: 'text-center', - data: 'status', - render: { - display: data => getStatusData(data), - sort: data => data.id - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - $(cell).find('i').tooltip(); - } - },{ - targets: 1, - title: '', - width: 26, - orderable: false, - className: [config.tableCellImageClass, 'text-center'].join(' '), - data: 'structure.id', - defaultContent: '', - render: { - _: function(data, type, row, meta){ - let value = data; - if(type === 'display' && value){ - value = ''; - } - return value; - } - } - },{ - targets: 2, - title: 'type', - width: 30, - className: [config.tableCellEllipsisClass].join(' '), - data: 'structure.name', - defaultContent: '', - },{ - targets: 3, - title: 'name', - width: 60, - className: [config.tableCellEllipsisClass].join(' '), - data: 'name' - },{ - targets: 4, - title: '', - width: 26, - orderable: false, - className: [config.tableCellImageClass, 'text-center'].join(' '), - data: 'owner.id', - defaultContent: '', - render: { - _: function(data, type, row, meta){ - let value = data; - if(type === 'display' && value){ - value = ''; - value += ''; - value += ''; - } - return value; - } - } - },{ - targets: 5, - title: 'owner', - width: 50, - className: [config.tableCellEllipsisClass].join(' '), - data: 'owner.name', - defaultContent: '', - },{ - targets: 6, - title: 'note', - className: [config.tableCellEllipsisClass].join(' '), - data: 'description' - },{ - targets: 7, - title: 'updated', - width: 60, - className: ['text-right', config.tableCellCounterClass].join(' '), - data: 'updated.updated' - },{ - targets: 8, - title: '', - orderable: false, - width: 10, - class: ['text-center', config.dataTableActionCellClass, config.moduleHeadlineIconClass].join(' '), - data: null, - render: { - display: data => { - let icon = ''; - if(data.corporation.id !== corporationId){ - icon = ''; - } - return icon; - } - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let tableApi = this.api(); - - if($(cell).is(':empty')){ - $(cell).removeClass(config.dataTableActionCellClass + ' ' + config.moduleHeadlineIconClass); - }else{ - $(cell).on('click', function(e) { - // get current row data (important!) - // -> "rowData" param is not current state, values are "on createCell()" state - rowData = tableApi.row( $(cell).parents('tr')).data(); - showStructureDialog(moduleElement, tableApi, systemData.systemId, rowData); - }); - } - } - },{ - targets: 9, - title: '', - orderable: false, - width: 10, - class: ['text-center', config.dataTableActionCellClass].join(' '), - data: null, - render: { - display: data => { - let icon = ''; - if(data.corporation.id !== corporationId){ - icon = ''; - } - return icon; - } - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let tableApi = this.api(); - - if($(cell).find('.fa-ban').length){ - $(cell).removeClass(config.dataTableActionCellClass + ' ' + config.moduleHeadlineIconClass); - $(cell).find('i').tooltip(); - }else{ - let confirmationSettings = { - container: 'body', - placement: 'left', - btnCancelClass: 'btn btn-sm btn-default', - btnCancelLabel: 'cancel', - btnCancelIcon: 'fas fa-fw fa-ban', - title: 'delete structure', - btnOkClass: 'btn btn-sm btn-danger', - btnOkLabel: 'delete', - btnOkIcon: 'fas fa-fw fa-times', - onConfirm : function(e, target){ - // get current row data (important!) - // -> "rowData" param is not current state, values are "on createCell()" state - rowData = tableApi.row( $(cell).parents('tr')).data(); - - // let deleteRowElement = $(cell).parents('tr'); - // tableApi.rows(deleteRowElement).remove().draw(); - deleteStructure({ - id: rowData.id - },{ - moduleElement: moduleElement, - tableApi: tableApi - }, callbackDeleteStructures); - } - }; - - // init confirmation dialog - $(cell).confirmation(confirmationSettings); - } - } - },{ - targets: 10, - name: 'corporation', - data: 'corporation', - visible: false, - render: { - sort: function(data){ - return data.name; - } - } - } - ], - drawCallback: function (settings){ - let tableApi = this.api(); - let columnCount = tableApi.columns(':visible').count(); - let rows = tableApi.rows( {page:'current'} ).nodes(); - let last= null; - - tableApi.column('corporation:name', {page:'current'} ).data().each( function ( group, i ) { - if ( !last || last.id !== group.id ) { - $(rows).eq(i).before( - '' + - '' + - '' + - '' + - '' - ); - last = group; - } - }); - - rows.to$().find('.' + config.tableCellCounterClass + ':not([data-counter])').initTimestampCounter('d'); - - let animationRows = rows.to$().filter(function() { - return ( - $(this).data('animationStatus') || - $(this).data('animationTimer') - ); - }); - - for(let i = 0; i < animationRows.length; i++){ - let animationRow = $(animationRows[i]); - animationRow.pulseTableRow(animationRow.data('animationStatus')); - animationRow.removeData('animationStatus'); - } - }, - initComplete: function(settings){ - // table data is load in updateModule() method - // -> no need to trigger additional ajax call here for data - // -> in case table update failed -> each if this initComplete() function finished before table updata - // e.g. return now promise in getModule() function - } - }); - - // init tooltips for this module - let tooltipElements = moduleElement.find('[data-toggle="tooltip"]'); - tooltipElements.tooltip({ - container: 'body' - }); - - moduleElement.showLoadingAnimation(); - - return moduleElement; - }; - - /** - * get universe typeIds for given categoryIds - * @param categoryIds - * @returns {Array} - */ - let getUniverseTypeIdsByCategoryIds = (categoryIds) => { - let typeIds = []; - let mapIds = type => type.id; - for(let categoryId of categoryIds){ - let categoryData = Util.getObjVal(Init, 'universeCategories.' + categoryId); - if(categoryData && categoryData.groups){ - for(let groupData of categoryData.groups){ - if(groupData && groupData.types){ - typeIds = typeIds.concat(groupData.types.map(mapIds)); - } - } - } - } - - return typeIds; - }; - - /** - * parse a copy&paste string from ingame dScan windows - * @param systemData - * @param clipboard - * @returns {Array} - */ - let parseDscanString = (systemData, clipboard) => { - let dScanData = []; - let structureTypeIds = getUniverseTypeIdsByCategoryIds([65]); - - if(clipboard.length){ - let dScanRows = clipboard.split(/\r\n|\r|\n/g); - - for(let rowData of dScanRows){ - rowData = rowData.split(/\t/g); - - if(rowData.length === 4){ - rowData[0] = parseInt(rowData[0]); - // valid dScan result - if(structureTypeIds.indexOf( rowData[0] ) !== -1){ - dScanData.push({ - structureId: rowData[0], - name: rowData[1], - systemId: systemData.systemId - }); - } - } - } - } - - return dScanData; - }; - - /** - * parse clipboard data for structures and update table - * @param systemData - * @param clipboard - * @param context - */ - let updateStructureTableByClipboard = (systemData, clipboard, context) => { - let structureData = parseDscanString(systemData, clipboard); - if(structureData.length){ - saveStructureData({ - structures: structureData - }, context, callbackUpdateStructureRows); - } - }; - - /** - * update trigger function for this module - * compare data and update module - * @param moduleElement - * @param systemData - */ - let updateModule = (moduleElement, systemData) => { - - // update structure table data - let structureTableElement = moduleElement.find('.' + config.systemStructuresTableClass); - let tableApi = structureTableElement.DataTable(); - - let context = { - tableApi: tableApi, - removeMissing: true - }; - - callbackUpdateStructureRows(context, systemData); - - moduleElement.hideLoadingAnimation(); - }; - - /** - * init intel module - * @param moduleElement - * @param mapId - * @param systemData - */ - let initModule = (moduleElement, mapId, systemData) => { - - let structureTableElement = moduleElement.find('.' + config.systemStructuresTableClass); - let tableApi = structureTableElement.DataTable(); - - // init structure dialog -------------------------------------------------------------------------------------- - moduleElement.find('.' + config.moduleHeadlineIconAddClass).on('click', function(e){ - showStructureDialog(moduleElement, tableApi, systemData.systemId); - }); - - // init structure dialog -------------------------------------------------------------------------------------- - moduleElement.find('.' + config.moduleHeadlineIconReaderClass).on('click', function(e){ - showDscanReaderDialog(moduleElement, tableApi, systemData); - }); - - // init refresh button ---------------------------------------------------------------------------------------- - moduleElement.find('.' + config.moduleHeadlineIconRefreshClass).on('click', function(e){ - getStructureData({ - mapId: mapId, - systemId: systemData.id - },{ - moduleElement: moduleElement, - tableApi: tableApi, - removeMissing: true - }, callbackAddStructureRows); - }); - - // init listener for global "past" dScan into this page ------------------------------------------------------- - moduleElement.on('pf:updateIntelModuleByClipboard', function(e, clipboard){ - updateStructureTableByClipboard(systemData, clipboard, { - moduleElement: moduleElement, - tableApi: tableApi - }); - }); - }; - - return { - config: config, - getModule: getModule, - initModule: initModule, - updateModule: updateModule - }; - -}); \ No newline at end of file diff --git a/public/js/v1.4.1/app/ui/system_killboard.js b/public/js/v1.4.1/app/ui/system_killboard.js deleted file mode 100644 index e11c0605..00000000 --- a/public/js/v1.4.1/app/ui/system_killboard.js +++ /dev/null @@ -1,404 +0,0 @@ -define([ - 'jquery', - 'app/init', - 'app/util', - 'morris' -], ($, Init, Util, Morris) => { - 'use strict'; - - let config = { - // module info - modulePosition: 2, - moduleName: 'systemKillboard', - moduleHeadClass: 'pf-module-head', // class for module header - moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler - - // headline toolbar - moduleHeadlineIconClass: 'pf-module-icon-button', // class for toolbar icons in the head - - // system killboard module - moduleTypeClass: 'pf-system-killboard-module', // class for this module - systemKillboardGraphKillsClass: 'pf-system-killboard-graph-kills', // class for system kill graph - - // system killboard list - systemKillboardListClass: 'pf-system-killboard-list', // class for a list with kill entries - systemKillboardListEntryClass: 'pf-system-killboard-list-entry', // class for a list entry - systemKillboardListImgShip: 'pf-system-killboard-img-ship', // class for all ship images - systemKillboardListImgChar: 'pf-system-killboard-img-char', // class for all character logos - systemKillboardListImgAlly: 'pf-system-killboard-img-ally', // class for all alliance logos - systemKillboardListImgCorp: 'pf-system-killboard-img-corp' // class for all corp logos - }; - - let cache = { - systemKillsGraphData: {} // data for system kills info graph - }; - - /** - * - * @param text - * @param options - * @returns {jQuery} - */ - let getLabel = (text, options) => { - let label = $('', { - class: ['label', options.type, options.align].join(' ') - }).text( text ); - - return label; - }; - - /** - * show killMails - * @param moduleElement - * @param killboardData - */ - let showKillmails = (moduleElement, killboardData) => { - - // show number of killMails - let killMailCounterMax = 20; - let killMailCounter = 0; - - // change order (show right to left) - killboardData.tableData.reverse(); - - let data = { - tableData: killboardData.tableData, - systemKillboardListClass: config.systemKillboardListClass, - systemKillboardListEntryClass: config.systemKillboardListEntryClass, - systemKillboardListImgShip: config.systemKillboardListImgShip, - systemKillboardListImgChar: config.systemKillboardListImgChar, - systemKillboardListImgAlly: config.systemKillboardListImgAlly, - systemKillboardListImgCorp: config.systemKillboardListImgCorp, - zKillboardUrl: 'https://zkillboard.com', - ccpImageServerUrl: Init.url.ccpImageServer, - - dateFormat: () => { - return (val, render) => { - let killDate = Util.convertDateToUTC(new Date(render(val))); - return Util.convertDateToString(killDate); - }; - }, - iskFormat: () => { - return (val, render) => { - return Util.formatPrice(render(val)); - }; - }, - checkRender : () => { - return (val, render) => { - if(killMailCounter < killMailCounterMax){ - return render(val); - } - }; - }, - increaseCount : () => { - return (val, render) => { - killMailCounter++; - }; - } - }; - - requirejs(['text!templates/modules/killboard.html', 'mustache'], function(template, Mustache) { - let content = Mustache.render(template, data); - - moduleElement.append(content); - - // animate kill li-elements - $('.' + config.systemKillboardListEntryClass).velocity('transition.expandIn', { - stagger: 50, - complete: function(){ - // init tooltips - moduleElement.find('[title]').tooltip({ - container: 'body' - }); - - } - }); - }); - }; - - /** - * updates the system info graph - * @param systemData - */ - $.fn.updateSystemInfoGraphs = function(systemData){ - let moduleElement = $(this); - - let killboardGraphElement = $('
', { - class: config.systemKillboardGraphKillsClass - }); - - moduleElement.append(killboardGraphElement); - - let showHours = 24; - let maxKillmailCount = 200; // limited by API - - let labelOptions = { - align: 'center-block' - }; - let label = ''; - - // private function draws a "system kills" graph - let drawGraph = function(data){ - let tableData = data.tableData; - - // change order (show right to left) - tableData.reverse(); - - if(data.count === 0){ - labelOptions.type = 'label-success'; - label = getLabel( 'No kills found within the last 24h', labelOptions ); - killboardGraphElement.append( label ); - - minifyKillboardGraphElement(killboardGraphElement); - return; - } - - let labelYFormat = function(y){ - return Math.round(y); - }; - - // draw chart - Morris.Bar({ - element: killboardGraphElement, - resize: true, - redraw: true, - grid: true, - gridStrokeWidth: 0.3, - gridTextSize: 9, - gridTextColor: '#63676a', - gridTextFamily: 'Oxygen Bold', - hideHover: true, - data: tableData, - xkey: 'label', - ykeys: ['kills'], - labels: ['Kills'], - yLabelFormat: labelYFormat, - xLabelMargin: 10, - padding: 10, - parseTime: false, - barOpacity: 0.8, - barRadius: [2, 2, 0, 0], - barSizeRatio: 0.5, - barGap: 3, - barColors: function (row, series, type) { - if (type === 'bar') { - // highlight last row -> recent kills found - if(this.xmax === row.x){ - return '#c2760c'; - } - } - - return '#375959'; - } - }); - - // show hint for recent kills - if(tableData[tableData.length - 1].kills > 0){ - labelOptions.type = 'label-warning'; - label = getLabel( tableData[tableData.length - 1].kills + ' kills within the last hour', labelOptions ); - killboardGraphElement.prepend( label ); - } - }; - - // get recent KB stats (last 24h)) - let localDate = new Date(); - - // cache result for 5min - let cacheKey = systemData.systemId + '_' + localDate.getHours() + '_' + ( Math.ceil( localDate.getMinutes() / 5 ) * 5); - - if(cache.systemKillsGraphData.hasOwnProperty(cacheKey) ){ - // cached results - - drawGraph( cache.systemKillsGraphData[cacheKey] ); - - // show killmail information - showKillmails(moduleElement, cache.systemKillsGraphData[cacheKey]); - }else{ - - // chart data - let chartData = []; - - for(let i = 0; i < showHours; i++){ - let tempData = { - label: i + 'h', - kills: 0 - }; - - chartData.push(tempData); - } - - // get kills within the last 24h - let timeFrameInSeconds = 60 * 60 * 24; - - // get current server time - let serverDate= Util.getServerTime(); - - // if system is w-space system -> add link modifier - let wSpaceLinkModifier = ''; - if(systemData.type.id === 1){ - wSpaceLinkModifier = 'w-space/'; - } - - let url = Init.url.zKillboard + '/'; - url += 'no-items/' + wSpaceLinkModifier + 'no-attackers/solarSystemID/' + systemData.systemId + '/pastSeconds/' + timeFrameInSeconds + '/'; - - killboardGraphElement.showLoadingAnimation(); - - $.ajax({ - url: url, - type: 'GET', - dataType: 'json' - }).done(function(kbData) { - - // the API wont return more than 200KMs ! - remember last bar block with complete KM information - let lastCompleteDiffHourData = 0; - - // loop kills and count kills by hour - for (let i = 0; i < kbData.length; i++) { - let killmailData = kbData[i]; - let killDate = Util.convertDateToUTC(new Date(killmailData.killmail_time)); - - // get time diff - let timeDiffMin = Math.round(( serverDate - killDate ) / 1000 / 60); - let timeDiffHour = Math.floor(timeDiffMin / 60); - - // update chart data - if (chartData[timeDiffHour]) { - chartData[timeDiffHour].kills++; - - // add kill mail data - if (chartData[timeDiffHour].killmails === undefined) { - chartData[timeDiffHour].killmails = []; - } - chartData[timeDiffHour].killmails.push(killmailData); - - if (timeDiffHour > lastCompleteDiffHourData) { - lastCompleteDiffHourData = timeDiffHour; - } - } - - } - - // remove empty chart Data - if (kbData.length >= maxKillmailCount) { - chartData = chartData.splice(0, lastCompleteDiffHourData + 1); - } - - // fill cache - cache.systemKillsGraphData[cacheKey] = {}; - cache.systemKillsGraphData[cacheKey].tableData = chartData; - cache.systemKillsGraphData[cacheKey].count = kbData.length; - - // draw table - drawGraph(cache.systemKillsGraphData[cacheKey]); - - // show killmail information - showKillmails(moduleElement, cache.systemKillsGraphData[cacheKey]); - - killboardGraphElement.hideLoadingAnimation(); - }).fail(function(e){ - - labelOptions.type = 'label-danger'; - label = getLabel( 'zKillboard is not responding', labelOptions ); - killboardGraphElement.prepend( label ); - - killboardGraphElement.hideLoadingAnimation(); - - minifyKillboardGraphElement(killboardGraphElement); - - Util.showNotify({title: e.status + ': Get system kills', text: 'Loading failed', type: 'error'}); - }); - } - - - - // init tooltips - let tooltipElements = moduleElement.find('[data-toggle="tooltip"]'); - tooltipElements.tooltip({ - container: 'body' - }); - - }; - - /** - * minify the killboard graph element e.g. if no kills where found, or on error - * @param killboardGraphElement - */ - let minifyKillboardGraphElement = (killboardGraphElement) => { - killboardGraphElement.velocity({ - height: '20px', - marginBottom: '0px' - },{ - duration: Init.animationSpeed.mapModule - }); - }; - - /** - * get module toolbar element - * @param systemData - * @returns {*|jQuery|HTMLElement|void} - */ - let getHeadlineToolbar = (systemData) => { - let headlineToolbar = $('
', { - class: 'pull-right' - }).append( - $('', { - class: ['fas', 'fa-fw', 'fa-external-link-alt ', config.moduleHeadlineIconClass].join(' '), - title: 'zkillboard.com' - }).on('click', function(e){ - window.open( - '//zkillboard.com/system/' + systemData.systemId, - '_blank' - ); - }).attr('data-toggle', 'tooltip') - ); - - headlineToolbar.find('[data-toggle="tooltip"]').tooltip({ - container: 'body' - }); - - return headlineToolbar; - }; - - /** - * before module "show" callback - * @param moduleElement - * @param systemData - */ - let beforeShow = (moduleElement, systemData) => { - // update graph - moduleElement.updateSystemInfoGraphs(systemData); - }; - - /** - * get module element - * @param parentElement - * @param mapId - * @param systemData - * @returns {jQuery} - */ - let getModule = (parentElement, mapId, systemData) => { - // create new module container - let moduleElement = $('
').append( - $('
', { - class: config.moduleHeadClass - }).append( - $('
', { - class: config.moduleHandlerClass - }), - $('
', { - text: 'Killboard' - }), - getHeadlineToolbar(systemData) - ) - ); - - return moduleElement; - }; - - return { - config: config, - getModule: getModule, - beforeShow: beforeShow - }; -}); \ No newline at end of file diff --git a/public/js/v1.4.1/app/ui/system_route.js b/public/js/v1.4.1/app/ui/system_route.js deleted file mode 100644 index 1ee400a7..00000000 --- a/public/js/v1.4.1/app/ui/system_route.js +++ /dev/null @@ -1,1369 +0,0 @@ -/** - * system route module - */ - -define([ - 'jquery', - 'app/init', - 'app/util', - 'bootbox', - 'app/map/util' -], ($, Init, Util, bootbox, MapUtil) => { - 'use strict'; - - let config = { - // module info - modulePosition: 1, - moduleName: 'systemRoute', - moduleHeadClass: 'pf-module-head', // class for module header - moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler - - routeCacheTTL: 5, // route cache timer (client) in seconds - - // system route module - moduleTypeClass: 'pf-system-route-module', // class for this module - - // headline toolbar - moduleHeadlineIconClass: 'pf-module-icon-button', // class for toolbar icons in the head - moduleHeadlineIconSearchClass: 'pf-module-icon-button-search', // class for "search" icon - moduleHeadlineIconSettingsClass: 'pf-module-icon-button-settings', // class for "settings" icon - moduleHeadlineIconRefreshClass: 'pf-module-icon-button-refresh', // class for "refresh" icon - - systemSecurityClassPrefix: 'pf-system-security-', // prefix class for system security level (color) - - // dialog - routeSettingsDialogId: 'pf-route-settings-dialog', // id for route "settings" dialog - routeDialogId: 'pf-route-dialog', // id for route "search" dialog - systemDialogSelectClass: 'pf-system-dialog-select', // class for system select Element - systemInfoRoutesTableClass: 'pf-system-route-table', // class for route tables - mapSelectId: 'pf-route-dialog-map-select', // id for "map" select - - dataTableActionCellClass: 'pf-table-action-cell', // class for "action" cells - dataTableRouteCellClass: 'pf-table-route-cell', // class for "route" cells - dataTableJumpCellClass: 'pf-table-jump-cell', // class for "route jump" cells - - rallyClass: 'pf-rally' // class for "rally point" style - }; - - // cache for system routes - let cache = { - systemRoutes: {}, // jump information between solar systems - mapConnections: {} // connection data read from UI - }; - - /** - * set cache data - * @param cacheType - * @param cacheKey - * @param data - */ - let setCacheData = (cacheType, cacheKey, data) => { - cache[cacheType][cacheKey] = { - data: data, - updated: Util.getServerTime().getTime() / 1000 - }; - }; - - /** - * get cache data - * @param cacheType - * @param cacheKey - * @returns {*} - */ - let getCacheData = (cacheType, cacheKey) => { - let cachedData = null; - let currentTimestamp = Util.getServerTime().getTime(); - - if( - cache[cacheType].hasOwnProperty(cacheKey) && - Math.round( - ( currentTimestamp - (new Date( cache[cacheType][cacheKey].updated * 1000).getTime())) / 1000 - ) <= config.routeCacheTTL - ){ - cachedData = cache[cacheType][cacheKey].data; - } - - return cachedData; - }; - - let getRouteDataCacheKey = (mapIds, sourceName, targetName) => { - return [mapIds.join('_'), sourceName.toLowerCase(), targetName.toLowerCase()].join('###'); - }; - - /** - * get a unique cache key name for "source"/"target"-name - * @param sourceName - * @param targetName - * @returns {*} - */ - let getConnectionDataCacheKey = (sourceName, targetName) => { - let key = false; - if(sourceName && targetName){ - // names can be "undefined" in case system is currently on drag/drop - key = [sourceName.toLowerCase(), targetName.toLowerCase()].sort().join('###'); - } - return key; - }; - - /** - * callback function, adds new row to a dataTable with jump information for a route - * @param context - * @param routesData - */ - let callbackAddRouteRows = (context, routesData) => { - if(routesData.length > 0){ - for(let routeData of routesData){ - // format routeData - let rowData = formatRouteData(routeData); - if(rowData.route){ - // update route cache - let cacheKey = getRouteDataCacheKey(rowData.mapIds, routeData.systemFromData.name, routeData.systemToData.name); - setCacheData('systemRoutes', cacheKey, rowData); - - addRow(context, rowData); - } - } - - // redraw dataTable - context.dataTable.draw(); - } - }; - - /** - * add a new dataTable row to the routes table - * @param context - * @param rowData - * @returns {*} - */ - let addRow = (context, rowData) => { - let dataTable = context.dataTable; - let rowElement = null; - let row = null; - let animationStatus = 'changed'; - - // search for an existing row (e.g. on mass "table refresh" [all routes]) - // get rowIndex where column 1 (equals to "systemToData.name") matches rowData.systemToData.name - let indexes = dataTable.rows().eq(0).filter((rowIdx) => { - return (dataTable.cell(rowIdx, 1).data().name === rowData.systemToData.name); - }); - - if(indexes.length > 0){ - // update row with FIRST index - // -> systemFrom should be unique! - row = dataTable.row( parseInt(indexes[0]) ); - // update row data - row.data(rowData); - }else{ - // no existing route found -> add new row - row = dataTable.row.add( rowData ); - - animationStatus = 'added'; - } - - if(row.length > 0){ - rowElement = row.nodes().to$(); - rowElement.data('animationStatus', animationStatus); - - rowElement.initTooltips({ - container: 'body' - }); - } - - return rowElement; - }; - - /** - * requests route data from eveCentral API and execute callback - * @param requestData - * @param context - * @param callback - */ - let getRouteData = (requestData, context, callback) => { - context.moduleElement.showLoadingAnimation(); - - $.ajax({ - url: Init.path.searchRoute, - type: 'POST', - dataType: 'json', - data: requestData, - context: context - }).done(function(routesData){ - this.moduleElement.hideLoadingAnimation(); - - // execute callback - callback(this, routesData.routesData); - }); - }; - - /** - * update complete routes table (refresh all) - * @param moduleElement - * @param dataTable - */ - let updateRoutesTable = (moduleElement, dataTable) => { - let context = { - moduleElement: moduleElement, - dataTable: dataTable - }; - let routeData = []; - - dataTable.rows().every( function() { - routeData.push( getRouteRequestDataFromRowData( this.data() )); - }); - - getRouteData({routeData: routeData}, context, callbackAddRouteRows); - }; - - /** - * format rowData for route search/update request - * @param {Object} rowData - * @returns {Object} - */ - let getRouteRequestDataFromRowData = (rowData) => { - return { - mapIds: (rowData.hasOwnProperty('mapIds')) ? rowData.mapIds : [], - systemFromData: (rowData.hasOwnProperty('systemFromData')) ? rowData.systemFromData : {}, - systemToData: (rowData.hasOwnProperty('systemToData')) ? rowData.systemToData : {}, - skipSearch: (rowData.hasOwnProperty('skipSearch')) ? rowData.skipSearch | 0 : 0, - stargates: (rowData.hasOwnProperty('stargates')) ? rowData.stargates | 0 : 1, - jumpbridges: (rowData.hasOwnProperty('jumpbridges')) ? rowData.jumpbridges | 0 : 1, - wormholes: (rowData.hasOwnProperty('wormholes')) ? rowData.wormholes | 0 : 1, - wormholesReduced: (rowData.hasOwnProperty('wormholesReduced')) ? rowData.wormholesReduced | 0 : 1, - wormholesCritical: (rowData.hasOwnProperty('wormholesCritical')) ? rowData.wormholesCritical | 0 : 1, - wormholesFrigate: (rowData.hasOwnProperty('wormholesFrigate')) ? rowData.wormholesFrigate | 0 : 1, - wormholesEOL: (rowData.hasOwnProperty('wormholesEOL')) ? rowData.wormholesEOL | 0 : 1, - connections: (rowData.hasOwnProperty('connections')) ? rowData.connections.value | 0 : 0, - flag: (rowData.hasOwnProperty('flag')) ? rowData.flag.value : 'shortest' - }; - }; - - /** - * show route dialog. User can search for systems and jump-info for each system is added to a data table - * @param dialogData - */ - let showFindRouteDialog = (dialogData) => { - - let mapSelectOptions = []; - let currentMapData = Util.getCurrentMapData(); - if(currentMapData !== false){ - for(let i = 0; i < currentMapData.length; i++){ - mapSelectOptions.push({ - id: currentMapData[i].config.id, - name: currentMapData[i].config.name, - selected: (dialogData.mapId === currentMapData[i].config.id) - }); - } - } - let data = { - id: config.routeDialogId, - selectClass: config.systemDialogSelectClass, - mapSelectId: config.mapSelectId, - systemFromData: dialogData.systemFromData, - systemToData: dialogData.systemToData, - mapSelectOptions: mapSelectOptions - }; - - requirejs(['text!templates/dialog/route.html', 'mustache'], (template, Mustache) => { - - let content = Mustache.render(template, data); - - let findRouteDialog = bootbox.dialog({ - title: 'Route finder', - message: content, - show: false, - buttons: { - close: { - label: 'cancel', - className: 'btn-default' - }, - success: { - label: ' search route', - className: 'btn-primary', - callback: function () { - // add new route to route table - - // get form Values - let form = $('#' + config.routeDialogId).find('form'); - - let routeDialogData = $(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; - } - - // get all system data from select2 - let systemSelectData = form.find('.' + config.systemDialogSelectClass).select2('data'); - - if( - systemSelectData && - systemSelectData.length === 1 - ){ - let context = { - moduleElement: dialogData.moduleElement, - dataTable: dialogData.dataTable - }; - - let requestData = { - routeData: [{ - mapIds: routeDialogData.mapIds, - systemFromData: dialogData.systemFromData, - systemToData: { - systemId: parseInt(systemSelectData[0].id), - name: systemSelectData[0].text - }, - stargates: routeDialogData.hasOwnProperty('stargates') ? parseInt(routeDialogData.stargates) : 0, - jumpbridges: routeDialogData.hasOwnProperty('jumpbridges') ? parseInt(routeDialogData.jumpbridges) : 0, - wormholes: routeDialogData.hasOwnProperty('wormholes') ? parseInt(routeDialogData.wormholes) : 0, - wormholesReduced: routeDialogData.hasOwnProperty('wormholesReduced') ? parseInt(routeDialogData.wormholesReduced) : 0, - wormholesCritical: routeDialogData.hasOwnProperty('wormholesCritical') ? parseInt(routeDialogData.wormholesCritical) : 0, - wormholesFrigate: routeDialogData.hasOwnProperty('wormholesFrigate') ? parseInt(routeDialogData.wormholesFrigate) : 0, - wormholesEOL: routeDialogData.hasOwnProperty('wormholesEOL') ? parseInt(routeDialogData.wormholesEOL) : 0 - }] - }; - - getRouteData(requestData, context, callbackAddRouteRows); - } - } - } - } - }); - - findRouteDialog.on('show.bs.modal', function(e) { - findRouteDialog.initTooltips(); - - // init some dialog/form observer - setDialogObserver( $(this) ); - - // init map select ---------------------------------------------------------------- - let mapSelect = $(this).find('#' + config.mapSelectId); - mapSelect.initMapSelect(); - }); - - - findRouteDialog.on('shown.bs.modal', function(e) { - - // init system select live search ------------------------------------------------ - // -> add some delay until modal transition has finished - let systemTargetSelect = $(this).find('.' + config.systemDialogSelectClass); - systemTargetSelect.delay(240).initSystemSelect({key: 'id'}); - }); - - // show dialog - findRouteDialog.modal('show'); - }); - }; - - /** - * draw route table - * @param mapId - * @param moduleElement - * @param systemFromData - * @param routesTable - * @param systemsToData - */ - let drawRouteTable = (mapId, moduleElement, systemFromData, routesTable, systemsToData) => { - let requestRouteData = []; - - // Skip some routes from search - // -> this should help to throttle requests (heavy CPU load for route calculation) - let defaultRoutesCount = Init.routeSearch.defaultCount; - let rowElements = []; - - for(let systemToData of systemsToData){ - if(systemFromData.name !== systemToData.name){ - // check for cached rowData - let cacheKey = getRouteDataCacheKey([mapId], systemFromData.name, systemToData.name); - let rowData = getCacheData('systemRoutes', cacheKey); - if(rowData){ - // route data is cached (client side) - let context = { - dataTable: routesTable - }; - - rowElements.push( addRow(context, rowData) ); - }else{ - // get route data -> ajax - let searchData = { - mapIds: [mapId], - systemFromData: systemFromData, - systemToData: systemToData, - skipSearch: requestRouteData.length >= defaultRoutesCount - }; - - requestRouteData.push( getRouteRequestDataFromRowData( searchData )); - } - } - } - - // rows added from cache -> redraw() table - if(rowElements.length){ - routesTable.draw(); - } - - // check if routes data is not cached and is requested - if(requestRouteData.length > 0){ - let contextData = { - moduleElement: moduleElement, - dataTable: routesTable - }; - - let requestData = { - routeData: requestRouteData - }; - - getRouteData(requestData, contextData, callbackAddRouteRows); - } - }; - - /** - * show route settings dialog - * @param dialogData - * @param moduleElement - * @param systemFromData - * @param routesTable - */ - let showSettingsDialog = (dialogData, moduleElement, systemFromData, routesTable) => { - - let promiseStore = MapUtil.getLocaleData('map', dialogData.mapId); - promiseStore.then(dataStore => { - // selected systems (if already stored) - let systemSelectOptions = []; - if( - dataStore && - dataStore.routes - ){ - systemSelectOptions = dataStore.routes; - } - - // max count of "default" target systems - let maxSelectionLength = Init.routeSearch.maxDefaultCount; - - let data = { - id: config.routeSettingsDialogId, - selectClass: config.systemDialogSelectClass, - systemSelectOptions: systemSelectOptions, - maxSelectionLength: maxSelectionLength - }; - - requirejs(['text!templates/dialog/route_settings.html', 'mustache'], (template, Mustache) => { - let content = Mustache.render(template, data); - - let settingsDialog = bootbox.dialog({ - title: 'Route settings', - message: content, - show: false, - buttons: { - close: { - label: 'cancel', - className: 'btn-default' - }, - success: { - label: ' save', - className: 'btn-success', - callback: function () { - let form = this.find('form'); - // get all system data from select2 - let systemSelectData = form.find('.' + config.systemDialogSelectClass).select2('data'); - let systemsTo = []; - - if( systemSelectData.length > 0 ){ - systemsTo = formSystemSelectData(systemSelectData); - MapUtil.storeLocalData('map', dialogData.mapId, 'routes', systemsTo); - }else{ - MapUtil.deleteLocalData('map', dialogData.mapId, 'routes'); - } - - Util.showNotify({title: 'Route settings stored', type: 'success'}); - - // (re) draw table - drawRouteTable(dialogData.mapId, moduleElement, systemFromData, routesTable, systemsTo); - } - } - } - }); - - settingsDialog.on('shown.bs.modal', function(e) { - - // init default system select ----------------------------------------------------- - // -> add some delay until modal transition has finished - let systemTargetSelect = $(this).find('.' + config.systemDialogSelectClass); - systemTargetSelect.delay(240).initSystemSelect({key: 'id', maxSelectionLength: maxSelectionLength}); - }); - - // show dialog - settingsDialog.modal('show'); - }); - }); - }; - - /** - * format select2 system data - * @param {Array} data - * @returns {Array} - */ - let formSystemSelectData = (data) => { - let formattedData = []; - for(let i = 0; i < data.length; i++){ - let tmpData = data[i]; - - formattedData.push({ - systemId: parseInt(tmpData.id), - name: tmpData.text - }); - } - - return formattedData; - }; - - /** - * set event observer for route finder dialog - * @param routeDialog - */ - let setDialogObserver = (routeDialog) => { - let wormholeCheckbox = routeDialog.find('input[type="checkbox"][name="wormholes"]'); - let wormholeReducedCheckbox = routeDialog.find('input[type="checkbox"][name="wormholesReduced"]'); - let wormholeCriticalCheckbox = routeDialog.find('input[type="checkbox"][name="wormholesCritical"]'); - let wormholeFrigateCheckbox = routeDialog.find('input[type="checkbox"][name="wormholesFrigate"]'); - let wormholeEolCheckbox = routeDialog.find('input[type="checkbox"][name="wormholesEOL"]'); - - // store current "checked" state for each box --------------------------------------------- - let storeCheckboxStatus = function(){ - wormholeReducedCheckbox.data('selectState', wormholeReducedCheckbox.prop('checked')); - wormholeCriticalCheckbox.data('selectState', wormholeCriticalCheckbox.prop('checked')); - wormholeFrigateCheckbox.data('selectState', wormholeFrigateCheckbox.prop('checked')); - wormholeEolCheckbox.data('selectState', wormholeEolCheckbox.prop('checked')); - }; - - // on wormhole checkbox change ------------------------------------------------------------ - let onWormholeCheckboxChange = function(){ - - if( $(this).is(':checked') ){ - wormholeReducedCheckbox.prop('disabled', false); - wormholeCriticalCheckbox.prop('disabled', false); - wormholeFrigateCheckbox.prop('disabled', false); - wormholeEolCheckbox.prop('disabled', false); - - wormholeReducedCheckbox.prop('checked', wormholeReducedCheckbox.data('selectState')); - wormholeCriticalCheckbox.prop('checked', wormholeCriticalCheckbox.data('selectState')); - wormholeFrigateCheckbox.prop('checked', wormholeFrigateCheckbox.data('selectState')); - wormholeEolCheckbox.prop('checked', wormholeEolCheckbox.data('selectState')); - }else{ - storeCheckboxStatus(); - - wormholeReducedCheckbox.prop('checked', false); - wormholeReducedCheckbox.prop('disabled', true); - wormholeCriticalCheckbox.prop('checked', false); - wormholeCriticalCheckbox.prop('disabled', true); - wormholeFrigateCheckbox.prop('checked', false); - wormholeFrigateCheckbox.prop('disabled', true); - wormholeEolCheckbox.prop('checked', false); - wormholeEolCheckbox.prop('disabled', true); - } - }.bind(wormholeCheckbox); - - wormholeCheckbox.on('change', onWormholeCheckboxChange); - - // initial checkbox check - storeCheckboxStatus(); - onWormholeCheckboxChange(); - }; - - /** - * get a connectionsData object that holds all connections for given mapIds (used as cache for route search) - * @param mapIds - * @returns {{}} - */ - let getConnectionsDataFromMaps = (mapIds) => { - let connectionsData = {}; - for(let mapId of mapIds) { - let map = MapUtil.getMapInstance(mapId); - if(map){ - let cacheKey = 'map_' + mapId; - let mapConnectionsData = getCacheData('mapConnections', cacheKey); - - if(!mapConnectionsData){ - mapConnectionsData = {}; - let connections = map.getAllConnections(); - if(connections.length){ - let connectionsData = MapUtil.getDataByConnections(connections); - for(let connectionData of connectionsData){ - let connectionDataCacheKey = getConnectionDataCacheKey(connectionData.sourceName, connectionData.targetName); - - // skip double connections between same systems - if( !mapConnectionsData.hasOwnProperty(connectionDataCacheKey) ){ - mapConnectionsData[connectionDataCacheKey] = { - map: { - id: mapId - }, - connection: { - id: connectionData.id, - type: connectionData.type, - scope: connectionData.scope - }, - source: { - id: connectionData.source, - name: connectionData.sourceName, - alias: connectionData.sourceAlias - }, - target: { - id: connectionData.target, - name: connectionData.targetName, - alias: connectionData.targetAlias - } - }; - } - } - } - - // update cache - setCacheData('mapConnections', cacheKey, mapConnectionsData); - } - - if(connectionsData !== null){ - connectionsData = Object.assign({}, mapConnectionsData, connectionsData); - } - } - } - - return connectionsData; - }; - - /** - * search for a specific connection by "source"/"target"-name inside connectionsData cache - * @param connectionsData - * @param sourceName - * @param targetName - * @returns {{}} - */ - let findConnectionsData = (connectionsData, sourceName, targetName) => { - let connectionDataCacheKey = getConnectionDataCacheKey(sourceName, targetName); - return connectionsData.hasOwnProperty(connectionDataCacheKey) ? - connectionsData[connectionDataCacheKey] : {}; - }; - - /** - * get stargate connection data (default connection type in case connection was not found on a map) - * @param sourceRouteNodeData - * @param targetRouteNodeData - * @returns {{connection: {id: number, type: string[], scope: string}, source: {id: number, name, alias}, target: {id: number, name, alias}}} - */ - let getStargateConnectionData = (sourceRouteNodeData, targetRouteNodeData) => { - return { - connection: { - id: 0, - type: ['stargate'], - scope: 'stargate' - }, - source: { - id: 0, - name: sourceRouteNodeData.system, - alias: sourceRouteNodeData.system - }, - target: { - id: 0, - name: targetRouteNodeData.system, - alias: targetRouteNodeData.system - } - }; - }; - - /** - * get fake connection Element - * @param connectionData - * @returns {string} - */ - let getFakeConnectionElement = (connectionData) => { - let mapId = Util.getObjVal(connectionData, 'map.id') | 0; - let connectionId = Util.getObjVal(connectionData, 'connection.id') | 0; - let scope = Util.getObjVal(connectionData, 'connection.scope'); - let classes = MapUtil.getConnectionFakeClassesByTypes(connectionData.connection.type); - let disabled = !mapId || !connectionId; - - let connectionElement = '
'; - return connectionElement; - }; - - /** - * format route data from API request into dataTable row format - * @param routeData - * @returns {{}} - */ - let formatRouteData = (routeData) => { - - /** - * get status icon for route - * @param status - * @returns {string} - */ - let getStatusIcon= (status) => { - let color = 'txt-color-danger'; - let title = 'route not found'; - switch(status){ - case 1: - color = 'txt-color-success'; - title = 'route exists'; - break; - case 2: - color = 'txt-color-warning'; - title = 'not search performed'; - break; - } - - return ''; - }; - - // route status: - // 0: not found - // 1: round (OK) - // 2: not searched - let routeStatus = routeData.skipSearch ? 2 : 0; - - // button class for flag (e.g. "secure" routes) - let flagButtonClass = routeData.flag === 'secure' ? 'txt-color-success' : ''; - - let connectionButton = ''; - let flagButton = ''; - let reloadButton = ''; - let searchButton = ''; - let deleteButton = ''; - - // default row data (e.g. no route found) - let tableRowData = { - systemFromData: routeData.systemFromData, - systemToData: routeData.systemToData, - jumps: { - value: 9999, // for sorting - formatted: '' - }, - avgTrueSec: { - value: '', - formatted: '' - }, - route: { - value: routeStatus === 2 ? 'search now' : 'not found', - data: routeData.route - }, - stargates: routeData.stargates, - jumpbridges: routeData.jumpbridges, - wormholes: routeData.wormholes, - wormholesReduced: routeData.wormholesReduced, - wormholesCritical: routeData.wormholesCritical, - wormholesFrigate: routeData.wormholesFrigate, - wormholesEOL: routeData.wormholesEOL, - connections: { - value: 0, - button: connectionButton - }, - flag: { - value: routeData.flag, - button: flagButton - }, - reload: { - button: routeData.skipSearch ? searchButton : reloadButton - }, - clear: { - button: deleteButton - }, - maps: routeData.maps, - mapIds: routeData.mapIds //map data (mapIds is "redundant") - }; - - if( - routeData.routePossible === true && - routeData.route.length > 0 - ){ - // route data available - routeStatus = 1; - - // add route Data - let routeJumpElements = []; - let avgSecTemp = 0; - - let connectionsData = getConnectionsDataFromMaps(routeData.mapIds); - let prevRouteNodeData = null; - // loop all systems on this route - for(let i = 0; i < routeData.route.length; i++){ - let routeNodeData = routeData.route[i]; - let systemName = routeNodeData.system; - - // fake connection elements between systems ----------------------------------------------------------- - if(prevRouteNodeData){ - let connectionData = findConnectionsData(connectionsData, prevRouteNodeData.system, systemName); - if(!connectionData.hasOwnProperty('connection')){ - connectionData = getStargateConnectionData(prevRouteNodeData, routeNodeData); - } - let connectionElement = getFakeConnectionElement(connectionData); - - routeJumpElements.push( connectionElement ); - } - - // system elements ------------------------------------------------------------------------------------ - let systemSec = Number(routeNodeData.security).toFixed(1).toString(); - let tempSystemSec = systemSec; - - if(tempSystemSec <= 0){ - tempSystemSec = '0-0'; - } - - let systemSecClass = config.systemSecurityClassPrefix + tempSystemSec.replace('.', '-'); - - // check for wormhole - let icon = 'fas fa-square'; - if( /^J\d+$/.test(systemName) ){ - icon = 'far fa-dot-circle'; - } - - let system = ''; - routeJumpElements.push( system ); - - // "source" system is not relevant for average security - if(i > 0){ - avgSecTemp += Number(routeNodeData.security); - } - - prevRouteNodeData = routeNodeData; - } - - let avgSec = ( avgSecTemp / (routeData.route.length - 1)).toFixed(2); - let avgSecForClass = Number(avgSec).toFixed(1); - - if(avgSecForClass <= 0){ - avgSecForClass = '0.0'; - } - - let avgSecClass = config.systemSecurityClassPrefix + avgSecForClass.toString().replace('.', '-'); - - tableRowData.jumps = { - value: routeData.routeJumps, - formatted: routeData.routeJumps - }; - - tableRowData.avgTrueSec = { - value: avgSec, - formatted: '' + avgSec + '' - }; - - tableRowData.route.value = routeJumpElements.join(' '); - } - - // route status data ---------------------------------------------------------------------- - tableRowData.status = { - value: routeStatus, - formatted: getStatusIcon(routeStatus) - }; - - return tableRowData; - }; - - /** - * get module element - * @param parentElement - * @param mapId - * @param systemData - * @returns {jQuery} - */ - let getModule = (parentElement, mapId, systemData) => { - - // create new module container - let moduleElement = $('
').append( - $('
', { - class: config.moduleHeadClass - }).append( - $('
', { - class: config.moduleHandlerClass - }), - $('
', { - class: 'pull-right' - }).append( - $('', { - class: ['fas', 'fa-fw', 'fa-sliders-h', config.moduleHeadlineIconClass, config.moduleHeadlineIconSettingsClass].join(' '), - title: 'settings' - }).attr('data-html', 'true').attr('data-toggle', 'tooltip'), - $('', { - class: ['fas', 'fa-fw', 'fa-search', config.moduleHeadlineIconClass, config.moduleHeadlineIconSearchClass].join(' '), - title: 'find route' - }).attr('data-html', 'true').attr('data-toggle', 'tooltip'), - $('', { - class: ['fas', 'fa-fw', 'fa-sync', config.moduleHeadlineIconClass, config.moduleHeadlineIconRefreshClass].join(' '), - title: 'refresh all' - }).attr('data-html', 'true').attr('data-toggle', 'tooltip') - ), - $('
', { - text: 'Routes' - }) - ) - ); - - // save systemFromData to module (data never changes during module lifetime) - // -> we need this later in updateModule() - let systemFromData = { - systemId: systemData.systemId, - name: systemData.name, - }; - - moduleElement.data('systemFromData', systemFromData); - - let table = $('
' + - '' + - '' + group.name + '
', { - class: ['compact', 'stripe', 'order-column', 'row-border', config.systemInfoRoutesTableClass].join(' ') - }); - moduleElement.append(table); - - // init empty table - let routesTable = table.DataTable( { - paging: false, - ordering: true, - order: [[ 2, 'asc' ], [ 0, 'asc' ]], - info: false, - searching: false, - hover: false, - autoWidth: false, - rowId: 'systemTo', - language: { - emptyTable: 'No routes added' - }, - columnDefs: [ - { - targets: 0, - orderable: true, - title: '', - width: 2, - class: ['text-center'].join(' '), - data: 'status', - render: { - _: 'formatted', - sort: 'value' - } - },{ - targets: 1, - orderable: true, - title: 'system   ', - class: Util.config.popoverTriggerClass, - data: 'systemToData', - render: { - _: 'name', - sort: 'name' - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex) { - // init context menu - $(cell).initSystemPopover({ - systemToData: rowData.systemToData - }); - - $(cell).toggleClass(config.rallyClass, cellData.hasOwnProperty('rally')); - } - },{ - targets: 2, - orderable: true, - title: '  ', - width: 16, - class: 'text-right', - data: 'jumps', - render: { - _: 'formatted', - sort: 'value' - } - },{ - targets: 3, - orderable: true, - title: 'Ø  ', - width: 14, - class: 'text-right', - data: 'avgTrueSec', - render: { - _: 'formatted', - sort: 'value' - } - },{ - targets: 4, - orderable: false, - title: 'route', - class: [config.dataTableRouteCellClass].join(' '), - data: 'route', - render: { - _: 'value' - } - },{ - targets: 5, - title: '', - orderable: false, - searchable: false, - width: 10, - class: ['text-center', config.dataTableActionCellClass].join(' '), - data: 'connections', - render: { - _: 'button' - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex) { - let tempTableApi = this.api(); - - $(cell).on('click', function(e) { - let routeCellElement = tempTableApi.cell( rowIndex, 4 ).nodes().to$(); - - if(routeCellElement.hasClass(config.dataTableJumpCellClass)){ - routeCellElement.toggleClass(config.dataTableJumpCellClass, false); - $(this).find('i').toggleClass('txt-color-orange', false); - }else{ - routeCellElement.toggleClass(config.dataTableJumpCellClass, true); - $(this).find('i').toggleClass('txt-color-orange', true); - } - }); - } - },{ - targets: 6, - title: '', - orderable: false, - searchable: false, - width: 10, - class: ['text-center', config.dataTableActionCellClass].join(' '), - data: 'flag', - render: { - _: 'button' - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex) { - let tempTableApi = this.api(); - - $(cell).on('click', function(e) { - // get current row data (important!) - // -> "rowData" param is not current state, values are "on createCell()" state - rowData = tempTableApi.row( $(cell).parents('tr')).data(); - let routeData = getRouteRequestDataFromRowData( rowData ); - - // overwrite some params - routeData.skipSearch = 0; - routeData.flag = routeData.flag === 'shortest' ? 'secure' : 'shortest'; // toggle - - let context = { - moduleElement: moduleElement, - dataTable: tempTableApi - }; - - let requestData = { - routeData: [routeData] - }; - - getRouteData(requestData, context, callbackAddRouteRows); - }); - } - },{ - targets: 7, - title: '', - orderable: false, - searchable: false, - width: 10, - class: ['text-center', config.dataTableActionCellClass].join(' '), - data: 'reload', - render: { - _: 'button' - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let tempTableApi = this.api(); - - $(cell).on('click', function(e) { - // get current row data (important!) - // -> "rowData" param is not current state, values are "on createCell()" state - rowData = tempTableApi.row( $(cell).parents('tr')).data(); - let routeData = getRouteRequestDataFromRowData( rowData ); - - // overwrite some params - routeData.skipSearch = 0; - - let context = { - moduleElement: moduleElement, - dataTable: tempTableApi - }; - - let requestData = { - routeData: [routeData] - }; - - getRouteData(requestData, context, callbackAddRouteRows); - }); - } - },{ - targets: 8, - title: '', - orderable: false, - searchable: false, - width: 10, - class: ['text-center', config.dataTableActionCellClass].join(' '), - data: 'clear', - render: { - _: 'button' - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let tempTableElement = this; - - let confirmationSettings = { - container: 'body', - placement: 'left', - btnCancelClass: 'btn btn-sm btn-default', - btnCancelLabel: 'cancel', - btnCancelIcon: 'fas fa-fw fa-ban', - title: 'delete route', - btnOkClass: 'btn btn-sm btn-danger', - btnOkLabel: 'delete', - btnOkIcon: 'fas fa-fw fa-times', - onConfirm : function(e, target){ - let deleteRowElement = $(cell).parents('tr'); - tempTableElement.api().rows(deleteRowElement).remove().draw(); - } - }; - - // init confirmation dialog - $(cell).confirmation(confirmationSettings); - } - } - ], - drawCallback: function(settings){ - - let animationRows = this.api().rows().nodes().to$().filter(function() { - return ( - $(this).data('animationStatus') || - $(this).data('animationTimer') - ); - }); - - for(let i = 0; i < animationRows.length; i++){ - let animationRow = $(animationRows[i]); - animationRow.pulseTableRow(animationRow.data('animationStatus')); - animationRow.removeData('animationStatus'); - } - }, - initComplete: function(settings, json){ - // click on "fake connection" ------------------------------------------------------------------------- - $(this).on('click', '.pf-fake-connection', function(){ - let fakeConnectionElement = $(this); - let mapId = fakeConnectionElement.attr('data-mapId'); - let connectionId = fakeConnectionElement.attr('data-connectionId'); - let connection = $().getConnectionById(mapId, connectionId); - - if(connection){ - let map = connection._jsPlumb.instance; - MapUtil.showConnectionInfo(map, [connection]); - } - }); - }, - data: [] // will be added dynamic - }); - - // init tooltips for this module - let tooltipElements = moduleElement.find('[data-toggle="tooltip"]'); - tooltipElements.tooltip({ - container: 'body' - }); - - return moduleElement; - }; - - - /** - * init system popover (e.g. for setWaypoints) - * @param options - */ - $.fn.initSystemPopover = function(options){ - let elements = $(this); - let eventNamespace = 'hideSystemPopup'; - let systemToData = options.systemToData; - - requirejs(['text!templates/tooltip/system_popover.html', 'mustache'], function (template, Mustache) { - let data = { - systemToData: systemToData - }; - - let content = Mustache.render(template, data); - - elements.each(function() { - let element = $(this); - // destroy "popover" and remove "click" event for animation - element.popover('destroy').off(); - - // init popover and add specific class to it (for styling) - element.popover({ - html: true, - title: systemToData.name, - trigger: 'manual', - placement: 'top', - container: 'body', - content: content - }).data('bs.popover').tip().addClass('pf-popover'); - }); - - // set popup "close" observer - elements.initPopoverClose(eventNamespace); - - // set "open" trigger on "right click" - // -> this is not supported by the "trigger" param in .popover(); - // -> therefore we need to set it up manually - elements.on('contextmenu', function(e){ - e.preventDefault(); - $(this).popover('show'); - }); - - // set link observer "on shown" event - elements.on('shown.bs.popover', function () { - let popoverRoot = $(this); - - popoverRoot.data('bs.popover').tip().find('a').on('click', function(){ - // hint: "data" attributes should be in lower case! - let systemData = { - systemId: $(this).data('systemid'), - name: $(this).data('name') - }; - Util.setDestination(systemData, 'set_destination'); - - // close popover - popoverRoot.popover('hide'); - }); - }); - }); - }; - - /** - * get data from all Rally Point systems - * @param mapId - * @returns {Array} - */ - let getRallySystemsData = (mapId) => { - let systemsRallyData = []; - let map = MapUtil.getMapInstance(mapId); - if(map){ - let mapContainer = $(map.getContainer()); - let systems = mapContainer.find('.pf-system-info-rally'); - - for(let system of systems){ - system = $(system); - systemsRallyData.push({ - systemId: system.data('systemId'), - name: system.data('name'), - rally: 1 - }); - } - } - - return systemsRallyData; - }; - - /** - * update trigger function for this module - * @param moduleElement - * @param data - */ - let updateModule = (moduleElement, data) => { - let routesTableElement = moduleElement.find('.' + config.systemInfoRoutesTableClass); - let routesTable = routesTableElement.DataTable(); - - switch(data.task){ - case 'showFindRouteDialog': - let dialogData = { - moduleElement: moduleElement, - mapId: data.mapId, - systemFromData: moduleElement.data('systemFromData'), - systemToData: data.systemToData, - dataTable: routesTable - }; - - showFindRouteDialog(dialogData); - break; - case 'findRoute': - drawRouteTable(data.mapId, moduleElement, moduleElement.data('systemFromData'), routesTable, [data.systemToData]); - break; - } - }; - - /** - * init route module - * -> request route path fore "default" trade hub systems - * @param moduleElement - * @param mapId - * @param systemData - */ - let initModule = (moduleElement, mapId, systemData) => { - - let systemFromData = { - systemId: systemData.systemId, - name: systemData.name - }; - - let routesTableElement = moduleElement.find('.' + config.systemInfoRoutesTableClass); - let routesTable = routesTableElement.DataTable(); - - // init refresh routes -------------------------------------------------------------------- - moduleElement.find('.' + config.moduleHeadlineIconRefreshClass).on('click', function(e){ - updateRoutesTable(moduleElement, routesTable); - }); - - // init search routes dialog -------------------------------------------------------------- - moduleElement.find('.' + config.moduleHeadlineIconSearchClass).on('click', function(e){ - let maxRouteSearchLimit = this.Init.routeSearch.limit; - - if(routesTable.rows().count() >= maxRouteSearchLimit){ - // max routes limit reached -> show warning - Util.showNotify({title: 'Route limit reached', text: 'Search is limited by ' + maxRouteSearchLimit, type: 'warning'}); - }else{ - let dialogData = { - moduleElement: moduleElement, - mapId: mapId, - systemFromData: systemFromData, - dataTable: routesTable - }; - - showFindRouteDialog(dialogData); - } - }.bind({ - Init: Init - })); - - // init settings dialog ------------------------------------------------------------------- - moduleElement.find('.' + config.moduleHeadlineIconSettingsClass).on('click', function(e){ - let dialogData = { - mapId: mapId - }; - - showSettingsDialog(dialogData, moduleElement, systemFromData, routesTable); - }); - - // fill routesTable with data ------------------------------------------------------------- - let promiseStore = MapUtil.getLocaleData('map', mapId); - promiseStore.then(function(dataStore) { - // selected systems (if already stored) - let systemsTo = [{ - systemId: 30000142, - name: 'Jita' - }]; - - if( - dataStore && - dataStore.routes - ){ - systemsTo = dataStore.routes; - } - - // add "Rally Point" systems to table - let systemsToData = getRallySystemsData(mapId); - systemsToData.push(...systemsTo); - - drawRouteTable(mapId, moduleElement, systemFromData, routesTable, systemsToData); - }); - - }; - - return { - config: config, - getModule: getModule, - initModule: initModule, - updateModule: updateModule - }; - -}); \ No newline at end of file diff --git a/public/js/v1.4.1/app/ui/system_signature.js b/public/js/v1.4.1/app/ui/system_signature.js deleted file mode 100644 index a52058f1..00000000 --- a/public/js/v1.4.1/app/ui/system_signature.js +++ /dev/null @@ -1,2513 +0,0 @@ -/** - * System signature module - */ - -define([ - 'jquery', - 'app/init', - 'app/util', - 'mustache', - 'bootbox', - 'app/map/map', - 'app/map/util', - 'app/ui/form_element' -], ($, Init, Util, Mustache, bootbox, Map, MapUtil, FormElement) => { - 'use strict'; - - let config = { - // module info - modulePosition: 4, - moduleName: 'systemSignature', - moduleHeadClass: 'pf-module-head', // class for module header - moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler - - moduleClass: 'pf-module', // class for each module - - // system signature module - moduleTypeClass: 'pf-signature-table-module', // module wrapper - - // headline toolbar - moduleHeadlineIconClass: 'pf-module-icon-button', // class for toolbar icons in the head - moduleHeadlineIconAddClass: 'pf-module-icon-button-add', // class for "add signature" icon - moduleHeadlineIconReaderClass: 'pf-module-icon-button-reader', // class for "signature reader" icon - moduleHeadlineIconLazyClass: 'pf-module-icon-button-lazy', // class for "lazy delete" toggle icon - - // tables - tableToolsActionClass: 'pf-table-tools-action', // class for table toolbar action - - // signature progress bar - signatureScannedProgressBarClass: 'pf-system-progress-scanned', // class for signature progress bar - - // table toolbar - sigTableClearButtonClass: 'pf-sig-table-clear-button', // class for "clear" signatures button - - // signature table - sigTableClass: 'pf-sig-table', // Table class for all Signature Tables - sigTablePrimaryClass: 'pf-sig-table-primary', // class for primary sig table - sigTableSecondaryClass: 'pf-sig-table-secondary', // class for secondary sig table - sigTableEditSigNameInput: 'pf-sig-table-edit-name-input', // class for editable fields (input) - sigTableEditSigGroupSelect: 'pf-sig-table-edit-group-select', // class for editable fields (sig group) - sigTableEditSigTypeSelect: 'pf-sig-table-edit-type-select', // class for editable fields (sig type) - sigTableEditSigConnectionSelect: 'pf-sig-table-edit-connection-select', // class for editable fields (sig connection) - sigTableEditSigDescriptionTextarea: 'pf-sig-table-edit-desc-text', // class for editable fields (sig description) - sigTableCreatedCellClass: 'pf-sig-table-created', // class for "created" cells - sigTableUpdatedCellClass: 'pf-sig-table-updated', // class for "updated" cells - - sigTableConnectionClass: 'pf-table-connection-cell', // class for "connection" cells - sigTableCounterClass: 'pf-table-counter-cell', // class for "counter" cells - sigTableActionCellClass: 'pf-table-action-cell', // class for "action" cells - - // xEditable - editableDescriptionInputClass: 'pf-editable-description', // class for "description" textarea - editableUnknownInputClass: 'pf-editable-unknown', // class for input fields (e.g. checkboxes) with "unknown" status - - signatureGroupsLabels: Util.getSignatureGroupInfo('label'), - signatureGroupsNames: Util.getSignatureGroupInfo('name') - }; - - // lock Signature Table update temporary (until. some requests/animations) are finished - let disableTableUpdate = true; - - // disable "copy&paste" from clipboard (until request finished) - let disableCopyFromClipboard = false; - - // cache for dataTable object6 - let dataTableInstances = {}; - - // empty signatureData object -> for "add row" table - let emptySignatureData = { - id: 0, - name: '', - groupId: 0, - typeId: 0, - description: '', - created: { - created: null - }, - updated: { - updated: null - } - }; - - let fullSignatureOptions = { - action: 'delete', - actionClass: ['fa-times', 'txt-color', 'txt-color-redDarker'].join(' ') - }; - - // empty signatureData row Options - let emptySignatureOptions = { - action: 'add', - actionClass: ['fa-plus'].join(' ') - }; - - let sigNameCache = {}; // cache signature names - - let validSignatureNames = [ // allowed signature type/names - 'Cosmic Anomaly', - 'Cosmic Signature', - 'Kosmische Anomalie', - 'Kosmische Signatur', - 'Anomalie cosmique', - 'Signature cosmique', - 'Космическая аномалия', // == "Cosmic Anomaly" - 'Источники сигналов' // == "Cosmic Signature" - ]; - - // some static signature data - let signatureGroupsLabels = Util.getSignatureGroupInfo('label'); - let signatureGroupsNames = Util.getSignatureGroupInfo('name'); - - /** - * get module toolbar element - * @returns {jQuery} - */ - let getHeadlineToolbar = () => { - let headlineToolbar = $('
', { - class: 'pull-right' - }).append( - $('', { - class: 'progress-label-right', - text: '0%' - }), - $('', { - class: ['fas', 'fa-fw', 'fa-plus', - config.moduleHeadlineIconClass, - config.moduleHeadlineIconAddClass].join(' '), - title: 'add' - }).attr('data-toggle', 'tooltip'), - $('', { - class: ['fas', 'fa-fw', 'fa-paste', - config.moduleHeadlineIconClass, - config.moduleHeadlineIconReaderClass].join(' '), - title: 'signature reader' - }).attr('data-toggle', 'tooltip'), - $('', { - class: ['fas', 'fa-fw', 'fa-exchange-alt', - config.moduleHeadlineIconClass, - config.moduleHeadlineIconLazyClass].join(' '), - title: 'lazy \'delete\' signatures' - }).attr('data-toggle', 'tooltip') - ); - - headlineToolbar.find('[data-toggle="tooltip"]').tooltip({ - container: 'body' - }); - - return headlineToolbar; - }; - - /** - * check whether a dataTable API instance exists in the global cache - * args: 1. mapId, 2. systemId, 3, tableType (primary/secondary) string - * @param args - * @returns {boolean} - */ - let checkDataTableInstance = (...args) => { - let obj = dataTableInstances; - for(let arg of args){ - if ( !obj || !obj.hasOwnProperty(arg) ){ - return false; - } - obj = obj[arg]; - } - return true; - }; - - /** - * stores a dataTableApi instance to global cache ( overwrites existing) - * @param mapId - * @param systemId - * @param tableType - * @param instance - */ - let setDataTableInstance = (mapId, systemId, tableType, instance) => { - let tmpObj = { - [mapId]: { - [systemId]: { - [tableType]: instance - } - } - }; - - $.extend(true, dataTableInstances, tmpObj); - }; - - /** - * get a dataTableApi instance from global cache - * @param mapId - * @param systemId - * @param tableType - * @returns {*} - */ - let getDataTableInstance = (mapId, systemId, tableType) => { - let instance = null; - if( checkDataTableInstance(mapId, systemId, tableType) ){ - instance = dataTableInstances[mapId][systemId][tableType]; - } - return instance; - }; - - /** - * get dataTable instance from "moduleElement" (DOM node) - * @param moduleElement - * @param tableType - * @returns {*} - */ - let getDataTableInstanceByModuleElement = (moduleElement, tableType) => { - return getDataTableInstance(moduleElement.data('mapId'), moduleElement.data('systemId'), tableType); - }; - - /** - * delete a dataTableApi instance from global cache - * -> see checkDataTableInstance() for parameter order - * @param args - */ - let deleteDataTableInstance = (...args) => { - // check if instance exists - if( checkDataTableInstance.apply(null, args) ){ - - // callback for "leaf" delete callback - let deleteCallback = (dataTableApi) => { - dataTableApi.destroy(); - }; - - // recursive delete from dataTableInstances Object cache - let deepDelete = (target, obj, callback) => { - if(target.length > 1){ - // remove first target param for next recursive call - let currentTarget = target.shift(); - - deepDelete(target, obj[currentTarget], callback); - - // delete "parent" key when current key became empty - if( !Object.keys( obj[currentTarget] ).length ){ - delete obj[currentTarget]; - } - }else{ - // check for callback function - if( typeof callback === 'function' ){ - callback(obj[target]); - } - - delete obj[target]; - } - }; - - deepDelete(args, dataTableInstances, deleteCallback); - } - }; - - /** - * collect all data of all editable fields in a signature table - * @param tableApi - * @returns {Array} - */ - let getTableData = tableApi => { - let tableData = []; - - if(tableApi){ - tableApi.rows().eq(0).each(function(idx){ - let row = tableApi.row(idx); - // default row data - let defaultRowData = row.data(); - let rowElement = row.nodes().to$(); - - if(defaultRowData.id > 0){ - // get all editable fields per row - let editableFields = rowElement.find('.editable'); - - if(editableFields.length > 0){ - let values = $(editableFields).editable('getValue'); - - if(values.name){ - // convert to lower for better compare options - values.name = values.name.toLowerCase(); - - // add pk for this row - values.id = defaultRowData.id; - - // add updated for this row - values.updated = defaultRowData.updated; - - // add row index - values.index = idx; - - tableData.push( values ); - } - } - } - }); - } - - return tableData; - }; - - /** - * updates a single cell with new data (e.g. "updated" cell) - * @param tableApi - * @param rowElement - * @param cellIndex - * @param data - */ - let updateSignatureCell = (tableApi, rowElement, cellIndex, data) => { - let rowIndex = tableApi.row(rowElement).index(); - let updateCell = tableApi.cell( rowIndex, cellIndex ); - let updateCellElement = updateCell.nodes().to$(); - - if(cellIndex === 7){ - // clear existing counter interval - clearInterval( updateCellElement.data('interval') ); - } - - // set new value - updateCell.data(data).draw(); - - if(cellIndex === 7){ - updateCellElement.initTimestampCounter(); - } - }; - - /** - * update trigger function for this module - * compare data and update module - * @param moduleElement - * @param systemData - */ - let updateModule = (moduleElement, systemData) => { - - if(systemData.signatures){ - updateSignatureTable(moduleElement, systemData.signatures, true); - } - - moduleElement.hideLoadingAnimation(); - }; - - /** - * Updates a signature table, changes all signatures where name matches - * add all new signatures as a row - * @param moduleElement - * @param signatureDataOrig - * @param deleteOutdatedSignatures -> set to "true" if signatures should be deleted that are not included in "signatureData" - */ - let updateSignatureTable = (moduleElement, signatureDataOrig, deleteOutdatedSignatures) => { - // check if table update is allowed - if(disableTableUpdate === true){ - return; - } - - // clone signature array because of further manipulation - let signatureData = $.extend([], signatureDataOrig); - - // disable update until function is ready; - lockSignatureTable(); - - // get signature table API - let signatureTableApi = getDataTableInstanceByModuleElement(moduleElement, 'primary'); - - // get current system data - let currentSystemData = Util.getCurrentSystemData(); - - let tableData = getTableData(signatureTableApi); - - let notificationCounter = { - added: 0, - changed: 0, - deleted: 0 - }; - - for(let i = 0; i < signatureData.length; i++){ - for(let j = 0; j < tableData.length; j++){ - if(signatureData[i].id === tableData[j].id){ - - // check if row was updated - if(signatureData[i].updated.updated > tableData[j].updated.updated){ - // row element to remove - let currentRowElement = signatureTableApi.row(tableData[j].index).nodes().to$(); - - // hide open editable fields on the row before removing them - currentRowElement.find('.editable').editable('destroy'); - - // remove "old" row - signatureTableApi.row(currentRowElement).remove().draw(); - - // and add "new" row - let changedRowElement = addSignatureRow(signatureTableApi, currentSystemData.systemData, signatureData[i], false); - - if(changedRowElement){ - // highlight - changedRowElement.pulseTableRow('changed'); - notificationCounter.changed++; - } - } - - // remove signature data -> all left signatures will be added - signatureData.splice(i, 1); - i--; - - // remove signature data -> all left signatures will be deleted - tableData.splice(j, 1); - j--; - - break; - } - } - } - - // delete signatures ------------------------------------------------------------------------------------------ - if(deleteOutdatedSignatures === true){ - - // callback function after row deleted - let toggleTableRowCallback = function(tempRowElement){ - // hide open editable fields on the row before removing them - tempRowElement.find('.editable').editable('destroy'); - - // delete signature row - signatureTableApi.row(tempRowElement).remove().draw(); - }; - - for(let l = 0; l < tableData.length; l++){ - let rowElement = signatureTableApi.row(tableData[l].index).nodes().to$(); - rowElement.toggleTableRow(toggleTableRowCallback); - notificationCounter.deleted++; - } - } - - // add new signatures ----------------------------------------------------------------------------------------- - for(let k = 0; k < signatureData.length; k++){ - // and add "new" row - let newRowElement = addSignatureRow(signatureTableApi, currentSystemData.systemData, signatureData[k], false); - - if(newRowElement){ - // highlight - newRowElement.pulseTableRow('added'); - notificationCounter.added++; - } - } - - // show notification ------------------------------------------------------------------------------------------ - if( - notificationCounter.added > 0 || - notificationCounter.changed > 0 || - notificationCounter.deleted > 0 - ){ - // update signature bar - moduleElement.updateScannedSignaturesBar({showNotice: true}); - - let notification = notificationCounter.added + ' added
'; - notification += notificationCounter.changed + ' updated
'; - notification += notificationCounter.deleted + ' deleted
'; - Util.showNotify({title: 'Signatures updated', text: notification, type: 'success'}); - - // wait until add/remove animations are finished before enable table for auto update again - unlockSignatureTable(false); - }else{ - // enable table for next update - unlockSignatureTable(true); - } - }; - - /** - * lock system signature table for - */ - let lockSignatureTable = () => { - disableTableUpdate = true; - }; - - /** - * unlock system signature table from been locked - * -> make table "update-able" again - * @param instant - */ - let unlockSignatureTable = instant =>{ - if(disableTableUpdate === true){ - if(instant === true){ - disableTableUpdate = false; - }else{ - // wait until add/remove animations are finished before enable table for auto update again - setTimeout(function(){ disableTableUpdate = false; }, 2000); - } - } - }; - - /** - * update Progressbar for all scanned signatures in a system - * @param options - */ - $.fn.updateScannedSignaturesBar = function(options){ - let moduleElement = $(this); - let signatureTableApi = getDataTableInstanceByModuleElement(moduleElement, 'primary'); - - // get progress bar - let progressBarWrapper = moduleElement.find('.' + config.signatureScannedProgressBarClass); - let progressBar = $(progressBarWrapper).find('.progress-bar'); - let progressBarLabel = moduleElement.find('.progress-label-right'); - - let tableData = getTableData(signatureTableApi); - - let sigCount = 0; - let percent = 0; - let sigIncompleteCount = 0; - let progressBarType = 'progress-bar-danger'; - - if(tableData){ - sigCount = tableData.length; - - // check for signatures without "groupId" -> these are un scanned signatures - - for(let i = 0; i < tableData.length; i++){ - let groupId = parseInt(tableData[i].groupId); - if(groupId === 0){ - sigIncompleteCount++; - } - } - - if(sigCount > 0){ - percent = 100 - Math.round( 100 / sigCount * sigIncompleteCount ); - } - - if(percent < 30){ - progressBarType = 'progress-bar-danger' ; - }else if(percent < 100){ - progressBarType = 'progress-bar-warning'; - }else{ - progressBarType = 'progress-bar-success'; - } - } - - setTimeout( - function() { - progressBarLabel.text(percent + '%'); - progressBar.removeClass().addClass('progress-bar').addClass(progressBarType); - progressBar.attr('aria-valuenow', percent); - progressBar.css({width: percent + '%'}); - - let notification = (sigCount - sigIncompleteCount) + ' / ' + sigCount + ' (' + percent + '%) signatures scanned'; - - // show notifications - if(options.showNotice !== false){ - if(percent < 100){ - Util.showNotify({title: 'Unscanned signatures', text: notification, type: 'info'}); - }else{ - Util.showNotify({title: 'System is scanned', text: notification, type: 'success'}); - } - } - }, 100); - }; - - /** - * open "signature reader" dialog for signature table - * @param systemData - */ - $.fn.showSignatureReaderDialog = function(systemData){ - let moduleElement = $(this); - - requirejs(['text!templates/dialog/signature_reader.html', 'mustache'], (template, Mustache) => { - let signatureReaderDialog = bootbox.dialog({ - title: 'Signature reader', - message: Mustache.render(template, {}), - buttons: { - close: { - label: 'cancel', - className: 'btn-default' - }, - success: { - label: ' update signatures', - className: 'btn-success', - callback: function () { - let form = this.find('form'); - let formData = form.getFormValues(); - let signatureOptions = { - deleteOld: (formData.deleteOld) ? 1 : 0 - }; - moduleElement.updateSignatureTableByClipboard(systemData, formData.clipboard, signatureOptions); - } - } - } - }); - - // dialog shown event - signatureReaderDialog.on('shown.bs.modal', function(e) { - signatureReaderDialog.initTooltips(); - - // set focus on sig-input textarea - signatureReaderDialog.find('textarea').focus(); - }); - }); - }; - - /** - * updates the signature table with all signatures pasted into the "signature reader" dialog - * -> Hint: copy&paste signature data (without any open dialog) will add signatures as well - * @param systemData - * @param clipboard data stream - * @param options - */ - $.fn.updateSignatureTableByClipboard = function(systemData, clipboard, options){ - let moduleElement = $(this); - - let saveSignatureData = function(signatureData){ - // lock update function until request is finished - lockSignatureTable(); - - // lock copy during request (prevent spamming (ctrl + c ) - disableCopyFromClipboard = true; - - let requestData = { - signatures: signatureData, - deleteOld: (options.deleteOld) ? 1 : 0, - systemId: parseInt(systemData.id) - }; - - $.ajax({ - type: 'POST', - url: Init.path.saveSignatureData, - data: requestData, - dataType: 'json', - context: { - moduleElement: moduleElement - } - }).done(function(responseData){ - // unlock table for update - unlockSignatureTable(true); - // updates table with new/updated signature information - updateSignatureTable(this.moduleElement, responseData.signatures, false); - }).fail(function( jqXHR, status, error) { - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': Update signatures', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }).always(function() { - unlockSignatureTable(true); - disableCopyFromClipboard = false; - }); - }; - - // check if copy&paste is enabled - if( !disableCopyFromClipboard ){ - // parse input stream - let signatureData = parseSignatureString(systemData, clipboard); - if(signatureData.length > 0){ - // valid signature data parsed - - // check if signatures will be added to a system where character is currently in - // if user is not in any system -> id === undefined -> no "confirmation required - let currentLocationData = Util.getCurrentLocationData(); - if( - currentLocationData.id && - currentLocationData.id !== systemData.id - ){ - - let systemNameStr = (systemData.name === systemData.alias) ? '"' + systemData.name + '"' : '"' + systemData.alias + '" (' + systemData.name + ')'; - systemNameStr = '' + systemNameStr + ''; - - let msg = ''; - msg += 'Update signatures in ' + systemNameStr + ' ? This not your current location, "' + currentLocationData.name + '" !'; - bootbox.confirm(msg, function(result) { - if(result){ - saveSignatureData(signatureData); - } - }); - }else{ - // current system selected -> no "confirmation" required - saveSignatureData(signatureData); - } - } - } - }; - - /** - * parses a copy&paste string from ingame scanning window - * @param systemData - * @param clipboard - * @returns {Array} - */ - let parseSignatureString = (systemData, clipboard) => { - let signatureData = []; - - if(clipboard.length){ - let signatureRows = clipboard.split(/\r\n|\r|\n/g); - let signatureGroupOptions = config.signatureGroupsNames; - let invalidSignatures = 0; - - for(let i = 0; i < signatureRows.length; i++){ - let rowData = signatureRows[i].split(/\t/g); - if(rowData.length === 6){ - // check if sig Type = anomaly or combat site - if(validSignatureNames.indexOf( rowData[1] ) !== -1){ - - let sigGroup = $.trim(rowData[2]).toLowerCase(); - let sigDescription = $.trim(rowData[3]); - let sigGroupId = 0; - let typeId = 0; - - // get groupId by groupName - for (let prop in signatureGroupOptions) { - if(signatureGroupOptions.hasOwnProperty(prop)){ - let reg = new RegExp(signatureGroupOptions[prop], 'i'); - if (reg.test(sigGroup)) { - sigGroupId = parseInt( prop ); - break; - } - } - } - - // wormhole type cant be extracted from signature string -> skip function call - if(sigGroupId !== 5){ - // try to get "typeId" by description string - typeId = Util.getSignatureTypeIdByName( systemData, sigGroupId, sigDescription ); - - // set signature name as "description" if signature matching failed - sigDescription = (typeId === 0) ? sigDescription : ''; - }else{ - sigDescription = ''; - } - - // map array values to signature Object - let signatureObj = { - systemId: systemData.id, - name: $.trim( rowData[0] ).toLowerCase(), - groupId: sigGroupId, - typeId: typeId, - description: sigDescription - }; - - signatureData.push(signatureObj); - }else{ - invalidSignatures++; - } - } - } - - if(invalidSignatures > 0){ - let notification = invalidSignatures + ' / ' + signatureRows.length + ' signatures invalid'; - Util.showNotify({title: 'Invalid signature(s)', text: notification, type: 'warning'}); - } - } - - return signatureData; - }; - - /** - * format signature data array into dataTable structure - * @param systemData - * @param signatureData - * @param options - * @returns {Array} - */ - let formatSignatureData = (systemData, signatureData, options) => { - let formattedData = []; - - // security check - if( - systemData && - systemData.id && - systemData.id > 0 - ){ - let systemTypeId = systemData.type.id; - - // areaId is required as a key for signature names - // if areaId is 0, no signature data is available for this system - let areaId = Util.getAreaIdBySecurity(systemData.security); - - for(let i = 0; i < signatureData.length; i++){ - let data = signatureData[i]; - - let tempData = {}; - - // set id --------------------------------------------------------------------------------------------- - let sigId = 0; - if(data.id > 0){ - sigId = data.id; - } - tempData.id = sigId; - - // set status ----------------------------------------------------------------------------------------- - let statusClass = ''; - if(data.updated.character !== undefined){ - statusClass = Util.getStatusInfoForCharacter(data.updated.character, 'class'); - } - let status = ''; - - tempData.status = { - status: status, - status_sort: statusClass - }; - - // set name ------------------------------------------------------------------------------------------- - let sigName = ' 0){ - sigName += 'data-pk="' + data.id + '" '; - } - sigName += '>' + data.name + ''; - - tempData.name = { - render: sigName, - name: data.name - }; - - // set group id --------------------------------------------------------------------------------------- - let sigGroup = ' 0){ - sigGroup += 'data-pk="' + data.id + '" '; - } - sigGroup += 'data-systemTypeId="' + systemTypeId + '" '; - sigGroup += 'data-areaId="' + areaId + '" '; - sigGroup += 'data-value="' + data.groupId + '" '; - sigGroup += '>'; - - tempData.group = { - group: sigGroup, - sort: config.signatureGroupsLabels[data.groupId] || '', - filter: config.signatureGroupsLabels[data.groupId] ||'' - }; - - // set type id ---------------------------------------------------------------------------------------- - let sigType = ' 0){ - sigType += 'data-pk="' + data.id + '" '; - } - - // set disabled if sig type is not selected - if(data.groupId < 1){ - sigType += 'data-disabled="1" '; - } - - sigType += 'data-systemTypeId="' + systemTypeId + '" '; - sigType += 'data-areaId="' + areaId + '" '; - sigType += 'data-groupId="' + data.groupId + '" '; - sigType += 'data-value="' + data.typeId + '" '; - sigType += '>'; - - tempData.type = sigType; - - // set connection (to target system) ------------------------------------------------------------------ - let sigConnection = ' 0){ - sigConnection += 'data-pk="' + data.id + '" '; - } - - // set disabled if group is not wormhole - if(data.groupId !== 5){ - sigConnection += 'data-disabled="1" '; - } - - if(data.connection){ - sigConnection += 'data-value="' + data.connection.id + '" '; - } - sigConnection += '>'; - - tempData.connection = { - render: sigConnection, - connection: data.connection - }; - - // set description ------------------------------------------------------------------------------------ - let sigDescription = ' 0){ - sigDescription += 'data-pk="' + data.id + '" '; - } - sigDescription += '>' + data.description + ''; - - tempData.description = sigDescription; - - // set created ---------------------------------------------------------------------------------------- - tempData.created = data.created; - - // set updated ---------------------------------------------------------------------------------------- - tempData.updated = data.updated; - - // info icon ------------------------------------------------------------------------------------------ - let infoButton = ''; - if(data.id > 0){ - infoButton = ''; - } - tempData.info = infoButton; - - // action icon ---------------------------------------------------------------------------------------- - - let actionButton = ''; - tempData.action = { - action: options.action, - button: actionButton - }; - - formattedData.push(tempData); - - } - - } - - return formattedData; - }; - - /** - * get all rows of a table - * @param tableApi - * @returns {*} - */ - let getRows = tableApi => { - let rows = tableApi.rows(); - return rows; - }; - - /** - * get all selected rows of a table - * @param tableApi - * @returns {*} - */ - let getSelectedRows = tableApi => { - let selectedRows = tableApi.rows('.selected'); - return selectedRows; - }; - - /** - * check the "delete signature" button. show/hide the button if a signature is selected - * @param moduleElement - */ - let checkDeleteSignaturesButton = tableApi => { - let selectedRows = getSelectedRows(tableApi); - let selectedRowCount = selectedRows.data().length; - let clearButton = tableApi.button('tableTools', 'delete:name').node(); - - if(selectedRowCount > 0){ - let allRows = getRows(tableApi); - let rowCount = allRows.data().length; - - let countText = selectedRowCount; - if(selectedRowCount >= rowCount){ - countText = 'all'; - } - clearButton.find('i+span').text(countText); - - // update clear signatures button text - clearButton.velocity('stop'); - - if( clearButton.is(':hidden') ){ - // show button - clearButton.velocity('transition.expandIn', { - duration: 100 - }); - }else{ - // highlight button - clearButton.velocity('callout.pulse', { - duration: 200 - }); - } - }else{ - // hide button - clearButton.velocity('transition.expandOut', { - duration: 100 - }); - } - }; - - /** - * draw signature table toolbar (add signature button, scan progress bar - * @param moduleElement - * @param mapId - * @param systemData - */ - let drawSignatureTableNew = (moduleElement, mapId, systemData) => { - // add toolbar action for table ------------------------------------------------------------------------------- - let tableToolbarAction = $('
', { - class: config.tableToolsActionClass - }); - - // create "empty table for new signature - let table = $('
', { - class: ['stripe', 'row-border', 'compact', 'nowrap', config.sigTableClass, config.sigTableSecondaryClass].join(' ') - }); - - tableToolbarAction.append(table); - - moduleElement.find('.' + config.moduleHeadClass).after(tableToolbarAction); - - let signatureData = formatSignatureData(systemData, [emptySignatureData], emptySignatureOptions); - let signatureTable = table.dataTable( { - data: signatureData, - paging: false, - info: false, - searching: false, - tabIndex: -1 - } ); - let signatureTableApi = signatureTable.api(); - - setDataTableInstance(mapId, systemData.id, 'secondary', signatureTableApi); - - table.makeEditable(signatureTableApi, systemData); - }; - - /** - * Update/set tooltip for an element - * @param element - * @param title - */ - let updateTooltip = (element, title) => { - $(element).attr('data-container', 'body').attr('title', title.toUpperCase()).tooltip('fixTitle') - .tooltip('setContent'); - }; - - /** - * helper function - jump to "next" editable field on save - * @param field - * @param selector - * @returns {*|jQuery|HTMLElement} - */ - let getNextEditableField = (field, selector) => { - let nextEditableField = null; - if(selector){ - // search specific sibling - nextEditableField = $(field).closest('td').nextAll(selector).find('.editable'); - }else{ - // get next sibling - nextEditableField = $(field).closest('td').next().find('.editable'); - } - - return $(nextEditableField); - }; - - /** - * helper function - get the next editable field in next table column - * @param fields - */ - let openNextEditDialogOnSave = fields => { - fields.on('save', function(e, params){ - let currentField = $(this); - let nextField = getNextEditableField(currentField); - nextField.editable('show'); - - setTimeout(() => { - // update scanning progressbar if sig "type" has changed AND - // the current field is in the "primary" table (not the "add" new sig row) - if( - $(e.target).hasClass(config.sigTableEditSigGroupSelect) && - $(e.target).parents('.' + config.sigTableClass).hasClass(config.sigTablePrimaryClass) - ){ - currentField.parents('.' + config.moduleClass).updateScannedSignaturesBar({showNotice: true}); - } - }, 200); - }); - }; - - /** - * make a table or row editable - * @param tableApi - * @param systemData - */ - $.fn.makeEditable = function(tableApi, systemData){ - // table element OR row element - let tableElement = $(this); - - // find editable fields - let sigNameFields = tableElement.find('.' + config.sigTableEditSigNameInput); - let sigGroupFields = tableElement.find('.' + config.sigTableEditSigGroupSelect); - let sigTypeFields = tableElement.find('.' + config.sigTableEditSigTypeSelect); - let sigDescriptionFields = tableElement.find('.' + config.sigTableEditSigDescriptionTextarea); - let sigConnectionFields = tableElement.find('.' + config.sigTableEditSigConnectionSelect); - - /** - * add map/system specific data for each editable field in the sig-table - * @param params - * @returns {*} - */ - let modifyFieldParamsOnSend = params => { - params.systemId = systemData.id; - return params; - }; - - // set global xEditable options for all table fields - $.extend($.fn.editable.defaults, { - url: Init.path.saveSignatureData, - dataType: 'json', - mode: 'popup', - container: 'body', - error: function(jqXHR, newValue){ - let reason = ''; - let status = ''; - if(jqXHR.name){ - // save error new sig (mass save) - reason = jqXHR.name; - status = 'Error'; - }else{ - reason = jqXHR.responseJSON.text; - status = jqXHR.status; - } - - Util.showNotify({title: status + ': save signature', text: reason, type: 'error'}); - $(document).setProgramStatus('problem'); - return reason; - } - }); - - - // Input sig name --------------------------------------------------------------------------------------------- - sigNameFields.editable({ - type: 'text', - title: 'signature id', - name: 'name', - emptytext: '? ? ?', - display: function(value) { - // change display value to first 3 letters - $(this).text($.trim( value.substr(0, 3) ).toLowerCase()); - }, - validate: function(value) { - if($.trim(value).length < 3) { - return 'Id is less than min of "3"'; - }else if($.trim(value).length > 10){ - return 'Id is more than max of "10"'; - } - }, - params: modifyFieldParamsOnSend, - success: function(response, newValue){ - if(response){ - let signatureNameField = $(this); - let columnElement = signatureNameField.parents('td'); - let rowElement = signatureNameField.parents('tr'); - let newRowData = response.signatures[0]; - - // update column tooltip - updateTooltip(columnElement, newValue); - - // update "updated" cell - updateSignatureCell(tableApi, rowElement, 7, newRowData.updated); - } - } - }); - - - // Select sig group (master) ---------------------------------------------------------------------------------- - sigGroupFields.editable({ - type: 'select', - title: 'group', - name: 'groupId', - emptytext: 'unknown', - onblur: 'submit', - showbuttons: false, - prepend: [{value: '0', text: ''}], - params: modifyFieldParamsOnSend, - source: function(){ - let signatureGroupField = $(this); - let systemTypeId = parseInt( signatureGroupField.attr('data-systemTypeId') ); - - // get all available Signature Types - let availableTypes = config.signatureGroupsLabels; - - return availableTypes; - }, - success: function(response, newValue){ - let signatureGroupField = $(this); - let rowElement = signatureGroupField.parents('tr'); - newValue = parseInt(newValue); - - if(response){ - let newRowData = response.signatures[0]; - - // update "updated" cell - updateSignatureCell(tableApi, rowElement, 7, newRowData.updated); - } - - // find related "type" select (same row) and change options - let signatureTypeField = getNextEditableField(signatureGroupField); - - let systemTypeId = parseInt( signatureGroupField.attr('data-systemTypeId') ); - let areaId = parseInt( signatureGroupField.attr('data-areaid') ); - - let newSelectOptions = getAllSignatureNames(systemData, systemTypeId, areaId, newValue); - signatureTypeField.editable('option', 'source', newSelectOptions); - - if( - newValue > 0 && - newSelectOptions.length > 0 - ){ - signatureTypeField.editable('enable'); - }else{ - signatureTypeField.editable('disable'); - } - - signatureTypeField.editable('setValue', null); - - // find "connection" select (same row) and change "enabled" flag - let signatureConnectionField = getNextEditableField(signatureGroupField, '.' + config.sigTableConnectionClass); - if(newValue === 5){ - // wormhole - signatureConnectionField.editable('enable'); - }else{ - checkConnectionConflicts(); - signatureConnectionField.editable('disable'); - } - - signatureConnectionField.editable('setValue', null); - } - }); - - - // Select sig type (slave: depends on sig type) --------------------------------------------------------------- - sigTypeFields.on('init', function(e, editable) { - // check if there are initial options available - let options = editable.input.options.source.bind(e.target)(); - if(options.length <= 0){ - editable.disable(); - } - }); - - sigTypeFields.editable({ - type: 'select', - title: 'type', - name: 'typeId', - emptytext: 'unknown', - onblur: 'submit', - showbuttons: false, - params: modifyFieldParamsOnSend, - display: function(value, sourceData){ - let selected = $.fn.editableutils.itemsByValue(value, sourceData); - if(selected.length && selected[0].text.length){ - $(this).html(FormElement.formatSignatureTypeSelectionData({text: selected[0].text})); - }else{ - $(this).empty(); - } - }, - source: function(){ - let signatureTypeField = $(this); - - let systemTypeId = parseInt( signatureTypeField.attr('data-systemTypeId') ); - let areaId = parseInt( signatureTypeField.attr('data-areaid') ); - let groupId = parseInt( signatureTypeField.attr('data-groupId') ); - let availableSigs = getAllSignatureNames(systemData, systemTypeId, areaId, groupId); - - return availableSigs; - }, - success: function(response, newValue){ - let signatureTypeField = $(this); - let rowElement = signatureTypeField.parents('tr'); - - if(response){ - let newRowData = response.signatures[0]; - // update "updated" cell - updateSignatureCell(tableApi, rowElement, 7, newRowData.updated); - }else{ - // "add new" signature -> set "+" focus for keyboard control - setTimeout(() => { - rowElement.find('.pf-table-action-cell')[0].focus(); - }, 50); - } - } - }); - - // Textarea sig description ----------------------------------------------------------------------------------- - sigDescriptionFields.editable({ - type: 'textarea', - title: 'description', - name: 'description', - emptytext: '', - onblur: 'submit', - mode: 'inline', - showbuttons: false, - inputclass: config.editableDescriptionInputClass, - params: modifyFieldParamsOnSend, - success: function(response, newValue){ - if(response){ - let signatureDescriptionField = $(this); - let rowElement = signatureDescriptionField.parents('tr'); - let newRowData = response.signatures[0]; - - // update "updated" cell - updateSignatureCell(tableApi, rowElement, 7, newRowData.updated); - } - } - }); - - // Select connection (target system) -------------------------------------------------------------------------- - let initCount = 0; - sigConnectionFields.on('init', function(e, editable) { - if(++initCount >= sigConnectionFields.length){ - checkConnectionConflicts(); - } - }); - - sigConnectionFields.editable({ - type: 'select', - title: 'system', - name: 'connectionId', - emptytext: 'unknown', - onblur: 'submit', - showbuttons: false, - prepend: [{value: '0', text: ''}], - params: modifyFieldParamsOnSend, - display: function(value, sourceData) { - let selected = $.fn.editableutils.itemsByValue(value, sourceData); - if(selected.length && selected[0].text.length){ - let errorIcon = ' '; - $(this).html(FormElement.formatSignatureConnectionSelectionData({text: selected[0].text})).prepend(errorIcon); - }else{ - $(this).empty() ; - } - }, - source: function(a,b){ - let activeMap = Util.getMapModule().getActiveMap(); - let mapId = activeMap.data('id'); - - let availableConnections = getSignatureConnectionOptions(mapId, systemData); - - return availableConnections; - }, - success: function(response, newValue){ - let signatureConnectionField = $(this); - let rowElement = signatureConnectionField.parents('tr'); - - if(response){ - let newRowData = response.signatures[0]; - - // update "updated" cell - updateSignatureCell(tableApi, rowElement, 7, newRowData.updated); - }else{ - // "add new" signature -> set "+" focus for keyboard control - setTimeout(() => { - rowElement.find('.pf-table-action-cell')[0].focus(); - }, 50); - } - } - }); - - sigGroupFields.on('shown', function(e, editable){ - let inputField = editable.input.$input; - inputField.addClass('pf-select2').initSignatureGroupSelect(); - }); - - sigTypeFields.on('shown', function(e, editable){ - // destroy possible open popovers (e.g. wormhole types) - $(this).destroyPopover(true); - - let inputField = editable.input.$input; - let hasOptGroups = inputField.has('optgroup').length > 0; - inputField.addClass('pf-select2').initSignatureTypeSelect({}, hasOptGroups); - }); - - sigConnectionFields.on('shown', function(e, editable){ - let inputField = editable.input.$input; - inputField.addClass('pf-select2').initSignatureConnectionSelect(); - }); - - sigDescriptionFields.on('shown', function(e, editable){ - // enlarge the tools-action container because the tables gets bigger - tableElement.parents('.' + config.tableToolsActionClass).css( 'height', '+=35px' ); - }); - - sigDescriptionFields.on('hidden', function(e, editable){ - // enlarge the tools-action container because the tables gets bigger - tableElement.parents('.' + config.tableToolsActionClass).css( 'height', '-=35px' ); - }); - - sigConnectionFields.on('save', function(e, editable){ - checkConnectionConflicts(); - }); - - $().add(sigNameFields).add(sigGroupFields).add(sigTypeFields) - .add(sigDescriptionFields).add(sigConnectionFields).on('hidden', function(e, editable) { - // re-focus element on close (keyboard navigation) - this.focus(); - }); - - // open next field dialog ------------------------------------------------------------------------------------- - openNextEditDialogOnSave(sigNameFields); - openNextEditDialogOnSave(sigGroupFields); - }; - - /** - * get all connection select options - * @param mapId - * @param systemData - * @returns {Array} - */ - let getSignatureConnectionOptions = (mapId, systemData) => { - let map = Map.getMapInstance( mapId ); - let systemId = MapUtil.getSystemId(mapId, systemData.id); - let systemConnections = MapUtil.searchConnectionsBySystems(map, [systemId], 'wh'); - let newSelectOptions = []; - let connectionOptions = []; - - for(let i = 0; i < systemConnections.length; i++){ - let connectionData = MapUtil.getDataByConnection(systemConnections[i]); - - // connectionId is required (must be stored) - if(connectionData.id){ - // check whether "source" or "target" system is relevant for this connection - // -> hint "source" === 'target' --> loop - if(systemData.id !== connectionData.target){ - let targetSystemData = MapUtil.getSystemData(mapId, connectionData.target); - if(targetSystemData){ - // take target... - connectionOptions.push({ - value: connectionData.id, - text: connectionData.targetAlias + ' - ' + targetSystemData.security - }); - } - }else if(systemData.id !== connectionData.source){ - let sourceSystemData = MapUtil.getSystemData(mapId, connectionData.source); - if(sourceSystemData){ - // take source... - connectionOptions.push({ - value: connectionData.id, - text: connectionData.sourceAlias + ' - ' + sourceSystemData.security - }); - } - } - } - } - - if(connectionOptions.length > 0){ - newSelectOptions.push({ text: 'System', children: connectionOptions}); - } - - return newSelectOptions; - }; - - /** - * check connectionIds for conflicts (multiple signatures -> same connection) - * -> show "conflict" icon next to select - */ - let checkConnectionConflicts = () => { - setTimeout(() => { - let connectionSelects = $('.' + config.sigTableConnectionClass + ' .editable'); - let connectionIds = []; - let duplicateConnectionIds = []; - let groupedSelects = []; - - connectionSelects.each(function(){ - let select = $(this); - let value = parseInt(select.editable('getValue', true) )|| 0; - - if( - connectionIds.indexOf(value) > -1 && - duplicateConnectionIds.indexOf(value) === -1 - ){ - // duplicate found - duplicateConnectionIds.push(value); - } - - if(groupedSelects[value] !== undefined){ - groupedSelects[value].push(select[0]); - }else{ - groupedSelects[value] = [select[0]]; - } - - connectionIds.push(value); - }); - - // update "conflict" icon next to select label for connectionIds - connectionSelects.each(function(){ - let select = $(this); - let value = parseInt(select.editable('getValue', true) )|| 0; - let conflictIcon = select.find('.fa-exclamation-triangle'); - if( - duplicateConnectionIds.indexOf(value) > -1 && - groupedSelects[value].indexOf(select[0]) > -1 - ){ - conflictIcon.removeClass('hide'); - }else{ - conflictIcon.addClass('hide'); - } - }); - }, 200); - }; - - /** - * get all signature types that can exist for a given system - * -> result is partially cached - * @param systemData - * @param systemTypeId - * @param areaId - * @param groupId - * @returns {Array} - */ - let getAllSignatureNames = (systemData, systemTypeId, areaId, groupId) => { - let newSelectOptions = []; - let cacheKey = [systemTypeId, areaId, groupId].join('_'); - let newSelectOptionsCount = 0; - - // check for cached signature names - if(sigNameCache.hasOwnProperty( cacheKey )){ - // cached signatures do not include static WHs! - // -> ".slice(0)" creates copy - newSelectOptions = sigNameCache[cacheKey].slice(0); - newSelectOptionsCount = getOptionsCount('children', newSelectOptions); - }else{ - // get new Options ---------- - // get all possible "static" signature names by the selected groupId - let tempSelectOptions = Util.getAllSignatureNames(systemTypeId, areaId, groupId); - - // format options into array with objects advantages: keep order, add more options (whs), use optgroup - if(tempSelectOptions){ - let fixSelectOptions = []; - for (let key in tempSelectOptions) { - if ( - key > 0 && - tempSelectOptions.hasOwnProperty(key) - ) { - newSelectOptionsCount++; - fixSelectOptions.push({value: parseInt(key), text: tempSelectOptions[key]}); - } - } - - if(newSelectOptionsCount > 0){ - if(groupId === 5){ - // "wormhole" selected => multiple available - newSelectOptions.push({ text: 'Wandering', children: fixSelectOptions}); - }else{ - newSelectOptions = fixSelectOptions; - } - } - } - - // wormhole (cached signatures) - if( groupId === 5 ){ - - // add possible frigate holes - let frigateHoles = getFrigateHolesBySystem(areaId); - let frigateWHData = []; - for(let frigKey in frigateHoles){ - if ( - frigKey > 0 && - frigateHoles.hasOwnProperty(frigKey) - ) { - newSelectOptionsCount++; - frigateWHData.push( {value: newSelectOptionsCount, text: frigateHoles[frigKey]} ); - } - } - - if(frigateWHData.length > 0){ - newSelectOptions.push({ text: 'Frigate', children: frigateWHData}); - } - - // add possible incoming holes - let incomingWHData = []; - for(let incomingKey in Init.incomingWormholes){ - if ( - incomingKey > 0 && - Init.incomingWormholes.hasOwnProperty(incomingKey) - ) { - newSelectOptionsCount++; - incomingWHData.push( {value: newSelectOptionsCount, text: Init.incomingWormholes[incomingKey]} ); - } - } - - if(incomingWHData.length > 0){ - newSelectOptions.push({ text: 'Incoming', children: incomingWHData}); - } - }else{ - // groups without "children" (optgroup) should be sorted by "value" - // this is completely optional and not necessary! - newSelectOptions = newSelectOptions.sortBy('text'); - } - - // update cache (clone array) -> further manipulation to this array, should not be cached - sigNameCache[cacheKey] = newSelectOptions.slice(0); - } - - // static wormholes (DO NOT CACHE) (not all C2 WHs have the same statics,... - if( groupId === 5 ){ - // add static WH(s) for this system - if(systemData.statics){ - let staticWHData = []; - for(let wormholeName of systemData.statics){ - let wormholeData = Object.assign({}, Init.wormholes[wormholeName]); - let staticWHName = wormholeData.name + ' - ' + wormholeData.security; - - newSelectOptionsCount++; - staticWHData.push( {value: newSelectOptionsCount, text: staticWHName} ); - } - - if(staticWHData.length > 0){ - newSelectOptions.unshift({ text: 'Static', children: staticWHData}); - } - } - } - - // if selectOptions available -> add "empty" option as well - if(newSelectOptionsCount > 0){ - newSelectOptions.unshift({ value: '0', text: ''}); - } - - return newSelectOptions; - }; - - /** - * get all signature types that can exist for a system (jQuery obj) - * @param systemElement - * @param groupId - * @returns {Array} - */ - let getAllSignatureNamesBySystem = (systemElement, groupId) => { - let systemTypeId = systemElement.data('typeId'); - let areaId = Util.getAreaIdBySecurity(systemElement.data('security')); - let systemData = {statics: systemElement.data('statics')}; - return getAllSignatureNames(systemData, systemTypeId, areaId, groupId); - }; - - /** - * sum up all options in nested (or not nested) object of objects - * -> e.g. - * { - * first: { - * count = [4, 2, 1] - * test = { ... } - * }, - * second: { - * count = [12, 13] - * test = { ... } - * } - * } - * -> getOptionsCount('count', obj) => 5; - * @param key - * @param obj - * @returns {number} - */ - let getOptionsCount = (key, obj) => { - let sum = 0; - for(let entry of obj){ - if(entry.hasOwnProperty(key)){ - sum += entry[key].length; - }else{ - sum++; - } - } - return sum; - }; - - /** - * get possible frig holes that could spawn in a system - * filtered by "systemTypeId" - * @param systemTypeId - * @returns {{}} - */ - let getFrigateHolesBySystem = systemTypeId => { - let signatureNames = {}; - - if(Init.frigateWormholes[systemTypeId]){ - signatureNames = Init.frigateWormholes[systemTypeId]; - } - - return signatureNames; - }; - - /** - * deletes signature rows from signature table - * @param tableApi - * @param rows - */ - let deleteSignatures = (tableApi, rows) => { - let deletedSignatures = 0; - - let moduleElement = $('.' + config.moduleTypeClass); - let data = rows.data(); - let rowElements = rows.nodes().to$(); - let signatureCount = data.length; - - let signatureIds = []; - for(let i = 0; i < data.length; i++){ - signatureIds.push(data[i].id); - } - - let requestData = { - signatureIds: signatureIds - }; - - // animation callback function - let removeCallback = function(rowElement){ - // delete signature row - tableApi.row(rowElement).remove().draw(); - - deletedSignatures++; - - if(deletedSignatures === signatureCount){ - // all animations finished - - // update signature bar - moduleElement.updateScannedSignaturesBar({showNotice: false}); - - // update connection conflicts - checkConnectionConflicts(); - - Util.showNotify({title: 'Signature deleted', text: signatureCount + ' deleted', type: 'success'}); - } - }; - - $.ajax({ - type: 'POST', - url: Init.path.deleteSignatureData, - data: requestData, - dataType: 'json' - }).done(function(data){ - - for(let j = 0; j < rowElements.length; j++){ - // removeRow - $(rowElements[j]).toggleTableRow(removeCallback); - } - - }).fail(function( jqXHR, status, error) { - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': Delete signature', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }); - }; - - /** - * adds a new row to signature Table - * @param signatureTableApi - * @param systemData - * @param signatureData - * @param animate - * @returns {*} - */ - let addSignatureRow = (signatureTableApi, systemData, signatureData, animate) => { - let newRowElement = null; - if(signatureTableApi){ - let newSignatureData = formatSignatureData(systemData, [signatureData], fullSignatureOptions); - let newRowNode = signatureTableApi.row.add(newSignatureData.shift()).draw().nodes(); - newRowElement = newRowNode.to$(); - - if(animate === true){ - newRowElement.hide(); - newRowElement.toggleTableRow(newRowElement => { - // make new row editable - newRowElement.makeEditable(signatureTableApi, systemData); - - // update scan progress bar - newRowElement.parents('.' + config.moduleClass).updateScannedSignaturesBar({showNotice: true}); - }); - }else{ - newRowElement.makeEditable(signatureTableApi, systemData); - } - } - - return newRowElement; - }; - - /** - * show/hides a table tag - * @param callback - */ - $.fn.toggleTableRow = function(callback){ - let rowElement = $(this); - let cellElements = rowElement.children('td'); - let duration = 100; - - // wrap each
into a container (for better animation performance) - // slideUp new wrapper divs - if(rowElement.is(':visible')){ - // hide row - - // stop sig counter by adding a stopClass to each , remove padding - cellElements.addClass('stopCounter') - .velocity({ - paddingTop: 0, - paddingBottom: 0 - },{ - duration: duration - }).wrapInner('
') - .children() - .css({ - 'willChange': 'height' - }).velocity('slideUp', { - duration: duration, - complete: function(animationElements){ - // remove wrapper - $(animationElements).children().unwrap(); - - if(callback !== undefined){ - callback(rowElement); - } - } - }); - }else{ - // show row - - // remove padding on "hidden" cells for smother animation - cellElements.css({ - 'padding-top': 0, - 'padding-bottom': 0, - 'willChange': 'padding-top, padding-top, height' - }); - - // add hidden wrapper for ea - cellElements.wrapInner($('
').hide()); - - // show row for padding animation - rowElement.show(); - - cellElements.velocity({ - paddingTop: '4px', - paddingBottom: '4px' - },{ - duration: duration, - queue: false, - complete: function(){ - // animate
wrapper - cellElements.children() - .css({ - 'willChange': 'height' - }).velocity('slideDown', { - duration: duration, - complete: function(animationElements){ - // remove wrapper - for(let i = 0; i < animationElements.length; i++){ - let currentWrapper = $(animationElements[i]); - if(currentWrapper.children().length > 0){ - currentWrapper.children().unwrap(); - }else{ - currentWrapper.parent().html( currentWrapper.html() ); - } - } - - if(callback !== undefined){ - callback(rowElement); - } - } - }); - } - }); - } - }; - - - /** - * get unique column data from column object for select filter options - * @param column - * @returns {{}} - */ - /* currently not needed but could be helpful one day - let getColumnTableDataForFilter = column => { - // get all available options from column - let source = {}; - column.data().unique().sort((a,b) => { - // sort alphabetically - let valA = a.filter.toLowerCase(); - let valB = b.filter.toLowerCase(); - - if(valA < valB) return -1; - if(valA > valB) return 1; - return 0; - }).each(callData => { - if(callData.filter){ - source[callData.filter] = callData.filter; - } - }); - - // add empty option - source[0] = ''; - - return source; - };*/ - - let searchGroupColumn = (tableApi, newValue, sourceOptions) => { - let column = tableApi.column('group:name'); - let pattern = ''; - - if(newValue.length <= sourceOptions.length){ - // all options selected + "prepend" option - pattern = newValue.map(val => val !== '0' ? $.fn.dataTable.util.escapeRegex(val) : '^$').join('|'); - } - column.search(pattern, true, false).draw(); - }; - - /** - * init table filter button "group" column - * @param tableApi - */ - let initGroupFilterButton = tableApi => { - let characterId = Util.getCurrentCharacterId(); - - let promiseStore = MapUtil.getLocaleData('character', Util.getCurrentCharacterId()); - promiseStore.then(data => { - let filterButton = tableApi.button('tableTools', 'filterGroup:name').node(); - let prependOptions = [{value: '0', text: 'unknown'}]; - let sourceOptions = []; - let selectedValues = []; - - // format group filter options - let groups = Object.assign({}, config.signatureGroupsLabels); - for(let [value, label] of Object.entries(groups)){ - if(label.length){ - sourceOptions.push({value: label, text: label}); - } - } - - if(data && data.filterSignatureGroups && data.filterSignatureGroups.length){ - // select local stored values - selectedValues = data.filterSignatureGroups; - }else{ - // no default group filter options -> show all - selectedValues = sourceOptions.map(option => option.text); - selectedValues.unshift('0'); - } - - filterButton.editable({ - mode: 'popup', - type: 'checklist', - showbuttons: false, - onblur: 'submit', - highlight: false, - title: 'filter groups', - value: selectedValues, - source: sourceOptions, - prepend: prependOptions, - inputclass: config.editableUnknownInputClass, - display: function(value, sourceData){ - // update filter button label - let html = 'group'; - let allSelected = value.length >= (sourceOptions.length + prependOptions.length); - if( !allSelected ){ - html += ' (' + value.length + ')'; - } - $(this).toggleClass('active', !allSelected).html(html); - } - }); - - filterButton.on('save', {tableApi: tableApi, sourceOptions: sourceOptions}, function(e, params){ - // store values local -> IndexDB - MapUtil.storeLocaleCharacterData('filterSignatureGroups', params.newValue); - - searchGroupColumn(e.data.tableApi, params.newValue, e.data.sourceOptions); - }); - - // set initial search string -> even if table ist currently empty - searchGroupColumn(tableApi, selectedValues, sourceOptions); - }); - }; - - /** - * draw empty signature table - * @param moduleElement - * @param mapId - * @param systemData - */ - let drawSignatureTable = (moduleElement, mapId, systemData) => { - - let table = $('', { - class: ['display', 'compact', 'nowrap', config.sigTableClass, config.sigTablePrimaryClass].join(' ') - }); - - moduleElement.append(table); - - let dataTableOptions = { - tabIndex: -1, - dom: '<"row"<"col-xs-3"l><"col-xs-5"B><"col-xs-4"f>>' + - '<"row"<"col-xs-12"tr>>' + - '<"row"<"col-xs-5"i><"col-xs-7"p>>', - buttons: { - name: 'tableTools', - buttons: [ - { - name: 'filterGroup', - className: config.moduleHeadlineIconClass, - text: '' // set by js (xEditable) - }, - { - name: 'selectAll', - className: config.moduleHeadlineIconClass, - text: 'select all', - action: function(e, tableApi, node, conf){ - let allRows = getRows(tableApi); - let selectedRows = getSelectedRows(tableApi); - let allRowElements = allRows.nodes().to$(); - - if(allRows.data().length === selectedRows.data().length){ - allRowElements.removeClass('selected'); - }else{ - allRowElements.addClass('selected'); - } - - // check delete button - checkDeleteSignaturesButton(tableApi); - } - }, - { - name: 'delete', - className: [config.moduleHeadlineIconClass, config.sigTableClearButtonClass].join(' '), - text: 'delete (0)', - action: function(e, tableApi, node, conf){ - let selectedRows = getSelectedRows(tableApi); - bootbox.confirm('Delete ' + selectedRows.data().length + ' signature?', function(result){ - if(result){ - deleteSignatures(tableApi, selectedRows); - } - }); - } - } - ] - }, - initComplete: function (settings, json){ - let tableApi = this.api(); - initGroupFilterButton(tableApi); - } - }; - - // create signature table and store the jquery object global for this module - let signatureTable = table.dataTable(dataTableOptions); - let signatureTableApi = signatureTable.api(); - setDataTableInstance(mapId, systemData.id, 'primary', signatureTableApi); - }; - - /** - * setup dataTable options for all signatureTables - * @param systemData - */ - let initSignatureDataTable = systemData => { - - $.extend( true, $.fn.dataTable.defaults, { - pageLength: -1, - lengthMenu: [[5, 10, 25, 50, -1], [5, 10, 25, 50, 'All']], - order: [1, 'asc'], - autoWidth: false, - responsive: { - details: false - }, - language: { - emptyTable: 'No signatures added', - zeroRecords: 'No signatures found', - lengthMenu: 'Show _MENU_ signatures', - info: 'Showing _START_ to _END_ of _TOTAL_ signatures' - }, - columnDefs: [ - { - targets: 0, - name: 'status', - orderable: true, - searchable: false, - title: '', - width: 2, - class: ['text-center', 'min-tablet-l'].join(' '), - data: 'status', - type: 'html', - render: { - _: 'status', - sort: 'status_sort' - } - },{ - targets: 1, - name: 'id', - orderable: true, - searchable: true, - title: 'id', - type: 'html', - width: 15, - data: 'name', - render: { - _: 'render' - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - // update column tooltip - updateTooltip(cell, cellData.name); - } - },{ - targets: 2, - name: 'group', - orderable: true, - searchable: true, - title: 'group', - type: 'html', - width: 40, - data: 'group', - render: { - _: 'group', - sort: 'sort', - filter: 'filter' - } - },{ - targets: 3, - name: 'type', - orderable: false, - searchable: false, - title: 'type', - type: 'html', - width: 180, - data: 'type' - },{ - targets: 4, - name: 'description', - orderable: false, - searchable: false, - title: 'description', - type: 'html', - data: 'description' - },{ - targets: 5, - name: 'connection', - orderable: false, - searchable: false, - className: [config.sigTableConnectionClass].join(' '), - title: 'leads to', - type: 'html', - width: 70, - data: 'connection', - render: { - _: 'render' - } - },{ - targets: 6, - name: 'created', - title: 'created', - width: 90, - searchable: false, - className: ['text-right', config.sigTableCounterClass, config.sigTableCreatedCellClass, 'min-tablet-l'].join(' '), - data: 'created', - render: { - _: 'created', - sort: 'created' - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - $(cell).initTimestampCounter(); - } - },{ - targets: 7, - name: 'updated', - title: 'updated', - width: 90, - searchable: false, - className: ['text-right', config.sigTableCounterClass, config.sigTableUpdatedCellClass, 'min-tablet-l'].join(' '), - data: 'updated', - render: { - _: 'updated', - sort: 'updated' - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - $(cell).initTimestampCounter(); - - // highlight cell - let diff = Math.floor((new Date()).getTime()) - cellData.updated * 1000; - - // age > 1 day - if( diff > 86400000){ - $(cell).addClass('txt-color txt-color-warning'); - } - } - },{ - targets: 8, - name: 'info', - title: '', - orderable: false, - searchable: false, - width: 10, - class: ['text-center', Util.config.helpClass].join(' '), - data: 'info', - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - if(rowData.id > 0){ - let tooltipData = { - created: rowData.created, - updated: rowData.updated - }; - - $(cell).addCharacterInfoTooltip( tooltipData ); - } - } - },{ - targets: 9, - name: 'action', - title: '', - orderable: false, - searchable: false, - width: 10, - class: ['text-center', config.sigTableActionCellClass].join(' '), - data: 'action', - render: { - _: 'button', - sort: 'action' - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let tempTableElement = this; - let rowElement = $(cell).parents('tr'); - - $(cell).attr('tabindex', 0).on('keydown', function(e){ - e.stopPropagation(); - if(e.which === 13){ - $(this).trigger('click'); - } - }); - - switch(cellData.action){ - case 'add': - // add new signature ------------------------------------------------------------------ - $(cell).on('click', function(e) { - e.stopPropagation(); - e.preventDefault(); - - // submit all fields within a table row - let formFields = rowElement.find('.editable'); - - // get the current "primary table" for insert row on ajax callback - // -> important: in case of long response, target table might have changed... - let moduleElement = $(e.target).parents('.' + config.moduleClass); - let primaryTable = moduleElement.find('.' + config.sigTablePrimaryClass); - let secondaryTable = moduleElement.find('.' + config.sigTableSecondaryClass); - - // the "hide" makes sure to take care about open editable fields (e.g. description) - // otherwise, changes would not be submitted in this field (not necessary) - formFields.editable('hide'); - - // submit all xEditable fields - formFields.editable('submit', { - url: Init.path.saveSignatureData, - ajaxOptions: { - dataType: 'json', //assuming json response - beforeSend: function( xhr, settings ){ - lockSignatureTable(); - }, - context: { - primaryTable: primaryTable, - secondaryTable: secondaryTable - } - }, - data: { - systemId: systemData.id, // additional data to submit - pk: 0 // new data no primary key - }, - error: $.fn.editable.defaults.error, // user default xEditable error function - success: function (data, editableConfig) { - let context = editableConfig.ajaxOptions.context; - let primaryTableApi = context.primaryTable.DataTable(); - let secondaryTableApi = context.secondaryTable.DataTable(); - - unlockSignatureTable(false); - - let newRowElement = addSignatureRow(primaryTableApi, systemData, data.signatures[0], true); - - if(newRowElement){ - // highlight - newRowElement.pulseTableRow('added'); - - // prepare "add signature" table for new entry -> reset ------------------- - let signatureData = formatSignatureData(systemData, [emptySignatureData], emptySignatureOptions); - let newAddRowElement = secondaryTableApi.clear().row.add(signatureData.shift()).draw().nodes(); - - newAddRowElement.to$().makeEditable(secondaryTableApi, systemData); - - Util.showNotify({ - title: 'Signature added', - text: 'Name: ' + data.name, - type: 'success' - }); - } - } - }); - }); - break; - case 'delete': - // delete signature ------------------------------------------------------------------- - let confirmationSettings = { - container: 'body', - placement: 'left', - btnCancelClass: 'btn btn-sm btn-default', - btnCancelLabel: 'cancel', - btnCancelIcon: 'fas fa-fw fa-ban', - title: 'delete signature', - btnOkClass: 'btn btn-sm btn-danger', - btnOkLabel: 'delete', - btnOkIcon: 'fas fa-fw fa-times', - onConfirm: function(e, target){ - // top scroll to top - e.preventDefault(); - - let tableApi = tempTableElement.DataTable(); - - let deleteRowElement = $(target).parents('tr'); - let row = tableApi.rows(deleteRowElement); - deleteSignatures(tableApi, row); - } - }; - - // init confirmation dialog - $(cell).confirmation(confirmationSettings); - - break; - } - - } - } - ] - }); - }; - - /** - * open xEditable input field in "new Signature" table - * @param moduleElement - */ - let focusNewSignatureEditableField = moduleElement => { - let secondaryTable = moduleElement.find('.' + config.sigTableSecondaryClass); - secondaryTable.find('.' + config.sigTableEditSigNameInput).editable('show'); - }; - - /** - * set module observer and look for relevant signature data to update - * @param moduleElement - * @param systemData - */ - let setModuleObserver = (moduleElement, systemData) => { - let primaryTable = moduleElement.find('.' + config.sigTablePrimaryClass); - let primaryTableApi = getDataTableInstanceByModuleElement(moduleElement, 'primary'); - - // add signature toggle --------------------------------------------------------------------------------------- - let toggleAddSignature = (show = 'auto') => { - let button = moduleElement.find('.' + config.moduleHeadlineIconAddClass); - let toolsElement = moduleElement.find('.' + config.tableToolsActionClass); - button.toggleClass('active', show === 'auto' ? undefined : show); - - if(toolsElement.is(':visible') && (!show || show === 'auto')){ - // hide container - toolsElement.velocity('stop').velocity({ - opacity: [0, 1], - height: [0, '70px'] - },{ - duration: 150, - display: 'none' - }); - }else if(!toolsElement.is(':visible') && (show || show === 'auto')){ - // show container - toolsElement.velocity('stop').velocity({ - opacity: [1, 0], - height: ['70px', 0] - },{ - duration: 150, - display: 'block', - complete: function(){ - focusNewSignatureEditableField(moduleElement); - } - }); - }else if(toolsElement.is(':visible') && show){ - // still visible -> no animation - focusNewSignatureEditableField(moduleElement); - } - }; - - moduleElement.find('.' + config.moduleHeadlineIconAddClass).on('click', function(e){ - toggleAddSignature('auto'); - }); - - moduleElement.on('pf:showSystemSignatureModuleAddNew', function(e){ - toggleAddSignature(true); - }); - - // signature reader dialog ------------------------------------------------------------------------------------ - moduleElement.find('.' + config.moduleHeadlineIconReaderClass).on('click', function(e) { - moduleElement.showSignatureReaderDialog(systemData); - }); - - // "lazy update" toggle --------------------------------------------------------------------------------------- - moduleElement.find('.' + config.moduleHeadlineIconLazyClass).on('click', function(e) { - let button = $(this); - button.toggleClass('active'); - }); - - // set multi row select --------------------------------------------------------------------------------------- - primaryTable.on('click', 'tr', {tableApi: primaryTableApi}, function(e){ - if(e.ctrlKey) { - $(this).toggleClass('selected'); - - // check delete button - checkDeleteSignaturesButton(e.data.tableApi); - } - }); - - // draw event for signature table ----------------------------------------------------------------------------- - primaryTableApi.on('draw.dt', function(e, settings){ - // check delete button - let tableApi = $(this).dataTable().api(); - checkDeleteSignaturesButton(tableApi); - }); - - // destroy dataTables event ----------------------------------------------------------------------------------- - primaryTable.on('destroy.dt', function(){ - $(this).destroyTimestampCounter(); - }); - primaryTableApi.on('destroy.dt', function(){ - $(this).destroyTimestampCounter(); - }); - - // event listener for global "paste" signatures into the page ------------------------------------------------- - moduleElement.on('pf:updateSystemSignatureModuleByClipboard', function(e, clipboard){ - // check "lazy update" toggle button - let signatureOptions = { - deleteOld: moduleElement.find('.' + config.moduleHeadlineIconLazyClass).hasClass('active') ? 1 : 0 - }; - - $(this).updateSignatureTableByClipboard(systemData, clipboard, signatureOptions); - }); - - // signature cell - "type" popover ---------------------------------------------------------------------------- - moduleElement.find('.' + config.sigTableClass).hoverIntent({ - over: function(e){ - let staticWormholeElement = $(this); - let wormholeName = staticWormholeElement.attr('data-name'); - let wormholeData = Util.getObjVal(Init, 'wormholes.' + wormholeName); - if(wormholeData){ - staticWormholeElement.addWormholeInfoTooltip(wormholeData, { - trigger: 'manual', - placement: 'top', - show: true - }); - } - }, - out: function(e){ - $(this).destroyPopover(); - }, - selector: '.editable-click:not(.editable-open) span[class^="pf-system-sec-"]' - }); - }; - - /** - * init callback - * @param moduleElement - * @param mapId - * @param systemData - */ - let initModule = (moduleElement, mapId, systemData) => { - unlockSignatureTable(true); - }; - - /** - * get module element - * @param parentElement - * @param mapId - * @param systemData - * @returns {*|jQuery|HTMLElement} - */ - let getModule = function(parentElement, mapId, systemData){ - // create new module container - let moduleElement = $('
').append( - $('
', { - class: config.moduleHeadClass - }).append( - $('
', { - class: config.moduleHandlerClass - }), - $('
', { - text: 'Signatures' - }), - getHeadlineToolbar() - ) - ); - - // scanned signatures progress bar ---------------------------------------------------------------------------- - requirejs(['text!templates/form/progress.html', 'mustache'], (template, Mustache) => { - let data = { - label: true, - wrapperClass: config.signatureScannedProgressBarClass, - class: ['progress-bar-success'].join(' '), - percent: 0 - }; - - let content = Mustache.render(template, data); - - moduleElement.find('.' + config.moduleHeadClass).append(content); - }); - - - - moduleElement.data('mapId', mapId); - moduleElement.data('systemId', systemData.id); - - moduleElement.showLoadingAnimation(); - - // init dataTables - initSignatureDataTable(systemData); - - // draw "new signature" add table - drawSignatureTableNew(moduleElement, mapId, systemData); - - // draw signature table - drawSignatureTable(moduleElement, mapId, systemData); - - // set module observer - setModuleObserver(moduleElement, systemData); - - return moduleElement; - }; - - /** - * before module reDraw callback - */ - let beforeReDraw = () => { - // disable update - lockSignatureTable(); - }; - - /** - * before module destroy callback - */ - let beforeDestroy = (moduleElement) => { - // Destroying the data tables throws - // -> safety remove all dataTables - let mapId = moduleElement.data('mapId'); - let systemId = moduleElement.data('systemId'); - deleteDataTableInstance(mapId, systemId, 'primary'); - deleteDataTableInstance(mapId, systemId, 'secondary'); - }; - - return { - config: config, - getModule: getModule, - initModule: initModule, - beforeReDraw: beforeReDraw, - updateModule: updateModule, - beforeDestroy: beforeDestroy, - getAllSignatureNamesBySystem: getAllSignatureNamesBySystem - }; - -});