/** * 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 data = { id: config.structureDialogId, structureData: structureData, structureStatus: Object.keys(structureStatusData).map((k) => { let data = structureStatusData[k]; data.selected = data.id === Util.getObjVal(structureData, 'status.id'); return data; }), 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: true, 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('shown.bs.modal', function(e) { // init type select live search let selectElementType = $(this).find('#' + config.typeSelectId); selectElementType.initUniverseTypeSelect({ categoryIds: [65], maxSelectionLength: 1, selected: [Util.getObjVal(structureData, 'structure.id')] }); // init corporation select live search let selectElementCorporation = $(this).find('#' + config.corporationSelectId); selectElementCorporation.initUniverseSearch({ categoryNames: ['corporation'], maxSelectionLength: 1 }); $(this).find('#' + config.statusSelectId).select2({ minimumResultsForSearch: -1 }); // init character counter let textarea = $(this).find('#' + config.descriptionTextareaId); let charCounter = $(this).find('.' + config.descriptionTextareaCharCounter); Util.updateCounter(textarea, charCounter, maxDescriptionLength); textarea.on('keyup', function(){ Util.updateCounter($(this), charCounter, maxDescriptionLength); }); // set form validator (after select2 init finish) $(this).find('form').initFormValidation(); }); }); }; /** * 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 }; });
' + '' + '' + group.name + '