mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-08 22:33:24 +00:00
UI improvements, save build feature partial implementation
This commit is contained in:
@@ -23,11 +23,13 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="bg"></div>
|
<div id="bg"></div>
|
||||||
<shipyard-menu></shipyard-menu>
|
<shipyard-header></shipyard-header>
|
||||||
<div id="main" ui-view ng-click="bgClicked($event)"></div>
|
<div id="main" ui-view ng-click="bgClicked($event)"></div>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<div class="right">Version <%= version %> - <%= date %></div>
|
<div class="right">
|
||||||
|
<a href="https://github.com/cmmcleod/ed-shipyard" target="_blank" title="Shipyard Github Project">Version <%= version %> - <%= date %></a>
|
||||||
|
</div>
|
||||||
<div class="left">
|
<div class="left">
|
||||||
Coriolis Shipyard was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes.<br>
|
Coriolis Shipyard was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes.<br>
|
||||||
It is not endorsed by nor reflects the views or opinions of Frontier Developments and no employee of Frontier Developments was involved in the making of it.
|
It is not endorsed by nor reflects the views or opinions of Frontier Developments and no employee of Frontier Developments was involved in the making of it.
|
||||||
|
|||||||
@@ -1,44 +1,9 @@
|
|||||||
angular.module('app', ['ui.router', 'shipyard', 'ngLodash', 'app.templates'])
|
angular.module('app', ['ui.router', 'shipyard', 'ngLodash', 'app.templates'])
|
||||||
.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', function($stateProvider, $urlRouterProvider, $locationProvider) {
|
|
||||||
$locationProvider.html5Mode(true);
|
|
||||||
$stateProvider
|
|
||||||
.state('outfit', {
|
|
||||||
url: '/outfit/:shipId/:code?bn',
|
|
||||||
params: {
|
|
||||||
// TODO: fix below, default, squash false not working
|
|
||||||
//shipId: { value: 'sidewinder', squash: false }, // Allow 'shipId' parameter to default to
|
|
||||||
code: { value: null, squash: true } // Allow 'code' parameter to be empty/optional
|
|
||||||
},
|
|
||||||
templateUrl: 'views/page-outfit.html',
|
|
||||||
controller: 'OutfitController',
|
|
||||||
resolve: {
|
|
||||||
shipId: ['$stateParams',function ($p) { // Ensure ship exists before loading controller
|
|
||||||
if (!DB.ships[$p.shipId]) {
|
|
||||||
throw { type: 404, message: 'Ship "' + $p.shipId + '" does not exist'};
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.state('shipyard', { url: '/', templateUrl: 'views/page-shipyard.html', controller: 'ShipyardController' })
|
|
||||||
.state('error', { params: {type:null, message:null, details: null }, templateUrl: 'views/page-error.html', controller: 'ErrorController' })
|
|
||||||
.state('notfound', { url: '*path', templateUrl: 'views/page-error.html', controller: 'ErrorController' });
|
|
||||||
|
|
||||||
}])
|
|
||||||
.config(['$provide',function($provide) {
|
|
||||||
// Global Error Handler, redirects uncaught errors to the error page
|
|
||||||
$provide.decorator('$exceptionHandler', ['$delegate', '$injector', function ($delegate, $injector) {
|
|
||||||
return function(exception, cause) {
|
|
||||||
$injector.get('$state').go('error', { details: exception }, {location:false, reload:true}); // Go to error state, reload the controller, keep the current URL
|
|
||||||
$delegate(exception, cause);
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
}])
|
|
||||||
.run(['$rootScope','$document','$state','commonArray','shipPurpose','shipSize','hardPointClass','internalGroupMap','hardpointsGroupMap', function ($rootScope, $doc, $state, CArr, shipPurpose, sz, hpc, igMap, hgMap) {
|
.run(['$rootScope','$document','$state','commonArray','shipPurpose','shipSize','hardPointClass','internalGroupMap','hardpointsGroupMap', function ($rootScope, $doc, $state, CArr, shipPurpose, sz, hpc, igMap, hgMap) {
|
||||||
|
|
||||||
// Redirect any state transition errors to the error controller/state
|
// Redirect any state transition errors to the error controller/state
|
||||||
$rootScope.$on('$stateChangeError', function(e, toState, toParams, fromState, fromParams, error){
|
$rootScope.$on('$stateChangeError', function(e, toState, toParams, fromState, fromParams, error){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$state.go('error',error, {location:false, reload:true}); // Go to error state, reload the controller, keep the current URL
|
$state.go('error', error, {location:false, reload:true}); // Go to error state, reload the controller, keep the current URL
|
||||||
});
|
});
|
||||||
|
|
||||||
// Global Reference variables
|
// Global Reference variables
|
||||||
@@ -47,7 +12,7 @@ angular.module('app', ['ui.router', 'shipyard', 'ngLodash', 'app.templates'])
|
|||||||
$rootScope.SZ = sz;
|
$rootScope.SZ = sz;
|
||||||
$rootScope.HPC = hpc;
|
$rootScope.HPC = hpc;
|
||||||
$rootScope.igMap = igMap;
|
$rootScope.igMap = igMap;
|
||||||
window.hgmap = $rootScope.hgMap = hgMap;
|
$rootScope.hgMap = hgMap;
|
||||||
$rootScope.ships = DB.ships;
|
$rootScope.ships = DB.ships;
|
||||||
$rootScope.title = 'Coriolis';
|
$rootScope.title = 'Coriolis';
|
||||||
|
|
||||||
@@ -61,11 +26,16 @@ angular.module('app', ['ui.router', 'shipyard', 'ngLodash', 'app.templates'])
|
|||||||
|
|
||||||
// Global Event Listeners
|
// Global Event Listeners
|
||||||
$doc.bind('keyup', function (e) {
|
$doc.bind('keyup', function (e) {
|
||||||
$rootScope.$broadcast('keyup', e);
|
if(e.keyCode == 27) { // Escape Key
|
||||||
|
$rootScope.$broadcast('close', e);
|
||||||
|
$rootScope.$apply();
|
||||||
|
} else {
|
||||||
|
$rootScope.$broadcast('keyup', e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$rootScope.bgClicked = function (e) {
|
$rootScope.bgClicked = function (e) {
|
||||||
$rootScope.$broadcast('bgClicked', e);
|
$rootScope.$broadcast('close', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}]);
|
}]);
|
||||||
|
|||||||
56
app/js/config.js
Normal file
56
app/js/config.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* Sets up the routes and handlers before the Angular app is kicked off.
|
||||||
|
*/
|
||||||
|
angular.module('app').config(['$provide','$stateProvider', '$urlRouterProvider', '$locationProvider', function ($provide, $stateProvider, $urlRouterProvider, $locationProvider) {
|
||||||
|
// Use HTML5 push and replace state if possible
|
||||||
|
$locationProvider.html5Mode(true);
|
||||||
|
/**
|
||||||
|
* Set up all states and their routes.
|
||||||
|
*/
|
||||||
|
$stateProvider
|
||||||
|
.state('outfit', {
|
||||||
|
url: '/outfit/:shipId/:code?bn',
|
||||||
|
params: {
|
||||||
|
// TODO: Squash:false not working due to UI-router issue
|
||||||
|
shipId: { value: 'sidewinder', squash: false}, // Allow 'shipId' parameter to default to
|
||||||
|
code: { value: undefined, squash: true } // Allow 'code' parameter to be empty/optional
|
||||||
|
},
|
||||||
|
templateUrl: 'views/page-outfit.html',
|
||||||
|
controller: 'OutfitController',
|
||||||
|
resolve: {
|
||||||
|
shipId: ['$stateParams',function ($p) { // Ensure ship exists before loading controller
|
||||||
|
if (!DB.ships[$p.shipId]) {
|
||||||
|
throw { type: 'no-ship', message: $p.shipId };
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.state('shipyard', { url: '/', templateUrl: 'views/page-shipyard.html', controller: 'ShipyardController' })
|
||||||
|
.state('error', { params: {type:null, message:null, details: null }, templateUrl: 'views/page-error.html', controller: 'ErrorController' })
|
||||||
|
|
||||||
|
// Redirects
|
||||||
|
$urlRouterProvider.when('/outfit','/outfit/sidewinder/');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 404 Handler - Keep current URL/ do not redirect, change to error state.
|
||||||
|
*/
|
||||||
|
$urlRouterProvider.otherwise(function ($injector, $location) {
|
||||||
|
// Go to error state, reload the controller, keep the current URL
|
||||||
|
$injector.get('$state').go('error', { type: 404, message: null, details: null }, {location:false, reload:true});
|
||||||
|
return $location.path;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global Error Handler. Decorates the existing error handler such that it
|
||||||
|
* redirects uncaught errors to the error page.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
$provide.decorator('$exceptionHandler', ['$delegate', '$injector', function ($delegate, $injector) {
|
||||||
|
return function(exception, cause) {
|
||||||
|
// Go to error state, reload the controller, keep the current URL
|
||||||
|
$injector.get('$state').go('error', { details: exception }, {location:false, reload:true});
|
||||||
|
$delegate(exception, cause);
|
||||||
|
};
|
||||||
|
}]);
|
||||||
|
|
||||||
|
}]);
|
||||||
@@ -1,15 +1,29 @@
|
|||||||
angular.module('app')
|
angular.module('app')
|
||||||
.controller('ErrorController', ['$rootScope','$scope','$stateParams', '$location', function ($rootScope, $scope, $p, $location) {
|
.controller('ErrorController', ['$rootScope','$scope','$stateParams', '$location', function ($rootScope, $scope, $p, $location) {
|
||||||
$rootScope.title = 'Error';
|
$rootScope.title = 'Error';
|
||||||
|
$scope.path = $location.path();
|
||||||
|
$scope.type = $p.type || 'unknown';
|
||||||
|
|
||||||
if ($p.path) { // If path is specified, 404
|
switch ($scope.type) {
|
||||||
$scope.type = 404; // Deep Space Image...
|
case 404:
|
||||||
$scope.message = ""
|
$scope.msgPre = 'Page';
|
||||||
$scope.path = $p.path;
|
$scope.msgHighlight = $scope.path;
|
||||||
} else {
|
$scope.msgPost = 'Not Found';
|
||||||
$scope.type = $p.type || 'unknown';
|
$scope.image = 'deep-space';
|
||||||
$scope.message = $p.message || "Uh, this is bad..";
|
break;
|
||||||
$scope.path = $location.path();
|
case 'no-ship':
|
||||||
|
$scope.msgPre = 'Ship';
|
||||||
|
$scope.msgHighlight = $p.message;
|
||||||
|
$scope.msgPost = 'does not exist';
|
||||||
|
$scope.image = 'thargoid';
|
||||||
|
break;
|
||||||
|
case 'build-fail':
|
||||||
|
$scope.msgPre = 'Build Failure!';
|
||||||
|
$scope.image = 'ship-explode';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$scope.msgPre = "Uh, this is bad..";
|
||||||
|
$scope.image = 'thargoid';
|
||||||
}
|
}
|
||||||
|
|
||||||
}]);
|
}]);
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
angular.module('app').controller('OutfitController', ['$rootScope','$scope', '$state', '$stateParams', 'Ship', 'Components', 'Serializer', 'Persist', function ($rootScope, $scope, $state, $p, Ship, Components, Serializer, Persist) {
|
angular.module('app').controller('OutfitController', ['$rootScope','$scope', '$state', '$stateParams', 'Ship', 'Components', 'Serializer', 'Persist', function ($rootScope, $scope, $state, $p, Ship, Components, Serializer, Persist) {
|
||||||
var data = DB.ships[$p.shipId];
|
var data = DB.ships[$p.shipId]; // Retrieve the basic ship properties, slots and defaults
|
||||||
var ship = new Ship($p.shipId, data.properties, data.slots); // Create a new Ship instance
|
var ship = new Ship($p.shipId, data.properties, data.slots); // Create a new Ship instance
|
||||||
|
|
||||||
|
// Update the ship instance with the code (if provided) or the 'factory' defaults.
|
||||||
if ($p.code) {
|
if ($p.code) {
|
||||||
Serializer.toShip(ship, $p.code); // Populate components from 'code' URL param
|
Serializer.toShip(ship, $p.code); // Populate components from 'code' URL param
|
||||||
$scope.code = $p.code;
|
$scope.code = $p.code;
|
||||||
@@ -10,7 +11,7 @@ angular.module('app').controller('OutfitController', ['$rootScope','$scope', '$s
|
|||||||
}
|
}
|
||||||
|
|
||||||
$scope.buildName = $p.bn;
|
$scope.buildName = $p.bn;
|
||||||
$rootScope.title = ship.name + $scope.buildName? ' - ' + $scope.buildName: '';
|
$rootScope.title = ship.name + ($scope.buildName? ' - ' + $scope.buildName: '');
|
||||||
$scope.ship = ship;
|
$scope.ship = ship;
|
||||||
$scope.pp = ship.common[0]; // Power Plant
|
$scope.pp = ship.common[0]; // Power Plant
|
||||||
$scope.th = ship.common[1]; // Thruster
|
$scope.th = ship.common[1]; // Thruster
|
||||||
@@ -23,11 +24,17 @@ angular.module('app').controller('OutfitController', ['$rootScope','$scope', '$s
|
|||||||
$scope.internal = ship.internal;
|
$scope.internal = ship.internal;
|
||||||
$scope.availCS = Components.forShip(ship.id);
|
$scope.availCS = Components.forShip(ship.id);
|
||||||
$scope.selectedSlot = null;
|
$scope.selectedSlot = null;
|
||||||
$scope.lastSaveCode = Persist.getBuild(ship.id, $scope.buildName);
|
$scope.savedCode = Persist.getBuild(ship.id, $scope.buildName);
|
||||||
|
|
||||||
// for debugging
|
// for debugging
|
||||||
window.myScope = $scope;
|
window.myScope = $scope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 'Opens' a select for component selection.
|
||||||
|
*
|
||||||
|
* @param {[type]} e The event object
|
||||||
|
* @param {[type]} slot The slot that is being 'opened' for selection
|
||||||
|
*/
|
||||||
$scope.selectSlot = function(e, slot) {
|
$scope.selectSlot = function(e, slot) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if ($scope.selectedSlot == slot) {
|
if ($scope.selectedSlot == slot) {
|
||||||
@@ -37,6 +44,14 @@ angular.module('app').controller('OutfitController', ['$rootScope','$scope', '$s
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the ships build with the selected component for the
|
||||||
|
* specified slot. Prevents the click event from propagation.
|
||||||
|
*
|
||||||
|
* @param {string} type Shorthand key/string for identifying the slot & component type
|
||||||
|
* @param {[type]} slot The slot object belonging to the ship instance
|
||||||
|
* @param {[type]} e The event object
|
||||||
|
*/
|
||||||
$scope.select = function(type, slot, e) {
|
$scope.select = function(type, slot, e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.srcElement.id) {
|
if (e.srcElement.id) {
|
||||||
@@ -54,7 +69,6 @@ angular.module('app').controller('OutfitController', ['$rootScope','$scope', '$s
|
|||||||
$scope.selectedSlot = null;
|
$scope.selectedSlot = null;
|
||||||
$scope.code = Serializer.fromShip(ship);
|
$scope.code = Serializer.fromShip(ship);
|
||||||
$state.go('outfit', {shipId: ship.id, code: $scope.code, bn: $scope.buildName}, {location:'replace', notify:false});
|
$state.go('outfit', {shipId: ship.id, code: $scope.code, bn: $scope.buildName}, {location:'replace', notify:false});
|
||||||
$scope.canSave = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,21 +76,28 @@ angular.module('app').controller('OutfitController', ['$rootScope','$scope', '$s
|
|||||||
* Reload the build from the last save.
|
* Reload the build from the last save.
|
||||||
*/
|
*/
|
||||||
$scope.reloadBuild = function() {
|
$scope.reloadBuild = function() {
|
||||||
if ($scope.buildName && $scope.lastSaveCode) {
|
if ($scope.buildName && $scope.savedCode) {
|
||||||
Serializer.toShip(ship, $scope.lastSaveCode); // Repopulate with components from last save
|
Serializer.toShip(ship, $scope.savedCode); // Repopulate with components from last save
|
||||||
$scope.code = $scope.lastSaveCode;
|
$scope.code = $scope.savedCode;
|
||||||
$state.go('outfit', {shipId: ship.id, code: $scope.lastSaveCode, bn: $scope.buildName}, {location:'replace', notify:false});
|
$state.go('outfit', {shipId: ship.id, code: $scope.savedCode, bn: $scope.buildName}, {location:'replace', notify:false});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the current build. Will replace the saved build if there is one
|
||||||
|
* for this ship & with the exact name.
|
||||||
|
*/
|
||||||
$scope.saveBuild = function() {
|
$scope.saveBuild = function() {
|
||||||
if ($scope.code && $scope.code != $scope.lastSaveCode) {
|
if($scope.code != $scope.savedCode) {
|
||||||
Persist.saveBuild(ship.id, $scope.buildName, $scope.code);
|
Persist.saveBuild(ship.id, $scope.buildName, $scope.code);
|
||||||
$scope.lastSaveCode = $scope.code;
|
$scope.savedCode = $scope.code;
|
||||||
$rootScope.$broadcast('buildSaved', ship.id, $scope.buildName, $scope.code);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permanently delete the current build and redirect/reload this controller
|
||||||
|
* with the 'factory' build of the current ship.
|
||||||
|
*/
|
||||||
$scope.deleteBuild = function() {
|
$scope.deleteBuild = function() {
|
||||||
Persist.deleteBuild(ship.id, $scope.buildName);
|
Persist.deleteBuild(ship.id, $scope.buildName);
|
||||||
$rootScope.$broadcast('buildDeleted', $scope.saveName, ship.id);
|
$rootScope.$broadcast('buildDeleted', $scope.saveName, ship.id);
|
||||||
@@ -84,17 +105,20 @@ angular.module('app').controller('OutfitController', ['$rootScope','$scope', '$s
|
|||||||
}
|
}
|
||||||
|
|
||||||
$rootScope.$on('keyup', function (e, keyEvent) {
|
$rootScope.$on('keyup', function (e, keyEvent) {
|
||||||
if(keyEvent.keyCode == 27) { // on Escape
|
// CTRL + S or CMD + S will override the default and save the build is possible
|
||||||
$scope.selectedSlot = null;
|
if (keyEvent.keycode == 83 && keyEvent.ctrlKey) {
|
||||||
$scope.$apply();
|
|
||||||
}
|
|
||||||
else if(keyEvent.keycode == 83 && keyEvent.ctrlKey){ // CTRL + S
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$scope.saveBuild();
|
$scope.saveBuild();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$rootScope.$on('bgClicked', function (e, keyEvent) {
|
// Hide any open menu/slot/etc if escape key is pressed
|
||||||
|
$rootScope.$on('escape', function (e, keyEvent) {
|
||||||
|
$scope.selectedSlot = null;
|
||||||
|
$scope.$apply();
|
||||||
|
});
|
||||||
|
// Hide any open menu/slot/etc if the background is clicked
|
||||||
|
$rootScope.$on('close', function (e, keyEvent) {
|
||||||
$scope.selectedSlot = null;
|
$scope.selectedSlot = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
angular.module('app')
|
angular.module('app').controller('ShipyardController', ['$rootScope', function ($rootScope) {
|
||||||
.controller('ShipyardController', ['$rootScope', function ($rootScope) {
|
|
||||||
$rootScope.title = 'Coriolis - Shipyard';
|
$rootScope.title = 'Coriolis - Shipyard';
|
||||||
}]);
|
}]);
|
||||||
37
app/js/directives/directive-header.js
Normal file
37
app/js/directives/directive-header.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
angular.module('app').directive('shipyardHeader', ['$rootScope', 'Persist', function ($rootScope, Persist) {
|
||||||
|
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
templateUrl: 'views/_header.html',
|
||||||
|
scope: true,
|
||||||
|
link: function (scope) {
|
||||||
|
scope.openedMenu = null;
|
||||||
|
scope.ships = DB.ships;
|
||||||
|
scope.allBuilds = Persist.builds;
|
||||||
|
scope.bs = Persist.state;
|
||||||
|
console.log(scope);
|
||||||
|
|
||||||
|
$rootScope.$on('$stateChangeStart',function(){
|
||||||
|
scope.openedMenu = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
$rootScope.$on('close', function (e, keyEvent) {
|
||||||
|
scope.openedMenu = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.openMenu = function (menu) {
|
||||||
|
if(menu == scope.openedMenu) {
|
||||||
|
scope.openedMenu = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menu == 'b' && !scope.bs.hasBuilds) {
|
||||||
|
scope.openedMenu = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scope.openedMenu = menu;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}]);
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
angular.module('app').directive('shipyardMenu', function () {
|
|
||||||
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
templateUrl: 'views/menu.html',
|
|
||||||
link: function () {
|
|
||||||
|
|
||||||
// TODO: Saved Ships: load, save, save as, delete, export
|
|
||||||
// TODO: Links: github, forum, etc
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* [description]
|
||||||
|
*/
|
||||||
angular.module('app').service('Persist', ['lodash', function (_) {
|
angular.module('app').service('Persist', ['lodash', function (_) {
|
||||||
var LS_KEY = 'builds';
|
var LS_KEY = 'builds';
|
||||||
|
|
||||||
@@ -9,6 +12,9 @@ angular.module('app').service('Persist', ['lodash', function (_) {
|
|||||||
this.builds = {};
|
this.builds = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
hasBuilds: Object.keys(this.builds).length > 0
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Persist a ship build in local storage.
|
* Persist a ship build in local storage.
|
||||||
*
|
*
|
||||||
@@ -22,7 +28,9 @@ angular.module('app').service('Persist', ['lodash', function (_) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.builds[shipId][name] = code;
|
this.builds[shipId][name] = code;
|
||||||
localStorage.setItem(LS_KEY, angular.toJson(this.builds)); // Persist updated build collection to localstorage
|
this.state.hasBuilds = true;
|
||||||
|
// Persist updated build collection to localstorage
|
||||||
|
localStorage.setItem(LS_KEY, angular.toJson(this.builds));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,11 +56,15 @@ angular.module('app').service('Persist', ['lodash', function (_) {
|
|||||||
* @param {string} name The name of the build
|
* @param {string} name The name of the build
|
||||||
*/
|
*/
|
||||||
this.deleteBuild = function (shipId, name) {
|
this.deleteBuild = function (shipId, name) {
|
||||||
delete build[shipId][name];
|
if(this.builds[shipId][name]) {
|
||||||
if (Object.keys(build[shipId]).length == 0) {
|
delete this.builds[shipId][name];
|
||||||
delete build[shipId];
|
if (Object.keys(this.builds[shipId]).length == 0) {
|
||||||
|
delete this.builds[shipId];
|
||||||
|
this.state.hasBuilds = Object.keys(this.builds).length > 0;
|
||||||
|
}
|
||||||
|
// Persist updated build collection to localstorage
|
||||||
|
localStorage.setItem(LS_KEY, angular.toJson(this.builds));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}]);
|
||||||
}]);
|
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
* information or behavoir in Elite Dangerous.
|
* information or behavoir in Elite Dangerous.
|
||||||
*
|
*
|
||||||
* This file contains values and functions that can be reused across the app.
|
* This file contains values and functions that can be reused across the app.
|
||||||
|
*
|
||||||
|
* @requires ngLodash is a dependency of this module.
|
||||||
*/
|
*/
|
||||||
angular.module('shipyard', [])
|
angular.module('shipyard', ['ngLodash'])
|
||||||
.value('commonArray', [
|
.value('commonArray', [
|
||||||
'Power Plant',
|
'Power Plant',
|
||||||
'Thrusters',
|
'Thrusters',
|
||||||
@@ -67,42 +69,39 @@ angular.module('shipyard', [])
|
|||||||
'Large',
|
'Large',
|
||||||
'Huge'
|
'Huge'
|
||||||
])
|
])
|
||||||
.factory('calcJumpRange', function() {
|
/**
|
||||||
/**
|
* Calculate the maximum single jump range based on mass and a specific FSD
|
||||||
* Calculate the maximum single jump range based on mass and a specific FSD
|
*
|
||||||
* @param {number} mass Mass of a ship: laden, unlanden, partially laden, etc
|
* @param {number} mass Mass of a ship: laden, unlanden, partially laden, etc
|
||||||
* @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass
|
* @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass
|
||||||
* @param {number} fuel Optional - The fuel consumed during the jump (must be less than the drives max fuel per jump)
|
* @param {number} fuel Optional - The fuel consumed during the jump (must be less than the drives max fuel per jump)
|
||||||
* @return {number} Distance in Light Years
|
* @return {number} Distance in Light Years
|
||||||
*/
|
*/
|
||||||
return function(mass, fsd, fuel) {
|
.value('calcJumpRange', function(mass, fsd, fuel) {
|
||||||
return Math.pow(Math.min(fuel || Infinity, fsd.maxfuel) / fsd.fuelmul, 1 / fsd.fuelpower ) * fsd.optmass / mass;
|
return Math.pow(Math.min(fuel || Infinity, fsd.maxfuel) / fsd.fuelmul, 1 / fsd.fuelpower ) * fsd.optmass / mass;
|
||||||
};
|
|
||||||
})
|
})
|
||||||
.factory('calcShieldStrength', function() {
|
/**
|
||||||
/**
|
* Calculate the a ships shield strength based on mass, shield generator and shield boosters used.
|
||||||
* Calculate the a ships shield strength based on mass, shield generator and shield boosters used.
|
*
|
||||||
*
|
* @private
|
||||||
* @private
|
* @param {number} mass Current mass of the ship
|
||||||
* @param {number} mass Current mass of the ship
|
* @param {number} shields Base Shield strength MJ for ship
|
||||||
* @param {number} shields Base Shield strength MJ for ship
|
* @param {object} sg The shield generator used
|
||||||
* @param {object} sg The shield generator used
|
* @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any)
|
||||||
* @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any)
|
* @return {number} Approximate shield strengh in MJ
|
||||||
* @return {number} Approximate shield strengh in MJ
|
*/
|
||||||
*/
|
.value('calcShieldStrength', function (mass, shields, sg, multiplier) {
|
||||||
return function (mass, shields, sg, multiplier) {
|
if (!sg) {
|
||||||
if (!sg) {
|
return 0;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (mass <= sg.minmass) {
|
|
||||||
return shields * multiplier * sg.minmul;
|
|
||||||
}
|
|
||||||
if (mass < sg.optmass) {
|
|
||||||
return shields * multiplier * (sg.minmul + (mass - sg.minmass) / (sg.optmass - sg.minmass) * (sg.optmul - sg.minmul));
|
|
||||||
}
|
|
||||||
if (mass < sg.maxmass) {
|
|
||||||
return shields * multiplier * (sg.optmul + (mass - sg.optmass) / (sg.maxmass - sg.optmass) * (sg.maxmul - sg.optmul));
|
|
||||||
}
|
|
||||||
return shields * multiplier * sg.maxmul;
|
|
||||||
}
|
}
|
||||||
|
if (mass <= sg.minmass) {
|
||||||
|
return shields * multiplier * sg.minmul;
|
||||||
|
}
|
||||||
|
if (mass < sg.optmass) {
|
||||||
|
return shields * multiplier * (sg.minmul + (mass - sg.minmass) / (sg.optmass - sg.minmass) * (sg.optmul - sg.minmul));
|
||||||
|
}
|
||||||
|
if (mass < sg.maxmass) {
|
||||||
|
return shields * multiplier * (sg.optmul + (mass - sg.optmass) / (sg.maxmass - sg.optmass) * (sg.maxmul - sg.optmul));
|
||||||
|
}
|
||||||
|
return shields * multiplier * sg.maxmul;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,14 +2,15 @@
|
|||||||
@import 'fonts';
|
@import 'fonts';
|
||||||
@import 'utilities';
|
@import 'utilities';
|
||||||
@import 'icons';
|
@import 'icons';
|
||||||
|
@import 'header';
|
||||||
@import 'shipyard';
|
@import 'shipyard';
|
||||||
@import 'list';
|
@import 'list';
|
||||||
@import 'slot';
|
@import 'slot';
|
||||||
@import 'ship';
|
@import 'outfit';
|
||||||
@import 'select';
|
@import 'select';
|
||||||
@import 'charts';
|
@import 'charts';
|
||||||
@import 'meters';
|
@import 'meters';
|
||||||
|
@import 'error';
|
||||||
|
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
@@ -65,44 +66,19 @@ body {
|
|||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
a, a:visited {
|
||||||
background-color: @bg;
|
color: @fg;
|
||||||
margin: 0;
|
|
||||||
height: 55px;
|
|
||||||
line-height: 55px;
|
|
||||||
font-family: @fTitle;
|
|
||||||
vertical-align: middle;
|
|
||||||
|
|
||||||
a {
|
|
||||||
vertical-align: middle;
|
|
||||||
color: @warning;
|
|
||||||
|
|
||||||
&:visited {
|
|
||||||
color: @warning;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
color: teal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 1.3em;
|
|
||||||
display: inline-block;
|
|
||||||
margin:0px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
font-size: 0.3em;
|
font-size: 0.6em;
|
||||||
color: #999;
|
color: #999;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
header, footer {
|
footer {
|
||||||
.right {
|
.right {
|
||||||
float: right;
|
float: right;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|||||||
@@ -10,10 +10,10 @@
|
|||||||
|
|
||||||
@disabled: #888;
|
@disabled: #888;
|
||||||
@primary-disabled: darken(@primary, @disabledDarken);
|
@primary-disabled: darken(@primary, @disabledDarken);
|
||||||
@secondary-disabled: darken(@primary, @disabledDarken);
|
@secondary-disabled: darken(@secondary, @disabledDarken);
|
||||||
@warning-disabled: darken(@primary, @disabledDarken);
|
@warning-disabled: darken(@warning, @disabledDarken);
|
||||||
|
|
||||||
@bgBlack: rgba(0,0,0,0.6);
|
@bgBlack: rgba(0,0,0,0.9);
|
||||||
|
|
||||||
@primary-bg: fadeout(darken(@primary, 45%), 30%);
|
@primary-bg: fadeout(darken(@primary, 45%), 30%);
|
||||||
@secondary-bg: fadeout(darken(@secondary, @bgDarken), @bgTransparency); // Brown background
|
@secondary-bg: fadeout(darken(@secondary, @bgDarken), @bgTransparency); // Brown background
|
||||||
|
|||||||
10
app/less/error.less
Normal file
10
app/less/error.less
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
.error {
|
||||||
|
width: 50%;
|
||||||
|
margin: 10% auto;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
small {
|
||||||
|
color: @primary-disabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
85
app/less/header.less
Normal file
85
app/less/header.less
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
header {
|
||||||
|
background-color: @bg;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 1em;
|
||||||
|
height: 4em;
|
||||||
|
line-height: 4em;
|
||||||
|
font-family: @fTitle;
|
||||||
|
vertical-align: middle;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
.user-select-none();
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-header {
|
||||||
|
height: 100%;
|
||||||
|
z-index: 2;
|
||||||
|
padding : 0 1em;
|
||||||
|
cursor: pointer;
|
||||||
|
color: @warning;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
color: @warning-disabled;
|
||||||
|
cursor: default;
|
||||||
|
&:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover, &.selected {
|
||||||
|
background-color: @bgBlack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-list {
|
||||||
|
width: 200%;
|
||||||
|
font-family: @fStandard;
|
||||||
|
position: absolute;
|
||||||
|
padding: 0 1em 1em 0;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: @bgBlack;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
line-height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style: none;
|
||||||
|
margin-left: 1em;
|
||||||
|
line-height: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
vertical-align: middle;
|
||||||
|
color: @warning;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&:visited {
|
||||||
|
color: @warning;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
color: teal;
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
color: @primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 1.3em;
|
||||||
|
display: inline-block;
|
||||||
|
margin:0px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,24 @@
|
|||||||
|
|
||||||
#overview {
|
#overview {
|
||||||
margin: 0px auto;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
|
font-family: @fTitle;
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
|
color: @primary;
|
||||||
|
float: left;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
div {
|
#build {
|
||||||
display: inline-block;
|
float: right;
|
||||||
margin: 0 5px;
|
line-height: 2em;
|
||||||
|
|
||||||
|
input {
|
||||||
|
background: @primary-bg;
|
||||||
|
color: @fg;
|
||||||
|
border: none;
|
||||||
|
font-size: 0.8em;
|
||||||
|
line-height: 2em;
|
||||||
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
.slot-group {
|
.slot-group {
|
||||||
float: left;
|
float: left;
|
||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
background-color: @bgBlack;
|
|
||||||
.user-select-none();
|
.user-select-none();
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
|
||||||
|
|||||||
21
app/views/_header.html
Normal file
21
app/views/_header.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<header>
|
||||||
|
<div class="l" style="margin-right: 2em;"><a ui-sref="shipyard" class="logo shipyard"></a></div>
|
||||||
|
|
||||||
|
<div class="l menu">
|
||||||
|
<div class="menu-header" ng-class="{selected: openedMenu=='b', disabled: !bs.hasBuilds}" ng-click="openMenu('b')">Builds</div>
|
||||||
|
<div class="menu-list" ng-if="openedMenu=='b'">
|
||||||
|
<ul class="left" ng-repeat="(shipId,builds) in allBuilds">
|
||||||
|
{{ships[shipId].properties.name}}
|
||||||
|
<li ng-repeat="(name, build) in builds">
|
||||||
|
<a ui-sref-active="active" ui-sref="outfit({shipId:shipId, code:build, bn:name})" ng-bind="name"></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="r">
|
||||||
|
<a class="logo github" href="https://github.com/cmmcleod/ed-shipyard" target="_blank" title="Shipyard Github Project"></a>
|
||||||
|
<a class="logo reddit" href="#" target="_blank" title="Reddit Thread"></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</header>
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<header>
|
|
||||||
<div class="left">
|
|
||||||
<a ui-sref="shipyard" class="logo shipyard"></a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="right">
|
|
||||||
<a class="logo github" href="https://github.com/cmmcleod/ed-shipyard" target="_blank" title="Shipyard Github Project"></a>
|
|
||||||
<a class="logo reddit" href="#" target="_blank" title="Reddit Thread"></a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</header>
|
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
|
<div class="error">
|
||||||
|
<h1>
|
||||||
|
<span ng-if="msgPre">{{msgPre}}</span>
|
||||||
|
<small ng-if="msgHighlight">{{msgHighlight}}</small>
|
||||||
|
<span ng-if="msgPost">{{msgPost}}</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<!-- TODO: add awesome relevant SVG based on code /-->
|
||||||
|
|
||||||
<div>
|
<p ng-if="path" ng-bind="path"></p>
|
||||||
<h1>Error {{type}}</h1>
|
<p ng-if="type" ng-bind="type"></p>
|
||||||
<h2 ng-bind="message"></h2>
|
|
||||||
|
|
||||||
<p ng-if="path" ng-bind="path"></p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TODO: formatting /-->
|
|
||||||
<!-- TODO: add awesome relevant SVG based on code /-->
|
|
||||||
<!-- TODO: Add github issue link /-->
|
<!-- TODO: Add github issue link /-->
|
||||||
@@ -124,16 +124,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="build">
|
|
||||||
<h1 ng-bind="ship.name"></h1>
|
|
||||||
<input ng-model="buildName" placeholder="Build Name" />
|
|
||||||
<button ng-click="saveBuild()" ng-disabled="code == lastSaveCode">Save</button>
|
|
||||||
<button ng-click="reloadBuild()" ng-disabled="!lastSaveCode || code == lastSaveCode">Reload</button>
|
|
||||||
<button ui-sref="outfit({shipId: ship.id,code:null})" ng-disabled="!code">Clear</button>
|
|
||||||
<button ng-click="deleteBuild()" ng-disabled="!lastSaveCode">Delete</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="summary">
|
<div id="summary">
|
||||||
|
<div id="overview" class="list">
|
||||||
|
<h1 ng-bind="ship.name"></h1>
|
||||||
|
<div id="build">
|
||||||
|
<input ng-model="buildName" placeholder="Enter Build Name" />
|
||||||
|
<button ng-click="saveBuild()" ng-disabled="code == savedCode">Save</button>
|
||||||
|
<button ng-click="reloadBuild()" ng-disabled="!savedCode || code == savedCode">Reload</button>
|
||||||
|
<button ui-sref="outfit({shipId: ship.id,code:null})" ng-disabled="!code">Clear</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<div class="header">Maneuverability</div>
|
<div class="header">Maneuverability</div>
|
||||||
<div class="summary">
|
<div class="summary">
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"d3": "~3.5.5",
|
"d3": "~3.5.5",
|
||||||
"ng-lodash": "~0.2.0",
|
"ng-lodash": "~0.2.0",
|
||||||
"angular-ui-router": "0.2.14"
|
"angular-ui-router": "^0.2.14"
|
||||||
},
|
},
|
||||||
"overrides": {}
|
"overrides": {}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user