- added "lazy update" (ctrl + v) for "intel module", closed #246

- fixed JS bug where signature sync (client side update) failed
This commit is contained in:
Mark Friedrich
2018-05-05 12:24:02 +02:00
parent efd768974f
commit 161c37c256
18 changed files with 621 additions and 244 deletions

View File

@@ -20,30 +20,61 @@ class Structure extends Controller\AccessController {
* @throws Exception
*/
public function save(\Base $f3){
$structureData = (array)$f3->get('POST');
$activeCharacter = $this->getCharacter();
$requestData = (array)$f3->get('POST');
$structuresData = (array)$requestData['structures'];
$return = (object) [];
$return->error = [];
/**
* @var $structure Model\StructureModel
*/
$structure = Model\BasicModel::getNew('StructureModel');
$structure->getById((int)$structureData['id']);
if($structuresData){
$activeCharacter = $this->getCharacter();
if($structure->dry() || $structure->hasAccess($activeCharacter)){
$newStructure = $structure->dry();
try{
$structure->copyfrom($structureData, ['structureId', 'corporationId', 'systemId', 'statusId', 'name', 'description']);
$structure->save();
if($activeCharacter->hasCorporation()){
// structures always belong to a corporation
/**
* @var $structure Model\StructureModel
*/
$structure = Model\BasicModel::getNew('StructureModel');
foreach ($structuresData as $structureData){
// reset on loop start because of potential "continue"
$structure->reset();
if($newStructure){
$activeCharacter->getCorporation()->saveStructure($structure);
if(!empty($structureData['id']) && $structureId = (int)$structureData['id']){
// update specific structure
$structure->getById($structureId);
if( !$structure->hasAccess($activeCharacter) ){
continue;
}
}elseif( !isset($structureData['id']) ){
// from clipboard -> search by structure by name
$structure->getByName($activeCharacter->getCorporation(), (string)$structureData['name']);
}
$newStructure = $structure->dry();
try{
$structure->copyfrom($structureData, ['structureId', 'corporationId', 'systemId', 'statusId', 'name', 'description']);
$structure->save();
if($newStructure){
$activeCharacter->getCorporation()->saveStructure($structure);
}
// group all updated structures by corporation -> just for return
$corporationsStructureData = $structure->getDataByCorporations();
foreach($corporationsStructureData as $corporationId => $corporationStructureData){
if(isset($return->structures[$corporationId])){
$return->structures[$corporationId]['structures'] = array_merge(
$return->structures[$corporationId]['structures'],
$corporationStructureData['structures']
);
}else{
$return->structures[$corporationId] = $corporationStructureData;
}
}
}catch(Exception\ValidationException $e){
$return->error[] = $e->getError();
}
}
$return->structures = $structure->getDataByCorporations();
}catch(Exception\ValidationException $e){
$return->error[] = $e->getError();
}
}

View File

@@ -49,6 +49,7 @@ class StructureModel extends BasicModel {
],
'statusId' => [
'type' => Schema::DT_INT,
'default' => 1,
'index' => true,
'belongs-to-one' => 'Model\StructureStatusModel',
'constraint' => [
@@ -159,6 +160,33 @@ class StructureModel extends BasicModel {
return $valid;
}
/**
* check whether this model is valid or not
* @return bool
*/
public function isValid(): bool {
if($valid = parent::isValid()){
// structure always belongs to a systemId
if(!(int)$this->systemId){
$valid = false;
}
}
return $valid;
}
/**
* Event "Hook" function
* can be overwritten
* return false will stop any further action
* @param BasicModel $self
* @param $pkeys
* @return bool
*/
public function beforeInsertEvent($self, $pkeys){
return $this->isValid() ? parent::beforeInsertEvent($self, $pkeys) : false;
}
/**
* check access by chraacter
* @param CharacterModel $characterModel
@@ -166,9 +194,7 @@ class StructureModel extends BasicModel {
*/
public function hasAccess(CharacterModel $characterModel) : bool {
$access = false;
if($this->dry()){
$access = true;
}elseif($characterModel->hasCorporation()){
if($characterModel->hasCorporation()){
$this->filter('structureCorporations', ['active = ?', 1]);
$this->has('structureCorporations.corporationId', ['active = ?', 1]);
$this->has('structureCorporations.corporationId', ['id = ?', $characterModel->get('corporationId', true)]);
@@ -201,6 +227,16 @@ class StructureModel extends BasicModel {
return $structuresData;
}
public function getByName(CorporationModel $corporation, string $name) {
if( !$corporation->dry() && $name){
$this->has('structureCorporations', ['corporationId = :corporationId', ':corporationId' => $corporation->_id]);
$this->load(['name = :name AND active = :active',
':name' => $name,
':active' => 1
]);
}
}
/**
* get universe type data for structureId
* @param int $structureId

View File

@@ -25,11 +25,11 @@ define([
label: 'Reload tab',
keyNames: ['CONTROL', 'R']
},
signaturePaste: {
clipboardPaste: {
group: 'global',
label: 'Paste signatures from clipboard',
label: 'Update signatures/D-Scan from clipboard',
keyNames: ['CONTROL', 'V'],
alias: 'paste'
alias: 'paste'
},
// map ----------------------------------------------------------------------------------------------

View File

@@ -69,7 +69,8 @@ define([
dynamicElementWrapperId: 'pf-dialog-wrapper',
// system signature module
systemSigModuleClass: 'pf-sig-table-module', // module wrapper (signatures)
systemSignatureModuleClass: 'pf-signature-table-module', // module wrapper (signatures)
systemIntelModuleClass: 'pf-system-intel-module', // module wrapper (intel)
};
let programStatusCounter = 0; // current count down in s until next status change is possible
@@ -137,13 +138,18 @@ define([
location.reload();
});
body.watchKey('signaturePaste', (e) => {
body.watchKey('clipboardPaste', (e) => {
// just send event to the current active map
let activeMap = Util.getMapModule().getActiveMap();
if(activeMap){
// look for active signature module (active system)
let signatureModuleElement = MapUtil.getTabContentElementByMapElement(activeMap).find('.' + config.systemSigModuleClass);
if(signatureModuleElement.length){
let mapContentElement = MapUtil.getTabContentElementByMapElement(activeMap);
let signatureModuleElement = mapContentElement.find('.' + config.systemSignatureModuleClass);
let intelModuleElement = mapContentElement.find('.' + config.systemIntelModuleClass);
if(
signatureModuleElement.length ||
intelModuleElement.length
){
e = e.originalEvent;
let targetElement = $(e.target);
// do not read clipboard if pasting into form elements
@@ -155,6 +161,7 @@ define([
){
let clipboard = (e.originalEvent || e).clipboardData.getData('text/plain');
signatureModuleElement.trigger('pf:updateSystemSignatureModuleByClipboard', [clipboard]);
intelModuleElement.trigger('pf:updateIntelModuleByClipboard', [clipboard]);
}
}
}

View File

@@ -21,6 +21,7 @@ define([
// 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
@@ -28,6 +29,7 @@ define([
// 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
@@ -79,6 +81,13 @@ define([
*/
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) {
@@ -99,6 +108,7 @@ define([
api.nodes().to$().data('animationStatus', 'changed').destroyTimestampCounter();
touchedRows.push(api.id());
notificationCounter.changed++;
}else{
// insert new row
//context.tableApi.row.add(structureData).nodes().to$().data('animationStatus', 'added');
@@ -106,6 +116,7 @@ define([
api.nodes().to$().data('animationStatus', 'added');
touchedRows.push(api.id());
notificationCounter.added++;
}
}
}
@@ -114,10 +125,20 @@ define([
}
if(context.removeMissing){
context.tableApi.rows((idx, data, node) => !touchedRows.includes(node.id)).remove();
notificationCounter.deleted += context.tableApi.rows((idx, data, node) => !touchedRows.includes(node.id)).remove().ids().count();
}
context.tableApi.draw();
// show notification ------------------------------------------------------------------------------------------
let notification = '';
notification += notificationCounter.added > 0 ? notificationCounter.added + ' added<br>' : '';
notification += notificationCounter.changed > 0 ? notificationCounter.changed + ' changed<br>' : '';
notification += notificationCounter.deleted > 0 ? notificationCounter.deleted + ' deleted<br>' : '';
if(hadData && notification.length){
Util.showNotify({title: 'Structures updated', text: notification, type: 'success'});
}
};
/**
@@ -127,14 +148,20 @@ define([
*/
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'});
}
};
@@ -215,6 +242,7 @@ define([
data.selected = data.id === Util.getObjVal(structureData, 'status.id');
return data;
}),
statusSelectId: config.statusSelectId,
typeSelectId: config.typeSelectId,
corporationSelectId: config.corporationSelectId,
descriptionTextareaId: config.descriptionTextareaId,
@@ -254,7 +282,9 @@ define([
formData.corporationId = Util.getObjVal(formData, 'corporationId') | 0;
formData.systemId = systemId | 0;
saveStructureData(formData,{
saveStructureData({
structures: [formData]
}, {
moduleElement: moduleElement,
tableApi: tableApi
}, callbackUpdateStructureRows);
@@ -282,6 +312,10 @@ define([
maxSelectionLength: 1
});
$(this).find('#' + config.statusSelectId).select2({
minimumResultsForSearch: -1
});
// init character counter
let textarea = $(this).find('#' + config.descriptionTextareaId);
let charCounter = $(this).find('.' + config.descriptionTextareaCharCounter);
@@ -298,9 +332,53 @@ define([
});
};
/**
* 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: '<i class="fas fa-fw fa-paste fa-fw"></i>&nbsp;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
* @returns {*}
* @param parentElement
* @param mapId
* @param systemData
* @returns {jQuery}
*/
let getModule = (parentElement, mapId, systemData) => {
let corporationId = Util.getCurrentUserInfo('corporationId');
@@ -320,6 +398,10 @@ define([
class: ['fas', 'fa-fw', 'fa-plus', config.moduleHeadlineIconClass, config.moduleHeadlineIconAddClass].join(' '),
title: 'add'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
$('<i>', {
class: ['fas', 'fa-fw', 'fa-paste', config.moduleHeadlineIconClass, config.moduleHeadlineIconReaderClass].join(' '),
title: 'D-Scan&nbsp;reader'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
$('<i>', {
class: ['fas', 'fa-fw', 'fa-sync', config.moduleHeadlineIconClass, config.moduleHeadlineIconRefreshClass].join(' '),
title: 'refresh&nbsp;all'
@@ -388,8 +470,8 @@ define([
}
},{
targets: 2,
title: 'structure',
width: 50,
title: 'type',
width: 30,
className: [config.tableCellEllipsisClass].join(' '),
data: 'structure.name',
defaultContent: '<i class="fas fa-question txt-color txt-color-orangeDark"></i>',
@@ -588,6 +670,76 @@ define([
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);
}
};
/**
* init intel module
* @param moduleElement
@@ -604,6 +756,11 @@ define([
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({
@@ -615,6 +772,14 @@ define([
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 {

View File

@@ -3,7 +3,7 @@ define([
'app/init',
'app/util',
'morris'
], function($, Init, Util, Morris) {
], ($, Init, Util, Morris) => {
'use strict';
let config = {
@@ -38,7 +38,7 @@ define([
* @param options
* @returns {jQuery}
*/
let getLabel = function(text, options){
let getLabel = (text, options) => {
let label = $('<span>', {
class: ['label', options.type, options.align].join(' ')
}).text( text );
@@ -51,7 +51,7 @@ define([
* @param moduleElement
* @param killboardData
*/
let showKillmails = function(moduleElement, killboardData){
let showKillmails = (moduleElement, killboardData) => {
// show number of killMails
let killMailCounterMax = 20;
@@ -389,7 +389,7 @@ define([
* minify the killboard graph element e.g. if no kills where found, or on error
* @param killboardGraphElement
*/
let minifyKillboardGraphElement = function(killboardGraphElement){
let minifyKillboardGraphElement = (killboardGraphElement) => {
killboardGraphElement.velocity({
height: '20px',
marginBottom: '0px'
@@ -438,8 +438,9 @@ define([
/**
* get module element
* @param parentElement
* @param mapId
* @param systemData
* @returns {*|jQuery|HTMLElement}
* @returns {jQuery}
*/
let getModule = (parentElement, mapId, systemData) => {
// create new module container

View File

@@ -23,15 +23,12 @@ define([
moduleClass: 'pf-module', // class for each module
// system signature module
moduleTypeClass: 'pf-sig-table-module', // module wrapper
moduleTypeClass: 'pf-signature-table-module', // module wrapper
// tables
tableToolsClass: 'pf-table-tools', // class for table toolbar
tableToolsActionClass: 'pf-table-tools-action', // class for table toolbar action
// dialogs
signatureReaderDialogId: 'pf-signature-reader-dialog', // id for signature reader dialog
// signature progress bar
signatureScannedProgressBarClass: 'pf-system-progress-scanned', // class for signature progress bar
@@ -340,10 +337,11 @@ define([
// and add "new" row
let changedRowElement = addSignatureRow(signatureTableApi, currentSystemData.systemData, signatureData[i], false);
// highlight
changedRowElement.pulseTableRow('changed');
notificationCounter.changed++;
if(changedRowElement){
// highlight
changedRowElement.pulseTableRow('changed');
notificationCounter.changed++;
}
}
// remove signature data -> all left signatures will be added
@@ -383,13 +381,13 @@ define([
// and add "new" row
let newRowElement = addSignatureRow(signatureTableApi, currentSystemData.systemData, signatureData[k], false);
// highlight
newRowElement.pulseTableRow('added');
notificationCounter.added++;
if(newRowElement){
// highlight
newRowElement.pulseTableRow('added');
notificationCounter.added++;
}
}
// show notification ------------------------------------------------------------------------------------------
if(
notificationCounter.added > 0 ||
@@ -399,9 +397,8 @@ define([
// update signature bar
moduleElement.updateScannedSignaturesBar({showNotice: true});
// show Notification
let notification = notificationCounter.added + ' added<br>';
notification += notificationCounter.changed + ' changed<br>';
notification += notificationCounter.changed + ' updated<br>';
notification += notificationCounter.deleted + ' deleted<br>';
Util.showNotify({title: 'Signatures updated', text: notification, type: 'success'});
@@ -499,7 +496,6 @@ define([
Util.showNotify({title: 'System is scanned', text: notification, type: 'success'});
}
}
}, 100);
};
@@ -508,36 +504,23 @@ define([
* @param systemData
*/
$.fn.showSignatureReaderDialog = function(systemData){
let moduleElement = $(this);
let data = {
id: config.signatureReaderDialogId
};
requirejs(['text!templates/dialog/signature_reader.html', 'mustache'], function(template, Mustache) {
let content = Mustache.render(template, data);
requirejs(['text!templates/dialog/signature_reader.html', 'mustache'], (template, Mustache) => {
let signatureReaderDialog = bootbox.dialog({
title: 'Signature reader',
message: content,
message: Mustache.render(template, {}),
buttons: {
close: {
label: 'cancel',
className: 'btn-default',
callback: function(){
$(signatureReaderDialog).modal('hide');
}
className: 'btn-default'
},
success: {
label: '<i class="fas fa-paste fa-fw"></i>&nbsp;update signatures',
className: 'btn-success',
callback: function () {
// get form Values
let form = $('#' + config.signatureReaderDialogId).find('form');
let formData = $(form).getFormValues();
let form = this.find('form');
let formData = form.getFormValues();
let signatureOptions = {
deleteOld: (formData.deleteOld) ? 1 : 0
};
@@ -554,8 +537,6 @@ define([
// set focus on sig-input textarea
signatureReaderDialog.find('textarea').focus();
});
});
};
@@ -607,10 +588,8 @@ define([
// check if copy&paste is enabled
if( !disableCopyFromClipboard ){
// parse input stream
let signatureData = parseSignatureString(systemData, clipboard);
if(signatureData.length > 0){
// valid signature data parsed
@@ -641,7 +620,7 @@ define([
};
/**
* parses a copy&paste string from ingame scanning window and parses it
* parses a copy&paste string from ingame scanning window
* @param systemData
* @param clipboard
* @returns {Array}
@@ -656,9 +635,7 @@ define([
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){
@@ -702,8 +679,6 @@ define([
}else{
invalidSignatures++;
}
}else{
invalidSignatures++;
}
}
@@ -875,7 +850,7 @@ define([
* @param options
* @returns {*|jQuery}
*/
let getLabledButton = function(options){
let getLabeledButton = function(options){
let buttonClasses = ['btn', 'btn-sm', 'btn-labeled'];
@@ -1002,7 +977,7 @@ define([
let tableToolbar = $('<div>', {
class: config.tableToolsClass
}).append(
getLabledButton({
getLabeledButton({
type: 'primary',
label: 'add',
icon: 'fa-plus',
@@ -1026,7 +1001,7 @@ define([
}
})
).append(
getLabledButton({
getLabeledButton({
type: 'primary',
label: 'signature reader',
icon: 'fa-paste',
@@ -1035,7 +1010,7 @@ define([
}
})
).append(
getLabledButton({
getLabeledButton({
type: 'default',
label: 'select all',
icon: 'fa-check-square',
@@ -1063,7 +1038,7 @@ define([
value: 1,
}).attr('data-toggle', 'toggle')
).append(
getLabledButton({
getLabeledButton({
type: 'danger',
classes: [config.sigTableClearButtonClass, 'pull-right'],
label: 'delete',
@@ -1812,7 +1787,7 @@ define([
// update connection conflicts
checkConnectionConflicts();
Util.showNotify({title: 'Signature deleted', text: signatureCount + ' signatures deleted', type: 'success'});
Util.showNotify({title: 'Signature deleted', text: signatureCount + ' deleted', type: 'success'});
}
};
@@ -1843,25 +1818,25 @@ define([
* @param animate
* @returns {*}
*/
let addSignatureRow = function(signatureTableApi, systemData, signatureData, animate){
let newSignatureData = formatSignatureData(systemData, [signatureData], fullSignatureOptions);
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$();
let newRowNode = signatureTableApi.row.add(newSignatureData.shift()).draw().nodes();
let newRowElement = newRowNode.to$();
if(animate === true){
newRowElement.hide();
newRowElement.toggleTableRow(function(newRowElement){
// make new row editable
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);
// update scan progress bar
newRowElement.parents('.' + config.moduleClass).updateScannedSignaturesBar({showNotice: true});
});
}else{
newRowElement.makeEditable(signatureTableApi, systemData);
}
}
return newRowElement;
@@ -2270,20 +2245,22 @@ define([
let newRowElement = addSignatureRow(primaryTableApi, systemData, data.signatures[0], true);
// highlight
newRowElement.pulseTableRow('added');
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();
// 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);
newAddRowElement.to$().makeEditable(secondaryTableApi, systemData);
Util.showNotify({
title: 'Signature added',
text: 'Name: ' + data.name,
type: 'success'
});
Util.showNotify({
title: 'Signature added',
text: 'Name: ' + data.name,
type: 'success'
});
}
}
});
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -25,11 +25,11 @@ define([
label: 'Reload tab',
keyNames: ['CONTROL', 'R']
},
signaturePaste: {
clipboardPaste: {
group: 'global',
label: 'Paste signatures from clipboard',
label: 'Update signatures/D-Scan from clipboard',
keyNames: ['CONTROL', 'V'],
alias: 'paste'
alias: 'paste'
},
// map ----------------------------------------------------------------------------------------------

View File

@@ -69,7 +69,8 @@ define([
dynamicElementWrapperId: 'pf-dialog-wrapper',
// system signature module
systemSigModuleClass: 'pf-sig-table-module', // module wrapper (signatures)
systemSignatureModuleClass: 'pf-signature-table-module', // module wrapper (signatures)
systemIntelModuleClass: 'pf-system-intel-module', // module wrapper (intel)
};
let programStatusCounter = 0; // current count down in s until next status change is possible
@@ -137,13 +138,18 @@ define([
location.reload();
});
body.watchKey('signaturePaste', (e) => {
body.watchKey('clipboardPaste', (e) => {
// just send event to the current active map
let activeMap = Util.getMapModule().getActiveMap();
if(activeMap){
// look for active signature module (active system)
let signatureModuleElement = MapUtil.getTabContentElementByMapElement(activeMap).find('.' + config.systemSigModuleClass);
if(signatureModuleElement.length){
let mapContentElement = MapUtil.getTabContentElementByMapElement(activeMap);
let signatureModuleElement = mapContentElement.find('.' + config.systemSignatureModuleClass);
let intelModuleElement = mapContentElement.find('.' + config.systemIntelModuleClass);
if(
signatureModuleElement.length ||
intelModuleElement.length
){
e = e.originalEvent;
let targetElement = $(e.target);
// do not read clipboard if pasting into form elements
@@ -155,6 +161,7 @@ define([
){
let clipboard = (e.originalEvent || e).clipboardData.getData('text/plain');
signatureModuleElement.trigger('pf:updateSystemSignatureModuleByClipboard', [clipboard]);
intelModuleElement.trigger('pf:updateIntelModuleByClipboard', [clipboard]);
}
}
}

View File

@@ -21,6 +21,7 @@ define([
// 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
@@ -28,6 +29,7 @@ define([
// 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
@@ -79,6 +81,13 @@ define([
*/
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) {
@@ -99,6 +108,7 @@ define([
api.nodes().to$().data('animationStatus', 'changed').destroyTimestampCounter();
touchedRows.push(api.id());
notificationCounter.changed++;
}else{
// insert new row
//context.tableApi.row.add(structureData).nodes().to$().data('animationStatus', 'added');
@@ -106,20 +116,29 @@ define([
api.nodes().to$().data('animationStatus', 'added');
touchedRows.push(api.id());
notificationCounter.added++;
}
}
}
}
}
}
console.log(touchedRows)
if(context.removeMissing){
context.tableApi.rows((idx, data, node) => !touchedRows.includes(node.id)).remove();
notificationCounter.deleted += context.tableApi.rows((idx, data, node) => !touchedRows.includes(node.id)).remove().ids().count();
}
context.tableApi.draw();
// show notification ------------------------------------------------------------------------------------------
let notification = '';
notification += notificationCounter.added > 0 ? notificationCounter.added + ' added<br>' : '';
notification += notificationCounter.changed > 0 ? notificationCounter.changed + ' changed<br>' : '';
notification += notificationCounter.deleted > 0 ? notificationCounter.deleted + ' deleted<br>' : '';
if(hadData && notification.length){
Util.showNotify({title: 'Structures updated', text: notification, type: 'success'});
}
};
/**
@@ -129,14 +148,20 @@ define([
*/
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'});
}
};
@@ -217,6 +242,7 @@ define([
data.selected = data.id === Util.getObjVal(structureData, 'status.id');
return data;
}),
statusSelectId: config.statusSelectId,
typeSelectId: config.typeSelectId,
corporationSelectId: config.corporationSelectId,
descriptionTextareaId: config.descriptionTextareaId,
@@ -256,7 +282,9 @@ define([
formData.corporationId = Util.getObjVal(formData, 'corporationId') | 0;
formData.systemId = systemId | 0;
saveStructureData(formData,{
saveStructureData({
structures: [formData]
}, {
moduleElement: moduleElement,
tableApi: tableApi
}, callbackUpdateStructureRows);
@@ -284,6 +312,10 @@ define([
maxSelectionLength: 1
});
$(this).find('#' + config.statusSelectId).select2({
minimumResultsForSearch: -1
});
// init character counter
let textarea = $(this).find('#' + config.descriptionTextareaId);
let charCounter = $(this).find('.' + config.descriptionTextareaCharCounter);
@@ -300,9 +332,53 @@ define([
});
};
/**
* 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: '<i class="fas fa-fw fa-paste fa-fw"></i>&nbsp;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
* @returns {*}
* @param parentElement
* @param mapId
* @param systemData
* @returns {jQuery}
*/
let getModule = (parentElement, mapId, systemData) => {
let corporationId = Util.getCurrentUserInfo('corporationId');
@@ -322,6 +398,10 @@ define([
class: ['fas', 'fa-fw', 'fa-plus', config.moduleHeadlineIconClass, config.moduleHeadlineIconAddClass].join(' '),
title: 'add'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
$('<i>', {
class: ['fas', 'fa-fw', 'fa-paste', config.moduleHeadlineIconClass, config.moduleHeadlineIconReaderClass].join(' '),
title: 'D-Scan&nbsp;reader'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
$('<i>', {
class: ['fas', 'fa-fw', 'fa-sync', config.moduleHeadlineIconClass, config.moduleHeadlineIconRefreshClass].join(' '),
title: 'refresh&nbsp;all'
@@ -390,8 +470,8 @@ define([
}
},{
targets: 2,
title: 'structure',
width: 50,
title: 'type',
width: 30,
className: [config.tableCellEllipsisClass].join(' '),
data: 'structure.name',
defaultContent: '<i class="fas fa-question txt-color txt-color-orangeDark"></i>',
@@ -590,6 +670,76 @@ define([
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);
}
};
/**
* init intel module
* @param moduleElement
@@ -606,6 +756,11 @@ define([
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({
@@ -617,6 +772,14 @@ define([
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 {

View File

@@ -3,7 +3,7 @@ define([
'app/init',
'app/util',
'morris'
], function($, Init, Util, Morris) {
], ($, Init, Util, Morris) => {
'use strict';
let config = {
@@ -38,7 +38,7 @@ define([
* @param options
* @returns {jQuery}
*/
let getLabel = function(text, options){
let getLabel = (text, options) => {
let label = $('<span>', {
class: ['label', options.type, options.align].join(' ')
}).text( text );
@@ -51,7 +51,7 @@ define([
* @param moduleElement
* @param killboardData
*/
let showKillmails = function(moduleElement, killboardData){
let showKillmails = (moduleElement, killboardData) => {
// show number of killMails
let killMailCounterMax = 20;
@@ -389,7 +389,7 @@ define([
* minify the killboard graph element e.g. if no kills where found, or on error
* @param killboardGraphElement
*/
let minifyKillboardGraphElement = function(killboardGraphElement){
let minifyKillboardGraphElement = (killboardGraphElement) => {
killboardGraphElement.velocity({
height: '20px',
marginBottom: '0px'
@@ -438,8 +438,9 @@ define([
/**
* get module element
* @param parentElement
* @param mapId
* @param systemData
* @returns {*|jQuery|HTMLElement}
* @returns {jQuery}
*/
let getModule = (parentElement, mapId, systemData) => {
// create new module container

View File

@@ -23,15 +23,12 @@ define([
moduleClass: 'pf-module', // class for each module
// system signature module
moduleTypeClass: 'pf-sig-table-module', // module wrapper
moduleTypeClass: 'pf-signature-table-module', // module wrapper
// tables
tableToolsClass: 'pf-table-tools', // class for table toolbar
tableToolsActionClass: 'pf-table-tools-action', // class for table toolbar action
// dialogs
signatureReaderDialogId: 'pf-signature-reader-dialog', // id for signature reader dialog
// signature progress bar
signatureScannedProgressBarClass: 'pf-system-progress-scanned', // class for signature progress bar
@@ -340,10 +337,11 @@ define([
// and add "new" row
let changedRowElement = addSignatureRow(signatureTableApi, currentSystemData.systemData, signatureData[i], false);
// highlight
changedRowElement.pulseTableRow('changed');
notificationCounter.changed++;
if(changedRowElement){
// highlight
changedRowElement.pulseTableRow('changed');
notificationCounter.changed++;
}
}
// remove signature data -> all left signatures will be added
@@ -383,13 +381,13 @@ define([
// and add "new" row
let newRowElement = addSignatureRow(signatureTableApi, currentSystemData.systemData, signatureData[k], false);
// highlight
newRowElement.pulseTableRow('added');
notificationCounter.added++;
if(newRowElement){
// highlight
newRowElement.pulseTableRow('added');
notificationCounter.added++;
}
}
// show notification ------------------------------------------------------------------------------------------
if(
notificationCounter.added > 0 ||
@@ -399,9 +397,8 @@ define([
// update signature bar
moduleElement.updateScannedSignaturesBar({showNotice: true});
// show Notification
let notification = notificationCounter.added + ' added<br>';
notification += notificationCounter.changed + ' changed<br>';
notification += notificationCounter.changed + ' updated<br>';
notification += notificationCounter.deleted + ' deleted<br>';
Util.showNotify({title: 'Signatures updated', text: notification, type: 'success'});
@@ -499,7 +496,6 @@ define([
Util.showNotify({title: 'System is scanned', text: notification, type: 'success'});
}
}
}, 100);
};
@@ -508,36 +504,23 @@ define([
* @param systemData
*/
$.fn.showSignatureReaderDialog = function(systemData){
let moduleElement = $(this);
let data = {
id: config.signatureReaderDialogId
};
requirejs(['text!templates/dialog/signature_reader.html', 'mustache'], function(template, Mustache) {
let content = Mustache.render(template, data);
requirejs(['text!templates/dialog/signature_reader.html', 'mustache'], (template, Mustache) => {
let signatureReaderDialog = bootbox.dialog({
title: 'Signature reader',
message: content,
message: Mustache.render(template, {}),
buttons: {
close: {
label: 'cancel',
className: 'btn-default',
callback: function(){
$(signatureReaderDialog).modal('hide');
}
className: 'btn-default'
},
success: {
label: '<i class="fas fa-paste fa-fw"></i>&nbsp;update signatures',
className: 'btn-success',
callback: function () {
// get form Values
let form = $('#' + config.signatureReaderDialogId).find('form');
let formData = $(form).getFormValues();
let form = this.find('form');
let formData = form.getFormValues();
let signatureOptions = {
deleteOld: (formData.deleteOld) ? 1 : 0
};
@@ -554,8 +537,6 @@ define([
// set focus on sig-input textarea
signatureReaderDialog.find('textarea').focus();
});
});
};
@@ -607,10 +588,8 @@ define([
// check if copy&paste is enabled
if( !disableCopyFromClipboard ){
// parse input stream
let signatureData = parseSignatureString(systemData, clipboard);
if(signatureData.length > 0){
// valid signature data parsed
@@ -641,7 +620,7 @@ define([
};
/**
* parses a copy&paste string from ingame scanning window and parses it
* parses a copy&paste string from ingame scanning window
* @param systemData
* @param clipboard
* @returns {Array}
@@ -656,9 +635,7 @@ define([
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){
@@ -702,8 +679,6 @@ define([
}else{
invalidSignatures++;
}
}else{
invalidSignatures++;
}
}
@@ -875,7 +850,7 @@ define([
* @param options
* @returns {*|jQuery}
*/
let getLabledButton = function(options){
let getLabeledButton = function(options){
let buttonClasses = ['btn', 'btn-sm', 'btn-labeled'];
@@ -1002,7 +977,7 @@ define([
let tableToolbar = $('<div>', {
class: config.tableToolsClass
}).append(
getLabledButton({
getLabeledButton({
type: 'primary',
label: 'add',
icon: 'fa-plus',
@@ -1026,7 +1001,7 @@ define([
}
})
).append(
getLabledButton({
getLabeledButton({
type: 'primary',
label: 'signature reader',
icon: 'fa-paste',
@@ -1035,7 +1010,7 @@ define([
}
})
).append(
getLabledButton({
getLabeledButton({
type: 'default',
label: 'select all',
icon: 'fa-check-square',
@@ -1063,7 +1038,7 @@ define([
value: 1,
}).attr('data-toggle', 'toggle')
).append(
getLabledButton({
getLabeledButton({
type: 'danger',
classes: [config.sigTableClearButtonClass, 'pull-right'],
label: 'delete',
@@ -1812,7 +1787,7 @@ define([
// update connection conflicts
checkConnectionConflicts();
Util.showNotify({title: 'Signature deleted', text: signatureCount + ' signatures deleted', type: 'success'});
Util.showNotify({title: 'Signature deleted', text: signatureCount + ' deleted', type: 'success'});
}
};
@@ -1843,25 +1818,25 @@ define([
* @param animate
* @returns {*}
*/
let addSignatureRow = function(signatureTableApi, systemData, signatureData, animate){
let newSignatureData = formatSignatureData(systemData, [signatureData], fullSignatureOptions);
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$();
let newRowNode = signatureTableApi.row.add(newSignatureData.shift()).draw().nodes();
let newRowElement = newRowNode.to$();
if(animate === true){
newRowElement.hide();
newRowElement.toggleTableRow(function(newRowElement){
// make new row editable
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);
// update scan progress bar
newRowElement.parents('.' + config.moduleClass).updateScannedSignaturesBar({showNotice: true});
});
}else{
newRowElement.makeEditable(signatureTableApi, systemData);
}
}
return newRowElement;
@@ -2270,20 +2245,22 @@ define([
let newRowElement = addSignatureRow(primaryTableApi, systemData, data.signatures[0], true);
// highlight
newRowElement.pulseTableRow('added');
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();
// 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);
newAddRowElement.to$().makeEditable(secondaryTableApi, systemData);
Util.showNotify({
title: 'Signature added',
text: 'Name: ' + data.name,
type: 'success'
});
Util.showNotify({
title: 'Signature added',
text: 'Name: ' + data.name,
type: 'success'
});
}
}
});
});

View File

@@ -0,0 +1,16 @@
<form role="form" class="form-horizontal">
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-2 control-label" for="form-dscan">D-scan</label>
<div class="col-sm-10">
<textarea style="resize: vertical" rows="3" id="form-dscan" name="clipboard" class="form-control custom-scroll" autofocus></textarea>
<span class="help-block">
Copy and paste D-Scan result. Press <kbd>ctrl</kbd> + <kbd>c</kbd> (copy)
then <kbd>ctrl</kbd> + <kbd>v</kbd> (paste).<br> This tool can add and update structures.
</span>
</div>
</div>
</div>
</div>
</form>

View File

@@ -1,34 +1,30 @@
<div id="{{id}}">
<form role="form" class="form-horizontal">
<form role="form" class="form-horizontal">
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-2 control-label" for="form_result">Scan result</label>
<div class="col-sm-10">
<textarea style="resize: vertical" rows="3" id="form_result" name="clipboard" class="form-control custom-scroll" autofocus></textarea>
<span class="help-block">
Copy and paste signatures from your probe scanning window.
Hit <kbd>ctrl</kbd> + <kbd>c</kbd> (copy)
then <kbd>ctrl</kbd> + <kbd>v</kbd> (paste). This tool can add and update signatures.
</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-2 control-label" for="form_result">Scan result</label>
<div class="col-sm-10">
<textarea style="resize: vertical" rows="3" id="form_result" name="clipboard" class="form-control custom-scroll" autofocus></textarea>
<span class="help-block">
Copy and paste signatures from your probe scanning window.
Hit <kbd>ctrl</kbd> + <kbd>c</kbd> (copy)
then <kbd>ctrl</kbd> + <kbd>v</kbd> (paste). This tool can add and update signatures.
</span>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10 col-xs-9">
<div class="checkbox checkbox-warning">
<input id="form_delete" name="deleteOld" value="1" type="checkbox">
<label for="form_delete" title="old signatures will be removed. Are you sure?">Delete old</label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10 col-xs-9">
<div class="checkbox checkbox-warning">
<input id="form_delete" name="deleteOld" value="1" type="checkbox">
<label for="form_delete" title="old signatures will be removed. Are you sure?">Delete old</label>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</form>

View File

@@ -7,15 +7,15 @@
<label for="form_name" class="col-sm-2 control-label">Name</label>
<div class="col-sm-10">
<input name="name" type="text" class="form-control" id="form_name" value="{{structureData.name}}" data-error="Name required" data-minlength="3" data-minlength-error="Min. of 3 characters" required>
<span class="note help-block with-errors">Choose a meaningful name</span>
<span class="note help-block with-errors"></span>
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label for="form_status" class="col-sm-4 control-label">Status</label>
<label for="{{statusSelectId}}" class="col-sm-4 control-label">Status</label>
<div class="col-sm-8">
<select name="statusId" id="form_status" class="form-control">
<select name="statusId" id="{{statusSelectId}}" class="form-control pf-select2">
{{#structureStatus}}
<option value="{{id}}" {{#selected}}selected{{/selected}}>{{name}}</option>
{{/structureStatus}}

View File

@@ -32,7 +32,7 @@
}
// signature table module ==================================================
.pf-sig-table-module{
.pf-signature-table-module{
// delete signature button
.pf-sig-table-clear-button{