Merge pull request #64 from exodus4d/develop

v0.0.15
This commit is contained in:
Exodus 4D
2015-11-11 20:38:46 +01:00
53 changed files with 805 additions and 200 deletions

View File

@@ -6,14 +6,17 @@ DEBUG = 0
; If TRUE, the framework, after having logged stack trace and errors, stops execution (die without any status) when a non-fatal error is detected.
HALT = FALSE
ONERROR = "Controller\Controller->showError"
; Timezone to use. Sync program with eve server time
TZ = "UTC"
; Cache backend. Can handle Memcache module, APC, WinCache, XCache and a filesystem-based cache.
CACHE = TRUE
; Callback functions ===================================================================================
ONERROR = "Controller\Controller->showError"
UNLOAD = "Controller\Controller->unload"
; Path configurations ==================================================================================
; relative to "BASE" dir

View File

@@ -141,6 +141,139 @@ class Map extends \Controller\AccessController {
echo json_encode($initData);
}
/**
* import new map data
* @param $f3
*/
public function import($f3){
$importData = (array)$f3->get('POST');
$return = (object) [];
$return->error = [];
if(
isset($importData['typeId']) &&
count($importData['mapData']) > 0
){
$user = $this->_getUser();
if($user){
$activeCharacter = $user->getActiveUserCharacter();
$map = Model\BasicModel::getNew('MapModel');
$system = Model\BasicModel::getNew('SystemModel');
$connection = Model\BasicModel::getNew('ConnectionModel');
foreach($importData['mapData'] as $mapData){
if(
isset($mapData['config']) &&
isset($mapData['data'])
){
if(
isset($mapData['data']['systems']) &&
isset($mapData['data']['connections'])
){
if(isset($mapData['config']['id'])){
unset($mapData['config']['id']);
}
$map->setData($mapData['config']);
$map->typeId = (int)$importData['typeId'];
$map->save();
// new system IDs will be generated
// therefore we need to temp store a mapping between IDs
$tempSystemIdMapping = [];
foreach($mapData['data']['systems'] as $systemData){
if(isset($systemData['id'])){
$oldId = (int)$systemData['id'];
unset($systemData['id']);
$system->setData($systemData);
$system->mapId = $map;
$system->createdCharacterId = $activeCharacter->characterId;
$system->updatedCharacterId = $activeCharacter->characterId;
$system->save();
$tempSystemIdMapping[$oldId] = $system->id;
$system->reset();
}
}
foreach($mapData['data']['connections'] as $connectionData){
// check if source and target IDs match with new system ID
if(
isset( $tempSystemIdMapping[$connectionData['source']] ) &&
isset( $tempSystemIdMapping[$connectionData['target']] )
){
if(isset($connectionData['id'])){
unset($connectionData['id']);
}
$connection->setData($connectionData);
$connection->mapId = $map;
$connection->source = $tempSystemIdMapping[$connectionData['source']];
$connection->target = $tempSystemIdMapping[$connectionData['target']];
$connection->save();
$connection->reset();
}
}
// map access info should not automatically imported
if($map->isPrivate()){
$map->setAccess($user);
}elseif($map->isCorporation()){
$corporation = $activeCharacter->getCharacter()->getCorporation();
if($corporation){
$map->setAccess($corporation);
}
}elseif($map->isAlliance()){
$alliance = $activeCharacter->getCharacter()->getAlliance();
if($alliance){
$map->setAccess($alliance);
}
}
}else{
// systems || connections missing
$missingConfigError = (object) [];
$missingConfigError->type = 'error';
$missingConfigError->message = 'Map data not valid (systems || connections) missing';
$return->error[] = $missingConfigError;
}
}else{
// map config || systems/connections missing
$missingConfigError = (object) [];
$missingConfigError->type = 'error';
$missingConfigError->message = 'Map data not valid (config || data) missing';
$return->error[] = $missingConfigError;
}
$map->reset();
}
}else{
// user not found
$return->error[] = $this->getUserLoggedOffError();
}
}else{
// map data missing
$missingDataError = (object) [];
$missingDataError->type = 'error';
$missingDataError->message = 'Map data missing';
$return->error[] = $missingDataError;
}
echo json_encode($return);
}
/**
* save a new map or update an existing map
* @param $f3
@@ -337,7 +470,7 @@ class Map extends \Controller\AccessController {
$return->error = [];
if($user){
// -> get active user object
// -> get active character
$activeCharacter = $user->getActiveUserCharacter();
$cacheKey = 'user_map_data_' . $activeCharacter->id;
@@ -383,10 +516,10 @@ class Map extends \Controller\AccessController {
foreach($systems as $i => $systemData){
// check if current system belongs to the current map
$map->filter('systems', array('id = ?', $systemData['id'] ));
$map->filter('systems', ['id = ?', $systemData['id'] ]);
$filteredMap = $map->find(
array('id = ?', $map->id ),
array('limit' => 1)
['id = ?', $map->id ],
['limit' => 1]
);
// this should never fail
@@ -412,10 +545,10 @@ class Map extends \Controller\AccessController {
foreach($connections as $i => $connectionData){
// check if the current connection belongs to the current map
$map->filter('connections', array('id = ?', $connectionData['id'] ));
$map->filter('connections', ['id = ?', $connectionData['id'] ]);
$filteredMap = $map->find(
array('id = ?', $map->id ),
array('limit' => 1)
['id = ?', $map->id ],
['limit' => 1]
);
// this should never fail

View File

@@ -217,14 +217,14 @@ class Route extends \Controller\AccessController {
SELECT
GROUP_CONCAT( NULLIF(map_sys_inner.solarSystemName, NULL) SEPARATOR ':')
FROM
mapsolarsystemjumps map_jump INNER JOIN
mapsolarsystems map_sys_inner ON
mapSolarSystemJumps map_jump INNER JOIN
mapSolarSystems map_sys_inner ON
map_sys_inner.solarSystemID = map_jump.toSolarSystemID
WHERE
map_jump.fromSolarSystemID = map_sys.solarSystemID
) system_neighbours
FROM
mapsolarsystems map_sys
mapSolarSystems map_sys
HAVING
-- skip systems without neighbors (e.g. WHs)
system_neighbours IS NOT NULL

View File

@@ -26,8 +26,8 @@ class System extends \Controller\AccessController {
SELECT
LOWER( system_effect.typeName )
FROM
invtypes system_effect INNER JOIN
mapdenormalize map_norm ON
invTypes system_effect INNER JOIN
mapDenormalize map_norm ON
map_norm.typeID = system_effect.typeID
WHERE
system_effect.groupID = 995 AND
@@ -39,16 +39,16 @@ class System extends \Controller\AccessController {
SELECT
map_worm_class.wormholeClassID system_class
FROM
maplocationwormholeclasses map_worm_class
mapLocationWormholeClasses map_worm_class
WHERE
map_worm_class.locationID = map_sys.regionID
LIMIT 1
), 7) security
FROM
mapsolarsystems map_sys INNER JOIN
mapconstellations map_con ON
mapSolarSystems map_sys INNER JOIN
mapConstellations map_con ON
map_con.constellationID = map_sys.constellationID INNER JOIN
mapregions map_reg ON
mapRegions map_reg ON
map_reg.regionID = map_sys.regionID";
private $whereQuery = "";

View File

@@ -175,7 +175,7 @@ class Controller {
$data = (object) [];
$data->trusted = false;
$data->values = [];
$headerData = apache_request_headers();
$headerData = self::getRequestHeaders();
foreach($headerData as $key => $value){
$key = strtolower($key);
@@ -199,6 +199,33 @@ class Controller {
return $data;
}
/**
* Helper function to return all headers because
* getallheaders() is not available under nginx
*
* @return array (string $key -> string $value)
*/
static function getRequestHeaders(){
$headers = [];
if(function_exists('apache_request_headers') ){
// Apache Webserver
$headers = apache_request_headers();
}else{
// Other webserver, e.g. nginx
// Unfortunately this "fallback" does not work for me (Apache)
// Therefore we can´t use this for all servers
// https://github.com/exodus4d/pathfinder/issues/58
foreach($_SERVER as $name => $value){
if(substr($name, 0, 5) == 'HTTP_'){
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
}
return $headers;
}
/**
* check if the current request was send from inGame
* @return bool
@@ -282,8 +309,9 @@ class Controller {
public function showError($f3){
// set HTTP status
if(!empty($f3->get('ERROR.code'))){
$f3->status($f3->get('ERROR.code'));
$errorCode = $f3->get('ERROR.code');
if(!empty($errorCode)){
$f3->status($errorCode);
}
if($f3->get('AJAX')){
@@ -293,7 +321,7 @@ class Controller {
$return = (object) [];
$error = (object) [];
$error->type = 'error';
$error->code = $f3->get('ERROR.code');
$error->code = $errorCode;
$error->status = $f3->get('ERROR.status');
$error->message = $f3->get('ERROR.text');
@@ -322,4 +350,12 @@ class Controller {
die();
}
/**
* Callback for framework "unload"
* -> config.ini
*/
public function unload(){
}
}

View File

@@ -21,7 +21,9 @@ class CharacterUpdate {
*/
function deleteLogData($f3){
$characterLogModel = Model\BasicModel::getNew('CharacterLogModel');
DB\Database::instance()->getDB('PF');
$characterLogModel = Model\BasicModel::getNew('CharacterLogModel', 0);
// find "old" character logs
$characterLogs = $characterLogModel->find([

View File

@@ -115,7 +115,6 @@ class BasicModel extends \DB\Cortex {
}
}
/**
* extent the fieldConf Array with static fields for each table
*/
@@ -128,7 +127,7 @@ class BasicModel extends \DB\Cortex {
'type' => Schema::DT_TIMESTAMP
],
'updated' => [
'type' => Schema::DT_TIMESTAMP
'type' => Schema::DF_CURRENT_TIMESTAMP
]
];

View File

@@ -63,10 +63,21 @@ class MapModel extends BasicModel {
foreach((array)$data as $key => $value){
if($key == 'created'){
continue;
}
if(!is_array($value)){
if($this->exists($key)){
$this->$key = $value;
}
}else{
// special array data
if($key == 'scope'){
$this->scopeId = (int)$value['id'];
}elseif($key == 'type'){
$this->typeId = (int)$value['id'];
}
}
}
}
@@ -161,12 +172,15 @@ class MapModel extends BasicModel {
* @return null
*/
public function getSystem($systemId){
$systems = $this->getSystems();
$searchSystem = null;
foreach($systems as $system){
if($system->id == $systemId){
$searchSystem = $system;
break;
if($systemId > 0){
$systems = $this->getSystems();
foreach($systems as $system){
if($system->id == $systemId){
$searchSystem = $system;
break;
}
}
}
@@ -179,7 +193,11 @@ class MapModel extends BasicModel {
*/
public function getSystems(){
// orderBy x-Coordinate for cleaner frontend animation (left to right)
$this->filter('systems', ['active = ?', 1], ['order' => 'posX']);
$this->filter('systems',
['active = :active AND id > 0',
':active' => 1
],
['order' => 'posX']);
$systems = [];
if($this->systems){
@@ -210,7 +228,10 @@ class MapModel extends BasicModel {
* @return array|mixed
*/
public function getConnections(){
$this->filter('connections', ['active = ?', 1]);
$this->filter('connections', [
'active = :active AND source > 0 AND target > 0',
':active' => 1
]);
$connections = [];
if($this->connections){

View File

@@ -45,6 +45,10 @@ class SystemModel extends BasicModel {
foreach((array)$systemData as $key => $value){
if($key == 'created'){
continue;
}
if(!is_array($value)){
if($this->exists($key)){
$this->$key = $value;

View File

@@ -1,7 +1,7 @@
[PATHFINDER]
NAME = "PATHFINDER"
; installed version (used for CSS/JS cache busting)
VERSION = "v0.0.14"
VERSION = "v0.0.15"
; contact information (DO NOT CHANGE)
CONTACT = "https://github.com/exodus4d"
; source code (DO NOT CHANGE)
@@ -39,7 +39,7 @@ DB_PASS =
; EVE-Online CCP Database export
DB_CCP_DNS = mysql:host=localhost;port=3306;dbname=
DB_CCP_NAME = eve_test
DB_CCP_NAME = eve_parallax_min
DB_CCP_USER = root
DB_CCP_PASS =

View File

@@ -27,6 +27,7 @@ define(['jquery'], function($) {
// map API
saveMap: 'api/map/save', // ajax URL - save/update map
deleteMap: 'api/map/delete', // ajax URL - delete map
importMap: 'api/map/import', // ajax URL - import map
// system API
searchSystem: 'api/system/search', // ajax URL - search system by name
saveSystem: 'api/system/save', // ajax URL - saves system to map

View File

@@ -7,8 +7,9 @@ define([
'app/init',
'app/util',
'app/render',
'bootbox'
], function($, Init, Util, Render, bootbox) {
'bootbox',
'app/ccp'
], function($, Init, Util, Render, bootbox, CCP) {
'use strict';
var config = {
@@ -17,11 +18,36 @@ define([
dialogMapCreateContainerId: 'pf-map-dialog-create', // id for the "new map" container
dialogMapEditContainerId: 'pf-map-dialog-edit', // id for the "edit" container
dialogMapSettingsContainerId: 'pf-map-dialog-settings', // id for the "settings" container
dialogMapDownloadContainerId: 'pf-map-dialog-download', // id for the "download" container
userSelectId: 'pf-map-dialog-user-select', // id for "user" select
corporationSelectId: 'pf-map-dialog-corporation-select', // id for "corporation" select
allianceSelectId: 'pf-map-dialog-alliance-select' // id for "alliance" select
allianceSelectId: 'pf-map-dialog-alliance-select', // id for "alliance" select
dialogMapExportFormId: 'pf-map-dialog-form-export', // id for "export" form
dialogMapImportFormId: 'pf-map-dialog-form-import', // id for "import" form
buttonExportId: 'pf-map-dialog-button-export', // id for "export" button
buttonImportId: 'pf-map-dialog-button-import', // id for "import" button
fieldExportId: 'pf-map-filename-export', // id for "export" filename field
fieldImportId: 'pf-map-filename-import', // id for "import" filename field
dialogMapImportInfoId: 'pf-map-import-container', // id for "info" container
dragDropElementClass: 'pf-form-dropzone' // class for "drag&drop" zone
};
/**
* format a given string into a valid filename
* @param filename
* @returns {string}
*/
var formatFilename = function(filename){
filename = filename.replace(/[^a-zA-Z0-9]/g,'_');
var nowDate = new Date();
var filenameDate = nowDate.toISOString().slice(0,10).replace(/-/g, '_');
return (filename + '_' + filenameDate).replace(/__/g,'_');
};
/**
@@ -46,15 +72,20 @@ define([
// if there are no maps -> hide settings tab
var hideSettingsTab = false;
var hideEditTab = false;
var hideDownloadTab = false;
if(mapData === false){
hideSettingsTab = true;
hideEditTab = true;
hideDownloadTab = true;
}
// available map "types" for a new or existing map
var mapTypes = Util.getMapTypes(true);
var data = {
scope: Util.getMapScopes(),
type: Util.getMapTypes(true),
type: mapTypes,
icon: Util.getMapIcons(),
formErrorContainerClass: Util.config.formErrorContainerClass,
formWarningContainerClass: Util.config.formWarningContainerClass,
@@ -89,18 +120,29 @@ define([
// render main dialog -----------------------------------------------------
data = {
id: config.newMapDialogId,
mapData: mapData,
type: mapTypes,
isInGameBrowser: CCP.isInGameBrowser(),
// message container
formErrorContainerClass: Util.config.formErrorContainerClass,
formWarningContainerClass: Util.config.formWarningContainerClass,
formInfoContainerClass: Util.config.formInfoContainerClass,
// default open tab ----------
openTabNew: options.tab === 'new',
openTabEdit: options.tab === 'edit',
openTabSettings: options.tab === 'settings',
openTabDownload: options.tab === 'download',
dialogMapCreateContainerId: config.dialogMapCreateContainerId,
dialogMapEditContainerId: config.dialogMapEditContainerId,
dialogMapSettingsContainerId: config.dialogMapSettingsContainerId,
dialogMapDownloadContainerId: config.dialogMapDownloadContainerId,
hideEditTab: hideEditTab,
hideSettingsTab: hideSettingsTab,
hideDownloadTab: hideDownloadTab,
// settings tab --------------
userSelectId: config.userSelectId,
@@ -115,18 +157,29 @@ define([
// access limitations --------
maxUser: Init.maxSharedCount.user,
maxCorporation: Init.maxSharedCount.corporation,
maxAlliance: Init.maxSharedCount.alliance
maxAlliance: Init.maxSharedCount.alliance,
// download tab --------------
dialogMapExportFormId: config.dialogMapExportFormId,
dialogMapImportFormId: config.dialogMapImportFormId,
buttonExportId: config.buttonExportId,
buttonImportId: config.buttonImportId,
fieldExportId: config.fieldExportId,
fieldImportId: config.fieldImportId,
dialogMapImportInfoId: config.dialogMapImportInfoId,
formatFilename: function(){
// format filename from "map name" (initial)
return function (mapName, render) {
var filename = render(mapName);
return formatFilename(filename);
};
}
};
var contentDialog = Mustache.render(templateMapDialog, data);
contentDialog = $(contentDialog);
// set mapId for "settings" tab
if(mapData !== false){
contentDialog.find('input[name="id"]').val( mapData.config.id );
}
// set tab content
$('#' + config.dialogMapCreateContainerId, contentDialog).html(contentNewMap);
$('#' + config.dialogMapEditContainerId, contentDialog).html(contentEditMap);
@@ -185,7 +238,7 @@ define([
dialogContent.hideLoadingAnimation();
if(responseData.error.length > 0){
if(responseData.error.length){
form.showFormMessage(responseData.error);
}else{
// success
@@ -216,15 +269,23 @@ define([
// after modal is shown =======================================================================
mapInfoDialog.on('shown.bs.modal', function(e) {
mapInfoDialog.on('shown.bs.modal', function(e){
mapInfoDialog.initTooltips();
// prevent "disabled" tabs from being clicked... "bootstrap" bugFix...
mapInfoDialog.find('.navbar a[data-toggle=tab]').on('click', function(e){
if ($(this).hasClass('disabled')){
e.preventDefault();
return false;
}
});
// set form validator
mapInfoDialog.find('form').initFormValidation();
// events for tab change
mapInfoDialog.find('.navbar a').on('shown.bs.tab', function (e) {
mapInfoDialog.find('.navbar a').on('shown.bs.tab', function(e){
var selectElementUser = mapInfoDialog.find('#' + config.userSelectId);
var selectElementCorporation = mapInfoDialog.find('#' + config.corporationSelectId);
@@ -232,7 +293,6 @@ define([
if($(e.target).attr('href') === '#' + config.dialogMapSettingsContainerId){
// "settings" tab
initSettingsSelectFields(mapInfoDialog);
}else{
if( $(selectElementUser).data('select2') !== undefined ){
@@ -247,6 +307,13 @@ define([
$(selectElementAlliance).select2('destroy');
}
}
// no "save" dialog button on "in/export" tab
if($(e.target).attr('href') === '#' + config.dialogMapDownloadContainerId){
mapInfoDialog.find('button.btn-success').hide();
}else{
mapInfoDialog.find('button.btn-success').show();
}
});
// show form messages -------------------------------------
@@ -259,17 +326,223 @@ define([
// no map data found (probably new user
form.showFormMessage([{type: 'warning', message: 'No maps found. Create a new map before you can start'}]);
}
// init select fields in case "settings" tab is open by default
if(options.tab === 'settings'){
initSettingsSelectFields(mapInfoDialog);
}
// init "download tab" ========================================================================
var downloadTabElement = mapInfoDialog.find('#' + config.dialogMapDownloadContainerId);
if(downloadTabElement.length){
// tab exists
// export map data ------------------------------------------------------------------------
downloadTabElement.find('#' + config.buttonExportId).on('click', { mapData: mapData }, function(e){
var exportForm = $('#' + config.dialogMapExportFormId);
var validExportForm = exportForm.isValidForm();
if(validExportForm){
// set map data right before download
$(this).setExportMapData(e.data.mapData);
}else{
e.preventDefault();
}
});
// import map data ------------------------------------------------------------------------
// check if "FileReader" API is supported
var importFormElement = downloadTabElement.find('#' + config.dialogMapImportFormId);
if(window.File && window.FileReader && window.FileList && window.Blob){
// show file info in UI
downloadTabElement.find('#' + config.fieldImportId).on('change', function(e){
e.stopPropagation();
e.preventDefault();
var infoContainerElement = importFormElement.find('#' + config.dialogMapImportInfoId);
infoContainerElement.hide().empty();
importFormElement.hideFormMessage('all');
var output = [];
var files = e.target.files;
for (var i = 0, f; !!(f = files[i]); i++) {
output.push(( i + 1 ) + '. file: ' + f.name + ' - ' +
f.size + ' bytes; last modified: ' +
f.lastModifiedDate.toLocaleDateString() );
}
if(output.length > 0){
infoContainerElement.html( output ).show();
}
importFormElement.validator('validate');
});
// drag&drop
var importData = {};
importData.mapData = [];
var files = [];
var filesCount = 0;
var filesCountFail = 0;
// onLoad for FileReader API
var readerOnLoad = function(readEvent) {
// get file content
try{
importData.mapData.push( JSON.parse( readEvent.target.result ) );
}catch(error){
filesCountFail++;
importFormElement.showFormMessage([{type: 'error', message: 'File can not be parsed'}]);
}
// start import when all files are parsed
if(
filesCount === files.length &&
filesCountFail === 0
){
importMaps(importData);
}
};
var handleDragOver = function(dragEvent) {
dragEvent.stopPropagation();
dragEvent.preventDefault();
dragEvent.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
};
var handleFileSelect = function(evt){
evt.stopPropagation();
evt.preventDefault();
importData = importFormElement.getFormValues();
importData.mapData = [];
filesCount = 0;
filesCountFail = 0;
files = evt.dataTransfer.files; // FileList object.
for (var file; !!(file = files[filesCount]); filesCount++){
var reader = new FileReader();
reader.onload = readerOnLoad;
reader.readAsText(file);
}
};
var dropZone = downloadTabElement.find('.' + config.dragDropElementClass);
dropZone[0].addEventListener('dragover', handleDragOver, false);
dropZone[0].addEventListener('drop', handleFileSelect, false);
// import "button"
downloadTabElement.find('#' + config.buttonImportId).on('click', function(e) {
importFormElement.validator('validate');
var validImportForm = importFormElement.isValidForm();
if(validImportForm){
importData = importFormElement.getFormValues();
importData.mapData = [];
var fileElement = downloadTabElement.find('#' + config.fieldImportId);
files = fileElement[0].files;
filesCount = 0;
filesCountFail = 0;
for (var file; !!(file = files[filesCount]); filesCount++){
var reader = new FileReader();
reader.onload = readerOnLoad;
reader.readAsText(file);
}
}
});
}else{
importFormElement.showFormMessage([{type: 'error', message: 'The File APIs are not fully supported in this browser.'}]);
}
}
});
// init select fields in case "settings" tab is open by default
if(options.tab === 'settings'){
initSettingsSelectFields(mapInfoDialog);
}
});
}
};
/**
* import new map(s) data
* @param importData
*/
var importMaps = function(importData){
var importForm = $('#' + config.dialogMapImportFormId);
importForm.hideFormMessage('all');
// lock dialog
var dialogContent = importForm.parents('.modal-content');
dialogContent.showLoadingAnimation();
$.ajax({
type: 'POST',
url: Init.path.importMap,
data: importData,
dataType: 'json'
}).done(function(responseData){
if(responseData.error.length){
// form.showFormMessage(responseData.error);
importForm.showFormMessage(responseData.error);
}else{
// success
Util.showNotify({title: 'Import finished', text: 'Map(s) imported', type: 'success'});
}
}).fail(function( jqXHR, status, error) {
var reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': importMap', text: reason, type: 'error'});
}).always(function() {
importForm.find('input, select').resetFormFields().trigger('change');
dialogContent.hideLoadingAnimation();
});
};
/**
* set json map data for export to an element (e.g. <a>-Tag or button) for download
* @param mapData
* @returns {*}
*/
$.fn.setExportMapData = function(mapData){
var fieldExport = $('#' + config.fieldExportId);
var filename = '';
var mapDataEncoded = '';
if(fieldExport.length){
filename = fieldExport.val();
if(filename.length > 0){
// remove object properties that should not be included in export
// -> e.g. jsPlumb object,...
var allowedKeys = ['config', 'data'];
var replace = function(obj, keys) {
var dup = {};
for (var key in obj) {
if (keys.indexOf(key) !== -1) {
dup[key] = obj[key];
}
}
return dup;
};
mapDataEncoded = 'text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify( replace(mapData, allowedKeys) ));
}
}
return this.each(function(){
var exportButton = $(this);
exportButton.attr('href', 'data:' + mapDataEncoded);
exportButton.attr('download', filename + '.json');
});
};
/**
* init select2 fields within the settings dialog

View File

@@ -202,7 +202,11 @@ define([
return this.each(function(){
var field = $(this);
field.val('');
if( !field.is('select') ){
// "input"
field.val('');
}
field.parents('.form-group').removeClass('has-error has-success');
});
};
@@ -293,6 +297,12 @@ define([
case 'info':
messageElement = formElement.find('.' + config.formInfoContainerClass);
break;
case 'all':
messageElement = formElement.find(
'.' + config.formErrorContainerClass + ', ' +
'.' + config.formWarningContainerClass + ', ' +
'.' + config.formInfoContainerClass
);
}
if(messageElement){
@@ -1603,7 +1613,6 @@ define([
redirect(data.reroute, ['logout']);
}
}).fail(function( jqXHR, status, error) {
var reason = status + ' ' + error;
showNotify({title: jqXHR.status + ': logout', text: reason, type: 'error'});
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -3,13 +3,32 @@
<nav class="navbar navbar-default" role="navigation">
<div class="navbar-header pull-left">
<ul class="nav navbar-nav {{dialogNavigationClass}}" role="tablist">
<li class="{{#openTabNew}}active{{/openTabNew}}"><a role="tab" data-toggle="tab" data-name="newMap" href="#{{dialogMapCreateContainerId}}"><i class="fa fa-plus fa-fw"></i>&nbsp;New map</a></li>
<li class="{{#openTabNew}}active{{/openTabNew}}">
<a role="tab" data-toggle="tab" data-name="newMap" href="#{{dialogMapCreateContainerId}}">
<i class="fa fa-plus fa-fw"></i>&nbsp;New map
</a>
</li>
{{^hideEditTab}}
<li class="{{#openTabEdit}}active{{/openTabEdit}}"><a role="tab" data-toggle="tab" data-name="editMap" href="#{{dialogMapEditContainerId}}"><i class="fa fa-edit fa-fw"></i>&nbsp;Edit map</a></li>
<li class="{{#openTabEdit}}active{{/openTabEdit}}">
<a role="tab" data-toggle="tab" data-name="editMap" href="#{{dialogMapEditContainerId}}">
<i class="fa fa-edit fa-fw"></i>&nbsp;Edit map
</a>
</li>
{{/hideEditTab}}
{{^hideSettingsTab}}
<li class="{{#openTabSettings}}active{{/openTabSettings}}"><a role="tab" data-toggle="tab" data-name="settings" href="#{{dialogMapSettingsContainerId}}"><i class="fa fa-gears fa-fw"></i>&nbsp;Settings</a></li>
<li class="{{#openTabSettings}}active{{/openTabSettings}}">
<a role="tab" data-toggle="tab" data-name="settings" href="#{{dialogMapSettingsContainerId}}">
<i class="fa fa-gears fa-fw"></i>&nbsp;Settings
</a>
</li>
{{/hideSettingsTab}}
{{^hideDownloadTab}}
<li class="{{#openTabDownload}}active{{/openTabDownload}} {{#isInGameBrowser}}disabled{{/isInGameBrowser}}" {{#isInGameBrowser}}title="Not working in IGB"{{/isInGameBrowser}}>
<a role="tab" data-toggle="tab" data-name="download" href="#{{dialogMapDownloadContainerId}}" {{#isInGameBrowser}}class="disabled"{{/isInGameBrowser}}>
<i class="fa fa-exchange fa-fw"></i>&nbsp;Import/Export
</a>
</li>
{{/hideDownloadTab}}
</ul>
</div>
</nav>
@@ -17,108 +36,181 @@
<div class="tab-content">
<div role="tabpanel" class="tab-pane fade {{#openTabNew}}in active{{/openTabNew}}" id="{{dialogMapCreateContainerId}}"></div>
<div role="tabpanel" class="tab-pane fade {{#openTabEdit}}in active{{/openTabEdit}}" id="{{dialogMapEditContainerId}}"></div>
<div role="tabpanel" class="tab-pane fade {{#openTabSettings}}in active{{/openTabSettings}}" id="{{dialogMapSettingsContainerId}}">
<form role="form" class="form-horizontal">
<h2><i class="fa fa-share-alt fa-fw"></i> Share settings</h2>
<div class="row">
<div class="col-sm-11">
<blockquote>
<p>
Use this feature with caution! - Shared map entities have full map access.
They even can take over control by removing other entities from this list.
</p>
<small>Reduce this risk by creating a new map for joined OPs.
</small>
</blockquote>
{{^hideSettingsTab}}
<div role="tabpanel" class="tab-pane fade {{#openTabSettings}}in active{{/openTabSettings}}" id="{{dialogMapSettingsContainerId}}">
<form role="form" class="form-horizontal">
<h4><i class="fa fa-share-alt fa-fw"></i> Share settings</h4>
<div class="row">
<div class="col-sm-11">
<blockquote>
<p>
Use this feature with caution! - Shared map entities have full map access.
They even can take over control by removing other entities from this list.
</p>
<small>Reduce this risk by creating a new map for joined OPs.
</small>
</blockquote>
</div>
</div>
</div>
{{! user search ----------------------------------------------------- }}
{{#accessUser.length}}
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-2 control-label" for="{{userSelectId}}">Username</label>
<div class="col-sm-10">
<div class="input-group" title="add/remove user">
<label for="{{userSelectId}}"></label>
<select id="{{userSelectId}}" name="mapUsers[]" multiple="multiple">
{{#accessUser}}
{{! user search ----------------------------------------------------- }}
{{#accessUser.length}}
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-2 control-label" for="{{userSelectId}}">Username</label>
<div class="col-sm-10">
<div class="input-group" title="add/remove user">
<label for="{{userSelectId}}"></label>
<select id="{{userSelectId}}" name="mapUsers[]" multiple="multiple">
{{#accessUser}}
<option value="{{id}}" selected>{{name}}</option>
{{/accessUser}}
</select>
<span class="help-block with-errors">Search user name (max {{maxUser}})</span>
{{/accessUser}}
</select>
<span class="help-block with-errors">Search user name (max {{maxUser}})</span>
</div>
</div>
</div>
</div>
</div>
</div>
{{/accessUser.length}}
{{/accessUser.length}}
{{! corporation search ---------------------------------------------- }}
{{#accessCorporation.length}}
<div class="row">
<div class="col-sm-9">
<div class="form-group">
<label class="col-sm-3 control-label" for="{{corporationSelectId}}">Corporations</label>
<div class="col-sm-9">
<div class="input-group" title="add/remove corporations">
<label for="{{corporationSelectId}}"></label>
<select id="{{corporationSelectId}}" name="mapCorporations[]" multiple="multiple">
{{#accessCorporation}}
{{! corporation search ---------------------------------------------- }}
{{#accessCorporation.length}}
<div class="row">
<div class="col-sm-9">
<div class="form-group">
<label class="col-sm-3 control-label" for="{{corporationSelectId}}">Corporations</label>
<div class="col-sm-9">
<div class="input-group" title="add/remove corporations">
<label for="{{corporationSelectId}}"></label>
<select id="{{corporationSelectId}}" name="mapCorporations[]" multiple="multiple">
{{#accessCorporation}}
<option value="{{id}}" selected="selected" >{{name}}</option>
{{/accessCorporation}}
</select>
<span class="help-block with-errors">Search corporation name (max {{maxCorporation}})</span>
{{/accessCorporation}}
</select>
<span class="help-block with-errors">Search corporation name (max {{maxCorporation}})</span>
</div>
</div>
</div>
</div>
</div>
</div>
{{/accessCorporation.length}}
{{/accessCorporation.length}}
{{! alliance search ------------------------------------------------- }}
{{#accessAlliance.length}}
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-2 control-label" for="{{allianceSelectId}}">Alliances</label>
<div class="col-sm-10">
<div class="input-group" title="add/remove alliances">
<label for="{{allianceSelectId}}"></label>
<select id="{{allianceSelectId}}" name="mapAlliances[]" multiple="multiple" >
{{#accessAlliance}}
<option value="{{id}}" selected="selected" >{{name}}</option>
{{/accessAlliance}}
</select>
<span class="help-block with-errors">Search alliance name (max {{maxAlliance}})</span>
{{! alliance search ------------------------------------------------- }}
{{#accessAlliance.length}}
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-2 control-label" for="{{allianceSelectId}}">Alliances</label>
<div class="col-sm-10">
<div class="input-group" title="add/remove alliances">
<label for="{{allianceSelectId}}"></label>
<select id="{{allianceSelectId}}" name="mapAlliances[]" multiple="multiple" >
{{#accessAlliance}}
<option value="{{id}}" selected="selected" >{{name}}</option>
{{/accessAlliance}}
</select>
<span class="help-block with-errors">Search alliance name (max {{maxAlliance}})</span>
</div>
</div>
</div>
</div>
</div>
</div>
{{/accessAlliance.length}}
{{/accessAlliance.length}}
<input type="hidden" name="id" value="0" />
</form>
<input type="hidden" name="id" value="{{ mapData.config.id }}" />
</form>
</div>
{{/hideSettingsTab}}
{{^hideDownloadTab}}
<div role="tabpanel" class="tab-pane fade {{#openTabDownload}}in active{{/openTabDownload}}" id="{{dialogMapDownloadContainerId}}">
<h4 class="pf-dynamic-area">Map export</h4>
<form role="form" class="form-horizontal" id="{{dialogMapExportFormId}}">
<div class="form-group">
<label class="control-label col-sm-2" for="{{fieldExportId}}">Export name</label>
<div class="col-sm-10">
<div class="input-group">
<input class="form-control" id="{{fieldExportId}}" type="text" name="{{fieldExportId}}" value="{{#formatFilename}}{{mapData.config.name}}{{/formatFilename}}" pattern="^[_a-zA-Z0-9]{1,}$" data-minlength="3" data-minlength-error="Min. of 3 characters" data-error="Invalid format: _ a-z A-Z 0-9" required>
<div class="input-group-btn">
<a type="button" id="{{buttonExportId}}" class="btn btn-default" href="" download="data.json">
<i class="fa fa-fw fa-upload"></i> Export
</a>
</div>
</div>
<div class="help-block with-errors"></div>
</div>
</div>
</form>
<h4 class="pf-dynamic-area">Map Import</h4>
<form role="form" class="form-horizontal" id="{{dialogMapImportFormId}}">
<div class="form-group">
<label for="type" class="col-sm-2 control-label">Type</label>
<div class="col-sm-3">
<select name="typeId" id="type" class="form-control" title="Alliance/Corporation maps require character authentication" data-placement="top">
{{#type}}
<option value="{{id}}">{{label}}</option>
{{/type}}
</select>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="{{fieldImportId}}">Import file</label>
<div class="col-sm-10">
<div class="row">
<div class="col-sm-12">
<div class="input-group">
<input class="form-control" id="{{fieldImportId}}" type="file" name="{{fieldImportId}}" accept=".json" data-error="Select a valid file" required>
<div class="input-group-btn">
<button type="button" id="{{buttonImportId}}" class="btn btn-default">
<i class="fa fa-fw fa-download"></i> Import
</button>
</div>
</div>
<div class="help-block with-errors"></div>
</div>
</div>
</div>
</div>
<div class="pf-form-dropzone">Drop map file here</div>
<h4 id="{{dialogMapImportInfoId}}" class="pf-dynamic-area" style="display: none;"></h4>
<div class="{{formErrorContainerClass}} alert alert-danger" style="display: none;">
<span class="txt-color txt-color-danger">Error</span>
<small> (important non-critical information)</small>
</div>
</form>
</div>
{{/hideDownloadTab}}
<div class="{{formInfoContainerClass}} alert alert-info" style="display: none;">
<span class="txt-color txt-color-information">Information</span>
<small> (important non-critical information)</small>
</div>
<div class="{{formWarningContainerClass}} alert alert-warning" style="display: none;">
<span class="txt-color txt-color-warning">Warning</span>
<small> (important non-critical information)</small>
</div>
<div class="{{formErrorContainerClass}} alert alert-danger" style="display: none;">
<span class="txt-color txt-color-danger">Error</span>
<small> (important non-critical information)</small>
</div>
</div>
<div class="{{formInfoContainerClass}} alert alert-info" style="display: none;">
<span class="txt-color txt-color-information">Information</span>
<small> (important non-critical information)</small>
</div>
<div class="{{formWarningContainerClass}} alert alert-warning" style="display: none;">
<span class="txt-color txt-color-warning">Warning</span>
<small> (important non-critical information)</small>
</div>
<div class="{{formErrorContainerClass}} alert alert-danger" style="display: none;">
<span class="txt-color txt-color-danger">Error</span>
<small> (important non-critical information)</small>
</div>
</div>

View File

@@ -346,7 +346,7 @@ $navbar-default-link-hover-color: $teal-lightest;
$navbar-default-link-hover-bg: transparent;
$navbar-default-link-active-color: $teal-lighter;
$navbar-default-link-active-bg: transparent;
$navbar-default-link-disabled-color: #ccc;
$navbar-default-link-disabled-color: $gray;
$navbar-default-link-disabled-bg: transparent;
// Navbar brand label

View File

@@ -236,8 +236,8 @@
}
// sharing dialog =========================================
#pf-sharing-dialog{
.pf-dynamic-area{
h2, h4{
&.pf-dynamic-area{
min-height: 0;
}
}

View File

@@ -18,8 +18,39 @@ input, select{
@include box-shadow(0 0 0 50px $gray-dark inset !important);
-webkit-text-fill-color: $gray-lighter;
}
// file input
&::-webkit-file-upload-button{
background-color: transparent;
border: none;
color: $gray-light;
outline: none;
}
}
// drag&drop zone
.pf-form-dropzone{
border: 2px dashed $gray-darker;
height: 100px;
background-color: darken($gray, 3%);
text-align: center;
font-size: 20px;
line-height: 100px;
margin: 15px 0;
color: $gray-darker;
@include border-radius(10px);
@include transition( color 0.18s ease-out, border-color 0.18s ease-out);
&:hover{
color: $teal-lighter;
border-color: $teal-lighter;
cursor: -moz-grabbing;
cursor: -webkit-grabbing;
cursor: grabbing;
}
}
// fix for bootstrap-toggle plugin
.toggle{
&.btn:active{

View File

@@ -729,11 +729,11 @@ select:active, select:hover {
}
.tooltip.top .tooltip-arrow,{
.tooltip.top .tooltip-arrow{
border-top-color: $gray-light;
}
.tooltip.right .tooltip-arrow,{
.tooltip.right .tooltip-arrow{
border-right-color: $gray-light;
}