Compare commits

..

56 Commits

Author SHA1 Message Date
William Blythe
beb19a07ec more work 2018-10-24 10:31:11 +11:00
William Blythe
98113e8807 initial watermelondb 2018-10-24 09:10:53 +11:00
William
dfffc3a268 Fix/no adsense (#412)
* No adsense

* remove script tag
2018-10-24 09:06:24 +11:00
William
b59fa15e00 Merge pull request #411 from EDCD/fix/chart-labels
Fix VerticalBarchart labels
2018-10-24 07:52:58 +11:00
felixlinker
12bca4c44e Fix VerticalBarchart labels 2018-10-23 21:48:07 +01:00
William Blythe
7d99471f89 sw 2018-10-23 12:41:56 +11:00
William Blythe
a2ab708ac9 format 2018-10-23 12:14:28 +11:00
William Blythe
a34a9c355f fix sw not registered 2018-10-23 11:57:07 +11:00
William Blythe
557c0afd9b Merge branch 'master' into develop 2018-10-23 11:50:58 +11:00
William Blythe
d52365a204 /shrug 2018-10-23 09:55:42 +11:00
felixlinker
14b2a14e58 Merge branch 'master' into develop 2018-10-22 23:52:09 +01:00
William Blythe
7f24904f77 various bits 2018-10-23 09:46:50 +11:00
William Blythe
da07790594 various bits 2018-10-23 09:40:13 +11:00
William
5008c7cd74 Update sw.js 2018-10-23 08:15:54 +11:00
willyb321
a778b1b6e1 update 2018-10-22 17:15:09 +11:00
William Blythe
bd9771f9ba test run 2018-10-22 11:49:15 +11:00
William Blythe
600c244f9b test run 2018-10-22 11:44:14 +11:00
William Blythe
a599b1a076 remove d3 rollup that doesnt seem to serve a purpose 2018-10-22 09:21:33 +11:00
willyb321
3e0a5e22b1 Merge branch 'develop' 2018-10-21 10:21:04 +11:00
willyb321
3a6ac818c2 reload anyway 2018-10-21 10:17:53 +11:00
willyb321
6f077d4c41 use register-service-worker 2018-10-21 10:16:31 +11:00
willyb321
9c767c928c fix icons and manifest
was not flattened
2018-10-21 10:03:01 +11:00
willyb321
515f4ad3da sw work 2018-10-21 09:50:15 +11:00
willyb321
4fcf074595 dont git reset in docker 2018-10-21 08:37:08 +11:00
willyb321
e5f8153a34 add announcements to menu 2018-10-21 08:27:53 +11:00
William
571854a11c Merge pull request #405 from EDCD/feature/code-quality
Cosmetical improvements
2018-10-20 11:00:58 +11:00
William
1f22f249a1 Merge pull request #407 from EDCD/feature/number-inputs
Enhance number editing
2018-10-20 11:00:47 +11:00
felixlinker
718ac0a514 Enhance number editing 2018-10-13 22:29:12 +01:00
willyb321
8f089cb1ee hopefully fix some sw bugs 2018-10-13 09:22:16 +11:00
felixlinker
d19a7276dd Optimized imports 2018-10-05 00:06:57 +01:00
felixlinker
10fffe67fc Code style fixes 2018-10-05 00:06:33 +01:00
William Blythe
f0bf8e8ce2 import babel polyfill 2018-10-03 14:17:56 +10:00
William
598cf8d677 Merge pull request #404 from EDCD/feature/fix-imports
Fix journal import
2018-10-02 10:14:02 +10:00
felixlinker
90f03de3fe Fix journal import 2018-10-01 22:24:18 +01:00
William
e0766f4424 Merge pull request #400 from EDCD/feature/fixes
Various fixes
2018-09-30 06:21:06 +10:00
felixlinker
28a90768e4 Start searching for available slots at 0 because the Type9 starts there 2018-09-29 20:26:40 +01:00
felixlinker
f3d917ccbe Fixed trailing zero check in journal style import 2018-09-29 20:04:40 +01:00
felixlinker
7e5d52385d Fixed loops in journal style import 2018-09-29 20:03:58 +01:00
felixlinker
4368015dc0 Fixed variable assignement that should be a comparison 2018-09-29 17:45:45 +01:00
felixlinker
1201da1811 Differentiate between uneditable and un-highlighted properties in the modifications menu 2018-09-29 16:40:36 +01:00
felixlinker
d195b568b0 eslint indentation fixes 2018-09-29 01:03:00 +01:00
felixlinker
c9866c146b Stop dividing burst two times by 100 2018-09-29 00:55:37 +01:00
felixlinker
5d52809d0d Catch undefined values in ship build parsing 2018-09-28 23:28:46 +01:00
willyb321
8f0cca4fd9 fix 2018-09-29 08:06:15 +10:00
willyb321
e46bb425fe minimize in prod 2018-09-29 07:50:36 +10:00
willyb321
06dc110025 migrate to babel 7, webpack 4, dep updates, lots 2018-09-29 07:49:04 +10:00
felixlinker
e9c34c636a Don't filter values but map falsy ones to zero 2018-09-28 22:35:07 +01:00
felixlinker
59d38cbd33 use react-extras instead of auto-bind 2018-09-26 23:15:09 +01:00
William
51f5188efc Merge pull request #393 from EDCD/feature/mc-pips
Add multicrew pips
2018-09-25 07:15:44 +10:00
felixlinker
be8934da80 Added multi crew pips 2018-09-23 22:30:05 +01:00
felixlinker
18d78b3089 Added auto-bind dependency 2018-09-23 22:29:45 +01:00
willyb321
b1ff4e84f7 Merge branch 'master' into develop 2018-09-23 11:42:45 +10:00
willyb321
bed2ede701 Merge branch 'release/3.0.0' 2018-09-23 11:42:44 +10:00
willyb321
124bd62d2c 3.0.0 2018-09-23 11:42:38 +10:00
willyb321
975846f4ab Merge branch 'master' into develop 2018-09-23 11:41:23 +10:00
willyb321
3f73f9be10 Merge branch 'release/2.9.18' 2018-09-23 11:41:22 +10:00
65 changed files with 7866 additions and 8144 deletions

View File

@@ -1,3 +1,51 @@
{ {
"presets": ["env", "react", "stage-0"] "presets": [
[
"@babel/preset-env",
{
"modules": "commonjs"
}
],
"@babel/preset-react"
],
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
],
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-syntax-import-meta",
[
"@babel/plugin-proposal-class-properties",
{
"loose": true
}
],
"@babel/plugin-proposal-json-strings",
"@babel/plugin-proposal-function-sent",
"@babel/plugin-proposal-export-namespace-from",
"@babel/plugin-proposal-numeric-separator",
"@babel/plugin-proposal-throw-expressions",
"@babel/plugin-proposal-export-default-from",
"@babel/plugin-proposal-logical-assignment-operators",
"@babel/plugin-proposal-optional-chaining",
[
"@babel/plugin-proposal-pipeline-operator",
{
"proposal": "minimal"
}
],
[
"@babel/plugin-transform-runtime",
{
"helpers": true,
"regenerator": true
}
],
"@babel/plugin-proposal-nullish-coalescing-operator",
"@babel/plugin-proposal-do-expressions",
"@babel/plugin-proposal-function-bind"
]
} }

View File

@@ -17,13 +17,11 @@ RUN npm i -g npm
# Set up coriolis-data # Set up coriolis-data
WORKDIR /src/app/coriolis-data WORKDIR /src/app/coriolis-data
RUN git fetch --all RUN git fetch --all
RUN git reset --hard origin/$BRANCH
RUN npm install --no-package-lock RUN npm install --no-package-lock
RUN npm start RUN npm start
WORKDIR /src/app/coriolis WORKDIR /src/app/coriolis
RUN git fetch --all RUN git fetch --all
RUN git reset --hard origin/$BRANCH
RUN npm install --no-package-lock RUN npm install --no-package-lock
RUN npm run build RUN npm run build

11
d3-funcs.js vendored
View File

@@ -1,11 +0,0 @@
export {
axisBottom,
axisLeft,
axisTop,
formatLocale,
line,
scaleBand,
scaleLinear,
scaleOrdinal,
select
} from 'd3';

12659
d3.js vendored

File diff suppressed because it is too large Load Diff

4
d3.min.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,125 +1,149 @@
{ {
"name": "coriolis_shipyard", "name": "coriolis_shipyard",
"version": "2.9.18", "version": "3.0.0",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/EDCD/coriolis" "url": "https://github.com/EDCD/coriolis"
}, },
"homepage": "https://coriolis.edcd.io", "homepage": "https://coriolis.edcd.io",
"bugs": "https://github.com/EDCD/coriolis/issues", "bugs": "https://github.com/EDCD/coriolis/issues",
"private": true, "private": true,
"engine": "node >= 4.8.1", "engine": "node >= 4.8.1",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"prepublish": "rollup -c && uglifyjs d3.js -c -m -o d3.min.js", "extract-translations": "grep -hroE \"(translate\\('[^']+'\\))|(tip.bind\\(null, '[^']+')\" src/* | grep -oE \"'[^']+'\" | grep -oE \"[^']+\" | sort -u -f",
"extract-translations": "grep -hroE \"(translate\\('[^']+'\\))|(tip.bind\\(null, '[^']+')\" src/* | grep -oE \"'[^']+'\" | grep -oE \"[^']+\" | sort -u -f", "clean": "rimraf build",
"clean": "rimraf build", "start": "node devServer.js",
"start": "node devServer.js", "lint": "eslint --ext .js,.jsx src",
"lint": "eslint --ext .js,.jsx src", "test": "jest",
"test": "jest", "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": "npm run clean && cross-env NODE_ENV=production webpack -p --config webpack.config.prod.js",
"build": "npm run clean && cross-env NODE_ENV=production webpack -p --config webpack.config.prod.js", "rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws",
"rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws", "deploy": "npm run lint && npm test && npm run build && npm run rsync"
"deploy": "npm run lint && npm test && npm run build && npm run rsync" },
}, "jest": {
"jest": { "transform": {
"transform": { ".*": "<rootDir>/node_modules/babel-jest"
".*": "<rootDir>/node_modules/babel-jest" },
}, "testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$",
"testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$", "moduleFileExtensions": [
"moduleFileExtensions": [ "js",
"js", "json",
"json", "jsx"
"jsx" ],
], "automock": true,
"automock": true, "bail": false,
"bail": false, "unmockedModulePathPatterns": [
"unmockedModulePathPatterns": [ "<rootDir>/node_modules/lodash",
"<rootDir>/node_modules/lodash", "<rootDir>/node_modules/react",
"<rootDir>/node_modules/react", "<rootDir>/node_modules/react-dom",
"<rootDir>/node_modules/react-dom", "<rootDir>/node_modules/react-transition-group",
"<rootDir>/node_modules/react-transition-group", "<rootDir>/node_modules/react-testutils-additions",
"<rootDir>/node_modules/react-testutils-additions", "<rootDir>/node_modules/fbjs",
"<rootDir>/node_modules/fbjs", "<rootDir>/node_modules/fbemitter",
"<rootDir>/node_modules/fbemitter", "<rootDir>/node_modules/classnames",
"<rootDir>/node_modules/classnames", "<rootDir>/node_modules/d3",
"<rootDir>/node_modules/d3", "<rootDir>/node_modules/lz-string",
"<rootDir>/node_modules/lz-string", "<rootDir>/node_modules/jsen",
"<rootDir>/node_modules/jsen", "coriolis-data",
"coriolis-data", "<rootDir>/src/app/shipyard",
"<rootDir>/src/app/shipyard", "<rootDir>/src/app/i18n",
"<rootDir>/src/app/i18n", "<rootDir>/src/app/utils",
"<rootDir>/src/app/utils", "<rootDir>/src/schemas",
"<rootDir>/src/schemas", "<rootDir>/__tests__"
"<rootDir>/__tests__" ]
] },
}, "devDependencies": {
"devDependencies": { "@babel/core": "^7.0.0",
"appcache-webpack-plugin": "^1.3.0", "@babel/plugin-proposal-class-properties": "^7.1.0",
"babel-core": "*", "@babel/plugin-proposal-decorators": "^7.1.2",
"babel-eslint": "*", "@babel/plugin-proposal-do-expressions": "^7.0.0",
"babel-jest": "*", "@babel/plugin-proposal-export-default-from": "^7.0.0",
"babel-loader": "*", "@babel/plugin-proposal-export-namespace-from": "^7.0.0",
"babel-preset-env": "*", "@babel/plugin-proposal-function-bind": "^7.0.0",
"babel-preset-react": "*", "@babel/plugin-proposal-function-sent": "^7.0.0",
"babel-preset-stage-0": "*", "@babel/plugin-proposal-json-strings": "^7.0.0",
"create-react-class": "^15.6.2", "@babel/plugin-proposal-logical-assignment-operators": "^7.0.0",
"cross-env": "^5.1.4", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
"css-loader": "^0.28.0", "@babel/plugin-proposal-numeric-separator": "^7.0.0",
"d3-selection": "1", "@babel/plugin-proposal-optional-chaining": "^7.0.0",
"esdoc": "^1.1.0", "@babel/plugin-proposal-pipeline-operator": "^7.0.0",
"esdoc-custom-theme": "^1.4.2", "@babel/plugin-proposal-throw-expressions": "^7.0.0",
"esdoc-ecmascript-proposal-plugin": "^1.0.0", "@babel/plugin-syntax-dynamic-import": "^7.0.0",
"esdoc-jsx-plugin": "^1.0.0", "@babel/plugin-syntax-import-meta": "^7.0.0",
"esdoc-publish-html-plugin": "^1.1.2", "@babel/plugin-transform-runtime": "^7.1.0",
"esdoc-react-plugin": "^1.0.1", "@babel/preset-env": "^7.0.0",
"esdoc-standard-plugin": "^1.0.0", "@babel/preset-react": "^7.0.0",
"eslint": "3.19.0", "appcache-webpack-plugin": "^1.4.0",
"eslint-plugin-react": "^6.10.3", "babel-eslint": "^10.0.1",
"expose-loader": "^0.7.3", "babel-jest": "^23.4.2",
"express": "^4.15.2", "babel-loader": "^8.0.0",
"extract-text-webpack-plugin": "2.1.0", "copy-webpack-plugin": "^4.5.2",
"file-loader": "^0.11.1", "create-react-class": "^15.6.3",
"html-webpack-plugin": "^2.28.0", "cross-env": "^5.2.0",
"jest-cli": "^21.2.1", "css-loader": "^1.0.0",
"jsen": "^0.6.4", "d3-selection": "^1.3.2",
"json-loader": "^0.5.4", "esdoc": "^1.1.0",
"less": "^2.7.2", "esdoc-custom-theme": "^1.4.2",
"less-loader": "^4.0.3", "esdoc-ecmascript-proposal-plugin": "^1.0.0",
"react-addons-perf": "^15.4.2", "esdoc-jsx-plugin": "^1.0.0",
"react-container-dimensions": "^1.4.1", "esdoc-publish-html-plugin": "^1.1.2",
"react-testutils-additions": "^15.2.0", "esdoc-react-plugin": "^1.0.1",
"react-transition-group": "^1.1.2", "esdoc-standard-plugin": "^1.0.0",
"rimraf": "^2.6.1", "eslint": "^5.6.0",
"rollup": "0.41", "eslint-plugin-react": "^7.11.1",
"rollup-plugin-node-resolve": "3", "expose-loader": "^0.7.5",
"style-loader": "^0.16.1", "express": "^4.16.3",
"uglify-js": "^2.4.11", "extract-text-webpack-plugin": "^4.0.0-beta.0",
"url-loader": "^0.5.8", "file-loader": "^2.0.0",
"webpack": "^2.4.1", "html-webpack-plugin": "^3.0.7",
"webpack-bugsnag-plugins": "^1.1.1", "jest-cli": "^23.6.0",
"webpack-dev-server": "^2.4.4", "jsen": "^0.6.4",
"webpack-notifier": "^1.6.0", "json-loader": "^0.5.4",
"workbox-webpack-plugin": "^3.4.1" "less": "^3.8.1",
}, "less-loader": "^4.1.0",
"dependencies": { "react-addons-perf": "^15.4.2",
"babel-polyfill": "*", "react-container-dimensions": "^1.4.1",
"browserify-zlib-next": "^1.0.1", "react-testutils-additions": "^16.0.0",
"classnames": "^2.2.5", "react-transition-group": "^2.5.0",
"coriolis-data": "../coriolis-data", "rimraf": "^2.6.1",
"d3": "4.8.0", "rollup": "^0.66.2",
"detect-browser": "^1.7.0", "rollup-plugin-node-resolve": "^3.4.0",
"fbemitter": "^2.1.1", "style-loader": "^0.23.0",
"lodash": "^4.17.10", "uglify-js": "^3.4.9",
"lz-string": "^1.4.4", "url-loader": "^1.1.1",
"pako": "^1.0.6", "webpack": "^4.20.2",
"prop-types": "^15.5.8", "webpack-bugsnag-plugins": "^1.2.2",
"react": "^15.5.4", "webpack-cli": "^3.1.1",
"react-dom": "^15.5.4", "webpack-dev-server": "^3.1.9",
"react-ga": "^2.5.3", "webpack-notifier": "^1.6.0",
"react-number-editor": "Athanasius/react-number-editor.git#miggy", "workbox-webpack-plugin": "^3.6.1",
"recharts": "^0.22.3", "worker-loader": "^2.0.0"
"superagent": "^3.5.2" },
} "dependencies": {
} "@babel/polyfill": "^7.0.0",
"@babel/runtime": "^7.1.2",
"@nozbe/watermelondb": "^0.6.2",
"babel-runtime": "^6.26.0",
"browserify-zlib-next": "^1.0.1",
"classnames": "^2.2.6",
"coriolis-data": "../coriolis-data",
"d3": "^5.7.0",
"detect-browser": "^3.0.1",
"fbemitter": "^2.1.1",
"lodash": "^4.17.11",
"lokijs": "^1.5.5",
"lz-string": "^1.4.4",
"pako": "^1.0.6",
"prop-types": "^15.6.2",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-extras": "^0.7.1",
"react-ga": "^2.5.3",
"react-number-editor": "Athanasius/react-number-editor.git#miggy",
"recharts": "^1.2.0",
"register-service-worker": "^1.5.2",
"superagent": "^3.8.3"
}
}

View File

