diff --git a/.jshintignore b/.jshintignore
deleted file mode 100644
index 4b71d483..00000000
--- a/.jshintignore
+++ /dev/null
@@ -1 +0,0 @@
-app/js/db.js
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..52561dc0
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,19 @@
+language: node_js
+notifications:
+ email: false
+sudo: false
+node_js:
+ - "0.12"
+cache:
+ directories:
+ - node_modules
+ - bower_components
+
+before_script:
+ - npm install -g gulp
+ - npm install -g bower
+ - bower install
+script:
+ - gulp lint
+ - gulp build-prod
+ - gulp test
\ No newline at end of file
diff --git a/README.md b/README.md
index 3eed4816..8c21a4e7 100755
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[ ](https://codeship.com/projects/85232) [](https://waffle.io/cmmcleod/coriolis) [](http://waffle.io/cmmcleod/coriolis)
+[](https://travis-ci.org/cmmcleod/coriolis) [](https://waffle.io/cmmcleod/coriolis) [](http://waffle.io/cmmcleod/coriolis)
@@ -6,11 +6,11 @@
The Coriolis project was inspired by [E:D Shipyard](http://www.edshipyard.com/) and, of course, [Elite Dangerous](http://www.elitedangerous.com). The ultimate goal of Coriolis is to provide rich features to support in-game play and planning while engaging the E:D community to support its development.
-Coriolis was created for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments.
+Coriolis was created for non-commercial purposes. Coriolis was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes. 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.
## Contributing
-Please [submit issues](https://github.com/cmmcleod/coriolis/issues), or better yet [pull requests](http://www.elitedangerous.com) for any corrections or additions to the database or the code.
+Please [submit issues](https://github.com/cmmcleod/coriolis/issues), or better yet [pull requests](https://github.com/cmmcleod/coriolis/pulls) for any corrections or additions to the database or the code.
### Feature Requests, Suggestions & Bugs
@@ -28,12 +28,15 @@ See [Data wiki](https://github.com/cmmcleod/coriolis/wiki/Database) for details
## License
-The MIT License
+All Data and [associated JSON](https://github.com/cmmcleod/coriolis/tree/master/data) files are intellectual property and copyright of Frontier Developments plc ('Frontier', 'Frontier Developments') and are subject to their
+[terms and conditions](https://www.frontierstore.net/terms-and-conditions/).
+
+The code (Javascript, CSS, HTML, and SVG files only) specificially for Coriolis.io is released under the MIT License.
Copyright (c) 2015 Coriolis.io, Colin McLeod
Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
+of this software (Javascript, CSS, HTML, and SVG files only), and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
diff --git a/app/icons/cancel-circle.svg b/app/icons/cancel-circle.svg
deleted file mode 100755
index dfb2f750..00000000
--- a/app/icons/cancel-circle.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/app/icons/feather.svg b/app/icons/feather.svg
new file mode 100644
index 00000000..4d512636
--- /dev/null
+++ b/app/icons/feather.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/icons/spinner11.svg b/app/icons/spinner11.svg
index 15cb4cdf..b6588c71 100755
--- a/app/icons/spinner11.svg
+++ b/app/icons/spinner11.svg
@@ -1,6 +1,3 @@
-
-
-
-
+
diff --git a/app/icons/switch.svg b/app/icons/switch.svg
index 4401f3f1..a3949c0d 100755
--- a/app/icons/switch.svg
+++ b/app/icons/switch.svg
@@ -1,6 +1,4 @@
-
-
-
-
+
+
diff --git a/app/index.html b/app/index.html
index 0de22846..6fd111f8 100755
--- a/app/index.html
+++ b/app/index.html
@@ -63,10 +63,10 @@
- Coriolis Shipyard was created for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments.
+ Coriolis was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes. 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.
diff --git a/app/js/app.js b/app/js/app.js
index c24da083..bfa1c99e 100755
--- a/app/js/app.js
+++ b/app/js/app.js
@@ -1,12 +1,13 @@
angular.module('app', ['ui.router', 'ct.ui.router.extras.sticky', 'ui.sortable', 'shipyard', 'ngLodash', 'app.templates'])
-.run(['$rootScope', '$location', '$window', '$document','$state','commonArray','shipPurpose','shipSize','hardPointClass','GroupMap', 'Persist', function ($rootScope, $location, $window, $doc, $state, CArr, shipPurpose, sz, hpc, GroupMap, Persist) {
+.run(['$rootScope', '$location', '$window', '$document', '$state', 'commonArray', 'shipPurpose', 'shipSize', 'hardPointClass', 'GroupMap', 'Persist',
+function($rootScope, $location, $window, $doc, $state, CArr, shipPurpose, sz, hpc, GroupMap, Persist) {
// App is running as a standalone web app on tablet/mobile
var isStandAlone = $window.navigator.standalone || ($window.external && $window.external.msIsSiteMode && $window.external.msIsSiteMode());
// 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();
- $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
});
// Track on Google analytics if available
@@ -16,12 +17,12 @@ angular.module('app', ['ui.router', 'ct.ui.router.extras.sticky', 'ui.sortable',
if (to.url) { // Only track states that have a URL
if ($window.ga) {
- ga('send', 'pageview', {page: $location.path()});
+ ga('send', 'pageview', { page: $location.path() });
}
if (isStandAlone) {
// Persist the current state
- Persist.setState({name: to.name, params: toParams});
+ Persist.setState({ name: to.name, params: toParams });
}
}
});
@@ -32,12 +33,14 @@ angular.module('app', ['ui.router', 'ct.ui.router.extras.sticky', 'ui.sortable',
$rootScope.SZ = sz;
$rootScope.HPC = hpc;
$rootScope.GMAP = GroupMap;
- $rootScope.STATUS = ['','DISABLED', 'OFF', 'ON'];
- $rootScope.STATUS_CLASS = ['','disabled', 'warning', 'secondary-disabled'];
+ $rootScope.insurance = { opts: [{ name: 'Standard', pct: 0.05 }, { name: 'Alpha', pct: 0.025 }, { name: 'Beta', pct: 0.035 }] };
+ $rootScope.discounts = { opts: [{ name: 'None', pct: 1 }, { name: 'Founders World - 10%', pct: 0.90 }] };
+ $rootScope.STATUS = ['', 'DISABLED', 'OFF', 'ON'];
+ $rootScope.STATUS_CLASS = ['', 'disabled', 'warning', 'secondary-disabled'];
$rootScope.title = 'Coriolis';
- $rootScope.cName = function (c) {
- return c.c? c.c.name? c.c.name : GroupMap[c.c.grp] : null;
+ $rootScope.cName = function(c) {
+ return c.c ? c.c.name ? c.c.name : GroupMap[c.c.grp] : null;
};
// Formatters
@@ -48,21 +51,21 @@ angular.module('app', ['ui.router', 'ct.ui.router.extras.sticky', 'ui.sortable',
$rootScope.fPct = d3.format('.2%');
$rootScope.f1Pct = d3.format('.1%');
$rootScope.fRPct = d3.format('%');
- $rootScope.fTime = function(d) { return Math.floor(d/60) + ":" + ("00" + Math.floor(d%60)).substr(-2,2); };
+ $rootScope.fTime = function(d) { return Math.floor(d / 60) + ':' + ('00' + Math.floor(d % 60)).substr(-2, 2); };
if (isStandAlone) {
var state = Persist.getState();
// If a previous state has been stored, load that state
if (state && state.name && state.params) {
- $state.go(state.name, state.params, {location:'replace'});
+ $state.go(state.name, state.params, { location: 'replace' });
} else {
- $state.go('shipyard', null, {location:'replace'}); // Default to home page
+ $state.go('shipyard', null, { location: 'replace' }); // Default to home page
}
}
// Global Event Listeners
- $doc.bind('keyup', function (e) {
- if(e.keyCode == 27) { // Escape Key
+ $doc.bind('keyup', function(e) {
+ if (e.keyCode == 27) { // Escape Key
$rootScope.$broadcast('close', e);
$rootScope.$apply();
} else {
@@ -70,7 +73,7 @@ angular.module('app', ['ui.router', 'ct.ui.router.extras.sticky', 'ui.sortable',
}
});
- $rootScope.bgClicked = function (e) {
+ $rootScope.bgClicked = function(e) {
$rootScope.$broadcast('close', e);
};
diff --git a/app/js/config.js b/app/js/config.js
index 6da41298..87e1f132 100755
--- a/app/js/config.js
+++ b/app/js/config.js
@@ -1,9 +1,9 @@
/**
* Sets up the routes and handlers before the Angular app is kicked off.
*/
-angular.module('app').config(['$provide','$stateProvider', '$urlRouterProvider', '$locationProvider', 'ShipsDB', function ($provide, $stateProvider, $urlRouterProvider, $locationProvider, ships) {
+angular.module('app').config(['$provide', '$stateProvider', '$urlRouterProvider', '$locationProvider', 'ShipsDB', function($provide, $stateProvider, $urlRouterProvider, $locationProvider, ships) {
// Use HTML5 push and replace state if possible
- $locationProvider.html5Mode({enabled: true, requireBase: false});
+ $locationProvider.html5Mode({ enabled: true, requireBase: false });
/**
* Set up all states and their routes.
*/
@@ -11,13 +11,13 @@ angular.module('app').config(['$provide','$stateProvider', '$urlRouterProvider',
.state('outfit', {
url: '/outfit/:shipId/:code?bn',
params: {
- shipId: { value: 'sidewinder', squash: false}, // Allow 'shipId' parameter to default to sidewinder
- code: { value: null, squash: true } // Allow 'code' parameter to be empty/optional
+ shipId: { value: 'sidewinder', squash: false }, // Allow 'shipId' parameter to default to sidewinder
+ 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
+ shipId: ['$stateParams', function($p) { // Ensure ship exists before loading controller
if (!ships[$p.shipId]) {
throw { type: 'no-ship', message: $p.shipId };
}
@@ -28,7 +28,7 @@ angular.module('app').config(['$provide','$stateProvider', '$urlRouterProvider',
.state('compare', {
url: '/compare/:name',
params: {
- name: {value: null, squash: true }
+ name: { value: null, squash: true }
},
templateUrl: 'views/page-comparison.html',
controller: 'ComparisonController',
@@ -41,26 +41,26 @@ angular.module('app').config(['$provide','$stateProvider', '$urlRouterProvider',
sticky: true
})
.state('shipyard', { url: '/', templateUrl: 'views/page-shipyard.html', controller: 'ShipyardController', sticky: true })
- .state('error', { params: {type:null, message:null, details: null }, templateUrl: 'views/page-error.html', controller: 'ErrorController', sticky: true })
+ .state('error', { params: { type: null, message: null, details: null }, templateUrl: 'views/page-error.html', controller: 'ErrorController', sticky: true })
// Modal States and views
- .state('modal', { abstract: true, views:{ "modal": { templateUrl: "views/_modal.html", controller: 'ModalController' } } })
- .state('modal.about', { views: { "modal-content": { templateUrl: "views/modal-about.html" } } })
- .state('modal.export', { params: {title:null, data: null, promise: null}, views: { "modal-content": { templateUrl: "views/modal-export.html", controller: 'ExportController' } } })
- .state('modal.import', { params: {obj:null}, views: { "modal-content": { templateUrl: "views/modal-import.html", controller: 'ImportController' } } })
- .state('modal.link', { params: {url:null}, views: { "modal-content": { templateUrl: "views/modal-link.html", controller: 'LinkController' } } })
- .state('modal.delete', { views: { "modal-content": { templateUrl: "views/modal-delete.html", controller: 'DeleteController' } } });
+ .state('modal', { abstract: true, views: { 'modal': { templateUrl: 'views/_modal.html', controller: 'ModalController' } } })
+ .state('modal.about', { views: { 'modal-content': { templateUrl: 'views/modal-about.html' } } })
+ .state('modal.export', { params: { title: null, data: null, promise: null }, views: { 'modal-content': { templateUrl: 'views/modal-export.html', controller: 'ExportController' } } })
+ .state('modal.import', { params: { obj: null }, views: { 'modal-content': { templateUrl: 'views/modal-import.html', controller: 'ImportController' } } })
+ .state('modal.link', { params: { url: null }, views: { 'modal-content': { templateUrl: 'views/modal-link.html', controller: 'LinkController' } } })
+ .state('modal.delete', { views: { 'modal-content': { templateUrl: 'views/modal-delete.html', controller: 'DeleteController' } } });
// Redirects
- $urlRouterProvider.when('/outfit','/outfit/sidewinder');
+ $urlRouterProvider.when('/outfit', '/outfit/sidewinder');
/**
* 404 Handler - Keep current URL/ do not redirect, change to error state.
*/
- $urlRouterProvider.otherwise(function ($injector, $location) {
+ $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});
+ $injector.get('$state').go('error', { type: 404, message: null, details: null }, { location: false, reload: true });
return $location.path;
});
@@ -69,10 +69,10 @@ angular.module('app').config(['$provide','$stateProvider', '$urlRouterProvider',
* redirects uncaught errors to the error page.
*
*/
- $provide.decorator('$exceptionHandler', ['$delegate', '$injector', function ($delegate, $injector) {
+ $provide.decorator('$exceptionHandler', ['$delegate', '$injector', function($delegate, $injector) {
return function(err, cause) {
// Go to error state, reload the controller, keep the current URL
- $injector.get('$state').go('error', {type:null, message: err.message, details: err.stack }, {location:false, reload:true});
+ $injector.get('$state').go('error', { type: null, message: err.message, details: err.stack }, { location: false, reload: true });
$delegate(err, cause);
};
}]);
diff --git a/app/js/controllers/controller-comparison.js b/app/js/controllers/controller-comparison.js
index 3ac392e9..d9914256 100755
--- a/app/js/controllers/controller-comparison.js
+++ b/app/js/controllers/controller-comparison.js
@@ -1,48 +1,49 @@
-angular.module('app').controller('ComparisonController', ['lodash', '$rootScope', '$filter', '$scope', '$state', '$stateParams', 'Utils', 'ShipFacets', 'ShipsDB', 'Ship', 'Persist', 'Serializer', function (_, $rootScope, $filter, $scope, $state, $stateParams, Utils, ShipFacets, Ships, Ship, Persist, Serializer) {
+angular.module('app').controller('ComparisonController', ['lodash', '$rootScope', '$filter', '$scope', '$state', '$stateParams', 'Utils', 'ShipFacets', 'ShipsDB', 'Ship', 'Persist', 'Serializer', function(_, $rootScope, $filter, $scope, $state, $stateParams, Utils, ShipFacets, Ships, Ship, Persist, Serializer) {
$rootScope.title = 'Coriolis - Compare';
$scope.predicate = 'name'; // Sort by ship name as default
$scope.desc = false;
- $scope.facetSortOpts = { containment: '#facet-container', orderChanged: function () { $scope.saved = false; } };
+ $scope.facetSortOpts = { containment: '#facet-container', orderChanged: function() { $scope.saved = false; } };
$scope.builds = [];
$scope.unusedBuilds = [];
$scope.name = $stateParams.name;
$scope.compareMode = !$stateParams.code;
$scope.importObj = {}; // Used for importing comparison builds (from permalinked comparison)
- var defaultFacets = [9,6,4,1,3,2]; // Reverse order of Armour, Shields, Speed, Jump Range, Cargo Capacity, Cost
+ var defaultFacets = [9, 6, 4, 1, 3, 2]; // Reverse order of Armour, Shields, Speed, Jump Range, Cargo Capacity, Cost
var facets = $scope.facets = angular.copy(ShipFacets);
+ var shipId, buildName, comparisonData;
/**
* Add an existing build to the comparison. The build must be saved locally.
- * @param {string} shipId The unique ship key/id
- * @param {string} buildName The build name
+ * @param {string} id The unique ship key/id
+ * @param {string} name The build name
*/
- $scope.addBuild = function (shipId, buildName, code) {
- var data = Ships[shipId]; // Get ship properties
- code = code? code : Persist.builds[shipId][buildName]; // Retrieve build code if not passed
- var b = new Ship(shipId, data.properties, data.slots); // Create a new Ship instance
+ $scope.addBuild = function(id, name, code) {
+ var data = Ships[id]; // Get ship properties
+ code = code ? code : Persist.builds[id][name]; // Retrieve build code if not passed
+ var b = new Ship(id, data.properties, data.slots); // Create a new Ship instance
Serializer.toShip(b, code); // Populate components from code
// Extend ship instance and add properties below
- b.buildName = buildName;
+ b.buildName = name;
b.code = code;
b.pctRetracted = b.powerRetracted / b.powerAvailable;
b.pctDeployed = b.powerDeployed / b.powerAvailable;
$scope.builds.push(b); // Add ship build to comparison
$scope.builds = $filter('orderBy')($scope.builds, $scope.predicate, $scope.desc); // Resort
- _.remove($scope.unusedBuilds, function (b) { // Remove from unused builds
- return b.id == shipId && b.buildName == buildName;
+ _.remove($scope.unusedBuilds, function(o) { // Remove from unused builds
+ return o.id == id && o.buildName == name;
});
$scope.saved = false;
};
/**
* Removes a build from the comparison
- * @param {string} shipId The unique ship key/id
- * @param {string} buildName The build name
+ * @param {string} id The unique ship key/id
+ * @param {string} name The build name
*/
- $scope.removeBuild = function (shipId, buildName) {
- _.remove($scope.builds, function (b) {
- if (b.id == shipId && b.buildName == buildName) {
- $scope.unusedBuilds.push({id: shipId, buildName: buildName, name: b.name}); // Add build back to unused builds
+ $scope.removeBuild = function(id, name) {
+ _.remove($scope.builds, function(s) {
+ if (s.id == id && s.buildName == name) {
+ $scope.unusedBuilds.push({ id: id, buildName: name, name: s.name }); // Add build back to unused builds
return true;
}
return false;
@@ -54,7 +55,7 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope'
* Toggles the selected the set of facets used in the comparison
* @param {number} i The index of the facet in facets
*/
- $scope.toggleFacet = function (i) {
+ $scope.toggleFacet = function(i) {
facets[i].active = !facets[i].active;
$scope.tblUpdate = !$scope.tblUpdate; // Simple switch to trigger the table to update
$scope.saved = false;
@@ -64,12 +65,12 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope'
* Click handler for sorting by facets in the table
* @param {object} e Event object
*/
- $scope.handleClick = function (e) {
+ $scope.handleClick = function(e) {
var elem = angular.element(e.target);
- if(elem.attr('prop')) { // Get component ID
+ if (elem.attr('prop')) { // Get component ID
$scope.sort(elem.attr('prop'));
- }
- else if (elem.attr('del')) { // Delete index
+
+ } else if (elem.attr('del')) { // Delete index
$scope.removeBuild(elem.attr('del'));
}
};
@@ -78,8 +79,8 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope'
* Sort the comparison array based on the selected facet / ship property
* @param {string} key Ship property
*/
- $scope.sort = function (key) {
- $scope.desc = ($scope.predicate == key)? !$scope.desc : $scope.desc;
+ $scope.sort = function(key) {
+ $scope.desc = $scope.predicate == key ? !$scope.desc : $scope.desc;
$scope.predicate = key;
$scope.builds = $filter('orderBy')($scope.builds, $scope.predicate, $scope.desc);
};
@@ -94,12 +95,12 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope'
}
var selectedFacets = [];
facets.forEach(function(f) {
- if(f.active) {
+ if (f.active) {
selectedFacets.unshift(f.index);
}
});
Persist.saveComparison($scope.name, $scope.builds, selectedFacets);
- $state.go('compare', {name: $scope.name}, {location:'replace', notify:false});
+ $state.go('compare', { name: $scope.name }, { location: 'replace', notify: false });
$scope.saved = true;
};
@@ -108,7 +109,7 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope'
*/
$scope.delete = function() {
Persist.deleteComparison($scope.name);
- $state.go('compare', {name: null}, {location:'replace', reload:true});
+ $state.go('compare', { name: null }, { location: 'replace', reload: true });
};
/**
@@ -134,7 +135,7 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope'
*/
$scope.permalink = function(e) {
e.stopPropagation();
- $state.go('modal.link', {url: genPermalink()});
+ $state.go('modal.link', { url: genPermalink() });
};
/**
@@ -147,14 +148,14 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope'
e.stopPropagation();
// Make a request to goo.gl to shorten the URL, returns a promise
var promise = Utils.shortenUrl( genPermalink()).then(
- function (shortUrl) {
+ function(shortUrl) {
return Utils.comparisonBBCode(facets, $scope.builds, shortUrl);
},
- function (e) {
- return 'Error - ' + e.statusText;
+ function(err) {
+ return 'Error - ' + err.statusText;
}
);
- $state.go('modal.export', {promise: promise, title:'Forum BBCode'});
+ $state.go('modal.export', { promise: promise, title: 'Forum BBCode' });
};
/**
@@ -164,7 +165,7 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope'
function genPermalink() {
var selectedFacets = [];
facets.forEach(function(f) {
- if(f.active) {
+ if (f.active) {
selectedFacets.unshift(f.index);
}
});
@@ -175,7 +176,7 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope'
$scope.predicate,
$scope.desc
);
- return $state.href('comparison', {code: code}, {absolute:true});
+ return $state.href('comparison', { code: code }, { absolute: true });
}
/* Event listeners */
@@ -184,7 +185,6 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope'
});
/* Initialization */
- var shipId, buildName, comparisonData;
if ($scope.compareMode) {
if ($scope.name == 'all') {
for (shipId in Persist.builds) {
@@ -195,13 +195,13 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope'
} else {
for (shipId in Persist.builds) {
for (buildName in Persist.builds[shipId]) {
- $scope.unusedBuilds.push({id: shipId, buildName: buildName, name: Ships[shipId].properties.name});
+ $scope.unusedBuilds.push({ id: shipId, buildName: buildName, name: Ships[shipId].properties.name });
}
}
comparisonData = Persist.getComparison($scope.name);
if (comparisonData) {
defaultFacets = comparisonData.facets;
- comparisonData.builds.forEach(function (b) {
+ comparisonData.builds.forEach(function(b) {
$scope.addBuild(b.shipId, b.buildName);
});
$scope.saved = true;
@@ -214,9 +214,9 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope'
$scope.name = comparisonData.n;
$scope.predicate = comparisonData.p;
$scope.desc = comparisonData.d;
- comparisonData.b.forEach(function (build) {
+ comparisonData.b.forEach(function(build) {
$scope.addBuild(build.s, build.n, build.c);
- if(!$scope.importObj[build.s]) {
+ if (!$scope.importObj[build.s]) {
$scope.importObj[build.s] = {};
}
$scope.importObj[build.s][build.n] = build.c;
@@ -226,9 +226,9 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope'
}
}
// Replace fmt with actual format function as defined in rootScope and retain original index
- facets.forEach(function(f,i) { f.fmt = $rootScope[f.fmt]; f.index = i; });
+ facets.forEach(function(f, i) { f.fmt = $rootScope[f.fmt]; f.index = i; });
// Remove default facets, mark as active, and add them back in selected order
- _.pullAt(facets, defaultFacets).forEach(function (f) { f.active = true; facets.unshift(f); });
+ _.pullAt(facets, defaultFacets).forEach(function(f) { f.active = true; facets.unshift(f); });
$scope.builds = $filter('orderBy')($scope.builds, $scope.predicate, $scope.desc);
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/controllers/controller-delete.js b/app/js/controllers/controller-delete.js
index 2d500916..5aec906e 100755
--- a/app/js/controllers/controller-delete.js
+++ b/app/js/controllers/controller-delete.js
@@ -1,7 +1,7 @@
-angular.module('app').controller('DeleteController', ['$scope', 'Persist', function ($scope, Persist) {
- $scope.deleteAll = function () {
+angular.module('app').controller('DeleteController', ['$scope', 'Persist', function($scope, Persist) {
+ $scope.deleteAll = function() {
Persist.deleteAll();
$scope.$parent.dismiss();
};
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/controllers/controller-error.js b/app/js/controllers/controller-error.js
index 4274c89a..b1f19cf1 100755
--- a/app/js/controllers/controller-error.js
+++ b/app/js/controllers/controller-error.js
@@ -1,5 +1,5 @@
angular.module('app')
-.controller('ErrorController', ['$window','$rootScope','$scope','$stateParams', '$location', function ($window, $rootScope, $scope, $p, $location) {
+.controller('ErrorController', ['$window', '$rootScope', '$scope', '$stateParams', '$location', function($window, $rootScope, $scope, $p, $location) {
$rootScope.title = 'Error';
$scope.path = $location.path();
$scope.type = $p.type || 'unknown';
@@ -21,9 +21,9 @@ angular.module('app')
$scope.details = $p.details;
break;
default:
- $scope.msgPre = "Uh, Jameson, we have a problem..";
+ $scope.msgPre = 'Uh, Jameson, we have a problem..';
$scope.errorMessage = $p.message;
$scope.details = $p.details;
}
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/controllers/controller-export.js b/app/js/controllers/controller-export.js
index dd9c44f6..bc10c3f3 100755
--- a/app/js/controllers/controller-export.js
+++ b/app/js/controllers/controller-export.js
@@ -1,18 +1,18 @@
-angular.module('app').controller('ExportController', ['$scope', '$stateParams', function ($scope, $stateParams) {
+angular.module('app').controller('ExportController', ['$scope', '$stateParams', function($scope, $stateParams) {
$scope.title = $stateParams.title || 'Export';
if ($stateParams.promise) {
$scope.export = 'Generating...';
- $stateParams.promise.then(function(data){
+ $stateParams.promise.then(function(data) {
$scope.export = data;
});
} else {
$scope.export = angular.toJson($stateParams.data, true);
}
- $scope.onTextClick = function ($event) {
+ $scope.onTextClick = function($event) {
$event.target.select();
};
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/controllers/controller-import.js b/app/js/controllers/controller-import.js
index f1971a7a..88f10620 100755
--- a/app/js/controllers/controller-import.js
+++ b/app/js/controllers/controller-import.js
@@ -1,4 +1,4 @@
-angular.module('app').controller('ImportController', ['$scope', '$stateParams', 'ShipsDB', 'Ship', 'Persist', 'Serializer', function ($scope, $stateParams, Ships, Ship, Persist, Serializer) {
+angular.module('app').controller('ImportController', ['$scope', '$stateParams', 'ShipsDB', 'Ship', 'Persist', 'Serializer', function($scope, $stateParams, Ships, Ship, Persist, Serializer) {
$scope.jsonValid = false;
$scope.importData = null;
$scope.errorMsg = null;
@@ -21,7 +21,7 @@ angular.module('app').controller('ImportController', ['$scope', '$stateParams',
return;
}
- if(typeof importObj != 'object') {
+ if (typeof importObj != 'object') {
$scope.errorMsg = 'Must be an object!';
return;
}
@@ -48,7 +48,7 @@ angular.module('app').controller('ImportController', ['$scope', '$stateParams',
}
}
} else {
- $scope.errorMsg = '"' + shipId + '" is not a valid Ship Id!';
+ $scope.errorMsg = '"' + shipId + '"" is not a valid Ship Id!';
return;
}
$scope.builds = importObj.builds;
@@ -57,7 +57,7 @@ angular.module('app').controller('ImportController', ['$scope', '$stateParams',
$scope.jsonValid = true;
};
- $scope.hasBuild = function (shipId, name) {
+ $scope.hasBuild = function(shipId, name) {
return Persist.getBuild(shipId, name) !== null;
};
@@ -98,4 +98,4 @@ angular.module('app').controller('ImportController', ['$scope', '$stateParams',
}
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/controllers/controller-link.js b/app/js/controllers/controller-link.js
index 75041907..59c4690c 100755
--- a/app/js/controllers/controller-link.js
+++ b/app/js/controllers/controller-link.js
@@ -1,16 +1,16 @@
-angular.module('app').controller('LinkController', ['$scope', 'Utils', '$stateParams', function ($scope, Utils, $stateParams) {
+angular.module('app').controller('LinkController', ['$scope', 'Utils', '$stateParams', function($scope, Utils, $stateParams) {
$scope.url = $stateParams.url;
$scope.shortenedUrl = 'Shortening...';
- $scope.onTextClick = function ($event) {
+ $scope.onTextClick = function($event) {
$event.target.select();
};
Utils.shortenUrl($scope.url)
.then(function(url) {
$scope.shortenedUrl = url;
- },function(e) {
+ }, function(e) {
$scope.shortenedUrl = 'Error - ' + e.statusText;
});
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/controllers/controller-modal.js b/app/js/controllers/controller-modal.js
index 203d6155..c73135ad 100755
--- a/app/js/controllers/controller-modal.js
+++ b/app/js/controllers/controller-modal.js
@@ -1,9 +1,9 @@
-angular.module('app').controller('ModalController', ['$rootScope','$scope', '$state', function ($rootScope, $scope, $state) {
+angular.module('app').controller('ModalController', ['$rootScope', '$scope', '$state', function($rootScope, $scope, $state) {
$scope.dismiss = function() {
if ($rootScope.prevState) {
var state = $rootScope.prevState;
- $state.go(state.name, state.params, {location: 'replace', reload: false});
+ $state.go(state.name, state.params, { location: 'replace', reload: false });
} else {
$state.go('shipyard');
}
@@ -11,4 +11,4 @@ angular.module('app').controller('ModalController', ['$rootScope','$scope', '$st
$scope.$on('close', $scope.dismiss);
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/controllers/controller-outfit.js b/app/js/controllers/controller-outfit.js
index 1159e4a1..91445217 100755
--- a/app/js/controllers/controller-outfit.js
+++ b/app/js/controllers/controller-outfit.js
@@ -1,4 +1,4 @@
-angular.module('app').controller('OutfitController', ['$window','$rootScope','$scope', '$state', '$stateParams', 'ShipsDB', 'Ship', 'Components', 'Serializer', 'Persist', function ($window, $rootScope, $scope, $state, $p, Ships, Ship, Components, Serializer, Persist) {
+angular.module('app').controller('OutfitController', ['$window', '$rootScope', '$scope', '$state', '$stateParams', 'ShipsDB', 'Ship', 'Components', 'Serializer', 'Persist', 'calcTotalRange', 'calcSpeed', function($window, $rootScope, $scope, $state, $p, Ships, Ship, Components, Serializer, Persist, calcTotalRange, calcSpeed) {
var data = 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 win = angular.element($window); // Angularized window object for event triggering
@@ -12,7 +12,7 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s
}
$scope.buildName = $p.bn;
- $rootScope.title = ship.name + ($scope.buildName? ' - ' + $scope.buildName : '');
+ $rootScope.title = ship.name + ($scope.buildName ? ' - ' + $scope.buildName : '');
$scope.ship = ship;
$scope.pp = ship.common[0]; // Power Plant
$scope.th = ship.common[1]; // Thruster
@@ -39,8 +39,7 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s
$scope.jrSeries = {
xMin: 0,
xMax: ship.cargoCapacity,
- // Slightly higher than actual based bacuse components are excluded
- yMax: ship.jumpRangeWithMass(ship.unladenMass),
+ yMax: ship.unladenRange,
yMin: 0,
func: function(cargo) { // X Axis is Cargo
return ship.jumpRangeWithMass(ship.unladenMass + $scope.fuel + cargo, $scope.fuel);
@@ -49,15 +48,59 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s
$scope.jrChart = {
labels: {
xAxis: {
- title:'Cargo',
+ title: 'Cargo',
unit: 'T'
},
yAxis: {
- title:'Jump Range',
+ title: 'Jump Range',
unit: 'LY'
}
- },
- watch: $scope.fsd
+ }
+ };
+
+ $scope.trSeries = {
+ xMin: 0,
+ xMax: ship.cargoCapacity,
+ yMax: ship.unladenTotalRange,
+ yMin: 0,
+ func: function(cargo) { // X Axis is Cargo
+ return calcTotalRange(ship.unladenMass + cargo, $scope.fsd.c, $scope.fuel);
+ }
+ };
+ $scope.trChart = {
+ labels: {
+ xAxis: {
+ title: 'Cargo',
+ unit: 'T'
+ },
+ yAxis: {
+ title: 'Total Range',
+ unit: 'LY'
+ }
+ }
+ };
+
+ $scope.speedSeries = {
+ xMin: 0,
+ xMax: ship.cargoCapacity,
+ yMax: 500,
+ yMin: 0,
+ series: ['speed', 'boost'],
+ func: function(cargo) { // X Axis is Cargo
+ return calcSpeed(ship.unladenMass + $scope.fuel + cargo, ship.speed, ship.boost, $scope.th.c);
+ }
+ };
+ $scope.speedChart = {
+ labels: {
+ xAxis: {
+ title: 'Cargo',
+ unit: 'T'
+ },
+ yAxis: {
+ title: 'Speed',
+ unit: 'm/s'
+ }
+ }
};
/**
@@ -83,14 +126,14 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s
* @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, id) {
e.stopPropagation();
- var id = angular.element(e.target).attr('cpid'); // Get component ID
+ id = id || angular.element(e.target).attr('cpid'); // Get component ID
if (id) {
if (id == 'empty') {
ship.use(slot, null, null);
- } else if(type == 'h') {
+ } else if (type == 'h') {
ship.use(slot, id, Components.hardpoints(id));
} else if (type == 'c') {
ship.use(slot, id, Components.common(ship.common.indexOf(slot), id));
@@ -100,8 +143,7 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s
ship.useBulkhead(id);
}
$scope.selectedSlot = null;
- $scope.code = Serializer.fromShip(ship);
- updateState();
+ updateState(Serializer.fromShip(ship));
}
};
@@ -111,11 +153,24 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s
$scope.reloadBuild = function() {
if ($scope.buildName && $scope.savedCode) {
Serializer.toShip(ship, $scope.savedCode); // Repopulate with components from last save
- $scope.code = $scope.savedCode;
- updateState();
+ updateState($scope.savedCode);
}
};
+ /**
+ * Strip ship to D-class and no other components.
+ */
+ $scope.stripBuild = function() {
+ for (var i = 0, l = ship.common.length - 1; i < l; i++) { // All except Fuel Tank
+ var id = ship.common[i].maxClass + 'D';
+ ship.use(ship.common[i], id, Components.common(i, id));
+ }
+ ship.hardpoints.forEach(function(slot) { ship.use(slot, null, null); });
+ ship.internal.forEach(function(slot) { ship.use(slot, null, null); });
+ ship.useBulkhead(0);
+ updateState(Serializer.fromShip(ship));
+ };
+
/**
* Save the current build. Will replace the saved build if there is one
* for this ship & with the exact name.
@@ -132,7 +187,7 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s
if ($scope.code != $scope.savedCode) {
Persist.saveBuild(ship.id, $scope.buildName, $scope.code);
$scope.savedCode = $scope.code;
- updateState();
+ updateState($scope.code);
}
};
@@ -142,13 +197,13 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s
*/
$scope.deleteBuild = function() {
Persist.deleteBuild(ship.id, $scope.buildName);
- $state.go('outfit', {shipId: ship.id, code: null, bn: null}, {location:'replace', reload:true});
+ $state.go('outfit', { shipId: ship.id, code: null, bn: null }, { location: 'replace', reload: true });
};
/**
* On build name change, retrieve the existing saved code if there is one
*/
- $scope.bnChange = function(){
+ $scope.bnChange = function() {
$scope.savedCode = Persist.getBuild(ship.id, $scope.buildName);
};
@@ -165,13 +220,13 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s
* @param {[type]} key [description]
* @return {[type]} [description]
*/
- $scope.sortCost = function (key) {
- $scope.costDesc = ($scope.costPredicate == key)? !$scope.costDesc : $scope.costDesc;
+ $scope.sortCost = function(key) {
+ $scope.costDesc = $scope.costPredicate == key ? !$scope.costDesc : $scope.costDesc;
$scope.costPredicate = key;
};
- $scope.sortPwr = function (key) {
- $scope.pwrDesc = ($scope.pwrPredicate == key)? !$scope.pwrDesc : $scope.pwrDesc;
+ $scope.sortPwr = function(key) {
+ $scope.pwrDesc = $scope.pwrPredicate == key ? !$scope.pwrDesc : $scope.pwrDesc;
$scope.pwrPredicate = key;
};
@@ -181,49 +236,47 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s
*/
$scope.togglePwr = function(c) {
ship.setSlotEnabled(c, !c.enabled);
- $scope.code = Serializer.fromShip(ship);
- updateState();
+ updateState(Serializer.fromShip(ship));
};
- $scope.incPriority = function (c) {
+ $scope.incPriority = function(c) {
if (ship.changePriority(c, c.priority + 1)) {
- $scope.code = Serializer.fromShip(ship);
- updateState();
+ updateState(Serializer.fromShip(ship));
}
};
- $scope.decPriority = function (c) {
+ $scope.decPriority = function(c) {
if (ship.changePriority(c, c.priority - 1)) {
- $scope.code = Serializer.fromShip(ship);
- updateState();
+ updateState(Serializer.fromShip(ship));
}
};
- $scope.fuelChange = function (fuel) {
+ $scope.fuelChange = function(fuel) {
$scope.fuel = fuel;
win.triggerHandler('render');
};
- $scope.statusRetracted = function (slot) {
+ $scope.statusRetracted = function(slot) {
return ship.getSlotStatus(slot, false);
};
- $scope.statusDeployed = function (slot) {
+ $scope.statusDeployed = function(slot) {
return ship.getSlotStatus(slot, true);
};
// Utilify functions
- function updateState() {
- $state.go('outfit', {shipId: ship.id, code: $scope.code, bn: $scope.buildName}, {location:'replace', notify:false});
- $scope.jrSeries.xMax = ship.cargoCapacity;
- $scope.jrSeries.yMax = ship.jumpRangeWithMass(ship.unladenMass);
- $scope.jrSeries.mass = ship.unladenMass;
+ function updateState(code) {
+ $scope.code = code;
+ $state.go('outfit', { shipId: ship.id, code: $scope.code, bn: $scope.buildName }, { location: 'replace', notify: false });
+ $scope.speedSeries.xMax = $scope.trSeries.xMax = $scope.jrSeries.xMax = ship.cargoCapacity;
+ $scope.jrSeries.yMax = ship.unladenRange;
+ $scope.trSeries.yMax = ship.unladenTotalRange;
win.triggerHandler('pwrchange');
}
// Hide any open menu/slot/etc if the background is clicked
- $scope.$on('close', function () {
+ $scope.$on('close', function() {
$scope.selectedSlot = null;
});
diff --git a/app/js/controllers/controller-shipyard.js b/app/js/controllers/controller-shipyard.js
index 78751edc..1cc5477f 100755
--- a/app/js/controllers/controller-shipyard.js
+++ b/app/js/controllers/controller-shipyard.js
@@ -1,4 +1,4 @@
-angular.module('app').controller('ShipyardController', ['$rootScope', 'ShipsDB', function ($rootScope, ships) {
+angular.module('app').controller('ShipyardController', ['$rootScope', 'ShipsDB', function($rootScope, ships) {
$rootScope.title = 'Coriolis';
$rootScope.ships = ships;
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/directives/directive-area-chart.js b/app/js/directives/directive-area-chart.js
index a7222eb7..0ade7b93 100755
--- a/app/js/directives/directive-area-chart.js
+++ b/app/js/directives/directive-area-chart.js
@@ -1,9 +1,7 @@
-angular.module('app').directive('areaChart', ['$window', function ($window) {
-
-
+angular.module('app').directive('areaChart', ['$window', function($window) {
return {
restrict: 'A',
- scope:{
+ scope: {
config: '=',
series: '='
},
@@ -11,64 +9,87 @@ angular.module('app').directive('areaChart', ['$window', function ($window) {
var series = scope.series,
config = scope.config,
labels = config.labels,
- margin = {top: 15, right: 15, bottom: 35, left: 60},
+ margin = { top: 15, right: 15, bottom: 35, left: 60 },
fmt = d3.format('.3r'),
fmtLong = d3.format('.2f'),
func = series.func,
drag = d3.behavior.drag(),
dragging = false,
// Define Axes
- xAxis = d3.svg.axis().outerTickSize(0).orient("bottom").tickFormat(d3.format('.2r')),
- yAxis = d3.svg.axis().ticks(6).outerTickSize(0).orient("left").tickFormat(fmt),
+ xAxis = d3.svg.axis().outerTickSize(0).orient('bottom').tickFormat(d3.format('.2r')),
+ yAxis = d3.svg.axis().ticks(6).outerTickSize(0).orient('left').tickFormat(fmt),
x = d3.scale.linear(),
- y = d3.scale.linear();
+ y = d3.scale.linear(),
+ data = [];
// Create chart
- var svg = d3.select(element[0]).append("svg");
- var vis = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
+ var svg = d3.select(element[0]).append('svg');
+ var vis = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// Define Area
var area = d3.svg.area();
- var gradient = vis.append("defs")
- .append("linearGradient")
- .attr("id", "gradient")
- .attr("x1", "0%").attr("y1", "0%")
- .attr("x2", "100%").attr("y2", "100%")
- .attr("spreadMethod", "pad");
- gradient.append("stop")
- .attr("offset", "0%")
- .attr("stop-color", "#ff8c0d")
- .attr("stop-opacity", 1);
- gradient.append("stop")
- .attr("offset", "100%")
- .attr("stop-color", "#ff3b00")
- .attr("stop-opacity", 1);
+ var gradient = vis.append('defs')
+ .append('linearGradient')
+ .attr('id', 'gradient')
+ .attr('x1', '0%').attr('y1', '0%')
+ .attr('x2', '100%').attr('y2', '100%')
+ .attr('spreadMethod', 'pad');
+ gradient.append('stop')
+ .attr('offset', '0%')
+ .attr('stop-color', '#ff8c0d')
+ .attr('stop-opacity', 1);
+ gradient.append('stop')
+ .attr('offset', '100%')
+ .attr('stop-color', '#ff3b00')
+ .attr('stop-opacity', 1);
// Create Y Axis SVG Elements
- var yTxt = vis.append("g").attr("class", "y axis")
- .append("text")
- .attr("transform", "rotate(-90)")
- .attr("y", -50)
- .attr("dy", ".1em")
- .style("text-anchor", "middle")
+ var yTxt = vis.append('g').attr('class', 'y axis')
+ .append('text')
+ .attr('transform', 'rotate(-90)')
+ .attr('y', -50)
+ .attr('dy', '.1em')
+ .style('text-anchor', 'middle')
.text(labels.yAxis.title + ' (' + labels.yAxis.unit + ')');
// Create X Axis SVG Elements
- var xLbl = vis.append("g").attr("class", "x axis");
- var xTxt = xLbl.append("text")
- .attr("y", 30)
- .attr("dy", ".1em")
- .style("text-anchor", "middle")
+ var xLbl = vis.append('g').attr('class', 'x axis');
+ var xTxt = xLbl.append('text')
+ .attr('y', 30)
+ .attr('dy', '.1em')
+ .style('text-anchor', 'middle')
.text(labels.xAxis.title + ' (' + labels.xAxis.unit + ')');
// Create and Add tooltip
- var tip = vis.append("g").style("display", "none");
- tip.append("rect").attr("width","4em").attr("height", "2em").attr("x", "0.5em").attr("y","-1em").attr("class","tip");
- tip.append("circle")
- .attr("class", "marker")
- .attr("r", 4);
- tip.append("text").attr("class", 'label x').attr("y", "-0.25em");
- tip.append("text").attr("class", 'label y').attr("y", '0.85em');
+ var tip = vis.append('g').style('display', 'none');
+ tip.append('rect').attr('width', '4.5em').attr('height', '2em').attr('x', '0.5em').attr('y', '-1em').attr('class', 'tip');
+ tip.append('circle')
+ .attr('class', 'marker')
+ .attr('r', 4);
+ tip.append('text').attr('class', 'label x').attr('y', '-0.25em');
+ tip.append('text').attr('class', 'label y').attr('y', '0.85em');
+
+ vis.insert('path', ':first-child') // Area/Path to appear behind everything else
+ .data([data])
+ .attr('class', 'area')
+ .attr('fill', 'url(#gradient)')
+ .attr('d', area)
+ .on('mouseover', showTip)
+ .on('mouseout', hideTip)
+ .on('mousemove', moveTip)
+ .call(drag);
+
+ drag
+ .on('dragstart', function() {
+ dragging = true;
+ moveTip.call(this);
+ showTip();
+ })
+ .on('dragend', function() {
+ dragging = false;
+ hideTip();
+ })
+ .on('drag', moveTip);
/**
* Watch for changes in the series data (mass changes, etc)
@@ -80,75 +101,55 @@ angular.module('app').directive('areaChart', ['$window', function ($window) {
var width = element[0].parentElement.offsetWidth,
height = width * 0.5,
w = width - margin.left - margin.right,
- h = height - margin.top - margin.bottom,
- data = [];
+ h = height - margin.top - margin.bottom;
+
+ data.length = 0; // Reset Data array
if (series.xMax == series.xMin) {
var yVal = func(series.xMin);
data.push([ series.xMin, yVal ]);
data.push([ series.xMin, yVal ]);
- area.x(function(d,i) { return i * w; }).y0(h).y1(function(d) { return y(d[1]); });
+ area.x(function(d, i) { return i * w; }).y0(h).y1(function(d) { return y(d[1]); });
} else {
- for (var d = series.xMin; d <= series.xMax; d += 1) {
- data.push([ d, func(d) ]);
+ for (var val = series.xMin; val <= series.xMax; val += 1) {
+ data.push([ val, func(val) ]);
}
area.x(function(d) { return x(d[0]); }).y0(h).y1(function(d) { return y(d[1]); });
}
// Update Chart Size
- svg.attr("width", width).attr("height", height);
- // Update domain and scale for axes;
+ svg.attr('width', width).attr('height', height);
+ // Update domain and scale for axes
x.range([0, w]).domain([series.xMin, series.xMax]).clamp(true);
xAxis.scale(x);
- xLbl.attr("transform", "translate(0," + h + ")");
- xTxt.attr("x", w/2);
+ xLbl.attr('transform', 'translate(0,' + h + ')');
+ xTxt.attr('x', w / 2);
y.range([h, 0]).domain([series.yMin, series.yMax]);
yAxis.scale(y);
- yTxt.attr("x", -h/2);
- vis.selectAll(".y.axis").call(yAxis);
- vis.selectAll(".x.axis").call(xAxis);
+ yTxt.attr('x', -h / 2);
+ vis.selectAll('.y.axis').call(yAxis);
+ vis.selectAll('.x.axis').call(xAxis);
- // Remove existing elements
- vis.selectAll('path.area').remove();
-
- vis.insert("path",':first-child') // Area/Path to appear behind everything else
- .datum(data)
- .attr("class", "area")
- .attr('fill', 'url(#gradient)')
- .attr("d", area)
- .on('mouseover', showTip)
- .on('mouseout', hideTip)
- .on('mousemove', moveTip)
- .call(drag);
-
- drag
- .on('dragstart', function() {
- dragging = true;
- moveTip.call(this);
- showTip();
- })
- .on("dragend", function() {
- dragging = false;
- hideTip();
- })
- .on('drag', moveTip);
+ vis.selectAll('path.area') // Area/Path to appear behind everything else
+ .data([data])
+ .attr('d', area);
}
function showTip() {
- tip.style("display", null);
+ tip.style('display', null);
}
function hideTip() {
if (!dragging) {
- tip.style("display", "none");
+ tip.style('display', 'none');
}
}
function moveTip() {
- var xPos = d3.mouse(this)[0], x0 = x.invert(xPos), y0 = func(x0), flip = (x0 / x.domain()[1] > 0.75);
- tip.attr("transform", "translate(" + x(x0) + "," + y(y0) + ")");
- tip.selectAll('rect').attr("x", flip? '-4.5em' : "0.5em").style("text-anchor", flip? 'end' : 'start');
- tip.selectAll('text.label').attr("x", flip? "-1em" : "1em").style("text-anchor", flip? 'end' : 'start');
+ var xPos = d3.mouse(this)[0], x0 = x.invert(xPos), y0 = func(x0), flip = (x0 / x.domain()[1] > 0.65);
+ tip.attr('transform', 'translate(' + x(x0) + ',' + y(y0) + ')');
+ tip.selectAll('rect').attr('x', flip ? '-5.75em' : '0.5em').style('text-anchor', flip ? 'end' : 'start');
+ tip.selectAll('text.label').attr('x', flip ? '-2em' : '1em').style('text-anchor', flip ? 'end' : 'start');
tip.select('text.label.x').text(fmtLong(x0) + ' ' + labels.xAxis.unit);
tip.select('text.label.y').text(fmtLong(y0) + ' ' + labels.yAxis.unit);
}
@@ -159,4 +160,4 @@ angular.module('app').directive('areaChart', ['$window', function ($window) {
}
};
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/directives/directive-bar-chart.js b/app/js/directives/directive-bar-chart.js
index da5e91a0..eab99af9 100755
--- a/app/js/directives/directive-bar-chart.js
+++ b/app/js/directives/directive-bar-chart.js
@@ -1,10 +1,10 @@
-angular.module('app').directive('barChart', ['$window', function ($window) {
+angular.module('app').directive('barChart', ['$window', function($window) {
- function bName (build) {
+ function bName(build) {
return build.buildName + '\n' + build.name;
}
- var insertLinebreaks = function (d) {
+ function insertLinebreaks(d) {
var el = d3.select(this);
var words = d.split('\n');
el.text('').attr('y', -6);
@@ -14,11 +14,11 @@ angular.module('app').directive('barChart', ['$window', function ($window) {
tspan.attr('x', -9).attr('dy', 12);
}
}
- };
+ }
return {
restrict: 'A',
- scope:{
+ scope: {
data: '=',
facet: '='
},
@@ -28,7 +28,7 @@ angular.module('app').directive('barChart', ['$window', function ($window) {
fmt = scope.facet.fmt,
properties = scope.facet.props,
unit = scope.facet.unit,
- margin = {top: 10, right: 20, bottom: 35, left: 150},
+ margin = { top: 10, right: 20, bottom: 35, left: 150 },
y0 = d3.scale.ordinal(),
y1 = d3.scale.ordinal(),
x = d3.scale.linear(),
@@ -43,7 +43,7 @@ angular.module('app').directive('barChart', ['$window', function ($window) {
var tip = d3.tip()
.attr('class', 'd3-tip')
.html(function(property, propertyIndex) {
- return (labels? (labels[propertyIndex] + ': ') : '') + fmt(property.value) + ' ' + unit;
+ return (labels ? (labels[propertyIndex] + ': ') : '') + fmt(property.value) + ' ' + unit;
});
vis.call(tip);
@@ -52,13 +52,13 @@ angular.module('app').directive('barChart', ['$window', function ($window) {
vis.append('g').attr('class', 'y axis');
vis.selectAll('g.y.axis g text').each(insertLinebreaks);
// Create X Axis SVG Elements
- var xAxisLbl = vis.append('g')
+ var xAxisLbl = vis.append('g')
.attr('class', 'x axis')
.append('text')
.attr('y', 30)
.attr('dy', '.1em')
.style('text-anchor', 'middle')
- .text(scope.facet.title + (unit? (' (' + unit + ')') : ''));
+ .text(scope.facet.title + (unit ? (' (' + unit + ')') : ''));
/**
@@ -84,11 +84,11 @@ angular.module('app').directive('barChart', ['$window', function ($window) {
// Update X & Y Axis
x.range([0, w]).domain([0, maxVal]);
- y0.domain(data.map(bName)).rangeRoundBands([0, h],0.3);
+ y0.domain(data.map(bName)).rangeRoundBands([0, h], 0.3);
y1.domain(properties).rangeRoundBands([0, y0.rangeBand()]);
vis.selectAll('.y.axis').call(yAxis);
vis.selectAll('.x.axis').attr('transform', 'translate(0,' + h + ')').call(xAxis);
- xAxisLbl.attr('x', w/2);
+ xAxisLbl.attr('x', w / 2);
// Update Y-Axis labels
vis.selectAll('g.y.axis g text').each(insertLinebreaks);
@@ -102,13 +102,13 @@ angular.module('app').directive('barChart', ['$window', function ($window) {
.data(function(build) {
var o = [];
for (var i = 0; i < properties.length; i++) {
- o.push({name: properties[i], value:build[properties[i]]});
+ o.push({ name: properties[i], value: build[properties[i]] });
}
return o;
})
.enter().append('rect')
.attr('height', y1.rangeBand())
- .attr('x',0)
+ .attr('x', 0)
.attr('y', function(d) {return y1(d.name); })
.attr('width', function(d) { return x(d.value); })
.on('mouseover', tip.show)
@@ -124,4 +124,4 @@ angular.module('app').directive('barChart', ['$window', function ($window) {
}
};
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/directives/directive-comparison-table.js b/app/js/directives/directive-comparison-table.js
index a47550a1..7219b988 100755
--- a/app/js/directives/directive-comparison-table.js
+++ b/app/js/directives/directive-comparison-table.js
@@ -1,4 +1,4 @@
-angular.module('app').directive('comparisonTable', ['$state', function ($state) {
+angular.module('app').directive('comparisonTable', ['$state', function($state) {
function tblHeader(facets) {
var r1 = ['Ship Build '];
@@ -8,17 +8,17 @@ angular.module('app').directive('comparisonTable', ['$state', function ($state)
var f = facets[i];
var p = f.props;
var pl = p.length;
- r1.push('' , f.lbls[j], ' ');
+ r2.push('', f.lbls[j], ' ');
}
}
- r1.push('>', f.title ,'');
+ r1.push('>', f.title, '');
}
}
r1.push('');
@@ -30,16 +30,16 @@ angular.module('app').directive('comparisonTable', ['$state', function ($state)
function tblBody(facets, builds) {
var body = [];
- if(builds.length === 0) {
+ if (builds.length === 0) {
return 'No builds added to comparison! ');
- var href = $state.href('outfit',{shipId: b.id, code: b.code, bn: b.buildName});
- body.push('', b.name,' ');
- body.push('', b.buildName,' ');
+ var href = $state.href('outfit', { shipId: b.id, code: b.code, bn: b.buildName });
+ body.push('', b.name, ' ');
+ body.push('', b.buildName, ' ');
for (var j = 0, fl = facets.length; j < fl; j++) {
if (facets[j].active) {
@@ -59,13 +59,13 @@ angular.module('app').directive('comparisonTable', ['$state', function ($state)
return {
restrict: 'A',
- link: function (scope, element) {
+ link: function(scope, element) {
var header = angular.element(' ');
var body = angular.element(' ');
element.append(header);
element.append(body);
- var updateAll = function (){
+ var updateAll = function() {
header.html(tblHeader(scope.facets));
body.html(tblBody(scope.facets, scope.builds));
};
@@ -77,4 +77,4 @@ angular.module('app').directive('comparisonTable', ['$state', function ($state)
});
}
};
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/directives/directive-component-select.js b/app/js/directives/directive-component-select.js
index 2f0b851b..b4070927 100755
--- a/app/js/directives/directive-component-select.js
+++ b/app/js/directives/directive-component-select.js
@@ -1,4 +1,4 @@
-angular.module('app').directive('componentSelect', function () {
+angular.module('app').directive('componentSelect', function() {
// Generting the HTML in this manner is MUCH faster than using an angular template.
@@ -8,42 +8,42 @@ angular.module('app').directive('componentSelect', function () {
var o = opts[i];
var id = o.id || (o.class + o.rating); // Common components' ID is their class and rating
- if(i > 0 && opts.length > 3 && o.class != prevClass && (!o.grp || o.rating != prevRating || o.mode)) {
+ if (i > 0 && opts.length > 3 && o.class != prevClass && (!o.grp || o.rating != prevRating || o.mode)) {
list.push(' ');
}
- list.push(' o.maxmass)? ' disabled"' : '" cpid="', id, '">');
+ list.push((o.maxmass && mass > o.maxmass) ? ' disabled"' : '" cpid="', id, '">');
- if(o.mode) {
- list.push(' ');
+ if (o.mode) {
+ list.push(' ');
}
list.push(o.class, o.rating);
- if(o.missile) {
+ if (o.missile) {
list.push('/' + o.missile);
}
- if(o.name) {
+ if (o.name) {
list.push(' ' + o.name);
}
list.push(' ');
prevClass = o.class;
- prevRating= o.rating;
+ prevRating = o.rating;
}
}
return {
restrict: 'A',
- scope:{
+ scope: {
opts: '=', // Component Options object
groups: '=', // Groups of Component Options
mass: '=', // Current ship unladen mass
@@ -57,13 +57,13 @@ angular.module('app').directive('componentSelect', function () {
var groups = scope.groups;
var mass = scope.mass || 0;
- if(groups) {
+ if (groups) {
// At present time slots with grouped options (Hardpoints and Internal) can be empty
list.push('EMPTY
');
for (var g in groups) {
var grp = groups[g];
var grpCode = grp[Object.keys(grp)[0]].grp; // Nasty operation to get the grp property of the first/any single component
- list.push('', g, '
');
+ list.push('', g, '
');
appendGroup(list, grp, cid, mass);
list.push(' ');
}
@@ -77,9 +77,9 @@ angular.module('app').directive('componentSelect', function () {
// If groups are present and a component is already selectd
if (groups && component && component.grp) {
var groupElement = angular.element(document.getElementById(component.grp));
- var parentElem = element[0].parentElement;
+ var parentElem = element[0].parentElement;
parentElem.scrollTop = groupElement[0].offsetTop; // Scroll to currently selected group
}
}
};
-});
\ No newline at end of file
+});
diff --git a/app/js/directives/directive-context-menu.js b/app/js/directives/directive-context-menu.js
new file mode 100644
index 00000000..6430a573
--- /dev/null
+++ b/app/js/directives/directive-context-menu.js
@@ -0,0 +1,12 @@
+angular.module('app').directive('contextMenu', ['$parse', function($parse) {
+ return function(scope, element, attrs) {
+ var fn = $parse(attrs.contextMenu);
+
+ element.bind('contextmenu', function(e) {
+ scope.$apply(function() {
+ e.preventDefault();
+ fn(scope, { $event: e });
+ });
+ });
+ };
+}]);
diff --git a/app/js/directives/directive-header.js b/app/js/directives/directive-header.js
index 1d61b560..0925089a 100755
--- a/app/js/directives/directive-header.js
+++ b/app/js/directives/directive-header.js
@@ -1,10 +1,10 @@
-angular.module('app').directive('shipyardHeader', ['lodash', '$rootScope', 'Persist', 'ShipsDB', function (_, $rootScope, Persist, ships) {
+angular.module('app').directive('shipyardHeader', ['lodash', '$rootScope', 'Persist', 'ShipsDB', function(_, $rootScope, Persist, ships) {
return {
restrict: 'E',
templateUrl: 'views/_header.html',
scope: true,
- link: function (scope) {
+ link: function(scope) {
scope.openedMenu = null;
scope.ships = ships;
scope.allBuilds = Persist.builds;
@@ -12,25 +12,17 @@ angular.module('app').directive('shipyardHeader', ['lodash', '$rootScope', 'Pers
scope.allComparisons = Persist.comparisons;
scope.bs = Persist.state;
- // Insurance options and management here for now.
- $rootScope.insurance = {
- opts: [
- { name:'Standard', pct: 0.05 },
- { name:'Alpha', pct: 0.025 },
- { name:'Beta', pct: 0.035 }
- ]
- };
-
var insIndex = _.findIndex($rootScope.insurance.opts, 'name', Persist.getInsurance());
- $rootScope.insurance.current = $rootScope.insurance.opts[insIndex != -1? insIndex : 0];
+ $rootScope.insurance.current = $rootScope.insurance.opts[insIndex != -1 ? insIndex : 0];
+ $rootScope.discounts.current = $rootScope.discounts.opts[Persist.getDiscount() || 0];
// Close menus if a navigation change event occurs
- $rootScope.$on('$stateChangeStart',function(){
+ $rootScope.$on('$stateChangeStart', function() {
scope.openedMenu = null;
});
// Listen to close event to close opened menus or modals
- $rootScope.$on('close', function () {
+ $rootScope.$on('close', function() {
scope.openedMenu = null;
$rootScope.showAbout = false;
});
@@ -38,13 +30,20 @@ angular.module('app').directive('shipyardHeader', ['lodash', '$rootScope', 'Pers
/**
* Save selected insurance option
*/
- scope.updateInsurance = function(){
+ scope.updateInsurance = function() {
Persist.setInsurance($rootScope.insurance.current.name);
};
- scope.openMenu = function (e, menu) {
+ /**
+ * Save selected discount option
+ */
+ scope.updateDiscount = function() {
+ Persist.setDiscount($rootScope.discounts.opts.indexOf($rootScope.discounts.current));
+ };
+
+ scope.openMenu = function(e, menu) {
e.stopPropagation();
- if(menu == scope.openedMenu) {
+ if (menu == scope.openedMenu) {
scope.openedMenu = null;
return;
}
@@ -63,7 +62,7 @@ angular.module('app').directive('shipyardHeader', ['lodash', '$rootScope', 'Pers
$rootScope.showAbout = true;
};
- $rootScope.hideAbout = function (){
+ $rootScope.hideAbout = function() {
$rootScope.showAbout = false;
};
@@ -72,4 +71,4 @@ angular.module('app').directive('shipyardHeader', ['lodash', '$rootScope', 'Pers
});
}
};
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/directives/directive-line-chart.js b/app/js/directives/directive-line-chart.js
new file mode 100644
index 00000000..9b5000b9
--- /dev/null
+++ b/app/js/directives/directive-line-chart.js
@@ -0,0 +1,190 @@
+angular.module('app').directive('lineChart', ['$window', function($window) {
+ return {
+ restrict: 'A',
+ scope: {
+ config: '=',
+ series: '='
+ },
+ link: function(scope, element) {
+ var seriesConfig = scope.series,
+ series = seriesConfig.series,
+ color = d3.scale.ordinal().range([ '#ff8c0d', '#1fb0ff', '#a05d56', '#d0743c']),
+ config = scope.config,
+ labels = config.labels,
+ margin = { top: 15, right: 15, bottom: 35, left: 60 },
+ fmt = d3.format('.3r'),
+ fmtLong = d3.format('.2f'),
+ func = seriesConfig.func,
+ drag = d3.behavior.drag(),
+ dragging = false,
+ // Define Scales
+ x = d3.scale.linear(),
+ y = d3.scale.linear(),
+ // Define Axes
+ xAxis = d3.svg.axis().scale(x).outerTickSize(0).orient('bottom').tickFormat(d3.format('.2r')),
+ yAxis = d3.svg.axis().scale(y).ticks(6).outerTickSize(0).orient('left').tickFormat(fmt),
+ data = [];
+
+ // Create chart
+ var svg = d3.select(element[0]).append('svg');
+ var vis = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ var lines = vis.append('g');
+
+ // Define Area
+ var line = d3.svg.line().y(function(d) { return y(d[1]); });
+
+ // Create Y Axis SVG Elements
+ var yTxt = vis.append('g').attr('class', 'y axis')
+ .append('text')
+ .attr('transform', 'rotate(-90)')
+ .attr('y', -50)
+ .attr('dy', '.1em')
+ .style('text-anchor', 'middle')
+ .text(labels.yAxis.title + ' (' + labels.yAxis.unit + ')');
+ // Create X Axis SVG Elements
+ var xLbl = vis.append('g').attr('class', 'x axis');
+ var xTxt = xLbl.append('text')
+ .attr('y', 30)
+ .attr('dy', '.1em')
+ .style('text-anchor', 'middle')
+ .text(labels.xAxis.title + ' (' + labels.xAxis.unit + ')');
+
+ // Create and Add tooltip
+ var tipWidth = (Math.max(labels.yAxis.unit.length, labels.xAxis.unit.length) * 1.25) + 2;
+ var tips = vis.append('g').style('display', 'none');
+
+ var background = vis.append('rect') // Background to capture hover/drag
+ .attr('fill-opacity', 0)
+ .on('mouseover', showTip)
+ .on('mouseout', hideTip)
+ .on('mousemove', moveTip)
+ .call(drag);
+
+ drag
+ .on('dragstart', function() {
+ dragging = true;
+ moveTip.call(this);
+ showTip();
+ })
+ .on('dragend', function() {
+ dragging = false;
+ hideTip();
+ })
+ .on('drag', moveTip);
+
+ /**
+ * Watch for changes in the series data (mass changes, etc)
+ */
+ scope.$watchCollection('series', render);
+ angular.element($window).bind('orientationchange resize render', render);
+
+ function render() {
+ var width = element[0].parentElement.offsetWidth,
+ height = width * 0.5,
+ xMax = seriesConfig.xMax,
+ xMin = seriesConfig.xMin,
+ yMax = seriesConfig.yMax,
+ yMin = seriesConfig.yMin,
+ w = width - margin.left - margin.right,
+ h = height - margin.top - margin.bottom,
+ s, val, yVal, delta;
+
+ data.length = 0; // Reset Data array
+
+ if (seriesConfig.xMax == seriesConfig.xMin) {
+ line.x(function(d, i) { return i * w; });
+ } else {
+ line.x(function(d) { return x(d[0]); });
+ }
+
+ if (series) {
+ for (s = 0; s < series.length; s++) {
+ data.push([]);
+ }
+
+ if (xMax == xMin) {
+ yVal = func(xMin);
+ for (s = 0; s < series.length; s++) {
+ data[s].push( [ xMin, yVal[ series[s] ] ], [ 1, yVal[ series[s] ] ]);
+ }
+ } else {
+ delta = (xMax - xMin) / 30; // Only render 30 points on the graph
+ for (val = xMin; val <= xMax; val += delta) {
+ yVal = func(val);
+ for (s = 0; s < series.length; s++) {
+ data[s].push([ val, yVal[ series[s] ] ]);
+ }
+ }
+ }
+
+ } else {
+ var seriesData = [];
+ if (xMax == xMin) {
+ yVal = func(xMin);
+ seriesData.push([ xMin, yVal ], [ 1, yVal ]);
+ } else {
+ delta = (xMax - xMin) / 30; // Only render 30 points on the graph
+ for (val = xMin; val <= xMax; val += delta) {
+ seriesData.push([val, func(val) ]);
+ }
+ }
+
+ data.push(seriesData);
+ }
+
+ // Update Chart Size
+ svg.attr('width', width).attr('height', height);
+ background.attr('height', h).attr('width', w);
+
+ // Update domain and scale for axes
+ x.range([0, w]).domain([xMin, xMax]).clamp(true);
+ xLbl.attr('transform', 'translate(0,' + h + ')');
+ xTxt.attr('x', w / 2);
+ y.range([h, 0]).domain([yMin, yMax]);
+ yTxt.attr('x', -h / 2);
+ vis.selectAll('.y.axis').call(yAxis);
+ vis.selectAll('.x.axis').call(xAxis);
+
+ lines.selectAll('path.line')
+ .data(data)
+ .attr('d', line) // Update existing series
+ .enter() // Add new series
+ .append('path')
+ .attr('class', 'line')
+ .attr('stroke', function(d, i) { return color(i); })
+ .attr('stroke-width', 2)
+ .attr('d', line);
+
+ var tip = tips.selectAll('g.tooltip').data(data).enter().append('g').attr('class', 'tooltip');
+ tip.append('rect').attr('width', tipWidth + 'em').attr('height', '2em').attr('x', '0.5em').attr('y', '-1em').attr('class', 'tip');
+ tip.append('circle').attr('class', 'marker').attr('r', 4);
+ tip.append('text').attr('class', 'label x').attr('y', '-0.25em');
+ tip.append('text').attr('class', 'label y').attr('y', '0.85em');
+ }
+
+ function showTip() {
+ tips.style('display', null);
+ }
+
+ function hideTip() {
+ if (!dragging) {
+ tips.style('display', 'none');
+ }
+ }
+
+ function moveTip() {
+ var xPos = d3.mouse(this)[0], x0 = x.invert(xPos), y0 = func(x0), flip = (x0 / x.domain()[1] > 0.65);
+ var tip = tips.selectAll('g.tooltip').attr('transform', function(d, i) { return 'translate(' + x(x0) + ',' + y(series ? y0[series[i]] : y0) + ')'; });
+ tip.selectAll('rect').attr('x', flip ? (-tipWidth - 0.5) + 'em' : '0.5em').style('text-anchor', flip ? 'end' : 'start');
+ tip.selectAll('text.label').attr('x', flip ? '-1em' : '1em').style('text-anchor', flip ? 'end' : 'start');
+ tip.selectAll('text.label.x').text(fmtLong(x0) + ' ' + labels.xAxis.unit);
+ tips.selectAll('text.label.y').text(function(d, i) { return fmtLong(series ? y0[series[i]] : y0) + ' ' + labels.yAxis.unit; });
+ }
+
+ scope.$on('$destroy', function() {
+ angular.element($window).unbind('orientationchange resize render', render);
+ });
+
+ }
+ };
+}]);
diff --git a/app/js/directives/directive-power-bands.js b/app/js/directives/directive-power-bands.js
index 2dd55c70..4ab6f28b 100644
--- a/app/js/directives/directive-power-bands.js
+++ b/app/js/directives/directive-power-bands.js
@@ -1,13 +1,14 @@
-angular.module('app').directive('powerBands', ['$window', function ($window) {
+angular.module('app').directive('powerBands', ['$window', function($window) {
return {
restrict: 'A',
- scope:{
+ scope: {
bands: '=',
available: '='
},
link: function(scope, element) {
- var margin = {top: 20, right: 130, bottom: 20, left: 40},
+ var margin = { top: 20, right: 130, bottom: 20, left: 40 },
barHeight = 20,
+ bands = null,
innerHeight = (barHeight * 2) + 3,
height = innerHeight + margin.top + margin.bottom + 1,
wattScale = d3.scale.linear(),
@@ -19,29 +20,43 @@ angular.module('app').directive('powerBands', ['$window', function ($window) {
// Create chart
svg = d3.select(element[0]).append('svg'),
vis = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'),
- deployed = vis.append('g'),
- retracted = vis.append('g');
+ deployed = vis.append('g').attr('class', 'power-band'),
+ retracted = vis.append('g').attr('class', 'power-band');
+
+ svg.on('contextmenu', function() {
+ d3.event.preventDefault();
+ for (var i = 0, l = bands.length; i < l; i++) {
+ bands[i].retSelected = false;
+ bands[i].depSelected = false;
+ }
+ render();
+ });
// Create Y Axis SVG Elements
vis.append('g').attr('class', 'watt axis');
vis.append('g').attr('class', 'pct axis');
- vis.append("text").attr('x', -35).attr('y', 16).attr('class','primary').text('RET');
- vis.append("text").attr('x', -35).attr('y', barHeight + 18).attr('class','primary').text('DEP');
+ vis.append('text').attr('x', -35).attr('y', 16).attr('class', 'primary').text('RET');
+ vis.append('text').attr('x', -35).attr('y', barHeight + 18).attr('class', 'primary').text('DEP');
- var retLbl = vis.append("text").attr('y', 16);
- var depLbl = vis.append("text").attr('y', barHeight + 18);
+ var retLbl = vis.append('text').attr('y', 16);
+ var depLbl = vis.append('text').attr('y', barHeight + 18);
// Watch for changes to data and events
scope.$watchCollection('available', render);
angular.element($window).bind('orientationchange resize pwrchange', render);
function render() {
- var bands = scope.bands,
- available = scope.available,
+ bands = scope.bands;
+
+ var available = scope.available,
width = element[0].offsetWidth,
w = width - margin.left - margin.right,
maxBand = bands[bands.length - 1],
- maxPwr = Math.max(available, maxBand.deployedSum);
+ deployedSum = 0,
+ retractedSum = 0,
+ retBandsSelected = false,
+ depBandsSelected = false,
+ maxPwr = Math.max(available, maxBand.retractedSum, maxBand.deployedSum);
// Update chart size
svg.attr('width', width).attr('height', height);
@@ -55,49 +70,80 @@ angular.module('app').directive('powerBands', ['$window', function ($window) {
// Update X & Y Axis
wattScale.range([0, w]).domain([0, maxPwr]).clamp(true);
pctScale.range([0, w]).domain([0, maxPwr / available]).clamp(true);
-
vis.selectAll('.watt.axis').call(wattAxis);
vis.selectAll('.pct.axis').attr('transform', 'translate(0,' + innerHeight + ')').call(pctAxis);
- retLbl
- .attr('x', w + 5 )
- .attr('class',maxBand.retractedSum > available? 'warning': 'primary')
- .text(wattFmt(Math.max(0,maxBand.retractedSum)) + ' (' + pctFmt(Math.max(0,maxBand.retractedSum / available)) + ')');
- depLbl
- .attr('x', w + 5 )
- .attr('class',maxBand.deployedSum > available? 'warning': 'primary')
- .text(wattFmt(Math.max(0,maxBand.deployedSum)) + ' (' + pctFmt(Math.max(0,maxBand.deployedSum / available)) + ')');
+ for (var b = 0, l = bands.length; b < l; b++) {
+ if (bands[b].retSelected) {
+ retractedSum += bands[b].retracted + bands[b].retOnly;
+ retBandsSelected = true;
+ }
+ if (bands[b].depSelected) {
+ deployedSum += bands[b].deployed + bands[b].retracted;
+ depBandsSelected = true;
+ }
+ }
- retracted.selectAll("rect").data(bands).enter().append("rect")
- .attr("height", barHeight)
- .attr("width", function(d) { return Math.max(wattScale(d.retracted) - 1, 0); })
- .attr("x", function(d) { return wattScale(d.retractedSum) - wattScale(d.retracted); })
+ updateLabel(retLbl, w, retBandsSelected, retBandsSelected ? retractedSum : maxBand.retractedSum, available);
+ updateLabel(depLbl, w, depBandsSelected, depBandsSelected ? deployedSum : maxBand.deployedSum, available);
+
+ retracted.selectAll('rect').data(bands).enter().append('rect')
+ .attr('height', barHeight)
+ .attr('width', function(d) { return Math.max(wattScale(d.retracted + d.retOnly) - 1, 0); })
+ .attr('x', function(d) { return wattScale(d.retractedSum) - wattScale(d.retracted + d.retOnly); })
.attr('y', 1)
- .attr('class',function(d){ return (d.retractedSum > available)? 'warning' :'primary'; });
+ .on('click', function(d) {
+ d.retSelected = !d.retSelected;
+ render();
+ })
+ .attr('class', function(d) { return getClass(d.retSelected, d.retractedSum, available); });
- retracted.selectAll("text").data(bands).enter().append("text")
- .attr('x', function(d) { return wattScale(d.retractedSum) - (wattScale(d.retracted) / 2); })
+ retracted.selectAll('text').data(bands).enter().append('text')
+ .attr('x', function(d) { return wattScale(d.retractedSum) - (wattScale(d.retracted + d.retOnly) / 2); })
.attr('y', 15)
.style('text-anchor', 'middle')
- .attr('class','primary-bg')
- .text(function(d,i) { return bandText(d.retracted, i); });
+ .attr('class', 'primary-bg')
+ .on('click', function(d) {
+ d.retSelected = !d.retSelected;
+ render();
+ })
+ .text(function(d, i) { return bandText(d.retracted + d.retOnly, i); });
- deployed.selectAll("rect").data(bands).enter().append("rect")
- .attr("height", barHeight)
- .attr("width", function(d) { return Math.max(wattScale(d.deployed + d.retracted) - 1, 0); })
- .attr("x", function(d) { return wattScale(d.deployedSum) - wattScale(d.retracted) - wattScale(d.deployed); })
+ deployed.selectAll('rect').data(bands).enter().append('rect')
+ .attr('height', barHeight)
+ .attr('width', function(d) { return Math.max(wattScale(d.deployed + d.retracted) - 1, 0); })
+ .attr('x', function(d) { return wattScale(d.deployedSum) - wattScale(d.retracted) - wattScale(d.deployed); })
.attr('y', barHeight + 2)
- .attr('class',function(d){ return (d.deployedSum > available)? 'warning' :'primary'; });
+ .on('click', function(d) {
+ d.depSelected = !d.depSelected;
+ render();
+ })
+ .attr('class', function(d) { return getClass(d.depSelected, d.deployedSum, available); });
- deployed.selectAll("text").data(bands).enter().append("text")
+ deployed.selectAll('text').data(bands).enter().append('text')
.attr('x', function(d) { return wattScale(d.deployedSum) - ((wattScale(d.retracted) + wattScale(d.deployed)) / 2); })
.attr('y', barHeight + 17)
.style('text-anchor', 'middle')
- .attr('class','primary-bg')
- .text(function(d,i) { return bandText(d.deployed + d.retracted, i); });
+ .attr('class', 'primary-bg')
+ .on('click', function(d) {
+ d.depSelected = !d.depSelected;
+ render();
+ })
+ .text(function(d, i) { return bandText(d.deployed + d.retracted, i); });
}
+ function updateLabel(lbl, width, selected, sum, available) {
+ lbl
+ .attr('x', width + 5 )
+ .attr('class', getClass(selected, sum, available))
+ .text(wattFmt(Math.max(0, sum)) + ' (' + pctFmt(Math.max(0, sum / available)) + ')');
+ }
+
+ function getClass(selected, sum, available) {
+ return selected ? 'secondary' : (sum > available) ? 'warning' : 'primary';
+ }
+
function bandText(val, index) {
if (val > 0 && wattScale(val) > 13) {
return index + 1;
@@ -110,4 +156,4 @@ angular.module('app').directive('powerBands', ['$window', function ($window) {
});
}
};
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/directives/directive-slider.js b/app/js/directives/directive-slider.js
index 4ab39544..bf88f273 100644
--- a/app/js/directives/directive-slider.js
+++ b/app/js/directives/directive-slider.js
@@ -1,33 +1,33 @@
-angular.module('app').directive('slider', ['$window', function ($window) {
+angular.module('app').directive('slider', ['$window', function($window) {
return {
restrict: 'A',
- scope:{
+ scope: {
max: '=',
unit: '=',
change: '&onChange'
},
link: function(scope, element) {
- var margin = {top: -10, right: 140, bottom: 0, left: 50},
+ var margin = { top: -10, right: 145, bottom: 0, left: 50 },
height = 40, // Height is fixed
h = height - margin.top - margin.bottom,
fmt = d3.format('.2f'),
pct = d3.format('.1%'),
unit = scope.unit,
val = scope.max,
- svg = d3.select(element[0]).append("svg"),
- vis = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"),
- xAxis = vis.append("g").attr("class", "x slider-axis").attr("transform", "translate(0," + h / 2 + ")"),
+ svg = d3.select(element[0]).append('svg'),
+ vis = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'),
+ xAxis = vis.append('g').attr('class', 'x slider-axis').attr('transform', 'translate(0,' + h / 2 + ')'),
x = d3.scale.linear(),
- slider = vis.append("g").attr("class", "slider"),
- filled = slider.append('path').attr('class', 'filled').attr("transform", "translate(0," + h/2 + ")"),
- brush = d3.svg.brush().x(x).extent([scope.max, scope.max]).on("brush", brushed),
- handle = slider.append("circle").attr("class", "handle").attr("r", '0.6em'),
- lbl = slider.append("g").append("text").attr("y", h/2);
+ slider = vis.append('g').attr('class', 'slider'),
+ filled = slider.append('path').attr('class', 'filled').attr('transform', 'translate(0,' + h / 2 + ')'),
+ brush = d3.svg.brush().x(x).extent([scope.max, scope.max]).on('brush', brushed),
+ handle = slider.append('circle').attr('class', 'handle').attr('r', '0.6em'),
+ lbl = slider.append('g').append('text').attr('y', h / 2);
slider.call(brush);
- slider.select(".background").attr("height", h);
- handle.attr("transform", "translate(0," + h / 2 + ")");
+ slider.select('.background').attr('height', h);
+ handle.attr('transform', 'translate(0,' + h / 2 + ')');
/**
* Watch for changes in the max, window size
@@ -42,21 +42,21 @@ angular.module('app').directive('slider', ['$window', function ($window) {
function render() {
var width = element[0].offsetWidth, w = width - margin.left - margin.right;
- svg.attr("width", width).attr("height", height);
+ svg.attr('width', width).attr('height', height);
x.domain([0, scope.max]).range([0, w]).clamp(true);
- handle.attr("cx", x(val));
+ handle.attr('cx', x(val));
xAxis
.call(d3.svg.axis()
.scale(x)
- .orient("bottom")
+ .orient('bottom')
.tickFormat(function(d) { return d + unit; })
.tickValues([0, scope.max / 4, scope.max / 2, (3 * scope.max) / 4, scope.max])
.tickSize(0)
.tickPadding(12))
- .select(".domain");
+ .select('.domain');
lbl.attr('x', w + 20);
slider.call(brush.extent([val, val])).call(brush.event);
- slider.selectAll(".extent,.resize").remove();
+ slider.selectAll('.extent,.resize').remove();
}
function brushed() {
@@ -65,10 +65,10 @@ angular.module('app').directive('slider', ['$window', function ($window) {
val = x.invert(d3.mouse(this)[0]);
brush.extent([val, val]);
}
- lbl.text(fmt(val) + ' ' + unit + ' ' + pct(val / scope.max));
- scope.change({val: val});
- handle.attr("cx", x(val));
- filled.attr("d", "M0,0V0H" + x(val) + "V0");
+ lbl.text(fmt(val) + ' ' + unit + ' ' + pct(val / scope.max));
+ scope.change({ val: val });
+ handle.attr('cx', x(val));
+ filled.attr('d', 'M0,0V0H' + x(val) + 'V0');
}
scope.$on('$destroy', function() {
@@ -77,4 +77,4 @@ angular.module('app').directive('slider', ['$window', function ($window) {
}
};
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/directives/directive-slot-hardpoint.js b/app/js/directives/directive-slot-hardpoint.js
index b35a8b35..262cc561 100755
--- a/app/js/directives/directive-slot-hardpoint.js
+++ b/app/js/directives/directive-slot-hardpoint.js
@@ -1,14 +1,14 @@
-angular.module('app').directive('slotHardpoint', ['$rootScope', function ($r) {
+angular.module('app').directive('slotHardpoint', ['$rootScope', function($r) {
return {
restrict: 'A',
- scope:{
+ scope: {
hp: '=',
size: '=',
- lbl: '=',
+ lbl: '='
},
templateUrl: 'views/_slot-hardpoint.html',
- link: function (scope) {
+ link: function(scope) {
scope.$r = $r;
}
};
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/directives/directive-slot-internal.js b/app/js/directives/directive-slot-internal.js
index d6aabb9d..2b67a7e8 100755
--- a/app/js/directives/directive-slot-internal.js
+++ b/app/js/directives/directive-slot-internal.js
@@ -1,7 +1,7 @@
-angular.module('app').directive('slotInternal', ['$rootScope', function ($r) {
+angular.module('app').directive('slotInternal', ['$rootScope', function($r) {
return {
restrict: 'A',
- scope:{
+ scope: {
c: '=slot',
lbl: '=',
fuel: '='
@@ -11,4 +11,4 @@ angular.module('app').directive('slotInternal', ['$rootScope', function ($r) {
scope.$r = $r;
}
};
-}]);
\ No newline at end of file
+}]);
diff --git a/app/js/factory-utils.js b/app/js/factory-utils.js
index 34c90579..452772a4 100755
--- a/app/js/factory-utils.js
+++ b/app/js/factory-utils.js
@@ -1,17 +1,17 @@
/**
* BBCode Generator functions for embedding in the Elite Dangerous Forums
*/
-angular.module('app').factory('Utils', ['$window','$state','$http', '$q', function ($window, $state, $http, $q) {
+angular.module('app').factory('Utils', ['$window', '$state', '$http', '$q', function($window, $state, $http, $q) {
var shortenAPI = 'https://www.googleapis.com/urlshortener/v1/url?key=';
function shortenUrl(url) {
if ($window.navigator.onLine) {
- return $http.post(shortenAPI + GAPI_KEY, {longUrl:url}).then(function(response) {
+ return $http.post(shortenAPI + GAPI_KEY, { longUrl: url }).then(function(response) {
return response.data.id;
});
} else {
- return $q.reject({statusText: 'Not Online'});
+ return $q.reject({ statusText: 'Not Online' });
}
}
@@ -39,7 +39,7 @@ angular.module('app').factory('Utils', ['$window','$state','$http', '$q', functi
for (i = 0; i < builds.length; i++) {
b = builds[i];
//var href = $state.href('outfit',{shipId: b.id, code: b.code, bn: b.buildName}, {absolute: true});
- l.push('[tr][td]', b.name,'[/td][td]', b.buildName ,'[/td]');
+ l.push('[tr][td]', b.name, '[/td][td]', b.buildName, '[/td]');
for (j = 0, fl = facets.length; j < fl; j++) {
if (facets[j].active) {
@@ -52,8 +52,8 @@ angular.module('app').factory('Utils', ['$window','$state','$http', '$q', functi
}
l.push('[/tr]\n');
}
- l.push('[tr][td="align: center, colspan:',colCount,'"][size=-3]\n[url=', link,']Interactive Comparison at Coriolis.io[/url][/td][/tr]\n[/size][/table]');
- l.unshift('[table="width:', colCount * 90,',align: center"]\n[tr][th][B][COLOR=#FF8C0D]Ship[/COLOR][/B][/th][th][B][COLOR="#FF8C0D"]Build[/COLOR][/B][/th]');
+ l.push('[tr][td="align: center, colspan:', colCount, '"][size=-3]\n[url=', link, ']Interactive Comparison at Coriolis.io[/url][/td][/tr]\n[/size][/table]');
+ l.unshift('[table="width:', colCount * 90, ',align: center"]\n[tr][th][B][COLOR=#FF8C0D]Ship[/COLOR][/B][/th][th][B][COLOR="#FF8C0D"]Build[/COLOR][/B][/th]');
return l.join('');
}
diff --git a/app/js/service-persist.js b/app/js/service-persist.js
index 75ef5e5f..6e065377 100755
--- a/app/js/service-persist.js
+++ b/app/js/service-persist.js
@@ -1,7 +1,7 @@
/**
* [description]
*/
-angular.module('app').service('Persist', ['$window','lodash', function ($window, _) {
+angular.module('app').service('Persist', ['$window', 'lodash', function($window, _) {
var LS_KEY_BUILDS = 'builds';
var LS_KEY_COMPARISONS = 'comparisons';
var localStorage = $window.localStorage;
@@ -19,8 +19,8 @@ angular.module('app').service('Persist', ['$window','lodash', function ($window,
this.lsEnabled = false;
}
- this.builds = buildJson? angular.fromJson(buildJson) : {};
- this.comparisons = comparisonJson? angular.fromJson(comparisonJson) : {};
+ this.builds = buildJson ? angular.fromJson(buildJson) : {};
+ this.comparisons = comparisonJson ? angular.fromJson(comparisonJson) : {};
var buildCount = Object.keys(this.builds).length;
this.state = {
@@ -35,7 +35,7 @@ angular.module('app').service('Persist', ['$window','lodash', function ($window,
* @param {string} name The name of the build
* @param {string} code The serialized code
*/
- this.saveBuild = function (shipId, name, code) {
+ this.saveBuild = function(shipId, name, code) {
if (!this.lsEnabled) {
return;
}
@@ -44,7 +44,7 @@ angular.module('app').service('Persist', ['$window','lodash', function ($window,
this.builds[shipId] = {};
}
- if(!this.builds[shipId][name]) {
+ if (!this.builds[shipId][name]) {
this.state.buildCount++;
this.state.hasBuilds = true;
}
@@ -62,7 +62,7 @@ angular.module('app').service('Persist', ['$window','lodash', function ($window,
* @param {string} name The name of the build
* @return {string} The serialized build string.
*/
- this.getBuild = function (shipId, name) {
+ this.getBuild = function(shipId, name) {
if (this.builds[shipId] && this.builds[shipId][name]) {
return this.builds[shipId][name];
}
@@ -76,8 +76,8 @@ angular.module('app').service('Persist', ['$window','lodash', function ($window,
* @param {string} shipId The unique id for a model of ship
* @param {string} name The name of the build
*/
- this.deleteBuild = function (shipId, name) {
- if(this.lsEnabled && this.builds[shipId][name]) {
+ this.deleteBuild = function(shipId, name) {
+ if (this.lsEnabled && this.builds[shipId][name]) {
delete this.builds[shipId][name];
if (Object.keys(this.builds[shipId]).length === 0) {
delete this.builds[shipId];
@@ -90,8 +90,8 @@ angular.module('app').service('Persist', ['$window','lodash', function ($window,
var comps = this.comparisons;
for (var c in comps) {
for (var i = 0; i < comps[c].builds.length; i++) { // For all builds in the current comparison
- if(comps[c].builds[i].shipId == shipId && comps[c].builds[i].buildName == name) {
- comps[c].builds.splice(i,1);
+ if (comps[c].builds[i].shipId == shipId && comps[c].builds[i].buildName == name) {
+ comps[c].builds.splice(i, 1);
break; // A build is unique ber comparison
}
}
@@ -107,7 +107,7 @@ angular.module('app').service('Persist', ['$window','lodash', function ($window,
* @param {array} builds Array of builds
* @param {array} facets Array of facet indices
*/
- this.saveComparison = function (name, builds, facets){
+ this.saveComparison = function(name, builds, facets) {
if (!this.lsEnabled) {
return;
}
@@ -117,7 +117,7 @@ angular.module('app').service('Persist', ['$window','lodash', function ($window,
}
this.comparisons[name] = {
facets: facets,
- builds: _.map(builds, function (b) { return {shipId: b.id, buildName: b.buildName }; })
+ builds: _.map(builds, function(b) { return { shipId: b.id, buildName: b.buildName }; })
};
localStorage.setItem(LS_KEY_COMPARISONS, angular.toJson(this.comparisons));
this.state.hasComparisons = true;
@@ -128,7 +128,7 @@ angular.module('app').service('Persist', ['$window','lodash', function ($window,
* @param {string} name [description]
* @return {object} Object containing array of facets and ship id + build names
*/
- this.getComparison = function (name) {
+ this.getComparison = function(name) {
if (this.comparisons[name]) {
return this.comparisons[name];
}
@@ -139,7 +139,7 @@ angular.module('app').service('Persist', ['$window','lodash', function ($window,
* Removes the comparison from localstorage.
* @param {string} name Comparison name
*/
- this.deleteComparison = function (name) {
+ this.deleteComparison = function(name) {
if (this.lsEnabled && this.comparisons[name]) {
delete this.comparisons[name];
localStorage.setItem(LS_KEY_COMPARISONS, angular.toJson(this.comparisons));
@@ -166,7 +166,7 @@ angular.module('app').service('Persist', ['$window','lodash', function ($window,
* Get the saved insurance type
* @return {string} The name of the saved insurance type of null
*/
- this.getInsurance = function () {
+ this.getInsurance = function() {
if (this.lsEnabled) {
return localStorage.getItem('insurance');
}
@@ -177,19 +177,40 @@ angular.module('app').service('Persist', ['$window','lodash', function ($window,
* Persist selected insurance type
* @param {string} name Insurance type name
*/
- this.setInsurance = function (name) {
+ this.setInsurance = function(name) {
if (this.lsEnabled) {
return localStorage.setItem('insurance', name);
}
};
+ /**
+ * Persist selected discount
+ * @param {number} val Discount value/amount
+ */
+ this.setDiscount = function(val) {
+ if (this.lsEnabled) {
+ return localStorage.setItem('discount', val);
+ }
+ };
+
+ /**
+ * Get the saved discount
+ * @return {number} val Discount value/amount
+ */
+ this.getDiscount = function() {
+ if (this.lsEnabled) {
+ return localStorage.getItem('discount');
+ }
+ return null;
+ };
+
/**
* Retrieve the last router state from local storage
* @param {object} state State object containing state name and params
*/
- this.getState = function () {
+ this.getState = function() {
if (this.lsEnabled) {
- var state = localStorage.getItem('state');
+ var state = localStorage.getItem('state');
if (state) {
return angular.fromJson(state);
}
@@ -201,9 +222,9 @@ angular.module('app').service('Persist', ['$window','lodash', function ($window,
* Save the current router state to localstorage
* @param {object} state State object containing state name and params
*/
- this.setState = function (state) {
+ this.setState = function(state) {
if (this.lsEnabled) {
- localStorage.setItem('state',angular.toJson(state));
+ localStorage.setItem('state', angular.toJson(state));
}
};
diff --git a/app/js/service-serializer.js b/app/js/service-serializer.js
index 17723d5b..a17ffc4d 100755
--- a/app/js/service-serializer.js
+++ b/app/js/service-serializer.js
@@ -1,7 +1,7 @@
/**
* Service managing seralization and deserialization of models for use in URLs and persistene.
*/
-angular.module('app').service('Serializer', ['lodash', function (_) {
+angular.module('app').service('Serializer', ['lodash', function(_) {
/**
* Serializes the ships selected components for all slots to a URL friendly string.
@@ -10,7 +10,7 @@ angular.module('app').service('Serializer', ['lodash', function (_) {
*/
this.fromShip = function(ship) {
var power = {
- enabled: [ship.cargoScoop.enabled? 1 : 0],
+ enabled: [ship.cargoScoop.enabled ? 1 : 0],
priorities: [ship.cargoScoop.priority]
};
@@ -20,9 +20,9 @@ angular.module('app').service('Serializer', ['lodash', function (_) {
_.map(ship.hardpoints, mapGroup, power),
_.map(ship.internal, mapGroup, power),
'.',
- LZString.compressToBase64(power.enabled.join('')).replace(/\//g,'-'),
+ LZString.compressToBase64(power.enabled.join('')).replace(/\//g, '-'),
'.',
- LZString.compressToBase64(power.priorities.join('')).replace(/\//g,'-')
+ LZString.compressToBase64(power.priorities.join('')).replace(/\//g, '-')
];
return _.flatten(data).join('');
@@ -35,11 +35,8 @@ angular.module('app').service('Serializer', ['lodash', function (_) {
* @param {Ship} ship The ship instance to be updated
* @param {string} code The string to deserialize
*/
- this.toShip = function (ship, dataString) {
- var commonCount = ship.common.length,
- hpCount = commonCount + ship.hardpoints.length,
- totalCount = hpCount + ship.internal.length,
- common = new Array(ship.common.length),
+ this.toShip = function(ship, dataString) {
+ var common = new Array(ship.common.length),
hardpoints = new Array(ship.hardpoints.length),
internal = new Array(ship.internal.length),
parts = dataString.split('.'),
@@ -47,12 +44,12 @@ angular.module('app').service('Serializer', ['lodash', function (_) {
enabled = null,
code = parts[0];
- if(parts[1]) {
- enabled = LZString.decompressFromBase64(parts[1].replace(/-/g,'/')).split('');
+ if (parts[1]) {
+ enabled = LZString.decompressFromBase64(parts[1].replace(/-/g, '/')).split('');
}
- if(parts[2]) {
- priorities = LZString.decompressFromBase64(parts[2].replace(/-/g,'/')).split('');
+ if (parts[2]) {
+ priorities = LZString.decompressFromBase64(parts[2].replace(/-/g, '/')).split('');
}
decodeToArray(code, internal, decodeToArray(code, hardpoints, decodeToArray(code, common, 1)));
@@ -61,19 +58,23 @@ angular.module('app').service('Serializer', ['lodash', function (_) {
// - priorities
// - enabled/disabled
- ship.buildWith({
- bulkheads: code.charAt(0) * 1,
- common: common,
- hardpoints: hardpoints,
- internal: internal,
- }, priorities, enabled);
+ ship.buildWith(
+ {
+ bulkheads: code.charAt(0) * 1,
+ common: common,
+ hardpoints: hardpoints,
+ internal: internal
+ },
+ priorities,
+ enabled
+ );
};
- this.fromComparison = function (name, builds, facets, predicate, desc) {
+ this.fromComparison = function(name, builds, facets, predicate, desc) {
var shipBuilds = [];
- builds.forEach(function (b) {
- shipBuilds.push({s: b.id, n: b.buildName, c: this.fromShip(b)});
+ builds.forEach(function(b) {
+ shipBuilds.push({ s: b.id, n: b.buildName, c: this.fromShip(b) });
}.bind(this));
return LZString.compressToBase64(angular.toJson({
@@ -81,12 +82,12 @@ angular.module('app').service('Serializer', ['lodash', function (_) {
b: shipBuilds,
f: facets,
p: predicate,
- d: desc? 1 : 0
- })).replace(/\//g,'-');
+ d: desc ? 1 : 0
+ })).replace(/\//g, '-');
};
- this.toComparison = function (code) {
- return angular.fromJson(LZString.decompressFromBase64(code.replace(/-/g,'/')));
+ this.toComparison = function(code) {
+ return angular.fromJson(LZString.decompressFromBase64(code.replace(/-/g, '/')));
};
/**
@@ -98,14 +99,14 @@ angular.module('app').service('Serializer', ['lodash', function (_) {
* @return {string} The id of the selected component or '-' if none selected
*/
function mapGroup(slot) {
- this.enabled.push(slot.enabled? 1 : 0);
+ this.enabled.push(slot.enabled ? 1 : 0);
this.priorities.push(slot.priority);
- return (slot.id === null)? '-' : slot.id;
+ return slot.id === null ? '-' : slot.id;
}
function decodeToArray(code, arr, codePos) {
- for (i = 0; i < arr.length; i++) {
+ for (var i = 0; i < arr.length; i++) {
if (code.charAt(codePos) == '-') {
arr[i] = 0;
codePos++;
diff --git a/app/js/shipyard/factory-component-set.js b/app/js/shipyard/factory-component-set.js
index 8ba6bcc6..15aeca64 100755
--- a/app/js/shipyard/factory-component-set.js
+++ b/app/js/shipyard/factory-component-set.js
@@ -1,4 +1,10 @@
-angular.module('shipyard').factory('ComponentSet', ['lodash', function (_) {
+angular.module('shipyard').factory('ComponentSet', ['lodash', function(_) {
+
+ function filter(data, maxClass, minClass, mass) {
+ return _.filter(data, function(c) {
+ return c.class <= maxClass && c.class >= minClass && (c.maxmass === undefined || mass <= c.maxmass);
+ });
+ }
function ComponentSet(components, mass, maxCommonArr, maxInternal, maxHardPoint) {
this.mass = mass;
@@ -8,7 +14,7 @@ angular.module('shipyard').factory('ComponentSet', ['lodash', function (_) {
this.hpClass = {};
this.intClass = {};
- for (var i = 0; i < components.common.length; i ++) {
+ for (var i = 0; i < components.common.length; i++) {
var max = maxCommonArr[i];
switch (i) {
// Slots where component class must be equal to slot class
@@ -22,21 +28,21 @@ angular.module('shipyard').factory('ComponentSet', ['lodash', function (_) {
}
}
- for(var h in components.hardpoints) {
+ for (var h in components.hardpoints) {
this.hardpoints[h] = filter(components.hardpoints[h], maxHardPoint, 0, this.mass);
}
- for(var g in components.internal) {
+ for (var g in components.internal) {
this.internal[g] = filter(components.internal[g], maxInternal, 0, this.mass);
}
}
ComponentSet.prototype.getHps = function(c) {
- if(!this.hpClass[c]) {
- var o = this.hpClass[c] = {};
- for(var key in this.hardpoints) {
- var data = filter(this.hardpoints[key], c, c? 1 : 0, this.mass);
- if(data.length) { // If group is not empty
+ if (!this.hpClass[c]) {
+ var o = this.hpClass[c] = {};
+ for (var key in this.hardpoints) {
+ var data = filter(this.hardpoints[key], c, c ? 1 : 0, this.mass);
+ if (data.length) { // If group is not empty
o[key] = data;
}
}
@@ -45,11 +51,11 @@ angular.module('shipyard').factory('ComponentSet', ['lodash', function (_) {
};
ComponentSet.prototype.getInts = function(c) {
- if(!this.intClass[c]) {
- var o = this.intClass[c] = {};
- for(var key in this.internal) {
+ if (!this.intClass[c]) {
+ var o = this.intClass[c] = {};
+ for (var key in this.internal) {
var data = filter(this.internal[key], c, 0, this.mass);
- if(data.length) { // If group is not empty
+ if (data.length) { // If group is not empty
o[key] = data;
}
}
@@ -57,12 +63,6 @@ angular.module('shipyard').factory('ComponentSet', ['lodash', function (_) {
return this.intClass[c];
};
- function filter (data, maxClass, minClass, mass) {
- return _.filter(data, function (c) {
- return c.class <= maxClass && c.class >= minClass && (c.maxmass === undefined || mass <= c.maxmass);
- });
- }
-
return ComponentSet;
}]);
diff --git a/app/js/shipyard/factory-ship.js b/app/js/shipyard/factory-ship.js
index d6d026c7..c75f004e 100755
--- a/app/js/shipyard/factory-ship.js
+++ b/app/js/shipyard/factory-ship.js
@@ -1,4 +1,22 @@
-angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', 'calcJumpRange', 'lodash', function (Components, calcShieldStrength, calcJumpRange, _) {
+angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', 'calcJumpRange', 'calcTotalRange', 'lodash', function(Components, calcShieldStrength, calcJumpRange, calcTotalRange, _) {
+
+ /**
+ * Returns the power usage type of a slot and it's particular component
+ * @param {object} slot The Slot
+ * @param {object} component The component in the slot
+ * @return {string} The key for the power usage type
+ */
+ function powerUsageType(slot, component) {
+ if (component) {
+ if (component.retractedOnly) {
+ return 'retOnly';
+ }
+ if (component.passive) {
+ return 'retracted';
+ }
+ }
+ return slot.cat != 1 ? 'retracted' : 'deployed';
+ }
/**
* Ship model used to track all ship components and properties.
@@ -10,18 +28,18 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
function Ship(id, properties, slots) {
this.id = id;
this.cargoScoop = { c: Components.cargoScoop(), type: 'SYS' };
- this.bulkheads = { incCost: true, maxClass: 8 };
+ this.bulkheads = { incCost: true, maxClass: 8, discount: 1 };
for (var p in properties) { this[p] = properties[p]; } // Copy all base properties from shipData
for (var slotType in slots) { // Initialize all slots
var slotGroup = slots[slotType];
var group = this[slotType] = []; // Initialize Slot group (Common, Hardpoints, Internal)
- for(var i = 0; i < slotGroup.length; i++){
- group.push({id: null, c: null, incCost: true, maxClass: slotGroup[i]});
+ for (var i = 0; i < slotGroup.length; i++) {
+ group.push({ id: null, c: null, incCost: true, maxClass: slotGroup[i], discount: 1 });
}
}
- this.c = { incCost: true, c: { name: this.name, cost: this.cost } }; // Make a 'Ship' component similar to other components
+ this.c = { incCost: true, discount: 1, c: { name: this.name, cost: this.cost } }; // Make a 'Ship' component similar to other components
this.costList = _.union(this.internal, this.common, this.hardpoints);
this.costList.push(this.bulkheads); // Add The bulkheads
@@ -37,22 +55,12 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
this.powerList.unshift(this.common[0]); // Add Power Plant
this.priorityBands = [
- {deployed: 0, retracted: 0},
- {deployed: 0, retracted: 0},
- {deployed: 0, retracted: 0},
- {deployed: 0, retracted: 0},
- {deployed: 0, retracted: 0}
+ { deployed: 0, retracted: 0, retOnly: 0 },
+ { deployed: 0, retracted: 0, retOnly: 0 },
+ { deployed: 0, retracted: 0, retOnly: 0 },
+ { deployed: 0, retracted: 0, retOnly: 0 },
+ { deployed: 0, retracted: 0, retOnly: 0 }
];
-
- // Cumulative and aggragate stats
- this.fuelCapacity = 0;
- this.cargoCapacity = 0;
- this.ladenMass = 0;
- this.armourAdded = 0;
- this.shieldMultiplier = 1;
- this.totalCost = this.cost;
- this.unladenMass = this.mass;
- this.armourTotal = this.armour;
}
/**
@@ -64,49 +72,70 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
common = this.common,
hps = this.hardpoints,
bands = this.priorityBands,
- cl = common.length, hl = hps.length, il = internal.length,
- i,l;
+ cl = common.length,
+ i, l;
+ // Reset Cumulative stats
+ this.fuelCapacity = 0;
+ this.cargoCapacity = 0;
+ this.ladenMass = 0;
+ this.armourAdded = 0;
+ this.shieldMultiplier = 1;
+ this.totalCost = this.cost;
+ this.unladenMass = this.mass;
+ this.armourTotal = this.armour;
+
+ this.bulkheads.c = null;
this.useBulkhead(comps.bulkheads || 0, true);
- this.cargoScoop.priority = priorities? priorities[0] * 1 : 0;
- this.cargoScoop.enabled = enabled? enabled[0] * 1 : true;
+ this.cargoScoop.priority = priorities ? priorities[0] * 1 : 0;
+ this.cargoScoop.enabled = enabled ? enabled[0] * 1 : true;
+
+ for (i = 0, l = this.priorityBands.length; i < l; i++) {
+ this.priorityBands[i].deployed = 0;
+ this.priorityBands[i].retracted = 0;
+ this.priorityBands[i].retOnly = 0;
+ }
if (this.cargoScoop.enabled) {
bands[this.cargoScoop.priority].retracted += this.cargoScoop.c.power;
}
- for(i = 0; i < cl; i++) {
- common[i].enabled = enabled? enabled[i + 1] * 1 : true;
- common[i].priority = priorities? priorities[i + 1] * 1 : 0;
+ for (i = 0; i < cl; i++) {
+ common[i].cat = 0;
+ common[i].enabled = enabled ? enabled[i + 1] * 1 : true;
+ common[i].priority = priorities ? priorities[i + 1] * 1 : 0;
common[i].type = 'SYS';
+ common[i].c = common[i].id = null; // Resetting 'old' component if there was one
this.use(common[i], comps.common[i], Components.common(i, comps.common[i]), true);
}
common[1].type = 'ENG'; // Thrusters
common[2].type = 'ENG'; // FSD
- cl++; // Increase accounting for Cargo Scoop
+ cl++; // Increase accounts for Cargo Scoop
- for(i = 0, l = comps.hardpoints.length; i < l; i++) {
- hps[i].enabled = enabled? enabled[cl + i] * 1 : true;
- hps[i].priority = priorities? priorities[cl + i] * 1 : 0;
- hps[i].type = hps[i].maxClass? 'WEP' : 'SYS';
+ for (i = 0, l = hps.length; i < l; i++) {
+ hps[i].cat = 1;
+ hps[i].enabled = enabled ? enabled[cl + i] * 1 : true;
+ hps[i].priority = priorities ? priorities[cl + i] * 1 : 0;
+ hps[i].type = hps[i].maxClass ? 'WEP' : 'SYS';
+ hps[i].c = hps[i].id = null; // Resetting 'old' component if there was one
if (comps.hardpoints[i] !== 0) {
this.use(hps[i], comps.hardpoints[i], Components.hardpoints(comps.hardpoints[i]), true);
- } else {
- hps[i].c = hps[i].id = null;
}
}
- for(i = 0, l = comps.internal.length; i < l; i++) {
- internal[i].enabled = enabled? enabled[hl + cl + i] * 1 : true;
- internal[i].priority = priorities? priorities[hl + cl + i] * 1 : 0;
+ cl += hps.length; // Increase accounts for hardpoints
+
+ for (i = 0, l = internal.length; i < l; i++) {
+ internal[i].cat = 2;
+ internal[i].enabled = enabled ? enabled[cl + i] * 1 : true;
+ internal[i].priority = priorities ? priorities[cl + i] * 1 : 0;
internal[i].type = 'SYS';
+ internal[i].id = internal[i].c = null; // Resetting 'old' component if there was one
if (comps.internal[i] !== 0) {
this.use(internal[i], comps.internal[i], Components.internal(comps.internal[i]), true);
- } else {
- internal[i].id = internal[i].c = null;
}
}
@@ -117,7 +146,7 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
};
Ship.prototype.useBulkhead = function(index, preventUpdate) {
- var oldBulkhead = this.bulkheads.c;
+ var oldBulkhead = this.bulkheads.c;
this.bulkheads.id = index;
this.bulkheads.c = Components.bulkheads(this.id, index);
this.updateStats(this.bulkheads, this.bulkheads.c, oldBulkhead, preventUpdate);
@@ -134,18 +163,14 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
*/
Ship.prototype.use = function(slot, id, component, preventUpdate) {
if (slot.id != id) { // Selecting a different component
- var slotIndex = this.internal.indexOf(slot);
// Slot is an internal slot, is not being emptied, and the selected component group/type must be of unique
- if(slotIndex != -1 && component && _.includes(['sg','rf','fs'],component.grp)) {
+ if (slot.cat == 2 && component && _.includes(['sg', 'rf', 'fs'], component.grp)) {
// Find another internal slot that already has this type/group installed
- var similarSlotIndex = this.findInternalByGroup(component.grp);
+ var similarSlot = this.findInternalByGroup(component.grp);
// If another slot has an installed component with of the same type
- if (similarSlotIndex != -1 && similarSlotIndex != slotIndex) {
- // Empty the slot
- var similarSlot = this.internal[similarSlotIndex];
+ if (!preventUpdate && similarSlot && similarSlot !== slot) {
this.updateStats(similarSlot, null, similarSlot.c, true); // Update stats but don't trigger a global update
- similarSlot.id = null;
- similarSlot.c = null;
+ similarSlot.id = similarSlot.c = null; // Empty the slot
}
}
var oldComponent = slot.c;
@@ -163,7 +188,7 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
* @param {number} fuel Fuel available in tons
* @return {number} Jump range in Light Years
*/
- Ship.prototype.jumpRangeWithMass = function (mass, fuel) {
+ Ship.prototype.jumpRangeWithMass = function(mass, fuel) {
return calcJumpRange(mass, this.common[2].c, fuel);
};
@@ -173,65 +198,81 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
* @param {string} group Component group/type
* @return {number} The index of the slot in ship.internal
*/
- Ship.prototype.findInternalByGroup = function (group) {
- return _.findIndex(this.internal, function (slot) {
+ Ship.prototype.findInternalByGroup = function(group) {
+ var index = _.findIndex(this.internal, function(slot) {
return slot.c && slot.c.grp == group;
});
+ if (index !== -1) {
+ return this.internal[index];
+ }
+ return null;
};
- Ship.prototype.changePriority = function (slot, newPriority) {
- if(newPriority >= 0 && newPriority < this.priorityBands.length) {
+ /**
+ * Will change the priority of the specified slot if the new priority is valid
+ * @param {object} slot The slot to be updated
+ * @param {number} newPriority The new priority to be set
+ * @return {boolean} Returns true if the priority was changed (within range)
+ */
+ Ship.prototype.changePriority = function(slot, newPriority) {
+ if (newPriority >= 0 && newPriority < this.priorityBands.length) {
var oldPriority = slot.priority;
slot.priority = newPriority;
- if (slot.enabled) {
- var usage = (slot.c.passive || this.hardpoints.indexOf(slot) == -1)? 'retracted' : 'deployed';
+ if (slot.enabled) { // Only update power if the slot is enabled
+ var usage = powerUsageType(slot, slot.c);
this.priorityBands[oldPriority][usage] -= slot.c.power;
this.priorityBands[newPriority][usage] += slot.c.power;
this.updatePower();
- return true;
}
+ return true;
}
return false;
};
- Ship.prototype.setCostIncluded = function (item, included) {
+ Ship.prototype.setCostIncluded = function(item, included) {
if (item.incCost != included && item.c) {
- this.totalCost += included? item.c.cost : -item.c.cost;
+ this.totalCost += included ? item.c.cost : -item.c.cost;
}
item.incCost = included;
};
- Ship.prototype.setSlotEnabled = function (slot, enabled) {
- if (slot.enabled != enabled && slot.c) { // Enabled state is changing
- var usage = (slot.c.passive || this.hardpoints.indexOf(slot) == -1)? 'retracted' : 'deployed';
- this.priorityBands[slot.priority][usage] += enabled? slot.c.power : -slot.c.power;
- this.updatePower();
+ Ship.prototype.setSlotEnabled = function(slot, enabled) {
+ if (slot.enabled != enabled) { // Enabled state is changing
+ slot.enabled = enabled;
+ if (slot.c) {
+ this.priorityBands[slot.priority][powerUsageType(slot, slot.c)] += enabled ? slot.c.power : -slot.c.power;
+
+ if (slot.c.grp == 'sg') {
+ this.updateShieldStrength();
+ } else if (slot.c.grp == 'sb') {
+ this.shieldMultiplier += slot.c.shieldmul * (enabled ? 1 : -1);
+ this.updateShieldStrength();
+ }
+
+ this.updatePower();
+ }
}
- slot.enabled = enabled;
};
- Ship.prototype.getSlotStatus = function (slot, deployed) {
- if(!slot.c) { // Empty Slot
+ Ship.prototype.getSlotStatus = function(slot, deployed) {
+ if (!slot.c) { // Empty Slot
return 0; // No Status (Not possible)
- }
- else if (!slot.enabled) {
+ } else if (!slot.enabled) {
return 1; // Disabled
- }
- else if (deployed) {
- return this.priorityBands[slot.priority].deployedSum > this.powerAvailable? 2 : 3; // Offline : Online
- }
- else if (this.hardpoints.indexOf(slot) != -1 && !slot.c.passive) { // Active hardpoints have no retracted status
+ } else if (deployed && !slot.c.retractedOnly) { // Certain component (e.g. Detaild Surface scanner) are power only while retracted
+ return this.priorityBands[slot.priority].deployedSum > this.powerAvailable ? 2 : 3; // Offline : Online
+ // Active hardpoints have no retracted status
+ } else if ((deployed && slot.c.retractedOnly) || (slot.cat === 1 && !slot.c.passive)) {
return 0; // No Status (Not possible)
}
- return this.priorityBands[slot.priority].retractedSum > this.powerAvailable? 2 : 3; // Offline : Online
+ return this.priorityBands[slot.priority].retractedSum > this.powerAvailable ? 2 : 3; // Offline : Online
};
/**
* Updates the ship's cumulative and aggregated stats based on the component change.
*/
Ship.prototype.updateStats = function(slot, n, old, preventUpdate) {
- var isHardPoint = this.hardpoints.indexOf(slot) != -1;
var powerChange = slot == this.common[0];
if (old) { // Old component now being removed
@@ -246,7 +287,7 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
this.armourAdded -= old.armouradd;
break;
case 'sb':
- this.shieldMultiplier -= old.shieldmul;
+ this.shieldMultiplier -= slot.enabled ? old.shieldmul : 0;
break;
}
@@ -254,8 +295,8 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
this.totalCost -= old.cost;
}
- if(old.power) {
- this.priorityBands[slot.priority][(isHardPoint && !old.passive)? 'deployed' : 'retracted'] -= old.power;
+ if (old.power && slot.enabled) {
+ this.priorityBands[slot.priority][powerUsageType(slot, old)] -= old.power;
powerChange = true;
}
this.unladenMass -= old.mass || 0;
@@ -276,7 +317,7 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
this.armourAdded += n.armouradd;
break;
case 'sb':
- this.shieldMultiplier += n.shieldmul;
+ this.shieldMultiplier += slot.enabled ? n.shieldmul : 0;
break;
}
@@ -284,8 +325,8 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
this.totalCost += n.cost;
}
- if (n.power) {
- this.priorityBands[slot.priority][(isHardPoint && !n.passive)? 'deployed' : 'retracted'] += n.power;
+ if (n.power && slot.enabled) {
+ this.priorityBands[slot.priority][powerUsageType(slot, n)] += n.power;
powerChange = true;
}
this.unladenMass += n.mass || 0;
@@ -294,7 +335,7 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
this.ladenMass = this.unladenMass + this.cargoCapacity + this.fuelCapacity;
this.armourTotal = this.armourAdded + this.armour;
- if(!preventUpdate) {
+ if (!preventUpdate) {
if (powerChange) {
this.updatePower();
}
@@ -307,10 +348,10 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
var bands = this.priorityBands;
var prevRetracted = 0, prevDeployed = 0;
- for(var i = 0, l = bands.length; i < l; i++) {
+ for (var i = 0, l = bands.length; i < l; i++) {
var band = bands[i];
- prevRetracted = band.retractedSum = prevRetracted + band.retracted;
- prevDeployed = band.deployedSum = prevDeployed + band.deployed + band.retracted;
+ prevRetracted = band.retractedSum = prevRetracted + band.retracted + band.retOnly;
+ prevDeployed = band.deployedSum = prevDeployed + band.deployed + band.retracted;
}
this.powerAvailable = this.common[0].c.pGen;
@@ -319,32 +360,21 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
};
Ship.prototype.updateShieldStrength = function() {
- var sgSI = this.findInternalByGroup('sg'); // Find Shield Generator slot Index if any
- this.shieldStrength = sgSI != -1? calcShieldStrength(this.mass, this.shields, this.internal[sgSI].c, this.shieldMultiplier) : 0;
+ var sgSlot = this.findInternalByGroup('sg'); // Find Shield Generator slot Index if any
+ this.shieldStrength = sgSlot && sgSlot.enabled ? calcShieldStrength(this.mass, this.shields, sgSlot.c, this.shieldMultiplier) : 0;
};
/**
* Jump Range and total range calculations
*/
Ship.prototype.updateJumpStats = function() {
- var fsd = this.common[2].c; // Frame Shift Drive;
- var fuelRemaining = this.fuelCapacity % fsd.maxfuel; // Fuel left after making N max jumps
- var jumps = this.fuelCapacity / fsd.maxfuel;
+ var fsd = this.common[2].c; // Frame Shift Drive;
this.unladenRange = calcJumpRange(this.unladenMass + fsd.maxfuel, fsd, this.fuelCapacity); // Include fuel weight for jump
this.fullTankRange = calcJumpRange(this.unladenMass + this.fuelCapacity, fsd, this.fuelCapacity); // Full Tanke
this.ladenRange = calcJumpRange(this.ladenMass, fsd, this.fuelCapacity);
- this.maxJumpCount = Math.ceil(jumps); // Number of full fuel jumps + final jump to empty tank
-
- // Going backwards, start with the last jump using the remaining fuel
- this.unladenTotalRange = fuelRemaining > 0? calcJumpRange(this.unladenMass + fuelRemaining, fsd, fuelRemaining): 0;
- this.ladenTotalRange = fuelRemaining > 0? calcJumpRange(this.unladenMass + this.cargoCapacity + fuelRemaining, fsd, fuelRemaining): 0;
-
- // For each max fuel jump, calculate the max jump range based on fuel left in the tank
- for (var j = 0, l = Math.floor(jumps); j < l; j++) {
- fuelRemaining += fsd.maxfuel;
- this.unladenTotalRange += calcJumpRange(this.unladenMass + fuelRemaining, fsd);
- this.ladenTotalRange += calcJumpRange(this.unladenMass + this.cargoCapacity + fuelRemaining, fsd);
- }
+ this.unladenTotalRange = calcTotalRange(this.unladenMass, fsd, this.fuelCapacity);
+ this.ladenTotalRange = calcTotalRange(this.unladenMass + this.cargoCapacity, fsd, this.fuelCapacity);
+ this.maxJumpCount = Math.ceil(this.fuelCapacity / fsd.maxfuel);
};
return Ship;
diff --git a/app/js/shipyard/module-shipyard.js b/app/js/shipyard/module-shipyard.js
index 8702f923..8c98dad4 100755
--- a/app/js/shipyard/module-shipyard.js
+++ b/app/js/shipyard/module-shipyard.js
@@ -22,48 +22,48 @@ angular.module('shipyard', ['ngLodash'])
// Map to lookup group labels/names for component grp
.value('GroupMap', {
// Common
- pp:'Power Plant',
- t:'Thrusters',
- fsd:'Frame Shift Drive',
- ls:'Life Support',
- pd:'Power Distributor',
- s:'Sensors',
- ft:'Fuel Tank',
+ pp: 'Power Plant',
+ t: 'Thrusters',
+ fsd: 'Frame Shift Drive',
+ ls: 'Life Support',
+ pd: 'Power Distributor',
+ s: 'Sensors',
+ ft: 'Fuel Tank',
// Internal
- fs:'Fuel Scoop',
- sc:'Scanners',
- am:'Auto Field-Maint. Unit',
- cr:'Cargo Racks',
- fi:'FSD Interdictor',
- hb:'Hatch Breaker Limpet Ctrl',
- hr:'Hull Reinforcement Package',
- rf:'Refinery',
- scb:'Shield Cell Bank',
- sg:'Shield Generator',
- dc:'Docking Computer',
- fx:'Fuel Transfer Limpet Ctrl',
- pc:'Prospector Limpet Ctrl',
- cc:'Collector Limpet Ctrl',
+ fs: 'Fuel Scoop',
+ sc: 'Scanners',
+ am: 'Auto Field-Maint. Unit',
+ cr: 'Cargo Racks',
+ fi: 'FSD Interdictor',
+ hb: 'Hatch Breaker Limpet Ctrl',
+ hr: 'Hull Reinforcement Package',
+ rf: 'Refinery',
+ scb: 'Shield Cell Bank',
+ sg: 'Shield Generator',
+ dc: 'Docking Computer',
+ fx: 'Fuel Transfer Limpet Ctrl',
+ pc: 'Prospector Limpet Ctrl',
+ cc: 'Collector Limpet Ctrl',
// Hard Points
- bl: "Beam Laser",
- ul: "Burst Laser",
- c: "Cannon",
- cs: "Cargo Scanner",
- cm: "Countermeasure",
- fc: "Fragment Cannon",
- ws: "Frame Shift Wake Scanner",
- kw: "Kill Warrant Scanner",
- nl: "Mine Launcher",
- ml: "Mining Laser",
- mr: "Missile Rack",
- pa: "Plasma Accelerator",
- mc: "Multi-cannon",
- pl: "Pulse Laser",
- rg: "Rail Gun",
- sb: "Shield Booster",
- tp: "Torpedo Pylon"
+ bl: 'Beam Laser',
+ ul: 'Burst Laser',
+ c: 'Cannon',
+ cs: 'Cargo Scanner',
+ cm: 'Countermeasure',
+ fc: 'Fragment Cannon',
+ ws: 'Frame Shift Wake Scanner',
+ kw: 'Kill Warrant Scanner',
+ nl: 'Mine Launcher',
+ ml: 'Mining Laser',
+ mr: 'Missile Rack',
+ pa: 'Plasma Accelerator',
+ mc: 'Multi-cannon',
+ pl: 'Pulse Laser',
+ rg: 'Rail Gun',
+ sb: 'Shield Booster',
+ tp: 'Torpedo Pylon'
})
.value('shipPurpose', {
mp: 'Multi Purpose',
@@ -77,7 +77,7 @@ angular.module('shipyard', ['ngLodash'])
'Small',
'Medium',
'Large',
- 'Capital',
+ 'Capital'
])
.value('hardPointClass', [
'Utility',
@@ -146,7 +146,7 @@ angular.module('shipyard', ['ngLodash'])
},
{ // 8
title: 'Power',
- props: ['powerRetracted','powerDeployed','powerAvailable'],
+ props: ['powerRetracted', 'powerDeployed', 'powerAvailable'],
lbls: ['Retracted', 'Deployed', 'Available'],
unit: 'MW',
fmt: 'fPwr'
@@ -163,7 +163,7 @@ angular.module('shipyard', ['ngLodash'])
lbls: ['Unladen', 'Laden'],
unit: 'LY',
fmt: 'fRound'
- },
+ }
])
/**
* Calculate the maximum single jump range based on mass and a specific FSD
@@ -174,7 +174,28 @@ angular.module('shipyard', ['ngLodash'])
* @return {number} Distance in Light Years
*/
.value('calcJumpRange', function(mass, fsd, fuel) {
- return Math.pow(Math.min(fuel === undefined? fsd.maxfuel : fuel, fsd.maxfuel) / fsd.fuelmul, 1 / fsd.fuelpower ) * fsd.optmass / mass;
+ return Math.pow(Math.min(fuel === undefined ? fsd.maxfuel : fuel, fsd.maxfuel) / fsd.fuelmul, 1 / fsd.fuelpower ) * fsd.optmass / mass;
+ })
+ /**
+ * Calculate the total range based on mass and a specific FSD, and all fuel available
+ *
+ * @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 {number} fuel The total fuel available
+ * @return {number} Distance in Light Years
+ */
+ .value('calcTotalRange', function(mass, fsd, fuel) {
+ var fuelRemaining = fuel % fsd.maxfuel; // Fuel left after making N max jumps
+ var jumps = fuel / fsd.maxfuel;
+ mass += fuelRemaining;
+ // Going backwards, start with the last jump using the remaining fuel
+ var totalRange = fuelRemaining > 0 ? Math.pow(fuelRemaining / fsd.fuelmul, 1 / fsd.fuelpower ) * fsd.optmass / mass : 0;
+ // For each max fuel jump, calculate the max jump range based on fuel left in the tank
+ for (var j = 0, l = Math.floor(jumps); j < l; j++) {
+ fuelRemaining += fsd.maxfuel;
+ totalRange += Math.pow(fsd.maxfuel / fsd.fuelmul, 1 / fsd.fuelpower ) * fsd.optmass / mass;
+ }
+ return totalRange;
})
/**
* Calculate the a ships shield strength based on mass, shield generator and shield boosters used.
@@ -186,10 +207,7 @@ angular.module('shipyard', ['ngLodash'])
* @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any)
* @return {number} Approximate shield strengh in MJ
*/
- .value('calcShieldStrength', function (mass, shields, sg, multiplier) {
- if (!sg) {
- return 0;
- }
+ .value('calcShieldStrength', function(mass, shields, sg, multiplier) {
if (mass <= sg.minmass) {
return shields * multiplier * sg.minmul;
}
@@ -200,4 +218,20 @@ angular.module('shipyard', ['ngLodash'])
return shields * multiplier * (sg.optmul + (mass - sg.optmass) / (sg.maxmass - sg.optmass) * (sg.maxmul - sg.optmul));
}
return shields * multiplier * sg.maxmul;
+ })
+ /**
+ * Calculate the a ships speed based on mass, and thrusters. Currently Innacurate / Incomplete :(
+ *
+ * @private
+ * @param {number} mass Current mass of the ship
+ * @param {number} baseSpeed Base speed m/s for ship
+ * @param {number} baseBoost Base boost m/s for ship
+ * @param {object} thrusters The shield generator used
+ * @return {object} Approximate speed and boost speed in m/s
+ */
+ .value('calcSpeed', function(mass, baseSpeed, baseBoost) { //, thrusters) {
+ //var speed = baseSpeed * (1 + ((thrusters.optmass / mass) * 0.1 ) ); // TODO: find thruser coefficient(s)
+ //var boost = baseBoost * (1 + ((thrusters.optmass / mass) * 0.1 ) );
+
+ return { boost: baseSpeed, speed: baseBoost };
});
diff --git a/app/js/shipyard/service-components.js b/app/js/shipyard/service-components.js
index ac9c1e95..05a8f202 100755
--- a/app/js/shipyard/service-components.js
+++ b/app/js/shipyard/service-components.js
@@ -1,10 +1,10 @@
-angular.module('shipyard').service('Components', ['lodash', 'ComponentsDB', 'ShipsDB', 'ComponentSet', function (_, C, Ships, ComponentSet) {
+angular.module('shipyard').service('Components', ['lodash', 'ComponentsDB', 'ShipsDB', 'ComponentSet', function(_, C, Ships, ComponentSet) {
this.cargoScoop = function() {
- return { name: 'Cargo Hatch', class: 1, rating: 'H', power: 0.6};
+ return { name: 'Cargo Hatch', class: 1, rating: 'H', power: 0.6 };
};
- this.common = function (typeIndex, componentId) {
+ this.common = function(typeIndex, componentId) {
return C.common[typeIndex][componentId];
};
@@ -49,9 +49,9 @@ angular.module('shipyard').service('Components', ['lodash', 'ComponentsDB', 'Shi
* @param {string} shipId Unique ship Id/Key
* @return {ComponentSet} The set of components the ship can install
*/
- this.forShip = function (shipId) {
+ this.forShip = function(shipId) {
var ship = Ships[shipId];
return new ComponentSet(C, ship.properties.mass + 5, ship.slots.common, ship.slots.internal[0], ship.slots.hardpoints[0]);
};
-}]);
\ No newline at end of file
+}]);
diff --git a/app/less/charts.less b/app/less/charts.less
index 1c1e77c9..6264599a 100755
--- a/app/less/charts.less
+++ b/app/less/charts.less
@@ -1,11 +1,9 @@
-
.chart {
.user-select-none();
display: inline-block;
margin: 0;
cursor: default;
overflow: hidden;
-
width: 33%;
box-sizing: border-box;
@@ -17,12 +15,23 @@
width: 100%;
});
+ .medPhone({
+ .axis {
+ font-size: 0.8em;
+
+ g.tick:nth-child(2n + 1) text {
+ display: none;
+ }
+ }
+ });
+
h3 {
text-align: center;
&[ng-click] {
cursor: pointer;
}
+
}
}
@@ -63,5 +72,3 @@ svg {
stroke-width: 1px;
}
}
-
-
diff --git a/app/less/header.less b/app/less/header.less
index 2f30123c..02e919d1 100755
--- a/app/less/header.less
+++ b/app/less/header.less
@@ -63,7 +63,7 @@ header {
.menu-item-label {
margin-left: 1em;
- .medPhone({
+ .largePhone({
display: none;
});
}
@@ -92,21 +92,41 @@ header {
.tablet({
- a {
+ li, a {
padding: 0.3em 0;
}
});
+ }
+ .dbl {
+ -webkit-column-count: 2; /* Chrome, Safari, Opera */
+ -moz-column-count: 2; /* Firefox */
+ column-count: 2;
+ ul {
+ min-width: 10em;
+ }
+ .smallTablet({
+ -webkit-column-count: 3; /* Chrome, Safari, Opera */
+ -moz-column-count: 3; /* Firefox */
+ column-count: 3;
- &.dbl {
+ ul {
+ min-width: 20em;
+ }
+ });
+
+ .largePhone({
-webkit-column-count: 2; /* Chrome, Safari, Opera */
-moz-column-count: 2; /* Firefox */
column-count: 2;
- ul {
- width: 10em;
- }
- }
+ });
+
+ .smallPhone({
+ -webkit-column-count: 1; /* Chrome, Safari, Opera */
+ -moz-column-count: 1; /* Firefox */
+ column-count: 1;
+ });
}
ul {
diff --git a/app/less/outfit.less b/app/less/outfit.less
index 2d281c86..faad7a9e 100755
--- a/app/less/outfit.less
+++ b/app/less/outfit.less
@@ -162,16 +162,38 @@ table.total {
});
.smallTablet({
- overflow-x: auto;
- -webkit-overflow-scrolling: touch;
+ width: 100% !important;
+ });
+ }
+
+ &.semi {
+ width: 33%;
+
+ .smallTablet({
+ .axis {
+ font-size: 0.8em;
+
+ g.tick:nth-child(2n + 1) text {
+ display: none;
+ }
+ }
+ });
+
+ .largePhone({
width: 100% !important;
});
}
}
+.power-band {
+ text, rect {
+ cursor: pointer;
+ }
+}
+
#componentPriority {
.tablet({
- text.primary, text.warning, text.primary-bg {
+ text.primary, text.warning, text.primary-bg, text.secondary {
font-size: 0.8em;
}
diff --git a/app/less/slot.less b/app/less/slot.less
index 3258f021..a7d15f0f 100755
--- a/app/less/slot.less
+++ b/app/less/slot.less
@@ -44,6 +44,7 @@
border-right: 1px solid @primary-disabled;
box-sizing: border-box;
padding-top: 0.2em;
+ padding-left: 0.1em;
}
.empty {
diff --git a/app/views/_header.html b/app/views/_header.html
index 162878b7..ce8d926f 100755
--- a/app/views/_header.html
+++ b/app/views/_header.html
@@ -18,13 +18,15 @@
-
@@ -49,6 +51,10 @@
+
diff --git a/app/views/modal-about.html b/app/views/modal-about.html
index 962f8bfc..85b5c887 100755
--- a/app/views/modal-about.html
+++ b/app/views/modal-about.html
@@ -16,7 +16,7 @@
Any and all contributions and feedback are welcome. If you encounter any bugs please report them and provide as much detail as possible.
-