mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-08 22:33:24 +00:00
More refactoring and porting to React
This commit is contained in:
43
.eslintrc
43
.eslintrc
@@ -1,21 +1,52 @@
|
|||||||
{
|
{
|
||||||
|
"parser": "babel-eslint",
|
||||||
"ecmaFeatures": {
|
"ecmaFeatures": {
|
||||||
"jsx": true,
|
"jsx": true,
|
||||||
|
"classes": true,
|
||||||
"modules": true
|
"modules": true
|
||||||
},
|
},
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"node": true
|
"node": true
|
||||||
},
|
},
|
||||||
"parser": "babel-eslint",
|
"plugins": [
|
||||||
|
"react"
|
||||||
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"strict": 0,
|
||||||
|
"no-underscore-dangle": 0,
|
||||||
|
"valid-jsdoc": [2, {
|
||||||
|
"requireReturn": false
|
||||||
|
}],
|
||||||
|
"require-jsdoc": [2, {
|
||||||
|
"require": {
|
||||||
|
"FunctionDeclaration": true,
|
||||||
|
"MethodDefinition": true,
|
||||||
|
"ClassDeclaration": true
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
|
||||||
|
"comma-style": [2, "last"],
|
||||||
|
"indent": [2, 2, { "SwitchCase": 1, "VariableDeclarator": 2 }],
|
||||||
"quotes": [2, "single"],
|
"quotes": [2, "single"],
|
||||||
"strict": [2, "never"],
|
"no-spaced-func": 2,
|
||||||
|
"operator-linebreak": [2, "after"],
|
||||||
|
"padded-blocks": [2, "never"],
|
||||||
|
"semi": [2, "always"],
|
||||||
|
"no-undef": 2,
|
||||||
|
"semi-spacing": [2, { "before": false, "after": true }],
|
||||||
|
"space-before-blocks": [2, "always"],
|
||||||
|
"space-before-function-paren": [2, "never"],
|
||||||
|
"object-curly-spacing": [2, "always"],
|
||||||
|
"array-bracket-spacing": [2, "never"],
|
||||||
|
"computed-property-spacing": [2, "never"],
|
||||||
|
"space-in-parens": [2, "never"],
|
||||||
|
"space-infix-ops": 2,
|
||||||
|
"spaced-comment": [2, "always"],
|
||||||
|
"no-var": 2,
|
||||||
|
"object-shorthand": [2, "always"],
|
||||||
"react/jsx-uses-react": 2,
|
"react/jsx-uses-react": 2,
|
||||||
"react/jsx-uses-vars": 2,
|
"react/jsx-uses-vars": 2,
|
||||||
"react/react-in-jsx-scope": 2
|
"react/react-in-jsx-scope": 2
|
||||||
},
|
}
|
||||||
"plugins": [
|
|
||||||
"react"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,12 @@ var config = require('./webpack.config.dev');
|
|||||||
new WebpackDevServer(webpack(config), {
|
new WebpackDevServer(webpack(config), {
|
||||||
publicPath: config.output.publicPath,
|
publicPath: config.output.publicPath,
|
||||||
hot: true,
|
hot: true,
|
||||||
historyApiFallback: true
|
historyApiFallback: {
|
||||||
|
rewrites: [
|
||||||
|
// For some reason connect-history-api-fallback does not allow '.' in the URL for history fallback...
|
||||||
|
{ from: /\/outfit\//, to: '/index.html' }
|
||||||
|
]
|
||||||
|
}
|
||||||
}).listen(3300, "0.0.0.0", function (err, result) {
|
}).listen(3300, "0.0.0.0", function (err, result) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -13,9 +13,10 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rimraf build",
|
"clean": "rimraf build",
|
||||||
"start": "node devServer.js",
|
"start": "node devServer.js",
|
||||||
"lint": "eslint ./src",
|
"lint": "eslint --ext .js,.jsx src",
|
||||||
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
|
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
|
||||||
"prod-stop": "kill -QUIT $(cat nginx.pid)",
|
"prod-stop": "kill -QUIT $(cat nginx.pid)",
|
||||||
|
"build:prod": "npm run clean && NODE_ENV=production CDN='//cdn.coriolis.io' webpack -d -p --config webpack.config.prod.js",
|
||||||
"build": "npm run clean && NODE_ENV=production webpack -d -p --config webpack.config.prod.js",
|
"build": "npm run clean && NODE_ENV=production webpack -d -p --config webpack.config.prod.js",
|
||||||
"rsync": "rsync -e 'ssh -i $CORIOLIS_PEM' -a --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/www",
|
"rsync": "rsync -e 'ssh -i $CORIOLIS_PEM' -a --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/www",
|
||||||
"deploy": "npm run lint && npm run build && npm run rsync"
|
"deploy": "npm run lint && npm run build && npm run rsync"
|
||||||
@@ -23,13 +24,15 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"appcache-webpack-plugin": "^1.2.1",
|
"appcache-webpack-plugin": "^1.2.1",
|
||||||
"babel-core": "^5.4.7",
|
"babel-core": "^5.4.7",
|
||||||
"babel-eslint": "^3.1.9",
|
"babel-eslint": "^4.1.6",
|
||||||
"babel-loader": "^5.1.2",
|
"babel-loader": "^5.1.2",
|
||||||
"babel-plugin-react-transform": "^1.1.1",
|
"babel-plugin-react-transform": "^1.1.1",
|
||||||
"css-loader": "^0.23.0",
|
"css-loader": "^0.23.0",
|
||||||
"eslint": "^1.3.1",
|
"eslint": "^1.10.1",
|
||||||
"eslint-plugin-react": "^2.3.0",
|
"eslint-plugin-react": "^2.3.0",
|
||||||
|
"expose-loader": "^0.7.1",
|
||||||
"express": "^4.13.3",
|
"express": "^4.13.3",
|
||||||
|
"extract-text-webpack-plugin": "^0.9.1",
|
||||||
"file-loader": "^0.8.4",
|
"file-loader": "^0.8.4",
|
||||||
"html-webpack-plugin": "^1.7.0",
|
"html-webpack-plugin": "^1.7.0",
|
||||||
"json-loader": "^0.5.3",
|
"json-loader": "^0.5.3",
|
||||||
@@ -47,7 +50,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"classnames": "^2.2.0",
|
"classnames": "^2.2.0",
|
||||||
"d3": "^3.5.9",
|
"d3": "^3.5.9",
|
||||||
"extract-text-webpack-plugin": "^0.9.1",
|
|
||||||
"fbemitter": "^2.0.0",
|
"fbemitter": "^2.0.0",
|
||||||
"lz-string": "^1.4.4",
|
"lz-string": "^1.4.4",
|
||||||
"react": "^0.14.2",
|
"react": "^0.14.2",
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ export default class Coriolis extends React.Component {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._setPage = this._setPage.bind(this);
|
this._setPage = this._setPage.bind(this);
|
||||||
|
this._openMenu = this._openMenu.bind(this);
|
||||||
|
this._closeMenu = this._closeMenu.bind(this);
|
||||||
|
this._showModal = this._showModal.bind(this);
|
||||||
|
this._hideModal = this._hideModal.bind(this);
|
||||||
|
this._onLanguageChange = this._onLanguageChange.bind(this)
|
||||||
|
this._keyDown = this._keyDown.bind(this);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
page: null,
|
page: null,
|
||||||
@@ -28,22 +34,20 @@ export default class Coriolis extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Router('', (r) => this._setPage(ShipyardPage, r));
|
Router('', (r) => this._setPage(ShipyardPage, r));
|
||||||
// Router('/', (ctx) => this._setPage(ShipyardPage, ctx));
|
Router('/outfit/:ship/:code?', (r) => this._setPage(OutfittingPage, r));
|
||||||
Router('/outfitting/:ship', (r) => this._setPage(OutfittingPage, r));
|
|
||||||
Router('/outfitting/:ship/:code', (r) => this._setPage(OutfittingPage, r));
|
|
||||||
// Router('/compare/:name', compare);
|
// Router('/compare/:name', compare);
|
||||||
// Router('/comparison/:code', comparison);
|
// Router('/comparison/:code', comparison);
|
||||||
// Router('/settings', settings);
|
|
||||||
Router('/about', (r) => this._setPage(AboutPage, r));
|
Router('/about', (r) => this._setPage(AboutPage, r));
|
||||||
Router('*', (r) => this._setPage(null, r));
|
Router('*', (r) => this._setPage(null, r));
|
||||||
}
|
}
|
||||||
|
|
||||||
_setPage(page, route) {
|
_setPage(page, route) {
|
||||||
this.setState({ page, route });
|
this.setState({ page, route, currentMenu: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
_onError(msg, scriptUrl, line, col, errObj) {
|
_onError(msg, scriptUrl, line, col, errObj) {
|
||||||
this._setPage(<div>Some errors occured!!</div>);
|
console.log('WINDOW ERROR', arguments);
|
||||||
|
//this._setPage(<div>Some errors occured!!</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onLanguageChange(lang) {
|
_onLanguageChange(lang) {
|
||||||
@@ -53,8 +57,8 @@ export default class Coriolis extends React.Component {
|
|||||||
_keyDown(e) {
|
_keyDown(e) {
|
||||||
switch (e.keyCode) {
|
switch (e.keyCode) {
|
||||||
case 27:
|
case 27:
|
||||||
InterfaceEvents.closeAll();
|
|
||||||
this._hideModal();
|
this._hideModal();
|
||||||
|
this._closeMenu();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,6 +74,18 @@ export default class Coriolis extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_openMenu(currentMenu) {
|
||||||
|
if (this.state.currentMenu != currentMenu) {
|
||||||
|
this.setState({ currentMenu });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_closeMenu() {
|
||||||
|
if (this.state.currentMenu) {
|
||||||
|
this.setState({ currentMenu: null });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getChildContext() {
|
getChildContext() {
|
||||||
return {
|
return {
|
||||||
language: this.state.language,
|
language: this.state.language,
|
||||||
@@ -82,28 +98,30 @@ export default class Coriolis extends React.Component {
|
|||||||
if (window.applicationCache) {
|
if (window.applicationCache) {
|
||||||
window.applicationCache.addEventListener('updateready', () => {
|
window.applicationCache.addEventListener('updateready', () => {
|
||||||
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
|
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
|
||||||
this.setState({appCacheUpdate: true}); // Browser downloaded a new app cache.
|
this.setState({ appCacheUpdate: true }); // Browser downloaded a new app cache.
|
||||||
}
|
}
|
||||||
}, false);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.onerror = this._onError.bind(this);
|
window.onerror = this._onError.bind(this);
|
||||||
document.addEventListener('keydown', this._keyDown.bind(this));
|
window.addEventListener('resize', InterfaceEvents.windowResized);
|
||||||
Persist.addListener('language', this._onLanguageChange.bind(this));
|
document.addEventListener('keydown', this._keyDown);
|
||||||
Persist.addListener('language', this._onLanguageChange.bind(this));
|
Persist.addListener('language', this._onLanguageChange);
|
||||||
InterfaceEvents.addListener('showModal', this._showModal.bind(this));
|
Persist.addListener('language', this._onLanguageChange);
|
||||||
InterfaceEvents.addListener('hideModal', this._hideModal.bind(this));
|
InterfaceEvents.addListener('openMenu', this._openMenu);
|
||||||
|
InterfaceEvents.addListener('closeMenu', this._closeMenu);
|
||||||
|
InterfaceEvents.addListener('showModal', this._showModal);
|
||||||
|
InterfaceEvents.addListener('hideModal', this._hideModal);
|
||||||
|
|
||||||
Router.start();
|
Router.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div onClick={InterfaceEvents.closeAll}>
|
<div onClick={this._closeMenu}>
|
||||||
<Header appCacheUpdate={this.state.appCacheUpdate} />
|
<Header appCacheUpdate={this.state.appCacheUpdate} currentMenu={this.state.currentMenu} />
|
||||||
{this.state.page? <this.state.page /> : <NotFoundPage/>}
|
{ this.state.page ? <this.state.page currentMenu={this.state.currentMenu} /> : <NotFoundPage/> }
|
||||||
{this.state.modal}
|
{ this.state.modal }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import Persist from './stores/Persist';
|
import Persist from './stores/Persist';
|
||||||
import InterfaceEvents from './utils/InterfaceEvents';
|
|
||||||
|
|
||||||
function isStandAlone() {
|
function isStandAlone() {
|
||||||
try {
|
try {
|
||||||
@@ -49,7 +48,7 @@ Router.start = function(){
|
|||||||
Router('/');
|
Router('/');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var url = location.pathname + location.search + location.hash;
|
var url = location.pathname + location.search;
|
||||||
Router.replace(url, null, true, true);
|
Router.replace(url, null, true, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -64,10 +63,11 @@ Router.start = function(){
|
|||||||
*/
|
*/
|
||||||
Router.go = function(path, state) {
|
Router.go = function(path, state) {
|
||||||
gaTrack(path);
|
gaTrack(path);
|
||||||
InterfaceEvents.closeAll();
|
|
||||||
var ctx = new Context(path, state);
|
var ctx = new Context(path, state);
|
||||||
Router.dispatch(ctx);
|
Router.dispatch(ctx);
|
||||||
if (!ctx.unhandled) ctx.pushState();
|
if (!ctx.unhandled) {
|
||||||
|
history.pushState(ctx.state, ctx.title, ctx.canonicalPath);
|
||||||
|
}
|
||||||
return ctx;
|
return ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -80,12 +80,11 @@ Router.go = function(path, state) {
|
|||||||
* @api public
|
* @api public
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Router.replace = function(path, state, init, dispatch) {
|
Router.replace = function(path, state, dispatch) {
|
||||||
gaTrack(path);
|
gaTrack(path);
|
||||||
var ctx = new Context(path, state);
|
var ctx = new Context(path, state);
|
||||||
ctx.init = init;
|
if (dispatch) Router.dispatch(ctx);
|
||||||
if (dispatch !== false) Router.dispatch(ctx);
|
history.replaceState(ctx.state, ctx.title, ctx.canonicalPath);
|
||||||
ctx.save();
|
|
||||||
return ctx;
|
return ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -119,8 +118,10 @@ Router.dispatch = function(ctx){
|
|||||||
|
|
||||||
function unhandled(ctx) {
|
function unhandled(ctx) {
|
||||||
var current = window.location.pathname + window.location.search;
|
var current = window.location.pathname + window.location.search;
|
||||||
if (current == ctx.canonicalPath) return;
|
if (current != ctx.canonicalPath) {
|
||||||
window.location = ctx.canonicalPath;
|
window.location = ctx.canonicalPath;
|
||||||
|
}
|
||||||
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,43 +143,14 @@ function Context(path, state) {
|
|||||||
this.state.path = path;
|
this.state.path = path;
|
||||||
this.querystring = ~i ? path.slice(i + 1) : '';
|
this.querystring = ~i ? path.slice(i + 1) : '';
|
||||||
this.pathname = ~i ? path.slice(0, i) : path;
|
this.pathname = ~i ? path.slice(0, i) : path;
|
||||||
this.params = [];
|
this.params = {};
|
||||||
|
|
||||||
// fragment
|
this.querystring.split('&').forEach((str) =>{
|
||||||
this.hash = '';
|
let query = str.split('=');
|
||||||
if (!~this.path.indexOf('#')) return;
|
this.params[query[0]] = decodeURIComponent(query[1]);
|
||||||
var parts = this.path.split('#');
|
}, this);
|
||||||
this.path = parts[0];
|
|
||||||
this.hash = parts[1] || '';
|
|
||||||
this.querystring = this.querystring.split('#')[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Expose `Context`.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Router.Context = Context;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Push state.
|
|
||||||
*
|
|
||||||
* @api private
|
|
||||||
*/
|
|
||||||
|
|
||||||
Context.prototype.pushState = function(){
|
|
||||||
history.pushState(this.state, this.title, this.canonicalPath);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the context state.
|
|
||||||
*
|
|
||||||
* @api public
|
|
||||||
*/
|
|
||||||
|
|
||||||
Context.prototype.save = function(){
|
|
||||||
history.replaceState(this.state, this.title, this.canonicalPath);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize `Route` with the given HTTP `path`,
|
* Initialize `Route` with the given HTTP `path`,
|
||||||
* and an array of `callbacks` and `options`.
|
* and an array of `callbacks` and `options`.
|
||||||
@@ -203,12 +175,6 @@ function Route(path, options) {
|
|||||||
, options.strict);
|
, options.strict);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Expose `Route`.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Router.Route = Route;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return route middleware with
|
* Return route middleware with
|
||||||
* the given callback `fn()`.
|
* the given callback `fn()`.
|
||||||
@@ -247,22 +213,33 @@ Route.prototype.match = function(path, params){
|
|||||||
for (var i = 1, len = m.length; i < len; ++i) {
|
for (var i = 1, len = m.length; i < len; ++i) {
|
||||||
var key = keys[i - 1];
|
var key = keys[i - 1];
|
||||||
|
|
||||||
var val = 'string' == typeof m[i]
|
var val = 'string' == typeof m[i] ? decodeURIComponent(m[i]) : m[i];
|
||||||
? decodeURIComponent(m[i])
|
|
||||||
: m[i];
|
|
||||||
|
|
||||||
if (key) {
|
if (key) {
|
||||||
params[key.name] = undefined !== params[key.name]
|
params[key.name] = undefined !== params[key.name] ? params[key.name] : val;
|
||||||
? params[key.name]
|
|
||||||
: val;
|
|
||||||
} else {
|
|
||||||
params.push(val);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the app is running in stand alone mode.
|
||||||
|
* @return {Boolean} true if running in Standalone mode
|
||||||
|
*/
|
||||||
|
function isStandAlone() {
|
||||||
|
try {
|
||||||
|
return window.navigator.standalone || (window.external && window.external.msIsSiteMode && window.external.msIsSiteMode());
|
||||||
|
} catch (ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track a page view in Google Analytics
|
||||||
|
* @param {string} path
|
||||||
|
*/
|
||||||
function gaTrack(path) {
|
function gaTrack(path) {
|
||||||
if (window.ga) {
|
if (window.ga) {
|
||||||
window.ga('send', 'pageview', { page: path });
|
window.ga('send', 'pageview', { page: path });
|
||||||
@@ -314,7 +291,7 @@ function pathtoRegexp(path, keys, sensitive, strict) {
|
|||||||
function onpopstate(e) {
|
function onpopstate(e) {
|
||||||
if (e.state) {
|
if (e.state) {
|
||||||
var path = e.state.path;
|
var path = e.state.path;
|
||||||
Router.replace(path, e.state);
|
Router.replace(path, e.state, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,13 +31,13 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
disabled: m.maxmass && (mass + (m.mass ? m.mass : 0)) > m.maxmass
|
disabled: m.maxmass && (mass + (m.mass ? m.mass : 0)) > m.maxmass
|
||||||
});
|
});
|
||||||
|
|
||||||
switch(m.mode) {
|
switch(m.mount) {
|
||||||
case 'F': mount = <MountFixed className={'lg'} />; break;
|
case 'F': mount = <MountFixed className={'lg'} />; break;
|
||||||
case 'G': mount = <MountGimballed className={'lg'}/>; break;
|
case 'G': mount = <MountGimballed className={'lg'}/>; break;
|
||||||
case 'T': mount = <MountTurret className={'lg'}/>; break;
|
case 'T': mount = <MountTurret className={'lg'}/>; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i > 0 && modules.length > 3 && m.class != prevClass && (m.rating != prevRating || m.mode) && m.grp != 'pa') {
|
if (i > 0 && modules.length > 3 && m.class != prevClass && (m.rating != prevRating || m.mount) && m.grp != 'pa') {
|
||||||
elems.push(<br key={m.grp + i} />);
|
elems.push(<br key={m.grp + i} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,15 +77,12 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (modules instanceof Array) {
|
if (modules instanceof Array) {
|
||||||
console.log(modules[0].grp, modules);
|
|
||||||
list = buildGroup(modules[0].grp, modules);
|
list = buildGroup(modules[0].grp, modules);
|
||||||
} else {
|
} else {
|
||||||
console.log('menu object')
|
|
||||||
list = [];
|
list = [];
|
||||||
// At present time slots with grouped options (Hardpoints and Internal) can be empty
|
// At present time slots with grouped options (Hardpoints and Internal) can be empty
|
||||||
list.push(<div className={'empty-c upp'} key={'empty'} onClick={this.props.onSelect.bind(null, null)} >{translate('empty')}</div>);
|
list.push(<div className={'empty-c upp'} key={'empty'} onClick={this.props.onSelect.bind(null, null)} >{translate('empty')}</div>);
|
||||||
for (let g in modules) {
|
for (let g in modules) {
|
||||||
let grp = modules[g];
|
|
||||||
list.push(<div ref={g} key={g} className={'select-group cap'}>{translate(g)}</div>);
|
list.push(<div ref={g} key={g} className={'select-group cap'}>{translate(g)}</div>);
|
||||||
list.push(buildGroup(g, modules[g]));
|
list.push(buildGroup(g, modules[g]));
|
||||||
}
|
}
|
||||||
|
|||||||
435
src/app/components/CostSection.jsx
Normal file
435
src/app/components/CostSection.jsx
Normal file
@@ -0,0 +1,435 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import cn from 'classnames';
|
||||||
|
import { Ships } from 'coriolis-data';
|
||||||
|
import Persist from '../stores/Persist';
|
||||||
|
import Ship from '../shipyard/Ship';
|
||||||
|
import { Insurance } from '../shipyard/Constants';
|
||||||
|
import { slotName, nameComparator } from '../utils/SlotFunctions';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
|
||||||
|
export default class CostSection extends TranslatedComponent {
|
||||||
|
|
||||||
|
static PropTypes = {
|
||||||
|
ship: React.PropTypes.object.isRequired,
|
||||||
|
shipId: React.PropTypes.string.isRequired,
|
||||||
|
code: React.PropTypes.string.isRequired,
|
||||||
|
buildName: React.PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this._costsTab = this._costsTab.bind(this);
|
||||||
|
|
||||||
|
let data = Ships[props.shipId]; // Retrieve the basic ship properties, slots and defaults
|
||||||
|
let retrofitName = props.buildName;
|
||||||
|
let shipDiscount = Persist.getShipDiscount();
|
||||||
|
let moduleDiscount = Persist.getModuleDiscount();
|
||||||
|
let existingBuild = Persist.getBuild(props.shipId, retrofitName);
|
||||||
|
let retrofitShip = new Ship(props.shipId, data.properties, data.slots); // Create a new Ship for retrofit comparison
|
||||||
|
|
||||||
|
if (existingBuild) {
|
||||||
|
retrofitShip.buildFrom(existingBuild); // Populate modules from existing build
|
||||||
|
} else {
|
||||||
|
retrofitShip.buildWith(data.defaults); // Populate with default components
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.ship.applyDiscounts(shipDiscount, moduleDiscount);
|
||||||
|
retrofitShip.applyDiscounts(shipDiscount, moduleDiscount);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
retrofitShip,
|
||||||
|
retrofitName,
|
||||||
|
shipDiscount,
|
||||||
|
moduleDiscount,
|
||||||
|
total: props.ship.totalCost,
|
||||||
|
insurance: Insurance[Persist.getInsurance()],
|
||||||
|
tab: Persist.getCostTab(),
|
||||||
|
buildOptions: Persist.getBuildsNamesFor(props.shipId),
|
||||||
|
ammoPredicate: 'module',
|
||||||
|
ammoDesc: true,
|
||||||
|
costPredicate: 'cr',
|
||||||
|
costDesc: true,
|
||||||
|
retroPredicate: 'module',
|
||||||
|
retroDesc: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_showTab(tab) {
|
||||||
|
Persist.setCostTab(tab);
|
||||||
|
this.setState({ tab });
|
||||||
|
}
|
||||||
|
|
||||||
|
_onDiscountChanged() {
|
||||||
|
let shipDiscount = Persist.getShipDiscount();
|
||||||
|
let moduleDiscount = Persist.getModuleDiscount();
|
||||||
|
this.props.ship.applyDiscounts(shipDiscount, moduleDiscount);
|
||||||
|
this.state.retrofitShip.applyDiscounts(shipDiscount, moduleDiscount);
|
||||||
|
this.setState({ shipDiscount, moduleDiscount });
|
||||||
|
}
|
||||||
|
|
||||||
|
_onInsuranceChanged(insuranceName) {
|
||||||
|
this.setState({ insurance: Insurance[insuranceName] });
|
||||||
|
}
|
||||||
|
|
||||||
|
_onBaseRetrofitChange(retrofitName) {
|
||||||
|
let existingBuild = Persist.getBuild(this.props.shipId, retrofitName);
|
||||||
|
this.state.retrofitShip.buildFrom(existingBuild); // Repopulate modules from existing build
|
||||||
|
this.setState({ retrofitName });
|
||||||
|
}
|
||||||
|
|
||||||
|
_onBuildSaved(shipId, name, code) {
|
||||||
|
if(this.state.retrofitName == name) {
|
||||||
|
this.state.retrofitShip.buildFrom(code); // Repopulate modules from saved build
|
||||||
|
} else {
|
||||||
|
this.setState({buildOptions: Persist.getBuildsNamesFor(this.props.shipId) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_toggleCost(item) {
|
||||||
|
this.props.ship.setCostIncluded(item, !item.incCost);
|
||||||
|
this.setState({ total: this.props.ship.totalCost });
|
||||||
|
}
|
||||||
|
|
||||||
|
_sortCost(predicate) {
|
||||||
|
let costList = this.props.ship.costList;
|
||||||
|
let { costPredicate, costDesc } = this.state;
|
||||||
|
|
||||||
|
if (predicate) {
|
||||||
|
if (costPredicate == predicate) {
|
||||||
|
costDesc = !costDesc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
predicate = costPredicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (predicate == 'm') {
|
||||||
|
let translate = this.context.language.translate;
|
||||||
|
costList.sort(nameComparator(translate));
|
||||||
|
} else {
|
||||||
|
costList.sort((a, b) => (a.m && a.m.cost ? a.m.cost : 0) - (b.m && b.m.cost ? b.m.cost : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!costDesc) {
|
||||||
|
costList.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ costPredicate: predicate, costDesc });
|
||||||
|
}
|
||||||
|
|
||||||
|
_sortAmmo(predicate) {
|
||||||
|
let { ammoPredicate, ammoDesc, ammoCosts } = this.state;
|
||||||
|
|
||||||
|
if (ammoPredicate == predicate) {
|
||||||
|
ammoDesc = !ammoDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (predicate) {
|
||||||
|
case 'm':
|
||||||
|
let translate = this.context.language.translate;
|
||||||
|
ammoCosts.sort(nameComparator(translate));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ammoCosts.sort((a, b) => a[predicate] - b[predicate]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ammoDesc) {
|
||||||
|
ammoCosts.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ ammoPredicate: predicate, ammoDesc });
|
||||||
|
}
|
||||||
|
|
||||||
|
_costsTab() {
|
||||||
|
let { ship } = this.props;
|
||||||
|
let { total, shipDiscount, moduleDiscount, insurance } = this.state;
|
||||||
|
let { translate, formats, units } = this.context.language;
|
||||||
|
let rows = [];
|
||||||
|
|
||||||
|
for (let i = 0, l = ship.costList.length; i < l; i++) {
|
||||||
|
let item = ship.costList[i];
|
||||||
|
if (item.m && item.m.cost) {
|
||||||
|
let toggle = this._toggleCost.bind(this, item);
|
||||||
|
rows.push(<tr key={i} className={cn('highlight', { disabled: !item.incCost })}>
|
||||||
|
<td className='ptr' style={{ width: '1em' }} onClick={toggle}>{item.m.class + item.m.rating}</td>
|
||||||
|
<td className='le ptr shorten cap' onClick={toggle}>{slotName(translate, item)}</td>
|
||||||
|
<td className='ri ptr' onClick={toggle}>{formats.int(item.discountedCost)}{units.CR}</td>
|
||||||
|
</tr>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
||||||
|
<thead>
|
||||||
|
<tr className='main'>
|
||||||
|
<th colSpan='2' className='sortable le' onClick={this._sortCost.bind(this,'m')}>
|
||||||
|
{translate('component')}
|
||||||
|
{shipDiscount < 1 && <u className='optional-hide'>{`[${translate('ship')} -${formats.rPct(1 - shipDiscount)}]`}</u>}
|
||||||
|
{moduleDiscount < 1 && <u className='optional-hide'>{`[${translate('modules')} -${formats.rPct(1 - moduleDiscount)}]`}</u>}
|
||||||
|
</th>
|
||||||
|
<th className='sortable le' onClick={this._sortCost.bind(this, 'cr')} >{translate('credits')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{rows}
|
||||||
|
<tr className='ri'>
|
||||||
|
<td colSpan='2' className='lbl' >{translate('total')}</td>
|
||||||
|
<td className='val'>{formats.int(total)}{units.CR}</td>
|
||||||
|
</tr>
|
||||||
|
<tr className='ri'>
|
||||||
|
<td colSpan='2' className='lbl'>{translate('insurance')}</td>
|
||||||
|
<td className='val'>{formats.int(total * insurance)}{units.CR}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRetrofitCosts() {
|
||||||
|
var costs = $scope.retrofitList = [];
|
||||||
|
var total = 0, i, l, item;
|
||||||
|
|
||||||
|
if (ship.bulkheads.id != retrofitShip.bulkheads.id) {
|
||||||
|
item = {
|
||||||
|
buyClassRating: ship.bulkheads.m.class + ship.bulkheads.m.rating,
|
||||||
|
buyName: ship.bulkheads.m.name,
|
||||||
|
sellClassRating: retrofitShip.bulkheads.m.class + retrofitShip.bulkheads.m.rating,
|
||||||
|
sellName: retrofitShip.bulkheads.m.name,
|
||||||
|
netCost: ship.bulkheads.discountedCost - retrofitShip.bulkheads.discountedCost,
|
||||||
|
retroItem: retrofitShip.bulkheads
|
||||||
|
};
|
||||||
|
costs.push(item);
|
||||||
|
if (retrofitShip.bulkheads.incCost) {
|
||||||
|
total += item.netCost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var g in { standard: 1, internal: 1, hardpoints: 1 }) {
|
||||||
|
var retroSlotGroup = retrofitShip[g];
|
||||||
|
var slotGroup = ship[g];
|
||||||
|
for (i = 0, l = slotGroup.length; i < l; i++) {
|
||||||
|
if (slotGroup[i].id != retroSlotGroup[i].id) {
|
||||||
|
item = { netCost: 0, retroItem: retroSlotGroup[i] };
|
||||||
|
if (slotGroup[i].id) {
|
||||||
|
item.buyName = slotGroup[i].m.name || slotGroup[i].m.grp;
|
||||||
|
item.buyClassRating = slotGroup[i].m.class + slotGroup[i].m.rating;
|
||||||
|
item.netCost = slotGroup[i].discountedCost;
|
||||||
|
}
|
||||||
|
if (retroSlotGroup[i].id) {
|
||||||
|
item.sellName = retroSlotGroup[i].m.name || retroSlotGroup[i].m.grp;
|
||||||
|
item.sellClassRating = retroSlotGroup[i].m.class + retroSlotGroup[i].m.rating;
|
||||||
|
item.netCost -= retroSlotGroup[i].discountedCost;
|
||||||
|
}
|
||||||
|
costs.push(item);
|
||||||
|
if (retroSlotGroup[i].incCost) {
|
||||||
|
total += item.netCost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$scope.retrofitTotal = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
_retrofitTab() {
|
||||||
|
// return <div>
|
||||||
|
// <div className='scroll-x'>
|
||||||
|
// <table style='width:100%'>
|
||||||
|
// <thead>
|
||||||
|
// <tr className='main'>
|
||||||
|
// <th colspan='2' className='sortable le' ng-click='sortRetrofit('sellName | translate')' >{translate('sell')}</th>
|
||||||
|
// <th colspan='2' className='sortable le' ng-click='sortRetrofit('buyName | translate')' >{translate('buy')}</th>
|
||||||
|
// <th className='sortable le' ng-click='sortRetrofit('netCost')'>
|
||||||
|
// {{'net cost' | translate}} <u className='optional-hide' ng-if='discounts.components < 1'>[-{{fRPct(1 - discounts.components)}}]</u>
|
||||||
|
// </th>
|
||||||
|
// </tr>
|
||||||
|
// </thead>
|
||||||
|
// <tbody>
|
||||||
|
// <tr ng-if='!retrofitList || retrofitList.length == 0'>
|
||||||
|
// <td colspan='5' style='padding: 3em 0;' >{translate('PHRASE_NO_RETROCH')}</td>
|
||||||
|
// </tr>
|
||||||
|
// <tr className='highlight' ng-repeat='item in retrofitList | orderBy:retroPredicate:retroDesc' ng-click='toggleRetrofitCost(item.retroItem)' className={cn({disabled: !item.retroItem.incCost})}>
|
||||||
|
// <td style='width:1em;'>{{item.sellClassRating}}</td>
|
||||||
|
// <td className='le shorten cap'>{{item.sellName | translate}}</td>
|
||||||
|
// <td style='width:1em;'>{{item.buyClassRating}}</td>
|
||||||
|
// <td className='le shorten cap'>{{item.buyName | translate}}</td>
|
||||||
|
// <td className={cn('ri', item.retroItem.incCost ? item.netCost > 0 ? 'warning' : 'secondary-disabled' : 'disabled' )}>{{ fCrd(item.netCost)}} <u translate>CR</u></td>
|
||||||
|
// </tr>
|
||||||
|
// </tbody>
|
||||||
|
// </table>
|
||||||
|
// </div>
|
||||||
|
// <table className='total'>
|
||||||
|
// <tr className='ri'>
|
||||||
|
// <td className='lbl' >{translate('cost')}</td>
|
||||||
|
// <td colSpan={2} className={retrofitTotal > 0 ? 'warning' : 'secondary-disabled'}>{{fCrd(retrofitTotal)}} <u translate>CR</u></td>
|
||||||
|
// </tr>
|
||||||
|
// <tr className='ri'>
|
||||||
|
// <td className='lbl cap' >{translate('retrofit from')}</td>
|
||||||
|
// <td className='cen' style='border-right:none;width: 1em;'><u className='primary-disabled'>▾</u></td>
|
||||||
|
// <td style='border-left:none;padding:0;'>
|
||||||
|
// <select style='width: 100%;padding: 0' ng-model='$parent.retrofitBuild' ng-change='setRetrofitBase()' ng-options='name as name for (name, build) in allBuilds[ship.id]'>
|
||||||
|
// <option value=''>{{'Stock' | translate}}</option>
|
||||||
|
// </select>
|
||||||
|
// </td>
|
||||||
|
// </tr>
|
||||||
|
// </table>
|
||||||
|
// </div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ammoTab() {
|
||||||
|
let { ammoTotal, ammoCosts } = this.state;
|
||||||
|
let { translate, formats, units } = this.context.language;
|
||||||
|
let int = formats.int;
|
||||||
|
let rows = [];
|
||||||
|
|
||||||
|
for (let i = 0, l = ammoCosts.length; i < l; i++) {
|
||||||
|
let item = ammoCosts[i];
|
||||||
|
rows.push(<tr key={i} className='highlight'>
|
||||||
|
<td style={{ width: '1em' }}>{item.m.class + item.m.rating}</td>
|
||||||
|
<td className='le shorten cap'>{slotName(translate, item)}</td>
|
||||||
|
<td className='ri'>{int(item.max)}</td>
|
||||||
|
<td className='ri'>{int(item.cost)}{units.CR}</td>
|
||||||
|
<td className='ri'>{int(item.total)}{units.CR}</td>
|
||||||
|
</tr>);
|
||||||
|
}
|
||||||
|
console.log(rows);
|
||||||
|
return <div>
|
||||||
|
<div className='scroll-x' >
|
||||||
|
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
||||||
|
<thead>
|
||||||
|
<tr className='main'>
|
||||||
|
<th colSpan='2' className='sortable le' onClick={this._sortAmmo.bind(this, 'm')} >{translate('module')}</th>
|
||||||
|
<th colSpan='1' className='sortable le' onClick={this._sortAmmo.bind(this, 'max')} >{translate('qty')}</th>
|
||||||
|
<th colSpan='1' className='sortable le' onClick={this._sortAmmo.bind(this, 'cost')} >{translate('unit cost')}</th>
|
||||||
|
<th className='sortable le' onClick={this._sortAmmo.bind(this, 'total')}>{translate('total cost')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{rows}
|
||||||
|
<tr className='ri'>
|
||||||
|
<td colSpan='4' className='lbl' >{translate('total')}</td>
|
||||||
|
<td className='val'>{int(ammoTotal)}{units.CR}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recalculate all ammo costs
|
||||||
|
*/
|
||||||
|
_updateAmmoCosts() {
|
||||||
|
let ship = this.props.ship;
|
||||||
|
let ammoCosts = [], ammoTotal = 0, item, q, limpets = 0, scoop = false;
|
||||||
|
|
||||||
|
for (let g in { standard: 1, internal: 1, hardpoints: 1 }) {
|
||||||
|
let slotGroup = ship[g];
|
||||||
|
for (let i = 0, l = slotGroup.length; i < l; i++) {
|
||||||
|
if (slotGroup[i].id) {
|
||||||
|
//special cases needed for SCB, AFMU, and limpet controllers since they don't use standard ammo/clip
|
||||||
|
q = 0;
|
||||||
|
switch (slotGroup[i].m.grp) {
|
||||||
|
case 'fs': //skip fuel calculation if scoop present
|
||||||
|
scoop = true;
|
||||||
|
break;
|
||||||
|
case 'scb':
|
||||||
|
q = slotGroup[i].m.cells;
|
||||||
|
break;
|
||||||
|
case 'am':
|
||||||
|
q = slotGroup[i].m.ammo;
|
||||||
|
break;
|
||||||
|
case 'fx': case 'hb': case 'cc': case 'pc':
|
||||||
|
limpets = ship.cargoCapacity;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
q = slotGroup[i].m.clip + slotGroup[i].m.ammo;
|
||||||
|
}
|
||||||
|
//calculate ammo costs only if a cost is specified
|
||||||
|
if (slotGroup[i].m.ammocost > 0) {
|
||||||
|
item = {
|
||||||
|
m: slotGroup[i].m,
|
||||||
|
max: q,
|
||||||
|
cost: slotGroup[i].m.ammocost,
|
||||||
|
total: q * slotGroup[i].m.ammocost
|
||||||
|
};
|
||||||
|
ammoCosts.push(item);
|
||||||
|
ammoTotal += item.total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//limpets if controllers exist and cargo space available
|
||||||
|
if (limpets > 0) {
|
||||||
|
item = {
|
||||||
|
m: { name: 'limpets', class: '', rating: '' },
|
||||||
|
max: ship.cargoCapacity,
|
||||||
|
cost: 101,
|
||||||
|
total: ship.cargoCapacity * 101
|
||||||
|
};
|
||||||
|
ammoCosts.push(item);
|
||||||
|
ammoTotal += item.total;
|
||||||
|
}
|
||||||
|
//calculate refuel costs if no scoop present
|
||||||
|
if (!scoop) {
|
||||||
|
item = {
|
||||||
|
m: { name: 'fuel', class: '', rating: '' },
|
||||||
|
max: ship.fuelCapacity,
|
||||||
|
cost: 50,
|
||||||
|
total: ship.fuelCapacity * 50
|
||||||
|
};
|
||||||
|
ammoCosts.push(item);
|
||||||
|
ammoTotal += item.total;
|
||||||
|
}
|
||||||
|
this.setState({ ammoTotal, ammoCosts });
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount(){
|
||||||
|
this.listeners = [
|
||||||
|
Persist.addListener('discounts', this._onDiscountChanged.bind(this)),
|
||||||
|
Persist.addListener('insurance', this._onInsuranceChanged.bind(this)),
|
||||||
|
];
|
||||||
|
this._updateAmmoCosts(this.props.ship);
|
||||||
|
this._sortCost.call(this, this.state.costPredicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps, nextContext) {
|
||||||
|
this._updateAmmoCosts(nextProps.ship);
|
||||||
|
|
||||||
|
this._sortCost();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount(){
|
||||||
|
// remove window listener
|
||||||
|
this.listeners.forEach(l => l.remove());
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let tab = this.state.tab;
|
||||||
|
let translate = this.context.language.translate;
|
||||||
|
let tabSection;
|
||||||
|
|
||||||
|
switch (tab) {
|
||||||
|
case 'ammo': tabSection = this._ammoTab(); break;
|
||||||
|
case 'retrofit': tabSection = this._retrofitTab(); break;
|
||||||
|
default:
|
||||||
|
tab = 'costs';
|
||||||
|
tabSection = this._costsTab();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='group half'>
|
||||||
|
<table className='tabs'>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style={{ width:'33%' }} className={cn({active: tab == 'costs'})} onClick={this._showTab.bind(this, 'costs')} >{translate('costs')}</th>
|
||||||
|
<th style={{ width:'33%' }} className={cn({active: tab == 'retrofit'})} onClick={this._showTab.bind(this, 'retrofit')} >{translate('retrofit costs')}</th>
|
||||||
|
<th style={{ width:'34%' }} className={cn({active: tab == 'ammo'})} onClick={this._showTab.bind(this, 'ammo')} >{translate('reload costs')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
{tabSection}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,17 +3,17 @@ import Slot from './Slot';
|
|||||||
|
|
||||||
export default class HardpointSlot extends Slot {
|
export default class HardpointSlot extends Slot {
|
||||||
|
|
||||||
getClassNames() {
|
_getClassNames() {
|
||||||
return this.props.size > 0 ? 'hardpoint' : null;
|
return this.props.maxClass > 0 ? 'hardpoint' : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSize(translate){
|
_getMaxClassLabel(translate){
|
||||||
return translate(['U','S','M','L','H'][this.props.size]);
|
return translate(['U','S','M','L','H'][this.props.maxClass]);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSlotDetails(m, translate, formats, u) {
|
_getSlotDetails(m, translate, formats, u) {
|
||||||
if (m) {
|
if (m) {
|
||||||
let classRating = `${m.class}${m.rating}${m.mode ? '/' + m.mode : ''}${m.missile ? m.missile : ''}`;
|
let classRating = `${m.class}${m.rating}${m.mount ? '/' + m.mount : ''}${m.missile ? m.missile : ''}`;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className={'l'}>{classRating + ' ' + translate(m.name || m.grp)}</div>
|
<div className={'l'}>{classRating + ' ' + translate(m.name || m.grp)}</div>
|
||||||
|
|||||||
@@ -13,27 +13,35 @@ export default class HardpointsSlotSection extends SlotSection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_empty() {
|
_empty() {
|
||||||
|
this.props.ship.emptyWeapons();
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
_fill(grp, mount) {
|
_fill(group, mount, event) {
|
||||||
|
this.props.ship.useWeapon(group, mount, null, event.getModifierState('Alt'));
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
|
}
|
||||||
|
|
||||||
|
_contextMenu() {
|
||||||
|
this._empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
_getSlots() {
|
_getSlots() {
|
||||||
let slots = [];
|
let slots = [];
|
||||||
let hardpoints = this.props.ship.hardpoints;
|
let hardpoints = this.props.ship.hardpoints;
|
||||||
let availableModules = this.props.ship.getAvailableModules();
|
let availableModules = this.props.ship.getAvailableModules();
|
||||||
let currentMenu = this.state.currentMenu;
|
let currentMenu = this.props.currentMenu;
|
||||||
|
|
||||||
for (let i = 0, l = hardpoints.length; i < l; i++) {
|
for (let i = 0, l = hardpoints.length; i < l; i++) {
|
||||||
let h = hardpoints[i];
|
let h = hardpoints[i];
|
||||||
if (h.maxClass) {
|
if (h.maxClass) {
|
||||||
slots.push(<HardpointSlot
|
slots.push(<HardpointSlot
|
||||||
key={i}
|
key={i}
|
||||||
size={h.maxClass}
|
maxClass={h.maxClass}
|
||||||
modules={availableModules.getHps(h.maxClass)}
|
availableModules={() => availableModules.getHps(h.maxClass)}
|
||||||
onOpen={this._openMenu.bind(this,h)}
|
onOpen={this._openMenu.bind(this, h)}
|
||||||
onSelect={this._selectModule.bind(this, h)}
|
onSelect={this._selectModule.bind(this, h)}
|
||||||
selected={currentMenu == h}
|
selected={currentMenu == h}
|
||||||
m={h.m}
|
m={h.m}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import Link from './Link';
|
|||||||
import ActiveLink from './ActiveLink';
|
import ActiveLink from './ActiveLink';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { Cogs, CoriolisLogo, Hammer, Rocket, StatsBars } from './SvgIcons';
|
import { Cogs, CoriolisLogo, Hammer, Rocket, StatsBars } from './SvgIcons';
|
||||||
import Ships from '../shipyard/Ships';
|
import { Ships } from 'coriolis-data';
|
||||||
import InterfaceEvents from '../utils/InterfaceEvents';
|
import InterfaceEvents from '../utils/InterfaceEvents';
|
||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
import ModalDeleteAll from './ModalDeleteAll';
|
import ModalDeleteAll from './ModalDeleteAll';
|
||||||
@@ -13,21 +13,12 @@ export default class Header extends TranslatedComponent {
|
|||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
|
||||||
openedMenu: null
|
|
||||||
};
|
|
||||||
|
|
||||||
this._close = this._close.bind(this);
|
|
||||||
this.shipOrder = Object.keys(Ships).sort();
|
this.shipOrder = Object.keys(Ships).sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
_close() {
|
|
||||||
this.setState({ openedMenu: null });
|
|
||||||
}
|
|
||||||
|
|
||||||
_setInsurance(e) {
|
_setInsurance(e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
Persist.setInsurance('Beta'); // TODO: get insurance name
|
Persist.setInsurance('beta'); // TODO: get insurance name
|
||||||
}
|
}
|
||||||
|
|
||||||
_setModuleDiscount(e) {
|
_setModuleDiscount(e) {
|
||||||
@@ -43,7 +34,6 @@ export default class Header extends TranslatedComponent {
|
|||||||
_showDeleteAll(e) {
|
_showDeleteAll(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
InterfaceEvents.showModal(<ModalDeleteAll />);
|
InterfaceEvents.showModal(<ModalDeleteAll />);
|
||||||
this._close();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_showBackup(e) {
|
_showBackup(e) {
|
||||||
@@ -76,21 +66,21 @@ export default class Header extends TranslatedComponent {
|
|||||||
Persist.setSizeRatio(1);
|
Persist.setSizeRatio(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
_openMenu(event, openedMenu) {
|
_openMenu(event, menu) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
if (this.state.openedMenu == openedMenu) {
|
if (this.props.currentMenu == menu) {
|
||||||
openedMenu = null;
|
menu = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ openedMenu });
|
InterfaceEvents.openMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getShipsMenu() {
|
_getShipsMenu() {
|
||||||
let shipList = [];
|
let shipList = [];
|
||||||
|
|
||||||
for (let s in Ships) {
|
for (let s in Ships) {
|
||||||
shipList.push(<ActiveLink key={s} href={'/outfitting/' + s} className={'block'}>{Ships[s].properties.name}</ActiveLink>);
|
shipList.push(<ActiveLink key={s} href={'/outfit/' + s} className={'block'}>{Ships[s].properties.name}</ActiveLink>);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -108,7 +98,7 @@ export default class Header extends TranslatedComponent {
|
|||||||
let shipBuilds = [];
|
let shipBuilds = [];
|
||||||
let buildNameOrder = Object.keys(builds[shipId]).sort();
|
let buildNameOrder = Object.keys(builds[shipId]).sort();
|
||||||
for (let buildName of buildNameOrder) {
|
for (let buildName of buildNameOrder) {
|
||||||
let href = ['/outfitting/', shipId, '/', builds[shipId][buildName], '?bn=', buildName].join('');
|
let href = ['/outfit/', shipId, '/', builds[shipId][buildName], '?bn=', buildName].join('');
|
||||||
shipBuilds.push(<li key={shipId + '-' + buildName} ><ActiveLink href={href} className={'block'}>{buildName}</ActiveLink></li>);
|
shipBuilds.push(<li key={shipId + '-' + buildName} ><ActiveLink href={href} className={'block'}>{buildName}</ActiveLink></li>);
|
||||||
}
|
}
|
||||||
buildList.push(<ul key={shipId}>{Ships[shipId].properties.name}{shipBuilds}</ul>);
|
buildList.push(<ul key={shipId}>{Ships[shipId].properties.name}{shipBuilds}</ul>);
|
||||||
@@ -196,21 +186,13 @@ export default class Header extends TranslatedComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
this.closeAllListener = InterfaceEvents.addListener('closeAll', this._close);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.closeAllListener.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let translate = this.context.language.translate;
|
let translate = this.context.language.translate;
|
||||||
let openedMenu = this.state.openedMenu;
|
let openedMenu = this.props.currentMenu;
|
||||||
let hasBuilds = Persist.hasBuilds();
|
let hasBuilds = Persist.hasBuilds();
|
||||||
|
|
||||||
if (this.props.appCacheUpdate) {
|
if (this.props.appCacheUpdate) {
|
||||||
return <div id="app-update" onClick={window.location.reload}>{ 'PHRASE_UPDATE_RDY' | translate }</div>;
|
return <div id="app-update" onClick={() => window.location.reload() }>{translate('PHRASE_UPDATE_RDY')}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Infinite } from './SvgIcons';
|
|||||||
|
|
||||||
export default class InternalSlot extends Slot {
|
export default class InternalSlot extends Slot {
|
||||||
|
|
||||||
getSlotDetails(m, translate, formats, u) {
|
_getSlotDetails(m, translate, formats, u) {
|
||||||
if (m) {
|
if (m) {
|
||||||
let classRating = m.class + m.rating;
|
let classRating = m.class + m.rating;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import cn from 'classnames';
|
||||||
import SlotSection from './SlotSection';
|
import SlotSection from './SlotSection';
|
||||||
import InternalSlot from './InternalSlot';
|
import InternalSlot from './InternalSlot';
|
||||||
import cn from 'classnames';
|
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||||
|
|
||||||
|
|
||||||
export default class InternalSlotSection extends SlotSection {
|
export default class InternalSlotSection extends SlotSection {
|
||||||
@@ -16,36 +17,70 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_empty() {
|
_empty() {
|
||||||
|
this.props.ship.emptyInternal();
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
_fillWithCargo() {
|
_fillWithCargo(event) {
|
||||||
|
let clobber = event.getModifierState('Alt');
|
||||||
|
let ship = this.props.ship;
|
||||||
|
ship.internal.forEach((slot) => {
|
||||||
|
if (clobber || !slot.m) {
|
||||||
|
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
_fillWithCells() {
|
_fillWithCells(event) {
|
||||||
|
let clobber = event.getModifierState('Alt');
|
||||||
|
let ship = this.props.ship;
|
||||||
|
let chargeCap = 0; // Capacity of single activation
|
||||||
|
ship.internal.forEach(function(slot) {
|
||||||
|
if ((!slot.m || (clobber && !ModuleUtils.isShieldGenerator(slot.m.grp))) && (!slot.eligible || slot.eligible.scb)) { // Check eligibility due to Orca special case
|
||||||
|
ship.use(slot, ModuleUtils.findInternal('scb', slot.maxClass, 'A'));
|
||||||
|
ship.setSlotEnabled(slot, chargeCap <= ship.shieldStrength); // Don't waste cell capacity on overcharge
|
||||||
|
chargeCap += slot.m.recharge;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
_fillWithArmor() {
|
_fillWithArmor(event) {
|
||||||
|
let clobber = event.getModifierState('Alt');
|
||||||
|
let ship = this.props.ship;
|
||||||
|
ship.internal.forEach((slot) => {
|
||||||
|
if (clobber || !slot.c) {
|
||||||
|
ship.use(slot, ModuleUtils.findInternal('hr', Math.min(slot.maxClass, 5), 'D')); // Hull reinforcements top out at 5D
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
|
}
|
||||||
|
|
||||||
|
_contextMenu() {
|
||||||
|
this._empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
_getSlots() {
|
_getSlots() {
|
||||||
let slots = [];
|
let slots = [];
|
||||||
let {internal, fuelCapacity, ladenMass } = this.props.ship;
|
let { currentMenu, ship } = this.props;
|
||||||
let availableModules = this.props.ship.getAvailableModules();
|
let {internal, fuelCapacity, ladenMass } = ship;
|
||||||
let currentMenu = this.state.currentMenu;
|
let availableModules = ship.getAvailableModules();
|
||||||
|
|
||||||
for (let i = 0, l = internal.length; i < l; i++) {
|
for (let i = 0, l = internal.length; i < l; i++) {
|
||||||
let s = internal[i];
|
let s = internal[i];
|
||||||
slots.push(<InternalSlot
|
slots.push(<InternalSlot
|
||||||
key={i}
|
key={i}
|
||||||
size={s.maxClass}
|
maxClass={s.maxClass}
|
||||||
modules={availableModules.getInts(s.maxClass, s.eligible)}
|
availableModules={() => availableModules.getInts(s.maxClass, s.eligible)}
|
||||||
onOpen={this._openMenu.bind(this,s)}
|
onOpen={this._openMenu.bind(this,s)}
|
||||||
onSelect={this._selectModule.bind(this, i, s)}
|
onSelect={this._selectModule.bind(this, s)}
|
||||||
selected={currentMenu == s}
|
selected={currentMenu == s}
|
||||||
|
enabled={s.enabled}
|
||||||
m={s.m}
|
m={s.m}
|
||||||
fuel={fuelCapacity}
|
fuel={fuelCapacity}
|
||||||
shipMass={ladenMass}
|
shipMass={ladenMass}
|
||||||
|
|||||||
51
src/app/components/LineChart.jsx
Normal file
51
src/app/components/LineChart.jsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
|
||||||
|
const RENDER_POINTS = 20; // Only render 20 points on the graph
|
||||||
|
|
||||||
|
export default class LineChart extends TranslatedComponent {
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
xMin: 0,
|
||||||
|
yMin: 0,
|
||||||
|
colors: ['#ff8c0d']
|
||||||
|
}
|
||||||
|
|
||||||
|
static PropTypes = {
|
||||||
|
xMax: React.PropTypes.number.isRequired,
|
||||||
|
yMax: React.PropTypes.number.isRequired,
|
||||||
|
func: React.PropTypes.func.isRequired,
|
||||||
|
series: React.PropTypes.array,
|
||||||
|
colors: React.PropTypes.array,
|
||||||
|
xMin: React.PropTypes.number,
|
||||||
|
yMin: React.PropTypes.number,
|
||||||
|
xUnit: React.PropTypes.string,
|
||||||
|
yUnit: React.PropTypes.string,
|
||||||
|
xLabel: React.PropTypes.string,
|
||||||
|
xLabel: React.PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
// init
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount(){
|
||||||
|
// Listen to window resize
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount(){
|
||||||
|
// remove window listener
|
||||||
|
// remove mouse move listener / touch listner?
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps, nextContext) {
|
||||||
|
// on language change update formatting
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <div></div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,12 +2,12 @@ import React from 'react';
|
|||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import InterfaceEvents from '../utils/InterfaceEvents';
|
import InterfaceEvents from '../utils/InterfaceEvents';
|
||||||
|
|
||||||
export default class DeleteAllModal extends TranslatedComponent {
|
export default class ModalExport extends TranslatedComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
title: React.propTypes.string,
|
title: React.PropTypes.string,
|
||||||
promise: : React.propTypes.func,
|
promise: React.PropTypes.func,
|
||||||
data: React.propTypes.oneOfType[React.propTypes.string, React.propTypes.object]
|
data: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.object])
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -19,7 +19,7 @@ export default class DeleteAllModal extends TranslatedComponent {
|
|||||||
} else if(typeof props.data == 'string') {
|
} else if(typeof props.data == 'string') {
|
||||||
exportJson = props.data;
|
exportJson = props.data;
|
||||||
} else {
|
} else {
|
||||||
exportJson = JSON.stringify(this.props.data);
|
exportJson = JSON.stringify(this.props.data, null, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state = { exportJson };
|
this.state = { exportJson };
|
||||||
@@ -41,7 +41,7 @@ export default class DeleteAllModal extends TranslatedComponent {
|
|||||||
<h2>{translate(this.props.title || 'Export')}</h2>
|
<h2>{translate(this.props.title || 'Export')}</h2>
|
||||||
{description}
|
{description}
|
||||||
<div>
|
<div>
|
||||||
<textarea className='cb json' onFocus={ (e) => e.target.select() }>{this.state.exportJson}</textarea>
|
<textarea className='cb json' onFocus={ (e) => e.target.select() } readOnly value={this.state.exportJson} />
|
||||||
</div>
|
</div>
|
||||||
<button className={'r dismiss cap'} onClick={InterfaceEvents.hideModal}>{translate('close')}</button>
|
<button className={'r dismiss cap'} onClick={InterfaceEvents.hideModal}>{translate('close')}</button>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
title: React.propTypes.string,
|
title: React.propTypes.string,
|
||||||
promise: : React.propTypes.func,
|
promise: React.propTypes.func,
|
||||||
data: React.propTypes.oneOfType[React.propTypes.string, React.propTypes.object]
|
data: React.propTypes.oneOfType[React.propTypes.string, React.propTypes.object]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -322,7 +322,7 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.discounts) {
|
if (this.state.discounts) {
|
||||||
Persist.setDiscount((this.state.discounts);
|
Persist.setDiscount(this.state.discounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.insurance) {
|
if (this.state.insurance) {
|
||||||
@@ -369,6 +369,7 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
comparisonTable = (
|
comparisonTable = (
|
||||||
<table className='l' style={{ overflow:'hidden', margin: '1em 0', width: '100%'}} >
|
<table className='l' style={{ overflow:'hidden', margin: '1em 0', width: '100%'}} >
|
||||||
@@ -397,7 +398,7 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
let hasBuild = Persist.hasBuild(shipId, b.useName);
|
let hasBuild = Persist.hasBuild(shipId, b.useName);
|
||||||
buildRows.push(
|
buildRows.push(
|
||||||
<tr className='cb'>
|
<tr className='cb'>
|
||||||
<td>{{Ships[shipId].properties.name}}</td>
|
<td>{Ships[shipId].properties.name}</td>
|
||||||
<td><input type='text' value={b.useName}/></td>
|
<td><input type='text' value={b.useName}/></td>
|
||||||
<td style={{ textAlign: 'center' }} className={cn({ warning: hasBuild, disabled: b.useName == ''})}>
|
<td style={{ textAlign: 'center' }} className={cn({ warning: hasBuild, disabled: b.useName == ''})}>
|
||||||
<span>{translate(b.useName == '' ? 'skip' : (hasBuild ? 'overwrite' : 'create'))}></span>
|
<span>{translate(b.useName == '' ? 'skip' : (hasBuild ? 'overwrite' : 'create'))}></span>
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ export default class ModalPermalink extends TranslatedComponent {
|
|||||||
<h2>{translate('permalink')}</h2>
|
<h2>{translate('permalink')}</h2>
|
||||||
<br/>
|
<br/>
|
||||||
<h3>{translate('URL')}</h3>
|
<h3>{translate('URL')}</h3>
|
||||||
<input value={this.props.url} size=40 onFocus={ (e) => e.target.select() }/>
|
<input value={this.props.url} size={40} onFocus={ (e) => e.target.select() }/>
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
<h3 >{translate('shortened')}</h3>
|
<h3 >{translate('shortened')}</h3>
|
||||||
<input value={this.state.shortenedUrl} size=25 onFocus={ (e) => e.target.select() }/>
|
<input value={this.state.shortenedUrl} size={25} onFocus={ (e) => e.target.select() }/>
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
<button className={'r dismiss cap'} onClick={InterfaceEvents.hideModal}>{translate('close')}</button>
|
<button className={'r dismiss cap'} onClick={InterfaceEvents.hideModal}>{translate('close')}</button>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
244
src/app/components/PowerBands.jsx
Normal file
244
src/app/components/PowerBands.jsx
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { findDOMNode } from 'react-dom';
|
||||||
|
import d3 from 'd3';
|
||||||
|
import cn from 'classnames';
|
||||||
|
import Persist from '../stores/Persist';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
import InterfaceEvents from '../utils/InterfaceEvents';
|
||||||
|
import { wrapCtxMenu } from '../utils/InterfaceEvents';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Round to avoid floating point precision errors
|
||||||
|
* @param {[type]} selected [description]
|
||||||
|
* @param {[type]} sum [description]
|
||||||
|
* @param {[type]} avail [description]
|
||||||
|
* @return {[type]} [description]
|
||||||
|
*/
|
||||||
|
function getClass(selected, sum, avail) {
|
||||||
|
return selected ? 'secondary' : ((Math.round(sum * 100) / 100) >= avail) ? 'warning' : 'primary';
|
||||||
|
}
|
||||||
|
|
||||||
|
function bandText(val, index, wattScale) {
|
||||||
|
return (val > 0 && wattScale(val) > 13) ? index + 1 : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class PowerBands extends TranslatedComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
bands: React.PropTypes.array.isRequired,
|
||||||
|
available: React.PropTypes.number.isRequired,
|
||||||
|
code: React.PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props);
|
||||||
|
this.wattScale = d3.scale.linear();
|
||||||
|
this.pctScale = d3.scale.linear().domain([0, 1]);
|
||||||
|
this.wattAxis = d3.svg.axis().scale(this.wattScale).outerTickSize(0).orient('top').tickFormat(context.language.formats.r2);
|
||||||
|
this.pctAxis = d3.svg.axis().scale(this.pctScale).outerTickSize(0).orient('bottom').tickFormat(context.language.formats.rPct);
|
||||||
|
|
||||||
|
this._updateWidth = this._updateWidth.bind(this);
|
||||||
|
this._updateAxes = this._updateAxes.bind(this);
|
||||||
|
this._selectNone = this._selectNone.bind(this);
|
||||||
|
|
||||||
|
let maxBand = props.bands[props.bands.length - 1];
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
outerWidth: 0,
|
||||||
|
innerWidth: 0,
|
||||||
|
sizes: this._initSizes(Persist.getSizeRatio()),
|
||||||
|
maxPwr: Math.max(props.available, maxBand.retractedSum, maxBand.deployedSum),
|
||||||
|
ret: {},
|
||||||
|
dep: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_initSizes(size) {
|
||||||
|
let barHeight = Math.round(20 * size);
|
||||||
|
let innerHeight = (barHeight * 2) + 2;
|
||||||
|
let mTop = Math.round(25 * size);
|
||||||
|
let mBottom = Math.round(25 * size);
|
||||||
|
|
||||||
|
return {
|
||||||
|
barHeight,
|
||||||
|
innerHeight,
|
||||||
|
mTop,
|
||||||
|
mBottom,
|
||||||
|
mLeft: Math.round(45 * size),
|
||||||
|
mRight: Math.round(130 * size),
|
||||||
|
height: innerHeight + mTop + mBottom,
|
||||||
|
retY: (barHeight / 2),
|
||||||
|
depY: (barHeight * 1.5) - 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_selectNone() {
|
||||||
|
this.setState({
|
||||||
|
ret : {},
|
||||||
|
dep: {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_selectRet(index) {
|
||||||
|
let ret = this.state.ret;
|
||||||
|
if(ret[index]) {
|
||||||
|
delete ret[index];
|
||||||
|
} else {
|
||||||
|
ret[index] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ ret: Object.assign({}, ret) });
|
||||||
|
}
|
||||||
|
|
||||||
|
_selectDep(index) {
|
||||||
|
let dep = this.state.dep;
|
||||||
|
|
||||||
|
if(dep[index]) {
|
||||||
|
delete dep[index];
|
||||||
|
} else {
|
||||||
|
dep[index] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ dep: Object.assign({}, dep) });
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateAxes(innerWidth, maxPwr, available) {
|
||||||
|
this.wattScale.range([0, innerWidth]).domain([0,maxPwr]).clamp(true);
|
||||||
|
this.pctScale.range([0, innerWidth]).domain([0, maxPwr / available]).clamp(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateWidth() {
|
||||||
|
let outerWidth = findDOMNode(this).offsetWidth;
|
||||||
|
let innerWidth = outerWidth - this.state.sizes.mLeft - this.state.sizes.mRight;
|
||||||
|
this._updateAxes(innerWidth, this.state.maxPwr, this.props.available);
|
||||||
|
this.setState({ outerWidth, innerWidth });
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount(){
|
||||||
|
this.resizeListener = InterfaceEvents.addListener('windowResized', this._updateWidth);
|
||||||
|
this.sizeListener = Persist.addListener('sizeRatio', (sizeRatio) => this.setState({ sizes: this._initSizes(sizeRatio) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this._updateWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps, nextContext) {
|
||||||
|
let { innerWidth, maxPwr } = this.state;
|
||||||
|
let maxBand = nextProps.bands[nextProps.bands.length - 1];
|
||||||
|
let nextMaxPwr = Math.max(nextProps.available, maxBand.retractedSum, maxBand.deployedSum);
|
||||||
|
|
||||||
|
if (maxPwr != nextMaxPwr) { // Update Axes if max power has changed
|
||||||
|
this._updateAxes(innerWidth, nextMaxPwr, nextProps.available);
|
||||||
|
this.setState({ maxPwr: nextMaxPwr });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.context !== nextContext) {
|
||||||
|
this.wattAxis.tickFormat(nextContext.language.formats.r2);
|
||||||
|
this.pctAxis.tickFormat(nextContext.language.formats.rPct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount(){
|
||||||
|
this.resizeListener.remove();
|
||||||
|
this.sizeListener.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { wattScale, pctScale, context, props, state } = this;
|
||||||
|
let { translate, formats } = context.language;
|
||||||
|
let { f2, pct1, rPct, r2 } = formats; // wattFmt, pctFmt, pctAxis, wattAxis
|
||||||
|
let { available, bands } = props;
|
||||||
|
let { sizes, outerWidth, innerWidth, maxPwr, ret, dep } = state;
|
||||||
|
let pwrWarningClass = cn('threshold', {exceeded: bands[0].retractedSum * 2 >= available });
|
||||||
|
let deployed = [];
|
||||||
|
let retracted = [];
|
||||||
|
let retSelected = Object.keys(ret).length > 0;
|
||||||
|
let depSelected = Object.keys(dep).length > 0;
|
||||||
|
let retSum = 0;
|
||||||
|
let depSum = 0;
|
||||||
|
|
||||||
|
if (outerWidth > 0) {
|
||||||
|
for (var i = 0; i < bands.length; i++) {
|
||||||
|
let b = bands[i];
|
||||||
|
retSum += (!retSelected || ret[i]) ? b.retracted : 0;
|
||||||
|
depSum += (!depSelected || dep[i]) ? b.deployed + b.retracted : 0;
|
||||||
|
|
||||||
|
if (outerWidth && b.retracted > 0) {
|
||||||
|
let retLbl = bandText(b.retracted, i, wattScale);
|
||||||
|
|
||||||
|
retracted.push(<rect
|
||||||
|
key={'rB' + i}
|
||||||
|
width={Math.ceil(Math.max(wattScale(b.retracted), 0))}
|
||||||
|
height={sizes.barHeight}
|
||||||
|
x={Math.floor(Math.max(wattScale(b.retractedSum) - wattScale(b.retracted), 0))}
|
||||||
|
y={1}
|
||||||
|
onClick={this._selectRet.bind(this, i)}
|
||||||
|
className={getClass(ret[i], b.retractedSum, available)}
|
||||||
|
/>);
|
||||||
|
|
||||||
|
if (retLbl) {
|
||||||
|
retracted.push(<text
|
||||||
|
key={'rT' + i}
|
||||||
|
dy='0.5em'
|
||||||
|
textAnchor='middle'
|
||||||
|
height={sizes.barHeight}
|
||||||
|
x={wattScale(b.retractedSum) - (wattScale(b.retracted) / 2)}
|
||||||
|
y={sizes.retY}
|
||||||
|
onClick={this._selectRet.bind(this, i)}
|
||||||
|
className='primary-bg'>{retLbl}</text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outerWidth && (b.retracted > 0 || b.deployed > 0)) {
|
||||||
|
let depLbl = bandText(b.deployed + b.retracted, i, wattScale);
|
||||||
|
|
||||||
|
deployed.push(<rect
|
||||||
|
key={'dB' + i}
|
||||||
|
width={Math.ceil(Math.max(wattScale(b.deployed + b.retracted), 0))}
|
||||||
|
height={sizes.barHeight}
|
||||||
|
x={Math.floor(Math.max(wattScale(b.deployedSum) - wattScale(b.retracted) - wattScale(b.deployed), 0))}
|
||||||
|
y={sizes.barHeight + 1}
|
||||||
|
onClick={this._selectDep.bind(this, i)}
|
||||||
|
className={getClass(dep[i], b.deployedSum, available)}
|
||||||
|
/>);
|
||||||
|
|
||||||
|
if (depLbl) {
|
||||||
|
deployed.push(<text
|
||||||
|
key={'dT' + i}
|
||||||
|
dy='0.5em'
|
||||||
|
textAnchor='middle'
|
||||||
|
height={sizes.barHeight}
|
||||||
|
x={wattScale(b.deployedSum) - ((wattScale(b.retracted) + wattScale(b.deployed)) / 2)}
|
||||||
|
y={sizes.depY}
|
||||||
|
onClick={this._selectDep.bind(this, i)}
|
||||||
|
className='primary-bg'>{depLbl}</text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg style={{ marginTop: '1em', width: '100%', height: sizes.height }} onContextMenu={wrapCtxMenu(this._selectNone)}>
|
||||||
|
<g transform={`translate(${sizes.mLeft},${sizes.mTop})`}>
|
||||||
|
<g className='power-band'>{retracted}</g>
|
||||||
|
<g className='power-band'>{deployed}</g>
|
||||||
|
<g ref={ (elem) => d3.select(elem).call(this.wattAxis) } className='watt axis'></g>
|
||||||
|
<g ref={ (elem) => {
|
||||||
|
let axis = d3.select(elem);
|
||||||
|
axis.call(this.pctAxis);
|
||||||
|
axis.select('g:nth-child(6)').selectAll('line, text').attr('class', pwrWarningClass);
|
||||||
|
}}
|
||||||
|
className='pct axis' transform={`translate(0,${sizes.innerHeight})`}></g>
|
||||||
|
<line x1={pctScale(0.5)} x2={pctScale(0.5)} y1='0' y2={sizes.innerHeight} className={pwrWarningClass} />
|
||||||
|
<text dy='0.5em' x='-3' y={sizes.retY} className='primary upp' textAnchor='end'>{translate('ret')}</text>
|
||||||
|
<text dy='0.5em' x='-3' y={sizes.depY} className='primary upp' textAnchor='end'>{translate('dep')}</text>
|
||||||
|
<text dy='0.5em' x={innerWidth + 5} y={sizes.retY} className={getClass(retSelected, retSum, available)}>{f2(Math.max(0, retSum)) + ' (' + pct1(Math.max(0, retSum / available)) + ')'}</text>
|
||||||
|
<text dy='0.5em' x={innerWidth + 5} y={sizes.depY} className={getClass(depSelected, depSum, available)}>{f2(Math.max(0, depSum)) + ' (' + pct1(Math.max(0, depSum / available)) + ')'}</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
179
src/app/components/PowerManagement.jsx
Normal file
179
src/app/components/PowerManagement.jsx
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import cn from 'classnames';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
import PowerBands from './PowerBands';
|
||||||
|
import { slotName, nameComparator } from '../utils/SlotFunctions';
|
||||||
|
import { Power, NoPower } from './SvgIcons';
|
||||||
|
|
||||||
|
const POWER = [
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
<NoPower className='icon warning' />,
|
||||||
|
<Power className='secondary-disabled' />
|
||||||
|
];
|
||||||
|
|
||||||
|
export default class PowerManagement extends TranslatedComponent {
|
||||||
|
static PropTypes = {
|
||||||
|
ship: React.PropTypes.object.isRequired,
|
||||||
|
code: React.PropTypes.string.isRequired,
|
||||||
|
onChange: React.PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this._renderPowerRows = this._renderPowerRows.bind(this);
|
||||||
|
this._sortName = this._sortName.bind(this);
|
||||||
|
this._sortType = this._sortType.bind(this);
|
||||||
|
this._sortPriority = this._sortPriority.bind(this);
|
||||||
|
this._sortPower = this._sortPower.bind(this);
|
||||||
|
this._sortRetracted = this._sortRetracted.bind(this);
|
||||||
|
this._sortDeployed = this._sortDeployed.bind(this);
|
||||||
|
|
||||||
|
this.state = {}; // State is initialized through componentWillMount
|
||||||
|
}
|
||||||
|
|
||||||
|
_sortOrder(predicate) {
|
||||||
|
let desc = this.state.desc;
|
||||||
|
if (predicate == this.state.predicate) {
|
||||||
|
desc = !desc;
|
||||||
|
} else {
|
||||||
|
desc = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!desc) {
|
||||||
|
this.props.ship.powerList.reverse();
|
||||||
|
}
|
||||||
|
this.setState({ predicate, desc });
|
||||||
|
}
|
||||||
|
|
||||||
|
_sortName() {
|
||||||
|
let translate = this.context.language.translate;
|
||||||
|
this.props.ship.powerList.sort(nameComparator(translate));
|
||||||
|
this._sortOrder('n');
|
||||||
|
}
|
||||||
|
|
||||||
|
_sortType() {
|
||||||
|
this.props.ship.powerList.sort((a, b) => a.type.localeCompare(b.type));
|
||||||
|
this._sortOrder('t');
|
||||||
|
}
|
||||||
|
|
||||||
|
_sortPriority() {
|
||||||
|
this.props.ship.powerList.sort((a, b) => a.priority - b.priority);
|
||||||
|
this._sortOrder('pri');
|
||||||
|
}
|
||||||
|
|
||||||
|
_sortPower() {
|
||||||
|
this.props.ship.powerList.sort((a, b) => (a.m ? a.m.power : 0) - (b.m ? b.m.power : 0));
|
||||||
|
this._sortOrder('pwr');
|
||||||
|
}
|
||||||
|
|
||||||
|
_sortRetracted() {
|
||||||
|
let ship = this.props.ship;
|
||||||
|
this.props.ship.powerList.sort((a, b) => ship.getSlotStatus(a) - ship.getSlotStatus(b));
|
||||||
|
this._sortOrder('ret');
|
||||||
|
}
|
||||||
|
|
||||||
|
_sortDeployed() {
|
||||||
|
let ship = this.props.ship;
|
||||||
|
this.props.ship.powerList.sort((a, b) => ship.getSlotStatus(a, true) - ship.getSlotStatus(b, true));
|
||||||
|
this._sortOrder('dep');
|
||||||
|
}
|
||||||
|
|
||||||
|
_priority(slot, inc) {
|
||||||
|
if (this.props.ship.setSlotPriority(slot, slot.priority + inc)) {
|
||||||
|
this.props.onChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_toggleEnabled(slot) {
|
||||||
|
this.props.ship.setSlotEnabled(slot, !slot.enabled);
|
||||||
|
this.props.onChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderPowerRows(ship, translate, pwr, pct) {
|
||||||
|
|
||||||
|
let powerRows = [];
|
||||||
|
|
||||||
|
for (var i = 0, l = ship.powerList.length; i < l; i++) {
|
||||||
|
let slot = ship.powerList[i];
|
||||||
|
|
||||||
|
if (slot.m && slot.m.power) {
|
||||||
|
let m = slot.m;
|
||||||
|
let toggleEnabled = this._toggleEnabled.bind(this, slot);
|
||||||
|
let retractedElem = null, deployedElem = null;
|
||||||
|
|
||||||
|
if (slot.enabled) {
|
||||||
|
retractedElem = <td className='ptr upp' onClick={toggleEnabled}>{POWER[ship.getSlotStatus(slot, false)]}</td>;
|
||||||
|
deployedElem = <td className='ptr upp' onClick={toggleEnabled}>{POWER[ship.getSlotStatus(slot, true)]}</td>;
|
||||||
|
} else {
|
||||||
|
retractedElem = <td className='ptr disabled upp' colSpan='2' onClick={toggleEnabled}>{translate('disabled')}</td>;
|
||||||
|
}
|
||||||
|
|
||||||
|
powerRows.push(<tr key={i} className={cn('highlight', { disabled: !slot.enabled })}>
|
||||||
|
<td className='ptr' style={{ width: '1em' }} onClick={toggleEnabled}>{m.class + m.rating}</td>
|
||||||
|
<td className='ptr le shorten cap' onClick={toggleEnabled}>{slotName(translate, slot)}</td>
|
||||||
|
<td className='ptr' onClick={toggleEnabled}><u>{translate(slot.type)}</u></td>
|
||||||
|
<td>
|
||||||
|
<span className='flip ptr' onClick={this._priority.bind(this, slot, -1)}>►</span>
|
||||||
|
{' ' + (slot.priority + 1) + ' '}
|
||||||
|
<span className='ptr' onClick={this._priority.bind(this, slot, 1)}>►</span>
|
||||||
|
</td>
|
||||||
|
<td className='ri ptr' style={{ width: '3.25em'}} onClick={toggleEnabled}>{pwr(m.power)}</td>
|
||||||
|
<td className='ri ptr' style={{ width: '3em' }} onClick={toggleEnabled}><u>{pct(m.power / ship.powerAvailable)}</u></td>
|
||||||
|
{retractedElem}
|
||||||
|
{deployedElem}
|
||||||
|
</tr>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return powerRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount(){
|
||||||
|
this._sortName();
|
||||||
|
// Listen to window resize
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount(){
|
||||||
|
// remove window listener
|
||||||
|
// remove mouse move listener / touch listner?
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { ship, code } = this.props;
|
||||||
|
let { translate, formats } = this.context.language;
|
||||||
|
let pwr = formats.f2;
|
||||||
|
let pp = ship.standard[0].m;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='group half' id='componentPriority'>
|
||||||
|
<table style={{ width: '100%' }}>
|
||||||
|
<thead>
|
||||||
|
<tr className='main'>
|
||||||
|
<th colSpan='2' className='sortable le' onClick={this._sortName} >{translate('module')}</th>
|
||||||
|
<th style={{ width: '3em' }} className='sortable' onClick={this._sortType} >{translate('type')}</th>
|
||||||
|
<th style={{ width: '4em' }} className='sortable' onClick={this._sortPriority} >{translate('pri')}</th>
|
||||||
|
<th colSpan='2' className='sortable' onClick={this._sortPower} >{translate('PWR')}</th>
|
||||||
|
<th style={{ width: '3em' }} className='sortable' onClick={this._sortRetracted} >{translate('ret')}</th>
|
||||||
|
<th style={{ width: '3em' }} className='sortable' onClick={this._sortDeployed} >{translate('dep')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>{pp.class + pp.rating}</td>
|
||||||
|
<td className='le shorten cap' >{translate('pp')}</td>
|
||||||
|
<td><u >{translate('SYS')}</u></td>
|
||||||
|
<td>1</td>
|
||||||
|
<td className='ri'>{pwr(pp.pGen)}</td>
|
||||||
|
<td className='ri'><u>100%</u></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr><td style={{ lineHeight:0 }} colSpan='8'><hr style={{ margin: '0 0 3px', background: '#ff8c0d', border: 0, height: 1 }} /></td></tr>
|
||||||
|
{this._renderPowerRows(ship, translate, pwr, formats.pct1)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<PowerBands code={code} available={ship.standard[0].m.pGen} bands={ship.priorityBands} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,10 +11,6 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
ship: React.PropTypes.object.isRequired
|
ship: React.PropTypes.object.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
let language = this.context.language;
|
let language = this.context.language;
|
||||||
|
|||||||
@@ -2,27 +2,46 @@ import React from 'react';
|
|||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import AvailableModulesMenu from './AvailableModulesMenu';
|
import AvailableModulesMenu from './AvailableModulesMenu';
|
||||||
import { contextMenuHandler } from '../utils/InterfaceEvents';
|
import { wrapCtxMenu } from '../utils/InterfaceEvents';
|
||||||
|
|
||||||
export default class Slot extends TranslatedComponent {
|
export default class Slot extends TranslatedComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
modules: React.PropTypes.oneOfType([ React.PropTypes.object, React.PropTypes.array ]).isRequired,
|
availableModules: React.PropTypes.func.isRequired,
|
||||||
onSelect: React.PropTypes.func.isRequired,
|
onSelect: React.PropTypes.func.isRequired,
|
||||||
onOpen: React.PropTypes.func.isRequired,
|
onOpen: React.PropTypes.func.isRequired,
|
||||||
size: React.PropTypes.number.isRequired,
|
maxClass: React.PropTypes.number.isRequired,
|
||||||
selected: React.PropTypes.bool,
|
selected: React.PropTypes.bool,
|
||||||
m: React.PropTypes.object,
|
m: React.PropTypes.object,
|
||||||
shipMass: React.PropTypes.number,
|
shipMass: React.PropTypes.number,
|
||||||
warning: React.PropTypes.func,
|
warning: React.PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
getClassNames() {
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this._contextMenu = wrapCtxMenu(this._contextMenu.bind(this));
|
||||||
|
this._getMaxClassLabel = this._getMaxClassLabel.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be implemented by subclasses:
|
||||||
|
// _getSlotDetails()
|
||||||
|
|
||||||
|
_getClassNames() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSize() {
|
/**
|
||||||
return this.props.size;
|
* Get the label for the slot size/class
|
||||||
|
* Should be overriden if necessary
|
||||||
|
* @return {string} label
|
||||||
|
*/
|
||||||
|
_getMaxClassLabel() {
|
||||||
|
return this.props.maxClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
_contextMenu(event) {
|
||||||
|
this.props.onSelect(null,null);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -32,15 +51,15 @@ export default class Slot extends TranslatedComponent {
|
|||||||
let slotDetails, menu;
|
let slotDetails, menu;
|
||||||
|
|
||||||
if (m) {
|
if (m) {
|
||||||
slotDetails = this.getSlotDetails(m, translate, language.formats, language.units); // Must be implemented by sub classes
|
slotDetails = this._getSlotDetails(m, translate, language.formats, language.units); // Must be implemented by sub classes
|
||||||
} else {
|
} else {
|
||||||
slotDetails = <div className={'empty'}>{translate('empty')}</div>;
|
slotDetails = <div className={'empty'}>{translate('empty')}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.selected) {
|
if (this.props.selected) {
|
||||||
menu = <AvailableModulesMenu
|
menu = <AvailableModulesMenu
|
||||||
className={this.getClassNames()}
|
className={this._getClassNames()}
|
||||||
modules={this.props.modules}
|
modules={this.props.availableModules()}
|
||||||
shipMass={this.props.shipMass}
|
shipMass={this.props.shipMass}
|
||||||
m={m}
|
m={m}
|
||||||
onSelect={this.props.onSelect}
|
onSelect={this.props.onSelect}
|
||||||
@@ -49,9 +68,9 @@ export default class Slot extends TranslatedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn('slot', {selected: this.props.selected})} onClick={this.props.onOpen} onContextMenu={ this.contextmenu === false ? null : contextMenuHandler(this.props.onSelect.bind(null, null))}>
|
<div className={cn('slot', {selected: this.props.selected})} onClick={this.props.onOpen} onContextMenu={this._contextMenu}>
|
||||||
<div className={'details'}>
|
<div className={'details'}>
|
||||||
<div className={'sz'}>{this.getSize(translate)}</div>
|
<div className={'sz'}>{this._getMaxClassLabel(translate)}</div>
|
||||||
{slotDetails}
|
{slotDetails}
|
||||||
</div>
|
</div>
|
||||||
{menu}
|
{menu}
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import InterfaceEvents from '../utils/InterfaceEvents';
|
import InterfaceEvents from '../utils/InterfaceEvents';
|
||||||
|
import { wrapCtxMenu } from '../utils/InterfaceEvents';
|
||||||
import { Equalizer } from '../components/SvgIcons';
|
import { Equalizer } from '../components/SvgIcons';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
|
|
||||||
export default class SlotSection extends TranslatedComponent {
|
export default class SlotSection extends TranslatedComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
ship: React.PropTypes.object.isRequired
|
ship: React.PropTypes.object.isRequired,
|
||||||
|
onChange: React.PropTypes.func.isRequired,
|
||||||
|
code: React.PropTypes.string.isRequired,
|
||||||
|
togglePwr: React.PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props, context, sectionId, sectionName) {
|
constructor(props, context, sectionId, sectionName) {
|
||||||
@@ -18,51 +22,51 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
this._getSlots = this._getSlots.bind(this);
|
this._getSlots = this._getSlots.bind(this);
|
||||||
this._selectModule = this._selectModule.bind(this);
|
this._selectModule = this._selectModule.bind(this);
|
||||||
this._getSectionMenu = this._getSectionMenu.bind(this);
|
this._getSectionMenu = this._getSectionMenu.bind(this);
|
||||||
|
this._contextMenu = this._contextMenu.bind(this);
|
||||||
this.state = {
|
this._close = this._close.bind(this);
|
||||||
currentMenu: null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be implemented by subclasses:
|
// Must be implemented by subclasses:
|
||||||
// _getSlots()
|
// _getSlots()
|
||||||
// _getSectionMenu()
|
// _getSectionMenu()
|
||||||
|
// _contextMenu()
|
||||||
|
|
||||||
_openMenu(menu) {
|
_openMenu(menu, event) {
|
||||||
this.setState({ currentMenu: menu });
|
event.stopPropagation();
|
||||||
InterfaceEvents.closeAll(menu);
|
if (this.props.currentMenu === menu) {
|
||||||
}
|
menu = null;
|
||||||
|
|
||||||
_closeMenu() {
|
|
||||||
if (this.state.currentMenu) {
|
|
||||||
this.setState({ currentMenu: null });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InterfaceEvents.openMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
_selectModule(index, slot, m) {
|
_selectModule(slot, m) {
|
||||||
this.props.ship.use(slot, m);
|
this.props.ship.use(slot, m);
|
||||||
this._closeMenu();
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps, nextContext) {
|
_togglePwr(slot) {
|
||||||
this.setState({ currentMenu: null });
|
this.props.ship.setSlotEnabled(slot, !slot.enabled);
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
_close() {
|
||||||
this.closeAllListener = InterfaceEvents.addListener('closeAll', this._closeMenu.bind(this));
|
if (this.props.currentMenu) {
|
||||||
}
|
InterfaceEvents.closeMenu();
|
||||||
|
}
|
||||||
componentWillUnmount() {
|
|
||||||
this.closeAllListener.remove();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let translate = this.context.language.translate;
|
let translate = this.context.language.translate;
|
||||||
let sectionMenuOpened = this.state.currentMenu === this.sectionName;
|
let sectionMenuOpened = this.props.currentMenu === this.sectionName;
|
||||||
|
let open = this._openMenu.bind(this, this.sectionName);
|
||||||
|
let ctx = wrapCtxMenu(this._contextMenu);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id={this.sectionId} className={'group'}>
|
<div id={this.sectionId} className={'group'}>
|
||||||
<div className={cn('section-menu', {selected: sectionMenuOpened})} onClick={this._openMenu.bind(this, this.sectionName)}>
|
<div className={cn('section-menu', {selected: sectionMenuOpened})} onClick={open} onContextMenu={ctx}>
|
||||||
<h1>{translate(this.sectionName)} <Equalizer/></h1>
|
<h1>{translate(this.sectionName)} <Equalizer/></h1>
|
||||||
{sectionMenuOpened ? this._getSectionMenu(translate) : null }
|
{sectionMenuOpened ? this._getSectionMenu(translate) : null }
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export default class StandardSlot extends TranslatedComponent {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
slot: React.PropTypes.object,
|
slot: React.PropTypes.object,
|
||||||
modules: React.PropTypes.oneOfType([ React.PropTypes.object, React.PropTypes.array ]).isRequired,
|
modules: React.PropTypes.array.isRequired,
|
||||||
onSelect: React.PropTypes.func.isRequired,
|
onSelect: React.PropTypes.func.isRequired,
|
||||||
onOpen: React.PropTypes.func.isRequired,
|
onOpen: React.PropTypes.func.isRequired,
|
||||||
selected: React.PropTypes.bool,
|
selected: React.PropTypes.bool,
|
||||||
@@ -17,7 +17,7 @@ export default class StandardSlot extends TranslatedComponent {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { translate, formats, units } = this.context.language;
|
let { translate, formats, units } = this.context.language;
|
||||||
let slot = this.props.slot
|
let { slot, warning } = this.props;
|
||||||
let m = slot.m;
|
let m = slot.m;
|
||||||
let classRating = m.class + m.rating;
|
let classRating = m.class + m.rating;
|
||||||
let menu;
|
let menu;
|
||||||
@@ -34,7 +34,7 @@ export default class StandardSlot extends TranslatedComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn('slot', {selected: this.props.selected})} onClick={this.props.onOpen}>
|
<div className={cn('slot', {selected: this.props.selected})} onClick={this.props.onOpen}>
|
||||||
<div className={'details'}>
|
<div className={cn('details', {warning: warning && warning(slot.m)})}>
|
||||||
<div className={'sz'}>{slot.maxClass}</div>
|
<div className={'sz'}>{slot.maxClass}</div>
|
||||||
<div>
|
<div>
|
||||||
<div className={'l'}>{classRating + ' ' + translate(m.grp)}</div>
|
<div className={'l'}>{classRating + ' ' + translate(m.grp)}</div>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import cn from 'classnames';
|
||||||
import SlotSection from './SlotSection';
|
import SlotSection from './SlotSection';
|
||||||
import StandardSlot from './StandardSlot';
|
import StandardSlot from './StandardSlot';
|
||||||
import cn from 'classnames';
|
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||||
import { ArmourMultiplier } from '../shipyard/Constants';
|
|
||||||
|
|
||||||
export default class StandardSlotSection extends SlotSection {
|
export default class StandardSlotSection extends SlotSection {
|
||||||
|
|
||||||
@@ -15,24 +15,97 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fill(rating) {
|
_fill(rating) {
|
||||||
|
this.props.ship.useStandard(rating);
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
_optimizeStandard() {
|
_optimizeStandard() {
|
||||||
|
this.props.ship.useLightestStandard();
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
_optimizeCargo() {
|
_optimizeCargo() {
|
||||||
|
let ship = this.props.ship;
|
||||||
|
ship.internal.forEach((slot) => {
|
||||||
|
var id = ModuleUtils.findInternalId('cr', slot.maxClass, 'E');
|
||||||
|
ship.use(slot, ModuleUtils.internal(id));
|
||||||
|
});
|
||||||
|
ship.useLightestStandard();
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
_optimizeExplorer() {
|
_optimizeExplorer() {
|
||||||
|
let ship = this.props.ship,
|
||||||
|
intLength = ship.internal.length,
|
||||||
|
heatSinkCount = 2, // Fit 2 heat sinks if possible
|
||||||
|
afmUnitCount = 2, // Fit 2 AFM Units if possible
|
||||||
|
sgSlot,
|
||||||
|
fuelScoopSlot,
|
||||||
|
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
||||||
|
|
||||||
|
ship.setSlotEnabled(ship.cargoHatch, false)
|
||||||
|
.use(ship.internal[--intLength], ModuleUtils.internal('2f')) // Advanced Discovery Scanner
|
||||||
|
.use(ship.internal[--intLength], ModuleUtils.internal('2i')); // Detailed Surface Scanner
|
||||||
|
|
||||||
|
for (let i = 0; i < intLength; i++) {
|
||||||
|
let slot = ship.internal[i];
|
||||||
|
let nextSlot = (i + 1) < intLength ? ship.internal[i + 1] : null;
|
||||||
|
if (!fuelScoopSlot && (!slot.eligible || slot.eligible.fs)) { // Fit best possible Fuel Scoop
|
||||||
|
fuelScoopSlot = slot;
|
||||||
|
ship.use(fuelScoopSlot, ModuleUtils.findInternal('fs', slot.maxClass, 'A'));
|
||||||
|
ship.setSlotEnabled(fuelScoopSlot, true);
|
||||||
|
|
||||||
|
// Mount a Shield generator if possible AND an AFM Unit has been mounted already (Guarantees at least 1 AFM Unit)
|
||||||
|
} else if (!sgSlot && afmUnitCount < 2 && sg.class <= slot.maxClass && (!slot.eligible || slot.eligible.sg) && (!nextSlot || nextSlot.maxClass < sg.class)) {
|
||||||
|
sgSlot = slot;
|
||||||
|
ship.use(sgSlot, sg);
|
||||||
|
ship.setSlotEnabled(sgSlot, true);
|
||||||
|
} else if (afmUnitCount > 0 && (!slot.eligible || slot.eligible.am)) {
|
||||||
|
afmUnitCount--;
|
||||||
|
let am = ModuleUtils.findInternal('am', slot.maxClass, afmUnitCount ? 'B' : 'A');
|
||||||
|
ship.use(slot, am);
|
||||||
|
ship.setSlotEnabled(slot, false); // Disabled power for AFM Unit
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ship.use(slot, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ship.hardpoints.forEach((s) => {
|
||||||
|
if (s.maxClass == 0 && heatSinkCount) { // Mount up to 2 heatsinks
|
||||||
|
ship.use(s, ModuleUtils.hardpoints('02'));
|
||||||
|
ship.setSlotEnabled(s, heatSinkCount == 2); // Only enable a single Heatsink
|
||||||
|
heatSinkCount--;
|
||||||
|
} else {
|
||||||
|
ship.use(s, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sgSlot) {
|
||||||
|
// The SG and Fuel scoop to not need to be powered at the same time
|
||||||
|
if (sgSlot.m.power > fuelScoopSlot.m.power) { // The Shield generator uses the most power
|
||||||
|
ship.setSlotEnabled(fuelScoopSlot, false);
|
||||||
|
} else { // The Fuel scoop uses the most power
|
||||||
|
ship.setSlotEnabled(sgSlot, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ship.useLightestStandard({ pd: '1D', ppRating: 'A' });
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
_selectBulkhead(bulkheadIndex) {
|
_selectBulkhead(bulkheadIndex) {
|
||||||
this.props.ship.useBulkhead(bulkheadIndex);
|
this.props.ship.useBulkhead(bulkheadIndex);
|
||||||
this._closeMenu();
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
|
}
|
||||||
|
|
||||||
|
_contextMenu() {
|
||||||
|
this._optimizeStandard();
|
||||||
}
|
}
|
||||||
|
|
||||||
_getSlots() {
|
_getSlots() {
|
||||||
@@ -45,7 +118,7 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
let st = ship.standard;
|
let st = ship.standard;
|
||||||
let avail = ship.getAvailableModules().standard;
|
let avail = ship.getAvailableModules().standard;
|
||||||
let bulkheads = ship.bulkheads;
|
let bulkheads = ship.bulkheads;
|
||||||
let currentMenu = this.state.currentMenu;
|
let currentMenu = this.props.currentMenu;
|
||||||
|
|
||||||
slots[0] = (
|
slots[0] = (
|
||||||
<div key='bh' className={cn('slot', {selected: currentMenu === bulkheads})} onClick={open.bind(this, bulkheads)}>
|
<div key='bh' className={cn('slot', {selected: currentMenu === bulkheads})} onClick={open.bind(this, bulkheads)}>
|
||||||
@@ -76,9 +149,9 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
slot={st[0]}
|
slot={st[0]}
|
||||||
modules={avail[0]}
|
modules={avail[0]}
|
||||||
onOpen={open.bind(this, st[0])}
|
onOpen={open.bind(this, st[0])}
|
||||||
onSelect={select.bind(this, 1, st[0])}
|
onSelect={select.bind(this, st[0])}
|
||||||
selected={currentMenu == st[0]}
|
selected={currentMenu == st[0]}
|
||||||
warning={(m) => m.pGen < ship.powerRetracted}
|
warning={m => m.pGen < ship.powerRetracted}
|
||||||
/>;
|
/>;
|
||||||
|
|
||||||
slots[2] = <StandardSlot
|
slots[2] = <StandardSlot
|
||||||
@@ -86,9 +159,9 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
slot={st[1]}
|
slot={st[1]}
|
||||||
modules={avail[1]}
|
modules={avail[1]}
|
||||||
onOpen={open.bind(this, st[1])}
|
onOpen={open.bind(this, st[1])}
|
||||||
onSelect={select.bind(this, 2, st[1])}
|
onSelect={select.bind(this, st[1])}
|
||||||
selected={currentMenu == st[1]}
|
selected={currentMenu == st[1]}
|
||||||
warning={(m) => m.maxmass < ship.ladenMass}
|
warning={m => m.maxmass < ship.ladenMass}
|
||||||
/>;
|
/>;
|
||||||
|
|
||||||
|
|
||||||
@@ -97,7 +170,7 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
slot={st[2]}
|
slot={st[2]}
|
||||||
modules={avail[2]}
|
modules={avail[2]}
|
||||||
onOpen={open.bind(this, st[2])}
|
onOpen={open.bind(this, st[2])}
|
||||||
onSelect={select.bind(this, 3, st[2])}
|
onSelect={select.bind(this, st[2])}
|
||||||
selected={currentMenu == st[2]}
|
selected={currentMenu == st[2]}
|
||||||
/>;
|
/>;
|
||||||
|
|
||||||
@@ -106,7 +179,7 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
slot={st[3]}
|
slot={st[3]}
|
||||||
modules={avail[3]}
|
modules={avail[3]}
|
||||||
onOpen={open.bind(this, st[3])}
|
onOpen={open.bind(this, st[3])}
|
||||||
onSelect={select.bind(this, 4, st[3])}
|
onSelect={select.bind(this, st[3])}
|
||||||
selected={currentMenu == st[3]}
|
selected={currentMenu == st[3]}
|
||||||
/>;
|
/>;
|
||||||
|
|
||||||
@@ -115,7 +188,7 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
slot={st[4]}
|
slot={st[4]}
|
||||||
modules={avail[4]}
|
modules={avail[4]}
|
||||||
onOpen={open.bind(this, st[4])}
|
onOpen={open.bind(this, st[4])}
|
||||||
onSelect={select.bind(this, 5, st[4])}
|
onSelect={select.bind(this, st[4])}
|
||||||
selected={currentMenu == st[4]}
|
selected={currentMenu == st[4]}
|
||||||
warning= {m => m.enginecapacity < ship.boostEnergy}
|
warning= {m => m.enginecapacity < ship.boostEnergy}
|
||||||
/>;
|
/>;
|
||||||
@@ -125,7 +198,7 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
slot={st[5]}
|
slot={st[5]}
|
||||||
modules={avail[5]}
|
modules={avail[5]}
|
||||||
onOpen={open.bind(this, st[5])}
|
onOpen={open.bind(this, st[5])}
|
||||||
onSelect={select.bind(this, 6, st[5])}
|
onSelect={select.bind(this, st[5])}
|
||||||
selected={currentMenu == st[5]}
|
selected={currentMenu == st[5]}
|
||||||
warning= {m => m.enginecapacity < ship.boostEnergy}
|
warning= {m => m.enginecapacity < ship.boostEnergy}
|
||||||
/>;
|
/>;
|
||||||
@@ -135,7 +208,7 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
slot={st[6]}
|
slot={st[6]}
|
||||||
modules={avail[6]}
|
modules={avail[6]}
|
||||||
onOpen={open.bind(this, st[6])}
|
onOpen={open.bind(this, st[6])}
|
||||||
onSelect={select.bind(this, 7, st[6])}
|
onSelect={select.bind(this, st[6])}
|
||||||
selected={currentMenu == st[6]}
|
selected={currentMenu == st[6]}
|
||||||
warning= {m => m.capacity < st[2].m.maxfuel} // Show warning when fuel tank is smaller than FSD Max Fuel
|
warning= {m => m.capacity < st[2].m.maxfuel} // Show warning when fuel tank is smaller than FSD Max Fuel
|
||||||
/>;
|
/>;
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ export class LinkIcon extends SvgIcon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class NoPower extends SvgIcon {
|
export class NoPower extends SvgIcon {
|
||||||
|
viewBox() { return '0 0 512 512' };
|
||||||
svg() {
|
svg() {
|
||||||
return <path d='M437.020 74.98c-48.353-48.351-112.64-74.98-181.020-74.98s-132.667 26.629-181.020 74.98c-48.351 48.353-74.98 112.64-74.98 181.020s26.629 132.667 74.98 181.020c48.353 48.351 112.64 74.98 181.020 74.98s132.667-26.629 181.020-74.98c48.351-48.353 74.98-112.64 74.98-181.020s-26.629-132.667-74.98-181.020zM448 256c0 41.407-13.177 79.794-35.556 111.19l-267.633-267.634c31.396-22.379 69.782-35.556 111.189-35.556 105.869 0 192 86.131 192 192zM64 256c0-41.407 13.177-79.793 35.556-111.189l267.635 267.634c-31.397 22.378-69.784 35.555-111.191 35.555-105.869 0-192-86.131-192-192z'/>;
|
return <path d='M437.020 74.98c-48.353-48.351-112.64-74.98-181.020-74.98s-132.667 26.629-181.020 74.98c-48.351 48.353-74.98 112.64-74.98 181.020s26.629 132.667 74.98 181.020c48.353 48.351 112.64 74.98 181.020 74.98s132.667-26.629 181.020-74.98c48.351-48.353 74.98-112.64 74.98-181.020s-26.629-132.667-74.98-181.020zM448 256c0 41.407-13.177 79.794-35.556 111.19l-267.633-267.634c31.396-22.379 69.782-35.556 111.189-35.556 105.869 0 192 86.131 192 192zM64 256c0-41.407 13.177-79.793 35.556-111.189l267.635 267.634c-31.397 22.378-69.784 35.555-111.191 35.555-105.869 0-192-86.131-192-192z'/>;
|
||||||
}
|
}
|
||||||
@@ -129,6 +130,7 @@ export class Notification extends SvgIcon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Power extends SvgIcon {
|
export class Power extends SvgIcon {
|
||||||
|
viewBox() { return '0 0 512 512' };
|
||||||
svg() {
|
svg() {
|
||||||
return <path d='M192 0l-192 256h192l-128 256 448-320h-256l192-192z'/>;
|
return <path d='M192 0l-192 256h192l-128 256 448-320h-256l192-192z'/>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,29 +12,38 @@ export default class UtilitySlotSection extends SlotSection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_empty() {
|
_empty() {
|
||||||
|
this.props.ship.emptyUtility();
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
_use(grp, rating) {
|
_use(group, rating, name, event) {
|
||||||
|
this.props.ship.useUtility(group, rating, name, event.getModifierState('Alt'));
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
|
}
|
||||||
|
|
||||||
|
_contextMenu() {
|
||||||
|
this._empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
_getSlots() {
|
_getSlots() {
|
||||||
let slots = [];
|
let slots = [];
|
||||||
let hardpoints = this.props.ship.hardpoints;
|
let hardpoints = this.props.ship.hardpoints;
|
||||||
let availableModules = this.props.ship.getAvailableModules();
|
let availableModules = this.props.ship.getAvailableModules();
|
||||||
let currentMenu = this.state.currentMenu;
|
let currentMenu = this.props.currentMenu;
|
||||||
|
|
||||||
for (let i = 0, l = hardpoints.length; i < l; i++) {
|
for (let i = 0, l = hardpoints.length; i < l; i++) {
|
||||||
let h = hardpoints[i];
|
let h = hardpoints[i];
|
||||||
if (h.maxClass === 0) {
|
if (h.maxClass === 0) {
|
||||||
slots.push(<HardpointSlot
|
slots.push(<HardpointSlot
|
||||||
key={i}
|
key={i}
|
||||||
size={h.maxClass}
|
maxClass={h.maxClass}
|
||||||
modules={availableModules.getHps(h.maxClass)}
|
availableModules={() => availableModules.getHps(h.maxClass)}
|
||||||
onOpen={this._openMenu.bind(this,h)}
|
onOpen={this._openMenu.bind(this,h)}
|
||||||
onSelect={this._selectModule.bind(this, h)}
|
onSelect={this._selectModule.bind(this, h)}
|
||||||
selected={currentMenu == h}
|
selected={currentMenu == h}
|
||||||
|
enabled={h.enabled}
|
||||||
m={h.m}
|
m={h.m}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
@@ -52,11 +61,15 @@ export default class UtilitySlotSection extends SlotSection {
|
|||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('sb')}</div>
|
<div className='select-group cap'>{translate('sb')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='c' onClick={_use.bind(this, 'sb', 'E')}>E</li>
|
<li className='c' onClick={_use.bind(this, 'sb', 'E', null)}>E</li>
|
||||||
<li className='c' onClick={_use.bind(this, 'sb', 'D')}>D</li>
|
<li className='c' onClick={_use.bind(this, 'sb', 'D', null)}>D</li>
|
||||||
<li className='c' onClick={_use.bind(this, 'sb', 'C')}>C</li>
|
<li className='c' onClick={_use.bind(this, 'sb', 'C', null)}>C</li>
|
||||||
<li className='c' onClick={_use.bind(this, 'sb', 'B')}>B</li>
|
<li className='c' onClick={_use.bind(this, 'sb', 'B', null)}>B</li>
|
||||||
<li className='c' onClick={_use.bind(this, 'sb', 'A')}>A</li>
|
<li className='c' onClick={_use.bind(this, 'sb', 'A', null)}>A</li>
|
||||||
|
</ul>
|
||||||
|
<div className='select-group cap'>{translate('cm')}</div>
|
||||||
|
<ul>
|
||||||
|
<li className='lc' onClick={_use.bind(this, 'cm', null, 'Heat Sink Launcher')}>{translate('Heat Sink Launcher')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,164 +0,0 @@
|
|||||||
angular.module('app').directive('areaChart', ['$window', '$translate', function($window, $translate) {
|
|
||||||
return {
|
|
||||||
restrict: 'A',
|
|
||||||
scope: {
|
|
||||||
config: '=',
|
|
||||||
series: '='
|
|
||||||
},
|
|
||||||
link: function(scope, element) {
|
|
||||||
var series = scope.series,
|
|
||||||
config = scope.config,
|
|
||||||
labels = config.labels,
|
|
||||||
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),
|
|
||||||
x = 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 + ')');
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Create Y Axis SVG Elements
|
|
||||||
var yTxt = vis.append('g').attr('class', 'y axis')
|
|
||||||
.append('text')
|
|
||||||
.attr('class', 'cap')
|
|
||||||
.attr('transform', 'rotate(-90)')
|
|
||||||
.attr('y', -50)
|
|
||||||
.attr('dy', '.1em')
|
|
||||||
.style('text-anchor', 'middle')
|
|
||||||
.text($translate.instant(labels.yAxis.title) + ' (' + $translate.instant(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($translate.instant(labels.xAxis.title) + ' (' + $translate.instant(labels.xAxis.unit) + ')');
|
|
||||||
|
|
||||||
// Create and Add tooltip
|
|
||||||
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)
|
|
||||||
*/
|
|
||||||
scope.$watchCollection('series', render);
|
|
||||||
angular.element($window).bind('orientationchange resize render', render);
|
|
||||||
|
|
||||||
function render() {
|
|
||||||
var width = element[0].parentElement.offsetWidth,
|
|
||||||
height = width * 0.5,
|
|
||||||
w = width - margin.left - margin.right,
|
|
||||||
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]); });
|
|
||||||
} else {
|
|
||||||
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
|
|
||||||
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);
|
|
||||||
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);
|
|
||||||
|
|
||||||
vis.selectAll('path.area') // Area/Path to appear behind everything else
|
|
||||||
.data([data])
|
|
||||||
.attr('d', area);
|
|
||||||
}
|
|
||||||
|
|
||||||
function showTip() {
|
|
||||||
tip.style('display', null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideTip() {
|
|
||||||
if (!dragging) {
|
|
||||||
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.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) + ' ' + $translate.instant(labels.xAxis.unit));
|
|
||||||
tip.select('text.label.y').text(fmtLong(y0) + ' ' + $translate.instant(labels.yAxis.unit));
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.$on('$destroy', function() {
|
|
||||||
angular.element($window).unbind('orientationchange resize render', render);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
angular.module('app').directive('powerBands', ['$window', '$translate', '$rootScope', function($window, $translate, $rootScope) {
|
|
||||||
return {
|
|
||||||
restrict: 'A',
|
|
||||||
scope: {
|
|
||||||
bands: '=',
|
|
||||||
available: '='
|
|
||||||
},
|
|
||||||
link: function(scope, element) {
|
|
||||||
var bands = null,
|
|
||||||
available = 0,
|
|
||||||
maxBand,
|
|
||||||
maxPwr,
|
|
||||||
deployedSum = 0,
|
|
||||||
retractedSum = 0,
|
|
||||||
retBandsSelected = false,
|
|
||||||
depBandsSelected = false,
|
|
||||||
wattScale = d3.scale.linear(),
|
|
||||||
pctScale = d3.scale.linear().domain([0, 1]),
|
|
||||||
wattFmt,
|
|
||||||
pctFmt,
|
|
||||||
wattAxis = d3.svg.axis().scale(wattScale).outerTickSize(0).orient('top'),
|
|
||||||
pctAxis = d3.svg.axis().scale(pctScale).outerTickSize(0).orient('bottom'),
|
|
||||||
// Create chart
|
|
||||||
svg = d3.select(element[0]).append('svg'),
|
|
||||||
vis = svg.append('g'),
|
|
||||||
deployed = vis.append('g').attr('class', 'power-band'),
|
|
||||||
retracted = vis.append('g').attr('class', 'power-band');
|
|
||||||
|
|
||||||
svg.on('contextmenu', function() {
|
|
||||||
if (!d3.event.shiftKey) {
|
|
||||||
d3.event.preventDefault();
|
|
||||||
for (var i = 0, l = bands.length; i < l; i++) {
|
|
||||||
bands[i].retSelected = false;
|
|
||||||
bands[i].depSelected = false;
|
|
||||||
}
|
|
||||||
dataChange();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create Y Axis SVG Elements
|
|
||||||
var wattAxisGroup = vis.append('g').attr('class', 'watt axis');
|
|
||||||
vis.append('g').attr('class', 'pct axis');
|
|
||||||
var retText = vis.append('text').attr('x', -3).style('text-anchor', 'end').attr('dy', '0.5em').attr('class', 'primary upp');
|
|
||||||
var depText = vis.append('text').attr('x', -3).style('text-anchor', 'end').attr('dy', '0.5em').attr('class', 'primary upp');
|
|
||||||
var retLbl = vis.append('text').attr('dy', '0.5em');
|
|
||||||
var depLbl = vis.append('text').attr('dy', '0.5em');
|
|
||||||
|
|
||||||
updateFormats(true);
|
|
||||||
|
|
||||||
function dataChange() {
|
|
||||||
bands = scope.bands;
|
|
||||||
available = scope.available;
|
|
||||||
maxBand = bands[bands.length - 1];
|
|
||||||
deployedSum = 0;
|
|
||||||
retractedSum = 0;
|
|
||||||
retBandsSelected = false;
|
|
||||||
depBandsSelected = false;
|
|
||||||
maxPwr = Math.max(available, maxBand.retractedSum, maxBand.deployedSum);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render();
|
|
||||||
}
|
|
||||||
|
|
||||||
function render() {
|
|
||||||
var size = $rootScope.sizeRatio,
|
|
||||||
mTop = Math.round(25 * size),
|
|
||||||
mRight = Math.round(130 * size),
|
|
||||||
mBottom = Math.round(25 * size),
|
|
||||||
mLeft = Math.round(45 * size),
|
|
||||||
barHeight = Math.round(20 * size),
|
|
||||||
width = element[0].offsetWidth,
|
|
||||||
innerHeight = (barHeight * 2) + 2,
|
|
||||||
height = innerHeight + mTop + mBottom,
|
|
||||||
w = width - mLeft - mRight,
|
|
||||||
repY = (barHeight / 2),
|
|
||||||
depY = (barHeight * 1.5) - 1;
|
|
||||||
|
|
||||||
// Update chart size
|
|
||||||
svg.attr('width', width).attr('height', height);
|
|
||||||
vis.attr('transform', 'translate(' + mLeft + ',' + mTop + ')');
|
|
||||||
|
|
||||||
// Remove existing elements
|
|
||||||
retracted.selectAll('rect').remove();
|
|
||||||
retracted.selectAll('text').remove();
|
|
||||||
deployed.selectAll('rect').remove();
|
|
||||||
deployed.selectAll('text').remove();
|
|
||||||
wattAxisGroup.selectAll('line.threshold').remove();
|
|
||||||
|
|
||||||
// Update X & Y Axis
|
|
||||||
wattScale.range([0, w]).domain([0, maxPwr]).clamp(true);
|
|
||||||
pctScale.range([0, w]).domain([0, maxPwr / available]).clamp(true);
|
|
||||||
wattAxisGroup.call(wattAxis);
|
|
||||||
vis.selectAll('.pct.axis').attr('transform', 'translate(0,' + innerHeight + ')').call(pctAxis);
|
|
||||||
|
|
||||||
var pwrWarningClass = 'threshold' + (bands[0].retractedSum * 2 >= available ? ' exceeded' : '');
|
|
||||||
vis.select('.pct.axis g:nth-child(6)').selectAll('line, text').attr('class', pwrWarningClass);
|
|
||||||
|
|
||||||
wattAxisGroup.append('line')
|
|
||||||
.attr('x1', pctScale(0.5))
|
|
||||||
.attr('x2', pctScale(0.5))
|
|
||||||
.attr('y1', 0)
|
|
||||||
.attr('y2', innerHeight)
|
|
||||||
.attr('class', pwrWarningClass);
|
|
||||||
|
|
||||||
retText.attr('y', repY);
|
|
||||||
depText.attr('y', depY);
|
|
||||||
updateLabel(retLbl, w, repY, retBandsSelected, retBandsSelected ? retractedSum : maxBand.retractedSum, available);
|
|
||||||
updateLabel(depLbl, w, depY, depBandsSelected, depBandsSelected ? deployedSum : maxBand.deployedSum, available);
|
|
||||||
|
|
||||||
retracted.selectAll('rect').data(bands).enter().append('rect')
|
|
||||||
.attr('height', barHeight)
|
|
||||||
.attr('width', function(d) { return Math.ceil(Math.max(wattScale(d.retracted + d.retOnly), 0)); })
|
|
||||||
.attr('x', function(d) { return Math.floor(Math.max(wattScale(d.retractedSum) - wattScale(d.retracted + d.retOnly), 0)); })
|
|
||||||
.attr('y', 1)
|
|
||||||
.on('click', function(d) {
|
|
||||||
d.retSelected = !d.retSelected;
|
|
||||||
dataChange();
|
|
||||||
})
|
|
||||||
.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 + d.retOnly) / 2); })
|
|
||||||
.attr('y', repY)
|
|
||||||
.attr('dy', '0.5em')
|
|
||||||
.style('text-anchor', 'middle')
|
|
||||||
.attr('class', 'primary-bg')
|
|
||||||
.on('click', function(d) {
|
|
||||||
d.retSelected = !d.retSelected;
|
|
||||||
dataChange();
|
|
||||||
})
|
|
||||||
.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.ceil(Math.max(wattScale(d.deployed + d.retracted), 0)); })
|
|
||||||
.attr('x', function(d) { return Math.floor(Math.max(wattScale(d.deployedSum) - wattScale(d.retracted) - wattScale(d.deployed), 0)); })
|
|
||||||
.attr('y', barHeight + 1)
|
|
||||||
.on('click', function(d) {
|
|
||||||
d.depSelected = !d.depSelected;
|
|
||||||
dataChange();
|
|
||||||
})
|
|
||||||
.attr('class', function(d) { return getClass(d.depSelected, d.deployedSum, available); });
|
|
||||||
|
|
||||||
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', depY)
|
|
||||||
.attr('dy', '0.5em')
|
|
||||||
.style('text-anchor', 'middle')
|
|
||||||
.attr('class', 'primary-bg')
|
|
||||||
.on('click', function(d) {
|
|
||||||
d.depSelected = !d.depSelected;
|
|
||||||
dataChange();
|
|
||||||
})
|
|
||||||
.text(function(d, i) { return bandText(d.deployed + d.retracted, i); });
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateLabel(lbl, width, y, selected, sum, avail) {
|
|
||||||
lbl
|
|
||||||
.attr('x', width + 5 )
|
|
||||||
.attr('y', y)
|
|
||||||
.attr('class', getClass(selected, sum, avail))
|
|
||||||
.text(wattFmt(Math.max(0, sum)) + ' (' + pctFmt(Math.max(0, sum / avail)) + ')');
|
|
||||||
}
|
|
||||||
|
|
||||||
function getClass(selected, sum, avail) {
|
|
||||||
// Round to avoid floating point precision errors
|
|
||||||
return selected ? 'secondary' : ((Math.round(sum * 100) / 100) >= avail) ? 'warning' : 'primary';
|
|
||||||
}
|
|
||||||
|
|
||||||
function bandText(val, index) {
|
|
||||||
if (val > 0 && wattScale(val) > 13) {
|
|
||||||
return index + 1;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateFormats(preventRender) {
|
|
||||||
retText.text($translate.instant('ret'));
|
|
||||||
depText.text($translate.instant('dep'));
|
|
||||||
wattFmt = $rootScope.localeFormat.numberFormat('.2f');
|
|
||||||
pctFmt = $rootScope.localeFormat.numberFormat('.1%');
|
|
||||||
wattAxis.tickFormat($rootScope.localeFormat.numberFormat('.2r'));
|
|
||||||
pctAxis.tickFormat($rootScope.localeFormat.numberFormat('%'));
|
|
||||||
if (!preventRender) {
|
|
||||||
render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch for changes to data and events
|
|
||||||
angular.element($window).bind('pwrchange', dataChange);
|
|
||||||
angular.element($window).bind('orientationchange resize', render);
|
|
||||||
scope.$watchCollection('available', dataChange);
|
|
||||||
scope.$on('languageChanged', updateFormats);
|
|
||||||
scope.$on('$destroy', function() {
|
|
||||||
angular.element($window).unbind('orientationchange resize pwrchange', render);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
@@ -34,28 +34,29 @@ export function getLanguage(langCode) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
formats: {
|
formats: {
|
||||||
gen: gen,
|
gen: gen, // General number format (.e.g 1,001,001.1234)
|
||||||
int: d3Locale.numberFormat(',.0f'),
|
int: d3Locale.numberFormat(',.0f'), // Fixed to 0 decimal places (.e.g 1,001)
|
||||||
pwr: d3Locale.numberFormat(',.2f'),
|
f2: d3Locale.numberFormat(',.2f'), // Fixed to 2 decimal places (.e.g 1,001.10)
|
||||||
round: (d) => gen(d3.round(d, 2)),
|
pct: d3Locale.numberFormat('.2%'), // % to 2 decimal places (.e.g 5.40%)
|
||||||
pct: d3Locale.numberFormat('.2%'),
|
pct1: d3Locale.numberFormat('.1%'), // % to 1 decimal places (.e.g 5.4%)
|
||||||
pct1: d3Locale.numberFormat('.1%'),
|
r2: d3Locale.numberFormat('.2r'), // Rounded to 2 decimal places (.e.g 5.12, 4.10)
|
||||||
rPct: d3.format('%'),
|
rPct: d3.format('%'), // % to 0 decimal places (.e.g 5%)
|
||||||
|
round: (d) => gen(d3.round(d, 2)), // Rounded to 0-2 decimal places (.e.g 5.12, 4.1)
|
||||||
time: (d) => Math.floor(d / 60) + ':' + ('00' + Math.floor(d % 60)).substr(-2, 2)
|
time: (d) => Math.floor(d / 60) + ':' + ('00' + Math.floor(d % 60)).substr(-2, 2)
|
||||||
},
|
},
|
||||||
translate,
|
translate,
|
||||||
units: {
|
units: {
|
||||||
CR: <u>{' ' + translate('CR')}</u>, // Credits
|
CR: <u>{' ' + translate('CR')}</u>, // Credits
|
||||||
kg: <u>{' ' + translate('kg')}</u>, // Kilograms
|
kg: <u>{' ' + translate('kg')}</u>, // Kilograms
|
||||||
kgs: <u>{' ' + translate('kg/s')}</u>, // Kilograms per second
|
kgs: <u>{' ' + translate('kg/s')}</u>, // Kilograms per second
|
||||||
km: <u>{' ' + translate('km')}</u>, // Kilometers
|
km: <u>{' ' + translate('km')}</u>, // Kilometers
|
||||||
Ls: <u>{' ' + translate('Ls')}</u>, // Light Seconds
|
Ls: <u>{' ' + translate('Ls')}</u>, // Light Seconds
|
||||||
LY: <u>{' ' + translate('LY')}</u>, // Light Years
|
LY: <u>{' ' + translate('LY')}</u>, // Light Years
|
||||||
MJ: <u>{' ' + translate('MJ')}</u>, // Mega Joules
|
MJ: <u>{' ' + translate('MJ')}</u>, // Mega Joules
|
||||||
ms: <u>{' ' + translate('m/s')}</u>, // Meters per second
|
ms: <u>{' ' + translate('m/s')}</u>, // Meters per second
|
||||||
MW: <u>{' ' + translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
|
MW: <u>{' ' + translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
|
||||||
ps: <u>{translate('/s')}</u>, // per second
|
ps: <u>{translate('/s')}</u>, // per second
|
||||||
T: <u>{' ' + translate('T')}</u>, // Metric Tons
|
T: <u>{' ' + translate('T')}</u>, // Metric Tons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,10 +64,9 @@ export function getLanguage(langCode) {
|
|||||||
|
|
||||||
export const Languages = {
|
export const Languages = {
|
||||||
en: 'English',
|
en: 'English',
|
||||||
de: 'Deutsh',
|
de: 'Deutsch',
|
||||||
it: 'Italiano',
|
it: 'Italiano',
|
||||||
es: 'Español',
|
es: 'Español',
|
||||||
fr: 'Français',
|
fr: 'Français',
|
||||||
ru: 'ру́сский'
|
ru: 'ру́сский'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,216 +15,219 @@ export const formats = {
|
|||||||
|
|
||||||
export const terms = {
|
export const terms = {
|
||||||
PHRASE_EXPORT_DESC: 'A detailed JSON export of your build for use in other sites and tools',
|
PHRASE_EXPORT_DESC: 'A detailed JSON export of your build for use in other sites and tools',
|
||||||
'A-Rated': 'A-Rated',
|
// 'A-Rated': 'A-Rated',
|
||||||
about: 'about',
|
// about: 'about',
|
||||||
action: 'action',
|
// action: 'action',
|
||||||
added: 'added',
|
// added: 'added',
|
||||||
Advanced: 'Advanced',
|
// Advanced: 'Advanced',
|
||||||
'Advanced Discovery Scanner': 'Advanced Discovery Scanner',
|
// 'Advanced Discovery Scanner': 'Advanced Discovery Scanner',
|
||||||
agility: 'agility',
|
// agility: 'agility',
|
||||||
alpha: 'alpha',
|
// alpha: 'alpha',
|
||||||
ammo: 'ammo',
|
// ammo: 'ammo',
|
||||||
PHRASE_CONFIRMATION: 'Are You Sure?',
|
PHRASE_CONFIRMATION: 'Are You Sure?',
|
||||||
armour: 'armour',
|
// armour: 'armour',
|
||||||
am: 'Auto Field-Maintenance Unit',
|
am: 'Auto Field-Maintenance Unit',
|
||||||
available: 'available',
|
// available: 'available',
|
||||||
backup: 'backup',
|
// backup: 'backup',
|
||||||
'Basic Discovery Scanner': 'Basic Discovery Scanner',
|
'Basic Discovery Scanner': 'Basic Discovery Scanner',
|
||||||
bl: 'Beam Laser',
|
bl: 'Beam Laser',
|
||||||
beta: 'beta',
|
// beta: 'beta',
|
||||||
bins: 'bins',
|
// bins: 'bins',
|
||||||
boost: 'boost',
|
// boost: 'boost',
|
||||||
build: 'build',
|
// build: 'build',
|
||||||
'build name': 'Build Name',
|
// 'build name': 'Build Name',
|
||||||
builds: 'builds',
|
// builds: 'builds',
|
||||||
bh: 'bulkheads',
|
bh: 'bulkheads',
|
||||||
|
bsg: 'Bi-Weave Shield Generator',
|
||||||
ul: 'Burst Laser',
|
ul: 'Burst Laser',
|
||||||
buy: 'buy',
|
buy: 'buy',
|
||||||
cancel: 'cancel',
|
// cancel: 'cancel',
|
||||||
c: 'Cannon',
|
c: 'Cannon',
|
||||||
capital: 'capital',
|
// capital: 'capital',
|
||||||
cargo: 'cargo',
|
// cargo: 'cargo',
|
||||||
'Cargo Hatch': 'Cargo Hatch',
|
// 'Cargo Hatch': 'Cargo Hatch',
|
||||||
cr: 'Cargo Rack',
|
cr: 'Cargo Rack',
|
||||||
cs: 'Cargo Scanner',
|
cs: 'Cargo Scanner',
|
||||||
cells: 'cells',
|
cells: 'cells',
|
||||||
'Chaff Launcher': 'Chaff Launcher',
|
// 'Chaff Launcher': 'Chaff Launcher',
|
||||||
close: 'close',
|
// close: 'close',
|
||||||
cc: 'Collector Limpet Controller',
|
cc: 'Collector Limpet Controller',
|
||||||
compare: 'compare',
|
// compare: 'compare',
|
||||||
'compare all': 'compare all',
|
// 'compare all': 'compare all',
|
||||||
comparison: 'comparison',
|
// comparison: 'comparison',
|
||||||
comparisons: 'comparisons',
|
// comparisons: 'comparisons',
|
||||||
component: 'component',
|
// component: 'component',
|
||||||
cost: 'cost',
|
// cost: 'cost',
|
||||||
costs: 'costs',
|
// costs: 'costs',
|
||||||
cm: 'Countermeasure',
|
cm: 'Countermeasure',
|
||||||
CR: 'CR',
|
// CR: 'CR',
|
||||||
create: 'create',
|
// create: 'create',
|
||||||
'create new': 'create new',
|
// 'create new': 'create new',
|
||||||
credits: 'credits',
|
// credits: 'credits',
|
||||||
Cytoscrambler: 'Cytoscrambler',
|
// Cytoscrambler: 'Cytoscrambler',
|
||||||
damage: 'damage',
|
// damage: 'damage',
|
||||||
delete: 'delete',
|
// delete: 'delete',
|
||||||
'delete all': 'delete all',
|
// 'delete all': 'delete all',
|
||||||
dep: 'dep',
|
// dep: 'dep',
|
||||||
deployed: 'deployed',
|
// deployed: 'deployed',
|
||||||
'detailed export': 'detailed export',
|
// 'detailed export': 'detailed export',
|
||||||
'Detailed Surface Scanner': 'Detailed Surface Scanner',
|
// 'Detailed Surface Scanner': 'Detailed Surface Scanner',
|
||||||
disabled: 'disabled',
|
// disabled: 'disabled',
|
||||||
discount: 'discount',
|
// discount: 'discount',
|
||||||
Distruptor: 'Distruptor',
|
// Distruptor: 'Distruptor',
|
||||||
dc: 'Docking Computer',
|
dc: 'Docking Computer',
|
||||||
done: 'done',
|
// done: 'done',
|
||||||
DPS: 'DPS',
|
// DPS: 'DPS',
|
||||||
'edit data': 'edit data',
|
// 'edit data': 'edit data',
|
||||||
efficiency: 'efficiency',
|
// efficiency: 'efficiency',
|
||||||
'Electronic Countermeasure': 'Electronic Countermeasure',
|
// 'Electronic Countermeasure': 'Electronic Countermeasure',
|
||||||
empty: 'empty',
|
// empty: 'empty',
|
||||||
Enforcer: 'Enforcer',
|
// Enforcer: 'Enforcer',
|
||||||
ENG: 'ENG',
|
// ENG: 'ENG',
|
||||||
'enter name': 'Enter Name',
|
// 'Enter Name': 'Enter Name',
|
||||||
EPS: 'EPS',
|
// EPS: 'EPS',
|
||||||
export: 'export',
|
// export: 'export',
|
||||||
fixed: 'fixed',
|
// fixed: 'fixed',
|
||||||
forum: 'forum',
|
// forum: 'forum',
|
||||||
fc: 'Fragment Cannon',
|
fc: 'Fragment Cannon',
|
||||||
fd: 'Frame Shift Drive',
|
fd: 'Frame Shift Drive',
|
||||||
ws: 'Frame Shift Wake Scanner',
|
ws: 'Frame Shift Wake Scanner',
|
||||||
FSD: 'FSD',
|
// FSD: 'FSD',
|
||||||
|
fsd: 'Frame Shift Drive',
|
||||||
fi: 'FSD Interdictor',
|
fi: 'FSD Interdictor',
|
||||||
fuel: 'fuel',
|
// fuel: 'fuel',
|
||||||
fs: 'Fuel Scoop',
|
fs: 'Fuel Scoop',
|
||||||
ft: 'Fuel Tank',
|
ft: 'Fuel Tank',
|
||||||
fx: 'Fuel Transfer Limpet Controller',
|
fx: 'Fuel Transfer Limpet Controller',
|
||||||
'full tank': 'full tank',
|
// 'full tank': 'full tank',
|
||||||
Gimballed: 'Gimballed',
|
// Gimballed: 'Gimballed',
|
||||||
H: 'H',
|
// H: 'H',
|
||||||
hardpoints: 'hardpoints',
|
// hardpoints: 'hardpoints',
|
||||||
hb: 'Hatch Breaker Limpet Controller',
|
hb: 'Hatch Breaker Limpet Controller',
|
||||||
'Heat Sink Launcher': 'Heat Sink Launcher',
|
// 'Heat Sink Launcher': 'Heat Sink Launcher',
|
||||||
huge: 'huge',
|
// huge: 'huge',
|
||||||
hull: 'hull',
|
// hull: 'hull',
|
||||||
hr: 'Hull Reinforcement Package',
|
hr: 'Hull Reinforcement Package',
|
||||||
'Imperial Hammer': 'Imperial Hammer',
|
// 'Imperial Hammer': 'Imperial Hammer',
|
||||||
import: 'import',
|
// import: 'import',
|
||||||
'import all': 'import all',
|
// 'import all': 'import all',
|
||||||
insurance: 'insurance',
|
// insurance: 'insurance',
|
||||||
'Intermediate Discovery Scanner': 'Intermediate Discovery Scanner',
|
// 'Intermediate Discovery Scanner': 'Intermediate Discovery Scanner',
|
||||||
'internal compartments': 'internal compartments',
|
// 'internal compartments': 'internal compartments',
|
||||||
'jump range': 'jump range',
|
// 'jump range': 'jump range',
|
||||||
jumps: 'jumps',
|
// jumps: 'jumps',
|
||||||
kw: 'Kill Warrant Scanner',
|
kw: 'Kill Warrant Scanner',
|
||||||
L: 'L',
|
// L: 'L',
|
||||||
laden: 'laden',
|
// laden: 'laden',
|
||||||
language: 'language',
|
// language: 'language',
|
||||||
large: 'large',
|
// large: 'large',
|
||||||
'limpets': 'Limpets',
|
// 'limpets': 'Limpets',
|
||||||
ls: 'life support',
|
ls: 'life support',
|
||||||
'Lightweight Alloy': 'Lightweight Alloy',
|
// 'Lightweight Alloy': 'Lightweight Alloy',
|
||||||
'lock factor': 'lock factor',
|
// 'lock factor': 'lock factor',
|
||||||
LS: 'Ls',
|
// LS: 'Ls',
|
||||||
LY: 'LY',
|
// LY: 'LY',
|
||||||
M: 'M',
|
// M: 'M',
|
||||||
'm/s': 'm/s',
|
// 'm/s': 'm/s',
|
||||||
mass: 'mass',
|
// mass: 'mass',
|
||||||
max: 'max',
|
// max: 'max',
|
||||||
'max mass': 'max mass',
|
// 'max mass': 'max mass',
|
||||||
medium: 'medium',
|
// medium: 'medium',
|
||||||
'Military Grade Composite': 'Military Grade Composite',
|
// 'Military Grade Composite': 'Military Grade Composite',
|
||||||
nl: 'Mine Launcher',
|
nl: 'Mine Launcher',
|
||||||
'Mining Lance': 'Mining Lance',
|
// 'Mining Lance': 'Mining Lance',
|
||||||
ml: 'Mining Laser',
|
ml: 'Mining Laser',
|
||||||
'Mirrored Surface Composite': 'Mirrored Surface Composite',
|
// 'Mirrored Surface Composite': 'Mirrored Surface Composite',
|
||||||
mr: 'Missile Rack',
|
mr: 'Missile Rack',
|
||||||
mc: 'Multi-cannon',
|
mc: 'Multi-cannon',
|
||||||
'net cost': 'net cost',
|
// 'net cost': 'net cost',
|
||||||
no: 'no',
|
// no: 'no',
|
||||||
PHRASE_NO_BUILDS: 'No builds added to comparison!',
|
PHRASE_NO_BUILDS: 'No builds added to comparison!',
|
||||||
PHRASE_NO_RETROCH: 'No Retrofitting changes',
|
PHRASE_NO_RETROCH: 'No Retrofitting changes',
|
||||||
none: 'none',
|
// none: 'none',
|
||||||
'none created': 'none created',
|
// 'none created': 'none created',
|
||||||
off: 'off',
|
// off: 'off',
|
||||||
on: 'on',
|
// on: 'on',
|
||||||
optimal: 'optimal',
|
// optimal: 'optimal',
|
||||||
'optimal mass': 'optimal mass',
|
// 'optimal mass': 'optimal mass',
|
||||||
'optimize mass': 'optimize mass',
|
// 'optimize mass': 'optimize mass',
|
||||||
overwrite: 'overwrite',
|
// overwrite: 'overwrite',
|
||||||
Pacifier: 'Pacifier',
|
// Pacifier: 'Pacifier',
|
||||||
'Pack-Hound': 'Pack-Hound',
|
// 'Pack-Hound': 'Pack-Hound',
|
||||||
PHRASE_IMPORT: 'Paste JSON or import here',
|
PHRASE_IMPORT: 'Paste JSON or import here',
|
||||||
pen: 'pen',
|
// pen: 'pen',
|
||||||
penetration: 'penetration',
|
// penetration: 'penetration',
|
||||||
permalink: 'permalink',
|
// permalink: 'permalink',
|
||||||
pa: 'Plasma Accelerator',
|
pa: 'Plasma Accelerator',
|
||||||
'Point Defence': 'Point Defence',
|
// 'Point Defence': 'Point Defence',
|
||||||
power: 'power',
|
// power: 'power',
|
||||||
pd: 'power distributor',
|
pd: 'power distributor',
|
||||||
pp: 'power plant',
|
pp: 'power plant',
|
||||||
pri: 'pri',
|
pri: 'pri',
|
||||||
priority: 'priority',
|
// priority: 'priority',
|
||||||
psg: 'Prismatic Shield Generator',
|
psg: 'Prismatic Shield Generator',
|
||||||
proceed: 'proceed',
|
// proceed: 'proceed',
|
||||||
pc: 'Prospector Limpet Controller',
|
pc: 'Prospector Limpet Controller',
|
||||||
pl: 'Pulse Laser',
|
pl: 'Pulse Laser',
|
||||||
PWR: 'PWR',
|
pv: 'Planetary Vehicle Hanger',
|
||||||
|
// PWR: 'PWR',
|
||||||
rg: 'Rail Gun',
|
rg: 'Rail Gun',
|
||||||
range: 'range',
|
// range: 'range',
|
||||||
rate: 'rate',
|
// rate: 'rate',
|
||||||
'Reactive Surface Composite': 'Reactive Surface Composite',
|
// 'Reactive Surface Composite': 'Reactive Surface Composite',
|
||||||
recharge: 'recharge',
|
// recharge: 'recharge',
|
||||||
rf: 'Refinery',
|
rf: 'Refinery',
|
||||||
'refuel time': 'refuel time',
|
// 'refuel time': 'refuel time',
|
||||||
'Reinforced Alloy': 'Reinforced Alloy',
|
// 'Reinforced Alloy': 'Reinforced Alloy',
|
||||||
reload: 'reload',
|
// reload: 'reload',
|
||||||
rename: 'rename',
|
// rename: 'rename',
|
||||||
repair: 'repair',
|
// repair: 'repair',
|
||||||
reset: 'reset',
|
// reset: 'reset',
|
||||||
ret: 'ret',
|
// ret: 'ret',
|
||||||
retracted: 'retracted',
|
// retracted: 'retracted',
|
||||||
'retrofit costs': 'retrofit costs',
|
// 'retrofit costs': 'retrofit costs',
|
||||||
'retrofit from': 'retrofit from',
|
// 'retrofit from': 'retrofit from',
|
||||||
ROF: 'ROF',
|
// ROF: 'ROF',
|
||||||
S: 'S',
|
// S: 'S',
|
||||||
save: 'save',
|
// save: 'save',
|
||||||
sc: 'scanner',
|
sc: 'scanner',
|
||||||
PHRASE_SELECT_BUILDS: 'Select Builds to Compare',
|
PHRASE_SELECT_BUILDS: 'Select Builds to Compare',
|
||||||
sell: 'sell',
|
// sell: 'sell',
|
||||||
s: 'sensors',
|
s: 'sensors',
|
||||||
settings: 'settings',
|
// settings: 'settings',
|
||||||
sb: 'Shield Booster',
|
sb: 'Shield Booster',
|
||||||
scb: 'Shield Cell Bank',
|
scb: 'Shield Cell Bank',
|
||||||
sg: 'Shield Generator',
|
sg: 'Shield Generator',
|
||||||
shields: 'shields',
|
// shields: 'shields',
|
||||||
ship: 'ship',
|
// ship: 'ship',
|
||||||
ships: 'ships',
|
// ships: 'ships',
|
||||||
shortened: 'shortened',
|
// shortened: 'shortened',
|
||||||
size: 'size',
|
// size: 'size',
|
||||||
skip: 'skip',
|
// skip: 'skip',
|
||||||
small: 'small',
|
// small: 'small',
|
||||||
speed: 'speed',
|
// speed: 'speed',
|
||||||
standard: 'standard',
|
// standard: 'standard',
|
||||||
'Standard Docking Computer': 'Standard Docking Computer',
|
// 'Standard Docking Computer': 'Standard Docking Computer',
|
||||||
Stock: 'Stock',
|
// Stock: 'Stock',
|
||||||
SYS: 'SYS',
|
// SYS: 'SYS',
|
||||||
T: 'T',
|
// T: 'T',
|
||||||
T_LOAD: 't-load',
|
T_LOAD: 't-load',
|
||||||
'The Retributor': 'The Retributor',
|
// 'The Retributor': 'The Retributor',
|
||||||
t: 'thrusters',
|
t: 'thrusters',
|
||||||
time: 'time',
|
// time: 'time',
|
||||||
tp: 'Torpedo Pylon',
|
tp: 'Torpedo Pylon',
|
||||||
total: 'total',
|
// total: 'total',
|
||||||
'total range': 'total range',
|
// 'total range': 'total range',
|
||||||
turret: 'turret',
|
// turret: 'turret',
|
||||||
type: 'type',
|
// type: 'type',
|
||||||
U: 'U',
|
// U: 'U',
|
||||||
unladen: 'unladen',
|
// unladen: 'unladen',
|
||||||
PHRASE_UPDATE_RDY: 'Update Available! Click to Refresh',
|
PHRASE_UPDATE_RDY: 'Update Available! Click to Refresh',
|
||||||
URL: 'URL',
|
// URL: 'URL',
|
||||||
utility: 'utility',
|
// utility: 'utility',
|
||||||
'utility mounts': 'utility mounts',
|
// 'utility mounts': 'utility mounts',
|
||||||
version: 'version',
|
// version: 'version',
|
||||||
WEP: 'WEP',
|
// WEP: 'WEP',
|
||||||
yes: 'yes',
|
// yes: 'yes',
|
||||||
PHRASE_BACKUP_DESC: 'Backup of all Coriolis data to save or transfer to another browser/device'
|
PHRASE_BACKUP_DESC: 'Backup of all Coriolis data to save or transfer to another browser/device'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -91,7 +91,6 @@ export const terms = {
|
|||||||
fc: 'Осколочное Орудие',
|
fc: 'Осколочное Орудие',
|
||||||
fd: 'Двигатель FSD',
|
fd: 'Двигатель FSD',
|
||||||
ws: 'FSD Сканнер',
|
ws: 'FSD Сканнер',
|
||||||
|
|
||||||
fi: 'Перехватчик FSD',
|
fi: 'Перехватчик FSD',
|
||||||
fuel: 'Топливо',
|
fuel: 'Топливо',
|
||||||
fs: 'Топливосборщик',
|
fs: 'Топливосборщик',
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default class ComparisonPage extends Page {
|
|||||||
_sortShips(shipPredicate, shipPredicateIndex) {
|
_sortShips(shipPredicate, shipPredicateIndex) {
|
||||||
let shipDesc = this.state.shipDesc;
|
let shipDesc = this.state.shipDesc;
|
||||||
|
|
||||||
if (typeof shipPredicateIndex == "object") {
|
if (typeof shipPredicateIndex == 'object') {
|
||||||
shipPredicateIndex = undefined;
|
shipPredicateIndex = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,79 +137,79 @@ export default class ComparisonPage extends Page {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'page'}>
|
<div className={'page'}>
|
||||||
<table id="comparison">
|
{/*<table id='comparison'>
|
||||||
<tr ng-show="compareMode">
|
<tr ng-show='compareMode'>
|
||||||
<td class="head" translate="comparison"></td>
|
<td class='head' translate='comparison'></td>
|
||||||
<td>
|
<td>
|
||||||
<input ng-model="name" ng-change="nameChange()" placeholder="{{'enter name' | translate}}" maxlength="50" />
|
<input ng-model='name' ng-change='nameChange()' placeholder={translate('enter name')} maxlength={50} />
|
||||||
<button ng-click="save()" ng-disabled="!name || name == 'all' || saved">
|
<button ng-click='save()'disabled{!name || name == 'all' || saved}>
|
||||||
<svg class="icon lg "><use xlink:href="#floppy-disk"></use></svg><span class="button-lbl"> {{'save' | translate}}</span>
|
<svg class='icon lg '><use xlink:href='#floppy-disk'></use></svg><span class='button-lbl'> {{'save' | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
<button ng-click="delete()" ng-disabled="name == 'all' || !saved"><svg class="icon lg warning "><use xlink:href="#bin"></use></svg></button>
|
<button ng-click='delete()' ng-disabled='name == 'all' || !saved'><svg class='icon lg warning '><use xlink:href='#bin'></use></svg></button>
|
||||||
<button ng-click="selectBuilds(true, $event)">
|
<button ng-click='selectBuilds(true, $event)'>
|
||||||
<svg class="icon lg "><use xlink:href="#rocket"></use></svg><span class="button-lbl"> {{'builds' | translate}}</span>
|
<svg class='icon lg '><use xlink:href='#rocket'></use></svg><span class='button-lbl'> {{'builds' | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="r" ng-click="permalink($event)" ng-disabled="builds.length == 0">
|
<button class='r' ng-click='permalink($event)' ng-disabled='builds.length == 0'>
|
||||||
<svg class="icon lg "><use xlink:href="#link"></use></svg><span class="button-lbl"> {{'permalink' | translate}}</span>
|
<svg class='icon lg '><use xlink:href='#link'></use></svg><span class='button-lbl'> {{'permalink' | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="r" ng-click="embed($event)" ng-disabled="builds.length == 0">
|
<button class='r' ng-click='embed($event)' ng-disabled='builds.length == 0'>
|
||||||
<svg class="icon lg "><use xlink:href="#embed"></use></svg><span class="button-lbl"> {{'forum' | translate}}</span>
|
<svg class='icon lg '><use xlink:href='#embed'></use></svg><span class='button-lbl'> {{'forum' | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-show="!compareMode">
|
<tr ng-show='!compareMode'>
|
||||||
<td class="head" translate="comparison"></td>
|
<td class='head' translate='comparison'></td>
|
||||||
<td>
|
<td>
|
||||||
<h3 ng-bind="name"></h3>
|
<h3 ng-bind='name'></h3>
|
||||||
<button class="r" ui-sref="modal.import({obj:importObj})"><svg class="icon lg "><use xlink:href="#download"></use></svg> {{'import' | translate}}</button>
|
<button class='r' ui-sref='modal.import({obj:importObj})'><svg class='icon lg '><use xlink:href='#download'></use></svg> {{'import' | translate}}</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="head" translate="compare"></td>
|
<td class='head' translate='compare'></td>
|
||||||
<td>
|
<td>
|
||||||
<ul id="facet-container" as-sortable="facetSortOpts" ng-model="facets" class="sortable" update="tblUpdate">
|
<ul id='facet-container' as-sortable='facetSortOpts' ng-model='facets' class='sortable' update='tblUpdate'>
|
||||||
<li ng-repeat="(i,f) in facets" as-sortable-item class="facet" ng-class="{active: f.active}" ng-click="toggleFacet(i)">
|
<li ng-repeat='(i,f) in facets' as-sortable-item class='facet' ng-class='{active: f.active}' ng-click='toggleFacet(i)'>
|
||||||
<div as-sortable-item-handle>↔ <span ng-bind="f.title | translate"></span></div>
|
<div as-sortable-item-handle>↔ <span ng-bind='f.title | translate'></span></div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="scroll-x">
|
<div class='scroll-x'>
|
||||||
<table id="comp-tbl" comparison-table ng-click="handleClick($event)"></table>
|
<table id='comp-tbl' comparison-table ng-click='handleClick($event)'></table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-repeat="f in facets | filter:{active:true}" ng-if="builds.length > 0" class="chart" bar-chart facet="f" data="builds">
|
<div ng-repeat='f in facets | filter:{active:true}' ng-if='builds.length > 0' class='chart' bar-chart facet='f' data='builds'>
|
||||||
<h3 ng-click="sort(f.props[0])" >{{f.title | translate}}</h3>
|
<h3 ng-click='sort(f.props[0])' >{{f.title | translate}}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-bg" ng-show="showBuilds" ng-click="selectBuilds(false, $event)">
|
<div class='modal-bg' ng-show='showBuilds' ng-click='selectBuilds(false, $event)'>
|
||||||
<div class="modal" ui-view="modal-content" ng-click="$event.stopPropagation()">
|
<div class='modal' ui-view='modal-content' ng-click='$event.stopPropagation()'>
|
||||||
<h3 translate="PHRASE_SELECT_BUILDS"></h3>
|
<h3 translate='PHRASE_SELECT_BUILDS'></h3>
|
||||||
<div id="build-select">
|
<div id='build-select'>
|
||||||
<table>
|
<table>
|
||||||
<thead><tr><th colspan="2" translate="available"></th></tr></thead>
|
<thead><tr><th colspan='2' translate='available'></th></tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="b in unusedBuilds | orderBy:'name'" ng-click="addBuild(b.id, b.buildName)">
|
<tr ng-repeat='b in unusedBuilds | orderBy:'name'' ng-click='addBuild(b.id, b.buildName)'>
|
||||||
<td class="tl" ng-bind="b.name"></td><td class="tl" ng-bind="b.buildName"></td>
|
<td class='tl' ng-bind='b.name'></td><td class='tl' ng-bind='b.buildName'></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<h1>⇆</h1>
|
<h1>⇆</h1>
|
||||||
<table>
|
<table>
|
||||||
<thead><tr><th colspan="2" translate="added"></th></tr></thead>
|
<thead><tr><th colspan='2' translate='added'></th></tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="b in builds | orderBy:'name'" ng-click="removeBuild(b.id, b.buildName)">
|
<tr ng-repeat='b in builds | orderBy:'name'' ng-click='removeBuild(b.id, b.buildName)'>
|
||||||
<td class="tl" ng-bind="b.name"></td><td class="tl" ng-bind="b.buildName"></td>
|
<td class='tl' ng-bind='b.name'></td><td class='tl' ng-bind='b.buildName'></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<button class="r dismiss cap" ng-click="selectBuilds(false, $event)" translate="done"></button>
|
<button class='r dismiss cap' ng-click='selectBuilds(false, $event)' translate='done'></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,34 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Page from './Page';
|
import Page from './Page';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
|
import Router from '../Router';
|
||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
import Ships from '../shipyard/Ships';
|
import InterfaceEvents from '../utils/InterfaceEvents';
|
||||||
|
import { Ships } from 'coriolis-data';
|
||||||
import Ship from '../shipyard/Ship';
|
import Ship from '../shipyard/Ship';
|
||||||
import { toShip } from '../shipyard/Serializer';
|
import { toDetailedBuild } from '../shipyard/Serializer';
|
||||||
import { FloppyDisk, Bin, Switch, Download, Reload } from '../components/SvgIcons';
|
import { FloppyDisk, Bin, Switch, Download, Reload } from '../components/SvgIcons';
|
||||||
import ShipSummaryTable from '../components/ShipSummaryTable';
|
import ShipSummaryTable from '../components/ShipSummaryTable';
|
||||||
import StandardSlotSection from '../components/StandardSlotSection';
|
import StandardSlotSection from '../components/StandardSlotSection';
|
||||||
import HardpointsSlotSection from '../components/HardpointsSlotSection';
|
import HardpointsSlotSection from '../components/HardpointsSlotSection';
|
||||||
import InternalSlotSection from '../components/InternalSlotSection';
|
import InternalSlotSection from '../components/InternalSlotSection';
|
||||||
import UtilitySlotSection from '../components/UtilitySlotSection';
|
import UtilitySlotSection from '../components/UtilitySlotSection';
|
||||||
|
import LineChart from '../components/LineChart';
|
||||||
|
import PowerManagement from '../components/PowerManagement';
|
||||||
|
import CostSection from '../components/CostSection';
|
||||||
|
import ModalExport from '../components/ModalExport';
|
||||||
|
|
||||||
|
const SPEED_SERIES = ['boost', '4 Pips', '2 Pips', '0 Pips'];
|
||||||
|
const SPEED_COLORS = ['#0088d2', '#ff8c0d', '#D26D00', '#c06400'];
|
||||||
|
|
||||||
export default class OutfittingPage extends Page {
|
export default class OutfittingPage extends Page {
|
||||||
|
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this._initState(props, context);
|
this.state = this._initState(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
_initState(props, context) {
|
_initState(context) {
|
||||||
let params = context.route.params;
|
let params = context.route.params;
|
||||||
let shipId = params.ship;
|
let shipId = params.ship;
|
||||||
let code = params.code;
|
let code = params.code;
|
||||||
@@ -34,22 +43,13 @@ export default class OutfittingPage extends Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let ship = new Ship(shipId, data.properties, data.slots); // Create a new Ship instance
|
let ship = new Ship(shipId, data.properties, data.slots); // Create a new Ship instance
|
||||||
let retrofitShip = new Ship(shipId, data.properties, data.slots); // Create a new Ship for retrofit comparison
|
|
||||||
|
|
||||||
if (code) {
|
if (code) {
|
||||||
toShip(ship, code); // Populate components from 'code' URL param
|
ship.buildFrom(code); // Populate modules from serialized 'code' URL param
|
||||||
} else {
|
} else {
|
||||||
ship.buildWith(data.defaults); // Populate with default components
|
ship.buildWith(data.defaults); // Populate with default components
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savedCode) {
|
|
||||||
toShip(retrofitShip, savedCode); // Populate components from last save
|
|
||||||
} else {
|
|
||||||
retrofitShip.buildWith(data.defaults);
|
|
||||||
}
|
|
||||||
|
|
||||||
//this.applyDiscounts();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: 'Outfitting - ' + data.properties.name,
|
title: 'Outfitting - ' + data.properties.name,
|
||||||
costTab: Persist.getCostTab() || 'costs',
|
costTab: Persist.getCostTab() || 'costs',
|
||||||
@@ -57,43 +57,94 @@ export default class OutfittingPage extends Page {
|
|||||||
shipId,
|
shipId,
|
||||||
ship,
|
ship,
|
||||||
code,
|
code,
|
||||||
savedCode,
|
savedCode
|
||||||
retrofitShip,
|
|
||||||
retrofitBuild: savedCode ? buildName : null
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_applyDiscounts() {
|
_buildNameChange(event) {
|
||||||
this.state.ship.applyDiscounts(Persist.getShipDiscount(), Persist.getComponentDiscount());
|
let stateChanges = {
|
||||||
this.state.retrofitShip.applyDiscounts(Persist.getShipDiscount(), Persist.getComponentDiscount());
|
buildName: event.target.value
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Persist.hasBuild(this.state.shipId, stateChanges.buildName)) {
|
||||||
|
stateChanges.savedCode = Persist.getBuild(this.state.shipId, stateChanges.buildName);
|
||||||
|
} else {
|
||||||
|
stateChanges.savedCode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState(stateChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildNameChange() {
|
_saveBuild() {
|
||||||
|
let code = this.state.ship.toString();
|
||||||
|
Persist.saveBuild(this.state.shipId, this.state.buildName, code);
|
||||||
|
this.setState({ code, savedCode: code});
|
||||||
}
|
}
|
||||||
|
|
||||||
_saveBuild() {}
|
_reloadBuild() {
|
||||||
|
this.state.ship.buildFrom(this.state.savedCode);
|
||||||
|
this._shipUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
_reloadBuild() {}
|
_resetBuild() {
|
||||||
|
this.state.ship.buildWith(Ships[this.state.shipId].defaults);
|
||||||
|
this._shipUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
_resetBuild() {}
|
_deleteBuild() {
|
||||||
|
Persist.deleteBuild(this.state.shipId, this.state.buildName);
|
||||||
|
Router.go(`/outfit/${this.state.shipId}`);
|
||||||
|
}
|
||||||
|
|
||||||
_exportBuild() {}
|
_exportBuild() {
|
||||||
|
let translate = this.context.language.translate;
|
||||||
|
let {buildName, ship } = this.state;
|
||||||
|
InterfaceEvents.showModal(<ModalExport
|
||||||
|
title={buildName + ' ' + translate('export')}
|
||||||
|
description={translate('PHRASE_EXPORT_DESC')}
|
||||||
|
data={toDetailedBuild(buildName, ship, ship.toString())}
|
||||||
|
/>);
|
||||||
|
}
|
||||||
|
|
||||||
|
_shipUpdated() {
|
||||||
|
let { shipId, buildName } = this.state;
|
||||||
|
let newCode = this.state.ship.toString();
|
||||||
|
this._updateRoute(shipId, newCode, buildName);
|
||||||
|
this.setState({ code: newCode });
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateRoute(shipId, code, buildName) {
|
||||||
|
let qStr = '';
|
||||||
|
|
||||||
|
if (buildName) {
|
||||||
|
qStr = '?bn=' + encodeURIComponent(buildName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Router.replace(`/outfit/${shipId}/${code}${qStr}`);
|
||||||
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps, nextContext) {
|
componentWillReceiveProps(nextProps, nextContext) {
|
||||||
this.setState(this._initState(nextProps, nextContext));
|
if (this.context.route !== nextContext.route) { // Only reinit state if the route has changed
|
||||||
|
this.setState(this._initState(nextContext));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let translate = this.context.language.translate;
|
let { translate, units } = this.context.language;
|
||||||
let { ship, code, savedCode, buildName } = this.state;
|
let state = this.state;
|
||||||
|
let { ship, code, savedCode, buildName } = state;
|
||||||
|
let menu = this.props.currentMenu;
|
||||||
|
let shipUpdated = this._shipUpdated;
|
||||||
|
let hStr = ship.getHardpointsString();
|
||||||
|
let sStr = ship.getStandardString();
|
||||||
|
let iStr = ship.getInternalString();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id='outfit' className={'page'}>
|
<div id='outfit' className={'page'}>
|
||||||
<div id='overview'>
|
<div id='overview'>
|
||||||
<h1>{ship.name}</h1>
|
<h1>{ship.name}</h1>
|
||||||
<div id='build'>
|
<div id='build'>
|
||||||
<input value={buildName} onChange={this._buildNameChange} placeholder={translate('enter name')} maxsize={50} />
|
<input value={buildName} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxsize={50} />
|
||||||
<button onClick={this._saveBuild} disabled={!buildName || savedCode && code == savedCode}>
|
<button onClick={this._saveBuild} disabled={!buildName || savedCode && code == savedCode}>
|
||||||
<FloppyDisk className='lg'/><span className='button-lbl'>{translate('save')}</span>
|
<FloppyDisk className='lg'/><span className='button-lbl'>{translate('save')}</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -112,20 +163,65 @@ export default class OutfittingPage extends Page {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ShipSummaryTable ship={ship} />
|
<ShipSummaryTable ship={ship} code={code} />
|
||||||
|
|
||||||
<StandardSlotSection ship={ship} />
|
<StandardSlotSection ship={ship} code={sStr} onChange={shipUpdated} currentMenu={menu} />
|
||||||
<InternalSlotSection ship={ship} />
|
<InternalSlotSection ship={ship} code={iStr} onChange={shipUpdated} currentMenu={menu} />
|
||||||
<HardpointsSlotSection ship={ship} />
|
<HardpointsSlotSection ship={ship} code={hStr} onChange={shipUpdated} currentMenu={menu} />
|
||||||
<UtilitySlotSection ship={ship} />
|
<UtilitySlotSection ship={ship} code={hStr} onChange={shipUpdated} currentMenu={menu} />
|
||||||
|
|
||||||
Component Priorities & Power
|
<PowerManagement ship={ship} code={code} onChange={shipUpdated} />
|
||||||
|
<CostSection ship={ship} shipId={state.shipId} buildName={buildName} code={sStr + hStr + iStr} />
|
||||||
|
|
||||||
Cost/Pricing List
|
<div className='group third'>
|
||||||
|
<h1>{translate('jump range')}</h1>
|
||||||
|
<LineChart
|
||||||
|
xMax={ship.cargoCapacity}
|
||||||
|
yMax={ship.unladenRange}
|
||||||
|
xUnit={units.T}
|
||||||
|
yUnit={units.LY}
|
||||||
|
yLabel={translate('jump range')}
|
||||||
|
xLabel={translate('cargo')}
|
||||||
|
func={state.jumpRangeChartFunc}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='group third'>
|
||||||
|
<h1>{translate('total range')}</h1>
|
||||||
|
<LineChart
|
||||||
|
xMax={ship.cargoCapacity}
|
||||||
|
yMax={ship.boostSpeed}
|
||||||
|
xUnit={units.T}
|
||||||
|
yUnit={units.ms}
|
||||||
|
yLabel={translate('total range')}
|
||||||
|
xLabel={translate('cargo')}
|
||||||
|
func={state.totalRangeChartFunc}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='group third'>
|
||||||
|
<h1>{translate('speed')}</h1>
|
||||||
|
<LineChart
|
||||||
|
xMax={ship.cargoCapacity}
|
||||||
|
yMax={ship.boostSpeed}
|
||||||
|
xUnit={units.T}
|
||||||
|
yUnit={units.ms}
|
||||||
|
yLabel={translate('speed')}
|
||||||
|
series={SPEED_SERIES}
|
||||||
|
color={SPEED_COLORS}
|
||||||
|
xLabel={translate('cargo')}
|
||||||
|
func={state.speedChartFunc}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/*
|
||||||
|
<div class='group half'>
|
||||||
|
<div slider max='ship.fuelCapacity' unit=''T'' on-change='::fuelChange(val)' style='position:relative; margin: 0 auto;'>
|
||||||
|
<svg class='icon xl primary-disabled' style='position:absolute;height: 100%;'><use xlink:href='#fuel'></use></svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
*/}
|
||||||
|
|
||||||
Jump Range Chart
|
|
||||||
Total Range Chart
|
|
||||||
Speed Chart
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,16 @@ export default class Page extends React.Component {
|
|||||||
language: React.PropTypes.object.isRequired
|
language: React.PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
currentMenu: React.PropTypes.any
|
||||||
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
// Autobind private functions
|
// Autobind private functions
|
||||||
Object.getOwnPropertyNames(this.constructor.prototype).forEach(prop => {
|
Object.getOwnPropertyNames(this.constructor.prototype).forEach(prop => {
|
||||||
if(prop.charAt(0) == '_' && typeof this[prop] === "function") {
|
if(prop.charAt(0) == '_' && typeof this[prop] === 'function') {
|
||||||
this[prop] = this[prop].bind(this);
|
this[prop] = this[prop].bind(this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Page from './Page';
|
import Page from './Page';
|
||||||
import Ships from '../shipyard/Ships';
|
import { Ships } from 'coriolis-data';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import Ship from '../shipyard/Ship';
|
import Ship from '../shipyard/Ship';
|
||||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||||
@@ -19,6 +19,8 @@ function countInt(slot) {
|
|||||||
this.maxCargo += crEligible ? ModuleUtils.findInternal('cr', slot.maxClass, 'E').capacity : 0;
|
this.maxCargo += crEligible ? ModuleUtils.findInternal('cr', slot.maxClass, 'E').capacity : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let cachedShipSummaries = null;
|
||||||
|
|
||||||
export default class ShipyardPage extends Page {
|
export default class ShipyardPage extends Page {
|
||||||
|
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
@@ -26,14 +28,18 @@ export default class ShipyardPage extends Page {
|
|||||||
this.state = {
|
this.state = {
|
||||||
title: 'Coriolis - Shipyard',
|
title: 'Coriolis - Shipyard',
|
||||||
shipPredicate: 'name',
|
shipPredicate: 'name',
|
||||||
shipDesc: false
|
shipDesc: true
|
||||||
};
|
};
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.shipSummaries = [];
|
|
||||||
|
|
||||||
for (let s in Ships) {
|
if (!cachedShipSummaries) {
|
||||||
this.shipSummaries.push(this._shipSummary(s, Ships[s]));
|
cachedShipSummaries = [];
|
||||||
|
for (let s in Ships) {
|
||||||
|
cachedShipSummaries.push(this._shipSummary(s, Ships[s]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.shipSummaries = cachedShipSummaries;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,7 +49,7 @@ export default class ShipyardPage extends Page {
|
|||||||
_sortShips(shipPredicate, shipPredicateIndex) {
|
_sortShips(shipPredicate, shipPredicateIndex) {
|
||||||
let shipDesc = this.state.shipDesc;
|
let shipDesc = this.state.shipDesc;
|
||||||
|
|
||||||
if (typeof shipPredicateIndex == "object") {
|
if (typeof shipPredicateIndex == 'object') {
|
||||||
shipPredicateIndex = undefined;
|
shipPredicateIndex = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,11 +61,6 @@ export default class ShipyardPage extends Page {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_shipSummary(shipId, shipData) {
|
_shipSummary(shipId, shipData) {
|
||||||
let language = this.context.language;
|
|
||||||
let u = language.units;
|
|
||||||
let fInt = language.formats.int;
|
|
||||||
let fRound = language.formats.round;
|
|
||||||
let translate = language.translate;
|
|
||||||
let summary = {
|
let summary = {
|
||||||
id: shipId,
|
id: shipId,
|
||||||
hpCount: 0,
|
hpCount: 0,
|
||||||
@@ -81,24 +82,23 @@ export default class ShipyardPage extends Page {
|
|||||||
ship.optimizeMass({ th: ship.standard[1].maxClass + 'A' }); // Optmize mass with Max Thrusters
|
ship.optimizeMass({ th: ship.standard[1].maxClass + 'A' }); // Optmize mass with Max Thrusters
|
||||||
summary.topSpeed = ship.topSpeed;
|
summary.topSpeed = ship.topSpeed;
|
||||||
summary.topBoost = ship.topBoost;
|
summary.topBoost = ship.topBoost;
|
||||||
summary.rowElement = this._shipRowElement(summary, translate, u, fInt, fRound);
|
|
||||||
|
|
||||||
return summary;
|
return summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
_shipRowElement(s, translate, u, fInt, fRound) {
|
_shipRowElement(s, translate, u, fInt, fRound) {
|
||||||
return <tr key={s.id} className={'highlight'}>
|
return <tr key={s.id} className='highlight'>
|
||||||
<td className={'le'}><Link href={'/outfitting/' + s.id}>{s.name}</Link></td>
|
<td className='le'><Link href={'/outfit/' + s.id}>{s.name}</Link></td>
|
||||||
<td className={'le'}>{s.manufacturer}</td>
|
<td className='le'>{s.manufacturer}</td>
|
||||||
<td className={'cap'}>{translate(SizeMap[s.class])}</td>
|
<td className='cap'>{translate(SizeMap[s.class])}</td>
|
||||||
<td className={'ri'}>{fInt(s.speed)}{u.ms}</td>
|
<td className='ri'>{fInt(s.speed)}{u.ms}</td>
|
||||||
<td className={'ri'}>{fInt(s.boost)}{u.ms}</td>
|
<td className='ri'>{fInt(s.boost)}{u.ms}</td>
|
||||||
<td className={'ri'}>{s.baseArmour}</td>
|
<td className='ri'>{s.baseArmour}</td>
|
||||||
<td className={'ri'}>{fInt(s.baseShieldStrength)}{u.MJ}</td>
|
<td className='ri'>{fInt(s.baseShieldStrength)}{u.MJ}</td>
|
||||||
<td className={'ri'}>{fInt(s.topSpeed)}{u.ms}</td>
|
<td className='ri'>{fInt(s.topSpeed)}{u.ms}</td>
|
||||||
<td className={'ri'}>{fInt(s.topBoost)}{u.ms}</td>
|
<td className='ri'>{fInt(s.topBoost)}{u.ms}</td>
|
||||||
<td className={'ri'}>{fRound(s.maxJumpRange)}{u.LY}</td>
|
<td className='ri'>{fRound(s.maxJumpRange)}{u.LY}</td>
|
||||||
<td className={'ri'}>{fInt(s.maxCargo)}{u.T}</td>
|
<td className='ri'>{fInt(s.maxCargo)}{u.T}</td>
|
||||||
<td className={cn({ disabled: !s.hp[1] })}>{s.hp[1]}</td>
|
<td className={cn({ disabled: !s.hp[1] })}>{s.hp[1]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[2] })}>{s.hp[2]}</td>
|
<td className={cn({ disabled: !s.hp[2] })}>{s.hp[2]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td>
|
<td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td>
|
||||||
@@ -112,31 +112,16 @@ export default class ShipyardPage extends Page {
|
|||||||
<td className={cn({ disabled: !s.int[5] })}>{s.int[5]}</td>
|
<td className={cn({ disabled: !s.int[5] })}>{s.int[5]}</td>
|
||||||
<td className={cn({ disabled: !s.int[6] })}>{s.int[6]}</td>
|
<td className={cn({ disabled: !s.int[6] })}>{s.int[6]}</td>
|
||||||
<td className={cn({ disabled: !s.int[7] })}>{s.int[7]}</td>
|
<td className={cn({ disabled: !s.int[7] })}>{s.int[7]}</td>
|
||||||
<td className={'ri'}>{fInt(s.hullMass)}{u.T}</td>
|
<td className='ri'>{fInt(s.hullMass)}{u.T}</td>
|
||||||
<td className={'ri'}>{s.masslock}</td>
|
<td className='ri'>{s.masslock}</td>
|
||||||
<td className={'ri'}>{fInt(s.retailCost)}{u.CR}</td>
|
<td className='ri'>{fInt(s.retailCost)}{u.CR}</td>
|
||||||
</tr>;
|
</tr>;
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderSummaries(language) {
|
|
||||||
let fInt = language.formats.int;
|
|
||||||
let fRound = language.formats.round;
|
|
||||||
let translate = language.translate;
|
|
||||||
let u = language.units;
|
|
||||||
// Regenerate ship rows on prop change
|
|
||||||
for (let s of this.shipSummaries) {
|
|
||||||
s.rowElement = this._shipRowElement(s, translate, u, fInt, fRound);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUpdate(nextProps, nextState, nextContext) {
|
|
||||||
if (this.context.language !== nextContext.language) {
|
|
||||||
this._renderSummaries(language);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let translate = this.context.language.translate;
|
let { translate, formats, units } = this.context.language;
|
||||||
|
let fInt = formats.int;
|
||||||
|
let fRound = formats.round;
|
||||||
let shipSummaries = this.shipSummaries;
|
let shipSummaries = this.shipSummaries;
|
||||||
let shipPredicate = this.state.shipPredicate;
|
let shipPredicate = this.state.shipPredicate;
|
||||||
let shipPredicateIndex = this.state.shipPredicateIndex;
|
let shipPredicateIndex = this.state.shipPredicateIndex;
|
||||||
@@ -172,52 +157,52 @@ export default class ShipyardPage extends Page {
|
|||||||
});
|
});
|
||||||
|
|
||||||
for (let s of shipSummaries) {
|
for (let s of shipSummaries) {
|
||||||
shipRows.push(s.rowElement);
|
shipRows.push(this._shipRowElement(s, translate, units, fInt, fRound));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'page'}>
|
<div className='page'>
|
||||||
<div className={'scroll-x'}>
|
<div className='scroll-x'>
|
||||||
<table style={{ fontSize:'0.85em', whiteSpace:'nowrap', margin: '0 auto' }} align='center'>
|
<table style={{ fontSize:'0.85em', whiteSpace:'nowrap', margin: '0 auto' }} align='center'>
|
||||||
<thead>
|
<thead>
|
||||||
<tr className={'main'}>
|
<tr className='main'>
|
||||||
<th rowSpan={2} className={'sortable le'} onClick={sortShips('name')}>{translate('ship')}</th>
|
<th rowSpan={2} className='sortable le' onClick={sortShips('name')}>{translate('ship')}</th>
|
||||||
<th rowSpan={2} className={'sortable'} onClick={sortShips('manufacturer')}>{translate('manufacturer')}</th>
|
<th rowSpan={2} className='sortable' onClick={sortShips('manufacturer')}>{translate('manufacturer')}</th>
|
||||||
<th rowSpan={2} className={'sortable'} onClick={sortShips('class')}>{translate('size')}</th>
|
<th rowSpan={2} className='sortable' onClick={sortShips('class')}>{translate('size')}</th>
|
||||||
<th colSpan={4}>{translate('base')}</th>
|
<th colSpan={4}>{translate('base')}</th>
|
||||||
<th colSpan={4}>{translate('max')}</th>
|
<th colSpan={4}>{translate('max')}</th>
|
||||||
<th colSpan={5} className={'sortable'} onClick={sortShips('hpCount')}>{translate('hardpoints')}</th>
|
<th colSpan={5} className='sortable' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th>
|
||||||
<th colSpan={8} className={'sortable'} onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
|
<th colSpan={8} className='sortable' onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
|
||||||
<th rowSpan={2} className={'sortable'} onClick={sortShips('hullMass')}>{translate('hull')}</th>
|
<th rowSpan={2} className='sortable' onClick={sortShips('hullMass')}>{translate('hull')}</th>
|
||||||
<th rowSpan={2} className={'sortable'} onClick={sortShips('masslock')}>{translate('MLF')}</th>
|
<th rowSpan={2} className='sortable' onClick={sortShips('masslock')}>{translate('MLF')}</th>
|
||||||
<th rowSpan={2} className={'sortable'} onClick={sortShips('retailCost')}>{translate('cost')}</th>
|
<th rowSpan={2} className='sortable' onClick={sortShips('retailCost')}>{translate('cost')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
{/* Base */}
|
{/* Base */}
|
||||||
<th className={'sortable lft'} onClick={sortShips('speed')}>{translate('speed')}</th>
|
<th className='sortable lft' onClick={sortShips('speed')}>{translate('speed')}</th>
|
||||||
<th className={'sortable'} onClick={sortShips('boost')}>{translate('boost')}</th>
|
<th className='sortable' onClick={sortShips('boost')}>{translate('boost')}</th>
|
||||||
<th className={'sortable'} onClick={sortShips('baseArmour')}>{translate('armour')}</th>
|
<th className='sortable' onClick={sortShips('baseArmour')}>{translate('armour')}</th>
|
||||||
<th className={'sortable'} onClick={sortShips('baseShieldStrength')}>{translate('shields')}</th>
|
<th className='sortable' onClick={sortShips('baseShieldStrength')}>{translate('shields')}</th>
|
||||||
{/* Max */}
|
{/* Max */}
|
||||||
<th className={'sortable lft'} onClick={sortShips('topSpeed')}>{translate('speed')}</th>
|
<th className='sortable lft' onClick={sortShips('topSpeed')}>{translate('speed')}</th>
|
||||||
<th className={'sortable'} onClick={sortShips('topBoost')}>{translate('boost')}</th>
|
<th className='sortable' onClick={sortShips('topBoost')}>{translate('boost')}</th>
|
||||||
<th className={'sortable'} onClick={sortShips('maxJumpRange')}>{translate('jump')}</th>
|
<th className='sortable' onClick={sortShips('maxJumpRange')}>{translate('jump')}</th>
|
||||||
<th className={'sortable'} onClick={sortShips('maxCargo')}>{translate('cargo')}</th>
|
<th className='sortable' onClick={sortShips('maxCargo')}>{translate('cargo')}</th>
|
||||||
{/* Hardpoints */}
|
{/* Hardpoints */}
|
||||||
<th className={'sortable lft'} onClick={sortShips('hp',1)}>{translate('S')}</th>
|
<th className='sortable lft' onClick={sortShips('hp',1)}>{translate('S')}</th>
|
||||||
<th className={'sortable'} onClick={sortShips('hp', 2)}>{translate('M')}</th>
|
<th className='sortable' onClick={sortShips('hp', 2)}>{translate('M')}</th>
|
||||||
<th className={'sortable'} onClick={sortShips('hp', 3)}>{translate('L')}</th>
|
<th className='sortable' onClick={sortShips('hp', 3)}>{translate('L')}</th>
|
||||||
<th className={'sortable'} onClick={sortShips('hp', 4)}>{translate('H')}</th>
|
<th className='sortable' onClick={sortShips('hp', 4)}>{translate('H')}</th>
|
||||||
<th className={'sortable'} onClick={sortShips('hp', 0)}>{translate('U')}</th>
|
<th className='sortable' onClick={sortShips('hp', 0)}>{translate('U')}</th>
|
||||||
{/* Internal */}
|
{/* Internal */}
|
||||||
<th className={'sortable lft'} onClick={sortShips('int', 0)} >1</th>
|
<th className='sortable lft' onClick={sortShips('int', 0)} >1</th>
|
||||||
<th className={'sortable'} onClick={sortShips('int', 1)} >2</th>
|
<th className='sortable' onClick={sortShips('int', 1)} >2</th>
|
||||||
<th className={'sortable'} onClick={sortShips('int', 2)} >3</th>
|
<th className='sortable' onClick={sortShips('int', 2)} >3</th>
|
||||||
<th className={'sortable'} onClick={sortShips('int', 3)} >4</th>
|
<th className='sortable' onClick={sortShips('int', 3)} >4</th>
|
||||||
<th className={'sortable'} onClick={sortShips('int', 4)} >5</th>
|
<th className='sortable' onClick={sortShips('int', 4)} >5</th>
|
||||||
<th className={'sortable'} onClick={sortShips('int', 5)} >6</th>
|
<th className='sortable' onClick={sortShips('int', 5)} >6</th>
|
||||||
<th className={'sortable'} onClick={sortShips('int', 6)} >7</th>
|
<th className='sortable' onClick={sortShips('int', 6)} >7</th>
|
||||||
<th className={'sortable'} onClick={sortShips('int', 7)} >8</th>
|
<th className='sortable' onClick={sortShips('int', 7)} >8</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
@@ -1,641 +0,0 @@
|
|||||||
angular.module('app').controller('OutfitController', ['$window', '$rootScope', '$scope', '$state', '$stateParams', '$translate', 'ShipsDB', 'Ship', 'Components', 'Serializer', 'Persist', 'calcTotalRange', 'calcSpeed', function($window, $rootScope, $scope, $state, $p, $translate, Ships, Ship, Components, Serializer, Persist, calcTotalRange, calcSpeed) {
|
|
||||||
var win = angular.element($window); // Angularized window object for event triggering
|
|
||||||
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 retrofitShip = new Ship($p.shipId, data.properties, data.slots); // Create a new Ship for retrofit comparison
|
|
||||||
|
|
||||||
// Update the ship instance with the code (if provided) or the 'factory' defaults.
|
|
||||||
if ($p.code) {
|
|
||||||
Serializer.toShip(ship, $p.code); // Populate components from 'code' URL param
|
|
||||||
$scope.code = $p.code;
|
|
||||||
} else {
|
|
||||||
ship.buildWith(data.defaults); // Populate with default components
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.buildName = $p.bn ? $window.decodeURIComponent($p.bn) : null;
|
|
||||||
$scope.ships = Ships;
|
|
||||||
$rootScope.title = ship.name + ($scope.buildName ? ' - ' + $scope.buildName : '');
|
|
||||||
$scope.ship = ship;
|
|
||||||
$scope.pp = ship.standard[0]; // Power Plant
|
|
||||||
$scope.th = ship.standard[1]; // Thruster
|
|
||||||
$scope.fsd = ship.standard[2]; // Frame Shrift Drive
|
|
||||||
$scope.ls = ship.standard[3]; // Life Support
|
|
||||||
$scope.pd = ship.standard[4]; // Power Distributor
|
|
||||||
$scope.ss = ship.standard[5]; // Sensors
|
|
||||||
$scope.ft = ship.standard[6]; // Fuel Tank
|
|
||||||
$scope.hps = ship.hardpoints;
|
|
||||||
$scope.internal = ship.internal;
|
|
||||||
$scope.costList = ship.costList;
|
|
||||||
$scope.powerList = ship.powerList;
|
|
||||||
$scope.priorityBands = ship.priorityBands;
|
|
||||||
$scope.availCS = ship.getAvailableComponents();
|
|
||||||
$scope.selectedSlot = null;
|
|
||||||
$scope.savedCode = Persist.getBuild(ship.id, $scope.buildName);
|
|
||||||
$scope.canSave = Persist.isEnabled();
|
|
||||||
$scope.allBuilds = Persist.builds;
|
|
||||||
$scope.fuel = 0;
|
|
||||||
$scope.pwrDesc = false;
|
|
||||||
$scope.pwrPredicate = 'type';
|
|
||||||
$scope.retroDesc = false;
|
|
||||||
$scope.retroPredicate = 'netCost';
|
|
||||||
$scope.costDesc = true;
|
|
||||||
$scope.costPredicate = 'c.cost';
|
|
||||||
$scope.ammoDesc = true;
|
|
||||||
$scope.ammoPredicate = 'ammoUnitCost';
|
|
||||||
$scope.costTab = Persist.getCostTab() || 'costs';
|
|
||||||
|
|
||||||
if ($scope.savedCode) {
|
|
||||||
Serializer.toShip(retrofitShip, $scope.savedCode); // Populate components from last save
|
|
||||||
$scope.retrofitBuild = $scope.buildName;
|
|
||||||
} else {
|
|
||||||
retrofitShip.buildWith(data.defaults);
|
|
||||||
$scope.retrofitBuild = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ship.applyDiscounts($rootScope.discounts.ship, $rootScope.discounts.components);
|
|
||||||
retrofitShip.applyDiscounts($rootScope.discounts.ship, $rootScope.discounts.components);
|
|
||||||
updateRetrofitCosts();
|
|
||||||
|
|
||||||
$scope.jrSeries = {
|
|
||||||
xMin: 0,
|
|
||||||
xMax: ship.cargoCapacity,
|
|
||||||
yMax: ship.unladenRange,
|
|
||||||
yMin: 0,
|
|
||||||
func: function(cargo) { // X Axis is Cargo
|
|
||||||
return ship.getJumpRangeForMass(ship.unladenMass + $scope.fuel + cargo, $scope.fuel);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
$scope.jrChart = {
|
|
||||||
labels: {
|
|
||||||
xAxis: {
|
|
||||||
title: 'cargo',
|
|
||||||
unit: 'T'
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
title: 'jump range',
|
|
||||||
unit: 'LY'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$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: calcSpeed(ship.unladenMass, ship.speed, ship.boost, $scope.th.c, ship.pipSpeed).boost,
|
|
||||||
yMin: 0,
|
|
||||||
series: ['boost', '4 Pips', '2 Pips', '0 Pips'],
|
|
||||||
colors: ['#0088d2', '#ff8c0d', '#D26D00', '#c06400'],
|
|
||||||
func: function(cargo) { // X Axis is Cargo
|
|
||||||
return calcSpeed(ship.unladenMass + $scope.fuel + cargo, ship.speed, ship.boost, $scope.th.c, ship.pipSpeed);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
$scope.speedChart = {
|
|
||||||
labels: {
|
|
||||||
xAxis: {
|
|
||||||
title: 'cargo',
|
|
||||||
unit: 'T'
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
title: 'speed',
|
|
||||||
unit: 'm/s'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 'Opens' a select for component selection.
|
|
||||||
*
|
|
||||||
* @param {[type]} e The event object
|
|
||||||
* @param {[type]} slot The slot that is being 'opened' for selection
|
|
||||||
*/
|
|
||||||
$scope.selectSlot = function(e, slot) {
|
|
||||||
e.stopPropagation();
|
|
||||||
if ($scope.selectedSlot == slot) {
|
|
||||||
$scope.selectedSlot = null;
|
|
||||||
} else {
|
|
||||||
$scope.selectedSlot = slot;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the ships build with the selected component for the
|
|
||||||
* specified slot. Prevents the click event from propagation.
|
|
||||||
*
|
|
||||||
* @param {string} type Shorthand key/string for identifying the slot & component type
|
|
||||||
* @param {[type]} slot The slot object belonging to the ship instance
|
|
||||||
* @param {[type]} e The event object
|
|
||||||
*/
|
|
||||||
$scope.select = function(type, slot, e, id) {
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
if (!id) { // Find component id if not passed
|
|
||||||
var elem = e.target;
|
|
||||||
while (elem && elem !== e.currentTarget && !elem.getAttribute('cpid')) {
|
|
||||||
elem = elem.parentElement;
|
|
||||||
}
|
|
||||||
if (elem) {
|
|
||||||
id = elem.getAttribute('cpid');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id) {
|
|
||||||
if (id == 'empty') {
|
|
||||||
ship.use(slot, null, null);
|
|
||||||
} else if (type == 'h') {
|
|
||||||
ship.use(slot, id, Components.hardpoints(id));
|
|
||||||
} else if (type == 'c') {
|
|
||||||
ship.use(slot, id, Components.standard(ship.standard.indexOf(slot), id));
|
|
||||||
} else if (type == 'i') {
|
|
||||||
ship.use(slot, id, Components.internal(id));
|
|
||||||
} else if (type == 'b') {
|
|
||||||
ship.useBulkhead(id);
|
|
||||||
}
|
|
||||||
$scope.selectedSlot = null;
|
|
||||||
updateState(Serializer.fromShip(ship));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reload the build from the last save.
|
|
||||||
*/
|
|
||||||
$scope.reloadBuild = function() {
|
|
||||||
if ($scope.buildName && $scope.savedCode) {
|
|
||||||
Serializer.toShip(ship, $scope.savedCode); // Repopulate with components from last save
|
|
||||||
updateState($scope.savedCode);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.resetBuild = function() {
|
|
||||||
ship.buildWith(data.defaults); // Populate with default components
|
|
||||||
updateState(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optimize for the lower mass build that can still boost and power the ship
|
|
||||||
* without power management.
|
|
||||||
*/
|
|
||||||
$scope.optimizeMassBuild = function() {
|
|
||||||
updateState(Serializer.fromShip(ship.optimizeMass()));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optimize for the lower mass build that can still boost and power the ship
|
|
||||||
* without power management.
|
|
||||||
*/
|
|
||||||
$scope.optimizeStandard = function() {
|
|
||||||
updateState(Serializer.fromShip(ship.useLightestStandard()));
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.useStandard = function(rating) {
|
|
||||||
updateState(Serializer.fromShip(ship.useStandard(rating)));
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.useHardpoint = function(group, mount, clobber, missile) {
|
|
||||||
updateState(Serializer.fromShip(ship.useWeapon(group, mount, clobber, missile)));
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.useUtility = function(group, rating, clobber) {
|
|
||||||
updateState(Serializer.fromShip(ship.useUtility(group, rating, clobber)));
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.emptyInternal = function() {
|
|
||||||
updateState(Serializer.fromShip(ship.emptyInternal()));
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.emptyHardpoints = function() {
|
|
||||||
updateState(Serializer.fromShip(ship.emptyWeapons()));
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.emptyUtility = function() {
|
|
||||||
updateState(Serializer.fromShip(ship.emptyUtility()));
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.fillWithCargo = function() {
|
|
||||||
ship.internal.forEach(function(slot) {
|
|
||||||
var id = Components.findInternalId('cr', slot.maxClass, 'E');
|
|
||||||
if (!slot.c) {
|
|
||||||
ship.use(slot, id, Components.internal(id));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
updateState(Serializer.fromShip(ship));
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.fillWithCells = function() {
|
|
||||||
var chargeCap = 0; // Capacity of single activation
|
|
||||||
ship.internal.forEach(function(slot) {
|
|
||||||
var id = Components.findInternalId('scb', slot.maxClass, 'A');
|
|
||||||
if (!slot.c && (!slot.eligible || slot.eligible.scb)) { // Check eligibility because of Orca, don't overwrite generator
|
|
||||||
ship.use(slot, id, Components.internal(id));
|
|
||||||
chargeCap += Components.internal(id).recharge;
|
|
||||||
ship.setSlotEnabled(slot, chargeCap <= ship.shieldStrength); // Don't waste cell capacity on overcharge
|
|
||||||
}
|
|
||||||
});
|
|
||||||
updateState(Serializer.fromShip(ship));
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.fillWithArmor = function() {
|
|
||||||
ship.internal.forEach(function(slot) {
|
|
||||||
var hr = Components.findInternal('hr', Math.min(slot.maxClass, 5), 'D'); // Hull reinforcements top out at 5D
|
|
||||||
if (!slot.c && hr) {
|
|
||||||
ship.use(slot, hr.id, hr);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
updateState(Serializer.fromShip(ship));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill all internal slots with Cargo Racks, and optmize internal components.
|
|
||||||
* Hardpoints are not altered.
|
|
||||||
*/
|
|
||||||
$scope.optimizeCargo = function() {
|
|
||||||
ship.internal.forEach(function(slot) {
|
|
||||||
var id = Components.findInternalId('cr', slot.maxClass, 'E');
|
|
||||||
ship.use(slot, id, Components.internal(id));
|
|
||||||
});
|
|
||||||
ship.useLightestStandard();
|
|
||||||
updateState(Serializer.fromShip(ship));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optimize standard and internal components, hardpoints for exploration
|
|
||||||
*/
|
|
||||||
$scope.optimizeExplorer = function() {
|
|
||||||
var intLength = ship.internal.length,
|
|
||||||
heatSinkCount = 2, // Fit 2 heat sinks if possible
|
|
||||||
afmUnitCount = 2, // Fit 2 AFM Units if possible
|
|
||||||
sgSlot,
|
|
||||||
fuelScoopSlot,
|
|
||||||
sgId = $scope.availCS.lightestShieldGenerator(ship.hullMass),
|
|
||||||
sg = Components.internal(sgId);
|
|
||||||
|
|
||||||
ship.setSlotEnabled(ship.cargoHatch, false)
|
|
||||||
.use(ship.internal[--intLength], '2f', Components.internal('2f')) // Advanced Discovery Scanner
|
|
||||||
.use(ship.internal[--intLength], '2i', Components.internal('2i')); // Detailed Surface Scanner
|
|
||||||
|
|
||||||
for (var i = 0; i < intLength; i++) {
|
|
||||||
var slot = ship.internal[i];
|
|
||||||
var nextSlot = (i + 1) < intLength ? ship.internal[i + 1] : null;
|
|
||||||
if (!fuelScoopSlot && (!slot.eligible || slot.eligible.fs)) { // Fit best possible Fuel Scoop
|
|
||||||
var fuelScoopId = Components.findInternalId('fs', slot.maxClass, 'A');
|
|
||||||
fuelScoopSlot = slot;
|
|
||||||
ship.use(fuelScoopSlot, fuelScoopId, Components.internal(fuelScoopId));
|
|
||||||
ship.setSlotEnabled(fuelScoopSlot, true);
|
|
||||||
|
|
||||||
// Mount a Shield generator if possible AND an AFM Unit has been mounted already (Guarantees at least 1 AFM Unit)
|
|
||||||
} else if (!sgSlot && afmUnitCount < 2 && sg.class <= slot.maxClass && (!slot.eligible || slot.eligible.sg) && (!nextSlot || nextSlot.maxClass < sg.class)) {
|
|
||||||
sgSlot = slot;
|
|
||||||
ship.use(sgSlot, sgId, sg);
|
|
||||||
ship.setSlotEnabled(sgSlot, true);
|
|
||||||
} else if (afmUnitCount > 0 && (!slot.eligible || slot.eligible.am)) {
|
|
||||||
afmUnitCount--;
|
|
||||||
var am = Components.findInternal('am', slot.maxClass, afmUnitCount ? 'B' : 'A');
|
|
||||||
ship.use(slot, am.id, am);
|
|
||||||
ship.setSlotEnabled(slot, false); // Disabled power for AFM Unit
|
|
||||||
|
|
||||||
} else {
|
|
||||||
ship.use(slot, null, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ship.hardpoints.forEach(function(s) {
|
|
||||||
if (s.maxClass == 0 && heatSinkCount) { // Mount up to 2 heatsinks
|
|
||||||
ship.use(s, '02', Components.hardpoints('02'));
|
|
||||||
ship.setSlotEnabled(s, heatSinkCount == 2); // Only enable a single Heatsink
|
|
||||||
heatSinkCount--;
|
|
||||||
} else {
|
|
||||||
ship.use(s, null, null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (sgSlot) {
|
|
||||||
// The SG and Fuel scoop to not need to be powered at the same time
|
|
||||||
if (sgSlot.c.power > fuelScoopSlot.c.power) { // The Shield generator uses the most power
|
|
||||||
ship.setSlotEnabled(fuelScoopSlot, false);
|
|
||||||
} else { // The Fuel scoop uses the most power
|
|
||||||
ship.setSlotEnabled(sgSlot, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ship.useLightestStandard({ pd: '1D', ppRating: 'A' });
|
|
||||||
updateState(Serializer.fromShip(ship));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the current build. Will replace the saved build if there is one
|
|
||||||
* for this ship & with the exact name.
|
|
||||||
*/
|
|
||||||
$scope.saveBuild = function() {
|
|
||||||
if (!$scope.buildName) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// No change hav been made, i.e. save ship default build under a name
|
|
||||||
if (!$scope.code) {
|
|
||||||
$scope.code = Serializer.fromShip(ship);
|
|
||||||
}
|
|
||||||
// Only save if there a build name and a change has been made or the build has never been saved
|
|
||||||
if ($scope.code != $scope.savedCode) {
|
|
||||||
Persist.saveBuild(ship.id, $scope.buildName, $scope.code);
|
|
||||||
$scope.savedCode = $scope.code;
|
|
||||||
if ($scope.retrofitBuild === $scope.buildName) {
|
|
||||||
Serializer.toShip(retrofitShip, $scope.code);
|
|
||||||
}
|
|
||||||
updateState($scope.code);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export the build to detailed JSON
|
|
||||||
*/
|
|
||||||
$scope.exportBuild = function(e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
if ($scope.buildName) {
|
|
||||||
$state.go('modal.export', {
|
|
||||||
title: $scope.buildName + ' ' + $translate.instant('export'),
|
|
||||||
description: $translate.instant('PHRASE_EXPORT_DESC'),
|
|
||||||
data: Serializer.toDetailedBuild($scope.buildName, ship, $scope.code || Serializer.fromShip(ship))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Permanently delete the current build and redirect/reload this controller
|
|
||||||
* with the 'factory' build of the current ship.
|
|
||||||
*/
|
|
||||||
$scope.deleteBuild = function() {
|
|
||||||
Persist.deleteBuild(ship.id, $scope.buildName);
|
|
||||||
$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.savedCode = Persist.getBuild(ship.id, $scope.buildName);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle cost of the selected component
|
|
||||||
* @param {object} item The component being toggled
|
|
||||||
*/
|
|
||||||
$scope.toggleCost = function(item) {
|
|
||||||
ship.setCostIncluded(item, !item.incCost);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle cost of the selected component for retrofitting comparison
|
|
||||||
* @param {object} item The component being toggled
|
|
||||||
*/
|
|
||||||
$scope.toggleRetrofitCost = function(item) {
|
|
||||||
retrofitShip.setCostIncluded(item, !item.incCost);
|
|
||||||
updateRetrofitCosts();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [sortCost description]
|
|
||||||
* @param {[type]} key [description]
|
|
||||||
* @return {[type]} [description]
|
|
||||||
*/
|
|
||||||
$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.pwrPredicate = key;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.sortRetrofit = function(key) {
|
|
||||||
$scope.retroDesc = $scope.retroPredicate == key ? !$scope.retroDesc : $scope.retroDesc;
|
|
||||||
$scope.retroPredicate = key;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.sortAmmo = function(key) {
|
|
||||||
$scope.ammoDesc = $scope.ammoPredicate == key ? !$scope.ammoDesc : $scope.ammoDesc;
|
|
||||||
$scope.ammoPredicate = key;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle the power on/off for the selected component
|
|
||||||
* @param {object} item The component being toggled
|
|
||||||
*/
|
|
||||||
$scope.togglePwr = function(c) {
|
|
||||||
ship.setSlotEnabled(c, !c.enabled);
|
|
||||||
updateState(Serializer.fromShip(ship));
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.incPriority = function(c) {
|
|
||||||
if (ship.changePriority(c, c.priority + 1)) {
|
|
||||||
updateState(Serializer.fromShip(ship));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.decPriority = function(c) {
|
|
||||||
if (ship.changePriority(c, c.priority - 1)) {
|
|
||||||
updateState(Serializer.fromShip(ship));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.fuelChange = function(fuel) {
|
|
||||||
$scope.fuel = fuel;
|
|
||||||
updateAmmoCosts();
|
|
||||||
win.triggerHandler('render');
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.statusRetracted = function(slot) {
|
|
||||||
return ship.getSlotStatus(slot, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.statusDeployed = function(slot) {
|
|
||||||
return ship.getSlotStatus(slot, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.setRetrofitBase = function() {
|
|
||||||
if ($scope.retrofitBuild) {
|
|
||||||
Serializer.toShip(retrofitShip, Persist.getBuild(ship.id, $scope.retrofitBuild));
|
|
||||||
} else {
|
|
||||||
retrofitShip.buildWith(data.defaults);
|
|
||||||
}
|
|
||||||
updateRetrofitCosts();
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.updateCostTab = function(tab) {
|
|
||||||
Persist.setCostTab(tab);
|
|
||||||
$scope.costTab = tab;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.ppWarning = function(pp) {
|
|
||||||
return pp.pGen < ship.powerRetracted;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.pdWarning = function(pd) {
|
|
||||||
return pd.enginecapacity < ship.boostEnergy;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Utilify functions
|
|
||||||
|
|
||||||
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;
|
|
||||||
$scope.speedSeries.yMax = calcSpeed(ship.unladenMass, ship.speed, ship.boost, $scope.th.c, ship.pipSpeed).boost;
|
|
||||||
updateRetrofitCosts();
|
|
||||||
win.triggerHandler('pwrchange');
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateRetrofitCosts() {
|
|
||||||
var costs = $scope.retrofitList = [];
|
|
||||||
var total = 0, i, l, item;
|
|
||||||
|
|
||||||
if (ship.bulkheads.id != retrofitShip.bulkheads.id) {
|
|
||||||
item = {
|
|
||||||
buyClassRating: ship.bulkheads.c.class + ship.bulkheads.c.rating,
|
|
||||||
buyName: ship.bulkheads.c.name,
|
|
||||||
sellClassRating: retrofitShip.bulkheads.c.class + retrofitShip.bulkheads.c.rating,
|
|
||||||
sellName: retrofitShip.bulkheads.c.name,
|
|
||||||
netCost: ship.bulkheads.discountedCost - retrofitShip.bulkheads.discountedCost,
|
|
||||||
retroItem: retrofitShip.bulkheads
|
|
||||||
};
|
|
||||||
costs.push(item);
|
|
||||||
if (retrofitShip.bulkheads.incCost) {
|
|
||||||
total += item.netCost;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var g in { standard: 1, internal: 1, hardpoints: 1 }) {
|
|
||||||
var retroSlotGroup = retrofitShip[g];
|
|
||||||
var slotGroup = ship[g];
|
|
||||||
for (i = 0, l = slotGroup.length; i < l; i++) {
|
|
||||||
if (slotGroup[i].id != retroSlotGroup[i].id) {
|
|
||||||
item = { netCost: 0, retroItem: retroSlotGroup[i] };
|
|
||||||
if (slotGroup[i].id) {
|
|
||||||
item.buyName = slotGroup[i].c.name || slotGroup[i].c.grp;
|
|
||||||
item.buyClassRating = slotGroup[i].c.class + slotGroup[i].c.rating;
|
|
||||||
item.netCost = slotGroup[i].discountedCost;
|
|
||||||
}
|
|
||||||
if (retroSlotGroup[i].id) {
|
|
||||||
item.sellName = retroSlotGroup[i].c.name || retroSlotGroup[i].c.grp;
|
|
||||||
item.sellClassRating = retroSlotGroup[i].c.class + retroSlotGroup[i].c.rating;
|
|
||||||
item.netCost -= retroSlotGroup[i].discountedCost;
|
|
||||||
}
|
|
||||||
costs.push(item);
|
|
||||||
if (retroSlotGroup[i].incCost) {
|
|
||||||
total += item.netCost;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$scope.retrofitTotal = total;
|
|
||||||
updateAmmoCosts();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateAmmoCosts() {
|
|
||||||
var costs = $scope.ammoList = [];
|
|
||||||
var total = 0, i, l, item, q, limpets = 0, scoop = false;
|
|
||||||
|
|
||||||
for (var g in { standard: 1, internal: 1, hardpoints: 1 }) {
|
|
||||||
var slotGroup = ship[g];
|
|
||||||
for (i = 0, l = slotGroup.length; i < l; i++) {
|
|
||||||
if (slotGroup[i].id) {
|
|
||||||
//special cases needed for SCB, AFMU, and limpet controllers since they don't use standard ammo/clip
|
|
||||||
q = 0;
|
|
||||||
switch (slotGroup[i].c.grp) {
|
|
||||||
case 'fs': //skip fuel calculation if scoop present
|
|
||||||
scoop = true;
|
|
||||||
break;
|
|
||||||
case 'scb':
|
|
||||||
q = slotGroup[i].c.cells;
|
|
||||||
break;
|
|
||||||
case 'am':
|
|
||||||
q = slotGroup[i].c.ammo;
|
|
||||||
break;
|
|
||||||
case 'fx': case 'hb': case 'cc': case 'pc':
|
|
||||||
limpets = ship.cargoCapacity;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
q = slotGroup[i].c.clip + slotGroup[i].c.ammo;
|
|
||||||
}
|
|
||||||
//calculate ammo costs only if a cost is specified
|
|
||||||
if (slotGroup[i].c.ammocost > 0) {
|
|
||||||
item = {
|
|
||||||
ammoClassRating: slotGroup[i].c.class + slotGroup[i].c.rating,
|
|
||||||
ammoName: slotGroup[i].c.name || slotGroup[i].c.grp,
|
|
||||||
ammoMax: q,
|
|
||||||
ammoUnitCost: slotGroup[i].c.ammocost,
|
|
||||||
ammoTotalCost: q * slotGroup[i].c.ammocost
|
|
||||||
};
|
|
||||||
costs.push(item);
|
|
||||||
total += item.ammoTotalCost;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//limpets if controllers exist and cargo space available
|
|
||||||
if (limpets > 0) {
|
|
||||||
item = {
|
|
||||||
ammoName: 'limpets',
|
|
||||||
ammoMax: ship.cargoCapacity,
|
|
||||||
ammoUnitCost: 101,
|
|
||||||
ammoTotalCost: ship.cargoCapacity * 101
|
|
||||||
};
|
|
||||||
costs.push(item);
|
|
||||||
total += item.ammoTotalCost;
|
|
||||||
}
|
|
||||||
//calculate refuel costs if no scoop present
|
|
||||||
if (!scoop) {
|
|
||||||
item = {
|
|
||||||
ammoName: 'fuel',
|
|
||||||
ammoMax: $scope.fuel,
|
|
||||||
ammoUnitCost: 50,
|
|
||||||
ammoTotalCost: $scope.fuel * 50
|
|
||||||
};
|
|
||||||
costs.push(item);
|
|
||||||
total += item.ammoTotalCost;
|
|
||||||
}
|
|
||||||
$scope.ammoTotal = total;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide any open menu/slot/etc if the background is clicked
|
|
||||||
$scope.$on('close', function() {
|
|
||||||
$scope.selectedSlot = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Hide any open menu/slot/etc if the background is clicked
|
|
||||||
$scope.$on('languageChanged', function() {
|
|
||||||
$scope.selectedSlot = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Hide any open menu/slot/etc if the background is clicked
|
|
||||||
$scope.$on('discountChange', function() {
|
|
||||||
ship.applyDiscounts($rootScope.discounts.ship, $rootScope.discounts.components);
|
|
||||||
retrofitShip.applyDiscounts($rootScope.discounts.ship, $rootScope.discounts.components);
|
|
||||||
updateRetrofitCosts();
|
|
||||||
});
|
|
||||||
|
|
||||||
}]);
|
|
||||||
@@ -24,6 +24,7 @@ export const ModuleGroupToName = {
|
|||||||
fs: 'Fuel Scoop',
|
fs: 'Fuel Scoop',
|
||||||
sc: 'Scanner',
|
sc: 'Scanner',
|
||||||
am: 'Auto Field-Maintenance Unit',
|
am: 'Auto Field-Maintenance Unit',
|
||||||
|
bsg: 'Bi-Weave Shield Generator',
|
||||||
cr: 'Cargo Rack',
|
cr: 'Cargo Rack',
|
||||||
fi: 'Frame Shift Drive Interdictor',
|
fi: 'Frame Shift Drive Interdictor',
|
||||||
hb: 'Hatch Breaker Limpet Controller',
|
hb: 'Hatch Breaker Limpet Controller',
|
||||||
@@ -31,6 +32,7 @@ export const ModuleGroupToName = {
|
|||||||
rf: 'Refinery',
|
rf: 'Refinery',
|
||||||
scb: 'Shield Cell Bank',
|
scb: 'Shield Cell Bank',
|
||||||
sg: 'Shield Generator',
|
sg: 'Shield Generator',
|
||||||
|
pv: 'Planetary Vehicle Hanger',
|
||||||
psg: 'Prismatic Shield Generator',
|
psg: 'Prismatic Shield Generator',
|
||||||
dc: 'Docking Computer',
|
dc: 'Docking Computer',
|
||||||
fx: 'Fuel Transfer Limpet Controller',
|
fx: 'Fuel Transfer Limpet Controller',
|
||||||
@@ -89,88 +91,98 @@ export const BulkheadNames = [
|
|||||||
* @type {Array}
|
* @type {Array}
|
||||||
*/
|
*/
|
||||||
export const ShipFacets = [
|
export const ShipFacets = [
|
||||||
{ // 0
|
{ // 0
|
||||||
title: 'agility',
|
title: 'agility',
|
||||||
props: ['agility'],
|
props: ['agility'],
|
||||||
unit: '',
|
unit: '',
|
||||||
fmt: 'fCrd'
|
fmt: 'fCrd'
|
||||||
},
|
},
|
||||||
{ // 1
|
{ // 1
|
||||||
title: 'speed',
|
title: 'speed',
|
||||||
props: ['topSpeed', 'topBoost'],
|
props: ['topSpeed', 'topBoost'],
|
||||||
lbls: ['thrusters', 'boost'],
|
lbls: ['thrusters', 'boost'],
|
||||||
unit: 'm/s',
|
unit: 'm/s',
|
||||||
fmt: 'fCrd'
|
fmt: 'fCrd'
|
||||||
},
|
},
|
||||||
{ // 2
|
{ // 2
|
||||||
title: 'armour',
|
title: 'armour',
|
||||||
props: ['armour'],
|
props: ['armour'],
|
||||||
unit: '',
|
unit: '',
|
||||||
fmt: 'fCrd'
|
fmt: 'fCrd'
|
||||||
},
|
},
|
||||||
{ // 3
|
{ // 3
|
||||||
title: 'shields',
|
title: 'shields',
|
||||||
props: ['shieldStrength'],
|
props: ['shieldStrength'],
|
||||||
unit: 'MJ',
|
unit: 'MJ',
|
||||||
fmt: 'fRound'
|
fmt: 'fRound'
|
||||||
},
|
},
|
||||||
{ // 4
|
{ // 4
|
||||||
title: 'jump range',
|
title: 'jump range',
|
||||||
props: ['unladenRange', 'fullTankRange', 'ladenRange'],
|
props: ['unladenRange', 'fullTankRange', 'ladenRange'],
|
||||||
lbls: ['max', 'full tank', 'laden'],
|
lbls: ['max', 'full tank', 'laden'],
|
||||||
unit: 'LY',
|
unit: 'LY',
|
||||||
fmt: 'fRound'
|
fmt: 'fRound'
|
||||||
},
|
},
|
||||||
{ // 5
|
{ // 5
|
||||||
title: 'mass',
|
title: 'mass',
|
||||||
props: ['unladenMass', 'ladenMass'],
|
props: ['unladenMass', 'ladenMass'],
|
||||||
lbls: ['unladen', 'laden'],
|
lbls: ['unladen', 'laden'],
|
||||||
unit: 'T',
|
unit: 'T',
|
||||||
fmt: 'fRound'
|
fmt: 'fRound'
|
||||||
},
|
},
|
||||||
{ // 6
|
{ // 6
|
||||||
title: 'cargo',
|
title: 'cargo',
|
||||||
props: ['cargoCapacity'],
|
props: ['cargoCapacity'],
|
||||||
unit: 'T',
|
unit: 'T',
|
||||||
fmt: 'fRound'
|
fmt: 'fRound'
|
||||||
},
|
},
|
||||||
{ // 7
|
{ // 7
|
||||||
title: 'fuel',
|
title: 'fuel',
|
||||||
props: ['fuelCapacity'],
|
props: ['fuelCapacity'],
|
||||||
unit: 'T',
|
unit: 'T',
|
||||||
fmt: 'fRound'
|
fmt: 'fRound'
|
||||||
},
|
},
|
||||||
{ // 8
|
{ // 8
|
||||||
title: 'power',
|
title: 'power',
|
||||||
props: ['powerRetracted', 'powerDeployed', 'powerAvailable'],
|
props: ['powerRetracted', 'powerDeployed', 'powerAvailable'],
|
||||||
lbls: ['retracted', 'deployed', 'available'],
|
lbls: ['retracted', 'deployed', 'available'],
|
||||||
unit: 'MW',
|
unit: 'MW',
|
||||||
fmt: 'fPwr'
|
fmt: 'fPwr'
|
||||||
},
|
},
|
||||||
{ // 9
|
{ // 9
|
||||||
title: 'cost',
|
title: 'cost',
|
||||||
props: ['totalCost'],
|
props: ['totalCost'],
|
||||||
unit: 'CR',
|
unit: 'CR',
|
||||||
fmt: 'fCrd'
|
fmt: 'fCrd'
|
||||||
},
|
},
|
||||||
{ // 10
|
{ // 10
|
||||||
title: 'total range',
|
title: 'total range',
|
||||||
props: ['unladenTotalRange', 'ladenTotalRange'],
|
props: ['unladenTotalRange', 'ladenTotalRange'],
|
||||||
lbls: ['unladen', 'laden'],
|
lbls: ['unladen', 'laden'],
|
||||||
unit: 'LY',
|
unit: 'LY',
|
||||||
fmt: 'fRound'
|
fmt: 'fRound'
|
||||||
},
|
},
|
||||||
{ // 11
|
{ // 11
|
||||||
title: 'DPS',
|
title: 'DPS',
|
||||||
props: ['totalDps'],
|
props: ['totalDps'],
|
||||||
lbls: ['DPS'],
|
lbls: ['DPS'],
|
||||||
unit: '',
|
unit: '',
|
||||||
fmt: 'fRound'
|
fmt: 'fRound'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
/**
|
|
||||||
* Set of all available / theoretical discounts
|
/**
|
||||||
*/
|
* Set of all insurance levels
|
||||||
|
*/
|
||||||
|
export const Insurance = {
|
||||||
|
'standard': 0.05,
|
||||||
|
'alpha': 0.025,
|
||||||
|
'beta': 0.0375
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of all available / theoretical discounts
|
||||||
|
*/
|
||||||
export const Discounts = {
|
export const Discounts = {
|
||||||
'0%': 1,
|
'0%': 1,
|
||||||
'5%': 0.95,
|
'5%': 0.95,
|
||||||
@@ -179,4 +191,3 @@ export const Discounts = {
|
|||||||
'20%': 0.80,
|
'20%': 0.80,
|
||||||
'25%': 0.75
|
'25%': 0.75
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -126,11 +126,12 @@ export default class ModuleSet {
|
|||||||
return sg;
|
return sg;
|
||||||
};
|
};
|
||||||
|
|
||||||
lightestPowerPlant(powerUsed, rating) {
|
lightestPowerPlant(powerNeeded, rating) {
|
||||||
var pp = this.standard[0][0];
|
var pp = this.standard[0][0];
|
||||||
|
|
||||||
for (let p of this.standard[0]) {
|
for (let p of this.standard[0]) {
|
||||||
if (p.mass < pp.mass && p.pGen >= powerUsed) {
|
// Provides enough power, is lighter or the same mass as current power plant but better output/efficiency
|
||||||
|
if (p.pGen >= powerNeeded && (p.mass < pp.mass || (p.mass == pp.mass && p.pGen > pp.pGen))) {
|
||||||
pp = p;
|
pp = p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,23 @@
|
|||||||
import { ModuleNameToGroup, BulkheadNames } from './Constants';
|
import { ModuleNameToGroup, BulkheadNames } from './Constants';
|
||||||
import ModuleSet from './ModuleSet';
|
import ModuleSet from './ModuleSet';
|
||||||
import Ships from './Ships';
|
import { Ships, Modules } from 'coriolis-data';
|
||||||
import Modules from './Modules';
|
|
||||||
|
|
||||||
export function cargoHatch() {
|
export function cargoHatch() {
|
||||||
return { name: 'Cargo Hatch', class: 1, rating: 'H', power: 0.6 };
|
return { name: 'Cargo Hatch', class: 1, rating: 'H', power: 0.6 };
|
||||||
};
|
};
|
||||||
|
|
||||||
export function standard(typeIndex, componentId) {
|
export function standard(typeIndex, id) {
|
||||||
return Modules.standard[typeIndex][componentId];
|
let standard = Modules.standard[typeIndex];
|
||||||
|
if (standard[id]) {
|
||||||
|
return standard[id];
|
||||||
|
} else {
|
||||||
|
for (let k in standard) {
|
||||||
|
if (standard[k].id == id){
|
||||||
|
return standard[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function hardpoints(id) {
|
export function hardpoints(id) {
|
||||||
@@ -149,17 +158,26 @@ export function findHardpointId(groupName, clss, rating, name, mount, missile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks up the bulkhead component for a specific ship and bulkhead
|
* Looks up the bulkhead module for a specific ship and bulkhead
|
||||||
* @param {string} shipId Unique ship Id/Key
|
* @param {string} shipId Unique ship Id/Key
|
||||||
* @param {number} bulkheadsId Id/Index for the specified bulkhead
|
* @param {string|number} bulkheadsId Id/Index for the specified bulkhead
|
||||||
* @return {object} The bulkhead component object
|
* @return {object} The bulkhead component object
|
||||||
*/
|
*/
|
||||||
export function bulkheads(shipId, bulkheadsId) {
|
export function bulkheads(shipId, index) {
|
||||||
return Modules.bulkheads[shipId][bulkheadsId];
|
let bulkhead = Ships[shipId].bulkheads[index];
|
||||||
|
bulkhead.class = 8;
|
||||||
|
bulkhead.rating = 'I';
|
||||||
|
bulkhead.name = BulkheadNames[index]
|
||||||
|
|
||||||
|
return bulkhead;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bulkheadIndex(bulkheadName) {
|
export function bulkheadIndex(bulkheadName) {
|
||||||
return Bulkheads.indexOf(bulkheadName);
|
return BulkheadNames.indexOf(bulkheadName);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isShieldGenerator(g) {
|
||||||
|
return g == 'sg' || g == 'psg' || g == 'bsg';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
import bulkheads from 'coriolis-data/components/bulkheads';
|
|
||||||
// Standard Modules
|
|
||||||
import frame_shift_drive from 'coriolis-data/components/standard/frame_shift_drive';
|
|
||||||
import fuel_tank from 'coriolis-data/components/standard/fuel_tank';
|
|
||||||
import life_support from 'coriolis-data/components/standard/life_support';
|
|
||||||
import power_distributor from 'coriolis-data/components/standard/power_distributor';
|
|
||||||
import power_plant from 'coriolis-data/components/standard/power_plant';
|
|
||||||
import sensors from 'coriolis-data/components/standard/sensors';
|
|
||||||
import thrusters from 'coriolis-data/components/standard/thrusters';
|
|
||||||
// Hardpoints and Utility modules
|
|
||||||
import { pl } from 'coriolis-data/components/hardpoints/pulse_laser';
|
|
||||||
import { ul } from 'coriolis-data/components/hardpoints/burst_laser';
|
|
||||||
import { bl } from 'coriolis-data/components/hardpoints/beam_laser';
|
|
||||||
import { mc } from 'coriolis-data/components/hardpoints/multi_cannon';
|
|
||||||
import { c } from 'coriolis-data/components/hardpoints/cannon';
|
|
||||||
import { fc } from 'coriolis-data/components/hardpoints/fragment_cannon';
|
|
||||||
import { rg } from 'coriolis-data/components/hardpoints/rail_gun';
|
|
||||||
import { pa } from 'coriolis-data/components/hardpoints/plasma_accelerator';
|
|
||||||
import { mr } from 'coriolis-data/components/hardpoints/missile_rack';
|
|
||||||
import { tp } from 'coriolis-data/components/hardpoints/torpedo_pylon';
|
|
||||||
import { nl } from 'coriolis-data/components/hardpoints/mine_launcher';
|
|
||||||
import { ml } from 'coriolis-data/components/hardpoints/mining_laser';
|
|
||||||
import { cs } from 'coriolis-data/components/hardpoints/cargo_scanner';
|
|
||||||
import { cm } from 'coriolis-data/components/hardpoints/countermeasures';
|
|
||||||
import { ws } from 'coriolis-data/components/hardpoints/frame_shift_wake_scanner';
|
|
||||||
import { kw } from 'coriolis-data/components/hardpoints/kill_warrant_scanner';
|
|
||||||
import { sb } from 'coriolis-data/components/hardpoints/shield_booster';
|
|
||||||
// Internal
|
|
||||||
import { am } from 'coriolis-data/components/internal/auto_field_maintenance_unit';
|
|
||||||
import { bsg } from 'coriolis-data/components/internal/bi_weave_shield_generator';
|
|
||||||
import { cr } from 'coriolis-data/components/internal/cargo_rack';
|
|
||||||
import { cc } from 'coriolis-data/components/internal/collector_limpet_controllers';
|
|
||||||
import { dc } from 'coriolis-data/components/internal/docking_computer';
|
|
||||||
import { fi } from 'coriolis-data/components/internal/frame_shift_drive_interdictor';
|
|
||||||
import { fs } from 'coriolis-data/components/internal/fuel_scoop';
|
|
||||||
import { fx } from 'coriolis-data/components/internal/fuel_transfer_limpet_controllers';
|
|
||||||
import { hb } from 'coriolis-data/components/internal/hatch_breaker_limpet_controller';
|
|
||||||
import { hr } from 'coriolis-data/components/internal/hull_reinforcement_package';
|
|
||||||
import { ft } from 'coriolis-data/components/internal/internal_fuel_tank';
|
|
||||||
import { psg } from 'coriolis-data/components/internal/pristmatic_shield_generator';
|
|
||||||
import { pc } from 'coriolis-data/components/internal/prospector_limpet_controllers';
|
|
||||||
import { rf } from 'coriolis-data/components/internal/refinery';
|
|
||||||
import { sc } from 'coriolis-data/components/internal/scanner';
|
|
||||||
import { scb } from 'coriolis-data/components/internal/shield_cell_bank';
|
|
||||||
import { sg } from 'coriolis-data/components/internal/shield_generator';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
standard: [
|
|
||||||
power_plant,
|
|
||||||
thrusters,
|
|
||||||
frame_shift_drive,
|
|
||||||
life_support,
|
|
||||||
power_distributor,
|
|
||||||
sensors,
|
|
||||||
fuel_tank
|
|
||||||
],
|
|
||||||
hardpoints: {
|
|
||||||
pl,
|
|
||||||
ul,
|
|
||||||
bl,
|
|
||||||
mc,
|
|
||||||
c,
|
|
||||||
fc,
|
|
||||||
rg,
|
|
||||||
pa,
|
|
||||||
mr,
|
|
||||||
tp,
|
|
||||||
nl,
|
|
||||||
ml,
|
|
||||||
cs,
|
|
||||||
cm,
|
|
||||||
ws,
|
|
||||||
kw,
|
|
||||||
sb
|
|
||||||
},
|
|
||||||
internal: {
|
|
||||||
am,
|
|
||||||
bsg,
|
|
||||||
cr,
|
|
||||||
cc,
|
|
||||||
dc,
|
|
||||||
fi,
|
|
||||||
fs,
|
|
||||||
fx,
|
|
||||||
hb,
|
|
||||||
hr,
|
|
||||||
ft,
|
|
||||||
psg,
|
|
||||||
pc,
|
|
||||||
rf,
|
|
||||||
sc,
|
|
||||||
scb,
|
|
||||||
sg
|
|
||||||
},
|
|
||||||
bulkheads
|
|
||||||
};
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ModuleGroupToName, MountMap } from './Constants';
|
import { ModuleGroupToName, MountMap, BulkheadNames } from './Constants';
|
||||||
import Ships from './Ships';
|
import { Ships } from 'coriolis-data';
|
||||||
import Ship from './Ship';
|
import Ship from './Ship';
|
||||||
import ModuleUtils from './ModuleUtils';
|
import ModuleUtils from './ModuleUtils';
|
||||||
import LZString from 'lz-string';
|
import LZString from 'lz-string';
|
||||||
@@ -8,122 +8,30 @@ import LZString from 'lz-string';
|
|||||||
* Service managing seralization and deserialization of models for use in URLs and persistene.
|
* Service managing seralization and deserialization of models for use in URLs and persistene.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility function to retrieve a safe string for selected component for a slot.
|
|
||||||
* Used for serialization to code only.
|
|
||||||
* TODO: comment on binding
|
|
||||||
* @private
|
|
||||||
* @param {object} slot The slot object.
|
|
||||||
* @return {string} The id of the selected component or '-' if none selected
|
|
||||||
*/
|
|
||||||
function mapGroup(slot) {
|
|
||||||
this.enabled.push(slot.enabled ? 1 : 0);
|
|
||||||
this.priorities.push(slot.priority);
|
|
||||||
|
|
||||||
return slot.id === null ? '-' : slot.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeToArray(code, arr, codePos) {
|
|
||||||
for (let i = 0; i < arr.length; i++) {
|
|
||||||
if (code.charAt(codePos) == '-') {
|
|
||||||
arr[i] = 0;
|
|
||||||
codePos++;
|
|
||||||
} else {
|
|
||||||
arr[i] = code.substring(codePos, codePos + 2);
|
|
||||||
codePos += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return codePos;
|
|
||||||
}
|
|
||||||
|
|
||||||
function slotToSchema(slot) {
|
function slotToSchema(slot) {
|
||||||
if (slot.c) {
|
if (slot.m) {
|
||||||
let o = {
|
let o = {
|
||||||
class: slot.c.class,
|
class: slot.m.class,
|
||||||
rating: slot.c.rating,
|
rating: slot.m.rating,
|
||||||
enabled: Boolean(slot.enabled),
|
enabled: Boolean(slot.enabled),
|
||||||
priority: slot.priority + 1,
|
priority: slot.priority + 1,
|
||||||
group: ModuleGroupToName[slot.c.grp]
|
group: ModuleGroupToName[slot.m.grp]
|
||||||
};
|
};
|
||||||
|
|
||||||
if (slot.c.name) {
|
if (slot.m.name) {
|
||||||
o.name = slot.c.name;
|
o.name = slot.m.name;
|
||||||
}
|
}
|
||||||
if (slot.c.mode) {
|
if (slot.m.mount) {
|
||||||
o.mount = MountMap[slot.c.mode];
|
o.mount = MountMap[slot.m.mount];
|
||||||
}
|
}
|
||||||
if (slot.c.missile) {
|
if (slot.m.missile) {
|
||||||
o.missile = slot.c.missile;
|
o.missile = slot.m.missile;
|
||||||
}
|
}
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serializes the ships selected components for all slots to a URL friendly string.
|
|
||||||
* @param {Ship} ship The ship to be serialized.
|
|
||||||
* @return {string} Encoded string of components
|
|
||||||
*/
|
|
||||||
export function fromShip(ship) {
|
|
||||||
let power = {
|
|
||||||
enabled: [ship.cargoHatch.enabled ? 1 : 0],
|
|
||||||
priorities: [ship.cargoHatch.priority]
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = [
|
|
||||||
ship.bulkheads.id,
|
|
||||||
_.map(ship.standard, mapGroup, power),
|
|
||||||
_.map(ship.hardpoints, mapGroup, power),
|
|
||||||
_.map(ship.internal, mapGroup, power),
|
|
||||||
'.',
|
|
||||||
LZString.compressToBase64(power.enabled.join('')).replace(/\//g, '-'),
|
|
||||||
'.',
|
|
||||||
LZString.compressToBase64(power.priorities.join('')).replace(/\//g, '-')
|
|
||||||
];
|
|
||||||
|
|
||||||
return _.flatten(data).join('');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates an existing ship instance's slots with components determined by the
|
|
||||||
* code.
|
|
||||||
*
|
|
||||||
* @param {Ship} ship The ship instance to be updated
|
|
||||||
* @param {string} dataString The string to deserialize
|
|
||||||
*/
|
|
||||||
export function toShip(ship, dataString) {
|
|
||||||
var standard = new Array(ship.standard.length),
|
|
||||||
hardpoints = new Array(ship.hardpoints.length),
|
|
||||||
internal = new Array(ship.internal.length),
|
|
||||||
parts = dataString.split('.'),
|
|
||||||
priorities = null,
|
|
||||||
enabled = null,
|
|
||||||
code = parts[0];
|
|
||||||
|
|
||||||
if (parts[1]) {
|
|
||||||
enabled = LZString.decompressFromBase64(parts[1].replace(/-/g, '/')).split('');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parts[2]) {
|
|
||||||
priorities = LZString.decompressFromBase64(parts[2].replace(/-/g, '/')).split('');
|
|
||||||
}
|
|
||||||
|
|
||||||
decodeToArray(code, internal, decodeToArray(code, hardpoints, decodeToArray(code, standard, 1)));
|
|
||||||
|
|
||||||
ship.buildWith(
|
|
||||||
{
|
|
||||||
bulkheads: code.charAt(0) * 1,
|
|
||||||
standard: standard,
|
|
||||||
hardpoints: hardpoints,
|
|
||||||
internal: internal
|
|
||||||
},
|
|
||||||
priorities,
|
|
||||||
enabled
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export function toDetailedBuild(buildName, ship, code) {
|
export function toDetailedBuild(buildName, ship, code) {
|
||||||
var standard = ship.standard,
|
var standard = ship.standard,
|
||||||
hardpoints = ship.hardpoints,
|
hardpoints = ship.hardpoints,
|
||||||
@@ -135,25 +43,25 @@ export function toDetailedBuild(buildName, ship, code) {
|
|||||||
ship: ship.name,
|
ship: ship.name,
|
||||||
references: [{
|
references: [{
|
||||||
name: 'Coriolis.io',
|
name: 'Coriolis.io',
|
||||||
url: $state.href('outfit', { shipId: ship.id, code: code, bn: buildName }, { absolute: true }),
|
url: `http://coriolis.io/outfit/${ship.id}/${code}?bn=${encodeURIComponent(buildName)}`,
|
||||||
code: code,
|
code: code,
|
||||||
shipId: ship.id
|
shipId: ship.id
|
||||||
}],
|
}],
|
||||||
components: {
|
components: {
|
||||||
standard: {
|
standard: {
|
||||||
bulkheads: ship.bulkheads.c.name,
|
bulkheads: BulkheadNames[ship.bulkheads.index],
|
||||||
cargoHatch: { enabled: Boolean(ship.cargoHatch.enabled), priority: ship.cargoHatch.priority + 1 },
|
cargoHatch: { enabled: Boolean(ship.cargoHatch.enabled), priority: ship.cargoHatch.priority + 1 },
|
||||||
powerPlant: { class: standard[0].c.class, rating: standard[0].c.rating, enabled: Boolean(standard[0].enabled), priority: standard[0].priority + 1 },
|
powerPlant: { class: standard[0].m.class, rating: standard[0].m.rating, enabled: Boolean(standard[0].enabled), priority: standard[0].priority + 1 },
|
||||||
thrusters: { class: standard[1].c.class, rating: standard[1].c.rating, enabled: Boolean(standard[1].enabled), priority: standard[1].priority + 1 },
|
thrusters: { class: standard[1].m.class, rating: standard[1].m.rating, enabled: Boolean(standard[1].enabled), priority: standard[1].priority + 1 },
|
||||||
frameShiftDrive: { class: standard[2].c.class, rating: standard[2].c.rating, enabled: Boolean(standard[2].enabled), priority: standard[2].priority + 1 },
|
frameShiftDrive: { class: standard[2].m.class, rating: standard[2].m.rating, enabled: Boolean(standard[2].enabled), priority: standard[2].priority + 1 },
|
||||||
lifeSupport: { class: standard[3].c.class, rating: standard[3].c.rating, enabled: Boolean(standard[3].enabled), priority: standard[3].priority + 1 },
|
lifeSupport: { class: standard[3].m.class, rating: standard[3].m.rating, enabled: Boolean(standard[3].enabled), priority: standard[3].priority + 1 },
|
||||||
powerDistributor: { class: standard[4].c.class, rating: standard[4].c.rating, enabled: Boolean(standard[4].enabled), priority: standard[4].priority + 1 },
|
powerDistributor: { class: standard[4].m.class, rating: standard[4].m.rating, enabled: Boolean(standard[4].enabled), priority: standard[4].priority + 1 },
|
||||||
sensors: { class: standard[5].c.class, rating: standard[5].c.rating, enabled: Boolean(standard[5].enabled), priority: standard[5].priority + 1 },
|
sensors: { class: standard[5].m.class, rating: standard[5].m.rating, enabled: Boolean(standard[5].enabled), priority: standard[5].priority + 1 },
|
||||||
fuelTank: { class: standard[6].c.class, rating: standard[6].c.rating, enabled: Boolean(standard[6].enabled), priority: standard[6].priority + 1 }
|
fuelTank: { class: standard[6].m.class, rating: standard[6].m.rating, enabled: Boolean(standard[6].enabled), priority: standard[6].priority + 1 }
|
||||||
},
|
},
|
||||||
hardpoints: _.map(_.filter(hardpoints, function(slot) { return slot.maxClass > 0; }), slotToSchema),
|
hardpoints: hardpoints.filter(slot => slot.maxClass > 0).map(slotToSchema),
|
||||||
utility: _.map(_.filter(hardpoints, function(slot) { return slot.maxClass === 0; }), slotToSchema),
|
utility: hardpoints.filter(slot => slot.maxClass === 0).map(slotToSchema),
|
||||||
internal: _.map(internal, slotToSchema)
|
internal: internal.map(slotToSchema)
|
||||||
},
|
},
|
||||||
stats: {}
|
stats: {}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { ArmourMultiplier } from './Constants';
|
import { ArmourMultiplier } from './Constants';
|
||||||
import * as Calc from './Calculations';
|
import * as Calc from './Calculations';
|
||||||
import * as ModuleUtils from './ModuleUtils';
|
import * as ModuleUtils from './ModuleUtils';
|
||||||
|
import LZString from 'lz-string';
|
||||||
|
|
||||||
const UNIQUE_MODULES = ['psg', 'sg', 'rf', 'fs'];
|
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the power usage type of a slot and it's particular modul
|
* Returns the power usage type of a slot and it's particular modul
|
||||||
@@ -19,6 +20,31 @@ function powerUsageType(slot, modul) {
|
|||||||
return slot.cat != 1 ? 'retracted' : 'deployed';
|
return slot.cat != 1 ? 'retracted' : 'deployed';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function decodeToArray(code, arr, codePos) {
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
if (code.charAt(codePos) == '-') {
|
||||||
|
arr[i] = 0;
|
||||||
|
codePos++;
|
||||||
|
} else {
|
||||||
|
arr[i] = code.substring(codePos, codePos + 2);
|
||||||
|
codePos += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return codePos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduce function used to get the IDs for a slot group (or array of slots)
|
||||||
|
* @param {array} idArray The current Array of IDs
|
||||||
|
* @param {object} slot Slot object
|
||||||
|
* @param {integer} slotIndex The index for the slot in its group
|
||||||
|
* @return {array} The mutated idArray
|
||||||
|
*/
|
||||||
|
function reduceToIDs(idArray, slot, slotIndex) {
|
||||||
|
idArray[slotIndex] = slot.m ? slot.m.id : '-';
|
||||||
|
return idArray;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ship model used to track all ship ModuleUtils and properties.
|
* Ship model used to track all ship ModuleUtils and properties.
|
||||||
*/
|
*/
|
||||||
@@ -31,6 +57,7 @@ export default class Ship {
|
|||||||
*/
|
*/
|
||||||
constructor(id, properties, slots) {
|
constructor(id, properties, slots) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
this.serialized = {};
|
||||||
this.cargoHatch = { m: ModuleUtils.cargoHatch(), type: 'SYS' };
|
this.cargoHatch = { m: ModuleUtils.cargoHatch(), type: 'SYS' };
|
||||||
this.bulkheads = { incCost: true, maxClass: 8 };
|
this.bulkheads = { incCost: true, maxClass: 8 };
|
||||||
this.availCS = ModuleUtils.forShip(id);
|
this.availCS = ModuleUtils.forShip(id);
|
||||||
@@ -49,7 +76,7 @@ export default class Ship {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Make a Ship 'slot'/item similar to other slots
|
// Make a Ship 'slot'/item similar to other slots
|
||||||
this.m = { incCost: true, type: 'SHIP', discountedCost: this.hullCost, m: { name: this.name, cost: this.hullCost } };
|
this.m = { incCost: true, type: 'SHIP', discountedCost: this.hullCost, m: { class: '', rating: '', name: this.name, cost: this.hullCost } };
|
||||||
this.costList = this.internal.concat(this.m, this.standard, this.hardpoints, this.bulkheads);
|
this.costList = this.internal.concat(this.m, this.standard, this.hardpoints, this.bulkheads);
|
||||||
this.powerList = this.internal.concat(
|
this.powerList = this.internal.concat(
|
||||||
this.cargoHatch,
|
this.cargoHatch,
|
||||||
@@ -81,14 +108,26 @@ export default class Ship {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canThrust() {
|
canThrust() {
|
||||||
// TODO: check thrusters are powered;
|
return this.getSlotStatus(this.standard[1]) == 3 // Thrusters are powered
|
||||||
return this.ladenMass < this.standard[1].m.maxmass;
|
&& this.ladenMass < this.standard[1].m.maxmass; // Max mass not exceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
canBoost() {
|
canBoost() {
|
||||||
return this.canThrust() && this.boostEnergy < this.standard[4].m.enginecapacity;
|
return this.canThrust() // Thrusters operational
|
||||||
|
&& this.getSlotStatus(this.standard[4]) == 3 // Power distributor operational
|
||||||
|
&& this.boostEnergy <= this.standard[4].m.enginecapacity; // PD capacitor is sufficient for boost
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the a slots power status:
|
||||||
|
* 0 - No status [Blank]
|
||||||
|
* 1 - Disabled (Switched off)
|
||||||
|
* 2 - Offline (Insufficient power available)
|
||||||
|
* 3 - Online
|
||||||
|
* @param {[type]} slot [description]
|
||||||
|
* @param {boolean} deployed True - power used when hardpoints are deployed
|
||||||
|
* @return {number} status index
|
||||||
|
*/
|
||||||
getSlotStatus(slot, deployed) {
|
getSlotStatus(slot, deployed) {
|
||||||
if (!slot.m) { // Empty Slot
|
if (!slot.m) { // Empty Slot
|
||||||
return 0; // No Status (Not possible to be active in this state)
|
return 0; // No Status (Not possible to be active in this state)
|
||||||
@@ -122,13 +161,57 @@ export default class Ship {
|
|||||||
*/
|
*/
|
||||||
findInternalByGroup(group) {
|
findInternalByGroup(group) {
|
||||||
var index;
|
var index;
|
||||||
if (group == 'sg' || group == 'psg') {
|
if (ModuleUtils.isShieldGenerator(group)) {
|
||||||
return this.internal.find(slot => slot.m && (slot.m.grp == 'sg' || slot.m.grp == 'psg'));
|
return this.internal.find(slot => slot.m && ModuleUtils.isShieldGenerator(slot.m.grp));
|
||||||
} else {
|
} else {
|
||||||
return this.internal.find(slot => slot.m && slot.m.grp == group);
|
return this.internal.find(slot => slot.m && slot.m.grp == group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return [
|
||||||
|
this.getStandardString(),
|
||||||
|
this.getHardpointsString(),
|
||||||
|
this.getInternalString(),
|
||||||
|
'.',
|
||||||
|
this.getPowerEnabledString(),
|
||||||
|
'.',
|
||||||
|
this.getPowerPrioritesString()
|
||||||
|
].join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
getStandardString() {
|
||||||
|
if(!this.serialized.standard) {
|
||||||
|
this.serialized.standard = this.bulkheads.index + this.standard.reduce((arr, slot, i) => {
|
||||||
|
arr[i] = slot.m ? slot.m.class + slot.m.rating : '-';
|
||||||
|
return arr;
|
||||||
|
}, new Array(this.standard.length)).join('');
|
||||||
|
}
|
||||||
|
return this.serialized.standard;
|
||||||
|
}
|
||||||
|
|
||||||
|
getInternalString() {
|
||||||
|
if(!this.serialized.internal) {
|
||||||
|
this.serialized.internal = this.internal.reduce(reduceToIDs, new Array(this.internal.length)).join('');
|
||||||
|
}
|
||||||
|
return this.serialized.internal;
|
||||||
|
}
|
||||||
|
|
||||||
|
getHardpointsString() {
|
||||||
|
if(!this.serialized.hardpoints) {
|
||||||
|
this.serialized.hardpoints = this.hardpoints.reduce(reduceToIDs, new Array(this.hardpoints.length)).join('');
|
||||||
|
}
|
||||||
|
return this.serialized.hardpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPowerEnabledString() {
|
||||||
|
return this.serialized.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPowerPrioritesString() {
|
||||||
|
return this.serialized.priorities;
|
||||||
|
}
|
||||||
|
|
||||||
//**********************//
|
//**********************//
|
||||||
// Mutate / Update Ship //
|
// Mutate / Update Ship //
|
||||||
//**********************//
|
//**********************//
|
||||||
@@ -244,12 +327,51 @@ export default class Ship {
|
|||||||
this.updatePower()
|
this.updatePower()
|
||||||
.updateJumpStats()
|
.updateJumpStats()
|
||||||
.updateShieldStrength()
|
.updateShieldStrength()
|
||||||
.updateTopSpeed();
|
.updateTopSpeed()
|
||||||
|
.updatePowerPrioritesString()
|
||||||
|
.updatePowerEnabledString();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an existing ship instance's slots with modules determined by the
|
||||||
|
* code.
|
||||||
|
*
|
||||||
|
* @param {string} serializedString The string to deserialize
|
||||||
|
*/
|
||||||
|
buildFrom(serializedString) {
|
||||||
|
var standard = new Array(this.standard.length),
|
||||||
|
hardpoints = new Array(this.hardpoints.length),
|
||||||
|
internal = new Array(this.internal.length),
|
||||||
|
parts = serializedString.split('.'),
|
||||||
|
priorities = null,
|
||||||
|
enabled = null,
|
||||||
|
code = parts[0];
|
||||||
|
|
||||||
|
if (parts[1]) {
|
||||||
|
enabled = LZString.decompressFromBase64(parts[1].replace(/-/g, '/')).split('');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts[2]) {
|
||||||
|
priorities = LZString.decompressFromBase64(parts[2].replace(/-/g, '/')).split('');
|
||||||
|
}
|
||||||
|
|
||||||
|
decodeToArray(code, internal, decodeToArray(code, hardpoints, decodeToArray(code, standard, 1)));
|
||||||
|
|
||||||
|
this.buildWith(
|
||||||
|
{
|
||||||
|
bulkheads: code.charAt(0) * 1,
|
||||||
|
standard: standard,
|
||||||
|
hardpoints: hardpoints,
|
||||||
|
internal: internal
|
||||||
|
},
|
||||||
|
priorities,
|
||||||
|
enabled
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
emptyHardpoints() {
|
emptyHardpoints() {
|
||||||
for (var i = this.hardpoints.length; i--; ) {
|
for (var i = this.hardpoints.length; i--; ) {
|
||||||
this.use(this.hardpoints[i], null);
|
this.use(this.hardpoints[i], null);
|
||||||
@@ -305,7 +427,7 @@ export default class Ship {
|
|||||||
if (slot.m) {
|
if (slot.m) {
|
||||||
this.priorityBands[slot.priority][powerUsageType(slot, slot.m)] += enabled ? slot.m.power : -slot.m.power;
|
this.priorityBands[slot.priority][powerUsageType(slot, slot.m)] += enabled ? slot.m.power : -slot.m.power;
|
||||||
|
|
||||||
if (slot.m.grp == 'sg' || slot.m.grp == 'psg') {
|
if (ModuleUtils.isShieldGenerator(slot.m.grp)) {
|
||||||
this.updateShieldStrength();
|
this.updateShieldStrength();
|
||||||
} else if (slot.m.grp == 'sb') {
|
} else if (slot.m.grp == 'sb') {
|
||||||
this.shieldMultiplier += slot.m.shieldmul * (enabled ? 1 : -1);
|
this.shieldMultiplier += slot.m.shieldmul * (enabled ? 1 : -1);
|
||||||
@@ -315,13 +437,42 @@ export default class Ship {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.updatePower();
|
this.updatePower();
|
||||||
|
this.updatePowerEnabledString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the ship's cumulative and aggregated stats based on the modul change.
|
* 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)
|
||||||
|
*/
|
||||||
|
setSlotPriority(slot, newPriority) {
|
||||||
|
if (newPriority >= 0 && newPriority < this.priorityBands.length) {
|
||||||
|
var oldPriority = slot.priority;
|
||||||
|
slot.priority = newPriority;
|
||||||
|
|
||||||
|
if (slot.enabled) { // Only update power if the slot is enabled
|
||||||
|
var usage = powerUsageType(slot, slot.m);
|
||||||
|
this.priorityBands[oldPriority][usage] -= slot.m.power;
|
||||||
|
this.priorityBands[newPriority][usage] += slot.m.power;
|
||||||
|
this.updatePowerPrioritesString();
|
||||||
|
this.updatePower();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the ship's cumulative and aggregated stats based on the module change.
|
||||||
|
* @param {object} slot The slot being updated
|
||||||
|
* @param {object} n The new module (may be null)
|
||||||
|
* @param {object} old The old module (may be null)
|
||||||
|
* @param {boolean} preventUpdate If true the global ship state will not be updated
|
||||||
|
* @return {this} The ship instance (for chaining operations)
|
||||||
*/
|
*/
|
||||||
updateStats(slot, n, old, preventUpdate) {
|
updateStats(slot, n, old, preventUpdate) {
|
||||||
var powerChange = slot == this.standard[0];
|
var powerChange = slot == this.standard[0];
|
||||||
@@ -445,6 +596,39 @@ export default class Ship {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePowerPrioritesString() {
|
||||||
|
let priorities = [this.cargoHatch.priority];
|
||||||
|
|
||||||
|
for (let slot of this.standard) {
|
||||||
|
priorities.push(slot.priority);
|
||||||
|
}
|
||||||
|
for (let slot of this.hardpoints) {
|
||||||
|
priorities.push(slot.priority);
|
||||||
|
}
|
||||||
|
for (let slot of this.internal) {
|
||||||
|
priorities.push(slot.priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.serialized.priorities = LZString.compressToBase64(priorities.join('')).replace(/\//g, '-');
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePowerEnabledString() {
|
||||||
|
let enabled = [this.cargoHatch.enabled ? 1 : 0];
|
||||||
|
|
||||||
|
for (let slot of this.standard) {
|
||||||
|
enabled.push(slot.enabled ? 1 : 0);
|
||||||
|
}
|
||||||
|
for (let slot of this.hardpoints) {
|
||||||
|
enabled.push(slot.enabled ? 1 : 0);
|
||||||
|
}
|
||||||
|
for (let slot of this.internal) {
|
||||||
|
enabled.push(slot.enabled ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.serialized.enabled = LZString.compressToBase64(enabled.join('')).replace(/\//g, '-');
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a slot with a the modul if the id is different from the current id for this slot.
|
* Update a slot with a the modul if the id is different from the current id for this slot.
|
||||||
@@ -472,6 +656,12 @@ export default class Ship {
|
|||||||
slot.m = m;
|
slot.m = m;
|
||||||
slot.discountedCost = (m && m.cost) ? m.cost * this.modulCostMultiplier : 0;
|
slot.discountedCost = (m && m.cost) ? m.cost * this.modulCostMultiplier : 0;
|
||||||
this.updateStats(slot, m, oldModule, preventUpdate);
|
this.updateStats(slot, m, oldModule, preventUpdate);
|
||||||
|
|
||||||
|
switch (slot.cat) {
|
||||||
|
case 0: this.serialized.standard = null; break;
|
||||||
|
case 1: this.serialized.hardpoints = null; break;
|
||||||
|
case 2: this.serialized.internal = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -484,12 +674,12 @@ export default class Ship {
|
|||||||
*/
|
*/
|
||||||
useBulkhead(index, preventUpdate) {
|
useBulkhead(index, preventUpdate) {
|
||||||
var oldBulkhead = this.bulkheads.m;
|
var oldBulkhead = this.bulkheads.m;
|
||||||
this.bulkheads.id = index;
|
this.bulkheads.index = index;
|
||||||
this.bulkheads.m = ModuleUtils.bulkheads(this.id, index);
|
this.bulkheads.m = ModuleUtils.bulkheads(this.id, index);
|
||||||
this.bulkheads.discountedCost = this.bulkheads.m.cost * this.modulCostMultiplier;
|
this.bulkheads.discountedCost = this.bulkheads.m.cost * this.modulCostMultiplier;
|
||||||
this.armourMultiplier = ArmourMultiplier[index];
|
this.armourMultiplier = ArmourMultiplier[index];
|
||||||
this.updateStats(this.bulkheads, this.bulkheads.m, oldBulkhead, preventUpdate);
|
this.updateStats(this.bulkheads, this.bulkheads.m, oldBulkhead, preventUpdate);
|
||||||
|
this.serialized.standard = null;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,52 +739,31 @@ export default class Ship {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
useUtility(group, rating, clobber) {
|
useUtility(group, rating, name, clobber) {
|
||||||
var modul = ModuleUtils.findHardpoint(group, 0, rating);
|
let m = ModuleUtils.findHardpoint(group, 0, rating, name);
|
||||||
for (var i = this.hardpoints.length; i--; ) {
|
for (let i = this.hardpoints.length; i--; ) {
|
||||||
if ((clobber || !this.hardpoints[i].m) && !this.hardpoints[i].maxClass) {
|
if ((clobber || !this.hardpoints[i].m) && !this.hardpoints[i].maxClass) {
|
||||||
this.use(this.hardpoints[i], modul);
|
this.use(this.hardpoints[i], m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
useWeapon(group, mount, clobber, missile) {
|
useWeapon(group, mount, missile, clobber) {
|
||||||
var hps = this.hardpoints;
|
let hps = this.hardpoints;
|
||||||
for (var i = hps.length; i--; ) {
|
for (let i = hps.length; i--; ) {
|
||||||
if (hps[i].maxClass) {
|
if (hps[i].maxClass) {
|
||||||
var size = hps[i].maxClass, modul;
|
let size = hps[i].maxClass, m;
|
||||||
do {
|
do {
|
||||||
modul = ModuleUtils.findHardpoint(group, size, null, null, mount, missile);
|
m = ModuleUtils.findHardpoint(group, size, null, null, mount, missile);
|
||||||
if ((clobber || !hps[i].m) && modul) {
|
if ((clobber || !hps[i].m) && m) {
|
||||||
this.use(hps[i], modul);
|
this.use(hps[i], m);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (!modul && (--size > 0));
|
} while (!m && (--size > 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
*/
|
|
||||||
changePriority(slot, newPriority) {
|
|
||||||
if (newPriority >= 0 && newPriority < this.priorityBands.length) {
|
|
||||||
var oldPriority = slot.priority;
|
|
||||||
slot.priority = newPriority;
|
|
||||||
|
|
||||||
if (slot.enabled) { // Only update power if the slot is enabled
|
|
||||||
var usage = powerUsageType(slot, slot.m);
|
|
||||||
this.priorityBands[oldPriority][usage] -= slot.m.power;
|
|
||||||
this.priorityBands[newPriority][usage] += slot.m.power;
|
|
||||||
this.updatePower();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
import { adder } from 'coriolis-data/ships/adder';
|
|
||||||
import { anaconda } from 'coriolis-data/ships/anaconda';
|
|
||||||
import { asp } from 'coriolis-data/ships/asp';
|
|
||||||
import { asp_scout } from 'coriolis-data/ships/asp_scout';
|
|
||||||
import { cobra_mk_iii } from 'coriolis-data/ships/cobra_mk_iii';
|
|
||||||
import { diamondback_explorer } from 'coriolis-data/ships/diamondback_explorer';
|
|
||||||
import { diamondback } from 'coriolis-data/ships/diamondback_scout';
|
|
||||||
import { eagle } from 'coriolis-data/ships/eagle';
|
|
||||||
import { federal_assault_ship } from 'coriolis-data/ships/federal_assault_ship';
|
|
||||||
import { federal_corvette } from 'coriolis-data/ships/federal_corvette';
|
|
||||||
import { federal_dropship } from 'coriolis-data/ships/federal_dropship';
|
|
||||||
import { federal_gunship } from 'coriolis-data/ships/federal_gunship';
|
|
||||||
import { fer_de_lance } from 'coriolis-data/ships/fer_de_lance';
|
|
||||||
import { hauler } from 'coriolis-data/ships/hauler';
|
|
||||||
import { imperial_clipper } from 'coriolis-data/ships/imperial_clipper';
|
|
||||||
import { imperial_courier } from 'coriolis-data/ships/imperial_courier';
|
|
||||||
import { imperial_cutter } from 'coriolis-data/ships/imperial_cutter';
|
|
||||||
import { imperial_eagle } from 'coriolis-data/ships/imperial_eagle';
|
|
||||||
import { keelback } from 'coriolis-data/ships/keelback';
|
|
||||||
import { orca } from 'coriolis-data/ships/orca';
|
|
||||||
import { python } from 'coriolis-data/ships/python';
|
|
||||||
import { sidewinder } from 'coriolis-data/ships/sidewinder';
|
|
||||||
import { type_6_transporter } from 'coriolis-data/ships/type_6_transporter';
|
|
||||||
import { type_7_transport } from 'coriolis-data/ships/type_7_transport';
|
|
||||||
import { type_9_heavy } from 'coriolis-data/ships/type_9_heavy';
|
|
||||||
import { viper } from 'coriolis-data/ships/viper';
|
|
||||||
import { viper_mk_iv } from 'coriolis-data/ships/viper_mk_iv';
|
|
||||||
import { vulture } from 'coriolis-data/ships/vulture';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
adder,
|
|
||||||
anaconda,
|
|
||||||
asp,
|
|
||||||
asp_scout,
|
|
||||||
cobra_mk_iii,
|
|
||||||
diamondback_explorer,
|
|
||||||
diamondback,
|
|
||||||
eagle,
|
|
||||||
federal_assault_ship,
|
|
||||||
federal_corvette,
|
|
||||||
federal_dropship,
|
|
||||||
federal_gunship,
|
|
||||||
fer_de_lance,
|
|
||||||
hauler,
|
|
||||||
imperial_clipper,
|
|
||||||
imperial_courier,
|
|
||||||
imperial_cutter,
|
|
||||||
imperial_eagle,
|
|
||||||
keelback,
|
|
||||||
orca,
|
|
||||||
python,
|
|
||||||
sidewinder,
|
|
||||||
type_6_transporter,
|
|
||||||
type_7_transport,
|
|
||||||
type_9_heavy,
|
|
||||||
viper,
|
|
||||||
viper_mk_iv,
|
|
||||||
vulture
|
|
||||||
};
|
|
||||||
@@ -53,7 +53,7 @@ class Persist extends EventEmitter {
|
|||||||
let comparisonJson = _get(LS_KEY_COMPARISONS);
|
let comparisonJson = _get(LS_KEY_COMPARISONS);
|
||||||
|
|
||||||
this.builds = buildJson ? buildJson : {};
|
this.builds = buildJson ? buildJson : {};
|
||||||
this.comparisons = comparisonJson ? comparisonJson: {};
|
this.comparisons = comparisonJson ? comparisonJson : {};
|
||||||
this.buildCount = Object.keys(this.builds).length;
|
this.buildCount = Object.keys(this.builds).length;
|
||||||
this.langCode = _getString(LS_KEY_LANG) || 'en';
|
this.langCode = _getString(LS_KEY_LANG) || 'en';
|
||||||
this.insurance = _getString(LS_KEY_INSURANCE);
|
this.insurance = _getString(LS_KEY_INSURANCE);
|
||||||
@@ -89,7 +89,7 @@ class Persist extends EventEmitter {
|
|||||||
this.builds[shipId][name] = code;
|
this.builds[shipId][name] = code;
|
||||||
_put(LS_KEY_BUILDS, this.builds);
|
_put(LS_KEY_BUILDS, this.builds);
|
||||||
if (newBuild) {
|
if (newBuild) {
|
||||||
this.emit('builds', this.builds);
|
this.emit('buildSaved', shipId, name, code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -109,10 +109,21 @@ class Persist extends EventEmitter {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
getBuilds() {
|
getBuilds(shipId) {
|
||||||
|
if(shipId && shipId.length > 0) {
|
||||||
|
return this.builds[shipId];
|
||||||
|
}
|
||||||
return this.builds;
|
return this.builds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getBuildsNamesFor(shipId) {
|
||||||
|
if (this.builds[shipId]) {
|
||||||
|
return Object.keys(this.builds[shipId]).sort();
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hasBuild(shipId, name) {
|
hasBuild(shipId, name) {
|
||||||
return this.builds[shipId] && this.builds[shipId][name];
|
return this.builds[shipId] && this.builds[shipId][name];
|
||||||
}
|
}
|
||||||
@@ -146,7 +157,7 @@ class Persist extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_put(LS_KEY_COMPARISONS, this.comparisons);
|
_put(LS_KEY_COMPARISONS, this.comparisons);
|
||||||
this.emit('builds', this.builds);
|
this.emit('buildDeleted', shipId, name);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -275,7 +286,7 @@ class Persist extends EventEmitter {
|
|||||||
* Get the saved ship discount
|
* Get the saved ship discount
|
||||||
* @return {number} val Discount value/amount
|
* @return {number} val Discount value/amount
|
||||||
*/
|
*/
|
||||||
getComponentDiscount() {
|
getModuleDiscount() {
|
||||||
return this.discounts[1];
|
return this.discounts[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
import { term, format } from '../i18n/Language';
|
|
||||||
|
|
||||||
export default function comparisonBBCode(facets, builds, link) {
|
|
||||||
|
/**
|
||||||
|
* Generate a BBCode (Forum) compatible table from comparisons
|
||||||
|
* @param {Function} translate Translate language function
|
||||||
|
* @param {object} formats Number Formats
|
||||||
|
* @param {array} facets Ship Facets
|
||||||
|
* @param {object} builds Ship builds
|
||||||
|
* @param {string} link Link to the comparison
|
||||||
|
* @return {string} the BBCode
|
||||||
|
*/
|
||||||
|
export default function comparisonBBCode(translate, formats, facets, builds, link) {
|
||||||
var colCount = 2, b, i, j, k, f, fl, p, pl, l = [];
|
var colCount = 2, b, i, j, k, f, fl, p, pl, l = [];
|
||||||
|
|
||||||
for (i = 0; i < facets.length; i++) {
|
for (i = 0; i < facets.length; i++) {
|
||||||
@@ -9,11 +18,11 @@ export default function comparisonBBCode(facets, builds, link) {
|
|||||||
p = f.props;
|
p = f.props;
|
||||||
|
|
||||||
if (p.length == 1) {
|
if (p.length == 1) {
|
||||||
l.push('[th][B][COLOR=#FF8C0D]', term(f.title).toUpperCase(), '[/COLOR][/B][/th]');
|
l.push('[th][B][COLOR=#FF8C0D]', translate(f.title).toUpperCase(), '[/COLOR][/B][/th]');
|
||||||
colCount++;
|
colCount++;
|
||||||
} else {
|
} else {
|
||||||
for (j = 0; j < p.length; j++) {
|
for (j = 0; j < p.length; j++) {
|
||||||
l.push('[th][B][COLOR=#FF8C0D]', term(f.title).toUpperCase(), '\n', term(f.lbls[j]).toUpperCase(), '[/COLOR][/B][/th]');
|
l.push('[th][B][COLOR=#FF8C0D]', translate(f.title).toUpperCase(), '\n', translate(f.lbls[j]).toUpperCase(), '[/COLOR][/B][/th]');
|
||||||
colCount++;
|
colCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,7 +32,7 @@ export default function comparisonBBCode(facets, builds, link) {
|
|||||||
|
|
||||||
for (i = 0; i < builds.length; i++) {
|
for (i = 0; i < builds.length; i++) {
|
||||||
b = builds[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++) {
|
for (j = 0, fl = facets.length; j < fl; j++) {
|
||||||
@@ -31,7 +40,7 @@ export default function comparisonBBCode(facets, builds, link) {
|
|||||||
f = facets[j];
|
f = facets[j];
|
||||||
p = f.props;
|
p = f.props;
|
||||||
for (k = 0, pl = p.length; k < pl; k++) {
|
for (k = 0, pl = p.length; k < pl; k++) {
|
||||||
l.push('[td="align: right"]', format[f.fmt](b[p[k]]), ' [size=-2]', term(f.unit), '[/size][/td]');
|
l.push('[td="align: right"]', formats[f.fmt](b[p[k]]), ' [size=-2]', translate(f.unit), '[/size][/td]');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,70 @@
|
|||||||
import { EventEmitter } from 'fbemitter';
|
import { EventEmitter } from 'fbemitter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class to be used as a Singleton for handling common
|
||||||
|
* interface events and operations
|
||||||
|
*/
|
||||||
class InterfaceEvents extends EventEmitter {
|
class InterfaceEvents extends EventEmitter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds the class methods
|
||||||
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.openMenu = this.openMenu.bind(this);
|
this.openMenu = this.openMenu.bind(this);
|
||||||
this.closeAll = this.closeAll.bind(this);
|
this.closeMenu = this.closeMenu.bind(this);
|
||||||
this.hideModal = this.hideModal.bind(this);
|
this.hideModal = this.hideModal.bind(this);
|
||||||
this.showModal = this.showModal.bind(this);
|
this.showModal = this.showModal.bind(this);
|
||||||
|
this.windowResized = this.windowResized.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [openMenu description]
|
||||||
|
* @param {[type]} menu [description]
|
||||||
|
*/
|
||||||
openMenu(menu) {
|
openMenu(menu) {
|
||||||
this.emit('openMenu', menu);
|
this.emit('openMenu', menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
closeAll() {
|
/**
|
||||||
this.emit('closeAll', null);
|
* Emits the close menu event
|
||||||
|
*/
|
||||||
|
closeMenu() {
|
||||||
|
this.emit('closeMenu');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits the hide modal event
|
||||||
|
*/
|
||||||
hideModal() {
|
hideModal() {
|
||||||
this.emit('hideModal');
|
this.emit('hideModal');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits the show modal event the content/component passed
|
||||||
|
* @param {React.Component} content React Component content
|
||||||
|
*/
|
||||||
showModal(content) {
|
showModal(content) {
|
||||||
this.emit('showModal', content);
|
this.emit('showModal', content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
windowResized() {
|
||||||
|
// debounce/ throttle
|
||||||
|
this.emit('windowResized');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new InterfaceEvents();
|
export default new InterfaceEvents();
|
||||||
|
|
||||||
export function contextMenuHandler(cb) {
|
/**
|
||||||
|
* Wraps the callback/context menu handler such that the default
|
||||||
|
* operation can proceed if the SHIFT key is held while right-clicked
|
||||||
|
* @param {Function} cb Callback for contextMenu
|
||||||
|
* @return {Function} Wrapped contextmenu handler
|
||||||
|
*/
|
||||||
|
export function wrapCtxMenu(cb) {
|
||||||
return (event) => {
|
return (event) => {
|
||||||
if (!event.getModifierState('Shift')){
|
if (!event.getModifierState('Shift')) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
cb.call(null, event);
|
cb.call(null, event);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,15 @@ import request from 'superagent';
|
|||||||
|
|
||||||
const SHORTEN_API = 'https://www.googleapis.com/urlshortener/v1/url?key=';
|
const SHORTEN_API = 'https://www.googleapis.com/urlshortener/v1/url?key=';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorten a URL using Google's URL shortener API
|
||||||
|
* @param {string} url The URL to shorten
|
||||||
|
* @param {function} success Success callback
|
||||||
|
* @param {function} error Failure/Error callback
|
||||||
|
*/
|
||||||
export default function shortenUrl(url, success, error) {
|
export default function shortenUrl(url, success, error) {
|
||||||
if (window.navigator.onLine) {
|
if (window.navigator.onLine) {
|
||||||
request.post(SHORTEN_API + GAPI_KEY)
|
request.post(SHORTEN_API + window.CORIOLIS_GAPI_KEY)
|
||||||
.send({ longUrl: url })
|
.send({ longUrl: url })
|
||||||
.end(function(err, response) {
|
.end(function(err, response) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -14,6 +20,6 @@ export default function shortenUrl(url, success, error) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return error('Not Online');
|
error('Not Online');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
src/app/utils/SlotFunctions.js
Normal file
19
src/app/utils/SlotFunctions.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* [slotName description]
|
||||||
|
* @param {[type]} translate [description]
|
||||||
|
* @param {[type]} slot [description]
|
||||||
|
* @return {[type]} [description]
|
||||||
|
*/
|
||||||
|
export function slotName(translate, slot) {
|
||||||
|
return slot.m ? translate(slot.m.name || slot.m.grp) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an internationalization friendly slot name comparator
|
||||||
|
* @param {function} translate Tranlation function
|
||||||
|
* @return {function} Comparator function for slot names
|
||||||
|
*/
|
||||||
|
export function nameComparator(translate) {
|
||||||
|
return (a, b) => slotName(translate, a).toLowerCase().localeCompare(slotName(translate, b).toLowerCase());
|
||||||
|
}
|
||||||
@@ -1,3 +1,10 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Compares A and B and return true using strict comparison (===)
|
||||||
|
* @param {any} objA A
|
||||||
|
* @param {any} objB B
|
||||||
|
* @return {boolean} true if A === B OR A properties === B properties
|
||||||
|
*/
|
||||||
export default function shallowEqual(objA, objB) {
|
export default function shallowEqual(objA, objB) {
|
||||||
if (objA === objB) {
|
if (objA === objB) {
|
||||||
return true;
|
return true;
|
||||||
@@ -8,20 +15,20 @@ export default function shallowEqual(objA, objB) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var keysA = Object.keys(objA);
|
let keysA = Object.keys(objA);
|
||||||
var keysB = Object.keys(objB);
|
let keysB = Object.keys(objB);
|
||||||
|
|
||||||
if (keysA.length !== keysB.length) {
|
if (keysA.length !== keysB.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test for A's keys different from B.
|
// Test for A's keys different from B.
|
||||||
var bHasOwnProperty = Object.prototype.hasOwnProperty.bind(objB);
|
let bHasOwnProperty = Object.prototype.hasOwnProperty.bind(objB);
|
||||||
for (var i = 0; i < keysA.length; i++) {
|
for (let i = 0; i < keysA.length; i++) {
|
||||||
if (!bHasOwnProperty(keysA[i]) || objA[keysA[i]] !== objB[keysA[i]]) {
|
if (!bHasOwnProperty(keysA[i]) || objA[keysA[i]] !== objB[keysA[i]]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
<browserconfig>
|
<browserconfig>
|
||||||
<msapplication>
|
<msapplication>
|
||||||
<tile>
|
<tile>
|
||||||
<square70x70logo src="images/logo/mstile-70x70.png"/>
|
<square70x70logo src="/mstile-70x70.png"/>
|
||||||
<square150x150logo src="images/logo/mstile-150x150.png"/>
|
<square150x150logo src="/mstile-150x150.png"/>
|
||||||
<square310x310logo src="images/logo/mstile-310x310.png"/>
|
<square310x310logo src="/mstile-310x310.png"/>
|
||||||
<wide310x150logo src="images/logo/mstile-310x150.png"/>
|
<wide310x150logo src="/mstile-310x150.png"/>
|
||||||
<TileColor>#000000</TileColor>
|
<TileColor>#000000</TileColor>
|
||||||
</tile>
|
</tile>
|
||||||
</msapplication>
|
</msapplication>
|
||||||
|
|||||||
@@ -3,25 +3,25 @@
|
|||||||
"short_name": "Coriolis",
|
"short_name": "Coriolis",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "images\/logo\/72x72.png",
|
"src": "\/72x72.png",
|
||||||
"sizes": "72x72",
|
"sizes": "72x72",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "1.5"
|
"density": "1.5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "images\/logo\/96x96.png",
|
"src": "\/96x96.png",
|
||||||
"sizes": "96x96",
|
"sizes": "96x96",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "2.0"
|
"density": "2.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "images\/logo\/144x144.png",
|
"src": "\/144x144.png",
|
||||||
"sizes": "144x144",
|
"sizes": "144x144",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "3.0"
|
"density": "3.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "images\/logo\/192x192.png",
|
"src": "\/192x192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "4.0"
|
"density": "4.0"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html {%= o.htmlWebpackPlugin.options.appCache ? 'manifest="/' + o.htmlWebpackPlugin.options.appCache + '"' : '' %} >
|
<html {%= o.htmlWebpackPlugin.options.appCache ? 'manifest=/' + o.htmlWebpackPlugin.options.appCache : '' %} >
|
||||||
<head>
|
<head>
|
||||||
<title>Coriolis</title>
|
<title>Coriolis</title>
|
||||||
<link rel="stylesheet" href="{%= o.htmlWebpackPlugin.files.css[0] %}">
|
<link rel="stylesheet" href="{%= o.htmlWebpackPlugin.files.css[0] %}">
|
||||||
@@ -7,70 +7,38 @@
|
|||||||
<meta name="description" content="A ship builder, outfitting and comparison tool for Elite Dangerous">
|
<meta name="description" content="A ship builder, outfitting and comparison tool for Elite Dangerous">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||||
<link rel="manifest" href="/images/logo/manifest.json">
|
<link rel="manifest" href="/manifest.json">
|
||||||
<link rel="icon" sizes="152x152 192x192" type="image/png" href="/images/logo/192x192.png">
|
<link rel="icon" sizes="152x152 192x192" type="image/png" href="/192x192.png">
|
||||||
<link rel="shortcut icon" href="/images/logo/favicon.ico">
|
|
||||||
|
|
||||||
<!-- Apple/iOS headers -->
|
<!-- Apple/iOS headers -->
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-title" content="Coriolis">
|
<meta name="apple-mobile-web-app-title" content="Coriolis">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
<link rel="apple-touch-icon-precomposed" sizes="180x180" href="/images/logo/apple-touch-icon-precomposed.png">
|
|
||||||
<link rel="apple-touch-icon" href="/images/logo/apple-touch-icon.png">
|
|
||||||
<!-- iPhone, iPod Touch, portrait -->
|
|
||||||
<link href="/images/splash/320x460.png" media="(device-width: 320px) and (device-height: 480px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 1)" rel="apple-touch-startup-image">
|
|
||||||
<!-- iPhone, iPod Touch, landscape -->
|
|
||||||
<link href="/images/splash/480x320.png" media="(device-width: 320px) and (device-height: 480px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 1)" rel="apple-touch-startup-image">
|
|
||||||
<!-- iPhone 4, 4S, portrait -->
|
|
||||||
<link href="/images/splash/640x920.png" media="(device-width: 320px) and (device-height: 480px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 2)" rel="apple-touch-startup-image">
|
|
||||||
<!-- iPhone 4, 4S, landscape -->
|
|
||||||
<link href="/images/splash/960x640.png" media="(device-width: 320px) and (device-height: 480px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 2)" rel="apple-touch-startup-image">
|
|
||||||
<!-- iPhone 5, 5S, 5C, portrait -->
|
|
||||||
<link href="/images/splash/640x1096.png" media="(device-width: 320px) and (device-height: 568px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 2)" rel="apple-touch-startup-image">
|
|
||||||
<!-- iPhone 5, 5S, 5C, landscape -->
|
|
||||||
<link href="/images/splash/1136x640.png" media="(device-width: 320px) and (device-height: 568px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 2)" rel="apple-touch-startup-image">
|
|
||||||
<!-- iPhone 6, portrait -->
|
|
||||||
<link href="/images/splash/750x1294.png" media="(device-width: 375px) and (device-height: 667px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 2)" rel="apple-touch-startup-image">
|
|
||||||
<!-- iPhone 6, landscape -->
|
|
||||||
<link href="/images/splash/1334x750.png" media="(device-width: 375px) and (device-height: 667px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 2)" rel="apple-touch-startup-image">
|
|
||||||
<!-- iPhone 6+, portrait -->
|
|
||||||
<link href="/images/splash/1242x2148.png" media="(device-width: 414px) and (device-height: 736px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 3)" rel="apple-touch-startup-image">
|
|
||||||
<!-- iPhone 6+, landscape -->
|
|
||||||
<link href="/images/splash/2208x1242.png" media="(device-width: 414px) and (device-height: 736px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 3)" rel="apple-touch-startup-image">
|
|
||||||
<!-- iPad 1, 2, Mini, portrait -->
|
|
||||||
<link href="/images/splash/768x1004.png" media="(device-width: 768px) and (device-height: 1024px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 1)" rel="apple-touch-startup-image">
|
|
||||||
<!-- iPad 1, 2, Mini, landscape -->
|
|
||||||
<link href="/images/splash/1024x748.png" media="(device-width: 768px) and (device-height: 1024px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 1)" rel="apple-touch-startup-image">
|
|
||||||
<!-- iPad 3, 4, Air, Air 2, Mini 2, Mini 3, portrait -->
|
|
||||||
<link href="/images/splash/1536x2008.png" media="(device-width: 768px) and (device-height: 1024px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 2)" rel="apple-touch-startup-image">
|
|
||||||
<!-- iPad 3, 4, Air, Air 2, Mini 2, Mini 3, landscape -->
|
|
||||||
<link href="/images/splash/2048x1496.png" media="(device-width: 768px) and (device-height: 1024px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 2)" rel="apple-touch-startup-image">
|
|
||||||
|
|
||||||
<!-- Microsoft Windows Phone/Tablet headers -->
|
<!-- Microsoft Windows Phone/Tablet headers -->
|
||||||
<meta name="msapplication-TileColor" content="#000000">
|
<meta name="msapplication-TileColor" content="#000000">
|
||||||
<meta name="msapplication-TileImage" content="/images/logo/mstile-144x144.png">
|
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
|
||||||
<meta name="msapplication-config" content="/images/logo/browserconfig.xml">
|
<meta name="msapplication-config" content="/browserconfig.xml">
|
||||||
<meta name="theme-color" content="#000000">
|
<meta name="theme-color" content="#000000">
|
||||||
</head>
|
</head>
|
||||||
<body style="background-color:#000;">
|
<body style="background-color:#000;">
|
||||||
<section id="coriolis"></section>
|
<section id="coriolis"></section>
|
||||||
<footer>
|
<footer>
|
||||||
<div class="right cap">
|
<div class="right cap">
|
||||||
<a href="https://github.com/cmmcleod/coriolis/releases/" target="_blank" title="Coriolis Github Project"><span translate="version"></span> {%= o.htmlWebpackPlugin.options.version %} - {%= new Date().toISOString().slice(0, 10) %}</a>
|
<a href="https://github.com/cmmcleod/coriolis/releases/" target="_blank" title="Coriolis Github Project">{%= o.htmlWebpackPlugin.options.version %} - {%= new Date().toISOString().slice(0, 10) %}</a>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="{%= o.htmlWebpackPlugin.files.chunks.lib.entry %}" charset="utf-8"></script>
|
<script src="{%= o.htmlWebpackPlugin.files.chunks.lib.entry %}" charset="utf-8" crossorigin="anonymous"></script>
|
||||||
<script src="{%= o.htmlWebpackPlugin.files.chunks.app.entry %}" charset="utf-8" crossorigin></script>
|
<script src="{%= o.htmlWebpackPlugin.files.chunks.app.entry %}" charset="utf-8" crossorigin="anonymous"></script>
|
||||||
{% if (o.htmlWebpackPlugin.options.uaTracking) { %}
|
{% if (o.htmlWebpackPlugin.options.uaTracking) { %}
|
||||||
<script>
|
<script>
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||||
|
|
||||||
ga('create', '{%= o.htmlWebpackPlugin.options.uaTracking %}', 'auto');
|
ga('create', '{%= o.htmlWebpackPlugin.options.uaTracking %}', 'auto');
|
||||||
var GAPI_KEY = '{%= o.htmlWebpackPlugin.options.gapiKey %}';
|
window.CORIOLIS_GAPI_KEY = '{%= o.htmlWebpackPlugin.options.gapiKey %}';
|
||||||
</script>
|
</script>
|
||||||
{% } %}
|
{% } %}
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -48,11 +48,12 @@ div, a, li {
|
|||||||
|
|
||||||
.page {
|
.page {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0.5em 0.5em;
|
padding: 0.5em 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
clear: both;
|
clear: both;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.l {
|
.l {
|
||||||
@@ -100,6 +101,10 @@ div, a, li {
|
|||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ptr {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-family: @fTitle;
|
font-family: @fTitle;
|
||||||
color: @primary;
|
color: @primary;
|
||||||
|
|||||||
@@ -8,11 +8,8 @@
|
|||||||
line-height: 3em;
|
line-height: 3em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: @bg;
|
background-color: @bg;
|
||||||
|
color: @warning;
|
||||||
a, a:visited {
|
cursor: pointer;
|
||||||
text-decoration: none;
|
|
||||||
color: @warning;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
|
|||||||
@@ -132,26 +132,13 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
table.total {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&, td {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border: 1px solid @primary-disabled;
|
|
||||||
}
|
|
||||||
.lbl {
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: @primary-bg;
|
|
||||||
background-color: @primary-disabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabs {
|
.tabs {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin-bottom: 1px;
|
margin-bottom: 1px;
|
||||||
|
|
||||||
&, th {
|
&, th {
|
||||||
|
cursor: pointer;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
color: @primary-disabled;
|
color: @primary-disabled;
|
||||||
background-color: @primary-bg;
|
background-color: @primary-bg;
|
||||||
|
|||||||
@@ -8,10 +8,6 @@ table {
|
|||||||
color: @primary;
|
color: @primary;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
[ng-click] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
thead {
|
thead {
|
||||||
@@ -67,8 +63,17 @@ tbody tr {
|
|||||||
td {
|
td {
|
||||||
line-height: 1.4em;
|
line-height: 1.4em;
|
||||||
padding: 0 0.3em;
|
padding: 0 0.3em;
|
||||||
}
|
|
||||||
|
|
||||||
|
&.val {
|
||||||
|
border: 1px solid @primary-disabled;
|
||||||
|
}
|
||||||
|
&.lbl {
|
||||||
|
border: 1px solid @primary-disabled;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: @primary-bg;
|
||||||
|
background-color: @primary-disabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
|
|||||||
@@ -1,408 +0,0 @@
|
|||||||
<div id="outfit">
|
|
||||||
|
|
||||||
<div id="standard" class="group">
|
|
||||||
<div class="section-menu" ng-class="{selected: selectedSlot=='standard'}" context-menu="optimizeStandard()" ng-click="selectSlot($event, 'standard')">
|
|
||||||
<h1>
|
|
||||||
{{'standard' | translate}}
|
|
||||||
<svg class="icon"><use xlink:href="#equalizer"></use></svg>
|
|
||||||
</h1>
|
|
||||||
<div class="select" ng-if="selectedSlot=='standard'">
|
|
||||||
<ul>
|
|
||||||
<li class="lc" ng-click="optimizeStandard()" translate="Optimize"></li>
|
|
||||||
<li class="c" ng-click="useStandard('E')">E</li>
|
|
||||||
<li class="c" ng-click="useStandard('D')">D</li>
|
|
||||||
<li class="c" ng-click="useStandard('C')">C</li>
|
|
||||||
<li class="c" ng-click="useStandard('B')">B</li>
|
|
||||||
<li class="c" ng-click="useStandard('A')">A</li>
|
|
||||||
</ul>
|
|
||||||
<div class="select-group cap" translate="builds / roles"></div>
|
|
||||||
<ul>
|
|
||||||
<li class="lc" ng-click="optimizeCargo()" translate="Trader"></li>
|
|
||||||
<li class="lc" ng-click="optimizeExplorer()" translate="Explorer"></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="slot" ng-click="selectSlot($event, ship.bulkheads)" ng-class="{selected: selectedSlot==ship.bulkheads}">
|
|
||||||
<div class="details">
|
|
||||||
<div class="sz"><span>8</span></div>
|
|
||||||
<div class="l cap" translate="bh"></div>
|
|
||||||
<div class="r">{{ship.bulkheads.c.mass}} <u translate>T</u></div>
|
|
||||||
<div class="cl l">{{ship.bulkheads.c.name | translate}}</div>
|
|
||||||
</div>
|
|
||||||
<div class="select" ng-if="selectedSlot==ship.bulkheads" ng-click="select('b',ship.bulkheads,$event)">
|
|
||||||
<ul>
|
|
||||||
<li class="lc" ng-class="{active: ship.bulkheads.id=='0'}" cpid="0" translate="Lightweight Alloy"></li>
|
|
||||||
<li class="lc" ng-class="{active: ship.bulkheads.id=='1'}" cpid="1" translate="Reinforced Alloy"></li>
|
|
||||||
<li class="lc" ng-class="{active: ship.bulkheads.id=='2'}" cpid="2" translate="Military Grade Composite"></li>
|
|
||||||
<li class="lc" ng-class="{active: ship.bulkheads.id=='3'}" cpid="3" translate="Mirrored Surface Composite"></li>
|
|
||||||
<li class="lc" ng-class="{active: ship.bulkheads.id=='4'}" cpid="4" translate="Reactive Surface Composite"></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="slot" ng-click="selectSlot($event, pp)" ng-class="{selected: selectedSlot==pp}">
|
|
||||||
<div class="details" ng-class="{warning: pp.c.pGen < ship.powerRetracted}">
|
|
||||||
<div class="sz">{{::pp.maxClass}}</div>
|
|
||||||
<div class="l">{{pp.id}} {{'pp' | translate}}</div>
|
|
||||||
<div class="r">{{pp.c.mass}} <u translate>T</u></div>
|
|
||||||
<div class="cb"></div>
|
|
||||||
<div class="l cap">{{'efficiency' | translate}}: {{pp.c.eff}}</div>
|
|
||||||
<div class="l cap">{{'power' | translate}}: {{pp.c.pGen}} <u translate>MW</u></div>
|
|
||||||
</div>
|
|
||||||
<div component-select class="select" s="pp" warning="ppWarning" opts="availCS.standard[0]" ng-if="selectedSlot==pp" ng-click="select('c',pp,$event)"></div>
|
|
||||||
</div>
|
|
||||||
<div class="slot" ng-click="selectSlot($event, th)" ng-class="{selected: selectedSlot==th}">
|
|
||||||
<div class="details" ng-class="{'warning': th.c.maxmass < ship.ladenMass}">
|
|
||||||
<div class="sz">{{::th.maxClass}}</div>
|
|
||||||
<div class="l">{{th.id}} {{'t' | translate}}</div>
|
|
||||||
<div class="r">{{th.c.mass}} <u translate>T</u></div>
|
|
||||||
<div class="cb"></div>
|
|
||||||
<div class="l">{{'optimal mass' | translate}}: {{th.c.optmass}} <u translate>T</u></div>
|
|
||||||
<div class="l">{{'max mass' | translate}}: {{th.c.maxmass}} <u translate>T</u></div>
|
|
||||||
</div>
|
|
||||||
<div component-select class="select" s="th" mass="ship.ladenMass" opts="availCS.standard[1]" ng-if="selectedSlot==th" ng-click="select('c',th,$event)"></div>
|
|
||||||
</div>
|
|
||||||
<div class="slot" ng-click="selectSlot($event, fsd)" ng-class="{selected: selectedSlot==fsd}">
|
|
||||||
<div class="details">
|
|
||||||
<div class="sz">{{::fsd.maxClass}}</div>
|
|
||||||
<div class="l">{{fsd.id}} {{'fd' | translate}}</div>
|
|
||||||
<div class="r cr">{{fsd.c.mass}} <u translate>T</u></div>
|
|
||||||
<div class="cb"></div>
|
|
||||||
<div class="l cap">{{'optimal mass' | translate}}: {{fsd.c.optmass}} <u translate>T</u></div>
|
|
||||||
<div class="l cap">{{'max' | translate}} {{'fuel' | translate}}: {{fsd.c.maxfuel}} <u translate>T</u></div>
|
|
||||||
</div>
|
|
||||||
<div component-select class="select" s="fsd" opts="availCS.standard[2]" ng-if="selectedSlot==fsd" ng-click="select('c',fsd,$event)"></div>
|
|
||||||
</div>
|
|
||||||
<div class="slot" ng-click="selectSlot($event, ls)" ng-class="{selected: selectedSlot==ls}">
|
|
||||||
<div class="details">
|
|
||||||
<div class="sz">{{::ls.maxClass}}</div>
|
|
||||||
<div class="l">{{ls.id}} {{'ls' | translate}}</div>
|
|
||||||
<div class="r">{{ls.c.mass}} <u translate>T</u></div>
|
|
||||||
<div class="cb"></div>
|
|
||||||
<div class="l cap">{{'time' | translate}}: {{fTime(ls.c.time)}}</div>
|
|
||||||
</div>
|
|
||||||
<div component-select class="select" s="ls" opts="availCS.standard[3]" ng-if="selectedSlot==ls" ng-click="select('c',ls,$event)"></div>
|
|
||||||
</div>
|
|
||||||
<div class="slot" ng-click="selectSlot($event, pd)" ng-class="{selected: selectedSlot==pd}">
|
|
||||||
<div class="details" ng-class="{warning: pd.c.enginecapacity < ship.boostEnergy}">
|
|
||||||
<div class="sz">{{::pd.maxClass}}</div>
|
|
||||||
<div class="l">{{pd.id}} {{'pd' | translate}}</div>
|
|
||||||
<div class="r">{{pd.c.mass}} <u translate>T</u></div>
|
|
||||||
<div class="cb"></div>
|
|
||||||
<div class="l">{{'WEP' | translate}}: {{pd.c.weaponcapacity}} <u translate>MJ</u> / {{pd.c.weaponrecharge}} <u translate>MW</u></div>
|
|
||||||
<div class="l">{{'SYS' | translate}}: {{pd.c.systemcapacity}} <u translate>MJ</u> / {{pd.c.systemrecharge}} <u translate>MW</u></div>
|
|
||||||
<div class="l">{{'ENG' | translate}}: {{pd.c.enginecapacity}} <u translate>MJ</u> / {{pd.c.enginerecharge}} <u translate>MW</u></div>
|
|
||||||
</div>
|
|
||||||
<div component-select class="select" s="pd" warning="pdWarning" opts="availCS.standard[4]" ng-if="selectedSlot==pd" ng-click="select('c',pd,$event)"></div>
|
|
||||||
</div>
|
|
||||||
<div class="slot" ng-click="selectSlot($event, ss)" ng-class="{selected: selectedSlot==ss}">
|
|
||||||
<div class="details">
|
|
||||||
<div class="sz">{{::ss.maxClass}}</div>
|
|
||||||
<div class="l">{{ss.id}} {{'s' | translate}}</div>
|
|
||||||
<div class="r">{{ss.c.mass}} <u translate>T</u></div>
|
|
||||||
<div class="cb"></div>
|
|
||||||
<div class="l cap">{{'range' | translate}}: {{ss.c.range}} <u translate>km</u></div>
|
|
||||||
</div>
|
|
||||||
<div component-select class="select" s="ss" opts="availCS.standard[5]" ng-if="selectedSlot==ss" ng-click="select('c',ss,$event)"></div>
|
|
||||||
</div>
|
|
||||||
<div class="slot" ng-click="selectSlot($event, ft)" ng-class="{selected: selectedSlot==ft}">
|
|
||||||
<div class="details">
|
|
||||||
<div class="sz">{{::ft.maxClass}}</div>
|
|
||||||
<div class="l">{{ft.id}} {{'ft' | translate}}</div>
|
|
||||||
<div class="r">{{ft.c.capacity}} <u translate>T</u></div>
|
|
||||||
</div>
|
|
||||||
<div component-select class="select" s="ft" opts="availCS.standard[6]" ng-if="selectedSlot==ft" ng-click="select('c',ft,$event)"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="internal" class="group">
|
|
||||||
<div class="section-menu" ng-class="{selected: selectedSlot=='internal'}" context-menu="emptyInternal()" ng-click="selectSlot($event, 'internal')">
|
|
||||||
<h1>
|
|
||||||
{{'internal compartments' | translate}}
|
|
||||||
<svg class="icon"><use xlink:href="#equalizer"></use></svg>
|
|
||||||
</h1>
|
|
||||||
<div class="select" ng-if="selectedSlot=='internal'">
|
|
||||||
<ul>
|
|
||||||
<li class="lc" ng-click="emptyInternal()" translate="empty all"></li>
|
|
||||||
<li class="lc" ng-click="fillWithCargo()" translate="cargo"></li>
|
|
||||||
<li class="lc" ng-click="fillWithCells()" translate="scb"></li>
|
|
||||||
<li class="lc" ng-click="fillWithArmor()" translate="hr"></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="slot" ng-repeat="i in ship.internal" ng-click="selectSlot($event, i)" context-menu="select('i', i, $event, 'empty')" ng-class="{selected: selectedSlot==i}">
|
|
||||||
<div slot-internal class="details" slot="i" fuel="ship.fuelCapacity"></div>
|
|
||||||
<div class="select" ng-if="selectedSlot==i" ng-click="select('i',i,$event)">
|
|
||||||
<div component-select s="i" groups="availCS.getInts(i.maxClass, i.eligible)"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="hardpoints" class="group">
|
|
||||||
<div class="section-menu" ng-class="{selected: selectedSlot=='hardpoints'}" context-menu="emptyHardpoints()" ng-click="selectSlot($event, 'hardpoints')">
|
|
||||||
<h1>
|
|
||||||
{{'hardpoints' | translate}}
|
|
||||||
<svg class="icon"><use xlink:href="#equalizer"></use></svg>
|
|
||||||
</h1>
|
|
||||||
<div class="select hardpoint" ng-if="selectedSlot=='hardpoints'">
|
|
||||||
<ul>
|
|
||||||
<li class="lc" ng-click="emptyHardpoints()" translate="empty all"></li>
|
|
||||||
</ul>
|
|
||||||
<div class="select-group cap" translate="pl"></div>
|
|
||||||
<ul>
|
|
||||||
<li class="c" ng-click="useHardpoint('pl','F')"><svg class="icon lg"><use xlink:href="#mount-F"></use></svg></li>
|
|
||||||
<li class="c" ng-click="useHardpoint('pl','G')"><svg class="icon lg"><use xlink:href="#mount-G"></use></svg></li>
|
|
||||||
<li class="c" ng-click="useHardpoint('pl','T')"><svg class="icon lg"><use xlink:href="#mount-T"></use></svg></li>
|
|
||||||
</ul>
|
|
||||||
<div class="select-group cap" translate="ul"></div>
|
|
||||||
<ul>
|
|
||||||
<li class="c" ng-click="useHardpoint('ul','F')"><svg class="icon lg"><use xlink:href="#mount-F"></use></svg></li>
|
|
||||||
<li class="c" ng-click="useHardpoint('ul','G')"><svg class="icon lg"><use xlink:href="#mount-G"></use></svg></li>
|
|
||||||
<li class="c" ng-click="useHardpoint('ul','T')"><svg class="icon lg"><use xlink:href="#mount-T"></use></svg></li>
|
|
||||||
</ul>
|
|
||||||
<div class="select-group cap" translate="bl"></div>
|
|
||||||
<ul>
|
|
||||||
<li class="c" ng-click="useHardpoint('bl','F')"><svg class="icon lg"><use xlink:href="#mount-F"></use></svg></li>
|
|
||||||
<li class="c" ng-click="useHardpoint('bl','G')"><svg class="icon lg"><use xlink:href="#mount-G"></use></svg></li>
|
|
||||||
<li class="c" ng-click="useHardpoint('bl','T')"><svg class="icon lg"><use xlink:href="#mount-T"></use></svg></li>
|
|
||||||
</ul>
|
|
||||||
<div class="select-group cap" translate="mc"></div>
|
|
||||||
<ul>
|
|
||||||
<li class="c" ng-click="useHardpoint('mc','F')"><svg class="icon lg"><use xlink:href="#mount-F"></use></svg></li>
|
|
||||||
<li class="c" ng-click="useHardpoint('mc','G')"><svg class="icon lg"><use xlink:href="#mount-G"></use></svg></li>
|
|
||||||
<li class="c" ng-click="useHardpoint('mc','T')"><svg class="icon lg"><use xlink:href="#mount-T"></use></svg></li>
|
|
||||||
</ul>
|
|
||||||
<div class="select-group cap" translate="c"></div>
|
|
||||||
<ul>
|
|
||||||
<li class="c" ng-click="useHardpoint('c','F')"><svg class="icon lg"><use xlink:href="#mount-F"></use></svg></li>
|
|
||||||
<li class="c" ng-click="useHardpoint('c','G')"><svg class="icon lg"><use xlink:href="#mount-G"></use></svg></li>
|
|
||||||
<li class="c" ng-click="useHardpoint('c','T')"><svg class="icon lg"><use xlink:href="#mount-T"></use></svg></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="slot" ng-repeat="h in ship.hardpoints | filter:{maxClass: '!0'}" ng-click="selectSlot($event, h)" context-menu="select('h', h, $event, 'empty')" ng-class="{selected: selectedSlot==h}">
|
|
||||||
<div slot-hardpoint class="details" hp="h" size="HPC[h.maxClass]"></div>
|
|
||||||
<div class="select" ng-class="{hardpoint: h.maxClass > 0}" ng-if="selectedSlot==h" ng-click="select('h',h,$event)">
|
|
||||||
<div component-select s="h" groups="availCS.getHps(h.maxClass)"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="utility" class="group">
|
|
||||||
<div class="section-menu" ng-class="{selected: selectedSlot=='utility'}" context-menu="emptyUtility()" ng-click="selectSlot($event, 'utility')">
|
|
||||||
<h1>
|
|
||||||
{{'utility mounts' | translate}}
|
|
||||||
<svg class="icon"><use xlink:href="#equalizer"></use></svg>
|
|
||||||
</h1>
|
|
||||||
<div class="select" ng-if="selectedSlot=='utility'">
|
|
||||||
<ul>
|
|
||||||
<li class="lc" ng-click="emptyUtility()" translate="empty all"></li>
|
|
||||||
</ul>
|
|
||||||
<div class="select-group cap" translate="sb"></div>
|
|
||||||
<ul>
|
|
||||||
<li class="c" ng-click="useUtility('sb','E')">E</li>
|
|
||||||
<li class="c" ng-click="useUtility('sb','D')">D</li>
|
|
||||||
<li class="c" ng-click="useUtility('sb','C')">C</li>
|
|
||||||
<li class="c" ng-click="useUtility('sb','B')">B</li>
|
|
||||||
<li class="c" ng-click="useUtility('sb','A')">A</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="slot" ng-repeat="h in ship.hardpoints | filter:{maxClass: '0'}" ng-click="selectSlot($event, h)" context-menu="select('h', h, $event, 'empty')" ng-class="{selected: selectedSlot==h}">
|
|
||||||
<div slot-hardpoint class="details" hp="h" size="HPC[h.maxClass]"></div>
|
|
||||||
<div class="select" ng-class="{hardpoint: h.maxClass > 0}" ng-if="selectedSlot==h" ng-click="select('h',h,$event)">
|
|
||||||
<div component-select s="h" groups="availCS.getHps(h.maxClass)"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="group half" id="componentPriority">
|
|
||||||
<table style="width:100%">
|
|
||||||
<thead>
|
|
||||||
<tr class="main">
|
|
||||||
<th colspan="2" class="sortable le" ng-click="sortPwr(cName)" translate="COMPONENT"></th>
|
|
||||||
<th style="width:3em;" class="sortable" ng-click="sortPwr('type')" translate="TYPE"></th>
|
|
||||||
<th style="width:4em;" class="sortable" ng-click="sortPwr('priority')" translate="PRI"></th>
|
|
||||||
<th colspan="2" class="sortable" ng-click="sortPwr('c.power')" translate="PWR"></th>
|
|
||||||
<th style="width:3em;" class="sortable" ng-click="sortPwr(statusRetracted)" translate="ret"></th>
|
|
||||||
<th style="width:3em;" class="sortable" ng-click="sortPwr(statusDeployed)" translate="dep"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>{{pp.c.class}}{{pp.c.rating}}</td>
|
|
||||||
<td class="le shorten cap" translate="pp"></td>
|
|
||||||
<td><u translate="SYS"></u></td>
|
|
||||||
<td>1</td>
|
|
||||||
<td class="ri">{{fPwr(pp.c.pGen)}}</td>
|
|
||||||
<td class="ri"><u>100%</u></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
<tr><td style="line-height:0;" colspan="8"><hr style="margin: 0 0 3px;background: #ff8c0d;border: 0;height: 1px;" /></td></tr>
|
|
||||||
<tr class="highlight" ng-repeat="c in powerList | orderBy:pwrPredicate:pwrDesc" ng-if="c.c.power" ng-class="{disabled:!c.enabled}">
|
|
||||||
<td style="width:1em;" ng-click="togglePwr(c)">{{c.c.class}}{{c.c.rating}}</td>
|
|
||||||
<td class="le shorten cap" ng-click="togglePwr(c)" ng-bind="cName(c)"></td>
|
|
||||||
<td ng-click="togglePwr(c)"><u ng-bind="c.type | translate"></u></td>
|
|
||||||
<td><span ng-click="decPriority(c)" class="flip">►</span> {{c.priority + 1}} <span ng-click="incPriority(c)">►</span></td>
|
|
||||||
<td class="ri" style="width:3.25em;" ng-click="togglePwr(c)">{{fPwr(c.c.power)}}</td>
|
|
||||||
<td class="ri" style="width:3em;" ng-click="togglePwr(c)"><u>{{f1Pct(c.c.power/ship.powerAvailable)}}</u></td>
|
|
||||||
<td ng-if="!c.enabled" class="disabled upp" colspan="2" translate="disabled" ng-click="togglePwr(c)"></td>
|
|
||||||
<td class="upp" ng-if="c.enabled" ng-click="togglePwr(c)">
|
|
||||||
<svg class="icon secondary-disabled" ng-if="statusRetracted(c) == 3"><use xlink:href="#power"><title class="cap">{{'on' | translate}}</title></use></svg>
|
|
||||||
<svg class="icon warning" ng-if="statusRetracted(c) == 2"><use xlink:href="#no-power"><title class="cap">{{'off' | translate}}</title></use></svg>
|
|
||||||
<span class="disabled" translate="disabled" ng-if="statusRetracted(c) == 1"></span>
|
|
||||||
</td>
|
|
||||||
<td class="upp" ng-if="c.enabled" ng-click="togglePwr(c)">
|
|
||||||
<svg class="icon secondary-disabled" ng-if="statusDeployed(c) == 3"><use xlink:href="#power"><title class="cap">{{'on' | translate}}</title></use></svg>
|
|
||||||
<svg class="icon warning" ng-if="statusDeployed(c) == 2"><use xlink:href="#no-power"><title class="cap">{{'off' | translate}}</title></use></svg>
|
|
||||||
<span class="disabled" translate="disabled" ng-if="statusDeployed(c) == 1"></span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div style="margin-top: 1em" power-bands bands="priorityBands" available="ship.powerAvailable"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="group half">
|
|
||||||
<table class="tabs">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style="width:34%" ng-class="{active: costTab == 'ammo'}" ng-click="updateCostTab('ammo')" translate="reload costs"></th>
|
|
||||||
<th style="width:33%" ng-class="{active: costTab == 'retrofit'}" ng-click="updateCostTab('retrofit')" translate="retrofit costs"></th>
|
|
||||||
<th style="width:33%" ng-class="{active: costTab == 'costs'}" ng-click="updateCostTab('costs')" translate="costs"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div ng-if="costTab == 'costs'">
|
|
||||||
<table style="width:100%">
|
|
||||||
<thead>
|
|
||||||
<tr class="main">
|
|
||||||
<th colspan="2" class="sortable le" ng-click="sortCost(cName)">
|
|
||||||
{{'component' | translate}}
|
|
||||||
<u class="optional-hide" ng-if="discounts.ship < 1">[{{'ship' | translate }} -{{fRPct(1 - discounts.ship)}}]</u>
|
|
||||||
<u class="optional-hide" ng-if="discounts.components < 1">[{{'components' | translate}} -{{fRPct(1 - discounts.components)}}]</u>
|
|
||||||
</th>
|
|
||||||
<th class="sortable le" ng-click="sortCost('discountedCost')" translate="credits"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr class="highlight" ng-repeat="item in costList | orderBy:costPredicate:costDesc" ng-if="item.c.cost > 0" ng-class="{disabled:!item.incCost}">
|
|
||||||
<td class="toggleable" style="width:1em;" ng-click="toggleCost(item)">{{item.c.class}}{{item.c.rating}}</td>
|
|
||||||
<td class="le toggleable shorten cap" ng-click="toggleCost(item)">{{cName(item)}}</td>
|
|
||||||
<td class="ri toggleable" ng-click="toggleCost(item)">{{fCrd(item.discountedCost)}} <u translate>CR</u></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<table class="total">
|
|
||||||
<tr class="ri">
|
|
||||||
<td class="lbl" translate="total"></td>
|
|
||||||
<td>{{fCrd(ship.totalCost)}} <u translate>CR</u></td>
|
|
||||||
</tr>
|
|
||||||
<tr class="ri">
|
|
||||||
<td class="lbl" translate="insurance"></td>
|
|
||||||
<td>{{fCrd(ship.totalCost * insurance.current.pct)}} <u translate>CR</u></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div ng-if="costTab == 'retrofit'">
|
|
||||||
<div class="scroll-x">
|
|
||||||
<table style="width:100%">
|
|
||||||
<thead>
|
|
||||||
<tr class="main">
|
|
||||||
<th colspan="2" class="sortable le" ng-click="sortRetrofit('sellName | translate')" translate="sell"></th>
|
|
||||||
<th colspan="2" class="sortable le" ng-click="sortRetrofit('buyName | translate')" translate="buy"></th>
|
|
||||||
<th class="sortable le" ng-click="sortRetrofit('netCost')">
|
|
||||||
{{'net cost' | translate}} <u class="optional-hide" ng-if="discounts.components < 1">[-{{fRPct(1 - discounts.components)}}]</u>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr ng-if="!retrofitList || retrofitList.length == 0">
|
|
||||||
<td colspan="5" style="padding: 3em 0;" translate="PHRASE_NO_RETROCH"></td>
|
|
||||||
</tr>
|
|
||||||
<tr class="highlight" ng-repeat="item in retrofitList | orderBy:retroPredicate:retroDesc" ng-click="toggleRetrofitCost(item.retroItem)" ng-class="{disabled: !item.retroItem.incCost}">
|
|
||||||
<td style="width:1em;">{{item.sellClassRating}}</td>
|
|
||||||
<td class="le shorten cap">{{item.sellName | translate}}</td>
|
|
||||||
<td style="width:1em;">{{item.buyClassRating}}</td>
|
|
||||||
<td class="le shorten cap">{{item.buyName | translate}}</td>
|
|
||||||
<td class="ri" ng-class="item.retroItem.incCost ? item.netCost > 0 ? 'warning' : 'secondary-disabled' : 'disabled'">{{ fCrd(item.netCost)}} <u translate>CR</u></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<table class="total">
|
|
||||||
<tr class="ri">
|
|
||||||
<td class="lbl" translate="cost"></td>
|
|
||||||
<td colspan="2" ng-class="retrofitTotal > 0 ? 'warning' : 'secondary-disabled'">{{fCrd(retrofitTotal)}} <u translate>CR</u></td>
|
|
||||||
</tr>
|
|
||||||
<tr class="ri">
|
|
||||||
<td class="lbl cap" translate="retrofit from"></td>
|
|
||||||
<td class="cen" style="border-right:none;width: 1em;"><u class="primary-disabled">▾</u></td>
|
|
||||||
<td style="border-left:none;padding:0;">
|
|
||||||
<select style="width: 100%;padding: 0" ng-model="$parent.retrofitBuild" ng-change="setRetrofitBase()" ng-options="name as name for (name, build) in allBuilds[ship.id]">
|
|
||||||
<option value="">{{'Stock' | translate}}</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div ng-if="costTab == 'ammo'">
|
|
||||||
<div class="scroll-x">
|
|
||||||
<table style="width:100%">
|
|
||||||
<thead>
|
|
||||||
<tr class="main">
|
|
||||||
<th colspan="2" class="sortable le" ng-click="sortAmmo('ammoName | translate')" translate="component"></th>
|
|
||||||
<th colspan="1" class="sortable le" ng-click="sortAmmo('ammoMax')" translate="quantity"></th>
|
|
||||||
<th colspan="1" class="sortable le" ng-click="sortAmmo('ammoUnitCost')" translate="unit cost"></th>
|
|
||||||
<th class="sortable le" ng-click="sortAmmo('ammoTotalCost')">
|
|
||||||
{{'total cost' | translate}} <u class="optional-hide" ng-if="discounts.ammo < 1">-[{{fRPct(1 - discounts.ammo)}}]</u>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr class="highlight" ng-repeat="item in ammoList | orderBy:ammoPredicate:ammoDesc">
|
|
||||||
<td style="width:1em;">{{item.ammoClassRating}}</td>
|
|
||||||
<td class="le shorten cap">{{item.ammoName | translate}}</td>
|
|
||||||
<td class="ri">{{fCrd(item.ammoMax)}}</td>
|
|
||||||
<td class="ri">{{fCrd(item.ammoUnitCost)}} <u translate>CR</u></td>
|
|
||||||
<td class="ri">{{fCrd(item.ammoTotalCost)}} <u translate>CR</u></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<table class="total">
|
|
||||||
<tr class="ri">
|
|
||||||
<td class="lbl" translate="total"></td>
|
|
||||||
<td>{{fCrd(ammoTotal)}} <u translate>CR</u></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="group third">
|
|
||||||
<h1 translate="jump range"></h1>
|
|
||||||
<div line-chart config="jrChart" series="jrSeries"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="group third">
|
|
||||||
<h1 translate="total range"></h1>
|
|
||||||
<div line-chart config="trChart" series="trSeries"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="group third">
|
|
||||||
<h1 translate="speed"></h1>
|
|
||||||
<div line-chart config="speedChart" series="speedSeries"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="group half">
|
|
||||||
<div slider max="ship.fuelCapacity" unit="'T'" on-change="::fuelChange(val)" style="position:relative; margin: 0 auto;">
|
|
||||||
<svg class="icon xl primary-disabled" style="position:absolute;height: 100%;"><use xlink:href="#fuel"></use></svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
@@ -6,16 +6,16 @@ var ExtractTextPlugin = require("extract-text-webpack-plugin");
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
devtool: 'eval',
|
devtool: 'eval',
|
||||||
|
devServer: {
|
||||||
|
headers: { "Access-Control-Allow-Origin": "*" }
|
||||||
|
},
|
||||||
entry: {
|
entry: {
|
||||||
app: [ 'webpack-dev-server/client?http://localhost:3300', 'webpack/hot/only-dev-server', path.join(__dirname, "src/app/index.js") ],
|
app: [ 'webpack-dev-server/client?http://localhost:3300', 'webpack/hot/only-dev-server', path.join(__dirname, "src/app/index.js") ],
|
||||||
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
|
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
// When requiring, you don't need to add these extensions
|
// When requiring, you don't need to add these extensions
|
||||||
extensions: ['', '.js', '.jsx', '.json', '.less'],
|
extensions: ['', '.js', '.jsx', '.json', '.less']
|
||||||
alias: {
|
|
||||||
'coriolis-data': path.resolve(__dirname, 'node_modules/coriolis-data')
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: path.join(__dirname, 'build'),
|
path: path.join(__dirname, 'build'),
|
||||||
@@ -27,7 +27,6 @@ module.exports = {
|
|||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
inject: false,
|
inject: false,
|
||||||
template: path.join(__dirname, "src/index.html"),
|
template: path.join(__dirname, "src/index.html"),
|
||||||
cdn: '',
|
|
||||||
version: pkgJson.version
|
version: pkgJson.version
|
||||||
}),
|
}),
|
||||||
new ExtractTextPlugin('app.css', {
|
new ExtractTextPlugin('app.css', {
|
||||||
@@ -45,9 +44,7 @@ module.exports = {
|
|||||||
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' },
|
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' },
|
||||||
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' },
|
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' },
|
||||||
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream' },
|
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream' },
|
||||||
{ test: /\.(png|jpg|jpeg|gif|ico)$/, loader: 'file-loader?name=/images/[name].[ext]' },
|
|
||||||
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file' },
|
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file' },
|
||||||
{ test: /\.xml$/, loader: 'file' },
|
|
||||||
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml' }
|
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,14 +23,12 @@ CopyDirPlugin.prototype.apply = function(compiler) {
|
|||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: {
|
entry: {
|
||||||
app: path.resolve(__dirname, 'src/app/index'),
|
app: path.resolve(__dirname, 'src/app/index'),
|
||||||
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
|
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
// When requiring, you don't need to add these extensions
|
|
||||||
extensions: ['', '.js', '.jsx', '.json', '.less'],
|
extensions: ['', '.js', '.jsx', '.json', '.less'],
|
||||||
alias: {
|
alias: {
|
||||||
'd3': d3Path,
|
'd3': d3Path,
|
||||||
@@ -42,7 +40,8 @@ module.exports = {
|
|||||||
output: {
|
output: {
|
||||||
path: path.join(__dirname, 'build'),
|
path: path.join(__dirname, 'build'),
|
||||||
filename: '[name].[hash:6].js',
|
filename: '[name].[hash:6].js',
|
||||||
publicPath: '//cdn.coriolis.io/'
|
chunkFilename: '[name].[hash:6]',
|
||||||
|
publicPath: process.env.CDN || '/'
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.optimize.UglifyJsPlugin({
|
new webpack.optimize.UglifyJsPlugin({
|
||||||
@@ -54,7 +53,7 @@ module.exports = {
|
|||||||
new webpack.optimize.CommonsChunkPlugin('lib', 'lib.[hash:6].js'),
|
new webpack.optimize.CommonsChunkPlugin('lib', 'lib.[hash:6].js'),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
inject: false,
|
inject: false,
|
||||||
appCache: 'coriolis.appcache'
|
appCache: 'coriolis.appcache',
|
||||||
minify: {
|
minify: {
|
||||||
collapseBooleanAttributes: true,
|
collapseBooleanAttributes: true,
|
||||||
collapseWhitespace: true,
|
collapseWhitespace: true,
|
||||||
@@ -74,6 +73,7 @@ module.exports = {
|
|||||||
allChunks: true
|
allChunks: true
|
||||||
}),
|
}),
|
||||||
new CopyDirPlugin(path.join(__dirname, 'src/schemas'), 'schemas'),
|
new CopyDirPlugin(path.join(__dirname, 'src/schemas'), 'schemas'),
|
||||||
|
new CopyDirPlugin(path.join(__dirname, 'src/images/logo/*'), ''),
|
||||||
new AppCachePlugin({
|
new AppCachePlugin({
|
||||||
network: ['*'],
|
network: ['*'],
|
||||||
settings: ['prefer-online'],
|
settings: ['prefer-online'],
|
||||||
@@ -84,6 +84,11 @@ module.exports = {
|
|||||||
module: {
|
module: {
|
||||||
noParse: [d3Path, reactPath, lzStringPath],
|
noParse: [d3Path, reactPath, lzStringPath],
|
||||||
loaders: [
|
loaders: [
|
||||||
|
// Expose non-parsed globally scoped libs
|
||||||
|
{ test: reactPath, loader: "expose?React" },
|
||||||
|
{ test: d3Path, loader: "expose?d3" },
|
||||||
|
{ test: lzStringPath, loader: "expose?LZString" },
|
||||||
|
|
||||||
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader','css-loader') },
|
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader','css-loader') },
|
||||||
{ test: /\.less$/, loader: ExtractTextPlugin.extract('style-loader','css-loader!less-loader') },
|
{ test: /\.less$/, loader: ExtractTextPlugin.extract('style-loader','css-loader!less-loader') },
|
||||||
{ test: /\.(js|jsx)$/, loaders: [ 'babel' ], include: path.join(__dirname, 'src') },
|
{ test: /\.(js|jsx)$/, loaders: [ 'babel' ], include: path.join(__dirname, 'src') },
|
||||||
@@ -92,9 +97,7 @@ module.exports = {
|
|||||||
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' },
|
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' },
|
||||||
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream' },
|
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream' },
|
||||||
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file' },
|
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file' },
|
||||||
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml' },
|
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml' }
|
||||||
{ test: /\.xml$/, loader: 'file' },
|
|
||||||
{ test: /\.(png|jpg|jpeg|gif|ico)$/, loader: 'file-loader?name=/images/[name].[hash:6].[ext]' }
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user