@@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Router from './Router'; import Router from './Router';
import { register } from 'register-service-worker'
import { EventEmitter } from 'fbemitter'; import { EventEmitter } from 'fbemitter';
import { getLanguage } from './i18n/Language'; import { getLanguage } from './i18n/Language';
import Persist from './stores/Persist'; import Persist from './stores/Persist';
@@ -22,12 +23,12 @@ import ShipyardPage from './pages/ShipyardPage';
import ErrorDetails from './pages/ErrorDetails'; import ErrorDetails from './pages/ErrorDetails';
const zlib = require('pako'); const zlib = require('pako');
const request = require('superagent');
/** /**
* Coriolis App * Coriolis App
*/ */
export default class Coriolis extends React.Component { export default class Coriolis extends React.Component {
static childContextTypes = { static childContextTypes = {
closeMenu: PropTypes.func.isRequired, closeMenu: PropTypes.func.isRequired,
hideModal: PropTypes.func.isRequired, hideModal: PropTypes.func.isRequired,
@@ -66,11 +67,12 @@ export default class Coriolis extends React.Component {
this.state = { this.state = {
noTouch: !('ontouchstart' in window || navigator.msMaxTouchPoints || navigator.maxTouchPoints), noTouch: !('ontouchstart' in window || navigator.msMaxTouchPoints || navigator.maxTouchPoints),
page: null, page: null,
announcements: [],
language: getLanguage(Persist.getLangCode()), language: getLanguage(Persist.getLangCode()),
route: {}, route: {},
sizeRatio: Persist.getSizeRatio() sizeRatio: Persist.getSizeRatio()
}; };
this._getAnnouncements()
Router('', (r) => this._setPage(ShipyardPage, r)); Router('', (r) => this._setPage(ShipyardPage, r));
Router('/import?', (r) => this._importBuild(r)); Router('/import?', (r) => this._importBuild(r));
Router('/import/:data', (r) => this._importBuild(r)); Router('/import/:data', (r) => this._importBuild(r));
@@ -109,6 +111,14 @@ export default class Coriolis extends React.Component {
} }
} }
_getAnnouncements() {
return request.get('https://orbis.zone/api/announcement')
.query({showInCoriolis: true})
.then(announces => {
this.setState({ announcements: announces.body })
})
}
/** /**
* Updates / Sets the page and route context * Updates / Sets the page and route context
* @param {[type]} page The page to be shown * @param {[type]} page The page to be shown
@@ -334,47 +344,37 @@ export default class Coriolis extends React.Component {
}); });
} }
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
window.addEventListener('load', () => { // Your service-worker.js *must* be located at the top-level directory relative to your site.
// Your service-worker.js *must* be located at the top-level directory relative to your site. // It won't be able to control pages unless it's located at the same level or higher than them.
// It won't be able to control pages unless it's located at the same level or higher than them. // *Don't* register service worker file in, e.g., a scripts/ sub-directory!
// *Don't* register service worker file in, e.g., a scripts/ sub-directory! // See https://github.com/slightlyoff/ServiceWorker/issues/468
// See https://github.com/slightlyoff/ServiceWorker/issues/468 const self = this;
const self = this; if (process.env.NODE_ENV === 'production') {
navigator.serviceWorker.register('/service-worker.js').then(function(reg) { register('/service-worker.js', {
// updatefound is fired if service-worker.js changes. ready (registration) {
reg.onupdatefound = function() { console.log('Service worker is active.')
// The updatefound event implies that reg.installing is set; see },
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event registered (registration) {
var installingWorker = reg.installing; console.log('Service worker has been registered.')
},
installingWorker.onstatechange = function() { cached (registration) {
switch (installingWorker.state) { console.log('Content has been cached for offline use.')
case 'installed': },
if (navigator.serviceWorker.controller) { updatefound (registration) {
// At this point, the old content will have been purged and the fresh content will console.log('New content is downloading.')
// have been added to the cache. },
// It's the perfect time to display a "New content is available; please refresh." updated (registration) {
// message in the page's interface. self.setState({ appCacheUpdate: true });
console.log('New or updated content is available.'); console.log('New content is available; please refresh.')
self.setState({ appCacheUpdate: true }); // Browser downloaded a new app cache. },
} else { offline () {
// At this point, everything has been precached. console.log('No internet connection found. App is running in offline mode.')
// It's the perfect time to display a "Content is cached for offline use." message. },
console.log('Content is now available offline!'); error (error) {
self.setState({ appCacheUpdate: true }); // Browser downloaded a new app cache. console.error('Error during service worker registration:', error)
} }
break;
case 'redundant':
console.error('The installing service worker became redundant.');
break;
}
};
};
}).catch(function(e) {
console.error('Error during service worker registration:', e);
}); });
}); }
} }
window.onerror = this._onError.bind(this); window.onerror = this._onError.bind(this);
window.addEventListener('resize', () => this.emitter.emit('windowResize')); window.addEventListener('resize', () => this.emitter.emit('windowResize'));
@@ -394,20 +394,20 @@ export default class Coriolis extends React.Component {
let currentMenu = this.state.currentMenu; let currentMenu = this.state.currentMenu;
return <div style={{ minHeight: '100%' }} onClick={this._closeMenu} return <div style={{ minHeight: '100%' }} onClick={this._closeMenu}
className={this.state.noTouch ? 'no-touch' : null}> className={this.state.noTouch ? 'no-touch' : null}>
<Header appCacheUpdate={this.state.appCacheUpdate} currentMenu={currentMenu}/> <Header announcements={this.state.announcements} appCacheUpdate={this.state.appCacheUpdate} currentMenu={currentMenu}/>
{this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) : {this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) :
<NotFoundPage/>} <NotFoundPage/>}
{this.state.modal} {this.state.modal}
{this.state.tooltip} {this.state.tooltip}
<footer> <footer>
<div className="right cap"> <div className="right cap">
<a href="https://github.com/EDCD/coriolis" target="_blank" <a href="https://github.com/EDCD/coriolis" target="_blank" rel="noopener noreferrer"
title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a> title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a>
<br/> <br/>
<a <a
href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'} href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'}
target="_blank" title={'Coriolis Commits since' + window.CORIOLIS_DATE}>Commits since last release target="_blank" rel="noopener noreferrer" title={'Coriolis Commits since' + window.CORIOLIS_DATE}>Commits since last release
({window.CORIOLIS_DATE})</a> ({window.CORIOLIS_DATE})</a>
</div> </div>
</footer> </footer>

View File

@@ -16,7 +16,6 @@ function isActive(href) {
* Active Link - Highlighted when URL matches window location * Active Link - Highlighted when URL matches window location
*/ */
export default class ActiveLink extends Link { export default class ActiveLink extends Link {
/** /**
* Renders the component * Renders the component
* @return {React.Component} The active link * @return {React.Component} The active link
@@ -29,5 +28,4 @@ export default class ActiveLink extends Link {
return <a {...this.props} className={className} onClick={this.handler}>{this.props.children}</a>; return <a {...this.props} className={className} onClick={this.handler}>{this.props.children}</a>;
} }
}
}

34
src/app/components/Ad.jsx Normal file
View File

@@ -0,0 +1,34 @@
import React, { Component, PropTypes } from 'react';
export default class Ad extends Component {
static propTypes = {
client: PropTypes.string,
slot: PropTypes.string,
format: PropTypes.string,
wrapperDivStyle: PropTypes.object
};
constructor(props) {
super(props);
}
// This code is ran when the component mounts
componentDidMount() {
}
// an outer div for styling purposes
// changed class to ClassName
// changed style from string to an object
render() {
return (
<div style={this.props.wrapperDivStyle}>
<ins
className="adsbygoogle"
style={{ display: 'block' }}
data-ad-client={this.props.client}
data-ad-slot={this.props.slot}
/>
</div>
);
}
}

View File

@@ -0,0 +1,31 @@
import React from 'react';
import PropTypes from 'prop-types';
import { autoBind } from 'react-extras';
/**
* Announcement component
*/
export default class Announcement extends React.Component {
static propTypes = {
text: PropTypes.string
};
/**
* Constructor
* @param {Object} props React Component properties
*/
constructor(props) {
super(props);
autoBind(this);
}
/**
* Renders the announcement
* @return {React.Component} A href element
*/
render() {
return <p>{this.props.text}</p>;
}
}

View File

@@ -98,7 +98,7 @@ const CATEGORIES = {
'defence': ['ch', 'po', 'ec'], 'defence': ['ch', 'po', 'ec'],
'scanners': ['sc', 'ss', 'cs', 'kw', 'ws'], // Overloaded with internal scanners 'scanners': ['sc', 'ss', 'cs', 'kw', 'ws'], // Overloaded with internal scanners
// Experimental // Experimental
'experimental': ['axmc', 'axmr', 'rfl', 'tbrfl', 'tbsc', 'tbem', 'xs', 'sfn', 'rcpl', 'dtl', 'rsl', 'mahr', ], 'experimental': ['axmc', 'axmr', 'rfl', 'tbrfl', 'tbsc', 'tbem', 'xs', 'sfn', 'rcpl', 'dtl', 'rsl', 'mahr',],
// Guardian // Guardian
'guardian': ['gpp', 'gpd', 'gpc', 'ggc', 'gsrp', 'gfsb', 'ghrp', 'gmrp', 'gsc'] 'guardian': ['gpp', 'gpd', 'gpc', 'ggc', 'gsrp', 'gfsb', 'ghrp', 'gmrp', 'gsc']
@@ -108,7 +108,6 @@ const CATEGORIES = {
* Available modules menu * Available modules menu
*/ */
export default class AvailableModulesMenu extends TranslatedComponent { export default class AvailableModulesMenu extends TranslatedComponent {
static propTypes = { static propTypes = {
modules: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired, modules: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired,
onSelect: PropTypes.func.isRequired, onSelect: PropTypes.func.isRequired,
@@ -159,7 +158,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
this._hideDiff(event); this._hideDiff(event);
onSelect(m); onSelect(m);
} }
); );
if (modules instanceof Array) { if (modules instanceof Array) {
list = buildGroup(modules[0].grp, modules); list = buildGroup(modules[0].grp, modules);
@@ -502,14 +501,13 @@ export default class AvailableModulesMenu extends TranslatedComponent {
render() { render() {
return ( return (
<div ref={node => this.node = node} <div ref={node => this.node = node}
className={cn('select', this.props.className)} className={cn('select', this.props.className)}
onScroll={this._hideDiff} onScroll={this._hideDiff}
onClick={(e) => e.stopPropagation() } onClick={(e) => e.stopPropagation() }
onContextMenu={stopCtxPropagation} onContextMenu={stopCtxPropagation}
> >
{this.state.list} {this.state.list}
</div> </div>
); );
} }
} }

View File

@@ -38,7 +38,6 @@ function insertLinebreaks(d) {
* Bar Chart * Bar Chart
*/ */
export default class BarChart extends TranslatedComponent { export default class BarChart extends TranslatedComponent {
static defaultProps = { static defaultProps = {
colors: ['#7b6888', '#6b486b', '#3182bd', '#a05d56', '#d0743c'], colors: ['#7b6888', '#6b486b', '#3182bd', '#a05d56', '#d0743c'],
labels: null, labels: null,

View File

@@ -1,13 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import { nameComparator } from '../utils/SlotFunctions';
import { Pip } from './SvgIcons';
import LineChart from '../components/LineChart';
import Slider from '../components/Slider';
import * as ModuleUtils from '../shipyard/ModuleUtils';
import Module from '../shipyard/Module';
/** /**
* Boost displays a boost button that toggles bosot * Boost displays a boost button that toggles bosot

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import Slider from '../components/Slider'; import Slider from '../components/Slider';
/** /**

View File

@@ -10,7 +10,6 @@ import { outfitURL } from '../utils/UrlGenerators';
* Comparison Table * Comparison Table
*/ */
export default class ComparisonTable extends TranslatedComponent { export default class ComparisonTable extends TranslatedComponent {
static propTypes = { static propTypes = {
facets: PropTypes.array.isRequired, facets: PropTypes.array.isRequired,
builds: PropTypes.array.isRequired, builds: PropTypes.array.isRequired,

View File

@@ -13,7 +13,6 @@ import { ShoppingIcon } from '../components/SvgIcons';
* Cost Section * Cost Section
*/ */
export default class CostSection extends TranslatedComponent { export default class CostSection extends TranslatedComponent {
static propTypes = { static propTypes = {
ship: PropTypes.object.isRequired, ship: PropTypes.object.isRequired,
code: PropTypes.string.isRequired, code: PropTypes.string.isRequired,
@@ -361,11 +360,11 @@ export default class CostSection extends TranslatedComponent {
for (let i = 0, l = retrofitCosts.length; i < l; i++) { for (let i = 0, l = retrofitCosts.length; i < l; i++) {
let item = retrofitCosts[i]; let item = retrofitCosts[i];
rows.push(<tr key={i} className={cn('highlight', { disabled: !item.retroItem.incCost })} onClick={this._toggleRetrofitCost.bind(this, item)}> rows.push(<tr key={i} className={cn('highlight', { disabled: !item.retroItem.incCost })} onClick={this._toggleRetrofitCost.bind(this, item)}>
<td className='ptr' style={{ width: '1em' }}>{item.sellClassRating}</td> <td className='ptr' style={{ width: '1em' }}>{item.sellClassRating}</td>
<td className='le ptr shorten cap'>{translate(item.sellName)}</td> <td className='le ptr shorten cap'>{translate(item.sellName)}</td>
<td className='ptr' style={{ width: '1em' }}>{item.buyClassRating}</td> <td className='ptr' style={{ width: '1em' }}>{item.buyClassRating}</td>
<td className='le ptr shorten cap'>{translate(item.buyName)}</td> <td className='le ptr shorten cap'>{translate(item.buyName)}</td>
<td colSpan='2' className={cn('ri ptr', item.retroItem.incCost ? item.netCost > 0 ? 'warning' : 'secondary-disabled' : 'disabled')}>{int(item.netCost)}{units.CR}</td> <td colSpan='2' className={cn('ri ptr', item.retroItem.incCost ? item.netCost > 0 ? 'warning' : 'secondary-disabled' : 'disabled')}>{int(item.netCost)}{units.CR}</td>
</tr>); </tr>);
} }
} else { } else {

View File

@@ -226,26 +226,26 @@ export default class Defence extends TranslatedComponent {
return ( return (
<span id='defence'> <span id='defence'>
{shield.total ? <span> {shield.total ? <span>
<div className='group quarter'> <div className='group quarter'>
<h2>{translate('shield metrics')}</h2> <h2>{translate('shield metrics')}</h2>
<br/> <br/>
<h2 onMouseOver={termtip.bind(null, <div>{shieldSourcesTt}</div>)} onMouseOut={tooltip.bind(null, null)} className='summary'>{translate('raw shield strength')}<br/>{formats.int(shield.total)}{units.MJ}</h2> <h2 onMouseOver={termtip.bind(null, <div>{shieldSourcesTt}</div>)} onMouseOut={tooltip.bind(null, null)} className='summary'>{translate('raw shield strength')}<br/>{formats.int(shield.total)}{units.MJ}</h2>
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_LOSE_SHIELDS'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_LOSE_SHIELDS')}<br/>{shielddamage.totalsdps == 0 ? translate('ever') : formats.time(Calc.timeToDeplete(shield.total, shielddamage.totalsdps, shielddamage.totalseps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * opponentWep / 4))}</h2> <h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_LOSE_SHIELDS'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_LOSE_SHIELDS')}<br/>{shielddamage.totalsdps == 0 ? translate('ever') : formats.time(Calc.timeToDeplete(shield.total, shielddamage.totalsdps, shielddamage.totalseps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * opponentWep / 4))}</h2>
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SG_RECOVER'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_RECOVER_SHIELDS')}<br/>{shield.recover === Math.Inf ? translate('never') : formats.time(shield.recover)}</h2> <h2 onMouseOver={termtip.bind(null, translate('PHRASE_SG_RECOVER'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_RECOVER_SHIELDS')}<br/>{shield.recover === Math.Inf ? translate('never') : formats.time(shield.recover)}</h2>
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SG_RECHARGE'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_RECHARGE_SHIELDS')}<br/>{shield.recharge === Math.Inf ? translate('never') : formats.time(shield.recharge)}</h2> <h2 onMouseOver={termtip.bind(null, translate('PHRASE_SG_RECHARGE'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_RECHARGE_SHIELDS')}<br/>{shield.recharge === Math.Inf ? translate('never') : formats.time(shield.recharge)}</h2>
</div> </div>
<div className='group quarter'> <div className='group quarter'>
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SHIELD_SOURCES'))} onMouseOut={tooltip.bind(null, null)}>{translate('shield sources')}</h2> <h2 onMouseOver={termtip.bind(null, translate('PHRASE_SHIELD_SOURCES'))} onMouseOut={tooltip.bind(null, null)}>{translate('shield sources')}</h2>
<PieChart data={shieldSourcesData} /> <PieChart data={shieldSourcesData} />
</div> </div>
<div className='group quarter'> <div className='group quarter'>
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_DAMAGE_TAKEN'))} onMouseOut={tooltip.bind(null, null)}>{translate('damage taken')}(%)</h2> <h2 onMouseOver={termtip.bind(null, translate('PHRASE_DAMAGE_TAKEN'))} onMouseOut={tooltip.bind(null, null)}>{translate('damage taken')}(%)</h2>
<VerticalBarChart data={shieldDamageTakenData} yMax={140} /> <VerticalBarChart data={shieldDamageTakenData} yMax={140} />
</div> </div>
<div className='group quarter'> <div className='group quarter'>
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_EFFECTIVE_SHIELD'))} onMouseOut={tooltip.bind(null, null)}>{translate('effective shield')}(MJ)</h2> <h2 onMouseOver={termtip.bind(null, translate('PHRASE_EFFECTIVE_SHIELD'))} onMouseOut={tooltip.bind(null, null)}>{translate('effective shield')}(MJ)</h2>
<VerticalBarChart data={effectiveShieldData} yMax={maxEffectiveShield}/> <VerticalBarChart data={effectiveShieldData} yMax={maxEffectiveShield}/>
</div> </div>
</span> : null } </span> : null }
<div className='group quarter'> <div className='group quarter'>

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import Slider from '../components/Slider'; import Slider from '../components/Slider';
/** /**

View File

@@ -1,12 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import { nameComparator } from '../utils/SlotFunctions';
import LineChart from '../components/LineChart'; import LineChart from '../components/LineChart';
import Slider from '../components/Slider';
import * as ModuleUtils from '../shipyard/ModuleUtils';
import Module from '../shipyard/Module';
import * as Calc from '../shipyard/Calculations'; import * as Calc from '../shipyard/Calculations';
/** /**

View File

@@ -1,12 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import { nameComparator } from '../utils/SlotFunctions';
import LineChart from '../components/LineChart'; import LineChart from '../components/LineChart';
import Slider from '../components/Slider';
import * as ModuleUtils from '../shipyard/ModuleUtils';
import Module from '../shipyard/Module';
import * as Calc from '../shipyard/Calculations'; import * as Calc from '../shipyard/Calculations';
/** /**
@@ -69,7 +64,7 @@ export default class FSDProfile extends TranslatedComponent {
const { formats, translate, units } = language; const { formats, translate, units } = language;
const { ship, cargo, fuel } = this.props; const { ship, cargo, fuel } = this.props;
// Calculate bounds for our line chart - use thruster info for X // Calculate bounds for our line chart - use thruster info for X
const thrusters = ship.standard[1].m; const thrusters = ship.standard[1].m;
const fsd = ship.standard[2].m; const fsd = ship.standard[2].m;

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import Slider from '../components/Slider'; import Slider from '../components/Slider';
/** /**

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import SlotSection from './SlotSection'; import SlotSection from './SlotSection';
import HardpointSlot from './HardpointSlot'; import HardpointSlot from './HardpointSlot';
import cn from 'classnames';
import { MountFixed, MountGimballed, MountTurret } from '../components/SvgIcons'; import { MountFixed, MountGimballed, MountTurret } from '../components/SvgIcons';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';

View File

@@ -16,6 +16,7 @@ import ModalExport from './ModalExport';
import ModalHelp from './ModalHelp'; import ModalHelp from './ModalHelp';
import ModalImport from './ModalImport'; import ModalImport from './ModalImport';
import Slider from './Slider'; import Slider from './Slider';
import Announcement from './Announcement';
import { outfitURL } from '../utils/UrlGenerators'; import { outfitURL } from '../utils/UrlGenerators';
const SIZE_MIN = 0.65; const SIZE_MIN = 0.65;
@@ -76,8 +77,11 @@ export default class Header extends TranslatedComponent {
this._openShips = this._openMenu.bind(this, 's'); this._openShips = this._openMenu.bind(this, 's');
this._openBuilds = this._openMenu.bind(this, 'b'); this._openBuilds = this._openMenu.bind(this, 'b');
this._openComp = this._openMenu.bind(this, 'comp'); this._openComp = this._openMenu.bind(this, 'comp');
this._openAnnounce = this._openMenu.bind(this, 'announce');
this._getAnnouncementsMenu = this._getAnnouncementsMenu.bind(this);
this._openSettings = this._openMenu.bind(this, 'settings'); this._openSettings = this._openMenu.bind(this, 'settings');
this._showHelp = this._showHelp.bind(this); this._showHelp = this._showHelp.bind(this);
this.update = this.update.bind(this);
this.languageOptions = []; this.languageOptions = [];
this.insuranceOptions = []; this.insuranceOptions = [];
this.state = { this.state = {
@@ -411,6 +415,29 @@ export default class Header extends TranslatedComponent {
); );
} }
/**
* Generate the announcement menu
* @return {React.Component} Menu
*/
_getAnnouncementsMenu() {
let announcements;
let translate = this.context.language.translate;
if (this.props.announcements) {
announcements = [];
for (let announce of this.props.announcements) {
announcements.push(<Announcement text={announce.message} />);
announcements.push(<hr/>);
}
}
return (
<div className='menu-list' onClick={ (e) => e.stopPropagation() } style={{ whiteSpace: 'nowrap' }}>
{announcements}
<hr />
</div>
);
}
/** /**
* Generate the settings menu * Generate the settings menu
* @return {React.Component} Menu * @return {React.Component} Menu
@@ -534,6 +561,15 @@ export default class Header extends TranslatedComponent {
} }
} }
async update() {
const reg = await navigator.serviceWorker.getRegistration();
if (!reg || !reg.waiting) {
return window.location.reload();
}
reg.waiting.postMessage('skipWaiting');
window.location.reload();
}
/** /**
* Render the header * Render the header
* @return {React.Component} Header * @return {React.Component} Header
@@ -544,7 +580,7 @@ export default class Header extends TranslatedComponent {
let hasBuilds = Persist.hasBuilds(); let hasBuilds = Persist.hasBuilds();
return ( return (
<header> <header>
{this.props.appCacheUpdate && <div id="app-update" onClick={() => window.location.reload() }>{translate('PHRASE_UPDATE_RDY')}</div>} {this.props.appCacheUpdate && <div id="app-update" onClick={this.update}>{translate('PHRASE_UPDATE_RDY')}</div>}
{this.props.appCacheUpdate ? <a className={'view-changes'} href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'} target="_blank"> {this.props.appCacheUpdate ? <a className={'view-changes'} href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'} target="_blank">
{'View Release Changes'} {'View Release Changes'}
</a> : null} </a> : null}
@@ -571,6 +607,13 @@ export default class Header extends TranslatedComponent {
{openedMenu == 'comp' ? this._getComparisonsMenu() : null} {openedMenu == 'comp' ? this._getComparisonsMenu() : null}
</div> </div>
<div className='l menu'>
<div className={cn('menu-header', { selected: openedMenu == 'announce', disabled: this.props.announcements.length === 0})} onClick={this.props.announcements.length !== 0 && this._openAnnounce}>
<span className='menu-item-label'>{translate('announcements')}</span>
</div>
{openedMenu == 'announce' ? this._getAnnouncementsMenu() : null}
</div>
{window.location.origin.search('.edcd.io') >= 0 ? {window.location.origin.search('.edcd.io') >= 0 ?
<div className='l menu'> <div className='l menu'>
<a href="https://youtu.be/4SvnLcefhtI" target="_blank"> <a href="https://youtu.be/4SvnLcefhtI" target="_blank">

View File

@@ -1,5 +1,4 @@
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 * as ModuleUtils from '../shipyard/ModuleUtils'; import * as ModuleUtils from '../shipyard/ModuleUtils';

View File

@@ -1,12 +1,8 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import { nameComparator } from '../utils/SlotFunctions';
import LineChart from '../components/LineChart'; import LineChart from '../components/LineChart';
import Slider from '../components/Slider'; import Slider from '../components/Slider';
import * as ModuleUtils from '../shipyard/ModuleUtils';
import Module from '../shipyard/Module';
import * as Calc from '../shipyard/Calculations'; import * as Calc from '../shipyard/Calculations';
/** /**

View File

@@ -6,7 +6,6 @@ import Persist from '../stores/Persist';
* Delete All saved data modal * Delete All saved data modal
*/ */
export default class ModalDeleteAll extends TranslatedComponent { export default class ModalDeleteAll extends TranslatedComponent {
/** /**
* Delete everything and hide the modal * Delete everything and hide the modal
*/ */

View File

@@ -9,7 +9,6 @@ import { isValueBeneficial } from '../utils/BlueprintFunctions';
* Modification * Modification
*/ */
export default class Modification extends TranslatedComponent { export default class Modification extends TranslatedComponent {
static propTypes = { static propTypes = {
ship: PropTypes.object.isRequired, ship: PropTypes.object.isRequired,
m: PropTypes.object.isRequired, m: PropTypes.object.isRequired,
@@ -39,10 +38,24 @@ export default class Modification extends TranslatedComponent {
* in a value by hand * in a value by hand
*/ */
_updateValue(value) { _updateValue(value) {
let { m, name, ship } = this.props;
value = Math.max(Math.min(value, 50000), -50000);
ship.setModification(m, name, value, true, true);
this.setState({ value }); this.setState({ value });
let reCast = String(Number(value));
if (reCast.endsWith(value) || reCast.startsWith(value)) {
let { m, name, ship } = this.props;
value = Math.max(Math.min(value, 50000), -50000);
ship.setModification(m, name, value, true, true);
}
}
/**
* Triggered when a key is pressed down with focus on the number editor.
* @param {SyntheticEvent} event Key down event
*/
_keyDown(event) {
if (event.key == 'Enter') {
this._updateFinished();
}
this.props.onKeyDown(event);
} }
/** /**
@@ -72,6 +85,11 @@ export default class Modification extends TranslatedComponent {
return null; return null;
} }
let inputClassNames = {
'cb': true,
'greyed-out': !this.props.highlight
};
return ( return (
<div onBlur={this._updateFinished.bind(this)} key={name} <div onBlur={this._updateFinished.bind(this)} key={name}
className={cn('cb', 'modification-container')} className={cn('cb', 'modification-container')}
@@ -84,24 +102,24 @@ export default class Modification extends TranslatedComponent {
<td className={'input-container'}> <td className={'input-container'}>
<span> <span>
{this.props.editable ? {this.props.editable ?
<NumberEditor className={'cb'} value={this.state.value} <NumberEditor className={cn(inputClassNames)} value={this.state.value}
decimals={2} style={{ textAlign: 'right' }} step={0.01} decimals={2} style={{ textAlign: 'right' }} step={0.01}
stepModifier={1} onKeyDown={ this.props.onKeyDown } stepModifier={1} onKeyDown={this._keyDown.bind(this)}
onValueChange={this._updateValue.bind(this)} /> : onValueChange={this._updateValue.bind(this)} /> :
<input type="text" value={formats.f2(this.state.value)} <input type="text" value={formats.f2(this.state.value)}
disabled className={'number-editor'} disabled className={cn('number-editor', 'greyed-out')}
style={{ textAlign: 'right', cursor: 'inherit' }}/> style={{ textAlign: 'right', cursor: 'inherit' }}/>
} }
<span className={'unit-container'}> <span className={'unit-container'}>
{units[m.getStoredUnitFor(name)]} {units[m.getStoredUnitFor(name)]}
</span> </span>
</span> </span>
</td> </td>
<td style={{ textAlign: 'center' }} className={ <td style={{ textAlign: 'center' }} className={
modValue ? modValue ?
isValueBeneficial(name, modValue) ? 'secondary': 'warning': isValueBeneficial(name, modValue) ? 'secondary' : 'warning' :
'' ''
}> }>
{formats.f2(modValue / 100) || 0}% {formats.f2(modValue / 100) || 0}%
</td> </td>
</tr> </tr>

View File

@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import * as _ from 'lodash'; import * as _ from 'lodash';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { isEmpty, stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';
import cn from 'classnames'; import cn from 'classnames';
import { Modifications } from 'coriolis-data/dist'; import { Modifications } from 'coriolis-data/dist';
import Modification from './Modification'; import Modification from './Modification';
@@ -23,7 +23,6 @@ const MODIFICATIONS_COMPARATOR = (mod1, mod2) => {
* Modifications menu * Modifications menu
*/ */
export default class ModificationsMenu extends TranslatedComponent { export default class ModificationsMenu extends TranslatedComponent {
static propTypes = { static propTypes = {
ship: PropTypes.object.isRequired, ship: PropTypes.object.isRequired,
m: PropTypes.object.isRequired, m: PropTypes.object.isRequired,
@@ -214,11 +213,11 @@ export default class ModificationsMenu extends TranslatedComponent {
for (const modName of Modifications.modules[m.grp].modifications) { for (const modName of Modifications.modules[m.grp].modifications) {
if (!Modifications.modifications[modName].hidden) { if (!Modifications.modifications[modName].hidden) {
const key = modName + (m.getModValue(modName) / 100 || 0); const key = modName + (m.getModValue(modName) / 100 || 0);
const editable = modName !== 'fallofffromrange' && const editable = modName !== 'fallofffromrange';
m.blueprint.grades[m.blueprint.grade].features[modName]; const highlight = m.blueprint.grades[m.blueprint.grade].features[modName];
this.lastNeId = modName; this.lastNeId = modName;
(editable ? modifiableModifications : modifications).push( (editable && highlight ? modifiableModifications : modifications).push(
<Modification key={ key } ship={ ship } m={ m } <Modification key={ key } ship={ ship } m={ m } highlight={highlight}
value={m.getPretty(modName) || 0} modItems={this.modItems} value={m.getPretty(modName) || 0} modItems={this.modItems}
onChange={onChange} onKeyDown={this._keyDown} name={modName} onChange={onChange} onKeyDown={this._keyDown} name={modName}
editable={editable} handleModChange = {this._handleModChange} /> editable={editable} handleModChange = {this._handleModChange} />
@@ -461,10 +460,10 @@ export default class ModificationsMenu extends TranslatedComponent {
} }
return ( return (
<div <div
className={cn('select', this.props.className)} className={cn('select', this.props.className)}
onClick={(e) => e.stopPropagation() } onClick={(e) => e.stopPropagation() }
onContextMenu={stopCtxPropagation} onContextMenu={stopCtxPropagation}
ref={modItem => this.modItems['modMainDiv'] = modItem} ref={modItem => this.modItems['modMainDiv'] = modItem}
> >
{ showBlueprintsMenu | showSpecialsMenu ? '' : haveBlueprint ? { showBlueprintsMenu | showSpecialsMenu ? '' : haveBlueprint ?
<div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onMouseOver={termtip.bind(null, blueprintTt)} onMouseOut={tooltip.bind(null, null)} onClick={_toggleBlueprintsMenu} onKeyDown={ this._keyDown } ref={modItems => this.modItems[this.firstBPLabel] = modItems}>{blueprintLabel}</div> : <div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onMouseOver={termtip.bind(null, blueprintTt)} onMouseOut={tooltip.bind(null, null)} onClick={_toggleBlueprintsMenu} onKeyDown={ this._keyDown } ref={modItems => this.modItems[this.firstBPLabel] = modItems}>{blueprintLabel}</div> :
@@ -473,11 +472,11 @@ export default class ModificationsMenu extends TranslatedComponent {
{ showSpecial & !showSpecialsMenu ? <div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: specialMenuOpened })} style={{ cursor: 'pointer' }} onMouseOver={specialTt ? termtip.bind(null, specialTt) : null} onMouseOut={specialTt ? tooltip.bind(null, null) : null} onClick={_toggleSpecialsMenu} onKeyDown={ this._keyDown }>{specialLabel}</div> : null } { showSpecial & !showSpecialsMenu ? <div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: specialMenuOpened })} style={{ cursor: 'pointer' }} onMouseOver={specialTt ? termtip.bind(null, specialTt) : null} onMouseOut={specialTt ? tooltip.bind(null, null) : null} onClick={_toggleSpecialsMenu} onKeyDown={ this._keyDown }>{specialLabel}</div> : null }
{ showSpecialsMenu ? specials : null } { showSpecialsMenu ? specials : null }
{ showReset ? <div tabIndex="0" className={'section-menu button-inline-menu warning'} style={{ cursor: 'pointer' }} onClick={_reset} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RESET')} onMouseOut={tooltip.bind(null, null)}> { translate('reset') } </div> : null } { showReset ? <div tabIndex="0" className={'section-menu button-inline-menu warning'} style={{ cursor: 'pointer' }} onClick={_reset} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RESET')} onMouseOut={tooltip.bind(null, null)}> { translate('reset') } </div> : null }
{ showRolls ? { showRolls ?
<table style={{ width: '100%', backgroundColor: 'transparent' }}> <table style={{ width: '100%', backgroundColor: 'transparent' }}>
<tbody> <tbody>
{ showRolls ? { showRolls ?
<tr> <tr>
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: false }) }> { translate('roll') }: </td> <td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: false }) }> { translate('roll') }: </td>
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 0 }) } style={{ cursor: 'pointer' }} onClick={_rollWorst} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')} onMouseOut={tooltip.bind(null, null)}> { translate('0%') } </td> <td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 0 }) } style={{ cursor: 'pointer' }} onClick={_rollWorst} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')} onMouseOut={tooltip.bind(null, null)}> { translate('0%') } </td>
@@ -485,7 +484,7 @@ export default class ModificationsMenu extends TranslatedComponent {
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 100 })} style={{ cursor: 'pointer' }} onClick={_rollFull} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('100%') } </td> <td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 100 })} style={{ cursor: 'pointer' }} onClick={_rollFull} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('100%') } </td>
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === null || blueprintCv % 50 != 0 })} style={{ cursor: 'pointer' }} onClick={_rollRandom} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RANDOM')} onMouseOut={tooltip.bind(null, null)}> { translate('random') } </td> <td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === null || blueprintCv % 50 != 0 })} style={{ cursor: 'pointer' }} onClick={_rollRandom} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RANDOM')} onMouseOut={tooltip.bind(null, null)}> { translate('random') } </td>
</tr> : null } </tr> : null }
</tbody> </tbody>
</table> : null } </table> : null }
{ showMods ? <hr /> : null } { showMods ? <hr /> : null }
{ showMods ? { showMods ?

View File

@@ -35,13 +35,13 @@ export default class Movement extends TranslatedComponent {
return ( return (
<span id='movement'> <span id='movement'>
<svg viewBox='0 0 600 600' fillRule="evenodd" clipRule="evenodd"> <svg viewBox='0 0 600 600' fillRule="evenodd" clipRule="evenodd">
// Axes {/* Axes */}
<path d="M150 250v300" strokeWidth='1'/> <path d="M150 250v300" strokeWidth='1'/>
<path d="M150 250l236 236" strokeWidth='1'/> <path d="M150 250l236 236" strokeWidth='1'/>
<path d="M150 250l350 -200" strokeWidth='1'/> <path d="M150 250l350 -200" strokeWidth='1'/>
// End Arrow {/* End Arrow */}
<path d="M508 43.3L487 67l-10-17.3 31-6.4z"/> <path d="M508 43.3L487 67l-10-17.3 31-6.4z"/>
// Axes arcs and arrows {/* Axes arcs and arrows */}
<path d="M71.7 251.7C64.2 259.2 60 269.4 60 280c0 22 18 40 40 40s40-18 40-40c0-10.6-4.2-20.8-11.7-28.3 7.5 7.5 11.7 17.7 11.7 28.3 0 22-18 40-40 40s-40-18-40-40c0-10.6 4.2-20.8 11.7-28.3z" strokeWidth='4' transform="matrix(.6 0 0 .3 87.5 376.3)"/> <path d="M71.7 251.7C64.2 259.2 60 269.4 60 280c0 22 18 40 40 40s40-18 40-40c0-10.6-4.2-20.8-11.7-28.3 7.5 7.5 11.7 17.7 11.7 28.3 0 22-18 40-40 40s-40-18-40-40c0-10.6 4.2-20.8 11.7-28.3z" strokeWidth='4' transform="matrix(.6 0 0 .3 87.5 376.3)"/>
<path d="M142.8 453l-13.2 8.7-2.6-9.7 15.8 1z"/> <path d="M142.8 453l-13.2 8.7-2.6-9.7 15.8 1z"/>
<path d="M144.7 451.6l.5 1.6-16.2 10.6h-.4l-3.5-13 .7-.4 19.3 1.2zm-14.2 7.7l7.7-5-9.2-.7 1.5 5.7zm25.7-6.3l15.8-1-2.6 9.7-13.2-8.8z"/> <path d="M144.7 451.6l.5 1.6-16.2 10.6h-.4l-3.5-13 .7-.4 19.3 1.2zm-14.2 7.7l7.7-5-9.2-.7 1.5 5.7zm25.7-6.3l15.8-1-2.6 9.7-13.2-8.8z"/>
@@ -57,13 +57,13 @@ export default class Movement extends TranslatedComponent {
<path d="M359.5 422.4l-1.2 19.3-1.6.4-10.7-16 .2-.2 13-3.4.3.4zm-9 5l5.2 7.8.6-9.3-5.7 1.2zm-10.5 24l-13.2 8.6-2.6-9.7 15.8 1z"/> <path d="M359.5 422.4l-1.2 19.3-1.6.4-10.7-16 .2-.2 13-3.4.3.4zm-9 5l5.2 7.8.6-9.3-5.7 1.2zm-10.5 24l-13.2 8.6-2.6-9.7 15.8 1z"/>
<path d="M342 450l.4 1.5-16.2 10.7-.4-.2-3.5-13 .3-.3L342 450zm-14.3 7.6l7.7-5-9.2-.6 1.5 5.6z"/> <path d="M342 450l.4 1.5-16.2 10.7-.4-.2-3.5-13 .3-.3L342 450zm-14.3 7.6l7.7-5-9.2-.6 1.5 5.6z"/>
// Speed {/* Speed */}
<text x="470" y="30" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcSpeed(eng, fuel, cargo, boost)) + 'm/s' : '-'}</text> <text x="470" y="30" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcSpeed(eng, fuel, cargo, boost)) + 'm/s' : '-'}</text>
// Pitch {/* Pitch */}
<text x="355" y="410" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcPitch(eng, fuel, cargo, boost)) + '°/s' : '-'}</text> <text x="355" y="410" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcPitch(eng, fuel, cargo, boost)) + '°/s' : '-'}</text>
// Roll {/* Roll */}
<text x="450" y="110" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcRoll(eng, fuel, cargo, boost)) + '°/s' : '-'}</text> <text x="450" y="110" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcRoll(eng, fuel, cargo, boost)) + '°/s' : '-'}</text>
// Yaw {/* Yaw */}
<text x="160" y="430" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcYaw(eng, fuel, cargo, boost)) + '°/s' : '-'}</text> <text x="160" y="430" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcYaw(eng, fuel, cargo, boost)) + '°/s' : '-'}</text>
</svg> </svg>
</span>); </span>);

View File

@@ -5,7 +5,6 @@ import * as Calc from '../shipyard/Calculations';
import PieChart from './PieChart'; import PieChart from './PieChart';
import { nameComparator } from '../utils/SlotFunctions'; import { nameComparator } from '../utils/SlotFunctions';
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons'; import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
import VerticalBarChart from './VerticalBarChart';
/** /**
* Generates an internationalization friendly weapon comparator that will * Generates an internationalization friendly weapon comparator that will
@@ -203,9 +202,9 @@ export default class Offence extends TranslatedComponent {
let totalSEps = 0; let totalSEps = 0;
let totalSDpsObject = {'absolute': 0, 'explosive': 0, 'kinetic': 0, 'thermal': 0}; let totalSDpsObject = { 'absolute': 0, 'explosive': 0, 'kinetic': 0, 'thermal': 0 };
let shieldsSDpsObject = {'absolute': 0, 'explosive': 0, 'kinetic': 0, 'thermal': 0}; let shieldsSDpsObject = { 'absolute': 0, 'explosive': 0, 'kinetic': 0, 'thermal': 0 };
let armourSDpsObject = {'absolute': 0, 'explosive': 0, 'kinetic': 0, 'thermal': 0}; let armourSDpsObject = { 'absolute': 0, 'explosive': 0, 'kinetic': 0, 'thermal': 0 };
const rows = []; const rows = [];
for (let i = 0; i < damage.length; i++) { for (let i = 0; i < damage.length; i++) {
@@ -267,22 +266,22 @@ export default class Offence extends TranslatedComponent {
return ( return (
<span id='offence'> <span id='offence'>
<div className='group full'> <div className='group full'>
<table> <table>
<thead> <thead>
<tr className='main'> <tr className='main'>
<th rowSpan='2' className='sortable' onClick={sortOrder.bind(this, 'n')}>{translate('weapon')}</th> <th rowSpan='2' className='sortable' onClick={sortOrder.bind(this, 'n')}>{translate('weapon')}</th>
<th colSpan='1'>{translate('overall')}</th> <th colSpan='1'>{translate('overall')}</th>
<th colSpan='2'>{translate('opponent\'s shields')}</th> <th colSpan='2'>{translate('opponent\'s shields')}</th>
<th colSpan='2'>{translate('opponent\'s armour')}</th> <th colSpan='2'>{translate('opponent\'s armour')}</th>
</tr> </tr>
<tr> <tr>
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_SHIELDS')} onMouseOut={tooltip.bind(null, null)} onClick={sortOrder.bind(this, 'esdpss')}>{'sdps'}</th> <th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_SHIELDS')} onMouseOut={tooltip.bind(null, null)} onClick={sortOrder.bind(this, 'esdpss')}>{'sdps'}</th>
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_SHIELDS')} onMouseOut={tooltip.bind(null, null)} onClick={sortOrder.bind(this, 'esdpss')}>{'sdps'}</th> <th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_SHIELDS')} onMouseOut={tooltip.bind(null, null)} onClick={sortOrder.bind(this, 'esdpss')}>{'sdps'}</th>
<th className='sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVENESS_SHIELDS')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'es')}>{'eft'}</th> <th className='sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVENESS_SHIELDS')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'es')}>{'eft'}</th>
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_ARMOUR')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'esdpsh')}>{'sdps'}</th> <th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_ARMOUR')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'esdpsh')}>{'sdps'}</th>
<th className='sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVENESS_ARMOUR')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'eh')}>{'eft'}</th> <th className='sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVENESS_ARMOUR')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'eh')}>{'eft'}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{rows} {rows}
{rows.length > 0 && {rows.length > 0 &&
@@ -296,7 +295,7 @@ export default class Offence extends TranslatedComponent {
</tr> </tr>
} }
</tbody> </tbody>
</table> </table>
</div> </div>
<div className='group quarter'> <div className='group quarter'>
<h2>{translate('offence metrics')}</h2> <h2>{translate('offence metrics')}</h2>

View File

@@ -1,8 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import cn from 'classnames'; import cn from 'classnames';
import { Ships } from 'coriolis-data/dist';
import Ship from '../shipyard/Ship';
import Persist from '../stores/Persist'; import Persist from '../stores/Persist';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import PowerManagement from './PowerManagement'; import PowerManagement from './PowerManagement';

View File

@@ -1,13 +1,8 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import { nameComparator } from '../utils/SlotFunctions';
import { Pip } from './SvgIcons'; import { Pip } from './SvgIcons';
import LineChart from '../components/LineChart'; import { autoBind } from 'react-extras';
import Slider from '../components/Slider';
import * as ModuleUtils from '../shipyard/ModuleUtils';
import Module from '../shipyard/Module';
/** /**
* Pips displays SYS/ENG/WEP pips and allows users to change them with key presses by clicking on the relevant area. * Pips displays SYS/ENG/WEP pips and allows users to change them with key presses by clicking on the relevant area.
@@ -18,6 +13,9 @@ export default class Pips extends TranslatedComponent {
sys: PropTypes.number.isRequired, sys: PropTypes.number.isRequired,
eng: PropTypes.number.isRequired, eng: PropTypes.number.isRequired,
wep: PropTypes.number.isRequired, wep: PropTypes.number.isRequired,
mcSys: PropTypes.number.isRequired,
mcEng: PropTypes.number.isRequired,
mcWep: PropTypes.number.isRequired,
onChange: PropTypes.func.isRequired onChange: PropTypes.func.isRequired
}; };
@@ -28,9 +26,7 @@ export default class Pips extends TranslatedComponent {
*/ */
constructor(props, context) { constructor(props, context) {
super(props); super(props);
const { sys, eng, wep } = props; autoBind(this);
this._keyDown = this._keyDown.bind(this);
} }
/** /**
@@ -74,30 +70,21 @@ export default class Pips extends TranslatedComponent {
} }
} }
/**
* Handle a click
* @param {string} which Which item was clicked
*/
onClick(which) {
if (which == 'SYS') {
this._incSys();
} else if (which == 'ENG') {
this._incEng();
} else if (which == 'WEP') {
this._incWep();
} else if (which == 'RST') {
this._reset();
}
}
/** /**
* Reset the capacitor * Reset the capacitor
*/ */
_reset() { _reset(isMc) {
let { sys, eng, wep } = this.props; let { sys, eng, wep, mcSys, mcEng, mcWep } = this.props;
if (sys != 2 || eng != 2 || wep != 2) { if (isMc) {
if (mcSys || mcEng || mcWep) {
sys -= mcSys;
eng -= mcEng;
wep -= mcWep;
this.props.onChange(sys, eng, wep, 0, 0, 0);
}
} else if (sys != 2 || eng != 2 || wep != 2) {
sys = eng = wep = 2; sys = eng = wep = 2;
this.props.onChange(sys, eng, wep); this.props.onChange(sys + mcSys, eng + mcEng, wep + mcWep, mcSys, mcEng, mcWep);
} }
} }
@@ -105,151 +92,133 @@ export default class Pips extends TranslatedComponent {
* Increment the SYS capacitor * Increment the SYS capacitor
*/ */
_incSys() { _incSys() {
let { sys, eng, wep } = this.props; this._inc('sys', false);
const required = Math.min(1, 4 - sys);
if (required > 0) {
if (required == 0.5) {
// Take from whichever is larger
if (eng > wep) {
eng -= 0.5;
sys += 0.5;
} else {
wep -= 0.5;
sys += 0.5;
}
} else {
// Required is 1 - take from both if possible
if (eng == 0) {
wep -= 1;
sys += 1;
} else if (wep == 0) {
eng -= 1;
sys += 1;
} else {
eng -= 0.5;
wep -= 0.5;
sys += 1;
}
}
this.props.onChange(sys, eng, wep);
}
} }
/** /**
* Increment the ENG capacitor * Increment the ENG capacitor
*/ */
_incEng() { _incEng() {
let { sys, eng, wep } = this.props; this._inc('eng', false);
const required = Math.min(1, 4 - eng);
if (required > 0) {
if (required == 0.5) {
// Take from whichever is larger
if (sys > wep) {
sys -= 0.5;
eng += 0.5;
} else {
wep -= 0.5;
eng += 0.5;
}
} else {
// Required is 1 - take from both if possible
if (sys == 0) {
wep -= 1;
eng += 1;
} else if (wep == 0) {
sys -= 1;
eng += 1;
} else {
sys -= 0.5;
wep -= 0.5;
eng += 1;
}
}
this.props.onChange(sys, eng, wep);
}
} }
/** /**
* Increment the WEP capacitor * Increment the WEP capacitor
*/ */
_incWep() { _incWep() {
let { sys, eng, wep } = this.props; this._inc('wep', false);
}
const required = Math.min(1, 4 - wep); _wrapMcClick(key) {
if (required > 0) { return (event) => {
if (required == 0.5) { event.stopPropagation();
// Take from whichever is larger event.preventDefault();
if (sys > eng) { if (key == 'rst') {
sys -= 0.5; this._reset(true);
wep += 0.5;
} else {
eng -= 0.5;
wep += 0.5;
}
} else { } else {
// Required is 1 - take from both if possible this._inc(key, true);
if (sys == 0) { }
eng -= 1; };
wep += 1; }
} else if (eng == 0) {
sys -= 1; /**
wep += 1; * Increases a given capacitor
* @param {String} key Pip name to increase (one of 'sys', 'eng', 'wep')
* @param {Boolean} isMc True when increase is by multi crew
*/
_inc(key, isMc) {
if (!['sys', 'eng', 'wep'].includes(key)) {
return;
}
let { sys, eng, wep, mcSys, mcEng, mcWep } = this.props;
let mc = key == 'sys' ? mcSys : (key == 'eng' ? mcEng : mcWep);
let pips = this.props[key] - mc;
let other1 = key == 'sys' ? eng - mcEng : sys - mcSys;
let other2 = key == 'wep' ? eng - mcEng : wep - mcWep;
const required = Math.min(1, 4 - mc - pips);
if (isMc) {
// We can only set full pips in multi-crew also we can only set two pips
if (required > 0.5 && mcSys + mcEng + mcWep < 2) {
if (key == 'sys') {
mcSys += 1;
} else if (key == 'eng') {
mcEng += 1;
} else { } else {
sys -= 0.5; mcWep += 1;
eng -= 0.5;
wep += 1;
} }
} }
this.props.onChange(sys, eng, wep); } else if (required > 0) {
if (required == 0.5) {
// Take from whichever is larger
if (other1 > other2) {
other1 -= 0.5;
} else {
other2 -= 0.5;
}
pips += 0.5;
} else {
// Required is 1 - take from both if possible
if (other1 == 0) {
other2 -= 1;
} else if (other2 == 0) {
other1 -= 1;
} else {
other1 -= 0.5;
other2 -= 0.5;
}
pips += 1;
}
} }
sys = mcSys + (key == 'sys' ? pips : other1);
eng = mcEng + (key == 'eng' ? pips : (key == 'sys' ? other1 : other2));
wep = mcWep + (key == 'wep' ? pips : other2);
this.props.onChange(sys, eng, wep, mcSys, mcEng, mcWep);
} }
/** /**
* Set up the rendering for pips * Set up the rendering for pips
* @param {int} sys the SYS pips * @param {Number} sys the SYS pips
* @param {int} eng the ENG pips * @param {Number} eng the ENG pips
* @param {int} wep the WEP pips * @param {Number} wep the WEP pips
* @param {Number} mcSys SYS pips from multi-crew
* @param {Number} mcEng ENG pips from multi-crew
* @param {Number} mcWep WEP pips from multi-crew
* @returns {Object} Object containing the rendering for the pips * @returns {Object} Object containing the rendering for the pips
*/ */
_renderPips(sys, eng, wep) { _renderPips(sys, eng, wep, mcSys, mcEng, mcWep) {
const pipsSvg = {}; const pipsSvg = {
SYS: [],
ENG: [],
WEP: [],
};
// SYS // Multi-crew pipsSettings actually are included in the overall pip count therefore
pipsSvg['SYS'] = []; // we can consider [0, sys - mcSys] as normal pipsSettings whilst [sys - mcSys, sys]
for (let i = 0; i < Math.floor(sys); i++) { // are the multi-crew pipsSettings in what follows.
pipsSvg['SYS'].push(<Pip className='full' key={i} />);
}
if (sys > Math.floor(sys)) {
pipsSvg['SYS'].push(<Pip className='half' key={'half'} />);
}
for (let i = Math.floor(sys + 0.5); i < 4; i++) {
pipsSvg['SYS'].push(<Pip className='empty' key={i} />);
}
// ENG let pipsSettings = {
pipsSvg['ENG'] = []; SYS: [sys, mcSys],
for (let i = 0; i < Math.floor(eng); i++) { ENG: [eng, mcEng],
pipsSvg['ENG'].push(<Pip className='full' key={i} />); WEP: [wep, mcWep],
} };
if (eng > Math.floor(eng)) {
pipsSvg['ENG'].push(<Pip className='half' key={'half'} />);
}
for (let i = Math.floor(eng + 0.5); i < 4; i++) {
pipsSvg['ENG'].push(<Pip className='empty' key={i} />);
}
// WEP for (let pipName in pipsSettings) {
pipsSvg['WEP'] = []; let [pips, mcPips] = pipsSettings[pipName];
for (let i = 0; i < Math.floor(wep); i++) { for (let i = 0; i < Math.floor(pips - mcPips); i++) {
pipsSvg['WEP'].push(<Pip className='full' key={i} />); pipsSvg[pipName].push(<Pip key={i} className='full' />);
} }
if (wep > Math.floor(wep)) { if (pips > Math.floor(pips)) {
pipsSvg['WEP'].push(<Pip className='half' key={'half'} />); pipsSvg[pipName].push(<Pip className='half' key={'half'} />);
} }
for (let i = Math.floor(wep + 0.5); i < 4; i++) { for (let i = pips - mcPips; i < Math.floor(pips); i++) {
pipsSvg['WEP'].push(<Pip className='empty' key={i} />); pipsSvg[pipName].push(<Pip key={i} className='mc' />);
}
for (let i = Math.floor(pips + 0.5); i < 4; i++) {
pipsSvg[pipName].push(<Pip className='empty' key={i} />);
}
} }
return pipsSvg; return pipsSvg;
@@ -260,15 +229,11 @@ export default class Pips extends TranslatedComponent {
* @return {React.Component} contents * @return {React.Component} contents
*/ */
render() { render() {
const { tooltip, termtip } = this.context;
const { formats, translate, units } = this.context.language; const { formats, translate, units } = this.context.language;
const { sys, eng, wep } = this.props; const { sys, eng, wep, mcSys, mcEng, mcWep } = this.props;
const onSysClicked = this.onClick.bind(this, 'SYS'); const pipsSvg = this._renderPips(sys, eng, wep, mcSys, mcEng, mcWep);
const onEngClicked = this.onClick.bind(this, 'ENG');
const onWepClicked = this.onClick.bind(this, 'WEP');
const onRstClicked = this.onClick.bind(this, 'RST');
const pipsSvg = this._renderPips(sys, eng, wep);
return ( return (
<span id='pips'> <span id='pips'>
<table> <table>
@@ -276,20 +241,38 @@ export default class Pips extends TranslatedComponent {
<tr> <tr>
<td>&nbsp;</td> <td>&nbsp;</td>
<td>&nbsp;</td> <td>&nbsp;</td>
<td className='clickable' onClick={onEngClicked}>{pipsSvg['ENG']}</td> <td className='clickable' onClick={() => this._inc('eng')}
onContextMenu={this._wrapMcClick('eng')}>{pipsSvg['ENG']}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
</tr> </tr>
<tr> <tr>
<td>&nbsp;</td> <td>&nbsp;</td>
<td className='clickable' onClick={onSysClicked}>{pipsSvg['SYS']}</td> <td className='clickable' onClick={this._incSys}
<td className='clickable' onClick={onEngClicked}>{translate('ENG')}</td> onContextMenu={this._wrapMcClick('sys')}>{pipsSvg['SYS']}</td>
<td className='clickable' onClick={onWepClicked}>{pipsSvg['WEP']}</td> <td className='clickable' onClick={this._incEng}
onContextMenu={this._wrapMcClick('eng')}>{translate('ENG')}</td>
<td className='clickable' onClick={this._incWep}
onContextMenu={this._wrapMcClick('wep')}>{pipsSvg['WEP']}</td>
</tr> </tr>
<tr> <tr>
<td>&nbsp;</td> <td>&nbsp;</td>
<td className='clickable' onClick={onSysClicked}>{translate('SYS')}</td> <td className='clickable' onClick={this._incSys}
<td className='clickable' onClick={onRstClicked}>{translate('RST')}</td> onContextMenu={this._wrapMcClick('sys')}>{translate('SYS')}</td>
<td className='clickable' onClick={onWepClicked}>{translate('WEP')}</td> <td className='clickable' onClick={this._reset.bind(this, false)}>
{translate('RST')}
</td>
<td className='clickable' onClick={this._incWep}
onContextMenu={this._wrapMcClick('wep')}>{translate('WEP')}</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td className='clickable secondary' onClick={this._wrapMcClick('rst')}
onMouseEnter={termtip.bind(null, 'PHRASE_MULTI_CREW_CAPACITOR_POINTS')}
onMouseLeave={tooltip.bind(null, null)}>
{translate('RST')}
</td>
<td>&nbsp;</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -32,7 +32,6 @@ function bandText(val, index, wattScale) {
* Renders the SVG to simulate in-game power bands * Renders the SVG to simulate in-game power bands
*/ */
export default class PowerBands extends TranslatedComponent { export default class PowerBands extends TranslatedComponent {
static propTypes = { static propTypes = {
bands: PropTypes.array.isRequired, bands: PropTypes.array.isRequired,
available: PropTypes.number.isRequired, available: PropTypes.number.isRequired,

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import Ship from '../shipyard/Ship';
import { Ships } from 'coriolis-data/dist'; import { Ships } from 'coriolis-data/dist';
import { Rocket } from './SvgIcons'; import { Rocket } from './SvgIcons';
import Persist from '../stores/Persist'; import Persist from '../stores/Persist';

View File

@@ -6,7 +6,6 @@ import AvailableModulesMenu from './AvailableModulesMenu';
import ModificationsMenu from './ModificationsMenu'; import ModificationsMenu from './ModificationsMenu';
import { diffDetails } from '../utils/SlotFunctions'; import { diffDetails } from '../utils/SlotFunctions';
import { wrapCtxMenu } from '../utils/UtilityFunctions'; import { wrapCtxMenu } from '../utils/UtilityFunctions';
import { stopCtxPropagation } from '../utils/UtilityFunctions';
/** /**
* Abstract Slot * Abstract Slot
@@ -88,7 +87,7 @@ export default class Slot extends TranslatedComponent {
if(event.target.className == 'r') { if(event.target.className == 'r') {
this._toggleModifications(); this._toggleModifications();
} }
this.props.onOpen(event); this.props.onOpen(event);
} }
} }
/** /**

View File

@@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import cn from 'classnames';
import SlotSection from './SlotSection'; import SlotSection from './SlotSection';
import HardpointSlot from './HardpointSlot'; import HardpointSlot from './HardpointSlot';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';
@@ -8,7 +7,6 @@ import { stopCtxPropagation } from '../utils/UtilityFunctions';
* Utility Slot Section * Utility Slot Section
*/ */
export default class UtilitySlotSection extends SlotSection { export default class UtilitySlotSection extends SlotSection {
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
@@ -133,5 +131,4 @@ export default class UtilitySlotSection extends SlotSection {
</ul> </ul>
</div>; </div>;
} }
} }

View File

@@ -1,7 +1,7 @@
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import ContainerDimensions from 'react-container-dimensions'; import ContainerDimensions from 'react-container-dimensions';
import { BarChart, Bar, XAxis, YAxis } from 'recharts'; import { BarChart, Bar, XAxis, YAxis, LabelList } from 'recharts';
const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D']; const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D'];
const LABEL_COLOUR = '#000000'; const LABEL_COLOUR = '#000000';
@@ -17,7 +17,6 @@ const merge = function(one, two) {
* A vertical bar chart * A vertical bar chart
*/ */
export default class VerticalBarChart extends TranslatedComponent { export default class VerticalBarChart extends TranslatedComponent {
static propTypes = { static propTypes = {
data : PropTypes.array.isRequired, data : PropTypes.array.isRequired,
yMax : PropTypes.number yMax : PropTypes.number
@@ -54,7 +53,9 @@ export default class VerticalBarChart extends TranslatedComponent {
<BarChart width={width} height={width * ASPECT} data={this.props.data} margin={{ top: 5, right: 5, left: 5, bottom: 5 }}> <BarChart width={width} height={width * ASPECT} data={this.props.data} margin={{ top: 5, right: 5, left: 5, bottom: 5 }}>
<XAxis interval={0} fontSize='0.8em' stroke={AXIS_COLOUR} dataKey='label' /> <XAxis interval={0} fontSize='0.8em' stroke={AXIS_COLOUR} dataKey='label' />
<YAxis interval={'preserveStart'} tickCount={11} fontSize='0.8em' stroke={AXIS_COLOUR} type='number' domain={[0, localMax]}/> <YAxis interval={'preserveStart'} tickCount={11} fontSize='0.8em' stroke={AXIS_COLOUR} type='number' domain={[0, localMax]}/>
<Bar dataKey='value' label={<ValueLabel />} fill={CORIOLIS_COLOURS[0]} isAnimationActive={false} onMouseOver={this._termtip} onMouseOut={tooltip.bind(null, null)}/> <Bar dataKey='value' fill={CORIOLIS_COLOURS[0]} isAnimationActive={false} onMouseOver={this._termtip} onMouseOut={tooltip.bind(null, null)}>
<LabelList dataKey='value' position='insideTop'/>
</Bar>
</BarChart> </BarChart>
</div> </div>
)} )}
@@ -77,29 +78,3 @@ export default class VerticalBarChart extends TranslatedComponent {
} }
} }
} }
/**
* A label that displays the value within the bar of the chart
*/
class ValueLabel extends React.Component {
static propTypes = {
x: PropTypes.number,
y: PropTypes.number,
payload: PropTypes.object,
value: PropTypes.number
};
/**
* Render offence
* @return {React.Component} contents
*/
render() {
const { x, y, payload, value } = this.props;
const em = value < 1000 ? '1em' : value < 1000 ? '0.8em' : '0.7em';
return (
<text x={x} y={y} fill="#000000" textAnchor="middle" dy={20} style={{ fontSize: em }}>{value}</text>
);
}
};

View File

@@ -1,13 +1,8 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import { nameComparator } from '../utils/SlotFunctions';
import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
import LineChart from '../components/LineChart'; import LineChart from '../components/LineChart';
import Slider from '../components/Slider';
import * as Calc from '../shipyard/Calculations'; import * as Calc from '../shipyard/Calculations';
import Module from '../shipyard/Module';
const DAMAGE_DEALT_COLORS = ['#FFFFFF', '#FF0000', '#00FF00', '#7777FF', '#FFFF00', '#FF00FF', '#00FFFF', '#777777']; const DAMAGE_DEALT_COLORS = ['#FFFFFF', '#FF0000', '#00FF00', '#7777FF', '#FFFF00', '#FF00FF', '#00FFFF', '#777777'];

View File

@@ -43,6 +43,7 @@
"PHRASE_SHIELD_DAMAGE": "Breakdown of sources for sustained DPS against shields", "PHRASE_SHIELD_DAMAGE": "Breakdown of sources for sustained DPS against shields",
"PHRASE_ARMOUR_DAMAGE": "Breakdown of sources for sustained DPS against armour", "PHRASE_ARMOUR_DAMAGE": "Breakdown of sources for sustained DPS against armour",
"PHRASE_TIME_TO_REMOVE_SHIELDS": "Will remove shields in", "PHRASE_TIME_TO_REMOVE_SHIELDS": "Will remove shields in",
"PHRASE_MULTI_CREW_CAPACITOR_POINTS": "Right click a capacitor to assign multi-crew capacitor points.",
"TT_TIME_TO_REMOVE_SHIELDS": "With sustained fire by all weapons", "TT_TIME_TO_REMOVE_SHIELDS": "With sustained fire by all weapons",
"PHRASE_TIME_TO_REMOVE_ARMOUR": "Will remove armour in", "PHRASE_TIME_TO_REMOVE_ARMOUR": "Will remove armour in",
"TT_TIME_TO_REMOVE_ARMOUR": "With sustained fire by all weapons", "TT_TIME_TO_REMOVE_ARMOUR": "With sustained fire by all weapons",

View File

@@ -1,4 +1,4 @@
import 'babel-polyfill'; import '@babel/polyfill';
import React from 'react'; import React from 'react';
import { render } from 'react-dom'; import { render } from 'react-dom';
import '../less/app.less'; import '../less/app.less';

12
src/app/model/Build.js Normal file
View File

@@ -0,0 +1,12 @@
import { Model } from '@nozbe/watermelondb';
import { field } from '@nozbe/watermelondb/decorators';
export default class Build extends Model {
static table = 'builds';
@field('title') title;
@field('code') code;
@field('ship_id') ship_id;
}

22
src/app/model/index.js Normal file
View File

@@ -0,0 +1,22 @@
import { Database } from '@nozbe/watermelondb';
import LokiJSAdapter from '@nozbe/watermelondb/adapters/lokijs';
import { coriolisSchema } from './schema';
import Build from './Build';
// import Post from './model/Post' // ⬅️ You'll import your Models here
// First, create the adapter to the underlying database:
const adapter = new LokiJSAdapter({
dbName: 'coriolis', // ⬅️ Give your database a name!
schema: coriolisSchema
});
// Then, make a Watermelon database from it!
export const database = new Database({
adapter,
modelClasses: [
Build
]
});
export const buildsCollection = database.collections.get('builds');

15
src/app/model/schema.js Normal file
View File

@@ -0,0 +1,15 @@
import {appSchema, tableSchema} from '@nozbe/watermelondb';
export const coriolisSchema = appSchema({
version: 2,
tables: [
tableSchema({
name: 'builds',
columns: [
{name: 'title', type: 'string', isIndexed: true},
{name: 'code', type: 'string'},
{name: 'ship_id', type: 'string'}
]
})
]
});

View File

@@ -6,7 +6,6 @@ import { CoriolisLogo, GitHub } from '../components/SvgIcons';
* About Page * About Page
*/ */
export default class AboutPage extends Page { export default class AboutPage extends Page {
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
@@ -23,33 +22,112 @@ export default class AboutPage extends Page {
* @return {React.Component} The page contents * @return {React.Component} The page contents
*/ */
renderPage() { renderPage() {
return <div className={'page'} style={{ textAlign: 'left', maxWidth: 800, margin: '0 auto' }}> return (
<h1><CoriolisLogo style={{ marginRight: '0.4em' }} className='xl'/><span className='warning'>Coriolis EDCD Edition</span></h1> <div
className={'page'}
style={{ textAlign: 'left', maxWidth: 800, margin: '0 auto' }}
>
<h1>
<CoriolisLogo style={{ marginRight: '0.4em' }} className="xl" />
<span className="warning">Coriolis EDCD Edition</span>
</h1>
<p>This is a clone of the Coriolis project, whose original author is currently unable to maintain it. This clone is maintained by the <a href="http://edcd.github.io/">EDCD community</a>.</p> <p>
<p>To recover your builds, go to <a href='https://coriolis.io/' target='_blank'>https://coriolis.io/</a>, backup your builds (Settings / Backup), copy the text, return here and import (Settings / Import).</p> This is a clone of the Coriolis project, whose original author is
<p>The Coriolis project was inspired by <a href='http://www.edshipyard.com/' target='_blank'>E:D Shipyard</a> and, of course, <a href='http://www.elitedangerous.com' target='_blank'>Elite Dangerous</a>. The ultimate goal of Coriolis is to provide rich features to support in-game play and planning while engaging the E:D community to support its development.</p> currently unable to maintain it. This clone is maintained by the{' '}
<p>Coriolis was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments. A number of assets were sourced from <a href='http://edassets.org' target='_blank'>ED Assets</a></p> <a href="http://edcd.github.io/">EDCD community</a>.
</p>
<p>
To recover your builds, go to{' '}
<a href="https://coriolis.io/" target="_blank">
https://coriolis.io/
</a>
, backup your builds (Settings / Backup), copy the text, return here
and import (Settings / Import).
</p>
<p>
The Coriolis project was inspired by{' '}
<a href="http://www.edshipyard.com/" target="_blank">
E:D Shipyard
</a>{' '}
and, of course,{' '}
<a href="http://www.elitedangerous.com" target="_blank">
Elite Dangerous
</a>
. The ultimate goal of Coriolis is to provide rich features to support
in-game play and planning while engaging the E:D community to support
its development.
</p>
<p>
Coriolis was created using assets and imagery from Elite: Dangerous,
with the permission of Frontier Developments plc, for non-commercial
purposes. It is not endorsed by nor reflects the views or opinions of
Frontier Developments. A number of assets were sourced from{' '}
<a href="http://edassets.org" target="_blank">
ED Assets
</a>
</p>
<a style={{ display: 'block', textDecoration: 'none' }} href='https://github.com/EDCD/coriolis' target='_blank' title='Coriolis Github Project'> <a
<GitHub style={{ margin: '0.4em' }} className='l fg xl'/> style={{ display: 'block', textDecoration: 'none' }}
<h2 style={{ margin: 0, textDecoration: 'none' }}>Github</h2> href="https://github.com/EDCD/coriolis"
github.com/EDCD/coriolis target="_blank"
</a> title="Coriolis Github Project"
>
<GitHub style={{ margin: '0.4em' }} className="l fg xl" />
<h2 style={{ margin: 0, textDecoration: 'none' }}>Github</h2>
github.com/EDCD/coriolis
</a>
<p>Coriolis is an open source project. Checkout the list of upcoming features and to-do list on github. Any and all contributions and feedback are welcome. If you encounter any bugs please report them and provide as much detail as possible.</p> <p>
Coriolis is an open source project. Checkout the list of upcoming
features and to-do list on github. Any and all contributions and
feedback are welcome. If you encounter any bugs please report them and
provide as much detail as possible.
</p>
<h3>Chat</h3> <h3>Chat</h3>
<p>You can chat to us on our <a href='https://discord.gg/0uwCh6R62aPRjk9w' target='_blank'>EDCD Discord server</a>.</p> <p>
You can chat to us on our{' '}
<a href="https://discord.gg/0uwCh6R62aPRjk9w" target="_blank">
EDCD Discord server
</a>
.
</p>
<h3>Supporting Coriolis</h3> <h3>Supporting Coriolis</h3>
<p>Coriolis is an open source project, and I work on it in my free time. I have set up a patreon at <a href='https://www.patreon.com/coriolis_elite'>patreon.com/coriolis_elite</a>, which will be used to keep Coriolis up to date and the servers running.</p> <p>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top"> Coriolis is an open source project, and I work on it in my free time.
<input type="hidden" name="cmd" value="_s-xclick"/> I have set up a patreon at{' '}
<input type="hidden" name="hosted_button_id" value="SJBKT2SWEEU68" /> <a href="https://www.patreon.com/coriolis_elite">
<input type="image" src="https://www.paypalobjects.com/en_AU/i/btn/btn_donate_SM.gif" border="0" name="submit" alt="PayPal The safer, easier way to pay online!" /> patreon.com/coriolis_elite
<img alt="" border="0" src="https://www.paypalobjects.com/en_AU/i/scr/pixel.gif" width="1" height="1" /> </a>
, which will be used to keep Coriolis up to date and the servers
running. I also run ads, which are also used for development and hosting.
</p>
<form
action="https://www.paypal.com/cgi-bin/webscr"
method="post"
target="_top"
>
<input type="hidden" name="cmd" value="_s-xclick" />
<input type="hidden" name="hosted_button_id" value="SJBKT2SWEEU68" />
<input
type="image"
src="https://www.paypalobjects.com/en_AU/i/btn/btn_donate_SM.gif"
border="0"
name="submit"
alt="PayPal The safer, easier way to pay online!"
/>
<img
alt=""
border="0"
src="https://www.paypalobjects.com/en_AU/i/scr/pixel.gif"
width="1"
height="1"
/>
</form> </form>
</div>; </div>
);
} }
} }

View File

@@ -13,7 +13,14 @@ import ModalCompare from '../components/ModalCompare';
import ModalExport from '../components/ModalExport'; import ModalExport from '../components/ModalExport';
import ModalPermalink from '../components/ModalPermalink'; import ModalPermalink from '../components/ModalPermalink';
import ModalImport from '../components/ModalImport'; import ModalImport from '../components/ModalImport';
import { FloppyDisk, Bin, Download, Embed, Rocket, LinkIcon } from '../components/SvgIcons'; import {
FloppyDisk,
Bin,
Download,
Embed,
Rocket,
LinkIcon
} from '../components/SvgIcons';
import ShortenUrl from '../utils/ShortenUrl'; import ShortenUrl from '../utils/ShortenUrl';
import { comparisonBBCode } from '../utils/BBCode'; import { comparisonBBCode } from '../utils/BBCode';
const browser = require('detect-browser'); const browser = require('detect-browser');
@@ -42,7 +49,6 @@ function sortBy(predicate) {
* Comparison Page * Comparison Page
*/ */
export default class ComparisonPage extends Page { export default class ComparisonPage extends Page {
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
@@ -81,7 +87,13 @@ export default class ComparisonPage extends Page {
for (let shipId in allBuilds) { for (let shipId in allBuilds) {
for (let buildName in allBuilds[shipId]) { for (let buildName in allBuilds[shipId]) {
if (buildName && allBuilds[shipId][buildName]) { if (buildName && allBuilds[shipId][buildName]) {
builds.push(this._createBuild(shipId, buildName, allBuilds[shipId][buildName])); builds.push(
this._createBuild(
shipId,
buildName,
allBuilds[shipId][buildName]
)
);
} }
} }
} }
@@ -89,7 +101,9 @@ export default class ComparisonPage extends Page {
let comparisonData = Persist.getComparison(name); let comparisonData = Persist.getComparison(name);
if (comparisonData) { if (comparisonData) {
defaultFacets = comparisonData.facets; defaultFacets = comparisonData.facets;
comparisonData.builds.forEach((b) => builds.push(this._createBuild(b.shipId, b.buildName))); comparisonData.builds.forEach(b =>
builds.push(this._createBuild(b.shipId, b.buildName))
);
saved = true; saved = true;
newName = name; newName = name;
} }
@@ -101,7 +115,7 @@ export default class ComparisonPage extends Page {
newName = name = comparisonData.n; newName = name = comparisonData.n;
predicate = comparisonData.p; predicate = comparisonData.p;
desc = comparisonData.d; desc = comparisonData.d;
comparisonData.b.forEach((build) => { comparisonData.b.forEach(build => {
builds.push(this._createBuild(build.s, build.n, build.c)); builds.push(this._createBuild(build.s, build.n, build.c));
if (!importObj[build.s]) { if (!importObj[build.s]) {
importObj[build.s] = {}; importObj[build.s] = {};
@@ -118,9 +132,9 @@ export default class ComparisonPage extends Page {
let selectedFacets = new Array(selectedLength); let selectedFacets = new Array(selectedLength);
for (let i = 0; i < ShipFacets.length; i++) { for (let i = 0; i < ShipFacets.length; i++) {
let facet = Object.assign({ }, ShipFacets[i]); let facet = Object.assign({}, ShipFacets[i]);
let defaultIndex = defaultFacets.indexOf(facet.i); let defaultIndex = defaultFacets.indexOf(facet.i);
if(defaultIndex == -1) { if (defaultIndex == -1) {
facets.push(facet); facets.push(facet);
} else { } else {
facet.active = true; facet.active = true;
@@ -155,17 +169,18 @@ export default class ComparisonPage extends Page {
_createBuild(id, name, code) { _createBuild(id, name, code) {
code = code ? code : Persist.getBuild(id, name); // Retrieve build code if not passed code = code ? code : Persist.getBuild(id, name); // Retrieve build code if not passed
if (!code) { // No build found if (!code) {
// No build found
return; return;
} }
let data = Ships[id]; // Get ship properties let data = Ships[id]; // Get ship properties
let b = new Ship(id, data.properties, data.slots); // Create a new Ship instance let b = new Ship(id, data.properties, data.slots); // Create a new Ship instance
b.buildFrom(code); // Populate components from code b.buildFrom(code); // Populate components from code
b.buildName = name; b.buildName = name;
b.applyDiscounts(Persist.getShipDiscount(), Persist.getModuleDiscount()); b.applyDiscounts(Persist.getShipDiscount(), Persist.getModuleDiscount());
return b; return b;
}; }
/** /**
* Update state with the specified sort predicates * Update state with the specified sort predicates
@@ -184,13 +199,18 @@ export default class ComparisonPage extends Page {
} }
this.setState({ predicate, desc }); this.setState({ predicate, desc });
}; }
/** /**
* Show selected builds modal * Show selected builds modal
*/ */
_selectBuilds() { _selectBuilds() {
this.context.showModal(<ModalCompare onSelect={this._buildsSelected} builds={this.state.builds} />); this.context.showModal(
<ModalCompare
onSelect={this._buildsSelected}
builds={this.state.builds}
/>
);
} }
/** /**
@@ -224,7 +244,7 @@ export default class ComparisonPage extends Page {
_facetDrag(e) { _facetDrag(e) {
this.nodeAfter = false; this.nodeAfter = false;
this.dragged = e.currentTarget; this.dragged = e.currentTarget;
let placeholder = this.placeholder = document.createElement('li'); let placeholder = (this.placeholder = document.createElement('li'));
placeholder.style.width = Math.round(this.dragged.offsetWidth) + 'px'; placeholder.style.width = Math.round(this.dragged.offsetWidth) + 'px';
placeholder.className = 'facet-placeholder'; placeholder.className = 'facet-placeholder';
if (!browser || (browser.name !== 'edge' && browser.name !== 'ie')) { if (!browser || (browser.name !== 'edge' && browser.name !== 'ie')) {
@@ -262,7 +282,7 @@ export default class ComparisonPage extends Page {
_facetDragOver(e) { _facetDragOver(e) {
e.preventDefault(); e.preventDefault();
if(e.target.className == 'facet-placeholder') { if (e.target.className == 'facet-placeholder') {
return; return;
} else if (e.target != e.currentTarget) { } else if (e.target != e.currentTarget) {
this.over = e.target; this.over = e.target;
@@ -272,7 +292,7 @@ export default class ComparisonPage extends Page {
let parent = e.target.parentNode; let parent = e.target.parentNode;
if (parent == e.currentTarget) { if (parent == e.currentTarget) {
if(relX > width && this.dragged != e.target) { if (relX > width && this.dragged != e.target) {
this.nodeAfter = true; this.nodeAfter = true;
parent.insertBefore(this.placeholder, e.target.nextElementSibling); parent.insertBefore(this.placeholder, e.target.nextElementSibling);
} else { } else {
@@ -321,7 +341,7 @@ export default class ComparisonPage extends Page {
let { newName, builds, facets } = this.state; let { newName, builds, facets } = this.state;
let selectedFacets = []; let selectedFacets = [];
facets.forEach((f) => { facets.forEach(f => {
if (f.active) { if (f.active) {
selectedFacets.unshift(f.i); selectedFacets.unshift(f.i);
} }
@@ -348,14 +368,20 @@ export default class ComparisonPage extends Page {
let code = fromComparison(name, builds, selectedFacets, predicate, desc); let code = fromComparison(name, builds, selectedFacets, predicate, desc);
let loc = window.location; let loc = window.location;
return loc.protocol + '//' + loc.host + '/comparison?code=' + encodeURIComponent(code); return (
loc.protocol +
'//' +
loc.host +
'/comparison?code=' +
encodeURIComponent(code)
);
} }
/** /**
* Generates the long permalink URL * Generates the long permalink URL
*/ */
_genPermalink() { _genPermalink() {
this.context.showModal(<ModalPermalink url={this._buildUrl()}/>); this.context.showModal(<ModalPermalink url={this._buildUrl()} />);
} }
/** /**
@@ -365,18 +391,25 @@ export default class ComparisonPage extends Page {
let { translate, formats } = this.context.language; let { translate, formats } = this.context.language;
let { facets, builds } = this.state; let { facets, builds } = this.state;
let generator = (callback) => { let generator = callback => {
let url = this._buildUrl(); let url = this._buildUrl();
ShortenUrl(url, ShortenUrl(
(shortenedUrl) => callback(comparisonBBCode(translate, formats, facets, builds, shortenedUrl)), url,
(error) => callback(comparisonBBCode(translate, formats, facets, builds, url)) shortenedUrl =>
callback(
comparisonBBCode(translate, formats, facets, builds, shortenedUrl)
),
error =>
callback(comparisonBBCode(translate, formats, facets, builds, url))
); );
}; };
this.context.showModal(<ModalExport this.context.showModal(
title={translate('forum') + ' BBCode'} <ModalExport
generator={generator} title={translate('forum') + ' BBCode'}
/>); generator={generator}
/>
);
} }
/** /**
@@ -409,7 +442,8 @@ export default class ComparisonPage extends Page {
* @param {Object} nextContext Incoming/Next conext * @param {Object} nextContext Incoming/Next conext
*/ */
componentWillReceiveProps(nextProps, nextContext) { componentWillReceiveProps(nextProps, nextContext) {
if (this.context.route !== nextContext.route) { // Only reinit state if the route has changed if (this.context.route !== nextContext.route) {
// Only reinit state if the route has changed
this.setState(this._initState(nextContext)); this.setState(this._initState(nextContext));
} }
} }
@@ -419,7 +453,10 @@ export default class ComparisonPage extends Page {
*/ */
componentWillMount() { componentWillMount() {
this.resizeListener = this.context.onWindowResize(this._updateDimensions); this.resizeListener = this.context.onWindowResize(this._updateDimensions);
this.persistListener = Persist.addListener('discounts', this._updateDiscounts); this.persistListener = Persist.addListener(
'discounts',
this._updateDiscounts
);
} }
/** /**
@@ -444,65 +481,132 @@ export default class ComparisonPage extends Page {
renderPage() { renderPage() {
let translate = this.context.language.translate; let translate = this.context.language.translate;
let compareHeader; let compareHeader;
let { newName, name, saved, builds, facets, predicate, desc, chartWidth } = this.state; let {
newName,
name,
saved,
builds,
facets,
predicate,
desc,
chartWidth
} = this.state;
if (this.state.compareMode) { if (this.state.compareMode) {
compareHeader = <tr> compareHeader = (
<td className='head'>{translate('comparison')}</td> <tr>
<td> <td className="head">{translate('comparison')}</td>
<input value={newName} onChange={this._onNameChange} placeholder={translate('Enter Name')} maxLength='50' /> <td>
<button onClick={this._save} disabled={!newName || newName == 'all' || saved}> <input
<FloppyDisk className='lg'/><span className='button-lbl'>{translate('save')}</span> value={newName}
</button> onChange={this._onNameChange}
<button onClick={this._delete} disabled={name == 'all' || !saved}><Bin className='lg warning'/></button> placeholder={translate('Enter Name')}
<button onClick={this._selectBuilds}> maxLength="50"
<Rocket className='lg'/><span className='button-lbl'>{translate('builds')}</span> />
</button> <button
<button className='r' onClick={this._genPermalink} disabled={builds.length == 0}> onClick={this._save}
<LinkIcon className='lg'/><span className='button-lbl'>{translate('permalink')}</span> disabled={!newName || newName == 'all' || saved}
</button> >
<button className='r' onClick={this._genBBcode} disabled={builds.length == 0}> <FloppyDisk className="lg" />
<Embed className='lg'/><span className='button-lbl'>{translate('forum')}</span> <span className="button-lbl">{translate('save')}</span>
</button> </button>
</td> <button onClick={this._delete} disabled={name == 'all' || !saved}>
</tr>; <Bin className="lg warning" />
</button>
<button onClick={this._selectBuilds}>
<Rocket className="lg" />
<span className="button-lbl">{translate('builds')}</span>
</button>
<button
className="r"
onClick={this._genPermalink}
disabled={builds.length == 0}
>
<LinkIcon className="lg" />
<span className="button-lbl">{translate('permalink')}</span>
</button>
<button
className="r"
onClick={this._genBBcode}
disabled={builds.length == 0}
>
<Embed className="lg" />
<span className="button-lbl">{translate('forum')}</span>
</button>
</td>
</tr>
);
} else { } else {
compareHeader = <tr> compareHeader = (
<td className='head'>{translate('comparison')}</td> <tr>
<td> <td className="head">{translate('comparison')}</td>
<h3>{name}</h3> <td>
<button className='r' onClick={this._import}><Download className='lg'/>{translate('import')}</button> <h3>{name}</h3>
</td> <button className="r" onClick={this._import}>
</tr>; <Download className="lg" />
{translate('import')}
</button>
</td>
</tr>
);
} }
return ( return (
<div className={'page'} style={{ fontSize: this.context.sizeRatio + 'em' }}> <div
<table id='comparison'> className={'page'}
style={{ fontSize: this.context.sizeRatio + 'em' }}
>
<table id="comparison">
<tbody> <tbody>
{compareHeader} {compareHeader}
<tr key='facets'> <tr key="facets">
<td className='head'>{translate('compare')}</td> <td className="head">{translate('compare')}</td>
<td> <td>
<ul id='facet-container' onDragOver={this._facetDragOver}> <ul id="facet-container" onDragOver={this._facetDragOver}>
{facets.map((f, i) => {facets.map((f, i) => (
<li key={f.title} data-i={i} draggable='true' onDragStart={this._facetDrag} onDragEnd={this._facetDrop} className={cn('facet', { active: f.active })} onClick={this._toggleFacet.bind(this, f)}> <li
key={f.title}
data-i={i}
draggable="true"
onDragStart={this._facetDrag}
onDragEnd={this._facetDrop}
className={cn('facet', { active: f.active })}
onClick={this._toggleFacet.bind(this, f)}
>
{'↔ ' + translate(f.title)} {'↔ ' + translate(f.title)}
</li> </li>
)} ))}
</ul> </ul>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<ComparisonTable builds={builds} facets={facets} onSort={this._sortShips} predicate={predicate} desc={desc} /> <ComparisonTable
builds={builds}
facets={facets}
onSort={this._sortShips}
predicate={predicate}
desc={desc}
/>
{!builds.length ? {!builds.length ? (
<div className='chart' ref={node => this.chartRef = node}>{translate('PHRASE_NO_BUILDS')}</div> : <div className="chart" ref={node => (this.chartRef = node)}>
facets.filter((f) => f.active).map((f, i) => {translate('PHRASE_NO_BUILDS')}
<div key={f.title} className='chart' ref={ i == 0 ? node => this.chartRef = node : null}> </div>
<h3 className='ptr' onClick={this._sortShips.bind(this, f.props[0])}>{translate(f.title)}</h3> ) : (
facets.filter(f => f.active).map((f, i) => (
<div
key={f.title}
className="chart"
ref={i == 0 ? node => (this.chartRef = node) : null}
>
<h3
className="ptr"
onClick={this._sortShips.bind(this, f.props[0])}
>
{translate(f.title)}
</h3>
<BarChart <BarChart
width={chartWidth} width={chartWidth}
data={builds} data={builds}
@@ -515,8 +619,8 @@ export default class ComparisonPage extends Page {
desc={desc} desc={desc}
/> />
</div> </div>
))
)} )}
</div> </div>
); );
} }

View File

@@ -5,7 +5,6 @@ import Page from './Page';
* 404 Page * 404 Page
*/ */
export default class NotFoundPage extends Page { export default class NotFoundPage extends Page {
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
@@ -22,6 +21,10 @@ export default class NotFoundPage extends Page {
* @return {React.Component} The page contents * @return {React.Component} The page contents
*/ */
renderPage() { renderPage() {
return <div className='page' style={{ marginTop: 30 }}>Page <small>{this.context.route.path}</small> Not Found</div>; return (
<div className="page" style={{ marginTop: 30 }}>
Page <small>{this.context.route.path}</small> Not Found
</div>
);
} }
} }

View File

@@ -38,6 +38,7 @@ import ModalExport from '../components/ModalExport';
import ModalPermalink from '../components/ModalPermalink'; import ModalPermalink from '../components/ModalPermalink';
import ModalShoppingList from '../components/ModalShoppingList'; import ModalShoppingList from '../components/ModalShoppingList';
import ModalOrbis from '../components/ModalOrbis'; import ModalOrbis from '../components/ModalOrbis';
import { autoBind } from 'react-extras';
/** /**
* Document Title Generator * Document Title Generator
@@ -53,7 +54,6 @@ function getTitle(shipName, buildName) {
* The Outfitting Page * The Outfitting Page
*/ */
export default class OutfittingPage extends Page { export default class OutfittingPage extends Page {
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
@@ -63,14 +63,7 @@ export default class OutfittingPage extends Page {
super(props, context); super(props, context);
// window.Perf = Perf; // window.Perf = Perf;
this.state = this._initState(props, context); this.state = this._initState(props, context);
this._keyDown = this._keyDown.bind(this); autoBind(this);
this._exportBuild = this._exportBuild.bind(this);
this._pipsUpdated = this._pipsUpdated.bind(this);
this._boostUpdated = this._boostUpdated.bind(this);
this._cargoUpdated = this._cargoUpdated.bind(this);
this._fuelUpdated = this._fuelUpdated.bind(this);
this._opponentUpdated = this._opponentUpdated.bind(this);
this._engagementRangeUpdated = this._engagementRangeUpdated.bind(this);
this._sectionMenuRefs = {}; this._sectionMenuRefs = {};
} }
@@ -84,23 +77,39 @@ export default class OutfittingPage extends Page {
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;
let savedCode = code;
let buildName = params.bn; let buildName = params.bn;
let data = Ships[shipId]; // Retrieve the basic ship properties, slots and defaults let data = Ships[shipId]; // Retrieve the basic ship properties, slots and defaults
let savedCode = Persist.getBuild(shipId, buildName);
if (!data) { if (!data) {
return { error: { message: 'Ship not found: ' + shipId } }; return { error: { message: 'Ship not found: ' + shipId } };
} }
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
if (code) { if (code) {
ship.buildFrom(code); // Populate modules from serialized '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
} }
this._getTitle = getTitle.bind(this, data.properties.name); this._getTitle = getTitle.bind(this, data.properties.name);
// Obtain ship control from code // Obtain ship control from code
const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange } = this._obtainControlFromCode(ship, code); const {
sys,
eng,
wep,
mcSys,
mcEng,
mcWep,
boost,
fuel,
cargo,
opponent,
opponentBuild,
opponentSys,
opponentEng,
opponentWep,
engagementRange
} = this._obtainControlFromCode(ship, code);
return { return {
error: null, error: null,
title: this._getTitle(buildName), title: this._getTitle(buildName),
@@ -114,6 +123,9 @@ export default class OutfittingPage extends Page {
sys, sys,
eng, eng,
wep, wep,
mcSys,
mcEng,
mcWep,
boost, boost,
fuel, fuel,
cargo, cargo,
@@ -126,6 +138,13 @@ export default class OutfittingPage extends Page {
}; };
} }
async getBuild() {
const build = await Persist.getBuild(this.state.shipId, this.state.buildName);
if (build) {
this.setState({id: build.id, savedCode: build.code})
}
}
/** /**
* Handle build name change and update state * Handle build name change and update state
* @param {SyntheticEvent} event React Event * @param {SyntheticEvent} event React Event
@@ -136,7 +155,10 @@ export default class OutfittingPage extends Page {
}; };
if (Persist.hasBuild(this.state.shipId, stateChanges.newBuildName)) { if (Persist.hasBuild(this.state.shipId, stateChanges.newBuildName)) {
stateChanges.savedCode = Persist.getBuild(this.state.shipId, stateChanges.newBuildName); stateChanges.savedCode = Persist.getBuild(
this.state.shipId,
stateChanges.newBuildName
);
} else { } else {
stateChanges.savedCode = null; stateChanges.savedCode = null;
} }
@@ -162,7 +184,9 @@ export default class OutfittingPage extends Page {
* @returns {string} the code for this ship * @returns {string} the code for this ship
*/ */
_fullCode(ship, fuel, cargo) { _fullCode(ship, fuel, cargo) {
return `${ship.toString()}.${LZString.compressToBase64(this._controlCode(fuel, cargo))}`; return `${ship.toString()}.${LZString.compressToBase64(
this._controlCode(fuel, cargo)
)}`;
} }
/** /**
@@ -176,10 +200,17 @@ export default class OutfittingPage extends Page {
let sys = 2; let sys = 2;
let eng = 2; let eng = 2;
let wep = 2; let wep = 2;
let mcSys = 0;
let mcEng = 0;
let mcWep = 0;
let boost = false; let boost = false;
let fuel = ship.fuelCapacity; let fuel = ship.fuelCapacity;
let cargo = ship.cargoCapacity; let cargo = ship.cargoCapacity;
let opponent = new Ship('eagle', Ships['eagle'].properties, Ships['eagle'].slots).buildWith(Ships['eagle'].defaults); let opponent = new Ship(
'eagle',
Ships['eagle'].properties,
Ships['eagle'].slots
).buildWith(Ships['eagle'].defaults);
let opponentSys = 2; let opponentSys = 2;
let opponentEng = 2; let opponentEng = 2;
let opponentWep = 2; let opponentWep = 2;
@@ -191,16 +222,22 @@ export default class OutfittingPage extends Page {
const parts = code.split('.'); const parts = code.split('.');
if (parts.length >= 5) { if (parts.length >= 5) {
// We have control information in the code // We have control information in the code
const control = LZString.decompressFromBase64(Utils.fromUrlSafe(parts[4])).split('/'); const control = LZString.decompressFromBase64(
sys = parseFloat(control[0]); Utils.fromUrlSafe(parts[4])
eng = parseFloat(control[1]); ).split('/');
wep = parseFloat(control[2]); sys = parseFloat(control[0]) || sys;
eng = parseFloat(control[1]) || eng;
wep = parseFloat(control[2]) || wep;
boost = control[3] == 1 ? true : false; boost = control[3] == 1 ? true : false;
fuel = parseFloat(control[4]); fuel = parseFloat(control[4]) || fuel;
cargo = parseInt(control[5]); cargo = parseInt(control[5]) || cargo;
if (control[6]) { if (control[6]) {
const shipId = control[6]; const shipId = control[6];
opponent = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots); opponent = new Ship(
shipId,
Ships[shipId].properties,
Ships[shipId].slots
);
if (control[7] && Persist.getBuild(shipId, control[7])) { if (control[7] && Persist.getBuild(shipId, control[7])) {
// Ship is a particular build // Ship is a particular build
const opponentCode = Persist.getBuild(shipId, control[7]); const opponentCode = Persist.getBuild(shipId, control[7]);
@@ -210,10 +247,12 @@ export default class OutfittingPage extends Page {
// Obtain opponent's sys/eng/wep pips from their code // Obtain opponent's sys/eng/wep pips from their code
const opponentParts = opponentCode.split('.'); const opponentParts = opponentCode.split('.');
if (opponentParts.length >= 5) { if (opponentParts.length >= 5) {
const opponentControl = LZString.decompressFromBase64(Utils.fromUrlSafe(opponentParts[4])).split('/'); const opponentControl = LZString.decompressFromBase64(
opponentSys = parseFloat(opponentControl[0]); Utils.fromUrlSafe(opponentParts[4])
opponentEng = parseFloat(opponentControl[1]); ).split('/');
opponentWep = parseFloat(opponentControl[2]); opponentSys = parseFloat(opponentControl[0]) || opponentSys;
opponentEng = parseFloat(opponentControl[1]) || opponentEng;
opponentWep = parseFloat(opponentControl[2]) || opponentWep;
} }
} }
} else { } else {
@@ -221,21 +260,50 @@ export default class OutfittingPage extends Page {
opponent.buildWith(Ships[shipId].defaults); opponent.buildWith(Ships[shipId].defaults);
} }
} }
engagementRange = parseInt(control[8]); engagementRange = parseInt(control[8]) || engagementRange;
// Multi-crew pips were introduced later on so assign default values
// because those values might not be present.
mcSys = parseInt(control[9]) || mcSys;
mcEng = parseInt(control[10]) || mcEng;
mcWep = parseInt(control[11]) || mcWep;
} }
} }
return { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange }; return {
sys,
eng,
wep,
mcSys,
mcEng,
mcWep,
boost,
fuel,
cargo,
opponent,
opponentBuild,
opponentSys,
opponentEng,
opponentWep,
engagementRange
};
} }
/** /**
* Triggered when pips have been updated * Triggered when pips have been updated. Multi-crew pips are already included
* in sys, eng and wep but mcSys, mcEng and mcWep make clear where each pip
* comes from.
* @param {number} sys SYS pips * @param {number} sys SYS pips
* @param {number} eng ENG pips * @param {number} eng ENG pips
* @param {number} wep WEP pips * @param {number} wep WEP pips
* @param {number} mcSys SYS pips from multi-crew
* @param {number} mcEng ENG pips from multi-crew
* @param {number} mcWep WEP pips from multi-crew
*/ */
_pipsUpdated(sys, eng, wep) { _pipsUpdated(sys, eng, wep, mcSys, mcEng, mcWep) {
this.setState({ sys, eng, wep }, () => this._updateRouteOnControlChange()); this.setState({ sys, eng, wep, mcSys, mcEng, mcWep }, () =>
this._updateRouteOnControlChange()
);
} }
/** /**
@@ -243,7 +311,7 @@ export default class OutfittingPage extends Page {
* @param {boolean} boost true if boosting * @param {boolean} boost true if boosting
*/ */
_boostUpdated(boost) { _boostUpdated(boost) {
this.setState({ boost }, () => this._updateRouteOnControlChange()); this.setState({ boost }, () => this._updateRouteOnControlChange());
} }
/** /**
@@ -251,7 +319,7 @@ export default class OutfittingPage extends Page {
* @param {number} fuel the amount of fuel, in T * @param {number} fuel the amount of fuel, in T
*/ */
_fuelUpdated(fuel) { _fuelUpdated(fuel) {
this.setState({ fuel }, () => this._updateRouteOnControlChange()); this.setState({ fuel }, () => this._updateRouteOnControlChange());
} }
/** /**
@@ -259,7 +327,7 @@ export default class OutfittingPage extends Page {
* @param {number} cargo the amount of cargo, in T * @param {number} cargo the amount of cargo, in T
*/ */
_cargoUpdated(cargo) { _cargoUpdated(cargo) {
this.setState({ cargo }, () => this._updateRouteOnControlChange()); this.setState({ cargo }, () => this._updateRouteOnControlChange());
} }
/** /**
@@ -267,7 +335,9 @@ export default class OutfittingPage extends Page {
* @param {number} engagementRange the engagement range, in m * @param {number} engagementRange the engagement range, in m
*/ */
_engagementRangeUpdated(engagementRange) { _engagementRangeUpdated(engagementRange) {
this.setState({ engagementRange }, () => this._updateRouteOnControlChange()); this.setState({ engagementRange }, () =>
this._updateRouteOnControlChange()
);
} }
/** /**
@@ -276,7 +346,11 @@ export default class OutfittingPage extends Page {
* @param {string} opponentBuild the name of the opponent's build * @param {string} opponentBuild the name of the opponent's build
*/ */
_opponentUpdated(opponent, opponentBuild) { _opponentUpdated(opponent, opponentBuild) {
const opponentShip = new Ship(opponent, Ships[opponent].properties, Ships[opponent].slots); const opponentShip = new Ship(
opponent,
Ships[opponent].properties,
Ships[opponent].slots
);
let opponentSys = this.state.opponentSys; let opponentSys = this.state.opponentSys;
let opponentEng = this.state.opponentEng; let opponentEng = this.state.opponentEng;
let opponentWep = this.state.opponentWep; let opponentWep = this.state.opponentWep;
@@ -284,9 +358,13 @@ export default class OutfittingPage extends Page {
// Ship is a particular build // Ship is a particular build
opponentShip.buildFrom(Persist.getBuild(opponent, opponentBuild)); opponentShip.buildFrom(Persist.getBuild(opponent, opponentBuild));
// Set pips for opponent // Set pips for opponent
const opponentParts = Persist.getBuild(opponent, opponentBuild).split('.'); const opponentParts = Persist.getBuild(opponent, opponentBuild).split(
'.'
);
if (opponentParts.length >= 5) { if (opponentParts.length >= 5) {
const opponentControl = LZString.decompressFromBase64(Utils.fromUrlSafe(opponentParts[4])).split('/'); const opponentControl = LZString.decompressFromBase64(
Utils.fromUrlSafe(opponentParts[4])
).split('/');
opponentSys = parseFloat(opponentControl[0]); opponentSys = parseFloat(opponentControl[0]);
opponentEng = parseFloat(opponentControl[1]); opponentEng = parseFloat(opponentControl[1]);
opponentWep = parseFloat(opponentControl[2]); opponentWep = parseFloat(opponentControl[2]);
@@ -299,7 +377,16 @@ export default class OutfittingPage extends Page {
opponentWep = 2; opponentWep = 2;
} }
this.setState({ opponent: opponentShip, opponentBuild, opponentSys, opponentEng, opponentWep }, () => this._updateRouteOnControlChange()); this.setState(
{
opponent: opponentShip,
opponentBuild,
opponentSys,
opponentEng,
opponentWep
},
() => this._updateRouteOnControlChange()
);
} }
/** /**
@@ -309,39 +396,71 @@ export default class OutfittingPage extends Page {
* @returns {string} The control code * @returns {string} The control code
*/ */
_controlCode(fuel, cargo) { _controlCode(fuel, cargo) {
const { sys, eng, wep, boost, opponent, opponentBuild, engagementRange } = this.state; const {
const code = `${sys}/${eng}/${wep}/${boost ? 1 : 0}/${fuel || this.state.fuel}/${cargo || this.state.cargo}/${opponent.id}/${opponentBuild ? opponentBuild : ''}/${engagementRange}`; sys,
eng,
wep,
mcSys,
mcEng,
mcWep,
boost,
opponent,
opponentBuild,
engagementRange
} = this.state;
const code = `${sys}/${eng}/${wep}/${boost ? 1 : 0}/${fuel ||
this.state.fuel}/${cargo || this.state.cargo}/${opponent.id}/${
opponentBuild ? opponentBuild : ''
}/${engagementRange}/${mcSys}/${mcEng}/${mcWep}`;
return code; return code;
} }
/** /**
* Save the current build * Save the current build
*/ */
_saveBuild() { async _saveBuild() {
const { ship, buildName, newBuildName, shipId } = this.state; const { ship, buildName, newBuildName, shipId, id } = this.state;
this.getBuild();
// If this is a stock ship the code won't be set, so ensure that we have it // If this is a stock ship the code won't be set, so ensure that we have it
const code = this.state.code || ship.toString(); const code = this.state.code || ship.toString();
Persist.saveBuild(shipId, newBuildName, code); const yes = await Persist.saveBuild(id, newBuildName, code, shipId);
console.log(yes)
this._updateRoute(shipId, newBuildName, code); this._updateRoute(shipId, newBuildName, code);
let opponent, opponentBuild, opponentSys, opponentEng, opponentWep; let opponent, opponentBuild, opponentSys, opponentEng, opponentWep;
if (shipId === this.state.opponent.id && buildName === this.state.opponentBuild) { if (
shipId === this.state.opponent.id &&
buildName === this.state.opponentBuild
) {
// This is a save of our current opponent build; update it // This is a save of our current opponent build; update it
opponentBuild = newBuildName; opponentBuild = newBuildName;
opponent = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots).buildFrom(code); opponent = new Ship(
shipId,
Ships[shipId].properties,
Ships[shipId].slots
).buildFrom(code);
opponentSys = this.state.sys; opponentSys = this.state.sys;
opponentEng = this.state.eng; opponentEng = this.state.eng;
opponentWep = this.state.wep; opponentWep = this.state.wep;
} else { } else {
opponentBuild = this.state.opponentBuild; opponentBuild = this.state.opponentBuild;
opponent = this.state.opponent; opponent = this.state.opponent;
opponentSys = this.state.opponentSys; opponentSys = this.state.opponentSys;
opponentEng = this.state.opponentEng; opponentEng = this.state.opponentEng;
opponentWep = this.state.opponentWep; opponentWep = this.state.opponentWep;
} }
this.setState({ buildName: newBuildName, code, savedCode: code, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, title: this._getTitle(newBuildName) }); this.setState({
buildName: newBuildName,
code,
savedCode: code,
opponent,
opponentBuild,
opponentSys,
opponentEng,
opponentWep,
title: this._getTitle(newBuildName)
});
} }
/** /**
@@ -353,7 +472,12 @@ export default class OutfittingPage extends Page {
Persist.deleteBuild(shipId, buildName); Persist.deleteBuild(shipId, buildName);
Persist.saveBuild(shipId, newBuildName, code); Persist.saveBuild(shipId, newBuildName, code);
this._updateRoute(shipId, newBuildName, code); this._updateRoute(shipId, newBuildName, code);
this.setState({ buildName: newBuildName, code, savedCode: code, opponentBuild: newBuildName }); this.setState({
buildName: newBuildName,
code,
savedCode: code,
opponentBuild: newBuildName
});
} }
} }
@@ -373,19 +497,38 @@ export default class OutfittingPage extends Page {
ship.buildWith(Ships[shipId].defaults); ship.buildWith(Ships[shipId].defaults);
// Reset controls // Reset controls
const code = ship.toString(); const code = ship.toString();
const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code); const {
// Update state, and refresh the ship
this.setState({
sys, sys,
eng, eng,
wep, wep,
mcSys,
mcEng,
mcWep,
boost, boost,
fuel, fuel,
cargo, cargo,
opponent, opponent,
opponentBuild, opponentBuild,
engagementRange engagementRange
}, () => this._updateRoute(shipId, buildName, code)); } = this._obtainControlFromCode(ship, code);
// Update state, and refresh the ship
this.setState(
{
sys,
eng,
wep,
mcSys,
mcEng,
mcWep,
boost,
fuel,
cargo,
opponent,
opponentBuild,
engagementRange
},
() => this._updateRoute(shipId, buildName, code)
);
} }
/** /**
@@ -396,7 +539,10 @@ export default class OutfittingPage extends Page {
Persist.deleteBuild(shipId, buildName); Persist.deleteBuild(shipId, buildName);
let opponentBuild; let opponentBuild;
if (shipId === this.state.opponent.id && buildName === this.state.opponentBuild) { if (
shipId === this.state.opponent.id &&
buildName === this.state.opponentBuild
) {
// Our current opponent has been deleted; revert to stock // Our current opponent has been deleted; revert to stock
opponentBuild = null; opponentBuild = null;
} else { } else {
@@ -413,11 +559,13 @@ export default class OutfittingPage extends Page {
_exportBuild() { _exportBuild() {
let translate = this.context.language.translate; let translate = this.context.language.translate;
let { buildName, ship } = this.state; let { buildName, ship } = this.state;
this.context.showModal(<ModalExport this.context.showModal(
title={(buildName || ship.name) + ' ' + translate('export')} <ModalExport
description={translate('PHRASE_EXPORT_DESC')} title={(buildName || ship.name) + ' ' + translate('export')}
data={toDetailedBuild(buildName, ship, ship.toString())} description={translate('PHRASE_EXPORT_DESC')}
/>); data={toDetailedBuild(buildName, ship, ship.toString())}
/>
);
} }
/** /**
@@ -430,19 +578,38 @@ export default class OutfittingPage extends Page {
this.state.ship.buildFrom(code); this.state.ship.buildFrom(code);
// Obtain controls from the code // Obtain controls from the code
const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code); const {
// Update state, and refresh the route when complete
this.setState({
sys, sys,
eng, eng,
wep, wep,
mcSys,
mcEng,
mcWep,
boost, boost,
fuel, fuel,
cargo, cargo,
opponent, opponent,
opponentBuild, opponentBuild,
engagementRange engagementRange
}, () => this._updateRoute(shipId, buildName, code)); } = this._obtainControlFromCode(ship, code);
// Update state, and refresh the route when complete
this.setState(
{
sys,
eng,
wep,
mcSys,
mcEng,
mcWep,
boost,
fuel,
cargo,
opponent,
opponentBuild,
engagementRange
},
() => this._updateRoute(shipId, buildName, code)
);
} }
/** /**
@@ -458,8 +625,14 @@ export default class OutfittingPage extends Page {
} }
const code = this._fullCode(ship, fuel, cargo); const code = this._fullCode(ship, fuel, cargo);
// Only update the state if this really has been updated // Only update the state if this really has been updated
if (this.state.code != code || this.state.cargo != cargo || this.state.fuel != fuel) { if (
this.setState({ code, cargo, fuel }, () => this._updateRoute(shipId, buildName, code)); this.state.code != code ||
this.state.cargo != cargo ||
this.state.fuel != fuel
) {
this.setState({ code, cargo, fuel }, () =>
this._updateRoute(shipId, buildName, code)
);
} }
} }
@@ -479,7 +652,8 @@ export default class OutfittingPage extends Page {
* @param {Object} nextContext Incoming/Next conext * @param {Object} nextContext Incoming/Next conext
*/ */
componentWillReceiveProps(nextProps, nextContext) { componentWillReceiveProps(nextProps, nextContext) {
if (this.context.route !== nextContext.route) { // Only reinit state if the route has changed if (this.context.route !== nextContext.route) {
// Only reinit state if the route has changed
this.setState(this._initState(nextProps, nextContext)); this.setState(this._initState(nextProps, nextContext));
} }
} }
@@ -488,6 +662,7 @@ export default class OutfittingPage extends Page {
* Add listeners when about to mount * Add listeners when about to mount
*/ */
componentWillMount() { componentWillMount() {
this.getBuild()
document.addEventListener('keydown', this._keyDown); document.addEventListener('keydown', this._keyDown);
} }
@@ -502,7 +677,7 @@ export default class OutfittingPage extends Page {
* Generates the short URL * Generates the short URL
*/ */
_genShortlink() { _genShortlink() {
this.context.showModal(<ModalPermalink url={window.location.href}/>); this.context.showModal(<ModalPermalink url={window.location.href} />);
} }
/** /**
@@ -519,7 +694,7 @@ export default class OutfittingPage extends Page {
data.ShipName = ship.id; data.ShipName = ship.id;
data.Ship = ship.id; data.Ship = ship.id;
console.log(data); console.log(data);
this.context.showModal(<ModalOrbis ship={data}/>); this.context.showModal(<ModalOrbis ship={data} />);
} }
/** /**
@@ -530,17 +705,23 @@ export default class OutfittingPage extends Page {
const shipId = Ships[ship.id].eddbID; const shipId = Ships[ship.id].eddbID;
// Provide unique list of non-PP module EDDB IDs // Provide unique list of non-PP module EDDB IDs
const modIds = ship.internal.concat(ship.bulkheads, ship.standard, ship.hardpoints).filter(slot => slot !== null && slot.m !== null && !slot.m.pp).map(slot => slot.m.eddbID).filter((v, i, a) => a.indexOf(v) === i); const modIds = ship.internal
.concat(ship.bulkheads, ship.standard, ship.hardpoints)
.filter(slot => slot !== null && slot.m !== null && !slot.m.pp)
.map(slot => slot.m.eddbID)
.filter((v, i, a) => a.indexOf(v) === i);
// Open up the relevant URL // Open up the relevant URL
window.open('https://eddb.io/station?s=' + shipId + '&m=' + modIds.join(',')); window.open(
'https://eddb.io/station?s=' + shipId + '&m=' + modIds.join(',')
);
} }
/** /**
* Generates the shopping list * Generates the shopping list
*/ */
_genShoppingList() { _genShoppingList() {
this.context.showModal(<ModalShoppingList ship={this.state.ship}/>); this.context.showModal(<ModalShoppingList ship={this.state.ship} />);
} }
/** /**
@@ -550,8 +731,9 @@ export default class OutfittingPage extends Page {
_keyDown(e) { _keyDown(e) {
// .keyCode will eventually be replaced with .key // .keyCode will eventually be replaced with .key
switch (e.keyCode) { switch (e.keyCode) {
case 69: // 'e' case 69: // 'e'
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + e if (e.ctrlKey || e.metaKey) {
// CTRL/CMD + e
e.preventDefault(); e.preventDefault();
this._exportBuild(); this._exportBuild();
} }
@@ -567,7 +749,28 @@ export default class OutfittingPage extends Page {
let state = this.state, let state = this.state,
{ language, termtip, tooltip, sizeRatio, onWindowResize } = this.context, { language, termtip, tooltip, sizeRatio, onWindowResize } = this.context,
{ translate, units, formats } = language, { translate, units, formats } = language,
{ ship, code, savedCode, buildName, newBuildName, sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange } = state, {
ship,
code,
savedCode,
buildName,
newBuildName,
sys,
eng,
wep,
mcSys,
mcEng,
mcWep,
boost,
fuel,
cargo,
opponent,
opponentBuild,
opponentSys,
opponentEng,
opponentWep,
engagementRange
} = state,
hide = tooltip.bind(null, null), hide = tooltip.bind(null, null),
menu = this.props.currentMenu, menu = this.props.currentMenu,
shipUpdated = this._shipUpdated, shipUpdated = this._shipUpdated,
@@ -585,11 +788,15 @@ export default class OutfittingPage extends Page {
const _pStr = `${ship.getPowerEnabledString()}${ship.getPowerPrioritiesString()}`; const _pStr = `${ship.getPowerEnabledString()}${ship.getPowerPrioritiesString()}`;
const _mStr = ship.getModificationsString(); const _mStr = ship.getModificationsString();
const standardSlotMarker = `${ship.name}${_sStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`; const standardSlotMarker = `${ship.name}${_sStr}${_pStr}${_mStr}${
ship.ladenMass
}${cargo}${fuel}`;
const internalSlotMarker = `${ship.name}${_iStr}${_pStr}${_mStr}`; const internalSlotMarker = `${ship.name}${_iStr}${_pStr}${_mStr}`;
const hardpointsSlotMarker = `${ship.name}${_hStr}${_pStr}${_mStr}`; const hardpointsSlotMarker = `${ship.name}${_hStr}${_pStr}${_mStr}`;
const boostMarker = `${ship.canBoost(cargo, fuel)}`; const boostMarker = `${ship.canBoost(cargo, fuel)}`;
const shipSummaryMarker = `${ship.name}${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`; const shipSummaryMarker = `${
ship.name
}${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;
const requirements = Ships[ship.id].requirements; const requirements = Ships[ship.id].requirements;
let requirementElements = []; let requirementElements = [];
@@ -601,94 +808,275 @@ export default class OutfittingPage extends Page {
*/ */
function renderRequirement(className, textKey, tooltipTextKey) { function renderRequirement(className, textKey, tooltipTextKey) {
if (textKey.startsWith('empire') || textKey.startsWith('federation')) { if (textKey.startsWith('empire') || textKey.startsWith('federation')) {
requirementElements.push(<div key={textKey} className={className} onMouseEnter={termtip.bind(null, tooltipTextKey)} onMouseLeave={hide}><a href={textKey.startsWith('empire') ? 'http://elite-dangerous.wikia.com/wiki/Empire/Ranks' : 'http://elite-dangerous.wikia.com/wiki/Federation/Ranks'} target="_blank" rel="noopener">{translate(textKey)}</a></div>); requirementElements.push(
<div
key={textKey}
className={className}
onMouseEnter={termtip.bind(null, tooltipTextKey)}
onMouseLeave={hide}
>
<a
href={
textKey.startsWith('empire') ?
'http://elite-dangerous.wikia.com/wiki/Empire/Ranks' :
'http://elite-dangerous.wikia.com/wiki/Federation/Ranks'
}
target="_blank"
rel="noopener"
>
{translate(textKey)}
</a>
</div>
);
} else { } else {
requirementElements.push(<div key={textKey} className={className} onMouseEnter={termtip.bind(null, tooltipTextKey)} onMouseLeave={hide}>{translate(textKey)}</div>); requirementElements.push(
<div
key={textKey}
className={className}
onMouseEnter={termtip.bind(null, tooltipTextKey)}
onMouseLeave={hide}
>
{translate(textKey)}
</div>
);
} }
} }
if (requirements) { if (requirements) {
requirements.federationRank && renderRequirement('federation', 'federation rank ' + requirements.federationRank, 'federation rank required'); requirements.federationRank &&
requirements.empireRank && renderRequirement('empire', 'empire rank ' + requirements.empireRank, 'empire rank required'); renderRequirement(
requirements.horizons && renderRequirement('horizons', 'horizons', 'horizons required'); 'federation',
requirements.horizonsEarlyAdoption && renderRequirement('horizons', 'horizons early adoption', 'horizons early adoption required'); 'federation rank ' + requirements.federationRank,
'federation rank required'
);
requirements.empireRank &&
renderRequirement(
'empire',
'empire rank ' + requirements.empireRank,
'empire rank required'
);
requirements.horizons &&
renderRequirement('horizons', 'horizons', 'horizons required');
requirements.horizonsEarlyAdoption &&
renderRequirement(
'horizons',
'horizons early adoption',
'horizons early adoption required'
);
} }
return ( return (
<div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}> <div
<div id='overview'> id="outfit"
className={'page'}
style={{ fontSize: sizeRatio * 0.9 + 'em' }}
>
<div id="overview">
<h1>{ship.name}</h1> <h1>{ship.name}</h1>
<div id='requirements'>{requirementElements}</div> <div id="requirements">{requirementElements}</div>
<div id='build'> <div id="build">
<input value={newBuildName || ''} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxLength={50} /> <input
<button onClick={canSave && this._saveBuild} disabled={!canSave} onMouseOver={termtip.bind(null, 'save')} onMouseOut={hide}> value={newBuildName || ''}
<FloppyDisk className='lg' /> onChange={this._buildNameChange}
placeholder={translate('Enter Name')}
maxLength={50}
/>
<button
onClick={canSave && this._saveBuild}
disabled={!canSave}
onMouseOver={termtip.bind(null, 'save')}
onMouseOut={hide}
>
<FloppyDisk className="lg" />
</button> </button>
<button onClick={canRename && this._renameBuild} disabled={!canRename} onMouseOver={termtip.bind(null, 'rename')} onMouseOut={hide}> <button
<span style={{ textTransform: 'none', fontSize: '1.8em' }}>a|</span> onClick={canRename && this._renameBuild}
disabled={!canRename}
onMouseOver={termtip.bind(null, 'rename')}
onMouseOut={hide}
>
<span style={{ textTransform: 'none', fontSize: '1.8em' }}>
a|
</span>
</button> </button>
<button onClick={canReload && this._reloadBuild} disabled={!canReload} onMouseOver={termtip.bind(null, 'reload')} onMouseOut={hide}> <button
<Reload className='lg'/> onClick={canReload && this._reloadBuild}
disabled={!canReload}
onMouseOver={termtip.bind(null, 'reload')}
onMouseOut={hide}
>
<Reload className="lg" />
</button> </button>
<button className={'danger'} onClick={savedCode && this._deleteBuild} disabled={!savedCode} onMouseOver={termtip.bind(null, 'delete')} onMouseOut={hide}> <button
<Bin className='lg'/> className={'danger'}
onClick={savedCode && this._deleteBuild}
disabled={!savedCode}
onMouseOver={termtip.bind(null, 'delete')}
onMouseOut={hide}
>
<Bin className="lg" />
</button> </button>
<button onClick={code && this._resetBuild} disabled={!code} onMouseOver={termtip.bind(null, 'reset')} onMouseOut={hide}> <button
<Switch className='lg'/> onClick={code && this._resetBuild}
disabled={!code}
onMouseOver={termtip.bind(null, 'reset')}
onMouseOut={hide}
>
<Switch className="lg" />
</button> </button>
<button onClick={buildName && this._exportBuild} disabled={!buildName} onMouseOver={termtip.bind(null, 'export')} onMouseOut={hide}> <button
<Download className='lg'/> onClick={buildName && this._exportBuild}
disabled={!buildName}
onMouseOver={termtip.bind(null, 'export')}
onMouseOut={hide}
>
<Download className="lg" />
</button> </button>
<button onClick={this._eddbShoppingList} onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_LIST')} onMouseOut={hide}> <button
<ShoppingIcon className='lg' /> onClick={this._eddbShoppingList}
onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_LIST')}
onMouseOut={hide}
>
<ShoppingIcon className="lg" />
</button> </button>
<button onClick={this._genShortlink} onMouseOver={termtip.bind(null, 'shortlink')} onMouseOut={hide}> <button
<LinkIcon className='lg' /> onClick={this._genShortlink}
onMouseOver={termtip.bind(null, 'shortlink')}
onMouseOut={hide}
>
<LinkIcon className="lg" />
</button> </button>
<button onClick={this._genOrbis} onMouseOver={termtip.bind(null, 'PHASE_UPLOAD_ORBIS')} onMouseOut={hide}> <button
<OrbisIcon className='lg' /> onClick={this._genOrbis}
onMouseOver={termtip.bind(null, 'PHASE_UPLOAD_ORBIS')}
onMouseOut={hide}
>
<OrbisIcon className="lg" />
</button> </button>
<button onClick={this._genShoppingList} onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_MATS')} onMouseOut={hide}> <button
<MatIcon className='lg' /> onClick={this._genShoppingList}
onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_MATS')}
onMouseOut={hide}
>
<MatIcon className="lg" />
</button> </button>
</div> </div>
</div> </div>
{/* Main tables */} {/* Main tables */}
<ShipSummaryTable ship={ship} fuel={fuel} cargo={cargo} marker={shipSummaryMarker} pips={{ sys: this.state.sys, wep: this.state.wep, eng: this.state.eng }} /> <ShipSummaryTable
<StandardSlotSection ship={ship} fuel={fuel} cargo={cargo} code={standardSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/> ship={ship}
<InternalSlotSection ship={ship} code={internalSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/> fuel={fuel}
<HardpointSlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/> cargo={cargo}
<UtilitySlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/> marker={shipSummaryMarker}
pips={{
sys: this.state.sys,
wep: this.state.wep,
eng: this.state.eng
}}
/>
<StandardSlotSection
ship={ship}
fuel={fuel}
cargo={cargo}
code={standardSlotMarker}
onChange={shipUpdated}
onCargoChange={this._cargoUpdated}
onFuelChange={this._fuelUpdated}
currentMenu={menu}
sectionMenuRefs={this._sectionMenuRefs}
/>
<InternalSlotSection
ship={ship}
code={internalSlotMarker}
onChange={shipUpdated}
onCargoChange={this._cargoUpdated}
onFuelChange={this._fuelUpdated}
currentMenu={menu}
sectionMenuRefs={this._sectionMenuRefs}
/>
<HardpointSlotSection
ship={ship}
code={hardpointsSlotMarker}
onChange={shipUpdated}
onCargoChange={this._cargoUpdated}
onFuelChange={this._fuelUpdated}
currentMenu={menu}
sectionMenuRefs={this._sectionMenuRefs}
/>
<UtilitySlotSection
ship={ship}
code={hardpointsSlotMarker}
onChange={shipUpdated}
onCargoChange={this._cargoUpdated}
onFuelChange={this._fuelUpdated}
currentMenu={menu}
sectionMenuRefs={this._sectionMenuRefs}
/>
{/* Control of ship and opponent */} {/* Control of ship and opponent */}
<div className='group quarter'> <div className="group quarter">
<div className='group half'> <div className="group half">
<h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>{translate('ship control')}</h2> <h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>
{translate('ship control')}
</h2>
</div> </div>
<div className='group half'> <div className="group half">
<Boost marker={boostMarker} ship={ship} boost={boost} onChange={this._boostUpdated} /> <Boost
marker={boostMarker}
ship={ship}
boost={boost}
onChange={this._boostUpdated}
/>
</div> </div>
</div> </div>
<div className='group quarter'> <div className="group quarter">
<Pips sys={sys} eng={eng} wep={wep} onChange={this._pipsUpdated} /> <Pips
sys={sys}
eng={eng}
wep={wep}
mcSys={mcSys}
mcEng={mcEng}
mcWep={mcWep}
onChange={this._pipsUpdated}
/>
</div> </div>
<div className='group quarter'> <div className="group quarter">
<Fuel fuelCapacity={ship.fuelCapacity} fuel={fuel} onChange={this._fuelUpdated}/> <Fuel
fuelCapacity={ship.fuelCapacity}
fuel={fuel}
onChange={this._fuelUpdated}
/>
</div> </div>
<div className='group quarter'> <div className="group quarter">
{ ship.cargoCapacity > 0 ? <Cargo cargoCapacity={ship.cargoCapacity} cargo={cargo} onChange={this._cargoUpdated}/> : null } {ship.cargoCapacity > 0 ? (
<Cargo
cargoCapacity={ship.cargoCapacity}
cargo={cargo}
onChange={this._cargoUpdated}
/>
) : null}
</div> </div>
<div className='group half'> <div className="group half">
<div className='group quarter'> <div className="group quarter">
<h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>{translate('opponent')}</h2> <h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>
{translate('opponent')}
</h2>
</div> </div>
<div className='group threequarters'> <div className="group threequarters">
<ShipPicker ship={opponent.id} build={opponentBuild} onChange={this._opponentUpdated}/> <ShipPicker
ship={opponent.id}
build={opponentBuild}
onChange={this._opponentUpdated}
/>
</div> </div>
</div> </div>
<div className='group half'> <div className="group half">
<EngagementRange ship={ship} engagementRange={engagementRange} onChange={this._engagementRangeUpdated}/> <EngagementRange
ship={ship}
engagementRange={engagementRange}
onChange={this._engagementRangeUpdated}
/>
</div> </div>
{/* Tabbed subpages */} {/* Tabbed subpages */}

View File

@@ -22,9 +22,11 @@ function countHp(slot) {
*/ */
function countInt(slot) { function countInt(slot) {
let crEligible = !slot.eligible || slot.eligible.cr; let crEligible = !slot.eligible || slot.eligible.cr;
this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment
this.intCount++; this.intCount++;
this.maxCargo += crEligible ? ModuleUtils.findInternal('cr', slot.maxClass, 'E').cargo : 0; this.maxCargo += crEligible ?
ModuleUtils.findInternal('cr', slot.maxClass, 'E').cargo :
0;
// if no eligiblity, then assume pce // if no eligiblity, then assume pce
let passSlotType = null; let passSlotType = null;
@@ -42,7 +44,9 @@ function countInt(slot) {
passSlotType = 'pcq'; passSlotType = 'pcq';
passSlotRating = 'B'; passSlotRating = 'B';
} }
let passengerBay = passSlotType ? ModuleUtils.findMaxInternal(passSlotType, slot.maxClass, passSlotRating) : null; let passengerBay = passSlotType ?
ModuleUtils.findMaxInternal(passSlotType, slot.maxClass, passSlotRating) :
null;
this.maxPassengers += passengerBay ? passengerBay.passengers : 0; this.maxPassengers += passengerBay ? passengerBay.passengers : 0;
} }
@@ -62,18 +66,21 @@ function shipSummary(shipId, shipData) {
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8 int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8
standard: shipData.slots.standard, standard: shipData.slots.standard,
agility: shipData.properties.pitch + shipData.properties.yaw + shipData.properties.roll agility:
shipData.properties.pitch +
shipData.properties.yaw +
shipData.properties.roll
}; };
Object.assign(summary, shipData.properties); Object.assign(summary, shipData.properties);
let ship = new Ship(shipId, shipData.properties, shipData.slots); let ship = new Ship(shipId, shipData.properties, shipData.slots);
// Build Ship // Build Ship
ship.buildWith(shipData.defaults); // Populate with stock/default components ship.buildWith(shipData.defaults); // Populate with stock/default components
ship.hardpoints.forEach(countHp.bind(summary)); // Count Hardpoints by class ship.hardpoints.forEach(countHp.bind(summary)); // Count Hardpoints by class
ship.internal.forEach(countInt.bind(summary)); // Count Internal Compartments by class ship.internal.forEach(countInt.bind(summary)); // Count Internal Compartments by class
summary.retailCost = ship.totalCost; // Record Stock/Default/retail cost summary.retailCost = ship.totalCost; // Record Stock/Default/retail cost
ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range
summary.maxJumpRange = ship.unladenRange; // Record Jump Range summary.maxJumpRange = ship.unladenRange; // Record Jump Range
// Best thrusters // Best thrusters
let th; let th;
@@ -97,7 +104,6 @@ function shipSummary(shipId, shipData) {
* The Shipyard summary page * The Shipyard summary page
*/ */
export default class ShipyardPage extends Page { export default class ShipyardPage extends Page {
static cachedShipSummaries = null; static cachedShipSummaries = null;
/** /**
@@ -145,12 +151,15 @@ export default class ShipyardPage extends Page {
shipPredicateIndex = undefined; shipPredicateIndex = undefined;
} }
if (this.state.shipPredicate == shipPredicate && this.state.shipPredicateIndex == shipPredicateIndex) { if (
this.state.shipPredicate == shipPredicate &&
this.state.shipPredicateIndex == shipPredicateIndex
) {
shipDesc = !shipDesc; shipDesc = !shipDesc;
} }
this.setState({ shipPredicate, shipDesc, shipPredicateIndex }); this.setState({ shipPredicate, shipDesc, shipPredicateIndex });
}; }
/** /**
* Generate the table row summary for the ship * Generate the table row summary for the ship
@@ -165,50 +174,55 @@ export default class ShipyardPage extends Page {
_shipRowElement(s, translate, u, fInt, fRound, highlight) { _shipRowElement(s, translate, u, fInt, fRound, highlight) {
let noTouch = this.context.noTouch; let noTouch = this.context.noTouch;
return <tr return (
<tr
key={s.id} key={s.id}
style={{ height: '1.5em' }} style={{ height: '1.5em' }}
className={cn({ highlighted: noTouch && this.state.shipId === s.id, alt: highlight })} className={cn({
highlighted: noTouch && this.state.shipId === s.id,
alt: highlight
})}
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)} onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
> >
<td className='ri'>{s.manufacturer}</td> <td className="ri">{s.manufacturer}</td>
<td className='ri'>{fInt(s.retailCost)}</td> <td className="ri">{fInt(s.retailCost)}</td>
<td className='ri cap'>{translate(SizeMap[s.class])}</td> <td className="ri cap">{translate(SizeMap[s.class])}</td>
<td className='ri'>{fInt(s.crew)}</td> <td className="ri">{fInt(s.crew)}</td>
<td className='ri'>{s.masslock}</td> <td className="ri">{s.masslock}</td>
<td className='ri'>{fInt(s.agility)}</td> <td className="ri">{fInt(s.agility)}</td>
<td className='ri'>{fInt(s.hardness)}</td> <td className="ri">{fInt(s.hardness)}</td>
<td className='ri'>{fInt(s.hullMass)}</td> <td className="ri">{fInt(s.hullMass)}</td>
<td className='ri'>{fInt(s.speed)}</td> <td className="ri">{fInt(s.speed)}</td>
<td className='ri'>{fInt(s.boost)}</td> <td className="ri">{fInt(s.boost)}</td>
<td className='ri'>{fInt(s.baseArmour)}</td> <td className="ri">{fInt(s.baseArmour)}</td>
<td className='ri'>{fInt(s.baseShieldStrength)}</td> <td className="ri">{fInt(s.baseShieldStrength)}</td>
<td className='ri'>{fInt(s.topSpeed)}</td> <td className="ri">{fInt(s.topSpeed)}</td>
<td className='ri'>{fInt(s.topBoost)}</td> <td className="ri">{fInt(s.topBoost)}</td>
<td className='ri'>{fRound(s.maxJumpRange)}</td> <td className="ri">{fRound(s.maxJumpRange)}</td>
<td className='ri'>{fInt(s.maxCargo)}</td> <td className="ri">{fInt(s.maxCargo)}</td>
<td className='ri'>{fInt(s.maxPassengers)}</td> <td className="ri">{fInt(s.maxPassengers)}</td>
<td className='cn'>{s.standard[0]}</td> <td className="cn">{s.standard[0]}</td>
<td className='cn'>{s.standard[1]}</td> <td className="cn">{s.standard[1]}</td>
<td className='cn'>{s.standard[2]}</td> <td className="cn">{s.standard[2]}</td>
<td className='cn'>{s.standard[3]}</td> <td className="cn">{s.standard[3]}</td>
<td className='cn'>{s.standard[4]}</td> <td className="cn">{s.standard[4]}</td>
<td className='cn'>{s.standard[5]}</td> <td className="cn">{s.standard[5]}</td>
<td className='cn'>{s.standard[6]}</td> <td className="cn">{s.standard[6]}</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>
<td className={cn({ disabled: !s.hp[4] })}>{s.hp[4]}</td> <td className={cn({ disabled: !s.hp[4] })}>{s.hp[4]}</td>
<td className={cn({ disabled: !s.hp[0] })}>{s.hp[0]}</td> <td className={cn({ disabled: !s.hp[0] })}>{s.hp[0]}</td>
<td className={cn({ disabled: !s.int[0] })}>{s.int[0]}</td> <td className={cn({ disabled: !s.int[0] })}>{s.int[0]}</td>
<td className={cn({ disabled: !s.int[1] })}>{s.int[1]}</td> <td className={cn({ disabled: !s.int[1] })}>{s.int[1]}</td>
<td className={cn({ disabled: !s.int[2] })}>{s.int[2]}</td> <td className={cn({ disabled: !s.int[2] })}>{s.int[2]}</td>
<td className={cn({ disabled: !s.int[3] })}>{s.int[3]}</td> <td className={cn({ disabled: !s.int[3] })}>{s.int[3]}</td>
<td className={cn({ disabled: !s.int[4] })}>{s.int[4]}</td> <td className={cn({ disabled: !s.int[4] })}>{s.int[4]}</td>
<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>
</tr>; </tr>
);
} }
/** /**
@@ -222,7 +236,8 @@ export default class ShipyardPage extends Page {
let fInt = formats.int; let fInt = formats.int;
let fRound = formats.round; let fRound = formats.round;
let { shipSummaries, shipPredicate, shipPredicateIndex } = this.state; let { shipSummaries, shipPredicate, shipPredicateIndex } = this.state;
let sortShips = (predicate, index) => this._sortShips.bind(this, predicate, index); let sortShips = (predicate, index) =>
this._sortShips.bind(this, predicate, index);
let filters = { let filters = {
// 'class': { 1: 1, 2: 1} // 'class': { 1: 1, 2: 1}
@@ -239,7 +254,8 @@ export default class ShipyardPage extends Page {
// Sort shipsOverview // Sort shipsOverview
shipSummaries.sort((a, b) => { shipSummaries.sort((a, b) => {
let valA = a[shipPredicate], valB = b[shipPredicate]; let valA = a[shipPredicate],
valB = b[shipPredicate];
if (shipPredicateIndex != undefined) { if (shipPredicateIndex != undefined) {
valA = valA[shipPredicateIndex]; valA = valA[shipPredicateIndex];
@@ -252,7 +268,7 @@ export default class ShipyardPage extends Page {
valB = val; valB = val;
} }
if(valA == valB) { if (valA == valB) {
if (a.name > b.name) { if (a.name > b.name) {
return 1; return 1;
} else { } else {
@@ -274,42 +290,65 @@ export default class ShipyardPage extends Page {
for (let s of shipSummaries) { for (let s of shipSummaries) {
let shipSortValue = s[shipPredicate]; let shipSortValue = s[shipPredicate];
if(shipPredicateIndex != undefined) { if (shipPredicateIndex != undefined) {
shipSortValue = shipSortValue[shipPredicateIndex]; shipSortValue = shipSortValue[shipPredicateIndex];
} }
if(shipSortValue != lastShipSortValue) { if (shipSortValue != lastShipSortValue) {
backgroundHighlight = !backgroundHighlight; backgroundHighlight = !backgroundHighlight;
lastShipSortValue = shipSortValue; lastShipSortValue = shipSortValue;
} }
detailRows[i] = this._shipRowElement(s, translate, units, fInt, formats.f1, backgroundHighlight); detailRows[i] = this._shipRowElement(
s,
translate,
units,
fInt,
formats.f1,
backgroundHighlight
);
shipRows[i] = ( shipRows[i] = (
<tr <tr
key={i} key={i}
style={{ height: '1.5em' }} style={{ height: '1.5em' }}
className={cn({ highlighted: noTouch && this.state.shipId === s.id, alt: backgroundHighlight })} className={cn({
highlighted: noTouch && this.state.shipId === s.id,
alt: backgroundHighlight
})}
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)} onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
> >
<td className='le'><Link href={'/outfit/' + s.id}>{s.name}</Link></td> <td className="le">
<Link href={'/outfit/' + s.id}>{s.name}</Link>
</td>
</tr> </tr>
); );
i++; i++;
} }
return ( return (
<div className='page' style={{ fontSize: sizeRatio + 'em' }}> <div className="page" style={{ fontSize: sizeRatio + 'em' }}>
<div style={{ whiteSpace: 'nowrap', margin: '0 auto', fontSize: '0.8em', position: 'relative', display: 'inline-block', maxWidth: '100%' }}> <div
style={{
whiteSpace: 'nowrap',
margin: '0 auto',
fontSize: '0.8em',
position: 'relative',
display: 'inline-block',
maxWidth: '100%'
}}
>
<table style={{ width: '12em', position: 'absolute', zIndex: 1 }}> <table style={{ width: '12em', position: 'absolute', zIndex: 1 }}>
<thead> <thead>
<tr> <tr>
<th className='le rgt'>&nbsp;</th> <th className="le rgt">&nbsp;</th>
</tr> </tr>
<tr className='main'> <tr className="main">
<th className='sortable le rgt' onClick={sortShips('name')}>{translate('ship')}</th> <th className="sortable le rgt" onClick={sortShips('name')}>
{translate('ship')}
</th>
</tr> </tr>
<tr> <tr>
<th className='le rgt invisible'>{units['m/s']}</th> <th className="le rgt invisible">{units['m/s']}</th>
</tr> </tr>
</thead> </thead>
<tbody onMouseLeave={this._highlightShip.bind(this, null)}> <tbody onMouseLeave={this._highlightShip.bind(this, null)}>
@@ -317,80 +356,261 @@ export default class ShipyardPage extends Page {
</tbody> </tbody>
</table> </table>
<div style={{ overflowX: 'scroll', maxWidth: '100%' }}> <div style={{ overflowX: 'scroll', maxWidth: '100%' }}>
<table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }}> <table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }}>
<thead> <thead>
<tr className='main'> <tr className="main">
<th rowSpan={3} className='sortable' onClick={sortShips('manufacturer')}>{translate('manufacturer')}</th> <th
<th>&nbsp;</th> rowSpan={3}
<th rowSpan={3} className='sortable' onClick={sortShips('class')}>{translate('size')}</th> className="sortable"
<th rowSpan={3} className='sortable' onClick={sortShips('crew')}>{translate('crew')}</th> onClick={sortShips('manufacturer')}
<th rowSpan={3} className='sortable' onMouseEnter={termtip.bind(null, 'mass lock factor')} onMouseLeave={hide} onClick={sortShips('masslock')} >{translate('MLF')}</th> >
<th rowSpan={3} className='sortable' onClick={sortShips('agility')}>{translate('agility')}</th> {translate('manufacturer')}
<th rowSpan={3} className='sortable' onMouseEnter={termtip.bind(null, 'hardness')} onMouseLeave={hide} onClick={sortShips('hardness')}>{translate('hrd')}</th> </th>
<th>&nbsp;</th> <th>&nbsp;</th>
<th colSpan={4}>{translate('base')}</th> <th
<th colSpan={5}>{translate('max')}</th> rowSpan={3}
<th className='lft' colSpan={7}></th> className="sortable"
<th className='lft' colSpan={5}></th> onClick={sortShips('class')}
<th className='lft' colSpan={8}></th> >
</tr> {translate('size')}
<tr> </th>
<th className='sortable lft' onClick={sortShips('retailCost')}>{translate('cost')}</th> <th
<th className='sortable lft' onClick={sortShips('hullMass')}>{translate('hull')}</th> rowSpan={3}
<th className='sortable lft' onClick={sortShips('speed')}>{translate('speed')}</th> className="sortable"
<th className='sortable' onClick={sortShips('boost')}>{translate('boost')}</th> onClick={sortShips('crew')}
<th className='sortable' onClick={sortShips('baseArmour')}>{translate('armour')}</th> >
<th className='sortable' onClick={sortShips('baseShieldStrength')}>{translate('shields')}</th> {translate('crew')}
</th>
<th
rowSpan={3}
className="sortable"
onMouseEnter={termtip.bind(null, 'mass lock factor')}
onMouseLeave={hide}
onClick={sortShips('masslock')}
>
{translate('MLF')}
</th>
<th
rowSpan={3}
className="sortable"
onClick={sortShips('agility')}
>
{translate('agility')}
</th>
<th
rowSpan={3}
className="sortable"
onMouseEnter={termtip.bind(null, 'hardness')}
onMouseLeave={hide}
onClick={sortShips('hardness')}
>
{translate('hrd')}
</th>
<th>&nbsp;</th>
<th colSpan={4}>{translate('base')}</th>
<th colSpan={5}>{translate('max')}</th>
<th className="lft" colSpan={7} />
<th className="lft" colSpan={5} />
<th className="lft" colSpan={8} />
</tr>
<tr>
<th
className="sortable lft"
onClick={sortShips('retailCost')}
>
{translate('cost')}
</th>
<th className="sortable lft" onClick={sortShips('hullMass')}>
{translate('hull')}
</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('baseArmour')}>
{translate('armour')}
</th>
<th
className="sortable"
onClick={sortShips('baseShieldStrength')}
>
{translate('shields')}
</th>
<th className='sortable lft' onClick={sortShips('topSpeed')}>{translate('speed')}</th> <th className="sortable lft" onClick={sortShips('topSpeed')}>
<th className='sortable' onClick={sortShips('topBoost')}>{translate('boost')}</th> {translate('speed')}
<th className='sortable' onClick={sortShips('maxJumpRange')}>{translate('jump')}</th> </th>
<th className='sortable' onClick={sortShips('maxCargo')}>{translate('cargo')}</th> <th className="sortable" onClick={sortShips('topBoost')}>
<th className='sortable' onClick={sortShips('maxPassengers')}>{translate('pax')}</th> {translate('boost')}
</th>
<th className="sortable" onClick={sortShips('maxJumpRange')}>
{translate('jump')}
</th>
<th className="sortable" onClick={sortShips('maxCargo')}>
{translate('cargo')}
</th>
<th className="sortable" onClick={sortShips('maxPassengers')}>
{translate('pax')}
</th>
<th className='lft' colSpan={7}>{translate('core module classes')}</th> <th className="lft" colSpan={7}>
<th colSpan={5} className='sortable lft' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th> {translate('core module classes')}
<th colSpan={8} className='sortable lft' onClick={sortShips('intCount')}>{translate('internal compartments')}</th> </th>
</tr> <th
<tr> colSpan={5}
<th className='sortable lft' onClick={sortShips('retailCost')}>{units.CR}</th> className="sortable lft"
<th className='sortable lft' onClick={sortShips('hullMass')}>{units.T}</th> onClick={sortShips('hpCount')}
<th className='sortable lft' onClick={sortShips('speed')}>{units['m/s']}</th> >
<th className='sortable' onClick={sortShips('boost')}>{units['m/s']}</th> {translate('hardpoints')}
<th>&nbsp;</th> </th>
<th className='sortable' onClick={sortShips('baseShieldStrength')}>{units.MJ}</th> <th
<th className='sortable lft' onClick={sortShips('topSpeed')}>{units['m/s']}</th> colSpan={8}
<th className='sortable' onClick={sortShips('topBoost')}>{units['m/s']}</th> className="sortable lft"
<th className='sortable' onClick={sortShips('maxJumpRange')}>{units.LY}</th> onClick={sortShips('intCount')}
<th className='sortable' onClick={sortShips('maxCargo')}>{units.T}</th> >
<th>&nbsp;</th> {translate('internal compartments')}
<th className='sortable lft' onMouseEnter={termtip.bind(null, 'power plant')} onMouseLeave={hide} onClick={sortShips('standard', 0)}>{'pp'}</th> </th>
<th className='sortable' onMouseEnter={termtip.bind(null, 'thrusters')} onMouseLeave={hide} onClick={sortShips('standard', 1)}>{'th'}</th> </tr>
<th className='sortable' onMouseEnter={termtip.bind(null, 'frame shift drive')} onMouseLeave={hide} onClick={sortShips('standard', 2)}>{'fsd'}</th> <tr>
<th className='sortable' onMouseEnter={termtip.bind(null, 'life support')} onMouseLeave={hide} onClick={sortShips('standard', 3)}>{'ls'}</th> <th
<th className='sortable' onMouseEnter={termtip.bind(null, 'power distriubtor')} onMouseLeave={hide} onClick={sortShips('standard', 4)}>{'pd'}</th> className="sortable lft"
<th className='sortable' onMouseEnter={termtip.bind(null, 'sensors')} onMouseLeave={hide} onClick={sortShips('standard', 5)}>{'s'}</th> onClick={sortShips('retailCost')}
<th className='sortable' onMouseEnter={termtip.bind(null, 'fuel tank')} onMouseLeave={hide} onClick={sortShips('standard', 6)}>{'ft'}</th> >
<th className='sortable lft' onClick={sortShips('hp',1)}>{translate('S')}</th> {units.CR}
<th className='sortable' onClick={sortShips('hp', 2)}>{translate('M')}</th> </th>
<th className='sortable' onClick={sortShips('hp', 3)}>{translate('L')}</th> <th className="sortable lft" onClick={sortShips('hullMass')}>
<th className='sortable' onClick={sortShips('hp', 4)}>{translate('H')}</th> {units.T}
<th className='sortable' onClick={sortShips('hp', 0)}>{translate('U')}</th> </th>
<th className="sortable lft" onClick={sortShips('speed')}>
{units['m/s']}
</th>
<th className="sortable" onClick={sortShips('boost')}>
{units['m/s']}
</th>
<th>&nbsp;</th>
<th
className="sortable"
onClick={sortShips('baseShieldStrength')}
>
{units.MJ}
</th>
<th className="sortable lft" onClick={sortShips('topSpeed')}>
{units['m/s']}
</th>
<th className="sortable" onClick={sortShips('topBoost')}>
{units['m/s']}
</th>
<th className="sortable" onClick={sortShips('maxJumpRange')}>
{units.LY}
</th>
<th className="sortable" onClick={sortShips('maxCargo')}>
{units.T}
</th>
<th>&nbsp;</th>
<th
className="sortable lft"
onMouseEnter={termtip.bind(null, 'power plant')}
onMouseLeave={hide}
onClick={sortShips('standard', 0)}
>
{'pp'}
</th>
<th
className="sortable"
onMouseEnter={termtip.bind(null, 'thrusters')}
onMouseLeave={hide}
onClick={sortShips('standard', 1)}
>
{'th'}
</th>
<th
className="sortable"
onMouseEnter={termtip.bind(null, 'frame shift drive')}
onMouseLeave={hide}
onClick={sortShips('standard', 2)}
>
{'fsd'}
</th>
<th
className="sortable"
onMouseEnter={termtip.bind(null, 'life support')}
onMouseLeave={hide}
onClick={sortShips('standard', 3)}
>
{'ls'}
</th>
<th
className="sortable"
onMouseEnter={termtip.bind(null, 'power distriubtor')}
onMouseLeave={hide}
onClick={sortShips('standard', 4)}
>
{'pd'}
</th>
<th
className="sortable"
onMouseEnter={termtip.bind(null, 'sensors')}
onMouseLeave={hide}
onClick={sortShips('standard', 5)}
>
{'s'}
</th>
<th
className="sortable"
onMouseEnter={termtip.bind(null, 'fuel tank')}
onMouseLeave={hide}
onClick={sortShips('standard', 6)}
>
{'ft'}
</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', 3)}>
{translate('L')}
</th>
<th className="sortable" onClick={sortShips('hp', 4)}>
{translate('H')}
</th>
<th className="sortable" onClick={sortShips('hp', 0)}>
{translate('U')}
</th>
<th className='sortable lft' onClick={sortShips('int', 0)} >1</th> <th className="sortable lft" onClick={sortShips('int', 0)}>
<th className='sortable' onClick={sortShips('int', 1)} >2</th> 1
<th className='sortable' onClick={sortShips('int', 2)} >3</th> </th>
<th className='sortable' onClick={sortShips('int', 3)} >4</th> <th className="sortable" onClick={sortShips('int', 1)}>
<th className='sortable' onClick={sortShips('int', 4)} >5</th> 2
<th className='sortable' onClick={sortShips('int', 5)} >6</th> </th>
<th className='sortable' onClick={sortShips('int', 6)} >7</th> <th className="sortable" onClick={sortShips('int', 2)}>
<th className='sortable' onClick={sortShips('int', 7)} >8</th> 3
</tr> </th>
</thead> <th className="sortable" onClick={sortShips('int', 3)}>
<tbody onMouseLeave={this._highlightShip.bind(this, null)}> 4
{detailRows} </th>
</tbody> <th className="sortable" onClick={sortShips('int', 4)}>
</table> 5
</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', 7)}>
8
</th>
</tr>
</thead>
<tbody onMouseLeave={this._highlightShip.bind(this, null)}>
{detailRows}
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -2,7 +2,6 @@
* Modification - a modification and its value * Modification - a modification and its value
*/ */
export default class Modification { export default class Modification {
/** /**
* @param {String} id Unique modification ID * @param {String} id Unique modification ID
* @param {Number} value Value of the modification * @param {Number} value Value of the modification
@@ -11,5 +10,4 @@ export default class Modification {
this.id = id; this.id = id;
this.value = value; this.value = value;
} }
} }

View File

@@ -7,7 +7,6 @@ import { STATS_FORMATTING, SI_PREFIXES } from './StatsFormatting';
* Module - active module in a ship's buildout * Module - active module in a ship's buildout
*/ */
export default class Module { export default class Module {
/** /**
* Construct a new module * Construct a new module
* @param {Object} params Module parameters. Either grp/id or template * @param {Object} params Module parameters. Either grp/id or template
@@ -81,8 +80,8 @@ export default class Module {
// the amount of base resistance the hrp has. // the amount of base resistance the hrp has.
if (!isNaN(result) && this.grp === 'hr' && if (!isNaN(result) && this.grp === 'hr' &&
(name === 'kinres' || name === 'thermres' || name === 'explres')) { (name === 'kinres' || name === 'thermres' || name === 'explres')) {
let baseRes = this[name]; let baseRes = this[name];
result = result * (1 - baseRes); result = result * (1 - baseRes);
} }
// Sanitise the resultant value to 4dp equivalent // Sanitise the resultant value to 4dp equivalent
@@ -180,7 +179,7 @@ export default class Module {
modValue = value - baseValue; modValue = value - baseValue;
if (this.grp === 'hr' && if (this.grp === 'hr' &&
(name === 'kinres' || name === 'thermres' || name === 'explres')) { (name === 'kinres' || name === 'thermres' || name === 'explres')) {
modValue = modValue / (1 - baseValue); modValue = modValue / (1 - baseValue);
} }
} else if (name === 'shieldboost' || name === 'hullboost') { } else if (name === 'shieldboost' || name === 'hullboost') {
modValue = (1 + value) / (1 + baseValue) - 1; modValue = (1 + value) / (1 + baseValue) - 1;
@@ -192,7 +191,7 @@ export default class Module {
modValue = modValue * 10000; modValue = modValue * 10000;
} else if (modification.type === 'numeric' && name !== 'burst' && } else if (modification.type === 'numeric' && name !== 'burst' &&
name !== 'burstrof') { name !== 'burstrof') {
modValue = modValue * 100; modValue = modValue * 100;
} }
this.setModValue(name, modValue, valueIsWithSpecial); this.setModValue(name, modValue, valueIsWithSpecial);
@@ -242,38 +241,38 @@ export default class Module {
const modification = Modifications.modifications[name]; const modification = Modifications.modifications[name];
let result = this[name]; let result = this[name];
if (modification) { if (modification) {
// We store percentages as decimals, so to get them back we need to divide by 10000. Otherwise // We store percentages as decimals, so to get them back we need to divide by 10000. Otherwise
// we divide by 100. Both ways we end up with a value with two decimal places // we divide by 100. Both ways we end up with a value with two decimal places
let modValue; let modValue;
if (modification.type === 'percentage') { if (modification.type === 'percentage') {
modValue = this.getModValue(name) / 10000; modValue = this.getModValue(name) / 10000;
} else if (modification.type === 'numeric') { } else if (modification.type === 'numeric') {
modValue = this.getModValue(name) / 100; modValue = this.getModValue(name) / 100;
} else { } else {
modValue = this.getModValue(name); modValue = this.getModValue(name);
}
if (modValue) {
if (!result && modification.method === 'additive') {
// If the modification is additive and no value is given by default we
// start at zero
result = 0;
} }
if (modValue) {
if (!result && modification.method === 'additive') {
// If the modification is additive and no value is given by default we
// start at zero
result = 0;
}
if (result !== undefined) { if (result !== undefined) {
if (modification.method === 'additive') { if (modification.method === 'additive') {
result = result + modValue; result = result + modValue;
} else if (modification.method === 'overwrite') { } else if (modification.method === 'overwrite') {
result = modValue; result = modValue;
} else if (name === 'shieldboost' || name === 'hullboost') { } else if (name === 'shieldboost' || name === 'hullboost') {
result = (1 + result) * (1 + modValue) - 1; result = (1 + result) * (1 + modValue) - 1;
} else { } else {
result = result * (1 + modValue); result = result * (1 + modValue);
} }
} else if (name === 'burst' || name === 'burstrof') { } else if (name === 'burstrof') {
// Burst and burst rate of fire are special, as it can not exist but // Burst and burst rate of fire are special, as it can not exist but
// have a modification // have a modification
result = modValue / 100; result = modValue / 100;
} }
} }
} }
@@ -1091,5 +1090,4 @@ export default class Module {
getHackTime(modified = true) { getHackTime(modified = true) {
return this.get('hacktime', modified); return this.get('hacktime', modified);
} }
} }

View File

@@ -17,7 +17,6 @@ function filter(arr, maxClass, minClass, mass) {
* The available module set for a specific ship * The available module set for a specific ship
*/ */
export default class ModuleSet { export default class ModuleSet {
/** /**
* Instantiate the module set * Instantiate the module set
* @param {Object} modules All Modules * @param {Object} modules All Modules

View File

@@ -71,7 +71,6 @@ function reduceToIDs(idArray, slot, slotIndex) {
* Ship Model - Encapsulates and models in-game ship behavior * Ship Model - Encapsulates and models in-game ship behavior
*/ */
export default class Ship { export default class Ship {
/** /**
* @param {String} id Unique ship Id / Key * @param {String} id Unique ship Id / Key
* @param {Object} properties Basic ship properties such as name, manufacturer, mass, etc * @param {Object} properties Basic ship properties such as name, manufacturer, mass, etc
@@ -416,16 +415,16 @@ export default class Ship {
clearModifications(m) { clearModifications(m) {
m.mods = {}; m.mods = {};
this.updatePowerGenerated() this.updatePowerGenerated()
.updatePowerUsed() .updatePowerUsed()
.recalculateMass() .recalculateMass()
.updateJumpStats() .updateJumpStats()
.recalculateShield() .recalculateShield()
.recalculateShieldCells() .recalculateShieldCells()
.recalculateArmour() .recalculateArmour()
.recalculateDps() .recalculateDps()
.recalculateEps() .recalculateEps()
.recalculateHps() .recalculateHps()
.updateMovement(); .updateMovement();
} }
/** /**
@@ -696,16 +695,16 @@ export default class Ship {
// Update aggragated stats // Update aggragated stats
if (comps) { if (comps) {
this.updatePowerGenerated() this.updatePowerGenerated()
.updatePowerUsed() .updatePowerUsed()
.recalculateMass() .recalculateMass()
.updateJumpStats() .updateJumpStats()
.recalculateShield() .recalculateShield()
.recalculateShieldCells() .recalculateShieldCells()
.recalculateArmour() .recalculateArmour()
.recalculateDps() .recalculateDps()
.recalculateEps() .recalculateEps()
.recalculateHps() .recalculateHps()
.updateMovement(); .updateMovement();
} }
return this.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString(); return this.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString();
@@ -1187,28 +1186,28 @@ export default class Ship {
// handle unladen mass // handle unladen mass
unladenMass += chain(slots) unladenMass += chain(slots)
.map(slot => slot.m ? slot.m.get('mass') : null) .map(slot => slot.m ? slot.m.get('mass') : null)
.filter() .map(mass => mass || 0)
.reduce((sum, mass) => sum + mass) .reduce((sum, mass) => sum + mass)
.value(); .value();
// handle fuel capacity // handle fuel capacity
fuelCapacity += chain(slots) fuelCapacity += chain(slots)
.map(slot => slot.m ? slot.m.get('fuel') : null) .map(slot => slot.m ? slot.m.get('fuel') : null)
.filter() .map(fuel => fuel || 0)
.reduce((sum, fuel) => sum + fuel) .reduce((sum, fuel) => sum + fuel)
.value(); .value();
// handle cargo capacity // handle cargo capacity
cargoCapacity += chain(slots) cargoCapacity += chain(slots)
.map(slot => slot.m ? slot.m.get('cargo') : null) .map(slot => slot.m ? slot.m.get('cargo') : null)
.filter() .map(cargo => cargo || 0)
.reduce((sum, cargo) => sum + cargo) .reduce((sum, cargo) => sum + cargo)
.value(); .value();
// handle passenger capacity // handle passenger capacity
passengerCapacity += chain(slots) passengerCapacity += chain(slots)
.map(slot => slot.m ? slot.m.get('passengers') : null) .map(slot => slot.m ? slot.m.get('passengers') : null)
.filter() .map(passengers => passengers || 0)
.reduce((sum, passengers) => sum + passengers) .reduce((sum, passengers) => sum + passengers)
.value(); .value();
@@ -1683,11 +1682,11 @@ export default class Ship {
updated; updated;
this.useBulkhead(0) this.useBulkhead(0)
.use(standard[2], fsd) // FSD .use(standard[2], fsd) // FSD
.use(standard[3], ls) // Life Support .use(standard[3], ls) // Life Support
.use(standard[5], s) // Sensors .use(standard[5], s) // Sensors
.use(standard[4], pd) // Power Distributor .use(standard[4], pd) // Power Distributor
.use(standard[6], ft); // Fuel Tank .use(standard[6], ft); // Fuel Tank
// Turn off nearly everything // Turn off nearly everything
if (m.fsdDisabled) this.setSlotEnabled(this.standard[2], false); if (m.fsdDisabled) this.setSlotEnabled(this.standard[2], false);

View File

@@ -1,5 +1,7 @@
import { EventEmitter } from 'fbemitter'; import { EventEmitter } from 'fbemitter';
import { Insurance } from '../shipyard/Constants'; import { Insurance } from '../shipyard/Constants';
import { buildsCollection, database } from '../model';
import {Q} from '@nozbe/watermelondb';
const LS_KEY_BUILDS = 'builds'; const LS_KEY_BUILDS = 'builds';
const LS_KEY_COMPARISONS = 'comparisons'; const LS_KEY_COMPARISONS = 'comparisons';
@@ -70,7 +72,6 @@ function _delete(key) {
* export is an instance (see end of this file). * export is an instance (see end of this file).
*/ */
export class Persist extends EventEmitter { export class Persist extends EventEmitter {
/** /**
* Create an instance * Create an instance
*/ */
@@ -82,7 +83,7 @@ export class Persist extends EventEmitter {
localStorage.setItem('test', 'test'); localStorage.setItem('test', 'test');
localStorage.removeItem('test'); localStorage.removeItem('test');
LS = localStorage; LS = localStorage;
} catch(e) { } catch (e) {
LS = null; LS = null;
} }
@@ -106,7 +107,7 @@ export class Persist extends EventEmitter {
this.comparisons = comparisonJson && typeof comparisonJson == 'object' ? comparisonJson : {}; this.comparisons = comparisonJson && typeof comparisonJson == 'object' ? comparisonJson : {};
this.costTab = _getString(LS_KEY_COST_TAB); this.costTab = _getString(LS_KEY_COST_TAB);
this.outfittingTab = _getString(LS_KEY_OUTFITTING_TAB); this.outfittingTab = _getString(LS_KEY_OUTFITTING_TAB);
this.state = _get(LS_KEY_STATE); this.state = _get(LS_KEY_STATE);
this.sizeRatio = _get(LS_KEY_SIZE_RATIO) || 1; this.sizeRatio = _get(LS_KEY_SIZE_RATIO) || 1;
this.matsPerGrade = matsPerGrade || { this.matsPerGrade = matsPerGrade || {
1: 2, 1: 2,
@@ -133,7 +134,7 @@ export class Persist extends EventEmitter {
let newValue = e.newValue; let newValue = e.newValue;
try { try {
switch(e.key) { switch (e.key) {
case LS_KEY_BUILDS: case LS_KEY_BUILDS:
this.builds = newValue ? JSON.parse(newValue) : {}; this.builds = newValue ? JSON.parse(newValue) : {};
this.emit('builds'); this.emit('builds');
@@ -250,17 +251,27 @@ export class Persist extends EventEmitter {
/** /**
* Persist a ship build in local storage. * Persist a ship build in local storage.
* *
* @param {String} shipId The unique id for a model of ship * @param {String} id The unique id for the ship or ''
* @param {String} shipId The coriolis ship id
* @param {String} name The name of the build * @param {String} name The name of the build
* @param {String} code The serialized code * @param {String} code The serialized code
*/ */
saveBuild(shipId, name, code) { async saveBuild(id, name, code, shipId) {
if (!this.builds[shipId]) { if (id) {
this.builds[shipId] = {}; const build = await buildsCollection.find(id);
if (build) {
return build.update(newBuild => {
newBuild.title = name;
newBuild.body = code;
});
}
} }
this.builds[shipId][name] = code;
_put(LS_KEY_BUILDS, this.builds); return await buildsCollection.create(build => {
this.emit('builds'); build.title = name;
build.ship_id = shipId;
build.body = code;
});
} }
/** /**
@@ -271,11 +282,10 @@ export class Persist extends EventEmitter {
* @param {String} name The name of the build * @param {String} name The name of the build
* @return {String} The serialized build string. * @return {String} The serialized build string.
*/ */
getBuild(shipId, name) { async getBuild(shipId, name) {
if (this.builds[shipId] && this.builds[shipId][name]) { const build = await buildsCollection.query(Q.where('ship_id', shipId), Q.where('title', name)).fetch();
return this.builds[shipId][name]; console.log(build);
} return build;
return null;
} }
/** /**
@@ -284,7 +294,7 @@ export class Persist extends EventEmitter {
* @return {Object | Array} Object if Ship Id is not provided * @return {Object | Array} Object if Ship Id is not provided
*/ */
getBuilds(shipId) { getBuilds(shipId) {
if(shipId && shipId.length > 0) { if (shipId && shipId.length > 0) {
return this.builds[shipId]; return this.builds[shipId];
} }
return this.builds; return this.builds;
@@ -363,7 +373,9 @@ export class Persist extends EventEmitter {
} }
this.comparisons[name] = { this.comparisons[name] = {
facets, facets,
builds: builds.map(b => { return { shipId: b.id || b.shipId, buildName: b.buildName }; }) builds: builds.map(b => {
return { shipId: b.id || b.shipId, buildName: b.buildName };
})
}; };
_put(LS_KEY_COMPARISONS, this.comparisons); _put(LS_KEY_COMPARISONS, this.comparisons);
this.emit('comparisons'); this.emit('comparisons');
@@ -506,6 +518,7 @@ export class Persist extends EventEmitter {
_put(LS_KEY_ROLLS, this.matsPerGrade); _put(LS_KEY_ROLLS, this.matsPerGrade);
this.emit('matsPerGrade'); this.emit('matsPerGrade');
} }
/** /**
* Get the saved Mats per grade * Get the saved Mats per grade
* @return {Object} # of rolls per grade * @return {Object} # of rolls per grade
@@ -557,6 +570,7 @@ export class Persist extends EventEmitter {
this.outfittingTab = tabName; this.outfittingTab = tabName;
_put(LS_KEY_OUTFITTING_TAB, tabName); _put(LS_KEY_OUTFITTING_TAB, tabName);
} }
/** /**
* Get the current outfitting tab * Get the current outfitting tab
* @return {string} the current outfitting tab * @return {string} the current outfitting tab

62
src/app/sw.js Normal file
View File

@@ -0,0 +1,62 @@
console.log('Hello from sw.js');
if (workbox) {
console.log('Yay! Workbox is loaded 🎉');
workbox.routing.registerRoute(
new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
workbox.strategies.cacheFirst({
cacheName: 'google-fonts',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 30
}),
new workbox.cacheableResponse.Plugin({
statuses: [0, 200]
})
]
})
);
try {
workbox.googleAnalytics.initialize();
} catch (e) {
console.log('Probably an ad-blocker');
}
} else {
console.log('Boo! Workbox didn\'t load 😬');
}
self.addEventListener('message', event => {
if (!event.data) {
return;
}
switch (event.data) {
case 'skipWaiting':
self.skipWaiting();
break;
default:
// NOOP
break;
}
});
const OFFLINE_URL = '/';
self.addEventListener('fetch', function(event) {
console.log('Handling fetch event for', event.request.url);
event.respondWith(
caches.match(event.request).then(function(response) {
if (response) {
return response;
}
return fetch(event.request)
.then(function(response) {
return response;
})
.catch(function(error) {
return caches.match(OFFLINE_URL);
});
})
);
});

View File

@@ -19,7 +19,7 @@ export function specialToolTip(translate, blueprint, grp, m, specialName) {
// We also add in any benefits from specials that aren't covered above // We also add in any benefits from specials that aren't covered above
if (m.blueprint) { if (m.blueprint) {
for (const feature in Modifications.modifierActions[specialName]) { for (const feature in Modifications.modifierActions[specialName]) {
// if (!blueprint.features[feature] && !m.mods.feature) { // if (!blueprint.features[feature] && !m.mods.feature) {
const featureDef = Modifications.modifications[feature]; const featureDef = Modifications.modifications[feature];
if (featureDef && !featureDef.hidden) { if (featureDef && !featureDef.hidden) {
let symbol = ''; let symbol = '';
@@ -37,14 +37,14 @@ export function specialToolTip(translate, blueprint, grp, m, specialName) {
const currentIsBeneficial = isValueBeneficial(feature, current); const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push( effects.push(
<tr key={feature + '_specialTT'}> <tr key={feature + '_specialTT'}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td> <td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} <td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'}
style={{ textAlign: 'right' }}>{current}{symbol}</td> style={{ textAlign: 'right' }}>{current}{symbol}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
</tr> </tr>
); );
} }
} }
} }
@@ -54,7 +54,7 @@ export function specialToolTip(translate, blueprint, grp, m, specialName) {
<div> <div>
<table width='100%'> <table width='100%'>
<tbody> <tbody>
{effects} {effects}
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -215,12 +215,12 @@ export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
<div> <div>
<table width='100%'> <table width='100%'>
<thead> <thead>
<tr> <tr>
<td>{translate('feature')}</td> <td>{translate('feature')}</td>
<td>{translate('worst')}</td> <td>{translate('worst')}</td>
{m ? <td>{translate('current')}</td> : null } {m ? <td>{translate('current')}</td> : null }
<td>{translate('best')}</td> <td>{translate('best')}</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{effects} {effects}
@@ -228,10 +228,10 @@ export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
</table> </table>
{ components ? <table width='100%'> { components ? <table width='100%'>
<thead> <thead>
<tr> <tr>
<td>{translate('component')}</td> <td>{translate('component')}</td>
<td>{translate('amount')}</td> <td>{translate('amount')}</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{components} {components}
@@ -239,9 +239,9 @@ export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
</table> : null } </table> : null }
{ engineersList ? <table width='100%'> { engineersList ? <table width='100%'>
<thead> <thead>
<tr> <tr>
<td>{translate('engineers')}</td> <td>{translate('engineers')}</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{engineersList} {engineersList}

View File

@@ -147,83 +147,79 @@ export function shipFromLoadoutJSON(json) {
break; break;
default: default:
} }
for (const module of json.Modules) { if (module.Slot.toLowerCase().search(/hardpoint/) !== -1) {
if (module.Slot.toLowerCase().search(/hardpoint/) !== -1) { // Add hardpoints
// Add hardpoints let hardpoint;
let hardpoint; let hardpointClassNum = -1;
let hardpointClassNum = -1; let hardpointSlotNum = -1;
let hardpointSlotNum = -1; let hardpointArrayNum = 0;
let hardpointArrayNum = 0; for (let i in shipTemplate.slots.hardpoints) {
for (let i in shipTemplate.slots.hardpoints) { if (shipTemplate.slots.hardpoints[i] === hardpointClassNum) {
if (shipTemplate.slots.hardpoints[i] === hardpointClassNum) { // Another slot of the same class
// Another slot of the same class hardpointSlotNum++;
hardpointSlotNum++; } else {
} else { // The first slot of a new class
// The first slot of a new class hardpointClassNum = shipTemplate.slots.hardpoints[i];
hardpointClassNum = shipTemplate.slots.hardpoints[i]; hardpointSlotNum = 1;
hardpointSlotNum = 1;
}
// Now that we know what we're looking for, find it
const hardpointName = HARDPOINT_NUM_TO_CLASS[hardpointClassNum] + 'Hardpoint' + hardpointSlotNum;
const hardpointSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === hardpointName.toLowerCase());
if (!hardpointSlot) {
// This can happen with old imports that don't contain new hardpoints
} else if (!hardpointSlot) {
// No module
} else {
hardpoint = _moduleFromFdName(hardpointSlot.Item);
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true);
ship.hardpoints[hardpointArrayNum].enabled = hardpointSlot.On;
ship.hardpoints[hardpointArrayNum].priority = hardpointSlot.Priority;
modsToAdd.push({ coriolisMod: hardpoint, json: hardpointSlot });
}
hardpointArrayNum++;
} }
}
if (module.Slot.toLowerCase().search(/slot\d/) !== -1) {
let internalSlotNum = 1;
let militarySlotNum = 1;
for (let i in shipTemplate.slots.internal) {
if (!shipTemplate.slots.internal.hasOwnProperty(i)) {
continue;
}
const isMilitary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name = 'military' : false;
// The internal slot might be a standard or a military slot. Military slots have a different naming system // Now that we know what we're looking for, find it
let internalSlot = null; const hardpointName = HARDPOINT_NUM_TO_CLASS[hardpointClassNum] + 'Hardpoint' + hardpointSlotNum;
if (isMilitary) { const hardpointSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === hardpointName.toLowerCase());
const internalName = 'Military0' + militarySlotNum; if (!hardpointSlot) {
// This can happen with old imports that don't contain new hardpoints
} else if (!hardpointSlot) {
// No module
} else {
hardpoint = _moduleFromFdName(hardpointSlot.Item);
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true);
ship.hardpoints[hardpointArrayNum].enabled = hardpointSlot.On;
ship.hardpoints[hardpointArrayNum].priority = hardpointSlot.Priority;
modsToAdd.push({ coriolisMod: hardpoint, json: hardpointSlot });
}
hardpointArrayNum++;
}
}
}
let internalSlotNum = 0;
let militarySlotNum = 1;
for (let i in shipTemplate.slots.internal) {
if (!shipTemplate.slots.internal.hasOwnProperty(i)) {
continue;
}
const isMilitary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name == 'Military' : false;
// The internal slot might be a standard or a military slot. Military slots have a different naming system
let internalSlot = null;
if (isMilitary) {
const internalName = 'Military0' + militarySlotNum;
internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase());
militarySlotNum++;
} else {
// Slot numbers are not contiguous so handle skips.
for (; internalSlot === null && internalSlotNum < 99; internalSlotNum++) {
// Slot sizes have no relationship to the actual size, either, so check all possibilities
for (let slotsize = 0; slotsize < 9; slotsize++) {
const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + slotsize;
if (json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase())) {
internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase()); internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase());
militarySlotNum++; break;
} else {
// Slot numbers are not contiguous so handle skips.
while (internalSlot === null && internalSlotNum < 99) {
// Slot sizes have no relationship to the actual size, either, so check all possibilities
for (let slotsize = 0; slotsize < 9; slotsize++) {
const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '0') + internalSlotNum + '_Size' + slotsize;
if (json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase())) {
internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase());
break;
}
}
internalSlotNum++;
}
}
if (!internalSlot) {
// This can happen with old imports that don't contain new slots
} else {
const internalJson = internalSlot;
const internal = _moduleFromFdName(internalJson.Item);
ship.use(ship.internal[i], internal, true);
ship.internal[i].enabled = internalJson.On === true;
ship.internal[i].priority = internalJson.Priority;
modsToAdd.push({ coriolisMod: internal, json: internalSlot });
} }
} }
} }
} }
if (!internalSlot) {
// This can happen with old imports that don't contain new slots
} else {
const internalJson = internalSlot;
const internal = _moduleFromFdName(internalJson.Item);
ship.use(ship.internal[i], internal, true);
ship.internal[i].enabled = internalJson.On === true;
ship.internal[i].priority = internalJson.Priority;
modsToAdd.push({ coriolisMod: internal, json: internalSlot });
}
} }
for (const i of modsToAdd) { for (const i of modsToAdd) {

View File

@@ -113,7 +113,7 @@ const API_ORBIS = 'https://orbis.zone/api/builds/add';
* @return {Promise<any>} Either a URL or error message. * @return {Promise<any>} Either a URL or error message.
*/ */
export function orbisUpload(ship, creds) { export function orbisUpload(ship, creds) {
return new Promise(async (resolve, reject) => { return new Promise(async(resolve, reject) => {
if (window.navigator.onLine) { if (window.navigator.onLine) {
try { try {
agent agent

View File

@@ -1,7 +1,4 @@
import React from 'react'; import React from 'react';
import cn from 'classnames';
import Module from '../shipyard/Module';
import { Infinite } from '../components/SvgIcons';
import Persist from '../stores/Persist'; import Persist from '../stores/Persist';
import * as ModuleUtils from '../shipyard/ModuleUtils'; import * as ModuleUtils from '../shipyard/ModuleUtils';

View File

@@ -27,7 +27,7 @@
"density": "4.0" "density": "4.0"
} }
], ],
"start_url": "https:\/\/edcd.coriolis.io", "start_url": "https:\/\/coriolis.io",
"display": "standalone", "display": "standalone",
"orientation": "portrait" "orientation": "portrait"
} }

View File

@@ -1,16 +1,19 @@
<!DOCTYPE html> <!DOCTYPE html>
<html manifest="/"> <html>
<head> <head>
<title>Coriolis EDCD Edition</title> <title>Coriolis EDCD Edition</title>
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[0] %>"> <link rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[0] %>">
<!-- Standard headers --> <!-- Standard headers -->
<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="/manifest.json"> <link rel="manifest" href="/manifest.json">
<link rel="shortcut icon" href=/favicon2.ico> <link rel="shortcut icon" href=/favicon2.ico>
<link rel="icon" sizes="152x152 192x192" type="image/png" href="/192x192.png"> <link rel="icon" sizes="152x152 192x192" type="image/png"
href="/192x192.png">
<!-- Apple/iOS headers --> <!-- Apple/iOS headers -->
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
@@ -30,16 +33,16 @@
window.BUGSNAG_VERSION = '<%- htmlWebpackPlugin.options.version + '-' + htmlWebpackPlugin.options.date.toISOString() %>'; window.BUGSNAG_VERSION = '<%- htmlWebpackPlugin.options.version + '-' + htmlWebpackPlugin.options.date.toISOString() %>';
</script> </script>
<% if (htmlWebpackPlugin.options.uaTracking) { %> <% if (htmlWebpackPlugin.options.uaTracking) { %>
<script> <script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date; window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', '<%- htmlWebpackPlugin.options.uaTracking %>', 'auto'); ga('create', '<%- htmlWebpackPlugin.options.uaTracking %>', 'auto');
ga('send', 'pageview'); ga('send', 'pageview');
</script> </script>
<script async src='https://www.google-analytics.com/analytics.js'></script> <script async src='https://www.google-analytics.com/analytics.js'></script>
<% } %> <% } %>
<!-- Piwik --> <!-- Piwik -->
<!-- <script type="text/javascript"> <!-- <script type="text/javascript">
var _paq = _paq || []; var _paq = _paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */ /* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(["setCookieDomain", "*.coriolis.edcd.io"]); _paq.push(["setCookieDomain", "*.coriolis.edcd.io"]);
@@ -53,19 +56,19 @@
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s); g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
})(); })();
</script>--> </script>-->
<!-- End Piwik Code --> <!-- End Piwik Code -->
<!-- Bugsnag --> <!-- Bugsnag -->
<script src="//d2wy8f7a9ursnm.cloudfront.net/v4/bugsnag.min.js"></script> <script src="//d2wy8f7a9ursnm.cloudfront.net/v4/bugsnag.min.js"></script>
<script src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-plugins/v1/bugsnag-react.min.js"></script> <script
<script> src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-plugins/v1/bugsnag-react.min.js"></script>
<script>
window.bugsnagClient = bugsnag('ba9fae819372850fb660755341fa6ef5', {appVersion: window.BUGSNAG_VERSION || undefined}) window.bugsnagClient = bugsnag('ba9fae819372850fb660755341fa6ef5', {appVersion: window.BUGSNAG_VERSION || undefined})
window.Bugsnag = window.bugsnagClient window.Bugsnag = window.bugsnagClient
</script> </script>
</head> </head>
<body style="background-color:#000;"> <body style="background-color:#000;">
<section id="coriolis"></section> <section id="coriolis"></section>
<script src="<%= htmlWebpackPlugin.files.chunks.lib.entry %>" charset="utf-8" crossorigin="anonymous"></script>
<script src="<%= htmlWebpackPlugin.files.chunks.app.entry %>" charset="utf-8" crossorigin="anonymous"></script> </body>
</body> </html>
</html>

View File

@@ -12,6 +12,12 @@
cursor: pointer; cursor: pointer;
} }
// A multi-crew pip
.mc {
stroke: @secondary;
fill: @secondary;
}
// A full pip // A full pip
.full { .full {
stroke: @primary; stroke: @primary;

View File

@@ -74,7 +74,7 @@
border-color:#fff; border-color:#fff;
} }
input:disabled { input.greyed-out {
border-color: #888; border-color: #888;
color: #888; color: #888;
} }

View File

@@ -1,46 +0,0 @@
console.log('Hello from sw.js');
if (workbox) {
workbox.skipWaiting();
workbox.clientsClaim();
console.log('Yay! Workbox is loaded 🎉');
workbox.routing.registerRoute(
new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
workbox.strategies.cacheFirst({
cacheName: 'google-fonts',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 30
}),
new workbox.cacheableResponse.Plugin({
statuses: [0, 200]
})
]
})
);
workbox.routing.registerRoute(
/\.(?:png|gif|jpg|jpeg|svg)$/,
workbox.strategies.cacheFirst({
cacheName: 'images',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60 // 30 Days
})
]
})
);
workbox.routing.registerRoute(
/\.(?:js|css)$/,
workbox.strategies.staleWhileRevalidate({
cacheName: 'static-resources'
})
);
try {
workbox.googleAnalytics.initialize();
} catch (e) {
console.log('Probably an ad-blocker');
}
} else {
console.log('Boo! Workbox didn\'t load 😬');
}

View File

@@ -1,49 +1,51 @@
const path = require('path'); const path = require('path');
const exec = require('child_process').exec;
const webpack = require('webpack'); const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); const ExtractTextPlugin = require('extract-text-webpack-plugin');
const WebpackNotifierPlugin = require('webpack-notifier'); const WebpackNotifierPlugin = require('webpack-notifier');
const pkgJson = require('./package'); const pkgJson = require('./package');
const buildDate = new Date(); const buildDate = new Date();
function CopyDirPlugin(source, destination) { const CopyWebpackPlugin = require('copy-webpack-plugin');
this.source = source;
this.destination = destination;
}
CopyDirPlugin.prototype.apply = function(compiler) {
compiler.plugin('done', () => {
console.log(compiler.outputPath, this.destination);
exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination));
});
};
module.exports = { module.exports = {
devtool: 'source-map', devtool: 'source-map',
devServer: { devServer: {
headers: { 'Access-Control-Allow-Origin': '*' } headers: { 'Access-Control-Allow-Origin': '*' }
}, },
mode: 'development',
entry: { entry: {
app: ['webpack-dev-server/client?http://0.0.0.0:3300', 'webpack/hot/only-dev-server', path.join(__dirname, 'src/app/index.js')], main: './src/app/index.js'
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']
}, },
optimization: {
minimize: false,
splitChunks: {
chunks: 'all'
}
},
output: { output: {
path: path.join(__dirname, 'build'), path: path.join(__dirname, 'build'),
filename: 'app.js', filename: 'app.js',
globalObject: 'this',
publicPath: '/' publicPath: '/'
}, },
node: {
fs: 'empty',
net: 'empty',
tls: 'empty',
'crypto': 'empty'
},
plugins: [ plugins: [
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''), new CopyWebpackPlugin(['src/.htaccess']),
new webpack.optimize.CommonsChunkPlugin({ // new webpack.optimize.CommonsChunkPlugin({
name: 'lib', // name: 'lib',
filename: 'lib.js' // filename: 'lib.js'
}), // }),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
inject: false, inject: true,
template: path.join(__dirname, 'src/index.ejs'), template: path.join(__dirname, 'src/index.ejs'),
version: pkgJson.version, version: pkgJson.version,
date: buildDate, date: buildDate,
@@ -61,8 +63,12 @@ module.exports = {
module: { module: {
rules: [ rules: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) }, { test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) },
{ test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' }) }, {
test: /\.less$/,
loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' })
},
{ test: /\.(js|jsx)$/, loaders: ['babel-loader'], include: path.join(__dirname, 'src') }, { test: /\.(js|jsx)$/, loaders: ['babel-loader'], include: path.join(__dirname, 'src') },
{ test: /\.worker\.js$/, use: { loader: 'worker-loader' } },
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' }, { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' }, { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/octet-stream' }, { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/octet-stream' },

View File

@@ -1,63 +1,43 @@
const path = require('path'); const path = require('path');
const exec = require('child_process').exec;
const webpack = require('webpack'); const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); const ExtractTextPlugin = require('extract-text-webpack-plugin');
const { InjectManifest } = require('workbox-webpack-plugin'); const { InjectManifest } = require('workbox-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { BugsnagSourceMapUploaderPlugin } = require('webpack-bugsnag-plugins'); const { BugsnagSourceMapUploaderPlugin } = require('webpack-bugsnag-plugins');
const pkgJson = require('./package'); const pkgJson = require('./package');
const buildDate = new Date(); const buildDate = new Date();
function CopyDirPlugin(source, destination) {
this.source = source;
this.destination = destination;
}
CopyDirPlugin.prototype.apply = function(compiler) {
compiler.plugin('done', () => {
console.log(compiler.outputPath, this.destination);
exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination));
});
};
module.exports = { module.exports = {
cache: true,
devtool: 'source-map', devtool: 'source-map',
entry: { entry: {
app: ['babel-polyfill', path.resolve(__dirname, 'src/app/index')], main: ['./src/app/index.js']
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
}, },
resolve: { resolve: {
extensions: ['.js', '.jsx', '.json', '.less'] extensions: ['.js', '.jsx', '.json', '.less']
}, },
output: { output: {
path: path.join(__dirname, 'build'), path: path.join(__dirname, 'build'),
filename: '[name].[chunkhash:6].js', filename: '[name].[hash].js',
chunkFilename: '[name].[chunkhash:6]', publicPath: '/',
publicPath: '/' globalObject: 'this'
},
node: { fs: 'empty' },
mode: 'production',
optimization: {
minimize: true,
splitChunks: {
chunks: 'all'
}
}, },
plugins: [ plugins: [
new webpack.optimize.UglifyJsPlugin({ new CopyWebpackPlugin(['src/.htaccess', { from: 'src/schemas', to: 'schemas' }, {from: 'src/images/logo/*', flatten: true, to: ''}]),
'screw-ie8': true,
sourceMap: true
}),
// new webpack.optimize.CommonsChunkPlugin({ // new webpack.optimize.CommonsChunkPlugin({
// name: 'lib', // name: 'lib',
// filename: 'lib.[chunkhash:6].js' // filename: 'lib.[chunkhash:6].js'
// }), // }),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
inject: false, inject: true,
appCache: 'coriolis.appcache',
minify: {
collapseBooleanAttributes: true,
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
removeEmptyAttributes: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true
},
template: path.join(__dirname, 'src/index.ejs'), template: path.join(__dirname, 'src/index.ejs'),
uaTracking: process.env.CORIOLIS_UA_TRACKING || '', uaTracking: process.env.CORIOLIS_UA_TRACKING || '',
gapiKey: process.env.CORIOLIS_GAPI_KEY || '', gapiKey: process.env.CORIOLIS_GAPI_KEY || '',
@@ -65,25 +45,23 @@ module.exports = {
version: pkgJson.version version: pkgJson.version
}), }),
new ExtractTextPlugin({ new ExtractTextPlugin({
filename: '[contenthash:6].css', filename: '[hash:6].css',
disable: false, disable: false,
allChunks: true allChunks: true
}), }),
new BugsnagSourceMapUploaderPlugin({ // new BugsnagSourceMapUploaderPlugin({
apiKey: 'ba9fae819372850fb660755341fa6ef5', // apiKey: 'ba9fae819372850fb660755341fa6ef5',
appVersion: `${pkgJson.version}-${buildDate.toISOString()}` // appVersion: `${pkgJson.version}-${buildDate.toISOString()}`
}), // }),
new CopyDirPlugin(path.join(__dirname, 'src/schemas'), 'schemas'),
new CopyDirPlugin(path.join(__dirname, 'src/images/logo/*'), ''),
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''),
new InjectManifest({ new InjectManifest({
swSrc: './src/sw.js', swSrc: './src/app/sw.js',
importWorkboxFrom: 'cdn', importWorkboxFrom: 'cdn',
swDest: 'service-worker.js' swDest: 'service-worker.js'
}), }),
], ],
module: { module: {
rules: [ rules: [
{test: /\.worker\.js$/, use: {loader: 'worker-loader'}},
{ test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) }, { test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) },
{ test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' }) }, { test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' }) },
{ test: /\.(js|jsx)$/, loader: 'babel-loader?cacheDirectory=true', include: path.join(__dirname, 'src') }, { test: /\.(js|jsx)$/, loader: 'babel-loader?cacheDirectory=true', include: path.join(__dirname, 'src') },