Compare commits

...

44 Commits

Author SHA1 Message Date
felixlinker
59b6945fee Allow testmode for ReactGA 2018-10-21 23:43:56 +01:00
felixlinker
4613e7ca7a Replace local storage variables with class in tests 2018-10-21 23:42:27 +01:00
felixlinker
3315570edf eslint fixes 2018-10-21 23:42:27 +01:00
felixlinker
1fd545ba1f Add new keys to test fixtures 2018-10-21 23:42:27 +01:00
felixlinker
25839de1fe Loop ships outside of test for meaningfull error messages 2018-10-21 23:42:27 +01:00
felixlinker
15feff9344 Don't test or describe fullTankRange because it is not used 2018-10-21 23:42:27 +01:00
William Blythe
a599b1a076 remove d3 rollup that doesnt seem to serve a purpose 2018-10-22 09:21:33 +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
68 changed files with 916 additions and 8154 deletions

View File

@@ -1,3 +1,34 @@
{ {
"presets": ["env", "react", "stage-0"] "presets": [
["@babel/preset-env", {"modules": "commonjs"}],
"@babel/preset-react"
],
"plugins": [
"@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-decorators",
{
"legacy": true
}
],
"@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-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

View File

@@ -275,11 +275,11 @@
"totalCost": 882362060, "totalCost": 882362060,
"unladenMass": 1179.2, "unladenMass": 1179.2,
"totalDps": 29, "totalDps": 29,
"passengerCapacity": 0,
"powerAvailable": 36, "powerAvailable": 36,
"powerRetracted": 23.33, "powerRetracted": 23.33,
"powerDeployed": 34.76, "powerDeployed": 34.76,
"unladenRange": 18.49, "unladenRange": 18.49,
"fullTankRange": 18.12,
"ladenRange": 16.39, "ladenRange": 16.39,
"unladenFastestRange": 73.21, "unladenFastestRange": 73.21,
"ladenFastestRange": 66.15, "ladenFastestRange": 66.15,

View File

@@ -287,6 +287,7 @@
"totalThermSDps": 58.64, "totalThermSDps": 58.64,
"baseShieldStrength": 350, "baseShieldStrength": 350,
"baseArmour": 945, "baseArmour": 945,
"hullCausRes": 0,
"hullExplRes": 0.22, "hullExplRes": 0.22,
"hullKinRes": 0.27, "hullKinRes": 0.27,
"hullMass": 400, "hullMass": 400,
@@ -303,13 +304,13 @@
"armour": 2227.5, "armour": 2227.5,
"baseArmour": 525, "baseArmour": 525,
"unladenMass": 1163.2, "unladenMass": 1163.2,
"passengerCapacity": 0,
"powerAvailable": 39.6, "powerAvailable": 39.6,
"powerRetracted": 23.33, "powerRetracted": 23.33,
"powerDeployed": 34.13, "powerDeployed": 34.13,
"roll": 60, "roll": 60,
"unladenRange": 18.74, "unladenRange": 18.74,
"yaw": 10, "yaw": 10,
"fullTankRange": 18.36,
"hardness": 65, "hardness": 65,
"ladenRange": 16.59, "ladenRange": 16.59,
"unladenFastestRange": 74.2, "unladenFastestRange": 74.2,

View File

@@ -237,14 +237,15 @@
"shieldExplRes": 0.5, "shieldExplRes": 0.5,
"shieldKinRes": 0.6, "shieldKinRes": 0.6,
"shieldThermRes": 1.2, "shieldThermRes": 1.2,
"hullCausRes": 0,
"hullExplRes": 1.4, "hullExplRes": 1.4,
"hullKinRes": 1.2, "hullKinRes": 1.2,
"hullThermRes": 1, "hullThermRes": 1,
"passengerCapacity": 0,
"powerAvailable": 20.41, "powerAvailable": 20.41,
"powerRetracted": 11.91, "powerRetracted": 11.91,
"powerDeployed": 11.91, "powerDeployed": 11.91,
"unladenRange": 50.45, "unladenRange": 50.45,
"fullTankRange": 47.03,
"ladenRange": 42.71, "ladenRange": 42.71,
"unladenFastestRange": 317.24, "unladenFastestRange": 317.24,
"ladenFastestRange": 287.02, "ladenFastestRange": 287.02,

View File

@@ -309,14 +309,15 @@
"shieldExplRes": 0.48, "shieldExplRes": 0.48,
"shieldKinRes": 0.55, "shieldKinRes": 0.55,
"shieldThermRes": 1.09, "shieldThermRes": 1.09,
"hullCausRes": 0,
"hullExplRes": 1.4, "hullExplRes": 1.4,
"hullKinRes": 1.2, "hullKinRes": 1.2,
"hullThermRes": 1, "hullThermRes": 1,
"passengerCapacity": 0,
"powerAvailable": 17.24, "powerAvailable": 17.24,
"powerRetracted": 11.3, "powerRetracted": 11.3,
"powerDeployed": 16.41, "powerDeployed": 16.41,
"unladenRange": 25.57, "unladenRange": 25.57,
"fullTankRange": 23.92,
"ladenRange": 22.09, "ladenRange": 22.09,
"unladenFastestRange": 116.23, "unladenFastestRange": 116.23,
"ladenFastestRange": 107, "ladenFastestRange": 107,

View File

@@ -115,7 +115,6 @@
"powerRetracted": 7.21, "powerRetracted": 7.21,
"powerDeployed": 7.21, "powerDeployed": 7.21,
"unladenRange": 32.48, "unladenRange": 32.48,
"fullTankRange": 30.27,
"ladenRange": 19.61, "ladenRange": 19.61,
"unladenFastestRange": 176.71, "unladenFastestRange": 176.71,
"ladenFastestRange": 112.92, "ladenFastestRange": 112.92,
@@ -249,7 +248,6 @@
"powerRetracted": 9.33, "powerRetracted": 9.33,
"powerDeployed": 10.33, "powerDeployed": 10.33,
"unladenRange": 30.24, "unladenRange": 30.24,
"fullTankRange": 28.32,
"ladenRange": 19.8, "ladenRange": 19.8,
"unladenFastestRange": 164.89, "unladenFastestRange": 164.89,
"ladenFastestRange": 114.03, "ladenFastestRange": 114.03,
@@ -379,7 +377,6 @@
"powerRetracted": 7.28, "powerRetracted": 7.28,
"powerDeployed": 8.06, "powerDeployed": 8.06,
"unladenRange": 31.71, "unladenRange": 31.71,
"fullTankRange": 29.61,
"ladenRange": 23.58, "ladenRange": 23.58,
"unladenFastestRange": 172.68, "unladenFastestRange": 172.68,
"ladenFastestRange": 136.46, "ladenFastestRange": 136.46,
@@ -511,7 +508,6 @@
"powerRetracted": 8.81, "powerRetracted": 8.81,
"powerDeployed": 8.81, "powerDeployed": 8.81,
"unladenRange": 26.41, "unladenRange": 26.41,
"fullTankRange": 24.97,
"ladenRange": 17.36, "ladenRange": 17.36,
"unladenFastestRange": 172.04, "unladenFastestRange": 172.04,
"ladenFastestRange": 118.55, "ladenFastestRange": 118.55,
@@ -653,7 +649,6 @@
"powerRetracted": 10.66, "powerRetracted": 10.66,
"powerDeployed": 11.66, "powerDeployed": 11.66,
"unladenRange": 25.77, "unladenRange": 25.77,
"fullTankRange": 24.39,
"ladenRange": 17.98, "ladenRange": 17.98,
"unladenFastestRange": 167.93, "unladenFastestRange": 167.93,
"ladenFastestRange": 122.84, "ladenFastestRange": 122.84,
@@ -791,7 +786,6 @@
"powerRetracted": 8.95, "powerRetracted": 8.95,
"powerDeployed": 9.73, "powerDeployed": 9.73,
"unladenRange": 19.27, "unladenRange": 19.27,
"fullTankRange": 18.95,
"ladenRange": 15.43, "ladenRange": 15.43,
"unladenFastestRange": 67.34, "unladenFastestRange": 67.34,
"ladenFastestRange": 54.75, "ladenFastestRange": 54.75,
@@ -954,7 +948,6 @@
"powerRetracted": 15.49, "powerRetracted": 15.49,
"powerDeployed": 19.43, "powerDeployed": 19.43,
"unladenRange": 27.1, "unladenRange": 27.1,
"fullTankRange": 25.58,
"ladenRange": 21.94, "ladenRange": 21.94,
"unladenFastestRange": 176.39, "unladenFastestRange": 176.39,
"ladenFastestRange": 150.58, "ladenFastestRange": 150.58,
@@ -1096,7 +1089,6 @@
"powerRetracted": 10.38, "powerRetracted": 10.38,
"powerDeployed": 12.58, "powerDeployed": 12.58,
"unladenRange": 26.01, "unladenRange": 26.01,
"fullTankRange": 25.42,
"ladenRange": 17.19, "ladenRange": 17.19,
"unladenFastestRange": 90.67, "unladenFastestRange": 90.67,
"ladenFastestRange": 61.04, "ladenFastestRange": 61.04,
@@ -1260,7 +1252,6 @@
"powerRetracted": 19.35, "powerRetracted": 19.35,
"powerDeployed": 25.31, "powerDeployed": 25.31,
"unladenRange": 15.19, "unladenRange": 15.19,
"fullTankRange": 14.99,
"ladenRange": 14.99, "ladenRange": 14.99,
"unladenFastestRange": 53.15, "unladenFastestRange": 53.15,
"ladenFastestRange": 53.15, "ladenFastestRange": 53.15,
@@ -1360,7 +1351,6 @@
"powerRetracted": 10.7, "powerRetracted": 10.7,
"powerDeployed": 10.7, "powerDeployed": 10.7,
"unladenRange": 24.25, "unladenRange": 24.25,
"fullTankRange": 23.73,
"ladenRange": 23.73, "ladenRange": 23.73,
"unladenFastestRange": 84.56, "unladenFastestRange": 84.56,
"ladenFastestRange": 84.56, "ladenFastestRange": 84.56,
@@ -1499,7 +1489,6 @@
"powerRetracted": 13.13, "powerRetracted": 13.13,
"powerDeployed": 13.13, "powerDeployed": 13.13,
"unladenRange": 19.51, "unladenRange": 19.51,
"fullTankRange": 18.58,
"ladenRange": 13.09, "ladenRange": 13.09,
"unladenFastestRange": 152.32, "unladenFastestRange": 152.32,
"ladenFastestRange": 106.49, "ladenFastestRange": 106.49,
@@ -1637,7 +1626,6 @@
"powerRetracted": 10.25, "powerRetracted": 10.25,
"powerDeployed": 10.25, "powerDeployed": 10.25,
"unladenRange": 28.25, "unladenRange": 28.25,
"fullTankRange": 26.6,
"ladenRange": 16.67, "ladenRange": 16.67,
"unladenFastestRange": 183.67, "unladenFastestRange": 183.67,
"ladenFastestRange": 113.69, "ladenFastestRange": 113.69,
@@ -1808,7 +1796,6 @@
"powerRetracted": 19.34, "powerRetracted": 19.34,
"powerDeployed": 26.18, "powerDeployed": 26.18,
"unladenRange": 20.32, "unladenRange": 20.32,
"fullTankRange": 19.46,
"ladenRange": 14.65, "ladenRange": 14.65,
"unladenFastestRange": 133.17, "unladenFastestRange": 133.17,
"ladenFastestRange": 99.65, "ladenFastestRange": 99.65,
@@ -1987,7 +1974,6 @@
"powerRetracted": 22.61, "powerRetracted": 22.61,
"powerDeployed": 29.63, "powerDeployed": 29.63,
"unladenRange": 14.32, "unladenRange": 14.32,
"fullTankRange": 13.89,
"ladenRange": 13.46, "ladenRange": 13.46,
"unladenFastestRange": 94.42, "unladenFastestRange": 94.42,
"ladenFastestRange": 91.49, "ladenFastestRange": 91.49,
@@ -2217,7 +2203,6 @@
"powerRetracted": 25.88, "powerRetracted": 25.88,
"powerDeployed": 37.51, "powerDeployed": 37.51,
"unladenRange": 16.99, "unladenRange": 16.99,
"fullTankRange": 16.68,
"ladenRange": 15.91, "ladenRange": 15.91,
"unladenFastestRange": 67.35, "unladenFastestRange": 67.35,
"ladenFastestRange": 64.2, "ladenFastestRange": 64.2,
@@ -2372,7 +2357,6 @@
"powerRetracted": 11.92, "powerRetracted": 11.92,
"powerDeployed": 11.92, "powerDeployed": 11.92,
"unladenRange": 39.26, "unladenRange": 39.26,
"fullTankRange": 37.65,
"ladenRange": 21.21, "ladenRange": 21.21,
"unladenFastestRange": 153.79, "unladenFastestRange": 153.79,
"ladenFastestRange": 85.82, "ladenFastestRange": 85.82,
@@ -2528,7 +2512,6 @@
"powerRetracted": 12.49, "powerRetracted": 12.49,
"powerDeployed": 12.49, "powerDeployed": 12.49,
"unladenRange": 38.44, "unladenRange": 38.44,
"fullTankRange": 36.89,
"ladenRange": 21.04, "ladenRange": 21.04,
"unladenFastestRange": 150.62, "unladenFastestRange": 150.62,
"ladenFastestRange": 85.16, "ladenFastestRange": 85.16,
@@ -2696,7 +2679,6 @@
"powerRetracted": 13, "powerRetracted": 13,
"powerDeployed": 12.4, "powerDeployed": 12.4,
"unladenRange": 38.04, "unladenRange": 38.04,
"fullTankRange": 30.11,
"ladenRange": 23.03, "ladenRange": 23.03,
"unladenFastestRange": 675.7, "unladenFastestRange": 675.7,
"ladenFastestRange": 501.97, "ladenFastestRange": 501.97,
@@ -2914,7 +2896,6 @@
"powerRetracted": 23.93, "powerRetracted": 23.93,
"powerDeployed": 35.56, "powerDeployed": 35.56,
"unladenRange": 18.49, "unladenRange": 18.49,
"fullTankRange": 18.12,
"ladenRange": 16.39, "ladenRange": 16.39,
"unladenFastestRange": 73.21, "unladenFastestRange": 73.21,
"ladenFastestRange": 66.15, "ladenFastestRange": 66.15,
@@ -3042,7 +3023,6 @@
"powerRetracted": 8.48, "powerRetracted": 8.48,
"powerDeployed": 7.88, "powerDeployed": 7.88,
"unladenRange": 35.99, "unladenRange": 35.99,
"fullTankRange": 33.36,
"ladenRange": 33.36, "ladenRange": 33.36,
"unladenFastestRange": 232.28, "unladenFastestRange": 232.28,
"ladenFastestRange": 232.28, "ladenFastestRange": 232.28,
@@ -3179,7 +3159,6 @@
"powerRetracted": 14.87, "powerRetracted": 14.87,
"powerDeployed": 17.51, "powerDeployed": 17.51,
"unladenRange": 15.06, "unladenRange": 15.06,
"fullTankRange": 14.86,
"ladenRange": 14.86, "ladenRange": 14.86,
"unladenFastestRange": 42.5, "unladenFastestRange": 42.5,
"ladenFastestRange": 42.5, "ladenFastestRange": 42.5,
@@ -3333,7 +3312,6 @@
"powerRetracted": 17.71, "powerRetracted": 17.71,
"powerDeployed": 23.14, "powerDeployed": 23.14,
"unladenRange": 12.51, "unladenRange": 12.51,
"fullTankRange": 12.38,
"ladenRange": 12.38, "ladenRange": 12.38,
"unladenFastestRange": 35.35, "unladenFastestRange": 35.35,
"ladenFastestRange": 35.35, "ladenFastestRange": 35.35,
@@ -3452,7 +3430,6 @@
"powerRetracted": 9.46, "powerRetracted": 9.46,
"powerDeployed": 11.17, "powerDeployed": 11.17,
"unladenRange": 17.12, "unladenRange": 17.12,
"fullTankRange": 16.71,
"ladenRange": 16.71, "ladenRange": 16.71,
"unladenFastestRange": 42.4, "unladenFastestRange": 42.4,
"ladenFastestRange": 42.4, "ladenFastestRange": 42.4,
@@ -3610,7 +3587,6 @@
"powerRetracted": 9.31, "powerRetracted": 9.31,
"powerDeployed": 13.91, "powerDeployed": 13.91,
"unladenRange": 8.43, "unladenRange": 8.43,
"fullTankRange": 8.09,
"ladenRange": 7.25, "ladenRange": 7.25,
"unladenFastestRange": 81.5, "unladenFastestRange": 81.5,
"ladenFastestRange": 72.9, "ladenFastestRange": 72.9,

View File

@@ -6,41 +6,70 @@ import TU from 'react-testutils-additions';
let origAddEventListener = window.addEventListener; let origAddEventListener = window.addEventListener;
let storageListener; let storageListener;
let ls = {};
// Implment mock localStorage /**
let localStorage = { * Mock local storage
getItem: function(key) { */
return ls[key]; class LocalStorage {
}, /**
setItem: function(key, value) { * Sets up storage variable
ls[key] = value; */
}, constructor() {
removeItem: function(key) { this.ls = {};
delete ls[key]; }
},
clear: function() { /**
ls = {}; * Retrieves a value from local storage
* @param {String} key Storage key
* @return {*} Storage value
*/
getItem(key) {
return this.ls[key];
}
/**
* Writes a value to local storage
* @param {String} key Storage key
* @param {*} value Storage value
*/
setItem(key, value) {
this.ls[key] = value;
}
/**
* Clears a value from local storage
* @param {String} key Storage key
*/
removeItem(key) {
delete this.ls[key];
}
/**
* Clears local storage
*/
clear() {
this.ls = {};
} }
} }
window.addEventListener = function(eventName, listener) { const LOCAL_STORAGE = new LocalStorage();
window.addEventListener = function(eventName, listener) {
if(eventName == 'storage') { if(eventName == 'storage') {
storageListener = listener; // Keep track of latest storage listener storageListener = listener; // Keep track of latest storage listener
} else { } else {
origAddEventListener.apply(arguments); origAddEventListener.apply(arguments);
} }
} };
describe('Persist', function() { describe('Persist', function() {
const Persist = require('../src/app/stores/Persist').Persist; const Persist = require('../src/app/stores/Persist').Persist;
describe('Multi tab/window', function() { describe('Multi tab/window', function() {
it("syncs builds", function() { it("syncs builds", function() {
window.localStorage = localStorage; window.localStorage = localStorage;
ls = {}; localStorage.clear();
let p = new Persist(); let p = new Persist();
let newBuilds = { let newBuilds = {
anaconda: { test: '1234' } anaconda: { test: '1234' }
@@ -54,7 +83,8 @@ describe('Persist', function() {
describe('General and Settings', function() { describe('General and Settings', function() {
it("has defaults", function() { it("has defaults", function() {
window.localStorage = localStorage; window.localStorage = localStorage;
ls = {}; localStorage.clear();
let p = new Persist(); let p = new Persist();
expect(p.getLangCode()).toBe('en'); expect(p.getLangCode()).toBe('en');
expect(p.showTooltips()).toBe(true); expect(p.showTooltips()).toBe(true);
@@ -65,14 +95,14 @@ describe('Persist', function() {
}); });
it("loads from localStorage correctly", function() { it("loads from localStorage correctly", function() {
window.localStorage = localStorage; window.localStorage = LOCAL_STORAGE;
let savedData = require('./fixtures/valid-backup'); let savedData = require('./fixtures/valid-backup');
ls = {}; LOCAL_STORAGE.clear();
ls.builds = JSON.stringify(savedData.builds); LOCAL_STORAGE.setItem('builds', JSON.stringify(savedData.builds));
ls.NG_TRANSLATE_LANG_KEY = 'de'; LOCAL_STORAGE.setItem('NG_TRANSLATE_LANG_KEY', 'de');
ls.insurance = 'Standard'; LOCAL_STORAGE.setItem('insurance', 'Standard');
ls.shipDiscount = 0.25; LOCAL_STORAGE.setItem('shipDiscount', 0.25);
ls.moduleDiscount = 0.15; LOCAL_STORAGE.setItem('moduleDiscount', 0.15);
let p = new Persist(); let p = new Persist();
expect(p.getInsurance()).toBe('standard'); expect(p.getInsurance()).toBe('standard');
@@ -86,13 +116,13 @@ describe('Persist', function() {
}); });
it("uses defaults from a corrupted localStorage", function() { it("uses defaults from a corrupted localStorage", function() {
window.localStorage = localStorage; window.localStorage = LOCAL_STORAGE;
ls = {}; LOCAL_STORAGE.clear();
ls.builds = "not valid json"; LOCAL_STORAGE.setItem('builds', 'not valid json');
ls.comparisons = "1, 3, 4"; LOCAL_STORAGE.setItem('comparisons', '1, 3, 4');
ls.insurance = 'this insurance does not exist'; LOCAL_STORAGE.setItem('insurance', 'this insurance does not exist');
ls.shipDiscount = 'this is not a number'; LOCAL_STORAGE.setItem('shipDiscount', 'this is not a number');
ls.moduleDiscount = 10; // Way to big LOCAL_STORAGE.setItem('moduleDiscount', 10); // Way to big
let p = new Persist(); let p = new Persist();
expect(p.getLangCode()).toBe('en'); expect(p.getLangCode()).toBe('en');
@@ -122,13 +152,13 @@ describe('Persist', function() {
}); });
it("generates the backup", function() { it("generates the backup", function() {
window.localStorage = localStorage; window.localStorage = LOCAL_STORAGE;
let savedData = require('./fixtures/valid-backup'); let savedData = require('./fixtures/valid-backup');
ls = {}; LOCAL_STORAGE.clear();
ls.builds = JSON.stringify(savedData.builds); LOCAL_STORAGE.setItem('builds', JSON.stringify(savedData.builds));
ls.insurance = 'Beta'; LOCAL_STORAGE.setItem('insurance', 'Beta');
ls.shipDiscount = 0.25; LOCAL_STORAGE.setItem('shipDiscount', 0.25);
ls.moduleDiscount = 0.15; LOCAL_STORAGE.setItem('moduleDiscount', 0.15);
let p = new Persist(); let p = new Persist();
let backup = p.getAll(); let backup = p.getAll();
@@ -140,4 +170,4 @@ describe('Persist', function() {
expect(backup.comparisons).toEqual({}); expect(backup.comparisons).toEqual({});
}); });
}); });
}) });

View File

@@ -3,32 +3,31 @@ import { Ships } from 'coriolis-data/dist';
import * as ModuleUtils from '../src/app/shipyard/ModuleUtils'; import * as ModuleUtils from '../src/app/shipyard/ModuleUtils';
describe("Ship", function() { describe("Ship", function() {
for (let s in Ships) {
it("can build all ships", function() { it("can build " + s, function() {
for (let s in Ships) {
let shipData = Ships[s]; let shipData = Ships[s];
let ship = new Ship(s, shipData.properties, shipData.slots); let ship = new Ship(s, shipData.properties, shipData.slots);
for (let p in shipData.properties) { for (let p in shipData.properties) {
expect(ship[p]).toEqual(shipData.properties[p], s + ' property [' + p + '] does not match when built'); expect(ship[p]).toEqual(shipData.properties[p]);
} }
ship.buildWith(shipData.defaults); ship.buildWith(shipData.defaults);
expect(ship.totalCost).toEqual(shipData.retailCost, s + ' retail cost does not match default build cost'); expect(ship.totalCost).toEqual(shipData.retailCost);
expect(ship.cargoCapacity).toBeDefined(); expect(ship.cargoCapacity).toBeDefined();
expect(ship.priorityBands[0].retracted).toBeGreaterThan(0, s + ' priorityBands'); expect(ship.priorityBands[0].retracted).toBeGreaterThan(0);
expect(ship.powerAvailable).toBeGreaterThan(0, s + ' powerAvailable'); expect(ship.powerAvailable).toBeGreaterThan(0);
expect(ship.unladenRange).toBeGreaterThan(0, s + ' unladenRange'); expect(ship.unladenRange).toBeGreaterThan(0);
expect(ship.ladenRange).toBeGreaterThan(0, s + ' ladenRange'); expect(ship.ladenRange).toBeGreaterThan(0);
expect(ship.fuelCapacity).toBeGreaterThan(0, s + ' fuelCapacity'); expect(ship.fuelCapacity).toBeGreaterThan(0);
expect(ship.unladenFastestRange).toBeGreaterThan(0, s + ' unladenFastestRange'); expect(ship.unladenFastestRange).toBeGreaterThan(0);
expect(ship.ladenFastestRange).toBeGreaterThan(0, s + ' ladenFastestRange'); expect(ship.ladenFastestRange).toBeGreaterThan(0);
expect(ship.shield).toBeGreaterThan(0, s + ' shield'); expect(ship.shield).toBeGreaterThan(0);
expect(ship.armour).toBeGreaterThan(0, s + ' armour'); expect(ship.armour).toBeGreaterThan(0);
expect(ship.topSpeed).toBeGreaterThan(0, s + ' topSpeed'); expect(ship.topSpeed).toBeGreaterThan(0);
} });
}); }
it("resets and rebuilds properly", function() { it("resets and rebuilds properly", function() {
var id = 'cobra_mk_iii'; var id = 'cobra_mk_iii';

11
d3-funcs.js vendored
View File

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

7312
d3.js vendored

File diff suppressed because it is too large Load Diff

3
d3.min.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
{ {
"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"
@@ -11,7 +11,6 @@
"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",
@@ -56,18 +55,35 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"appcache-webpack-plugin": "^1.3.0", "@babel/core": "^7.0.0",
"babel-core": "*", "@babel/plugin-proposal-class-properties": "^7.0.0",
"babel-eslint": "*", "@babel/plugin-proposal-decorators": "^7.0.0",
"babel-jest": "*", "@babel/plugin-proposal-do-expressions": "^7.0.0",
"babel-loader": "*", "@babel/plugin-proposal-export-default-from": "^7.0.0",
"babel-preset-env": "*", "@babel/plugin-proposal-export-namespace-from": "^7.0.0",
"babel-preset-react": "*", "@babel/plugin-proposal-function-bind": "^7.0.0",
"babel-preset-stage-0": "*", "@babel/plugin-proposal-function-sent": "^7.0.0",
"create-react-class": "^15.6.2", "@babel/plugin-proposal-json-strings": "^7.0.0",
"cross-env": "^5.1.4", "@babel/plugin-proposal-logical-assignment-operators": "^7.0.0",
"css-loader": "^0.28.0", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
"d3-selection": "1", "@babel/plugin-proposal-numeric-separator": "^7.0.0",
"@babel/plugin-proposal-optional-chaining": "^7.0.0",
"@babel/plugin-proposal-pipeline-operator": "^7.0.0",
"@babel/plugin-proposal-throw-expressions": "^7.0.0",
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
"@babel/plugin-syntax-import-meta": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"appcache-webpack-plugin": "^1.4.0",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.0.1",
"babel-jest": "^23.6.0",
"babel-loader": "^8.0.0",
"copy-webpack-plugin": "^4.5.2",
"create-react-class": "^15.6.3",
"cross-env": "^5.2.0",
"css-loader": "^1.0.0",
"d3-selection": "^1.3.2",
"esdoc": "^1.1.0", "esdoc": "^1.1.0",
"esdoc-custom-theme": "^1.4.2", "esdoc-custom-theme": "^1.4.2",
"esdoc-ecmascript-proposal-plugin": "^1.0.0", "esdoc-ecmascript-proposal-plugin": "^1.0.0",
@@ -75,51 +91,54 @@
"esdoc-publish-html-plugin": "^1.1.2", "esdoc-publish-html-plugin": "^1.1.2",
"esdoc-react-plugin": "^1.0.1", "esdoc-react-plugin": "^1.0.1",
"esdoc-standard-plugin": "^1.0.0", "esdoc-standard-plugin": "^1.0.0",
"eslint": "3.19.0", "eslint": "^5.6.0",
"eslint-plugin-react": "^6.10.3", "eslint-plugin-react": "^7.11.1",
"expose-loader": "^0.7.3", "expose-loader": "^0.7.5",
"express": "^4.15.2", "express": "^4.16.3",
"extract-text-webpack-plugin": "2.1.0", "extract-text-webpack-plugin": "^4.0.0-beta.0",
"file-loader": "^0.11.1", "file-loader": "^2.0.0",
"html-webpack-plugin": "^2.28.0", "html-webpack-plugin": "^3.0.7",
"jest-cli": "^21.2.1", "jest-cli": "^23.6.0",
"jsen": "^0.6.4", "jsen": "^0.6.4",
"json-loader": "^0.5.4", "json-loader": "^0.5.4",
"less": "^2.7.2", "less": "^3.8.1",
"less-loader": "^4.0.3", "less-loader": "^4.1.0",
"react-addons-perf": "^15.4.2", "react-addons-perf": "^15.4.2",
"react-container-dimensions": "^1.4.1", "react-container-dimensions": "^1.4.1",
"react-testutils-additions": "^15.2.0", "react-testutils-additions": "^16.0.0",
"react-transition-group": "^1.1.2", "react-transition-group": "^2.5.0",
"rimraf": "^2.6.1", "rimraf": "^2.6.1",
"rollup": "0.41", "rollup": "^0.66.2",
"rollup-plugin-node-resolve": "3", "rollup-plugin-node-resolve": "^3.4.0",
"style-loader": "^0.16.1", "style-loader": "^0.23.0",
"uglify-js": "^2.4.11", "uglify-js": "^3.4.9",
"url-loader": "^0.5.8", "url-loader": "^1.1.1",
"webpack": "^2.4.1", "webpack": "^4.20.2",
"webpack-bugsnag-plugins": "^1.1.1", "webpack-bugsnag-plugins": "^1.2.2",
"webpack-dev-server": "^2.4.4", "webpack-cli": "^3.1.1",
"webpack-dev-server": "^3.1.9",
"webpack-notifier": "^1.6.0", "webpack-notifier": "^1.6.0",
"workbox-webpack-plugin": "^3.4.1" "workbox-webpack-plugin": "^3.6.1"
}, },
"dependencies": { "dependencies": {
"babel-polyfill": "*", "@babel/polyfill": "^7.0.0",
"browserify-zlib-next": "^1.0.1", "browserify-zlib-next": "^1.0.1",
"classnames": "^2.2.5", "classnames": "^2.2.6",
"coriolis-data": "../coriolis-data", "coriolis-data": "../coriolis-data",
"d3": "4.8.0", "d3": "^5.7.0",
"detect-browser": "^1.7.0", "detect-browser": "^3.0.1",
"fbemitter": "^2.1.1", "fbemitter": "^2.1.1",
"lodash": "^4.17.10", "lodash": "^4.17.11",
"lz-string": "^1.4.4", "lz-string": "^1.4.4",
"pako": "^1.0.6", "pako": "^1.0.6",
"prop-types": "^15.5.8", "prop-types": "^15.6.2",
"react": "^15.5.4", "react": "^15.5.4",
"react-dom": "^15.5.4", "react-dom": "^15.5.4",
"react-extras": "^0.7.1",
"react-ga": "^2.5.3", "react-ga": "^2.5.3",
"react-number-editor": "Athanasius/react-number-editor.git#miggy", "react-number-editor": "Athanasius/react-number-editor.git#miggy",
"recharts": "^0.22.3", "recharts": "^1.2.0",
"superagent": "^3.5.2" "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
@@ -340,39 +350,29 @@ export default class Coriolis extends React.Component {
// *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;
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);
}); });
}); });
} }
@@ -394,8 +394,8 @@ 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}
@@ -403,7 +403,7 @@ export default class Coriolis extends React.Component {
<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"
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'}

View File

@@ -1,6 +1,13 @@
// Check whether a test is run because then we need to
// initialize ReactGA in test mode
let isTest = false;
try {
isTest = process.env.node_env === 'test';
} catch (e) {}
import Persist from './stores/Persist'; import Persist from './stores/Persist';
import ReactGA from 'react-ga'; import ReactGA from 'react-ga';
ReactGA.initialize('UA-55840909-18'); ReactGA.initialize('UA-55840909-18', { testMode: isTest });
let standalone = undefined; let standalone = undefined;
/** /**

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>;
} }
} }

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';
/** /**

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

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

@@ -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

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';

View File

@@ -100,7 +100,7 @@ export default class OutfittingPage extends Page {
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 +114,9 @@ export default class OutfittingPage extends Page {
sys, sys,
eng, eng,
wep, wep,
mcSys,
mcEng,
mcWep,
boost, boost,
fuel, fuel,
cargo, cargo,
@@ -176,6 +179,9 @@ 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;
@@ -192,12 +198,12 @@ export default class OutfittingPage extends Page {
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(Utils.fromUrlSafe(parts[4])).split('/');
sys = parseFloat(control[0]); sys = parseFloat(control[0]) || sys;
eng = parseFloat(control[1]); eng = parseFloat(control[1]) || eng;
wep = parseFloat(control[2]); 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);
@@ -211,9 +217,9 @@ export default class OutfittingPage extends Page {
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(Utils.fromUrlSafe(opponentParts[4])).split('/');
opponentSys = parseFloat(opponentControl[0]); opponentSys = parseFloat(opponentControl[0]) || opponentSys;
opponentEng = parseFloat(opponentControl[1]); opponentEng = parseFloat(opponentControl[1]) || opponentEng;
opponentWep = parseFloat(opponentControl[2]); opponentWep = parseFloat(opponentControl[2]) || opponentWep;
} }
} }
} else { } else {
@@ -221,21 +227,32 @@ 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());
} }
/** /**
@@ -309,8 +326,8 @@ 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 { 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}`; 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;
} }
@@ -373,12 +390,15 @@ 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 { sys, eng, wep, mcSys, mcEng, mcWep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code);
// Update state, and refresh the ship // Update state, and refresh the ship
this.setState({ this.setState({
sys, sys,
eng, eng,
wep, wep,
mcSys,
mcEng,
mcWep,
boost, boost,
fuel, fuel,
cargo, cargo,
@@ -430,12 +450,15 @@ 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 { sys, eng, wep, mcSys, mcEng, mcWep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code);
// Update state, and refresh the route when complete // Update state, and refresh the route when complete
this.setState({ this.setState({
sys, sys,
eng, eng,
wep, wep,
mcSys,
mcEng,
mcWep,
boost, boost,
fuel, fuel,
cargo, cargo,
@@ -567,7 +590,7 @@ 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,
@@ -671,7 +694,7 @@ export default class OutfittingPage extends Page {
</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}/>

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

@@ -70,7 +70,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
*/ */

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

@@ -65,7 +65,6 @@
</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

@@ -227,11 +227,6 @@
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
}, },
"fullTankRange": {
"description": "Single Jump range with a full tank (unladenMass + fuel)",
"type": "number",
"minimum": 0
},
"ladenMass": { "ladenMass": {
"description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)", "description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)",
"type": "number", "type": "number",

View File

@@ -254,11 +254,6 @@
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
}, },
"fullTankRange": {
"description": "Single Jump range with a full tank (unladenMass + fuel)",
"type": "number",
"minimum": 0
},
"ladenMass": { "ladenMass": {
"description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)", "description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)",
"type": "number", "type": "number",

View File

@@ -258,11 +258,6 @@
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
}, },
"fullTankRange": {
"description": "Single Jump range with a full tank (unladenMass + fuel)",
"type": "number",
"minimum": 0
},
"ladenMass": { "ladenMass": {
"description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)", "description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)",
"type": "number", "type": "number",

View File

@@ -300,11 +300,6 @@
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
}, },
"fullTankRange": {
"description": "Single Jump range with a full tank (unladenMass + fuel)",
"type": "number",
"minimum": 0
},
"ladenMass": { "ladenMass": {
"description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)", "description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)",
"type": "number", "type": "number",

View File

@@ -1,8 +1,6 @@
console.log('Hello from sw.js'); console.log('Hello from sw.js');
if (workbox) { if (workbox) {
workbox.skipWaiting();
workbox.clientsClaim();
console.log('Yay! Workbox is loaded 🎉'); console.log('Yay! Workbox is loaded 🎉');
workbox.routing.registerRoute( workbox.routing.registerRoute(
new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'), new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
@@ -44,3 +42,39 @@ if (workbox) {
} else { } else {
console.log('Boo! Workbox didn\'t load 😬'); 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;
}
});
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

@@ -1,49 +1,44 @@
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',
publicPath: '/' publicPath: '/'
}, },
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,

View File

@@ -1,63 +1,42 @@
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'
},
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,17 +44,14 @@ 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/sw.js',
importWorkboxFrom: 'cdn', importWorkboxFrom: 'cdn',