mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-08 22:33:24 +00:00
2.0.1 Beta
This commit is contained in:
@@ -3,7 +3,7 @@ notifications:
|
||||
email: false
|
||||
sudo: false
|
||||
node_js:
|
||||
- "0.12"
|
||||
- "4.2.6"
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
@@ -13,4 +13,3 @@ before_script:
|
||||
script:
|
||||
- npm run lint
|
||||
- npm test
|
||||
- npm run build
|
||||
@@ -1,5 +1,4 @@
|
||||
[](https://travis-ci.org/cmmcleod/coriolis) [](https://waffle.io/cmmcleod/coriolis) [](http://waffle.io/cmmcleod/coriolis)
|
||||
|
||||
 [](https://travis-ci.org/cmmcleod/coriolis) [](https://waffle.io/cmmcleod/coriolis) [](http://waffle.io/cmmcleod/coriolis) [](https://www.hipchat.com/gfYQiZcmy)
|
||||
|
||||
|
||||
## About
|
||||
@@ -10,12 +9,12 @@ Coriolis was created using assets and imagery from Elite: Dangerous, with the pe
|
||||
|
||||
## Contributing
|
||||
|
||||
Chat to us on [HipChat](https://www.hipchat.com/gfYQiZcmy)!
|
||||
|
||||
Please [submit issues](https://github.com/cmmcleod/coriolis/issues), or better yet [pull requests](https://github.com/cmmcleod/coriolis/pulls) for any corrections or additions to the database or the code.
|
||||
|
||||
### Feature Requests, Suggestions & Bugs
|
||||
|
||||
Chat to us on [HipChat](https://www.hipchat.com/gfYQiZcmy)!
|
||||
|
||||
All such requests are managed and tracked through [issues](https://github.com/cmmcleod/coriolis/issues). An overview of these can be found [here](https://waffle.io/cmmcleod/coriolis).
|
||||
|
||||
## Development
|
||||
@@ -23,7 +22,7 @@ All such requests are managed and tracked through [issues](https://github.com/cm
|
||||
See the [Developer's Guide](https://github.com/cmmcleod/coriolis/wiki/Developer's-Guide) in the wiki.
|
||||
|
||||
|
||||
### Ship and Component Database
|
||||
### Ship and Module Database
|
||||
|
||||
See the [Data wiki](https://github.com/cmmcleod/coriolis-data/wiki) for details on structure, etc.
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"$schema": "http://cdn.coriolis.io/schemas/ship-loadout/3.json#",
|
||||
"name": "Test",
|
||||
"name": "Test My Ship",
|
||||
"ship": "Anaconda",
|
||||
"references": [
|
||||
{
|
||||
"name": "Coriolis.io",
|
||||
"url": "http://localhost:3300/outfit/anaconda/48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA?bn=Test?bn=Test",
|
||||
"url": "http://localhost:3300/outfit/anaconda/48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA?bn=Test%20My%20Ship",
|
||||
"code": "48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA",
|
||||
"shipId": "anaconda"
|
||||
}
|
||||
@@ -281,8 +281,8 @@
|
||||
"unladenRange": 18.49,
|
||||
"fullTankRange": 18.12,
|
||||
"ladenRange": 16.39,
|
||||
"unladenTotalRange": 73.21,
|
||||
"ladenTotalRange": 66.15,
|
||||
"unladenFastestRange": 73.21,
|
||||
"ladenFastestRange": 66.15,
|
||||
"maxJumpCount": 4,
|
||||
"shieldStrength": 833
|
||||
}
|
||||
|
||||
@@ -117,8 +117,8 @@
|
||||
"unladenRange": 32.48,
|
||||
"fullTankRange": 30.27,
|
||||
"ladenRange": 19.61,
|
||||
"unladenTotalRange": 176.71,
|
||||
"ladenTotalRange": 112.92,
|
||||
"unladenFastestRange": 176.71,
|
||||
"ladenFastestRange": 112.92,
|
||||
"maxJumpCount": 6,
|
||||
"shieldStrength": 86.49
|
||||
}
|
||||
@@ -251,8 +251,8 @@
|
||||
"unladenRange": 30.24,
|
||||
"fullTankRange": 28.32,
|
||||
"ladenRange": 19.8,
|
||||
"unladenTotalRange": 164.89,
|
||||
"ladenTotalRange": 114.03,
|
||||
"unladenFastestRange": 164.89,
|
||||
"ladenFastestRange": 114.03,
|
||||
"maxJumpCount": 6,
|
||||
"shieldStrength": 149.2
|
||||
}
|
||||
@@ -381,8 +381,8 @@
|
||||
"unladenRange": 31.71,
|
||||
"fullTankRange": 29.61,
|
||||
"ladenRange": 23.58,
|
||||
"unladenTotalRange": 172.68,
|
||||
"ladenTotalRange": 136.46,
|
||||
"unladenFastestRange": 172.68,
|
||||
"ladenFastestRange": 136.46,
|
||||
"maxJumpCount": 6,
|
||||
"shieldStrength": 86.49
|
||||
}
|
||||
@@ -513,8 +513,8 @@
|
||||
"unladenRange": 26.41,
|
||||
"fullTankRange": 24.97,
|
||||
"ladenRange": 17.36,
|
||||
"unladenTotalRange": 172.04,
|
||||
"ladenTotalRange": 118.55,
|
||||
"unladenFastestRange": 172.04,
|
||||
"ladenFastestRange": 118.55,
|
||||
"maxJumpCount": 7,
|
||||
"shieldStrength": 89.07
|
||||
}
|
||||
@@ -655,8 +655,8 @@
|
||||
"unladenRange": 25.77,
|
||||
"fullTankRange": 24.39,
|
||||
"ladenRange": 17.98,
|
||||
"unladenTotalRange": 167.93,
|
||||
"ladenTotalRange": 122.84,
|
||||
"unladenFastestRange": 167.93,
|
||||
"ladenFastestRange": 122.84,
|
||||
"maxJumpCount": 7,
|
||||
"shieldStrength": 125.07
|
||||
}
|
||||
@@ -793,8 +793,8 @@
|
||||
"unladenRange": 19.27,
|
||||
"fullTankRange": 18.95,
|
||||
"ladenRange": 15.43,
|
||||
"unladenTotalRange": 67.34,
|
||||
"ladenTotalRange": 54.75,
|
||||
"unladenFastestRange": 67.34,
|
||||
"ladenFastestRange": 54.75,
|
||||
"maxJumpCount": 4,
|
||||
"shieldStrength": 111.07
|
||||
}
|
||||
@@ -956,8 +956,8 @@
|
||||
"unladenRange": 27.1,
|
||||
"fullTankRange": 25.58,
|
||||
"ladenRange": 21.94,
|
||||
"unladenTotalRange": 176.39,
|
||||
"ladenTotalRange": 150.58,
|
||||
"unladenFastestRange": 176.39,
|
||||
"ladenFastestRange": 150.58,
|
||||
"maxJumpCount": 7,
|
||||
"shieldStrength": 253.58
|
||||
}
|
||||
@@ -1098,8 +1098,8 @@
|
||||
"unladenRange": 26.01,
|
||||
"fullTankRange": 25.42,
|
||||
"ladenRange": 17.19,
|
||||
"unladenTotalRange": 90.67,
|
||||
"ladenTotalRange": 61.04,
|
||||
"unladenFastestRange": 90.67,
|
||||
"ladenFastestRange": 61.04,
|
||||
"maxJumpCount": 4,
|
||||
"shieldStrength": 191.82
|
||||
}
|
||||
@@ -1262,8 +1262,8 @@
|
||||
"unladenRange": 15.19,
|
||||
"fullTankRange": 14.99,
|
||||
"ladenRange": 14.99,
|
||||
"unladenTotalRange": 53.15,
|
||||
"ladenTotalRange": 53.15,
|
||||
"unladenFastestRange": 53.15,
|
||||
"ladenFastestRange": 53.15,
|
||||
"maxJumpCount": 4,
|
||||
"shieldStrength": 489.6
|
||||
}
|
||||
@@ -1362,8 +1362,8 @@
|
||||
"unladenRange": 24.25,
|
||||
"fullTankRange": 23.73,
|
||||
"ladenRange": 23.73,
|
||||
"unladenTotalRange": 84.56,
|
||||
"ladenTotalRange": 84.56,
|
||||
"unladenFastestRange": 84.56,
|
||||
"ladenFastestRange": 84.56,
|
||||
"maxJumpCount": 4,
|
||||
"shieldStrength": 0
|
||||
}
|
||||
@@ -1501,8 +1501,8 @@
|
||||
"unladenRange": 19.51,
|
||||
"fullTankRange": 18.58,
|
||||
"ladenRange": 13.09,
|
||||
"unladenTotalRange": 152.32,
|
||||
"ladenTotalRange": 106.49,
|
||||
"unladenFastestRange": 152.32,
|
||||
"ladenFastestRange": 106.49,
|
||||
"maxJumpCount": 8,
|
||||
"shieldStrength": 170.57
|
||||
}
|
||||
@@ -1639,8 +1639,8 @@
|
||||
"unladenRange": 28.25,
|
||||
"fullTankRange": 26.6,
|
||||
"ladenRange": 16.67,
|
||||
"unladenTotalRange": 183.67,
|
||||
"ladenTotalRange": 113.69,
|
||||
"unladenFastestRange": 183.67,
|
||||
"ladenFastestRange": 113.69,
|
||||
"maxJumpCount": 7,
|
||||
"shieldStrength": 214.26
|
||||
}
|
||||
@@ -1810,8 +1810,8 @@
|
||||
"unladenRange": 20.32,
|
||||
"fullTankRange": 19.46,
|
||||
"ladenRange": 14.65,
|
||||
"unladenTotalRange": 133.17,
|
||||
"ladenTotalRange": 99.65,
|
||||
"unladenFastestRange": 133.17,
|
||||
"ladenFastestRange": 99.65,
|
||||
"maxJumpCount": 7,
|
||||
"shieldStrength": 486.35
|
||||
}
|
||||
@@ -1989,8 +1989,8 @@
|
||||
"unladenRange": 14.32,
|
||||
"fullTankRange": 13.89,
|
||||
"ladenRange": 13.46,
|
||||
"unladenTotalRange": 94.42,
|
||||
"ladenTotalRange": 91.49,
|
||||
"unladenFastestRange": 94.42,
|
||||
"ladenFastestRange": 91.49,
|
||||
"maxJumpCount": 7,
|
||||
"shieldStrength": 645.57
|
||||
}
|
||||
@@ -2219,8 +2219,8 @@
|
||||
"unladenRange": 16.99,
|
||||
"fullTankRange": 16.68,
|
||||
"ladenRange": 15.91,
|
||||
"unladenTotalRange": 67.35,
|
||||
"ladenTotalRange": 64.2,
|
||||
"unladenFastestRange": 67.35,
|
||||
"ladenFastestRange": 64.2,
|
||||
"maxJumpCount": 4,
|
||||
"shieldStrength": 952
|
||||
}
|
||||
@@ -2374,8 +2374,8 @@
|
||||
"unladenRange": 39.26,
|
||||
"fullTankRange": 37.65,
|
||||
"ladenRange": 21.21,
|
||||
"unladenTotalRange": 153.79,
|
||||
"ladenTotalRange": 85.82,
|
||||
"unladenFastestRange": 153.79,
|
||||
"ladenFastestRange": 85.82,
|
||||
"maxJumpCount": 4,
|
||||
"shieldStrength": 372.98
|
||||
}
|
||||
@@ -2530,8 +2530,8 @@
|
||||
"unladenRange": 38.44,
|
||||
"fullTankRange": 36.89,
|
||||
"ladenRange": 21.04,
|
||||
"unladenTotalRange": 150.62,
|
||||
"ladenTotalRange": 85.16,
|
||||
"unladenFastestRange": 150.62,
|
||||
"ladenFastestRange": 85.16,
|
||||
"maxJumpCount": 4,
|
||||
"shieldStrength": 372.98
|
||||
}
|
||||
@@ -2698,8 +2698,8 @@
|
||||
"unladenRange": 38.04,
|
||||
"fullTankRange": 30.11,
|
||||
"ladenRange": 23.03,
|
||||
"unladenTotalRange": 675.7,
|
||||
"ladenTotalRange": 501.97,
|
||||
"unladenFastestRange": 675.7,
|
||||
"ladenFastestRange": 501.97,
|
||||
"maxJumpCount": 20,
|
||||
"shieldStrength": 372.98
|
||||
}
|
||||
@@ -2916,8 +2916,8 @@
|
||||
"unladenRange": 18.49,
|
||||
"fullTankRange": 18.12,
|
||||
"ladenRange": 16.39,
|
||||
"unladenTotalRange": 73.21,
|
||||
"ladenTotalRange": 66.15,
|
||||
"unladenFastestRange": 73.21,
|
||||
"ladenFastestRange": 66.15,
|
||||
"maxJumpCount": 4,
|
||||
"shieldStrength": 833
|
||||
}
|
||||
@@ -3044,8 +3044,8 @@
|
||||
"unladenRange": 35.99,
|
||||
"fullTankRange": 33.36,
|
||||
"ladenRange": 33.36,
|
||||
"unladenTotalRange": 232.28,
|
||||
"ladenTotalRange": 232.28,
|
||||
"unladenFastestRange": 232.28,
|
||||
"ladenFastestRange": 232.28,
|
||||
"maxJumpCount": 7,
|
||||
"shieldStrength": 92.25
|
||||
}
|
||||
@@ -3181,8 +3181,8 @@
|
||||
"unladenRange": 15.06,
|
||||
"fullTankRange": 14.86,
|
||||
"ladenRange": 14.86,
|
||||
"unladenTotalRange": 42.5,
|
||||
"ladenTotalRange": 42.5,
|
||||
"unladenFastestRange": 42.5,
|
||||
"ladenFastestRange": 42.5,
|
||||
"maxJumpCount": 3,
|
||||
"shieldStrength": 548.74
|
||||
}
|
||||
@@ -3335,8 +3335,8 @@
|
||||
"unladenRange": 12.51,
|
||||
"fullTankRange": 12.38,
|
||||
"ladenRange": 12.38,
|
||||
"unladenTotalRange": 35.35,
|
||||
"ladenTotalRange": 35.35,
|
||||
"unladenFastestRange": 35.35,
|
||||
"ladenFastestRange": 35.35,
|
||||
"maxJumpCount": 3,
|
||||
"shieldStrength": 760.16
|
||||
}
|
||||
@@ -3453,8 +3453,8 @@
|
||||
"unladenRange": 17.12,
|
||||
"fullTankRange": 16.71,
|
||||
"ladenRange": 16.71,
|
||||
"unladenTotalRange": 42.4,
|
||||
"ladenTotalRange": 42.4,
|
||||
"unladenFastestRange": 42.4,
|
||||
"ladenFastestRange": 42.4,
|
||||
"maxJumpCount": 3,
|
||||
"shieldStrength": 102
|
||||
}
|
||||
@@ -3611,8 +3611,8 @@
|
||||
"unladenRange": 8.43,
|
||||
"fullTankRange": 8.09,
|
||||
"ladenRange": 7.25,
|
||||
"unladenTotalRange": 81.5,
|
||||
"ladenTotalRange": 72.9,
|
||||
"unladenFastestRange": 81.5,
|
||||
"ladenFastestRange": 72.9,
|
||||
"maxJumpCount": 10,
|
||||
"shieldStrength": 299.48
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { getLanguage } from '../src/app/i18n/Language';
|
||||
|
||||
describe('Import Modal', function() {
|
||||
|
||||
let MockRouter = require('../src/app/Router');
|
||||
const Persist = require('../src/app/stores/Persist').default;
|
||||
const ModalImport = require('../src/app/components/ModalImport').default;
|
||||
const mockContext = {
|
||||
@@ -24,12 +25,15 @@ describe('Import Modal', function() {
|
||||
onWindowResize: jest.genMockFunction()
|
||||
};
|
||||
|
||||
MockRouter.go = jest.genMockFunction();
|
||||
|
||||
let modal, render, ContextProvider = Utils.createContextProvider(mockContext);
|
||||
|
||||
/**
|
||||
* Clear saved builds, and reset React DOM
|
||||
*/
|
||||
function reset() {
|
||||
MockRouter.go.mockClear();
|
||||
Persist.deleteAll();
|
||||
render = TU.renderIntoDocument(<ContextProvider><ModalImport /></ContextProvider>);
|
||||
modal = TU.findRenderedComponentWithType(render, ModalImport);
|
||||
@@ -135,16 +139,9 @@ describe('Import Modal', function() {
|
||||
|
||||
expect(modal.state.importValid).toBeTruthy();
|
||||
expect(modal.state.errorMsg).toEqual(null);
|
||||
|
||||
clickProceed();
|
||||
|
||||
expect(modal.state.processed).toBeTruthy();
|
||||
|
||||
clickImport();
|
||||
|
||||
expect(Persist.getBuilds()).toEqual({
|
||||
anaconda: { 'Test': '48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA' }
|
||||
});
|
||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda/48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA?bn=Test%20My%20Ship');
|
||||
});
|
||||
|
||||
it('catches an invalid build', function() {
|
||||
@@ -152,7 +149,7 @@ describe('Import Modal', function() {
|
||||
pasteText(JSON.stringify(importData).replace('components', 'comps'));
|
||||
|
||||
expect(modal.state.importValid).toBeFalsy();
|
||||
expect(modal.state.errorMsg).toEqual('Anaconda Build "Test": Invalid data');
|
||||
expect(modal.state.errorMsg).toEqual('Anaconda Build "Test My Ship": Invalid data');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -188,16 +185,13 @@ describe('Import Modal', function() {
|
||||
|
||||
for (let i = 0; i < imports.length; i++ ) {
|
||||
reset();
|
||||
pasteText(imports[i].buildText);
|
||||
let fixture = imports[i];
|
||||
pasteText(fixture.buildText);
|
||||
expect(modal.state.importValid).toBeTruthy();
|
||||
expect(modal.state.errorMsg).toEqual(null, 'Build #' + i + ': ' + imports[i].buildName);
|
||||
expect(modal.state.errorMsg).toEqual(null);
|
||||
clickProceed();
|
||||
expect(modal.state.processed).toBeTruthy();
|
||||
clickImport();
|
||||
let allBuilds = Persist.getBuilds();
|
||||
let shipBuilds = allBuilds ? allBuilds[imports[i].shipId] : null;
|
||||
let build = shipBuilds ? shipBuilds[imports[i].buildName] : null;
|
||||
expect(build).toEqual(imports[i].buildCode, 'Build #' + i + ': ' + imports[i].buildName);
|
||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/' + fixture.shipId + '/' + fixture.buildCode + '?bn=' + encodeURIComponent(fixture.buildName));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -4,32 +4,103 @@ import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import TU from 'react-testutils-additions';
|
||||
|
||||
xdescribe('Persist', function() {
|
||||
let origAddEventListener = window.addEventListener;
|
||||
let storageListener;
|
||||
let ls = {};
|
||||
|
||||
const Persist = require('../src/app/stores/Persist').default;
|
||||
// Implment mock localStorage
|
||||
let localStorage = {
|
||||
getItem: function(key) {
|
||||
return ls[key];
|
||||
},
|
||||
setItem: function(key, value) {
|
||||
ls[key] = value;
|
||||
},
|
||||
removeItem: function(key) {
|
||||
delete ls[key];
|
||||
},
|
||||
clear: function() {
|
||||
ls = {};
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener = function(eventName, listener) {
|
||||
|
||||
if(eventName == 'storage') {
|
||||
storageListener = listener; // Keep track of latest storage listener
|
||||
} else {
|
||||
origAddEventListener.apply(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
describe('Persist', function() {
|
||||
|
||||
const Persist = require('../src/app/stores/Persist').Persist;
|
||||
|
||||
describe('Builds', function() {
|
||||
it("loads from localStorage correctly", function() {
|
||||
|
||||
});
|
||||
|
||||
it("can save a build", function() {
|
||||
|
||||
});
|
||||
|
||||
it("can delete a build", function() {
|
||||
|
||||
});
|
||||
|
||||
it("works without localStorage", function() {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('Comparisons', function() {
|
||||
|
||||
it("loads from localStorage correctly", function() {
|
||||
|
||||
});
|
||||
|
||||
describe('Settings', function() {
|
||||
it("works without localStorage", function() {
|
||||
|
||||
it("has defaults", function() {
|
||||
expect(false).toBeTruthy('Implement');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Multi tab/window', function() {
|
||||
it.only("syncs builds", function() {
|
||||
window.localStorage = localStorage;
|
||||
|
||||
let p = new Persist();
|
||||
let newBuilds = {};
|
||||
|
||||
storageListener({ key: 'builds', newValue: JSON.stringify(newBuilds) });
|
||||
});
|
||||
});
|
||||
|
||||
describe('General and Settings', function() {
|
||||
it.only("has defaults", function() {
|
||||
let p = new Persist();
|
||||
expect(p.getLangCode()).toBe('en');
|
||||
expect(p.showTooltips()).toBe(true);
|
||||
expect(p.getInsurance()).toBe('standard');
|
||||
expect(p.getShipDiscount()).toBe(1);
|
||||
expect(p.getModuleDiscount()).toBe(1);
|
||||
expect(p.getSizeRatio()).toBe(1);
|
||||
});
|
||||
|
||||
it("loads from localStorage correctly", function() {
|
||||
expect(false).toBeTruthy('Implement');
|
||||
expect(false).toBeTruthy('TODO: Implement');
|
||||
});
|
||||
|
||||
it("uses defaults from a corrupted localStorage", function() {
|
||||
expect(false).toBeTruthy('TODO: Implement');
|
||||
});
|
||||
|
||||
it("works without localStorage", function() {
|
||||
|
||||
});
|
||||
|
||||
it("generates the backup", function() {
|
||||
expect(false).toBeTruthy('Implement');
|
||||
expect(false).toBeTruthy('TODO: Implement');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Ship from '../src/app/shipyard/Ship';
|
||||
import { Ships } from 'coriolis-data';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import * as Serializer from '../src/app/shipyard/Serializer';
|
||||
import jsen from 'jsen';
|
||||
|
||||
@@ -11,7 +11,7 @@ describe("Serializer", function() {
|
||||
|
||||
describe("To Detailed Build", function() {
|
||||
let testBuild = new Ship('anaconda', anaconda.properties, anaconda.slots).buildFrom(code);
|
||||
let exportData = Serializer.toDetailedBuild('Test', testBuild);
|
||||
let exportData = Serializer.toDetailedBuild('Test My Ship', testBuild);
|
||||
|
||||
it("conforms to the v3 ship-loadout schema", function() {
|
||||
expect(validate(exportData)).toBe(true);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Ship from '../src/app/shipyard/Ship';
|
||||
import { Ships } from 'coriolis-data';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import * as ModuleUtils from '../src/app/shipyard/ModuleUtils';
|
||||
|
||||
describe("Ship Factory", function() {
|
||||
@@ -22,8 +22,8 @@ describe("Ship Factory", function() {
|
||||
expect(ship.unladenRange).toBeGreaterThan(0, s + ' unladenRange');
|
||||
expect(ship.ladenRange).toBeGreaterThan(0, s + ' ladenRange');
|
||||
expect(ship.fuelCapacity).toBeGreaterThan(0, s + ' fuelCapacity');
|
||||
expect(ship.unladenTotalRange).toBeGreaterThan(0, s + ' unladenTotalRange');
|
||||
expect(ship.ladenTotalRange).toBeGreaterThan(0, s + ' ladenTotalRange');
|
||||
expect(ship.unladenFastestRange).toBeGreaterThan(0, s + ' unladenFastestRange');
|
||||
expect(ship.ladenFastestRange).toBeGreaterThan(0, s + ' ladenFastestRange');
|
||||
expect(ship.shieldStrength).toBeGreaterThan(0, s + ' shieldStrength');
|
||||
expect(ship.armour).toBeGreaterThan(0, s + ' armour');
|
||||
expect(ship.topSpeed).toBeGreaterThan(0, s + ' topSpeed');
|
||||
|
||||
@@ -37,7 +37,7 @@ http {
|
||||
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
|
||||
server {
|
||||
listen 3300;
|
||||
listen 3301;
|
||||
server_name localhost;
|
||||
root ./build/;
|
||||
index index.html;
|
||||
|
||||
14
package.json
14
package.json
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "coriolis_shipyard",
|
||||
"version": "2.0.0-alpha",
|
||||
"version": "2.0.1-Beta",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cmmcleod/coriolis"
|
||||
},
|
||||
"homepage": "http://coriolis.io",
|
||||
"homepage": "https://coriolis.io",
|
||||
"bugs": "https://github.com/cmmcleod/coriolis/issues",
|
||||
"private": true,
|
||||
"engine": "node >= 4.0.0",
|
||||
@@ -17,9 +17,8 @@
|
||||
"test": "jest",
|
||||
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
|
||||
"prod-stop": "kill -QUIT $(cat nginx.pid)",
|
||||
"build:prod": "npm run clean && NODE_ENV=production CDN='//cdn.coriolis.io' webpack -d -p --config webpack.config.prod.js",
|
||||
"build": "npm run clean && NODE_ENV=production webpack -d -p --config webpack.config.prod.js",
|
||||
"rsync": "rsync -e 'ssh -i $CORIOLIS_PEM' -a --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/www",
|
||||
"rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws",
|
||||
"deploy": "npm run lint && npm test && npm run build:prod && npm run rsync"
|
||||
},
|
||||
"jest": {
|
||||
@@ -73,7 +72,7 @@
|
||||
"json-loader": "^0.5.3",
|
||||
"less": "^2.5.3",
|
||||
"less-loader": "^2.2.1",
|
||||
"react-addons-test-utils": "^0.14.6",
|
||||
"react-addons-test-utils": "^0.14.7",
|
||||
"react-testutils-additions": "^0.16.0",
|
||||
"rimraf": "^2.4.3",
|
||||
"style-loader": "^0.13.0",
|
||||
@@ -84,11 +83,12 @@
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.3.14",
|
||||
"classnames": "^2.2.0",
|
||||
"coriolis-data": "cmmcleod/coriolis-data",
|
||||
"d3": "^3.5.9",
|
||||
"fbemitter": "^2.0.0",
|
||||
"lz-string": "^1.4.4",
|
||||
"react": "^0.14.6",
|
||||
"react-dom": "^0.14.6",
|
||||
"react": "^0.14.7",
|
||||
"react-dom": "^0.14.7",
|
||||
"superagent": "^1.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@ import Persist from './stores/Persist';
|
||||
|
||||
import Header from './components/Header';
|
||||
import Tooltip from './components/Tooltip';
|
||||
import ModalImport from './components/ModalImport';
|
||||
|
||||
import AboutPage from './pages/AboutPage';
|
||||
import NotFoundPage from './pages/NotFoundPage';
|
||||
import OutfittingPage from './pages/OutfittingPage';
|
||||
import ComparisonPage from './pages/ComparisonPage';
|
||||
import ShipyardPage from './pages/ShipyardPage';
|
||||
import ErrorDetails from './pages/ErrorDetails';
|
||||
|
||||
/**
|
||||
* Coriolis App
|
||||
@@ -28,7 +30,8 @@ export default class Coriolis extends React.Component {
|
||||
hideModal: React.PropTypes.func.isRequired,
|
||||
tooltip: React.PropTypes.func.isRequired,
|
||||
termtip: React.PropTypes.func.isRequired,
|
||||
onWindowResize: React.PropTypes.func.isRequired
|
||||
onWindowResize: React.PropTypes.func.isRequired,
|
||||
onCommand: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -44,6 +47,7 @@ export default class Coriolis extends React.Component {
|
||||
this._tooltip = this._tooltip.bind(this);
|
||||
this._termtip = this._termtip.bind(this);
|
||||
this._onWindowResize = this._onWindowResize.bind(this);
|
||||
this._onCommand = this._onCommand.bind(this);
|
||||
this._onLanguageChange = this._onLanguageChange.bind(this);
|
||||
this._onSizeRatioChange = this._onSizeRatioChange.bind(this);
|
||||
this._keyDown = this._keyDown.bind(this);
|
||||
@@ -70,12 +74,15 @@ export default class Coriolis extends React.Component {
|
||||
* @param {Object} route The current route
|
||||
*/
|
||||
_setPage(page, route) {
|
||||
this.setState({ page, route, currentMenu: null });
|
||||
this.setState({ page, route, currentMenu: null, modal: null, error: null });
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle unexpected error
|
||||
* TODO: Implement and fix to work with Webpack (dev + prod)
|
||||
* Handle unexpected error. This is most likely an unhandled React Error which
|
||||
* is also most likely unrecoverable. The best option is to catch as many details
|
||||
* as possible so the user can report the error and provide a link to reload the page
|
||||
* to reset the VM and clear any error state.
|
||||
*
|
||||
* @param {string} msg Message
|
||||
* @param {string} scriptUrl URL
|
||||
* @param {number} line Line number
|
||||
@@ -83,8 +90,16 @@ export default class Coriolis extends React.Component {
|
||||
* @param {Object} errObj Error Object
|
||||
*/
|
||||
_onError(msg, scriptUrl, line, col, errObj) {
|
||||
console.log('WINDOW ERROR', arguments);
|
||||
// this._setPage(<div>Some errors occured!!</div>);
|
||||
console && console.error && console.error(arguments); // eslint-disable-line no-console
|
||||
this.setState({
|
||||
error: <ErrorDetails error={{ message: msg, details: { scriptUrl, line, col, error: JSON.stringify(errObj) } }}/>,
|
||||
page: null,
|
||||
currentMenu: null,
|
||||
modal: null
|
||||
});
|
||||
// TODO: Improve in the event of React Errors
|
||||
// Potentially ReactDOM.render into dom here instead
|
||||
// ReactDOM.render(this, document.getElementById('coriolis'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,11 +123,23 @@ export default class Coriolis extends React.Component {
|
||||
* @param {Event} e Keyboard Event
|
||||
*/
|
||||
_keyDown(e) {
|
||||
// .keyCode will eventually be replaced with .key
|
||||
switch (e.keyCode) {
|
||||
case 27:
|
||||
case 27: // Escape Key
|
||||
this._hideModal();
|
||||
this._closeMenu();
|
||||
break;
|
||||
case 73: // 'i'
|
||||
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + i
|
||||
e.preventDefault();
|
||||
this._showModal(<ModalImport />);
|
||||
}
|
||||
break;
|
||||
case 101010: // 's'
|
||||
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + i
|
||||
e.preventDefault();
|
||||
this.emitter.emit('command', 'save');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +148,7 @@ export default class Coriolis extends React.Component {
|
||||
* @param {React.Component} content Modal Content
|
||||
*/
|
||||
_showModal(content) {
|
||||
let modal = <div className='modal-bg' onTouchTap={(e) => this._hideModal() }>{content}</div>;
|
||||
let modal = <div className='modal-bg' onClick={(e) => this._hideModal() }>{content}</div>;
|
||||
this.setState({ modal });
|
||||
}
|
||||
|
||||
@@ -169,16 +196,20 @@ export default class Coriolis extends React.Component {
|
||||
|
||||
/**
|
||||
* Show the term tip
|
||||
* @param {string} term Term
|
||||
* @param {[type]} orientation Tooltip orientation (n,e,s,w)
|
||||
* @param {string} term Term or Phrase
|
||||
* @param {Object} opts Options - dontCap, orientation (n,e,s,w)
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_termtip(term, orientation, event) {
|
||||
if (typeof orientation != 'string') {
|
||||
event = orientation;
|
||||
orientation = null;
|
||||
_termtip(term, opts, event) {
|
||||
if (opts && opts.nativeEvent) { // Opts is a SyntheticEvent
|
||||
event = opts;
|
||||
opts = { cap: true };
|
||||
}
|
||||
this._tooltip(<div className='cap cen'>{this.state.language.translate(term)}</div>, event.currentTarget.getBoundingClientRect(), { orientation });
|
||||
this._tooltip(
|
||||
<div className={'cen' + (opts.cap ? ' cap' : '')}>{this.state.language.translate(term)}</div>,
|
||||
event.currentTarget.getBoundingClientRect(),
|
||||
opts
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,6 +221,15 @@ export default class Coriolis extends React.Component {
|
||||
return this.emitter.addListener('windowResize', listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener to global commands such as save,
|
||||
* @param {Function} listener Listener callback
|
||||
* @return {Object} Subscription token
|
||||
*/
|
||||
_onCommand(listener) {
|
||||
return this.emitter.addListener('command', listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the context to be passed down to pages / components containing
|
||||
* language, sizeRatio and route references
|
||||
@@ -206,7 +246,8 @@ export default class Coriolis extends React.Component {
|
||||
hideModal: this._hideModal,
|
||||
tooltip: this._tooltip,
|
||||
termtip: this._termtip,
|
||||
onWindowResize: this._onWindowResize
|
||||
onWindowResize: this._onWindowResize,
|
||||
onCommand: this._onCommand
|
||||
};
|
||||
}
|
||||
|
||||
@@ -238,9 +279,9 @@ export default class Coriolis extends React.Component {
|
||||
* @return {React.Component} The main app
|
||||
*/
|
||||
render() {
|
||||
return <div onTouchTap={this._closeMenu}>
|
||||
return <div onClick={this._closeMenu}>
|
||||
<Header appCacheUpdate={this.state.appCacheUpdate} currentMenu={this.state.currentMenu} />
|
||||
{ this.state.page ? <this.state.page currentMenu={this.state.currentMenu} /> : <NotFoundPage/> }
|
||||
{ this.state.error ? this.state.error : this.state.page ? <this.state.page currentMenu={this.state.currentMenu} /> : <NotFoundPage/> }
|
||||
{ this.state.modal }
|
||||
{ this.state.tooltip }
|
||||
</div>;
|
||||
|
||||
@@ -9,7 +9,7 @@ import cn from 'classnames';
|
||||
* @return {boolean} If matches
|
||||
*/
|
||||
function isActive(href) {
|
||||
return encodeURI(href) == (window.location.pathname + window.location.search);
|
||||
return href == (window.location.pathname + window.location.search);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -22,13 +22,12 @@ export default class ActiveLink extends Link {
|
||||
* @return {React.Component} The active link
|
||||
*/
|
||||
render() {
|
||||
let action = this.handler.bind(this);
|
||||
let className = this.props.className;
|
||||
if (isActive(this.props.href)) {
|
||||
className = cn(className, 'active');
|
||||
}
|
||||
|
||||
return <a {...this.props} className={className} onTouchTap={action}>{this.props.children}</a>;
|
||||
return <a {...this.props} className={className} onClick={this.handler}>{this.props.children}</a>;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
import cn from 'classnames';
|
||||
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||
|
||||
const PRESS_THRESHOLD = 5000; // mouse/touch down threshold
|
||||
|
||||
/**
|
||||
* Available modules menu
|
||||
*/
|
||||
@@ -31,20 +33,19 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
this._hideDiff = this._hideDiff.bind(this);
|
||||
this._diffMove = this._diffMove.bind(this);
|
||||
this.state = { list: this._initList(props, context) };
|
||||
this.state = this._initState(props, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate the list of available moduels
|
||||
* @param {Object} props React Component properties
|
||||
* @param {Object} context React Component context
|
||||
* @return {Array} Array of React Components
|
||||
* @return {Object} list: Array of React Components, currentGroup Component if any
|
||||
*/
|
||||
_initList(props, context) {
|
||||
_initState(props, context) {
|
||||
let translate = context.language.translate;
|
||||
let { m, warning, shipMass, onSelect, modules } = props;
|
||||
let list;
|
||||
let list, currentGroup;
|
||||
let buildGroup = this._buildGroup.bind(
|
||||
this,
|
||||
translate,
|
||||
@@ -62,14 +63,19 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
} else {
|
||||
list = [];
|
||||
// At present time slots with grouped options (Hardpoints and Internal) can be empty
|
||||
list.push(<div className={'empty-c upp'} key={'empty'} onClick={onSelect.bind(null, null)} >{translate('empty')}</div>);
|
||||
list.push(<div className='empty-c upp' key='empty' onClick={onSelect.bind(null, null)} >{translate('empty')}</div>);
|
||||
for (let g in modules) {
|
||||
list.push(<div ref={g} key={g} className={'select-group cap'}>{translate(g)}</div>);
|
||||
if (m && g == m.grp) {
|
||||
list.push(<div ref={(elem) => this.groupElem = elem} key={g} className={'select-group cap'}>{translate(g)}</div>);
|
||||
} else {
|
||||
list.push(<div key={g} className={'select-group cap'}>{translate(g)}</div>);
|
||||
}
|
||||
|
||||
list.push(buildGroup(g, modules[g]));
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
return { list, currentGroup };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,6 +103,22 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
active,
|
||||
disabled
|
||||
});
|
||||
let eventHandlers;
|
||||
|
||||
if (disabled || active) {
|
||||
eventHandlers = {};
|
||||
} else {
|
||||
let showDiff = this._showDiff.bind(this, mountedModule, m);
|
||||
let select = onSelect.bind(null, m);
|
||||
|
||||
eventHandlers = {
|
||||
onMouseEnter: this._over.bind(this, showDiff),
|
||||
onTouchStart: this._touchStart.bind(this, showDiff),
|
||||
onTouchEnd: this._touchEnd.bind(this, select),
|
||||
onMouseLeave: this._hideDiff,
|
||||
onClick: select
|
||||
};
|
||||
}
|
||||
|
||||
switch(m.mount) {
|
||||
case 'F': mount = <MountFixed className={'lg'} />; break;
|
||||
@@ -108,19 +130,10 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
elems.push(<br key={m.grp + i} />);
|
||||
}
|
||||
|
||||
let showDiff = disabled || active ? null : this._showDiff.bind(this, mountedModule, m);
|
||||
|
||||
elems.push(
|
||||
<li
|
||||
key={m.id}
|
||||
className={classes}
|
||||
onMouseEnter={showDiff}
|
||||
onTouchStart={showDiff}
|
||||
onMouseLeave={this._hideDiff}
|
||||
onClick={disabled ? null : onSelect.bind(null, m)}
|
||||
>
|
||||
<li key={m.id} className={classes} {...eventHandlers}>
|
||||
{mount}
|
||||
<span>{(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')}</span>
|
||||
{(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')}
|
||||
</li>
|
||||
);
|
||||
prevClass = m.class;
|
||||
@@ -135,22 +148,46 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
* mounted module and the hovered modules
|
||||
* @param {Object} mm The module mounet currently
|
||||
* @param {Object} m The hovered module
|
||||
* @param {DOMRect} rect DOMRect for target element
|
||||
*/
|
||||
_showDiff(mm, m, rect) {
|
||||
if (this.props.diffDetails) {
|
||||
this.touchTimeout = null;
|
||||
this.context.tooltip(this.props.diffDetails(m, mm), rect);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mouse over diff handler
|
||||
* @param {Function} showDiff diff tooltip callback
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_showDiff(mm, m, event) {
|
||||
_over(showDiff, event) {
|
||||
event.preventDefault();
|
||||
if (this.props.diffDetails) {
|
||||
this.context.tooltip(this.props.diffDetails(m, mm), event.currentTarget.getBoundingClientRect());
|
||||
}
|
||||
showDiff(event.currentTarget.getBoundingClientRect());
|
||||
}
|
||||
|
||||
_touchStart(event) {
|
||||
event.preventDefault();
|
||||
console.log(Object.assign({}, event));
|
||||
/**
|
||||
* Toucch Start - Show diff after press, otherwise treat as tap
|
||||
* @param {Function} showDiff diff tooltip callback
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_touchStart(showDiff, event) {
|
||||
let rect = event.currentTarget.getBoundingClientRect();
|
||||
this.touchTimeout = setTimeout(showDiff.bind(this, rect), PRESS_THRESHOLD);
|
||||
}
|
||||
|
||||
_diffMove(event) {
|
||||
console.log(Object.assign({}, event));
|
||||
/**
|
||||
* Touch End - Select module on tap
|
||||
* @param {Function} select Select module callback
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_touchEnd(select, event) {
|
||||
if (this.touchTimeout !== null) { // If timeout has not fired (been nulled out) yet
|
||||
select();
|
||||
}
|
||||
event.preventDefault();
|
||||
this._hideDiff();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,18 +195,17 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_hideDiff(event) {
|
||||
event.preventDefault();
|
||||
clearTimeout(this.touchTimeout);
|
||||
this.touchTimeout = null;
|
||||
this.context.tooltip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to mounted (if it exists) component on mount
|
||||
* Scroll to mounted (if it exists) module group on mount
|
||||
*/
|
||||
componentDidMount() {
|
||||
let m = this.props.m;
|
||||
|
||||
if (!(this.props.modules instanceof Array) && m && m.grp) {
|
||||
findDOMNode(this).scrollTop = this.refs[m.grp].offsetTop; // Scroll to currently selected group
|
||||
if (this.groupElem) { // Scroll to currently selected group
|
||||
findDOMNode(this).scrollTop = this.groupElem.offsetTop;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +215,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
* @param {Object} nextContext Incoming/Next conext
|
||||
*/
|
||||
componentWillReceiveProps(nextProps, nextContext) {
|
||||
this.setState({ list: this._initList(nextProps, nextContext) });
|
||||
this.setState(this._initState(nextProps, nextContext));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,9 +228,6 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
className={cn('select', this.props.className)}
|
||||
onScroll={this._hideDiff}
|
||||
onClick={(e) => e.stopPropagation() }
|
||||
onTouchStart={this._touchStart}
|
||||
onTouchEnd={this._hideDiff}
|
||||
onTouchCancel={this._hideDiff}
|
||||
onContextMenu={stopCtxPropagation}
|
||||
>
|
||||
{this.state.list}
|
||||
|
||||
@@ -40,18 +40,21 @@ export default class BarChart extends TranslatedComponent {
|
||||
|
||||
static defaultProps = {
|
||||
colors: ['#7b6888', '#6b486b', '#3182bd', '#a05d56', '#d0743c'],
|
||||
labels: null,
|
||||
unit: ''
|
||||
};
|
||||
|
||||
static PropTypes = {
|
||||
data: React.PropTypes.array.isRequired,
|
||||
width: React.PropTypes.number.isRequired,
|
||||
format: React.PropTypes.string.isRequired,
|
||||
label: React.PropTypes.string.isRequired,
|
||||
unit: React.PropTypes.string.isRequired,
|
||||
colors: React.PropTypes.array,
|
||||
data: React.PropTypes.array.isRequired,
|
||||
desc: React.PropTypes.bool,
|
||||
format: React.PropTypes.string.isRequired,
|
||||
labels: React.PropTypes.array,
|
||||
predicate: React.PropTypes.string,
|
||||
desc: React.PropTypes.bool
|
||||
properties: React.PropTypes.array,
|
||||
title: React.PropTypes.string.isRequired,
|
||||
unit: React.PropTypes.string.isRequired,
|
||||
width: React.PropTypes.number.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -78,23 +81,28 @@ export default class BarChart extends TranslatedComponent {
|
||||
* Generate and Show tooltip
|
||||
* @param {Object} build Ship build
|
||||
* @param {string} property Property to display
|
||||
* @param {number} propertyIndex Property Label index
|
||||
*/
|
||||
_showTip(build, property) {
|
||||
let { unit, format } = this.props;
|
||||
_showTip(build, property, propertyIndex) {
|
||||
let { unit, format, labels } = this.props;
|
||||
let { scale, y0, y1 } = this.state;
|
||||
let { formats } = this.context.language;
|
||||
let { translate, formats } = this.context.language;
|
||||
let fontSize = parseFloat(window.getComputedStyle(document.getElementById('coriolis')).getPropertyValue('font-size') || 16);
|
||||
let val = build[property];
|
||||
let lblStr = labels ? translate(labels[propertyIndex]) + ': ' : '';
|
||||
let valStr = formats[format](val) + ' ' + unit;
|
||||
let width = (valStr.length / 1.7) * fontSize;
|
||||
let midPoint = width / 2;
|
||||
let valMidPoint = scale(val) / 2;
|
||||
let y = y0(bName(build)) + y1(property) - fontSize - 5;
|
||||
let width = ((lblStr.length + valStr.length) / 1.8) * fontSize;
|
||||
let midPoint = width / 2;
|
||||
|
||||
let tooltip = <g>
|
||||
<g transform={`translate(${Math.max(0, valMidPoint - midPoint)},${y})`}>
|
||||
<rect className='primary-disabled' height={fontSize} width={width} />
|
||||
<text x={midPoint} y={fontSize} dy='-0.4em' style={{ textAnchor: 'middle', fontSize: '0.7em' }}>{valStr}</text>
|
||||
<text x={midPoint} y={fontSize} dy={fontSize / -4} style={{ textAnchor: 'middle', fontSize: '0.7em' }}>
|
||||
<tspan style={{ textTransform: 'capitalize' }}>{lblStr}</tspan>
|
||||
<tspan>{valStr}</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<path className='primary-disabled' d='M0,0L5,5L10,0Z' dy='1em' transform={`translate(${Math.max(0, valMidPoint - 5)},${y + fontSize})`} />
|
||||
</g>;
|
||||
@@ -173,12 +181,12 @@ export default class BarChart extends TranslatedComponent {
|
||||
return null;
|
||||
}
|
||||
|
||||
let { label, unit, width, data, properties } = this.props;
|
||||
let { title, unit, width, data, properties } = this.props;
|
||||
let { innerWidth, outerHeight, innerHeight, y0, y1, scale, color, tooltip } = this.state;
|
||||
|
||||
let bars = data.map((build, i) =>
|
||||
<g key={i} transform={`translate(0,${y0(bName(build))})`}>
|
||||
{ properties.map((p) =>
|
||||
{ properties.map((p, propIndex) =>
|
||||
<rect
|
||||
key={p}
|
||||
x={0}
|
||||
@@ -186,7 +194,7 @@ export default class BarChart extends TranslatedComponent {
|
||||
width={scale(build[p])}
|
||||
height={y1.rangeBand()}
|
||||
fill={color(p)}
|
||||
onMouseOver={this._showTip.bind(this, build, p)}
|
||||
onMouseOver={this._showTip.bind(this, build, p, propIndex)}
|
||||
onMouseOut={this._hideTip}
|
||||
/>
|
||||
)}
|
||||
@@ -199,7 +207,7 @@ export default class BarChart extends TranslatedComponent {
|
||||
{tooltip}
|
||||
<g className='x axis' ref={(elem) => d3.select(elem).call(this.xAxis)} transform={`translate(0,${innerHeight})`}>
|
||||
<text className='cap' y='30' dy='.1em' x={innerWidth / 2} style={{ textAnchor: 'middle' }}>
|
||||
<tspan>{label}</tspan>
|
||||
<tspan>{title}</tspan>
|
||||
{ unit ? <tspan className='metric'>{` (${unit})`}</tspan> : null }
|
||||
</text>
|
||||
</g>
|
||||
|
||||
@@ -38,8 +38,8 @@ export default class ComparisonTable extends TranslatedComponent {
|
||||
*/
|
||||
_buildHeaders(facets, onSort, translate) {
|
||||
let header = [
|
||||
<th key='ship' rowSpan='2' className='sortable' onTouchTap={onSort.bind(null, 'name')}>{translate('ship')}</th>,
|
||||
<th key='build' rowSpan='2' className='sortable' onTouchTap={onSort.bind(null, 'buildName')}>{translate('build')}</th>
|
||||
<th key='ship' rowSpan='2' className='sortable' onClick={onSort.bind(null, 'name')}>{translate('ship')}</th>,
|
||||
<th key='build' rowSpan='2' className='sortable' onClick={onSort.bind(null, 'buildName')}>{translate('build')}</th>
|
||||
];
|
||||
let subHeader = [];
|
||||
|
||||
@@ -47,13 +47,13 @@ export default class ComparisonTable extends TranslatedComponent {
|
||||
if (f.active) {
|
||||
let p = f.props;
|
||||
let pl = p.length;
|
||||
header.push(<th key={f.title} rowSpan={pl === 1 ? 2 : 1} colSpan={pl} className={cn({ sortable: pl === 1 })} onTouchTap={pl === 1 ? onSort.bind(null, p[0]) : null }>
|
||||
header.push(<th key={f.title} rowSpan={pl === 1 ? 2 : 1} colSpan={pl} className={cn({ sortable: pl === 1 })} onClick={pl === 1 ? onSort.bind(null, p[0]) : null }>
|
||||
{translate(f.title)}
|
||||
</th>);
|
||||
|
||||
if (pl > 1) {
|
||||
for (let i = 0; i < pl; i++) {
|
||||
subHeader.push(<th key={p[i]} className={cn('sortable', { lft: i === 0 })} onTouchTap={onSort.bind(null, p[i])} >{translate(f.lbls[i])}</th>);
|
||||
subHeader.push(<th key={p[i]} className={cn('sortable', { lft: i === 0 })} onClick={onSort.bind(null, p[i])} >{translate(f.lbls[i])}</th>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import { Ships } from 'coriolis-data';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Persist from '../stores/Persist';
|
||||
import Ship from '../shipyard/Ship';
|
||||
import { Insurance } from '../shipyard/Constants';
|
||||
@@ -46,7 +46,6 @@ export default class CostSection extends TranslatedComponent {
|
||||
retrofitName,
|
||||
shipDiscount,
|
||||
moduleDiscount,
|
||||
total: props.ship.totalCost,
|
||||
insurance: Insurance[Persist.getInsurance()],
|
||||
tab: Persist.getCostTab(),
|
||||
buildOptions: Persist.getBuildsNamesFor(props.ship.id),
|
||||
@@ -108,6 +107,7 @@ export default class CostSection extends TranslatedComponent {
|
||||
let moduleDiscount = Persist.getModuleDiscount();
|
||||
this.props.ship.applyDiscounts(shipDiscount, moduleDiscount);
|
||||
this.state.retrofitShip.applyDiscounts(shipDiscount, moduleDiscount);
|
||||
this._updateRetrofit(this.props.ship, this.state.retrofitShip);
|
||||
this.setState({ shipDiscount, moduleDiscount });
|
||||
}
|
||||
|
||||
@@ -137,32 +137,28 @@ export default class CostSection extends TranslatedComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* On build save
|
||||
* @param {string} shipId Ship Id
|
||||
* @param {string} name Build name
|
||||
* @param {string} code Serialized ship 'code'
|
||||
* On builds changed check to see if the retrofit ship needs
|
||||
* to be updated
|
||||
*/
|
||||
_onBuildSaved(shipId, name, code) {
|
||||
if(this.state.retrofitName == name) {
|
||||
this.state.retrofitShip.buildFrom(code); // Repopulate modules from saved build
|
||||
this._updateRetrofit(this.props.ship, this.state.retrofitShip);
|
||||
} else {
|
||||
this.setState({ buildOptions: Persist.getBuildsNamesFor(this.props.shipId) });
|
||||
}
|
||||
_onBuildsChanged() {
|
||||
let update = false;
|
||||
let ship = this.props.ship;
|
||||
let { retrofitName, retrofitShip } = this.state;
|
||||
|
||||
if(!Persist.hasBuild(ship.id, retrofitName)) {
|
||||
retrofitShip.buildWith(Ships[ship.id].defaults); // Retrofit ship becomes stock build
|
||||
this.setState({ retrofitName: null });
|
||||
update = true;
|
||||
} else if (Persist.getBuild(ship.id, retrofitName) != retrofitShip.toString()) {
|
||||
retrofitShip.buildFrom(Persist.getBuild(ship.id, retrofitName)); // Repopulate modules from saved build
|
||||
update = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* On build deleted
|
||||
* @param {string} shipId Ship Id
|
||||
* @param {string} name Build name
|
||||
* @param {string} code Serialized ship 'code'
|
||||
*/
|
||||
_onBuildDeleted(shipId, name, code) {
|
||||
if(this.state.retrofitName == name) {
|
||||
this.state.retrofitShip.buildWith(Ships[shipId].defaults); // Retrofit ship becomes stock build
|
||||
this.setState({ retrofitName: null });
|
||||
if (update) { // Update retrofit comparison
|
||||
this._updateRetrofit(ship, retrofitShip);
|
||||
}
|
||||
this.setState({ buildOptions: Persist.getBuildsNamesFor(shipId) });
|
||||
// Update list of retrofit base build options
|
||||
this.setState({ buildOptions: Persist.getBuildsNamesFor(ship.id) });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,7 +167,7 @@ export default class CostSection extends TranslatedComponent {
|
||||
*/
|
||||
_toggleCost(item) {
|
||||
this.props.ship.setCostIncluded(item, !item.incCost);
|
||||
this.setState({ total: this.props.ship.totalCost });
|
||||
this.forceUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,7 +282,7 @@ export default class CostSection extends TranslatedComponent {
|
||||
*/
|
||||
_costsTab() {
|
||||
let { ship } = this.props;
|
||||
let { total, shipDiscount, moduleDiscount, insurance } = this.state;
|
||||
let { shipDiscount, moduleDiscount, insurance } = this.state;
|
||||
let { translate, formats, units } = this.context.language;
|
||||
let rows = [];
|
||||
|
||||
@@ -295,9 +291,9 @@ export default class CostSection extends TranslatedComponent {
|
||||
if (item.m && item.m.cost) {
|
||||
let toggle = this._toggleCost.bind(this, item);
|
||||
rows.push(<tr key={i} className={cn('highlight', { disabled: !item.incCost })}>
|
||||
<td className='ptr' style={{ width: '1em' }} onTouchTap={toggle}>{item.m.class + item.m.rating}</td>
|
||||
<td className='le ptr shorten cap' onTouchTap={toggle}>{slotName(translate, item)}</td>
|
||||
<td className='ri ptr' onTouchTap={toggle}>{formats.int(item.discountedCost)}{units.CR}</td>
|
||||
<td className='ptr' style={{ width: '1em' }} onClick={toggle}>{item.m.class + item.m.rating}</td>
|
||||
<td className='le ptr shorten cap' onClick={toggle}>{slotName(translate, item)}</td>
|
||||
<td className='ri ptr' onClick={toggle}>{formats.int(item.discountedCost)}{units.CR}</td>
|
||||
</tr>);
|
||||
}
|
||||
}
|
||||
@@ -306,23 +302,23 @@ export default class CostSection extends TranslatedComponent {
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
||||
<thead>
|
||||
<tr className='main'>
|
||||
<th colSpan='2' className='sortable le' onTouchTap={this._sortCostBy.bind(this,'m')}>
|
||||
<th colSpan='2' className='sortable le' onClick={this._sortCostBy.bind(this,'m')}>
|
||||
{translate('component')}
|
||||
{shipDiscount < 1 && <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('ship')} -${formats.pct1(1 - shipDiscount)}]`}</u>}
|
||||
{moduleDiscount < 1 && <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('modules')} -${formats.pct1(1 - moduleDiscount)}]`}</u>}
|
||||
</th>
|
||||
<th className='sortable le' onTouchTap={this._sortCostBy.bind(this, 'cr')} >{translate('credits')}</th>
|
||||
<th className='sortable le' onClick={this._sortCostBy.bind(this, 'cr')} >{translate('credits')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows}
|
||||
<tr className='ri'>
|
||||
<td colSpan='2' className='lbl' >{translate('total')}</td>
|
||||
<td className='val'>{formats.int(total)}{units.CR}</td>
|
||||
<td className='val'>{formats.int(ship.totalCost)}{units.CR}</td>
|
||||
</tr>
|
||||
<tr className='ri'>
|
||||
<td colSpan='2' className='lbl'>{translate('insurance')}</td>
|
||||
<td className='val'>{formats.int(total * insurance)}{units.CR}</td>
|
||||
<td className='val'>{formats.int(ship.totalCost * insurance)}{units.CR}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -346,12 +342,12 @@ export default class CostSection extends TranslatedComponent {
|
||||
if (retrofitCosts.length) {
|
||||
for (let i = 0, l = retrofitCosts.length; i < l; i++) {
|
||||
let item = retrofitCosts[i];
|
||||
rows.push(<tr key={i} className={cn('highlight', { disabled: !item.retroItem.incCost })} onTouchTap={this._toggleRetrofitCost.bind(this, item)}>
|
||||
<td style={{ width: '1em' }}>{item.sellClassRating}</td>
|
||||
<td className='le shorten cap'>{translate(item.sellName)}</td>
|
||||
<td style={{ width: '1em' }}>{item.buyClassRating}</td>
|
||||
<td className='le shorten cap'>{translate(item.buyName)}</td>
|
||||
<td colSpan='2' className={cn('ri', item.retroItem.incCost ? item.netCost > 0 ? 'warning' : 'secondary-disabled' : 'disabled')}>{int(item.netCost)}{units.CR}</td>
|
||||
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='le ptr shorten cap'>{translate(item.sellName)}</td>
|
||||
<td className='ptr' style={{ width: '1em' }}>{item.buyClassRating}</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>
|
||||
</tr>);
|
||||
}
|
||||
} else {
|
||||
@@ -363,11 +359,11 @@ export default class CostSection extends TranslatedComponent {
|
||||
<table style={{ width: '100%' }}>
|
||||
<thead>
|
||||
<tr className='main'>
|
||||
<th colSpan='2' className='sortable le' onTouchTap={this._sortRetrofitBy.bind(this, 'sellName')}>{translate('sell')}</th>
|
||||
<th colSpan='2' className='sortable le' onTouchTap={this._sortRetrofitBy.bind(this, 'buyName')}>{translate('buy')}</th>
|
||||
<th colSpan='2' className='sortable le' onTouchTap={this._sortRetrofitBy.bind(this, 'cr')}>
|
||||
<th colSpan='2' className='sortable le' onClick={this._sortRetrofitBy.bind(this, 'sellName')}>{translate('sell')}</th>
|
||||
<th colSpan='2' className='sortable le' onClick={this._sortRetrofitBy.bind(this, 'buyName')}>{translate('buy')}</th>
|
||||
<th colSpan='2' className='sortable le' onClick={this._sortRetrofitBy.bind(this, 'cr')}>
|
||||
{translate('net cost')}
|
||||
{moduleDiscount < 1 && <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('modules')} -${formats.rPct(1 - moduleDiscount)}]`}</u>}
|
||||
{moduleDiscount < 1 && <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('modules')} -${formats.pct1(1 - moduleDiscount)}]`}</u>}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -473,10 +469,10 @@ export default class CostSection extends TranslatedComponent {
|
||||
<table style={{ width: '100%' }}>
|
||||
<thead>
|
||||
<tr className='main'>
|
||||
<th colSpan='2' className='sortable le' onTouchTap={this._sortAmmoBy.bind(this, 'm')} >{translate('module')}</th>
|
||||
<th colSpan='1' className='sortable le' onTouchTap={this._sortAmmoBy.bind(this, 'max')} >{translate('qty')}</th>
|
||||
<th colSpan='1' className='sortable le' onTouchTap={this._sortAmmoBy.bind(this, 'cost')} >{translate('unit cost')}</th>
|
||||
<th className='sortable le' onTouchTap={this._sortAmmoBy.bind(this, 'total')}>{translate('total cost')}</th>
|
||||
<th colSpan='2' className='sortable le' onClick={this._sortAmmoBy.bind(this, 'm')} >{translate('module')}</th>
|
||||
<th colSpan='1' className='sortable le' onClick={this._sortAmmoBy.bind(this, 'max')} >{translate('qty')}</th>
|
||||
<th colSpan='1' className='sortable le' onClick={this._sortAmmoBy.bind(this, 'cost')} >{translate('unit cost')}</th>
|
||||
<th className='sortable le' onClick={this._sortAmmoBy.bind(this, 'total')}>{translate('total cost')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -583,8 +579,7 @@ export default class CostSection extends TranslatedComponent {
|
||||
this.listeners = [
|
||||
Persist.addListener('discounts', this._onDiscountChanged.bind(this)),
|
||||
Persist.addListener('insurance', this._onInsuranceChanged.bind(this)),
|
||||
Persist.addListener('buildSaved', this._onBuildSaved.bind(this)),
|
||||
Persist.addListener('buildDeleted', this._onBuildDeleted.bind(this))
|
||||
Persist.addListener('builds', this._onBuildsChanged.bind(this)),
|
||||
];
|
||||
this._updateAmmoCosts(this.props.ship);
|
||||
this._updateRetrofit(this.props.ship, this.state.retrofitShip);
|
||||
@@ -613,7 +608,6 @@ export default class CostSection extends TranslatedComponent {
|
||||
if (nextProps.ship != this.props.ship || nextProps.code != this.props.code) {
|
||||
this._updateAmmoCosts(nextProps.ship);
|
||||
this._updateRetrofit(nextProps.ship, retrofitShip);
|
||||
this.setState({ total: nextProps.ship.totalCost });
|
||||
this._sortCost(nextProps.ship);
|
||||
}
|
||||
}
|
||||
@@ -673,9 +667,9 @@ export default class CostSection extends TranslatedComponent {
|
||||
<table className='tabs'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ width:'33%' }} className={cn({ active: tab == 'costs' })} onTouchTap={this._showTab.bind(this, 'costs')} >{translate('costs')}</th>
|
||||
<th style={{ width:'33%' }} className={cn({ active: tab == 'retrofit' })} onTouchTap={this._showTab.bind(this, 'retrofit')} >{translate('retrofit costs')}</th>
|
||||
<th style={{ width:'34%' }} className={cn({ active: tab == 'ammo' })} onTouchTap={this._showTab.bind(this, 'ammo')} >{translate('reload costs')}</th>
|
||||
<th style={{ width:'33%' }} className={cn({ active: tab == 'costs' })} onClick={this._showTab.bind(this, 'costs')} >{translate('costs')}</th>
|
||||
<th style={{ width:'33%' }} className={cn({ active: tab == 'retrofit' })} onClick={this._showTab.bind(this, 'retrofit')} >{translate('retrofit costs')}</th>
|
||||
<th style={{ width:'34%' }} className={cn({ active: tab == 'ammo' })} onClick={this._showTab.bind(this, 'ammo')} >{translate('reload costs')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
||||
@@ -91,39 +91,39 @@ export default class HardpointsSlotSection extends SlotSection {
|
||||
_getSectionMenu(translate) {
|
||||
let _fill = this._fill;
|
||||
|
||||
return <div className='select hardpoint' onTouchTap={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
return <div className='select hardpoint' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
<ul>
|
||||
<li className='lc' onTouchTap={this._empty}>{translate('empty all')}</li>
|
||||
<li className='lc' onClick={this._empty}>{translate('empty all')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('pl')}</div>
|
||||
<ul>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'pl', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'pl', 'G')}><MountGimballed className='lg'/></li>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'pl', 'T')}><MountTurret className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'pl', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'pl', 'G')}><MountGimballed className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'pl', 'T')}><MountTurret className='lg'/></li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('ul')}</div>
|
||||
<ul>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'ul', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'ul', 'G')}><MountGimballed className='lg'/></li>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'ul', 'T')}><MountTurret className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'ul', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'ul', 'G')}><MountGimballed className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'ul', 'T')}><MountTurret className='lg'/></li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('bl')}</div>
|
||||
<ul>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'bl', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'bl', 'G')}><MountGimballed className='lg'/></li>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'bl', 'T')}><MountTurret className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'bl', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'bl', 'G')}><MountGimballed className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'bl', 'T')}><MountTurret className='lg'/></li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('mc')}</div>
|
||||
<ul>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'mc', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'mc', 'G')}><MountGimballed className='lg'/></li>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'mc', 'T')}><MountTurret className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'mc', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'mc', 'G')}><MountGimballed className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'mc', 'T')}><MountTurret className='lg'/></li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('c')}</div>
|
||||
<ul>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'c', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'c', 'G')}><MountGimballed className='lg'/></li>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'c', 'T')}><MountTurret className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'c', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'c', 'G')}><MountGimballed className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'c', 'T')}><MountTurret className='lg'/></li>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -6,13 +6,14 @@ import Link from './Link';
|
||||
import ActiveLink from './ActiveLink';
|
||||
import cn from 'classnames';
|
||||
import { Cogs, CoriolisLogo, Hammer, Rocket, StatsBars } from './SvgIcons';
|
||||
import { Ships } from 'coriolis-data';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Persist from '../stores/Persist';
|
||||
import { toDetailedExport } from '../shipyard/Serializer';
|
||||
import ModalDeleteAll from './ModalDeleteAll';
|
||||
import ModalExport from './ModalExport';
|
||||
import ModalImport from './ModalImport';
|
||||
import Slider from './Slider';
|
||||
import { outfitURL } from '../utils/UrlGenerators';
|
||||
|
||||
const SIZE_MIN = 0.65;
|
||||
const SIZE_RANGE = 0.55;
|
||||
@@ -181,11 +182,11 @@ export default class Header extends TranslatedComponent {
|
||||
let shipList = [];
|
||||
|
||||
for (let s in Ships) {
|
||||
shipList.push(<ActiveLink key={s} href={'/outfit/' + s} className='block'>{Ships[s].properties.name}</ActiveLink>);
|
||||
shipList.push(<ActiveLink key={s} href={outfitURL(s)} className='block'>{Ships[s].properties.name}</ActiveLink>);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='menu-list dbl no-wrap' onTouchTap={ (e) => e.stopPropagation() }>
|
||||
<div className='menu-list dbl no-wrap' onClick={ (e) => e.stopPropagation() }>
|
||||
{shipList}
|
||||
</div>
|
||||
);
|
||||
@@ -203,7 +204,7 @@ export default class Header extends TranslatedComponent {
|
||||
let shipBuilds = [];
|
||||
let buildNameOrder = Object.keys(builds[shipId]).sort();
|
||||
for (let buildName of buildNameOrder) {
|
||||
let href = ['/outfit/', shipId, '/', builds[shipId][buildName], '?bn=', buildName].join('');
|
||||
let href = outfitURL(shipId, builds[shipId][buildName], buildName);
|
||||
shipBuilds.push(<li key={shipId + '-' + buildName} ><ActiveLink href={href} className='block'>{buildName}</ActiveLink></li>);
|
||||
}
|
||||
buildList.push(<ul key={shipId}>{Ships[shipId].properties.name}{shipBuilds}</ul>);
|
||||
@@ -211,7 +212,7 @@ export default class Header extends TranslatedComponent {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='menu-list' onTouchTap={ (e) => e.stopPropagation() }>
|
||||
<div className='menu-list' onClick={ (e) => e.stopPropagation() }>
|
||||
<div className='dbl'>{buildList}</div>
|
||||
</div>
|
||||
);
|
||||
@@ -237,10 +238,10 @@ export default class Header extends TranslatedComponent {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='menu-list' onTouchTap={ (e) => e.stopPropagation() } style={{ whiteSpace: 'nowrap' }}>
|
||||
<div className='menu-list' onClick={ (e) => e.stopPropagation() } style={{ whiteSpace: 'nowrap' }}>
|
||||
{comparisons}
|
||||
<hr />
|
||||
<Link href='/compare/all' ui-sref="compare({name: 'all'})" className='block cap'>{translate('compare all')}</Link>
|
||||
<Link href='/compare/all' className='block cap'>{translate('compare all')}</Link>
|
||||
<Link href='/compare' className='block cap'>{translate('create new')}</Link>
|
||||
</div>
|
||||
);
|
||||
@@ -255,14 +256,14 @@ export default class Header extends TranslatedComponent {
|
||||
let tips = Persist.showTooltips();
|
||||
|
||||
return (
|
||||
<div className='menu-list no-wrap cap' onTouchTap={ (e) => e.stopPropagation() }>
|
||||
<div className='menu-list no-wrap cap' onClick={ (e) => e.stopPropagation() }>
|
||||
<div style={{ lineHeight: '2em' }}>
|
||||
{translate('language')}
|
||||
<select className='cap' value={Persist.getLangCode()} onChange={this._setLanguage}>
|
||||
{this.languageOptions}
|
||||
</select>
|
||||
<br/>
|
||||
<span className='cap ptr' onTouchTap={this._toggleTooltips} >
|
||||
<span className='cap ptr' onClick={this._toggleTooltips} >
|
||||
{translate('tooltips')}
|
||||
<div className={cn({ disabled: !tips, 'primary-disabled': tips })} style={{ marginLeft: '0.5em', display: 'inline-block' }}>{(tips ? '✓' : '✗')}</div>
|
||||
</span>
|
||||
@@ -285,10 +286,10 @@ export default class Header extends TranslatedComponent {
|
||||
<hr />
|
||||
<ul>
|
||||
{translate('builds')} & {translate('comparisons')}
|
||||
<li><a href="#" className='block' onTouchTap={this._showBackup.bind(this)}>{translate('backup')}</a></li>
|
||||
<li><a href="#" className='block' onTouchTap={this._showDetailedExport.bind(this)}>{translate('detailed export')}</a></li>
|
||||
<li><a href="#" className='block' onTouchTap={this._showImport.bind(this)}>{translate('import')}</a></li>
|
||||
<li><a href="#" onTouchTap={this._showDeleteAll.bind(this)}>{translate('delete all')}</a></li>
|
||||
<li><Link href="#" className='block' onClick={this._showBackup.bind(this)}>{translate('backup')}</Link></li>
|
||||
<li><Link href="#" className='block' onClick={this._showDetailedExport.bind(this)}>{translate('detailed export')}</Link></li>
|
||||
<li><Link href="#" className='block' onClick={this._showImport.bind(this)}>{translate('import')}</Link></li>
|
||||
<li><Link href="#" onClick={this._showDeleteAll.bind(this)}>{translate('delete all')}</Link></li>
|
||||
</ul>
|
||||
<hr />
|
||||
<table style={{ width: 300, backgroundColor: 'transparent' }}>
|
||||
@@ -299,7 +300,7 @@ export default class Header extends TranslatedComponent {
|
||||
<td style={{ width: 20 }}><span style={{ fontSize: 30 }}>A</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colSpan='3' style={{ textAlign: 'center', cursor: 'pointer' }} className='primary-disabled cap' onTouchTap={this._resetTextSize.bind(this)}>{translate('reset')}</td>
|
||||
<td colSpan='3' style={{ textAlign: 'center', cursor: 'pointer' }} className='primary-disabled cap' onClick={this._resetTextSize.bind(this)}>{translate('reset')}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -313,13 +314,13 @@ export default class Header extends TranslatedComponent {
|
||||
* Add listeners on mount
|
||||
*/
|
||||
componentWillMount() {
|
||||
Persist.addListener('language', () => this.forceUpdate());
|
||||
Persist.addListener('insurance', () => this.forceUpdate());
|
||||
Persist.addListener('discounts', () => this.forceUpdate());
|
||||
Persist.addListener('deletedAll', () => this.forceUpdate());
|
||||
Persist.addListener('buildSaved', () => this.forceUpdate());
|
||||
Persist.addListener('buildDeleted', () => this.forceUpdate());
|
||||
Persist.addListener('tooltips', () => this.forceUpdate());
|
||||
let update = () => this.forceUpdate();
|
||||
Persist.addListener('language', update);
|
||||
Persist.addListener('insurance', update);
|
||||
Persist.addListener('discounts', update);
|
||||
Persist.addListener('deletedAll', update);
|
||||
Persist.addListener('builds', update);
|
||||
Persist.addListener('tooltips', update);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -347,36 +348,36 @@ export default class Header extends TranslatedComponent {
|
||||
let hasBuilds = Persist.hasBuilds();
|
||||
|
||||
if (this.props.appCacheUpdate) {
|
||||
return <div id="app-update" onTouchTap={() => window.location.reload() }>{translate('PHRASE_UPDATE_RDY')}</div>;
|
||||
return <div id="app-update" onClick={() => window.location.reload() }>{translate('PHRASE_UPDATE_RDY')}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<header>
|
||||
<Link className='l' href="/" style={{ marginRight: '1em' }} title="Home"><CoriolisLogo className='icon xl' /></Link>
|
||||
<Link className='l' href='/' style={{ marginRight: '1em' }} title='Home'><CoriolisLogo className='icon xl' /></Link>
|
||||
|
||||
<div className='l menu'>
|
||||
<div className={cn('menu-header', { selected: openedMenu == 's' })} onTouchTap={this._openShips}>
|
||||
<div className={cn('menu-header', { selected: openedMenu == 's' })} onClick={this._openShips}>
|
||||
<Rocket className='warning' /><span className='menu-item-label'>{' ' + translate('ships')}</span>
|
||||
</div>
|
||||
{openedMenu == 's' ? this._getShipsMenu() : null}
|
||||
</div>
|
||||
|
||||
<div className='l menu'>
|
||||
<div className={cn('menu-header', { selected: openedMenu == 'b', disabled: !hasBuilds })} onTouchTap={hasBuilds && this._openBuilds}>
|
||||
<div className={cn('menu-header', { selected: openedMenu == 'b', disabled: !hasBuilds })} onClick={hasBuilds && this._openBuilds}>
|
||||
<Hammer className={cn('warning', { 'warning-disabled': !hasBuilds })} /><span className='menu-item-label'>{' ' + translate('builds')}</span>
|
||||
</div>
|
||||
{openedMenu == 'b' ? this._getBuildsMenu() : null}
|
||||
</div>
|
||||
|
||||
<div className='l menu'>
|
||||
<div className={cn('menu-header', { selected: openedMenu == 'comp', disabled: !hasBuilds })} onTouchTap={hasBuilds && this._openComp}>
|
||||
<div className={cn('menu-header', { selected: openedMenu == 'comp', disabled: !hasBuilds })} onClick={hasBuilds && this._openComp}>
|
||||
<StatsBars className={cn('warning', { 'warning-disabled': !hasBuilds })} /><span className='menu-item-label'>{' ' + translate('compare')}</span>
|
||||
</div>
|
||||
{openedMenu == 'comp' ? this._getComparisonsMenu() : null}
|
||||
</div>
|
||||
|
||||
<div className='r menu'>
|
||||
<div className={cn('menu-header', { selected: openedMenu == 'settings' })} onTouchTap={this._openSettings}>
|
||||
<div className={cn('menu-header', { selected: openedMenu == 'settings' })} onClick={this._openSettings}>
|
||||
<Cogs className='xl warning'/><span className='menu-item-label'>{translate('settings')}</span>
|
||||
</div>
|
||||
{openedMenu == 'settings' ? this._getSettingsMenu() : null}
|
||||
|
||||
@@ -132,12 +132,12 @@ export default class InternalSlotSection extends SlotSection {
|
||||
* @return {React.Component} Section menu
|
||||
*/
|
||||
_getSectionMenu(translate) {
|
||||
return <div className='select' onTouchTap={e => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
return <div className='select' onClick={e => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
<ul>
|
||||
<li className='lc' onTouchTap={this._empty}>{translate('empty all')}</li>
|
||||
<li className='lc' onTouchTap={this._fillWithCargo}>{translate('cargo')}</li>
|
||||
<li className='lc' onTouchTap={this._fillWithCells}>{translate('scb')}</li>
|
||||
<li className='lc' onTouchTap={this._fillWithArmor}>{translate('hr')}</li>
|
||||
<li className='lc' onClick={this._empty}>{translate('empty all')}</li>
|
||||
<li className='lc' onClick={this._fillWithCargo}>{translate('cargo')}</li>
|
||||
<li className='lc' onClick={this._fillWithCells}>{translate('scb')}</li>
|
||||
<li className='lc' onClick={this._fillWithArmor}>{translate('hr')}</li>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import d3 from 'd3';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
|
||||
const RENDER_POINTS = 20; // Only render 20 points on the graph
|
||||
const MARGIN = { top: 15, right: 15, bottom: 35, left: 60 };
|
||||
const MARGIN = { top: 15, right: 20, bottom: 35, left: 60 };
|
||||
|
||||
/**
|
||||
* Line Chart
|
||||
@@ -47,7 +47,7 @@ export default class LineChart extends TranslatedComponent {
|
||||
this._moveTip = this._moveTip.bind(this);
|
||||
|
||||
let markerElems = [];
|
||||
let detailElems = [<text key={'lbl'} className='label x' y='1.25em'/>];
|
||||
let detailElems = [<text key='lbl' className='text-tip x' y='1.25em'/>];
|
||||
let xScale = d3.scale.linear();
|
||||
let xAxisScale = d3.scale.linear();
|
||||
let yScale = d3.scale.linear();
|
||||
@@ -60,7 +60,7 @@ export default class LineChart extends TranslatedComponent {
|
||||
for(let i = 0, l = series ? series.length : 1; i < l; i++) {
|
||||
let yAccessor = series ? function(d) { return yScale(d[1][this]); }.bind(series[i]) : (d) => yScale(d[1]);
|
||||
seriesLines.push(d3.svg.line().x((d) => xScale(d[0])).y(yAccessor));
|
||||
detailElems.push(<text key={i} className='label y' y={1.25 * (i + 2) + 'em'}/>);
|
||||
detailElems.push(<text key={i} className='text-tip y' y={1.25 * (i + 2) + 'em'}/>);
|
||||
markerElems.push(<circle key={i} className='marker' r='4' />);
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export default class LineChart extends TranslatedComponent {
|
||||
seriesLines,
|
||||
detailElems,
|
||||
markerElems,
|
||||
tipHeight: 2 + (1.25 * (series ? series.length : 0.75))
|
||||
tipHeight: 2 + (1.2 * (series ? series.length : 0.8))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ export default class LineChart extends TranslatedComponent {
|
||||
|
||||
xPos = xScale(x0); // Clamp xPos
|
||||
|
||||
tips.selectAll('text.label.y').text(function(d, i) {
|
||||
tips.selectAll('text.text-tip.y').text(function(d, i) {
|
||||
let yVal = series ? y0[series[i]] : y0;
|
||||
yTotal += yVal;
|
||||
return (series ? translate(series[i]) : '') + ' ' + formats.f2(yVal);
|
||||
@@ -110,8 +110,8 @@ export default class LineChart extends TranslatedComponent {
|
||||
|
||||
tipWidth += 8;
|
||||
tips.attr('transform', 'translate(' + xPos + ',' + tipY + ')');
|
||||
tips.selectAll('text.label').attr('x', flip ? -12 : 12).style('text-anchor', flip ? 'end' : 'start');
|
||||
tips.selectAll('text.label.x').text(formats.f2(x0)).append('tspan').attr('class', 'metric').text(' ' + xUnit);
|
||||
tips.selectAll('text.text-tip').attr('x', flip ? -12 : 12).style('text-anchor', flip ? 'end' : 'start');
|
||||
tips.selectAll('text.text-tip.x').text(formats.f2(x0)).append('tspan').attr('class', 'metric').text(' ' + xUnit);
|
||||
tips.selectAll('rect').attr('width', tipWidth + 4).attr('x', flip ? -tipWidth - 12 : 8).attr('y', 0).style('text-anchor', flip ? 'end' : 'start');
|
||||
this.markersContainer.selectAll('circle').attr('cx', xPos).attr('cy', (d, i) => yScale(series ? y0[series[i]] : y0));
|
||||
}
|
||||
|
||||
@@ -7,6 +7,15 @@ import { shallowEqual } from '../utils/UtilityFunctions';
|
||||
*/
|
||||
export default class Link extends React.Component {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handler = this.handler.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a component should be rerendered
|
||||
* @param {object} nextProps Next properties
|
||||
@@ -21,18 +30,15 @@ export default class Link extends React.Component {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
handler(event) {
|
||||
if (event.getModifierState &&
|
||||
(event.getModifierState('Shift') ||
|
||||
event.getModifierState('Alt') ||
|
||||
event.getModifierState('Control') ||
|
||||
event.getModifierState('Meta') ||
|
||||
event.button > 1)) {
|
||||
if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey || event.button > 1) {
|
||||
return;
|
||||
}
|
||||
event.nativeEvent && event.preventDefault && event.preventDefault();
|
||||
event.preventDefault();
|
||||
|
||||
if (this.props.href) {
|
||||
Router.go(encodeURI(this.props.href));
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick(event);
|
||||
} else if (this.props.href) {
|
||||
Router.go(this.props.href);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +47,7 @@ export default class Link extends React.Component {
|
||||
* @return {React.Component} A href element
|
||||
*/
|
||||
render() {
|
||||
let action = this.handler.bind(this);
|
||||
return <a {...this.props} onTouchTap={action}>{this.props.children}</a>;
|
||||
return <a {...this.props} onClick={this.handler}>{this.props.children}</a>;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Persist from '../stores/Persist';
|
||||
|
||||
/**
|
||||
@@ -96,39 +96,47 @@ export default class ModalCompare extends TranslatedComponent {
|
||||
let translate = this.context.language.translate;
|
||||
|
||||
let availableBuilds = unusedBuilds.map((build, i) =>
|
||||
<tr key={i} onTouchTap={this._addBuild.bind(this, i)}>
|
||||
<tr key={i} onClick={this._addBuild.bind(this, i)}>
|
||||
<td className='tl'>{build.name}</td>
|
||||
<td className='tl'>{build.buildName}</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
let selectedBuilds = usedBuilds.map((build, i) =>
|
||||
<tr key={i} onTouchTap={this._removeBuild.bind(this, i)}>
|
||||
<tr key={i} onClick={this._removeBuild.bind(this, i)}>
|
||||
<td className='tl'>{build.name}</td><
|
||||
td className='tl'>{build.buildName}</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
return <div className='modal' onTouchTap={ (e) => e.stopPropagation() }>
|
||||
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
|
||||
<h3>{translate('PHRASE_SELECT_BUILDS')}</h3>
|
||||
<div id='build-select'>
|
||||
<div className='build-section'>
|
||||
<h1>{translate('available')}</h1>
|
||||
<div>
|
||||
<table>
|
||||
<thead><tr><th colSpan='2'>{translate('available')}</th></tr></thead>
|
||||
<tbody>
|
||||
{availableBuilds}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h1>⇆</h1>
|
||||
<div className='build-section'>
|
||||
<h1>{translate('added')}</h1>
|
||||
<div>
|
||||
<table>
|
||||
<thead><tr><th colSpan='2'>{translate('added')}</th></tr></thead>
|
||||
<tbody>
|
||||
{selectedBuilds}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<button className='cap' onTouchTap={this._selectBuilds.bind(this)}>{translate('Ok')}</button>
|
||||
<button className='r cap' onTouchTap={() => this.context.hideModal()}>{translate('Cancel')}</button>
|
||||
<button className='cap' onClick={this._selectBuilds.bind(this)}>{translate('Ok')}</button>
|
||||
<button className='r cap' onClick={() => this.context.hideModal()}>{translate('Cancel')}</button>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,11 +22,11 @@ export default class ModalDeleteAll extends TranslatedComponent {
|
||||
render() {
|
||||
let translate = this.context.language.translate;
|
||||
|
||||
return <div className='modal' onTouchTap={(e) => e.stopPropagation()}>
|
||||
return <div className='modal' onClick={(e) => e.stopPropagation()}>
|
||||
<h2>{translate('delete all')}</h2>
|
||||
<p className='cen'>{translate('PHRASE_CONFIRMATION')}</p>
|
||||
<button className='l cap' onTouchTap={this._deleteAll.bind(this)}>{translate('yes')}</button>
|
||||
<button className='r cap' onTouchTap={this.context.hideModal}>{translate('no')}</button>
|
||||
<button className='l cap' onClick={this._deleteAll.bind(this)}>{translate('yes')}</button>
|
||||
<button className='r cap' onClick={this.context.hideModal}>{translate('no')}</button>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
|
||||
/**
|
||||
@@ -40,6 +41,17 @@ export default class ModalExport extends TranslatedComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Focus on textarea and select all
|
||||
*/
|
||||
componentDidMount() {
|
||||
let e = findDOMNode(this.refs.exportField);
|
||||
if (e) {
|
||||
e.focus();
|
||||
e.select();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the modal
|
||||
* @return {React.Component} Modal Content
|
||||
@@ -52,13 +64,13 @@ export default class ModalExport extends TranslatedComponent {
|
||||
description = <div>{translate(this.props.description)}</div>;
|
||||
}
|
||||
|
||||
return <div className='modal' onTouchTap={ (e) => e.stopPropagation() }>
|
||||
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
|
||||
<h2>{translate(this.props.title || 'Export')}</h2>
|
||||
{description}
|
||||
<div>
|
||||
<textarea className='cb json' onFocus={ (e) => e.target.select() } readOnly value={this.state.exportJson} />
|
||||
<textarea className='cb json' ref='exportField' readOnly value={this.state.exportJson} />
|
||||
</div>
|
||||
<button className='r dismiss cap' onTouchTap={this.context.hideModal}>{translate('close')}</button>
|
||||
<button className='r dismiss cap' onClick={this.context.hideModal}>{translate('close')}</button>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import React from 'react';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
import cn from 'classnames';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import Router from '../Router';
|
||||
import Persist from '../stores/Persist';
|
||||
import { Ships } from 'coriolis-data';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Ship from '../shipyard/Ship';
|
||||
import { ModuleNameToGroup, Insurance } from '../shipyard/Constants';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import { fromDetailedBuild } from '../shipyard/Serializer';
|
||||
import { Download } from './SvgIcons';
|
||||
import { outfitURL } from '../utils/UrlGenerators';
|
||||
|
||||
const textBuildRegex = new RegExp('^\\[([\\w \\-]+)\\]\n');
|
||||
const lineRegex = new RegExp('^([\\dA-Z]{1,2}): (\\d)([A-I])[/]?([FGT])?([SD])? ([\\w\\- ]+)');
|
||||
@@ -24,15 +27,6 @@ function isEmptySlot(slot) {
|
||||
return slot.maxClass == this && slot.m === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Equal ignore case utility function. Must be bound to a string
|
||||
* @param {string} str String
|
||||
* @return {Boolean} True if equal
|
||||
*/
|
||||
function equalsIgnoreCase(str) {
|
||||
return str.toLowerCase() == this.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a build is valid
|
||||
* @param {string} shipId Ship ID
|
||||
@@ -273,7 +267,7 @@ export default class ModalImport extends TranslatedComponent {
|
||||
let builds = {};
|
||||
builds[shipId] = {};
|
||||
builds[shipId]['Imported ' + buildName] = ship.toString();
|
||||
this.setState({ builds });
|
||||
this.setState({ builds, singleBuild: true });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,6 +285,7 @@ export default class ModalImport extends TranslatedComponent {
|
||||
errorMsg: null,
|
||||
importValid: false,
|
||||
insurance: null,
|
||||
singleBuild: false,
|
||||
importString,
|
||||
});
|
||||
|
||||
@@ -312,6 +307,7 @@ export default class ModalImport extends TranslatedComponent {
|
||||
this._importDetailedArray(importData);
|
||||
} else if (importData.ship && typeof importData.name !== undefined) { // Using JSON from a single ship build export
|
||||
this._importDetailedArray([importData]); // Convert to array with singleobject
|
||||
this.setState({ singleBuild: true });
|
||||
} else { // Using Backup JSON
|
||||
this._importBackup(importData);
|
||||
}
|
||||
@@ -330,6 +326,16 @@ export default class ModalImport extends TranslatedComponent {
|
||||
_process() {
|
||||
let builds = null, comparisons = null;
|
||||
|
||||
// If only importing a single build go straight to the outfitting page
|
||||
if (this.state.singleBuild) {
|
||||
builds = this.state.builds;
|
||||
let shipId = Object.keys(builds)[0];
|
||||
let name = Object.keys(builds[shipId])[0];
|
||||
Router.go(outfitURL(shipId, builds[shipId][name], name));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (this.state.builds) {
|
||||
builds = {}; // Create new builds object such that orginal name retained, but can be renamed
|
||||
for (let shipId in this.state.builds) {
|
||||
@@ -412,6 +418,14 @@ export default class ModalImport extends TranslatedComponent {
|
||||
this._process();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* If textarea is shown focus on mount
|
||||
*/
|
||||
componentDidMount() {
|
||||
if (!this.props.builds && findDOMNode(this.refs.importField)) {
|
||||
findDOMNode(this.refs.importField).focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the import modal
|
||||
@@ -425,7 +439,7 @@ export default class ModalImport extends TranslatedComponent {
|
||||
if (!state.processed) {
|
||||
importStage = (
|
||||
<div>
|
||||
<textarea className='cb json' onChange={this._validateImport} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
|
||||
<textarea className='cb json' ref='importField' onChange={this._validateImport} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
|
||||
<button id='proceed' className='l cap' onClick={this._process} disabled={!state.importValid} >{translate('proceed')}</button>
|
||||
<div className='l warning' style={{ marginLeft:'3em' }}>{state.errorMsg}</div>
|
||||
</div>
|
||||
@@ -508,7 +522,7 @@ export default class ModalImport extends TranslatedComponent {
|
||||
);
|
||||
}
|
||||
|
||||
return <div className='modal' onTouchTap={ (e) => e.stopPropagation() } onClick={ (e) => e.stopPropagation() }>
|
||||
return <div className='modal' onClick={ (e) => e.stopPropagation() } onClick={ (e) => e.stopPropagation() }>
|
||||
<h2 >{translate('import')}</h2>
|
||||
{importStage}
|
||||
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
|
||||
|
||||
@@ -40,7 +40,7 @@ export default class ModalPermalink extends TranslatedComponent {
|
||||
render() {
|
||||
let translate = this.context.language.translate;
|
||||
|
||||
return <div className='modal' onTouchTap={ (e) => e.stopPropagation() }>
|
||||
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
|
||||
<h2>{translate('permalink')}</h2>
|
||||
<br/>
|
||||
<h3>{translate('URL')}</h3>
|
||||
@@ -49,7 +49,7 @@ export default class ModalPermalink extends TranslatedComponent {
|
||||
<h3 >{translate('shortened')}</h3>
|
||||
<input value={this.state.shortenedUrl} readOnly size={25} onFocus={ (e) => e.target.select() }/>
|
||||
<br/><br/>
|
||||
<button className={'r dismiss cap'} onTouchTap={this.context.hideModal}>{translate('close')}</button>
|
||||
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ export default class PowerBands extends TranslatedComponent {
|
||||
height={state.barHeight}
|
||||
x={Math.floor(Math.max(wattScale(b.retractedSum) - wattScale(b.retracted), 0))}
|
||||
y={1}
|
||||
onTouchTap={this._selectRet.bind(this, i)}
|
||||
onClick={this._selectRet.bind(this, i)}
|
||||
className={getClass(ret[i], b.retractedSum, available)}
|
||||
/>);
|
||||
|
||||
@@ -223,7 +223,7 @@ export default class PowerBands extends TranslatedComponent {
|
||||
height={state.barHeight}
|
||||
x={wattScale(b.retractedSum) - (wattScale(b.retracted) / 2)}
|
||||
y={state.retY}
|
||||
onTouchTap={this._selectRet.bind(this, i)}
|
||||
onClick={this._selectRet.bind(this, i)}
|
||||
className='primary-bg'>{retLbl}</text>
|
||||
);
|
||||
}
|
||||
@@ -238,7 +238,7 @@ export default class PowerBands extends TranslatedComponent {
|
||||
height={state.barHeight}
|
||||
x={Math.floor(Math.max(wattScale(b.deployedSum) - wattScale(b.retracted) - wattScale(b.deployed), 0))}
|
||||
y={state.barHeight + 1}
|
||||
onTouchTap={this._selectDep.bind(this, i)}
|
||||
onClick={this._selectDep.bind(this, i)}
|
||||
className={getClass(dep[i], b.deployedSum, available)}
|
||||
/>);
|
||||
|
||||
@@ -250,7 +250,7 @@ export default class PowerBands extends TranslatedComponent {
|
||||
height={state.barHeight}
|
||||
x={wattScale(b.deployedSum) - ((wattScale(b.retracted) + wattScale(b.deployed)) / 2)}
|
||||
y={state.depY}
|
||||
onTouchTap={this._selectDep.bind(this, i)}
|
||||
onClick={this._selectDep.bind(this, i)}
|
||||
className='primary-bg'>{depLbl}</text>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -119,23 +119,23 @@ export default class PowerManagement extends TranslatedComponent {
|
||||
let retractedElem = null, deployedElem = null;
|
||||
|
||||
if (slot.enabled) {
|
||||
retractedElem = <td className='ptr upp' onTouchTap={toggleEnabled}>{POWER[ship.getSlotStatus(slot, false)]}</td>;
|
||||
deployedElem = <td className='ptr upp' onTouchTap={toggleEnabled}>{POWER[ship.getSlotStatus(slot, true)]}</td>;
|
||||
retractedElem = <td className='ptr upp' onClick={toggleEnabled}>{POWER[ship.getSlotStatus(slot, false)]}</td>;
|
||||
deployedElem = <td className='ptr upp' onClick={toggleEnabled}>{POWER[ship.getSlotStatus(slot, true)]}</td>;
|
||||
} else {
|
||||
retractedElem = <td className='ptr disabled upp' colSpan='2' onTouchTap={toggleEnabled}>{translate('disabled')}</td>;
|
||||
retractedElem = <td className='ptr disabled upp' colSpan='2' onClick={toggleEnabled}>{translate('disabled')}</td>;
|
||||
}
|
||||
|
||||
powerRows.push(<tr key={i} className={cn('highlight', { disabled: !slot.enabled })}>
|
||||
<td className='ptr' style={{ width: '1em' }} onTouchTap={toggleEnabled}>{m.class + m.rating}</td>
|
||||
<td className='ptr le shorten cap' onTouchTap={toggleEnabled}>{slotName(translate, slot)}</td>
|
||||
<td className='ptr' onTouchTap={toggleEnabled}><u>{translate(slot.type)}</u></td>
|
||||
<td className='ptr' style={{ width: '1em' }} onClick={toggleEnabled}>{m.class + m.rating}</td>
|
||||
<td className='ptr le shorten cap' onClick={toggleEnabled}>{slotName(translate, slot)}</td>
|
||||
<td className='ptr' onClick={toggleEnabled}><u>{translate(slot.type)}</u></td>
|
||||
<td>
|
||||
<span className='flip ptr btn' onTouchTap={this._priority.bind(this, slot, -1)}>►</span>
|
||||
<span className='flip ptr btn' onClick={this._priority.bind(this, slot, -1)}>►</span>
|
||||
{' ' + (slot.priority + 1) + ' '}
|
||||
<span className='ptr btn' onTouchTap={this._priority.bind(this, slot, 1)}>►</span>
|
||||
<span className='ptr btn' onClick={this._priority.bind(this, slot, 1)}>►</span>
|
||||
</td>
|
||||
<td className='ri ptr' style={{ width: '3.25em' }} onTouchTap={toggleEnabled}>{pwr(m.power)}</td>
|
||||
<td className='ri ptr' style={{ width: '3em' }} onTouchTap={toggleEnabled}><u>{pct(m.power / ship.powerAvailable)}</u></td>
|
||||
<td className='ri ptr' style={{ width: '3.25em' }} onClick={toggleEnabled}>{pwr(m.power)}</td>
|
||||
<td className='ri ptr' style={{ width: '3em' }} onClick={toggleEnabled}><u>{pct(m.power / ship.powerAvailable)}</u></td>
|
||||
{retractedElem}
|
||||
{deployedElem}
|
||||
</tr>);
|
||||
@@ -200,12 +200,12 @@ export default class PowerManagement extends TranslatedComponent {
|
||||
<table style={{ width: '100%' }}>
|
||||
<thead>
|
||||
<tr className='main'>
|
||||
<th colSpan='2' className='sortable le' onTouchTap={sortOrder.bind(this, 'n')} >{translate('module')}</th>
|
||||
<th style={{ width: '3em' }} className='sortable' onTouchTap={sortOrder.bind(this, 't')} >{translate('type')}</th>
|
||||
<th style={{ width: '4em' }} className='sortable' onTouchTap={sortOrder.bind(this, 'pri')} >{translate('pri')}</th>
|
||||
<th colSpan='2' className='sortable' onTouchTap={sortOrder.bind(this, 'pwr')} >{translate('PWR')}</th>
|
||||
<th style={{ width: '3em' }} className='sortable' onTouchTap={sortOrder.bind(this, 'r')} >{translate('ret')}</th>
|
||||
<th style={{ width: '3em' }} className='sortable' onTouchTap={sortOrder.bind(this, 'd')} >{translate('dep')}</th>
|
||||
<th colSpan='2' className='sortable le' onClick={sortOrder.bind(this, 'n')} >{translate('module')}</th>
|
||||
<th style={{ width: '3em' }} className='sortable' onClick={sortOrder.bind(this, 't')} >{translate('type')}</th>
|
||||
<th style={{ width: '4em' }} className='sortable' onClick={sortOrder.bind(this, 'pri')} >{translate('pri')}</th>
|
||||
<th colSpan='2' className='sortable' onClick={sortOrder.bind(this, 'pwr')} >{translate('PWR')}</th>
|
||||
<th style={{ width: '3em' }} className='sortable' onClick={sortOrder.bind(this, 'r')} >{translate('ret')}</th>
|
||||
<th style={{ width: '3em' }} className='sortable' onClick={sortOrder.bind(this, 'd')} >{translate('dep')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@@ -24,8 +24,11 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
let u = language.units;
|
||||
let formats = language.formats;
|
||||
let round = formats.round;
|
||||
let int = formats.int;
|
||||
let { time, int } = formats;
|
||||
let armourDetails = null;
|
||||
let sgClassNames = cn({ warning: ship.sgSlot && !ship.shieldStrength, muted: !ship.sgSlot });
|
||||
let sgRecover = '-';
|
||||
let sgRecharge = '-';
|
||||
let hide = tooltip.bind(null, null);
|
||||
|
||||
if (ship.armourMultiplier > 1 || ship.armourAdded) {
|
||||
@@ -35,6 +38,11 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
})</u>;
|
||||
}
|
||||
|
||||
if (ship.shieldStrength) {
|
||||
sgRecover = time(ship.calcShieldRecovery());
|
||||
sgRecharge = time(ship.calcShieldRecharge());
|
||||
}
|
||||
|
||||
return <div id='summary'>
|
||||
<table id='summaryTable'>
|
||||
<thead>
|
||||
@@ -43,20 +51,23 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<th onMouseEnter={termtip.bind(null, 'maneuverability')} onMouseLeave={hide} rowSpan={2}>{translate('MNV')}</th>
|
||||
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !ship.canThrust() }) }>{translate('speed')}</th>
|
||||
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !ship.canBoost() }) }>{translate('boost')}</th>
|
||||
<th onMouseOver={termtip.bind(null, 'damage per second')} onMouseOut={hide} rowSpan={2}>{translate('DPS')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'damage per second')} onMouseLeave={hide} rowSpan={2}>{translate('DPS')}</th>
|
||||
<th rowSpan={2}>{translate('armour')}</th>
|
||||
<th rowSpan={2}>{translate('shields')}</th>
|
||||
<th colSpan={3}>{translate('shields')}</th>
|
||||
<th colSpan={3}>{translate('mass')}</th>
|
||||
<th rowSpan={2}>{translate('cargo')}</th>
|
||||
<th rowSpan={2}>{translate('fuel')}</th>
|
||||
<th colSpan={3}>{translate('jump range')}</th>
|
||||
<th colSpan={3}>{translate('total range')}</th>
|
||||
<th onMouseOver={termtip.bind(null, 'mass lock factor')} onMouseOut={hide} rowSpan={2}>{translate('MLF')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'PHRASE_FASTEST_RANGE')} onMouseLeave={hide} colSpan={3}>{translate('fastest range')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'mass lock factor')} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className='lft'>{translate('strength')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECOVER', { cap: 0 })} onMouseLeave={hide}>{translate('recover')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECHARGE', { cap: 0 })} onMouseLeave={hide}>{translate('recharge')}</th>
|
||||
<th className='lft'>{translate('hull')}</th>
|
||||
<th>{translate('unladen')}</th>
|
||||
<th>{translate('laden')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'PHRASE_UNLADEN', { cap: 0 })} onMouseLeave={hide}>{translate('unladen')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'PHRASE_LADEN', { cap: 0 })} onMouseLeave={hide}>{translate('laden')}</th>
|
||||
<th className='lft'>{translate('max')}</th>
|
||||
<th>{translate('full tank')}</th>
|
||||
<th>{translate('laden')}</th>
|
||||
@@ -73,7 +84,9 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<td>{ ship.canBoost() ? <span>{int(ship.topBoost)} {u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||
<td>{round(ship.totalDps)}</td>
|
||||
<td>{int(ship.armour)} {armourDetails}</td>
|
||||
<td>{int(ship.shieldStrength)} {u.MJ} { ship.shieldMultiplier > 1 && ship.shieldStrength > 0 ? <u>({formats.rPct(ship.shieldMultiplier)})</u> : null }</td>
|
||||
<td className={sgClassNames}>{int(ship.shieldStrength)} {u.MJ} { ship.shieldMultiplier > 1 && ship.shieldStrength > 0 ? <u>({formats.rPct(ship.shieldMultiplier)})</u> : null }</td>
|
||||
<td className={sgClassNames}>{sgRecover}</td>
|
||||
<td className={sgClassNames}>{sgRecharge}</td>
|
||||
<td>{ship.hullMass} {u.T}</td>
|
||||
<td>{round(ship.unladenMass)} {u.T}</td>
|
||||
<td>{round(ship.ladenMass)} {u.T}</td>
|
||||
@@ -83,8 +96,8 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<td>{round(ship.fullTankRange)} {u.LY}</td>
|
||||
<td>{round(ship.ladenRange)} {u.LY}</td>
|
||||
<td>{round(ship.maxJumpCount)}</td>
|
||||
<td>{round(ship.unladenTotalRange)} {u.LY}</td>
|
||||
<td>{round(ship.ladenTotalRange)} {u.LY}</td>
|
||||
<td>{round(ship.unladenFastestRange)} {u.LY}</td>
|
||||
<td>{round(ship.ladenFastestRange)} {u.LY}</td>
|
||||
<td>{ship.masslock}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -98,7 +98,7 @@ export default class Slider extends React.Component {
|
||||
*/
|
||||
_updateDimensions() {
|
||||
this.setState({
|
||||
outerWidth: findDOMNode(this).offsetWidth
|
||||
outerWidth: findDOMNode(this).getBoundingClientRect().width
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,8 @@ export default class Slot extends TranslatedComponent {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_contextMenu(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.props.onSelect(null,null);
|
||||
}
|
||||
|
||||
@@ -95,7 +97,7 @@ export default class Slot extends TranslatedComponent {
|
||||
// TODO: implement touch dragging
|
||||
|
||||
return (
|
||||
<div className={cn('slot', dropClass, { selected })} onTouchTap={onOpen} onContextMenu={this._contextMenu} onDragOver={dragOver}>
|
||||
<div className={cn('slot', dropClass, { selected })} onClick={onOpen} onContextMenu={this._contextMenu} onDragOver={dragOver}>
|
||||
<div className='details-container'>
|
||||
<div className='sz'>{this._getMaxClassLabel(translate)}</div>
|
||||
{slotDetails}
|
||||
|
||||
@@ -177,7 +177,7 @@ export default class SlotSection extends TranslatedComponent {
|
||||
|
||||
return (
|
||||
<div id={this.sectionId} className={'group'} onDragLeave={this._dragOverNone}>
|
||||
<div className={cn('section-menu', { selected: sectionMenuOpened })} onTouchTap={open} onContextMenu={ctx}>
|
||||
<div className={cn('section-menu', { selected: sectionMenuOpened })} onClick={open} onContextMenu={ctx}>
|
||||
<h1>{translate(this.sectionName)} <Equalizer/></h1>
|
||||
{sectionMenuOpened ? this._getSectionMenu(translate) : null }
|
||||
</div>
|
||||
|
||||
@@ -43,7 +43,7 @@ export default class StandardSlot extends TranslatedComponent {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn('slot', { selected: this.props.selected })} onTouchTap={this.props.onOpen}>
|
||||
<div className={cn('slot', { selected: this.props.selected })} onClick={this.props.onOpen}>
|
||||
<div className={cn('details-container', { warning: warning && warning(slot.m) })}>
|
||||
<div className={'sz'}>{slot.maxClass}</div>
|
||||
<div>
|
||||
|
||||
@@ -172,7 +172,7 @@ export default class StandardSlotSection extends SlotSection {
|
||||
let bh = ship.bulkheads;
|
||||
|
||||
slots[0] = (
|
||||
<div key='bh' className={cn('slot', { selected: currentMenu === bh })} onTouchTap={open.bind(this, bh)}>
|
||||
<div key='bh' className={cn('slot', { selected: currentMenu === bh })} onClick={open.bind(this, bh)}>
|
||||
<div className={'details-container'}>
|
||||
<div className={'details'}>
|
||||
<div className={'sz'}>8</div>
|
||||
@@ -184,21 +184,21 @@ export default class StandardSlotSection extends SlotSection {
|
||||
</div>
|
||||
</div>
|
||||
{currentMenu === bh &&
|
||||
<div className='select' onTouchTap={ e => e.stopPropagation() }>
|
||||
<div className='select' onClick={ e => e.stopPropagation() }>
|
||||
<ul>
|
||||
<li onTouchTap={selBulkhead.bind(this, 0)} onMouseOver={this._bhDiff.bind(this, 0)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 0 })}>
|
||||
<li onClick={selBulkhead.bind(this, 0)} onMouseOver={this._bhDiff.bind(this, 0)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 0 })}>
|
||||
{translate('Lightweight Alloy')}
|
||||
</li>
|
||||
<li onTouchTap={selBulkhead.bind(this, 1)} onMouseOver={this._bhDiff.bind(this, 1)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 1 })}>
|
||||
<li onClick={selBulkhead.bind(this, 1)} onMouseOver={this._bhDiff.bind(this, 1)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 1 })}>
|
||||
{translate('Reinforced Alloy')}
|
||||
</li>
|
||||
<li onTouchTap={selBulkhead.bind(this, 2)} onMouseOver={this._bhDiff.bind(this, 2)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 2 })}>
|
||||
<li onClick={selBulkhead.bind(this, 2)} onMouseOver={this._bhDiff.bind(this, 2)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 2 })}>
|
||||
{translate('Military Grade Composite')}
|
||||
</li>
|
||||
<li onTouchTap={selBulkhead.bind(this, 3)} onMouseOver={this._bhDiff.bind(this, 3)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 3 })}>
|
||||
<li onClick={selBulkhead.bind(this, 3)} onMouseOver={this._bhDiff.bind(this, 3)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 3 })}>
|
||||
{translate('Mirrored Surface Composite')}
|
||||
</li>
|
||||
<li onTouchTap={selBulkhead.bind(this, 4)} onMouseOver={this._bhDiff.bind(this, 4)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 4 })}>
|
||||
<li onClick={selBulkhead.bind(this, 4)} onMouseOver={this._bhDiff.bind(this, 4)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 4 })}>
|
||||
{translate('Reactive Surface Composite')}
|
||||
</li>
|
||||
</ul>
|
||||
@@ -294,19 +294,19 @@ export default class StandardSlotSection extends SlotSection {
|
||||
_getSectionMenu(translate) {
|
||||
let _fill = this._fill;
|
||||
|
||||
return <div className='select' onTouchTap={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
<ul>
|
||||
<li className='lc' onTouchTap={this._optimizeStandard}>{translate('Optimize')}</li>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'E')}>E</li>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'D')}>D</li>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'C')}>C</li>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'B')}>B</li>
|
||||
<li className='c' onTouchTap={_fill.bind(this, 'A')}>A</li>
|
||||
<li className='lc' onClick={this._optimizeStandard}>{translate('Optimize')}</li>
|
||||
<li className='c' onClick={_fill.bind(this, 'E')}>E</li>
|
||||
<li className='c' onClick={_fill.bind(this, 'D')}>D</li>
|
||||
<li className='c' onClick={_fill.bind(this, 'C')}>C</li>
|
||||
<li className='c' onClick={_fill.bind(this, 'B')}>B</li>
|
||||
<li className='c' onClick={_fill.bind(this, 'A')}>A</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('builds / roles')}</div>
|
||||
<ul>
|
||||
<li className='lc' onTouchTap={this._optimizeCargo}>{translate('Trader')}</li>
|
||||
<li className='lc' onTouchTap={this._optimizeExplorer}>{translate('Explorer')}</li>
|
||||
<li className='lc' onClick={this._optimizeCargo}>{translate('Trader')}</li>
|
||||
<li className='lc' onClick={this._optimizeExplorer}>{translate('Explorer')}</li>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -91,21 +91,21 @@ export default class UtilitySlotSection extends SlotSection {
|
||||
_getSectionMenu(translate) {
|
||||
let _use = this._use;
|
||||
|
||||
return <div className='select' onTouchTap={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
<ul>
|
||||
<li className='lc' onTouchTap={this._empty}>{translate('empty all')}</li>
|
||||
<li className='lc' onClick={this._empty}>{translate('empty all')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('sb')}</div>
|
||||
<ul>
|
||||
<li className='c' onTouchTap={_use.bind(this, 'sb', 'E', null)}>E</li>
|
||||
<li className='c' onTouchTap={_use.bind(this, 'sb', 'D', null)}>D</li>
|
||||
<li className='c' onTouchTap={_use.bind(this, 'sb', 'C', null)}>C</li>
|
||||
<li className='c' onTouchTap={_use.bind(this, 'sb', 'B', null)}>B</li>
|
||||
<li className='c' onTouchTap={_use.bind(this, 'sb', 'A', null)}>A</li>
|
||||
<li className='c' onClick={_use.bind(this, 'sb', 'E', null)}>E</li>
|
||||
<li className='c' onClick={_use.bind(this, 'sb', 'D', null)}>D</li>
|
||||
<li className='c' onClick={_use.bind(this, 'sb', 'C', null)}>C</li>
|
||||
<li className='c' onClick={_use.bind(this, 'sb', 'B', null)}>B</li>
|
||||
<li className='c' onClick={_use.bind(this, 'sb', 'A', null)}>A</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('cm')}</div>
|
||||
<ul>
|
||||
<li className='lc' onTouchTap={_use.bind(this, 'cm', null, 'Heat Sink Launcher')}>{translate('Heat Sink Launcher')}</li>
|
||||
<li className='lc' onClick={_use.bind(this, 'cm', null, 'Heat Sink Launcher')}>{translate('Heat Sink Launcher')}</li>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@ export const terms = {
|
||||
PHRASE_NO_RETROCH: 'No Retrofitting changes',
|
||||
PHRASE_SELECT_BUILDS: 'Select Builds to Compare',
|
||||
PHRASE_UPDATE_RDY: 'Update Available! Click to Refresh',
|
||||
PHRASE_UNLADEN: 'Ship Mass excluding Fuel and Cargo',
|
||||
PHRASE_LADEN: 'Ship Mass + Fuel + Cargo',
|
||||
PHRASE_FASTEST_RANGE: 'Consecutive max range jumps',
|
||||
PHRASE_SG_RECOVER: 'Recovery (to 50%) after collapse',
|
||||
PHRASE_SG_RECHARGE: 'Time from 50% to 100% Charge',
|
||||
am: 'Auto Field-Maintenance Unit',
|
||||
'Basic Discovery Scanner': 'Basic Discovery Scanner',
|
||||
bl: 'Beam Laser',
|
||||
|
||||
@@ -3,9 +3,10 @@ import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import '../less/app.less';
|
||||
import Coriolis from './Coriolis';
|
||||
import TapEventPlugin from 'react/lib/TapEventPlugin';
|
||||
import EventPluginHub from 'react/lib/EventPluginHub';
|
||||
// import TapEventPlugin from 'react/lib/TapEventPlugin';
|
||||
// import EventPluginHub from 'react/lib/EventPluginHub';
|
||||
|
||||
EventPluginHub.injection.injectEventPluginsByName({ TapEventPlugin });
|
||||
// onTouchTap not ready for primetime yet, too many issues with preventing default
|
||||
// EventPluginHub.injection.injectEventPluginsByName({ TapEventPlugin });
|
||||
|
||||
render(<Coriolis />, document.getElementById('coriolis'));
|
||||
|
||||
@@ -22,11 +22,10 @@ export default class AboutPage extends Page {
|
||||
* Render the Page
|
||||
* @return {React.Component} The page contents
|
||||
*/
|
||||
render() {
|
||||
renderPage() {
|
||||
return <div className={'page'} style={{ textAlign: 'left', maxWidth: 800, margin: '0 auto' }}>
|
||||
<h1><CoriolisLogo style={{ marginRight: '0.4em' }} className='xl'/><span className='warning'>Coriolis</span></h1>
|
||||
<p>The Coriolis project was inspired by <a href='http://www.edshipyard.com/' target='_blank'>E:D Shipyard</a> and, of course,
|
||||
<a href='http://www.elitedangerous.com' target='_blank'>Elite Dangerous</a>. The ultimate goal of Coriolis is to provide rich features to support in-game play and planning while engaging the E:D community to support its development.</p>
|
||||
<p>The Coriolis project was inspired by <a href='http://www.edshipyard.com/' target='_blank'>E:D Shipyard</a> and, of course, <a href='http://www.elitedangerous.com' target='_blank'>Elite Dangerous</a>. The ultimate goal of Coriolis is to provide rich features to support in-game play and planning while engaging the E:D community to support its development.</p>
|
||||
|
||||
<p>Coriolis was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments.</p>
|
||||
|
||||
@@ -36,8 +35,7 @@ export default class AboutPage extends Page {
|
||||
github.com/cmmcleod/coriolis
|
||||
</a>
|
||||
|
||||
<p>Coriolis is an open source project. Checkout the list of upcoming features and to-do list on github.
|
||||
Any and all contributions and feedback are welcome. If you encounter any bugs please report them and provide as much detail as possible.</p>
|
||||
<p>Coriolis is an open source project. Checkout the list of upcoming features and to-do list on github. Any and all contributions and feedback are welcome. If you encounter any bugs please report them and provide as much detail as possible.</p>
|
||||
|
||||
<form action='https://www.paypal.com/cgi-bin/webscr' method='post' target='_blank'>
|
||||
<input type='hidden' name='cmd' value='_s-xclick' />
|
||||
@@ -47,6 +45,9 @@ export default class AboutPage extends Page {
|
||||
</form>
|
||||
|
||||
<p>Help keep the lights on! Donations will be used to cover costs of running and maintaining Coriolis. Thanks for helping!</p>
|
||||
|
||||
<h3>Chat</h3>
|
||||
<p>You can chat to us on <a href='https://www.hipchat.com/gfYQiZcmy' target='_blank'>HipChat</a> and sign up to chat on all E:D HipChat groups <a href='https://elite-dangerous.hipchat.com/invite/74670/a3af7ea57008362d83d05fada09bdf84' target='_blank'>here</a>.</p>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { findDOMNode } from 'react-dom';
|
||||
import Page from './Page';
|
||||
import Router from '../Router';
|
||||
import cn from 'classnames';
|
||||
import { Ships } from 'coriolis-data';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Ship from '../shipyard/Ship';
|
||||
import { fromComparison, toComparison } from '../shipyard/Serializer';
|
||||
import Persist from '../stores/Persist';
|
||||
@@ -418,7 +418,7 @@ export default class ComparisonPage extends Page {
|
||||
* Render the Page
|
||||
* @return {React.Component} The page contents
|
||||
*/
|
||||
render() {
|
||||
renderPage() {
|
||||
let translate = this.context.language.translate;
|
||||
let compareHeader;
|
||||
let { newName, name, saved, builds, facets, predicate, desc, chartWidth } = this.state;
|
||||
@@ -428,17 +428,17 @@ export default class ComparisonPage extends Page {
|
||||
<td className='head'>{translate('comparison')}</td>
|
||||
<td>
|
||||
<input value={newName} onChange={this._onNameChange} placeholder={translate('Enter Name')} maxLength='50' />
|
||||
<button onTouchTap={this._save} disabled={!newName || newName == 'all' || saved}>
|
||||
<button onClick={this._save} disabled={!newName || newName == 'all' || saved}>
|
||||
<FloppyDisk className='lg'/><span className='button-lbl'>{translate('save')}</span>
|
||||
</button>
|
||||
<button onTouchTap={this._delete} disabled={name == 'all' || !saved}><Bin className='lg warning'/></button>
|
||||
<button onTouchTap={this._selectBuilds}>
|
||||
<button onClick={this._delete} disabled={name == 'all' || !saved}><Bin className='lg warning'/></button>
|
||||
<button onClick={this._selectBuilds}>
|
||||
<Rocket className='lg'/><span className='button-lbl'>{translate('builds')}</span>
|
||||
</button>
|
||||
<button className='r' onTouchTap={this._genPermalink} disabled={builds.length == 0}>
|
||||
<button className='r' onClick={this._genPermalink} disabled={builds.length == 0}>
|
||||
<LinkIcon className='lg'/><span className='button-lbl'>{translate('permalink')}</span>
|
||||
</button>
|
||||
<button className='r' onTouchTap={this._genBBcode} disabled={builds.length == 0}>
|
||||
<button className='r' onClick={this._genBBcode} disabled={builds.length == 0}>
|
||||
<Embed className='lg'/><span className='button-lbl'>{translate('forum')}</span>
|
||||
</button>
|
||||
</td>
|
||||
@@ -448,7 +448,7 @@ export default class ComparisonPage extends Page {
|
||||
<td className='head'>{translate('comparison')}</td>
|
||||
<td>
|
||||
<h3>{name}</h3>
|
||||
<button className='r' onTouchTap={this._import}><Download className='lg'/>{translate('import')}</button>
|
||||
<button className='r' onClick={this._import}><Download className='lg'/>{translate('import')}</button>
|
||||
</td>
|
||||
</tr>;
|
||||
}
|
||||
@@ -463,7 +463,7 @@ export default class ComparisonPage extends Page {
|
||||
<td>
|
||||
<ul id='facet-container' onDragOver={this._facetDragOver}>
|
||||
{facets.map((f, i) =>
|
||||
<li key={f.title} data-i={i} draggable='true' onDragStart={this._facetDrag} onDragEnd={this._facetDrop} className={cn('facet', { active: f.active })} onTouchTap={this._toggleFacet.bind(this, f)}>
|
||||
<li key={f.title} data-i={i} draggable='true' onDragStart={this._facetDrag} onDragEnd={this._facetDrop} className={cn('facet', { active: f.active })} onClick={this._toggleFacet.bind(this, f)}>
|
||||
{'↔ ' + translate(f.title)}
|
||||
</li>
|
||||
)}
|
||||
@@ -479,14 +479,15 @@ export default class ComparisonPage extends Page {
|
||||
<div className='chart' ref={'chartRef'}>{translate('PHRASE_NO_BUILDS')}</div> :
|
||||
facets.filter((f) => f.active).map((f, i) =>
|
||||
<div key={f.title} className='chart' ref={ i == 0 ? 'chartRef' : null}>
|
||||
<h3 className='ptr' onTouchTap={this._sortShips.bind(this, f.props[0])}>{translate(f.title)}</h3>
|
||||
<h3 className='ptr' onClick={this._sortShips.bind(this, f.props[0])}>{translate(f.title)}</h3>
|
||||
<BarChart
|
||||
width={chartWidth}
|
||||
data={builds}
|
||||
properties={f.props}
|
||||
labels={f.lbls}
|
||||
unit={translate(f.unit)}
|
||||
format={f.fmt}
|
||||
label={translate(f.title)}
|
||||
title={translate(f.title)}
|
||||
predicate={predicate}
|
||||
desc={desc}
|
||||
/>
|
||||
|
||||
48
src/app/pages/ErrorDetails.jsx
Normal file
48
src/app/pages/ErrorDetails.jsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Unexpected Error page / block
|
||||
*/
|
||||
export default class ErrorDetails extends React.Component {
|
||||
|
||||
static contextTypes = {
|
||||
route: React.PropTypes.object.isRequired,
|
||||
language: React.PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
error: React.PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the Page
|
||||
* @return {React.Component} The page contents
|
||||
*/
|
||||
render() {
|
||||
let content = null;
|
||||
let error = this.props.error;
|
||||
let ed = error.details;
|
||||
|
||||
if (ed) {
|
||||
content = <div style={{ textAlign:'left', fontSize:'0.8em', width: '43em', margin: '0 auto' }}>
|
||||
<div className='cen'>
|
||||
<a href='https://github.com/cmmcleod/coriolis/issues' target='_blank' title='Coriolis Github Project'>Create an issue on Github</a>
|
||||
{' if this keeps happening. Add these details:'}
|
||||
</div>
|
||||
<div style={{ marginTop: '2em' }}>
|
||||
<div><span className='warning'>Browser:</span> {window.navigator.userAgent}</div>
|
||||
<div><span className='warning'>Path:</span> {this.context.route.canonicalPath}</div>
|
||||
<div><span className='warning'>Error:</span> {error.type || 'Unknown'}</div>
|
||||
<div className='warning'>Details:</div>
|
||||
<div><pre>{typeof ed == 'object' ? Object.keys(ed).map((e) => `${e}: ${ed[e]}\n`) : ed}</pre></div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
return <div className='error'>
|
||||
<h1>Jameson, we have a problem..</h1>
|
||||
<h1><small>{error.message}</small></h1>
|
||||
{content}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
import React from 'react';
|
||||
import Page from './Page';
|
||||
|
||||
/**
|
||||
* Unexpected Error page
|
||||
* TODO: Implement properly and test
|
||||
*/
|
||||
export default class ErrorPage extends Page {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
title: 'Error!'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Page
|
||||
* @return {React.Component} The page contents
|
||||
*/
|
||||
render() {
|
||||
let msgPre, msgHighlight, msgPost, errorMessage, details, type;
|
||||
|
||||
switch (type) {
|
||||
case 404:
|
||||
msgPre = 'Page';
|
||||
msgHighlight = this.context.route.path;
|
||||
msgPost = 'Not Found';
|
||||
break;
|
||||
case 'no-ship':
|
||||
msgPre = 'Ship';
|
||||
msgHighlight = this.props.message;
|
||||
msgPost = 'does not exist';
|
||||
break;
|
||||
case 'build':
|
||||
msgPre = 'Build Failure!';
|
||||
break;
|
||||
default:
|
||||
msgPre = 'Uh, Jameson, we have a problem..';
|
||||
errorMessage = <div>Message:<pre>{this.props.message}</pre></div>;
|
||||
}
|
||||
|
||||
if (this.props.details) {
|
||||
details = <div>Details:<br/><pre>{this.props.details}</pre></div>;
|
||||
}
|
||||
|
||||
return <div className='error'>
|
||||
<h1>
|
||||
<span>{msgPre}</span>
|
||||
<small>{msgHighlight}</small>
|
||||
<span>{msgPost}</span>
|
||||
</h1>
|
||||
|
||||
<div style={{ textAlign:'left', fontSize:'0.8em', width: '43em', margin: '0 auto' }}>
|
||||
<div className='cen'>
|
||||
<a href='https://github.com/cmmcleod/coriolis/issues' target='_blank' title='Coriolis Github Project'>Create an issue on Github</a>
|
||||
if this keeps happening. Add these details:
|
||||
</div>
|
||||
<div style={{ marginTop: '2em' }}>
|
||||
<div>Browser: {window.navigator.userAgent}</div>
|
||||
<div>Path: {this.context.route.canonicalPath}</div>
|
||||
<div>Error:<br/>{this.props.type || 'Unknown'}</div>
|
||||
{errorMessage}
|
||||
{details}
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ export default class NotFoundPage extends Page {
|
||||
* Render the Page
|
||||
* @return {React.Component} The page contents
|
||||
*/
|
||||
render() {
|
||||
return <div className={'page'}>Page {JSON.stringify(this.props.context)} Not Found</div>;
|
||||
renderPage() {
|
||||
return <div className='page' style={{ marginTop: 30 }}>Page <small>{this.context.route.path}</small> Not Found</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import React from 'react';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
import Page from './Page';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import cn from 'classnames';
|
||||
import Page from './Page';
|
||||
import Router from '../Router';
|
||||
import Persist from '../stores/Persist';
|
||||
import { Ships } from 'coriolis-data';
|
||||
import Ship from '../shipyard/Ship';
|
||||
import { toDetailedBuild } from '../shipyard/Serializer';
|
||||
import { outfitURL } from '../utils/UrlGenerators';
|
||||
|
||||
import { FloppyDisk, Bin, Switch, Download, Reload, Fuel } from '../components/SvgIcons';
|
||||
import ShipSummaryTable from '../components/ShipSummaryTable';
|
||||
import StandardSlotSection from '../components/StandardSlotSection';
|
||||
@@ -51,9 +53,7 @@ export default class OutfittingPage extends Page {
|
||||
let savedCode = Persist.getBuild(shipId, buildName);
|
||||
|
||||
if (!data) {
|
||||
// TODO: throw Error for error page - Ship not found
|
||||
// Router.errorPage(details) - something along these lines
|
||||
throw { msg: 'Ship not found:' + shipId };
|
||||
return { error: { message: 'Ship not found: ' + shipId } };
|
||||
}
|
||||
|
||||
let ship = new Ship(shipId, data.properties, data.slots); // Create a new Ship instance
|
||||
@@ -67,6 +67,7 @@ export default class OutfittingPage extends Page {
|
||||
let fuelCapacity = ship.fuelCapacity;
|
||||
|
||||
return {
|
||||
error: null,
|
||||
title: 'Outfitting - ' + data.properties.name,
|
||||
costTab: Persist.getCostTab() || 'costs',
|
||||
buildName,
|
||||
@@ -77,7 +78,7 @@ export default class OutfittingPage extends Page {
|
||||
fuelCapacity,
|
||||
fuelLevel: 1,
|
||||
jumpRangeChartFunc: ship.calcJumpRangeWith.bind(ship, fuelCapacity),
|
||||
totalRangeChartFunc: ship.calcFastestRangeWith.bind(ship, fuelCapacity),
|
||||
fastestRangeChartFunc: ship.calcFastestRangeWith.bind(ship, fuelCapacity),
|
||||
speedChartFunc: ship.calcSpeedsWith.bind(ship, fuelCapacity)
|
||||
};
|
||||
}
|
||||
@@ -106,6 +107,7 @@ export default class OutfittingPage extends Page {
|
||||
_saveBuild() {
|
||||
let code = this.state.ship.toString();
|
||||
Persist.saveBuild(this.state.shipId, this.state.buildName, code);
|
||||
this._updateRoute(this.state.shipId, code, this.state.buildName);
|
||||
this.setState({ code, savedCode: code });
|
||||
}
|
||||
|
||||
@@ -130,7 +132,7 @@ export default class OutfittingPage extends Page {
|
||||
*/
|
||||
_deleteBuild() {
|
||||
Persist.deleteBuild(this.state.shipId, this.state.buildName);
|
||||
Router.go(`/outfit/${this.state.shipId}`);
|
||||
Router.go(outfitURL(this.state.shipId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,13 +170,7 @@ export default class OutfittingPage extends Page {
|
||||
* @param {string} buildName Current build name
|
||||
*/
|
||||
_updateRoute(shipId, code, buildName) {
|
||||
let qStr = '';
|
||||
|
||||
if (buildName) {
|
||||
qStr = '?bn=' + encodeURIComponent(buildName);
|
||||
}
|
||||
|
||||
Router.replace(`/outfit/${shipId}/${code}${qStr}`);
|
||||
Router.replace(outfitURL(shipId, code, buildName));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,7 +185,7 @@ export default class OutfittingPage extends Page {
|
||||
fuelLevel,
|
||||
fuelCapacity,
|
||||
jumpRangeChartFunc: ship.calcJumpRangeWith.bind(ship, fuel),
|
||||
totalRangeChartFunc: ship.calcFastestRangeWith.bind(ship, fuel),
|
||||
fastestRangeChartFunc: ship.calcFastestRangeWith.bind(ship, fuel),
|
||||
speedChartFunc: ship.calcSpeedsWith.bind(ship, fuel)
|
||||
});
|
||||
}
|
||||
@@ -198,10 +194,14 @@ export default class OutfittingPage extends Page {
|
||||
* Update dimenions from rendered DOM
|
||||
*/
|
||||
_updateDimensions() {
|
||||
let elem = findDOMNode(this.refs.chartThird);
|
||||
|
||||
if (elem) {
|
||||
this.setState({
|
||||
chartWidth: findDOMNode(this.refs.chartThird).offsetWidth
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update state based on context changes
|
||||
@@ -239,17 +239,19 @@ export default class OutfittingPage extends Page {
|
||||
* Render the Page
|
||||
* @return {React.Component} The page contents
|
||||
*/
|
||||
render() {
|
||||
let state = this.state;
|
||||
let { language, termtip, tooltip, sizeRatio, onWindowResize } = this.context;
|
||||
let { translate, units, formats } = language;
|
||||
let { ship, code, savedCode, buildName, chartWidth, fuelCapacity, fuelLevel } = state;
|
||||
let hide = tooltip.bind(null, null);
|
||||
let menu = this.props.currentMenu;
|
||||
let shipUpdated = this._shipUpdated;
|
||||
let hStr = ship.getHardpointsString();
|
||||
let sStr = ship.getStandardString();
|
||||
let iStr = ship.getInternalString();
|
||||
renderPage() {
|
||||
let state = this.state,
|
||||
{ language, termtip, tooltip, sizeRatio, onWindowResize } = this.context,
|
||||
{ translate, units, formats } = language,
|
||||
{ ship, code, savedCode, buildName, chartWidth, fuelCapacity, fuelLevel } = state,
|
||||
hide = tooltip.bind(null, null),
|
||||
menu = this.props.currentMenu,
|
||||
shipUpdated = this._shipUpdated,
|
||||
canSave = buildName && code !== savedCode,
|
||||
canReload = savedCode && canSave,
|
||||
hStr = ship.getHardpointsString(),
|
||||
sStr = ship.getStandardString(),
|
||||
iStr = ship.getInternalString();
|
||||
|
||||
return (
|
||||
<div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}>
|
||||
@@ -257,19 +259,19 @@ export default class OutfittingPage extends Page {
|
||||
<h1>{ship.name}</h1>
|
||||
<div id='build'>
|
||||
<input value={buildName} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxsize={50} />
|
||||
<button onTouchTap={this._saveBuild} disabled={!buildName || savedCode && code == savedCode} onMouseOver={termtip.bind(null, 'save')} onMouseOut={hide}>
|
||||
<button onClick={canSave && this._saveBuild} disabled={!canSave} onMouseOver={termtip.bind(null, 'save')} onMouseOut={hide}>
|
||||
<FloppyDisk className='lg' />
|
||||
</button>
|
||||
<button onTouchTap={this._reloadBuild} disabled={!savedCode || code == savedCode} onMouseOver={termtip.bind(null, 'reload')} onMouseOut={hide}>
|
||||
<button onClick={canReload && this._reloadBuild} disabled={!canReload} onMouseOver={termtip.bind(null, 'reload')} onMouseOut={hide}>
|
||||
<Reload className='lg'/>
|
||||
</button>
|
||||
<button className={'danger'} onTouchTap={this._deleteBuild} disabled={!savedCode} onMouseOver={termtip.bind(null, 'delete')} onMouseOut={hide}>
|
||||
<button className={'danger'} onClick={savedCode && this._deleteBuild} disabled={!savedCode} onMouseOver={termtip.bind(null, 'delete')} onMouseOut={hide}>
|
||||
<Bin className='lg'/>
|
||||
</button>
|
||||
<button onTouchTap={this._resetBuild} disabled={!code} onMouseOver={termtip.bind(null, 'reset')} onMouseOut={hide}>
|
||||
<button onClick={code && this._resetBuild} disabled={!code} onMouseOver={termtip.bind(null, 'reset')} onMouseOut={hide}>
|
||||
<Switch className='lg'/>
|
||||
</button>
|
||||
<button onTouchTap={this._exportBuild} disabled={!buildName} onMouseOver={termtip.bind(null, 'export')} onMouseOut={hide}>
|
||||
<button onClick={buildName && this._exportBuild} disabled={!buildName} onMouseOver={termtip.bind(null, 'export')} onMouseOut={hide}>
|
||||
<Download className='lg'/>
|
||||
</button>
|
||||
</div>
|
||||
@@ -302,12 +304,12 @@ export default class OutfittingPage extends Page {
|
||||
<LineChart
|
||||
width={chartWidth}
|
||||
xMax={ship.cargoCapacity}
|
||||
yMax={ship.unladenTotalRange}
|
||||
yMax={ship.unladenFastestRange}
|
||||
xUnit={translate('T')}
|
||||
yUnit={translate('LY')}
|
||||
yLabel={translate('total range')}
|
||||
yLabel={translate('fastest range')}
|
||||
xLabel={translate('cargo')}
|
||||
func={state.totalRangeChartFunc}
|
||||
func={state.fastestRangeChartFunc}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import ErrorDetails from './ErrorDetails';
|
||||
import { shallowEqual } from '../utils/UtilityFunctions';
|
||||
|
||||
/**
|
||||
@@ -16,7 +17,8 @@ export default class Page extends React.Component {
|
||||
hideModal: React.PropTypes.func.isRequired,
|
||||
tooltip: React.PropTypes.func.isRequired,
|
||||
termtip: React.PropTypes.func.isRequired,
|
||||
onWindowResize: React.PropTypes.func.isRequired
|
||||
onWindowResize: React.PropTypes.func.isRequired,
|
||||
onCommand: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
@@ -67,4 +69,16 @@ export default class Page extends React.Component {
|
||||
document.title = newState.title || 'Coriolis';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks error state before rendering the page contents.
|
||||
* Pages should catch all errors where possible capture details to state.error.
|
||||
* @return {React.Component} Page contents
|
||||
*/
|
||||
render() {
|
||||
if (this.state.error) {
|
||||
return <ErrorDetails error={this.state.error} />;
|
||||
}
|
||||
return this.renderPage();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import Page from './Page';
|
||||
import { Ships } from 'coriolis-data';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import cn from 'classnames';
|
||||
import Ship from '../shipyard/Ship';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
@@ -155,7 +155,7 @@ export default class ShipyardPage extends Page {
|
||||
* Render the Page
|
||||
* @return {React.Component} The page contents
|
||||
*/
|
||||
render() {
|
||||
renderPage() {
|
||||
let { translate, formats, units } = this.context.language;
|
||||
let fInt = formats.int;
|
||||
let fRound = formats.round;
|
||||
@@ -205,44 +205,44 @@ export default class ShipyardPage extends Page {
|
||||
<table style={{ fontSize:'0.85em', whiteSpace:'nowrap', margin: '0 auto' }} align='center'>
|
||||
<thead>
|
||||
<tr className='main'>
|
||||
<th rowSpan={2} className='sortable le' onTouchTap={sortShips('name')}>{translate('ship')}</th>
|
||||
<th rowSpan={2} className='sortable' onTouchTap={sortShips('manufacturer')}>{translate('manufacturer')}</th>
|
||||
<th rowSpan={2} className='sortable' onTouchTap={sortShips('class')}>{translate('size')}</th>
|
||||
<th rowSpan={2} className='sortable' onMouseEnter={tip.bind(null, 'maneuverability')} onMouseLeave={hide} onTouchTap={sortShips('agility')}>{translate('mnv')}</th>
|
||||
<th rowSpan={2} className='sortable le' onClick={sortShips('name')}>{translate('ship')}</th>
|
||||
<th rowSpan={2} className='sortable' onClick={sortShips('manufacturer')}>{translate('manufacturer')}</th>
|
||||
<th rowSpan={2} className='sortable' onClick={sortShips('class')}>{translate('size')}</th>
|
||||
<th rowSpan={2} className='sortable' onMouseEnter={tip.bind(null, 'maneuverability')} onMouseLeave={hide} onClick={sortShips('agility')}>{translate('mnv')}</th>
|
||||
<th colSpan={4}>{translate('base')}</th>
|
||||
<th colSpan={4}>{translate('max')}</th>
|
||||
<th colSpan={5} className='sortable' onTouchTap={sortShips('hpCount')}>{translate('hardpoints')}</th>
|
||||
<th colSpan={8} className='sortable' onTouchTap={sortShips('intCount')}>{translate('internal compartments')}</th>
|
||||
<th rowSpan={2} className='sortable' onTouchTap={sortShips('hullMass')}>{translate('hull')}</th>
|
||||
<th rowSpan={2} className='sortable' onMouseEnter={tip.bind(null, 'mass lock factor')} onMouseLeave={hide} onTouchTap={sortShips('masslock')} >{translate('MLF')}</th>
|
||||
<th rowSpan={2} className='sortable' onTouchTap={sortShips('retailCost')}>{translate('cost')}</th>
|
||||
<th colSpan={5} className='sortable' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th>
|
||||
<th colSpan={8} className='sortable' onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
|
||||
<th rowSpan={2} className='sortable' onClick={sortShips('hullMass')}>{translate('hull')}</th>
|
||||
<th rowSpan={2} className='sortable' onMouseEnter={tip.bind(null, 'mass lock factor')} onMouseLeave={hide} onClick={sortShips('masslock')} >{translate('MLF')}</th>
|
||||
<th rowSpan={2} className='sortable' onClick={sortShips('retailCost')}>{translate('cost')}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* Base */}
|
||||
<th className='sortable lft' onTouchTap={sortShips('speed')}>{translate('speed')}</th>
|
||||
<th className='sortable' onTouchTap={sortShips('boost')}>{translate('boost')}</th>
|
||||
<th className='sortable' onTouchTap={sortShips('baseArmour')}>{translate('armour')}</th>
|
||||
<th className='sortable' onTouchTap={sortShips('baseShieldStrength')}>{translate('shields')}</th>
|
||||
<th className='sortable lft' onClick={sortShips('speed')}>{translate('speed')}</th>
|
||||
<th className='sortable' onClick={sortShips('boost')}>{translate('boost')}</th>
|
||||
<th className='sortable' onClick={sortShips('baseArmour')}>{translate('armour')}</th>
|
||||
<th className='sortable' onClick={sortShips('baseShieldStrength')}>{translate('shields')}</th>
|
||||
{/* Max */}
|
||||
<th className='sortable lft' onTouchTap={sortShips('topSpeed')}>{translate('speed')}</th>
|
||||
<th className='sortable' onTouchTap={sortShips('topBoost')}>{translate('boost')}</th>
|
||||
<th className='sortable' onTouchTap={sortShips('maxJumpRange')}>{translate('jump')}</th>
|
||||
<th className='sortable' onTouchTap={sortShips('maxCargo')}>{translate('cargo')}</th>
|
||||
<th className='sortable lft' onClick={sortShips('topSpeed')}>{translate('speed')}</th>
|
||||
<th className='sortable' onClick={sortShips('topBoost')}>{translate('boost')}</th>
|
||||
<th className='sortable' onClick={sortShips('maxJumpRange')}>{translate('jump')}</th>
|
||||
<th className='sortable' onClick={sortShips('maxCargo')}>{translate('cargo')}</th>
|
||||
{/* Hardpoints */}
|
||||
<th className='sortable lft' onTouchTap={sortShips('hp',1)}>{translate('S')}</th>
|
||||
<th className='sortable' onTouchTap={sortShips('hp', 2)}>{translate('M')}</th>
|
||||
<th className='sortable' onTouchTap={sortShips('hp', 3)}>{translate('L')}</th>
|
||||
<th className='sortable' onTouchTap={sortShips('hp', 4)}>{translate('H')}</th>
|
||||
<th className='sortable' onTouchTap={sortShips('hp', 0)}>{translate('U')}</th>
|
||||
<th className='sortable lft' onClick={sortShips('hp',1)}>{translate('S')}</th>
|
||||
<th className='sortable' onClick={sortShips('hp', 2)}>{translate('M')}</th>
|
||||
<th className='sortable' onClick={sortShips('hp', 3)}>{translate('L')}</th>
|
||||
<th className='sortable' onClick={sortShips('hp', 4)}>{translate('H')}</th>
|
||||
<th className='sortable' onClick={sortShips('hp', 0)}>{translate('U')}</th>
|
||||
{/* Internal */}
|
||||
<th className='sortable lft' onTouchTap={sortShips('int', 0)} >1</th>
|
||||
<th className='sortable' onTouchTap={sortShips('int', 1)} >2</th>
|
||||
<th className='sortable' onTouchTap={sortShips('int', 2)} >3</th>
|
||||
<th className='sortable' onTouchTap={sortShips('int', 3)} >4</th>
|
||||
<th className='sortable' onTouchTap={sortShips('int', 4)} >5</th>
|
||||
<th className='sortable' onTouchTap={sortShips('int', 5)} >6</th>
|
||||
<th className='sortable' onTouchTap={sortShips('int', 6)} >7</th>
|
||||
<th className='sortable' onTouchTap={sortShips('int', 7)} >8</th>
|
||||
<th className='sortable lft' onClick={sortShips('int', 0)} >1</th>
|
||||
<th className='sortable' onClick={sortShips('int', 1)} >2</th>
|
||||
<th className='sortable' onClick={sortShips('int', 2)} >3</th>
|
||||
<th className='sortable' onClick={sortShips('int', 3)} >4</th>
|
||||
<th className='sortable' onClick={sortShips('int', 4)} >5</th>
|
||||
<th className='sortable' onClick={sortShips('int', 5)} >6</th>
|
||||
<th className='sortable' onClick={sortShips('int', 6)} >7</th>
|
||||
<th className='sortable' onClick={sortShips('int', 7)} >8</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@@ -12,25 +12,25 @@ export function jumpRange(mass, fsd, fuel) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the total range based on mass and a specific FSD, and all fuel available
|
||||
* Calculate the fastest (total) range based on mass and a specific FSD, and all fuel available
|
||||
*
|
||||
* @param {number} mass Mass of a ship: laden, unlanden, partially laden, etc
|
||||
* @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass
|
||||
* @param {number} fuel The total fuel available
|
||||
* @return {number} Distance in Light Years
|
||||
*/
|
||||
export function totalRange(mass, fsd, fuel) {
|
||||
export function fastestRange(mass, fsd, fuel) {
|
||||
let fuelRemaining = fuel % fsd.maxfuel; // Fuel left after making N max jumps
|
||||
let jumps = Math.floor(fuel / fsd.maxfuel);
|
||||
mass += fuelRemaining;
|
||||
// Going backwards, start with the last jump using the remaining fuel
|
||||
let totalRange = fuelRemaining > 0 ? Math.pow(fuelRemaining / fsd.fuelmul, 1 / fsd.fuelpower) * fsd.optmass / mass : 0;
|
||||
let fastestRange = fuelRemaining > 0 ? Math.pow(fuelRemaining / fsd.fuelmul, 1 / fsd.fuelpower) * fsd.optmass / mass : 0;
|
||||
// For each max fuel jump, calculate the max jump range based on fuel mass left in the tank
|
||||
for (let j = 0; j < jumps; j++) {
|
||||
mass += fsd.maxfuel;
|
||||
totalRange += Math.pow(fsd.maxfuel / fsd.fuelmul, 1 / fsd.fuelpower) * fsd.optmass / mass;
|
||||
fastestRange += Math.pow(fsd.maxfuel / fsd.fuelmul, 1 / fsd.fuelpower) * fsd.optmass / mass;
|
||||
}
|
||||
return totalRange;
|
||||
return fastestRange;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -164,8 +164,8 @@ export const ShipFacets = [
|
||||
i: 9
|
||||
},
|
||||
{ // 10
|
||||
title: 'total range',
|
||||
props: ['unladenTotalRange', 'ladenTotalRange'],
|
||||
title: 'fastest range',
|
||||
props: ['unladenFastestRange', 'ladenFastestRange'],
|
||||
lbls: ['unladen', 'laden'],
|
||||
unit: 'LY',
|
||||
fmt: 'round',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ModuleNameToGroup, BulkheadNames } from './Constants';
|
||||
import ModuleSet from './ModuleSet';
|
||||
import { Ships, Modules } from 'coriolis-data';
|
||||
import { Ships, Modules } from 'coriolis-data/dist';
|
||||
|
||||
/**
|
||||
* Created a cargo hatch model
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ModuleGroupToName, MountMap, BulkheadNames } from './Constants';
|
||||
import { Ships } from 'coriolis-data';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Ship from './Ship';
|
||||
import * as ModuleUtils from './ModuleUtils';
|
||||
import LZString from 'lz-string';
|
||||
|
||||
@@ -7,9 +7,9 @@ const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs'];
|
||||
|
||||
/**
|
||||
* Returns the power usage type of a slot and it's particular modul
|
||||
* @param {object} slot The Slot
|
||||
* @param {object} modul The modul in the slot
|
||||
* @return {string} The key for the power usage type
|
||||
* @param {Object} slot The Slot
|
||||
* @param {Object} modul The modul in the slot
|
||||
* @return {String} The key for the power usage type
|
||||
*/
|
||||
function powerUsageType(slot, modul) {
|
||||
if (modul) {
|
||||
@@ -23,10 +23,10 @@ function powerUsageType(slot, modul) {
|
||||
/**
|
||||
* Populates the category array with module IDs from
|
||||
* the provided code
|
||||
* @param {string} code Serialized ship code
|
||||
* @param {String} code Serialized ship code
|
||||
* @param {Array} arr Category array
|
||||
* @param {number} codePos Current position/Index of code string
|
||||
* @return {number} Next position/Index of code string
|
||||
* @param {Number} codePos Current position/Index of code string
|
||||
* @return {Number} Next position/Index of code string
|
||||
*/
|
||||
function decodeToArray(code, arr, codePos) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
@@ -44,7 +44,7 @@ function decodeToArray(code, arr, codePos) {
|
||||
/**
|
||||
* Reduce function used to get the IDs for a slot group (or array of slots)
|
||||
* @param {array} idArray The current Array of IDs
|
||||
* @param {object} slot Slot object
|
||||
* @param {Object} slot Slot object
|
||||
* @param {integer} slotIndex The index for the slot in its group
|
||||
* @return {array} The mutated idArray
|
||||
*/
|
||||
@@ -59,9 +59,9 @@ function reduceToIDs(idArray, slot, slotIndex) {
|
||||
export default class Ship {
|
||||
|
||||
/**
|
||||
* @param {string} id Unique ship Id / Key
|
||||
* @param {object} properties Basic ship properties such as name, manufacturer, mass, etc
|
||||
* @param {object} slots Collection of slot groups (standard/standard, internal, hardpoints) with their max class size.
|
||||
* @param {String} id Unique ship Id / Key
|
||||
* @param {Object} properties Basic ship properties such as name, manufacturer, mass, etc
|
||||
* @param {Object} slots Collection of slot groups (standard/standard, internal, hardpoints) with their max class size.
|
||||
*/
|
||||
constructor(id, properties, slots) {
|
||||
this.id = id;
|
||||
@@ -131,9 +131,9 @@ export default class Ship {
|
||||
/**
|
||||
* Calculate hypothetical jump range using the installed FSD and the
|
||||
* specified mass which can be more or less than ships actual mass
|
||||
* @param {number} fuel Fuel available in tons
|
||||
* @param {number} cargo Cargo in tons
|
||||
* @return {number} Jump range in Light Years
|
||||
* @param {Number} fuel Fuel available in tons
|
||||
* @param {Number} cargo Cargo in tons
|
||||
* @return {Number} Jump range in Light Years
|
||||
*/
|
||||
calcJumpRangeWith(fuel, cargo) {
|
||||
return Calc.jumpRange(this.unladenMass + fuel + cargo, this.standard[2].m, fuel);
|
||||
@@ -141,10 +141,10 @@ export default class Ship {
|
||||
|
||||
/**
|
||||
* Calculate the hypothetical laden jump range based on a potential change in mass, fuel, or FSD
|
||||
* @param {number} massDelta Optional - Change in laden mass (mass + cargo + fuel)
|
||||
* @param {number} fuel Optional - Available fuel (defaults to max fuel based on FSD)
|
||||
* @param {Number} massDelta Optional - Change in laden mass (mass + cargo + fuel)
|
||||
* @param {Number} fuel Optional - Available fuel (defaults to max fuel based on FSD)
|
||||
* @param {Object} fsd Optional - Frame Shift Drive (or use mounted FSD)
|
||||
* @return {number} Jump range in Light Years
|
||||
* @return {Number} Jump range in Light Years
|
||||
*/
|
||||
calcLadenRange(massDelta, fuel, fsd) {
|
||||
return Calc.jumpRange(this.ladenMass + (massDelta || 0), fsd || this.standard[2].m, fuel);
|
||||
@@ -152,10 +152,10 @@ export default class Ship {
|
||||
|
||||
/**
|
||||
* Calculate the hypothetical unladen jump range based on a potential change in mass, fuel, or FSD
|
||||
* @param {number} massDelta Optional - Change in ship mass
|
||||
* @param {number} fuel Optional - Available fuel (defaults to lesser of fuel capacity or max fuel based on FSD)
|
||||
* @param {Number} massDelta Optional - Change in ship mass
|
||||
* @param {Number} fuel Optional - Available fuel (defaults to lesser of fuel capacity or max fuel based on FSD)
|
||||
* @param {Object} fsd Optional - Frame Shift Drive (or use mounted FSD)
|
||||
* @return {number} Jump range in Light Years
|
||||
* @return {Number} Jump range in Light Years
|
||||
*/
|
||||
calcUnladenRange(massDelta, fuel, fsd) {
|
||||
fsd = fsd || this.standard[2].m;
|
||||
@@ -165,44 +165,71 @@ export default class Ship {
|
||||
/**
|
||||
* Calculate cumulative (total) jump range when making longest jumps using the installed FSD and the
|
||||
* specified mass which can be more or less than ships actual mass
|
||||
* @param {number} fuel Fuel available in tons
|
||||
* @param {number} cargo Cargo in tons
|
||||
* @return {number} Total/Cumulative Jump range in Light Years
|
||||
* @param {Number} fuel Fuel available in tons
|
||||
* @param {Number} cargo Cargo in tons
|
||||
* @return {Number} Total/Cumulative Jump range in Light Years
|
||||
*/
|
||||
calcFastestRangeWith(fuel, cargo) {
|
||||
return Calc.totalRange(this.unladenMass + fuel + cargo, this.standard[2].m, fuel);
|
||||
return Calc.fastestRange(this.unladenMass + fuel + cargo, this.standard[2].m, fuel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the hypothetical top speeds at cargo and fuel tonnage
|
||||
* @param {number} fuel Fuel available in tons
|
||||
* @param {number} cargo Cargo in tons
|
||||
* @param {Number} fuel Fuel available in tons
|
||||
* @param {Number} cargo Cargo in tons
|
||||
* @return {Object} Speed at pip settings and boost
|
||||
*/
|
||||
calcSpeedsWith(fuel, cargo) {
|
||||
return Calc.speed(this.unladenMass + fuel + cargo, this.speed, this.boost, this.standard[1].m, this.pipSpeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the recovery time after losing or turning on shields
|
||||
* Thanks to CMDRs Al Gray, GIF, and Nomad Enigma for providing Shield recharge data and formulas
|
||||
*
|
||||
* @return {Number} Recovery time in seconds
|
||||
*/
|
||||
calcShieldRecovery() {
|
||||
if (this.shieldStrength && this.sgSlot) {
|
||||
// 50% of shield strength / recovery recharge rate + 15 second delay before recharge starts
|
||||
return ((this.shieldStrength / 2) / this.sgSlot.m.recover) + 15;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the recharge time for a shield going from 50% to 100%
|
||||
* Thanks to CMDRs Al Gray, GIF, and Nomad Enigma for providing Shield recharge data and formulas
|
||||
*
|
||||
* @return {Number} 50 - 100% Recharge time in seconds
|
||||
*/
|
||||
calcShieldRecharge() {
|
||||
if (this.shieldStrength && this.sgSlot) {
|
||||
// 50% -> 100% recharge time, Bi-Weave shields charge at 1.8 MJ/s
|
||||
return (this.shieldStrength / 2) / (this.sgSlot.m.grp == 'bsg' ? 1.8 : 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the hypothetical shield strength for the ship using the specified parameters
|
||||
* @param {Object} sg [optional] Shield Generator to use
|
||||
* @param {number} multiplierDelta [optional] Change to shield multiplier (+0.2, - 0.12, etc)
|
||||
* @return {number} Shield strength in MH
|
||||
* @param {Number} multiplierDelta [optional] Change to shield multiplier (+0.2, - 0.12, etc)
|
||||
* @return {Number} Shield strength in MH
|
||||
*/
|
||||
calcShieldStrengthWith(sg, multiplierDelta) {
|
||||
if (!sg) {
|
||||
let sgSlot = this.findInternalByGroup('sg'); // Find Shield Generator slot Index if any
|
||||
if (!sgSlot) {
|
||||
if (!this.sgSlot) {
|
||||
return 0;
|
||||
}
|
||||
sg = sgSlot.m;
|
||||
sg = this.sgSlot.m;
|
||||
}
|
||||
return Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sg, this.shieldMultiplier + (multiplierDelta || 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* [getAvailableModules description]
|
||||
* @return {[type]} [description]
|
||||
* Get the set of available modules for this ship
|
||||
* @return {ModuleSet} Available module set
|
||||
*/
|
||||
getAvailableModules() {
|
||||
return this.availCS;
|
||||
@@ -216,7 +243,7 @@ export default class Ship {
|
||||
* 3 - Online
|
||||
* @param {Object} slot Slot model
|
||||
* @param {boolean} deployed True - power used when hardpoints are deployed
|
||||
* @return {number} status index
|
||||
* @return {Number} status index
|
||||
*/
|
||||
getSlotStatus(slot, deployed) {
|
||||
if (!slot.m) { // Empty Slot
|
||||
@@ -235,11 +262,10 @@ export default class Ship {
|
||||
/**
|
||||
* Find an internal slot that has an installed modul of the specific group.
|
||||
*
|
||||
* @param {string} group Module group/type
|
||||
* @return {number} The index of the slot in ship.internal
|
||||
* @param {String} group Module group/type
|
||||
* @return {Number} The index of the slot in ship.internal
|
||||
*/
|
||||
findInternalByGroup(group) {
|
||||
let index;
|
||||
if (ModuleUtils.isShieldGenerator(group)) {
|
||||
return this.internal.find(slot => slot.m && ModuleUtils.isShieldGenerator(slot.m.grp));
|
||||
} else {
|
||||
@@ -249,7 +275,7 @@ export default class Ship {
|
||||
|
||||
/**
|
||||
* Serializes the ship to a string
|
||||
* @return {string} Serialized ship 'code'
|
||||
* @return {String} Serialized ship 'code'
|
||||
*/
|
||||
toString() {
|
||||
return [
|
||||
@@ -265,7 +291,7 @@ export default class Ship {
|
||||
|
||||
/**
|
||||
* Serializes the standard modules to a string
|
||||
* @return {string} Serialized standard modules 'code'
|
||||
* @return {String} Serialized standard modules 'code'
|
||||
*/
|
||||
getStandardString() {
|
||||
if(!this.serialized.standard) {
|
||||
@@ -279,7 +305,7 @@ export default class Ship {
|
||||
|
||||
/**
|
||||
* Serializes the internal modules to a string
|
||||
* @return {string} Serialized internal modules 'code'
|
||||
* @return {String} Serialized internal modules 'code'
|
||||
*/
|
||||
getInternalString() {
|
||||
if(!this.serialized.internal) {
|
||||
@@ -290,7 +316,7 @@ export default class Ship {
|
||||
|
||||
/**
|
||||
* Serializes the hardpoints and utility modules to a string
|
||||
* @return {string} Serialized hardpoints and utility modules 'code'
|
||||
* @return {String} Serialized hardpoints and utility modules 'code'
|
||||
*/
|
||||
getHardpointsString() {
|
||||
if(!this.serialized.hardpoints) {
|
||||
@@ -301,7 +327,7 @@ export default class Ship {
|
||||
|
||||
/**
|
||||
* Get the serialized module active/inactive settings
|
||||
* @return {string} Serialized active/inactive settings
|
||||
* @return {String} Serialized active/inactive settings
|
||||
*/
|
||||
getPowerEnabledString() {
|
||||
return this.serialized.enabled;
|
||||
@@ -309,7 +335,7 @@ export default class Ship {
|
||||
|
||||
/**
|
||||
* Get the serialized module priority settings
|
||||
* @return {string} Serialized priority settings
|
||||
* @return {String} Serialized priority settings
|
||||
*/
|
||||
getPowerPrioritesString() {
|
||||
return this.serialized.priorities;
|
||||
@@ -319,8 +345,8 @@ export default class Ship {
|
||||
|
||||
/**
|
||||
* Recalculate all item costs and total based on discounts.
|
||||
* @param {number} shipCostMultiplier Ship cost multiplier discount (e.g. 0.9 === 10% discount)
|
||||
* @param {number} moduleCostMultiplier Module cost multiplier discount (e.g. 0.75 === 25% discount)
|
||||
* @param {Number} shipCostMultiplier Ship cost multiplier discount (e.g. 0.9 === 10% discount)
|
||||
* @param {Number} moduleCostMultiplier Module cost multiplier discount (e.g. 0.75 === 25% discount)
|
||||
* @return {this} The current ship instance for chaining
|
||||
*/
|
||||
applyDiscounts(shipCostMultiplier, moduleCostMultiplier) {
|
||||
@@ -442,7 +468,7 @@ export default class Ship {
|
||||
* Updates an existing ship instance's slots with modules determined by the
|
||||
* code.
|
||||
*
|
||||
* @param {string} serializedString The string to deserialize
|
||||
* @param {String} serializedString The string to deserialize
|
||||
* @return {this} The current ship instance for chaining
|
||||
*/
|
||||
buildFrom(serializedString) {
|
||||
@@ -527,7 +553,7 @@ export default class Ship {
|
||||
/**
|
||||
* Optimize for the lower mass build that can still boost and power the ship
|
||||
* without power management.
|
||||
* @param {object} m Standard Module overrides
|
||||
* @param {Object} m Standard Module overrides
|
||||
* @return {this} The current ship instance for chaining
|
||||
*/
|
||||
optimizeMass(m) {
|
||||
@@ -537,7 +563,7 @@ export default class Ship {
|
||||
/**
|
||||
* Include/Exclude a item/slot in cost calculations
|
||||
* @param {Object} item Slot or item
|
||||
* @param {Boolean} included Cost included
|
||||
* @param {boolean} included Cost included
|
||||
* @return {this} The current ship instance for chaining
|
||||
*/
|
||||
setCostIncluded(item, included) {
|
||||
@@ -551,7 +577,7 @@ export default class Ship {
|
||||
/**
|
||||
* Set slot active/inactive
|
||||
* @param {Object} slot Slot model
|
||||
* @param {Boolean} enabled True - active
|
||||
* @param {boolean} enabled True - active
|
||||
* @return {this} The current ship instance for chaining
|
||||
*/
|
||||
setSlotEnabled(slot, enabled) {
|
||||
@@ -578,8 +604,8 @@ export default class Ship {
|
||||
|
||||
/**
|
||||
* Will change the priority of the specified slot if the new priority is valid
|
||||
* @param {object} slot The slot to be updated
|
||||
* @param {number} newPriority The new priority to be set
|
||||
* @param {Object} slot The slot to be updated
|
||||
* @param {Number} newPriority The new priority to be set
|
||||
* @return {boolean} Returns true if the priority was changed (within range)
|
||||
*/
|
||||
setSlotPriority(slot, newPriority) {
|
||||
@@ -601,9 +627,9 @@ export default class Ship {
|
||||
|
||||
/**
|
||||
* Updates the ship's cumulative and aggregated stats based on the module change.
|
||||
* @param {object} slot The slot being updated
|
||||
* @param {object} n The new module (may be null)
|
||||
* @param {object} old The old module (may be null)
|
||||
* @param {Object} slot The slot being updated
|
||||
* @param {Object} n The new module (may be null)
|
||||
* @param {Object} old The old module (may be null)
|
||||
* @param {boolean} preventUpdate If true the global ship state will not be updated
|
||||
* @return {this} The ship instance (for chaining operations)
|
||||
*/
|
||||
@@ -722,8 +748,8 @@ export default class Ship {
|
||||
* @return {this} The ship instance (for chaining operations)
|
||||
*/
|
||||
updateShieldStrength() {
|
||||
let sgSlot = this.findInternalByGroup('sg'); // Find Shield Generator slot Index if any
|
||||
this.shieldStrength = sgSlot && sgSlot.enabled ? Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sgSlot.m, this.shieldMultiplier) : 0;
|
||||
this.sgSlot = this.findInternalByGroup('sg'); // Find Shield Generator slot Index if any
|
||||
this.shieldStrength = this.sgSlot && this.sgSlot.enabled ? Calc.shieldStrength(this.hullMass, this.baseShieldStrength, this.sgSlot.m, this.shieldMultiplier) : 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -737,8 +763,8 @@ export default class Ship {
|
||||
this.unladenRange = this.calcUnladenRange(); // Includes fuel weight for jump
|
||||
this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd); // Full Tank
|
||||
this.ladenRange = this.calcLadenRange(); // Includes full tank and caro
|
||||
this.unladenTotalRange = Calc.totalRange(unladenMass, fsd, fuelCapacity);
|
||||
this.ladenTotalRange = Calc.totalRange(unladenMass + this.cargoCapacity, fsd, fuelCapacity);
|
||||
this.unladenFastestRange = Calc.fastestRange(unladenMass, fsd, fuelCapacity);
|
||||
this.ladenFastestRange = Calc.fastestRange(unladenMass + this.cargoCapacity, fsd, fuelCapacity);
|
||||
this.maxJumpCount = Math.ceil(fuelCapacity / fsd.maxfuel);
|
||||
return this;
|
||||
}
|
||||
@@ -791,7 +817,7 @@ export default class Ship {
|
||||
*
|
||||
* @param {Object} slot The modul slot
|
||||
* @param {Object} m Properties for the selected module
|
||||
* @param {Boolean} preventUpdate If true, do not update aggregated stats
|
||||
* @param {boolean} preventUpdate If true, do not update aggregated stats
|
||||
* @return {this} The ship instance (for chaining operations)
|
||||
*/
|
||||
use(slot, m, preventUpdate) {
|
||||
@@ -823,8 +849,8 @@ export default class Ship {
|
||||
|
||||
/**
|
||||
* Mount the specified bulkhead type (index)
|
||||
* @param {number} index Bulkhead index [0-4]
|
||||
* @param {Boolean} preventUpdate Prevent summary update
|
||||
* @param {Number} index Bulkhead index [0-4]
|
||||
* @param {boolean} preventUpdate Prevent summary update
|
||||
* @return {this} The ship instance (for chaining operations)
|
||||
*/
|
||||
useBulkhead(index, preventUpdate) {
|
||||
@@ -841,7 +867,7 @@ export default class Ship {
|
||||
/**
|
||||
* Set all standard slots to use the speficied rating and class based on
|
||||
* the slot's max class
|
||||
* @param {string} rating Module Rating (A-E)
|
||||
* @param {String} rating Module Rating (A-E)
|
||||
* @return {this} The ship instance (for chaining operations)
|
||||
*/
|
||||
useStandard(rating) {
|
||||
@@ -854,7 +880,7 @@ export default class Ship {
|
||||
|
||||
/**
|
||||
* Use the lightest standard ModuleUtils unless otherwise specified
|
||||
* @param {object} m Module overrides
|
||||
* @param {Object} m Module overrides
|
||||
* @return {this} The ship instance (for chaining operations)
|
||||
*/
|
||||
useLightestStandard(m) {
|
||||
@@ -898,9 +924,9 @@ export default class Ship {
|
||||
|
||||
/**
|
||||
* Fill all utility slots with the specified module
|
||||
* @param {string} group Group name
|
||||
* @param {string} rating Rating [A-I]
|
||||
* @param {string} name Module name
|
||||
* @param {String} group Group name
|
||||
* @param {String} rating Rating [A-I]
|
||||
* @param {String} name Module name
|
||||
* @param {boolean} clobber Overwrite non-empty slots
|
||||
* @return {this} The ship instance (for chaining operations)
|
||||
*/
|
||||
|
||||
@@ -14,8 +14,8 @@ let LS;
|
||||
|
||||
// Safe check to determine if localStorage is enabled
|
||||
try {
|
||||
localStorage.setItem('test_string', 1);
|
||||
localStorage.removeItem('test_string');
|
||||
localStorage.setItem('test', 'test');
|
||||
localStorage.removeItem('test');
|
||||
LS = localStorage;
|
||||
} catch(e) {
|
||||
LS = null;
|
||||
@@ -23,7 +23,7 @@ try {
|
||||
|
||||
/**
|
||||
* Safe localstorage put
|
||||
* @param {string} key key
|
||||
* @param {String} key key
|
||||
* @param {any} value data to store
|
||||
*/
|
||||
function _put(key, value) {
|
||||
@@ -34,8 +34,8 @@ function _put(key, value) {
|
||||
|
||||
/**
|
||||
* Safe localstorage get string
|
||||
* @param {string} key key
|
||||
* @return {string} The stored string
|
||||
* @param {String} key key
|
||||
* @return {String} The stored string
|
||||
*/
|
||||
function _getString(key) {
|
||||
return LS ? LS.getItem(key) : null;
|
||||
@@ -43,7 +43,7 @@ function _getString(key) {
|
||||
|
||||
/**
|
||||
* Safe localstorage get
|
||||
* @param {string} key key
|
||||
* @param {String} key key
|
||||
* @return {object | number} The stored data
|
||||
*/
|
||||
function _get(key) {
|
||||
@@ -53,7 +53,7 @@ function _get(key) {
|
||||
|
||||
/**
|
||||
* Safe localstorage delete
|
||||
* @param {string} key key
|
||||
* @param {String} key key
|
||||
*/
|
||||
function _delete(key) {
|
||||
if (LS) {
|
||||
@@ -64,8 +64,10 @@ function _delete(key) {
|
||||
|
||||
/**
|
||||
* Persist store / service for all user settings. Currently uses localstorage only
|
||||
* Should be treated as a singleton / single instance should be used hence why the default
|
||||
* export is an instance (see end of this file).
|
||||
*/
|
||||
class Persist extends EventEmitter {
|
||||
export class Persist extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Create an instance
|
||||
@@ -75,22 +77,69 @@ class Persist extends EventEmitter {
|
||||
let buildJson = _get(LS_KEY_BUILDS);
|
||||
let comparisonJson = _get(LS_KEY_COMPARISONS);
|
||||
let tips = _get(LS_KEY_TOOLTIPS);
|
||||
let discounts = _get(LS_KEY_DISCOUNTS);
|
||||
|
||||
this.onStorageChange = this.onStorageChange.bind(this);
|
||||
this.langCode = _getString(LS_KEY_LANG) || 'en';
|
||||
this.insurance = _getString(LS_KEY_INSURANCE) || 'standard';
|
||||
this.discounts = discounts && !isNaN(discounts[0]) && !isNaN(discounts[1]) ? discounts : [1, 1];
|
||||
this.builds = buildJson ? buildJson : {};
|
||||
this.comparisons = comparisonJson ? comparisonJson : {};
|
||||
this.buildCount = Object.keys(this.builds).length;
|
||||
this.langCode = _getString(LS_KEY_LANG) || 'en';
|
||||
this.insurance = _getString(LS_KEY_INSURANCE) || 'standard';
|
||||
this.discounts = _get(LS_KEY_DISCOUNTS) || [1, 1];
|
||||
this.costTab = _getString(LS_KEY_COST_TAB);
|
||||
this.state = _get(LS_KEY_STATE);
|
||||
this.sizeRatio = _get(LS_KEY_SIZE_RATIO) || 1;
|
||||
this.tooltipsEnabled = tips === null ? true : tips;
|
||||
|
||||
if (LS) {
|
||||
window.addEventListener('storage', this.onStorageChange);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to storage changes from other windows/tabs
|
||||
* and update accordingly
|
||||
* @param {StorageEvent} e Storage Event
|
||||
*/
|
||||
onStorageChange(e) {
|
||||
let newValue = e.newValue;
|
||||
|
||||
try {
|
||||
switch(e.key) {
|
||||
case LS_KEY_BUILDS:
|
||||
this.builds = newValue ? JSON.parse(newValue) : {};
|
||||
this.emit('builds');
|
||||
break;
|
||||
case LS_KEY_COMPARISONS:
|
||||
this.comparisons = newValue ? JSON.parse(newValue) : {};
|
||||
this.emit('comparisons');
|
||||
break;
|
||||
case LS_KEY_LANG:
|
||||
this.langCode = newValue;
|
||||
this.emit('language', newValue);
|
||||
break;
|
||||
case LS_KEY_INSURANCE:
|
||||
this.insurance = newValue;
|
||||
this.emit('insurance', newValue);
|
||||
break;
|
||||
case LS_KEY_DISCOUNTS:
|
||||
this.discounts = JSON.parse(newValue);
|
||||
this.emit('discounts', this.discounts);
|
||||
break;
|
||||
case LS_KEY_TOOLTIPS:
|
||||
this.tooltipsEnabled = !!newValue && newValue.toLowerCase() == 'true';
|
||||
this.emit('tooltips', this.tooltipsEnabled);
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
// On JSON.Parse Error - don't sync or do anything
|
||||
console && console.error && console.error('Localstorage Sync Error', e); // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current language code
|
||||
* @return {stirng} language code
|
||||
* @return {String} language code
|
||||
*/
|
||||
getLangCode() {
|
||||
return this.langCode;
|
||||
@@ -98,7 +147,7 @@ class Persist extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Update and save the current language
|
||||
* @param {string} langCode language code
|
||||
* @param {String} langCode language code
|
||||
*/
|
||||
setLangCode(langCode) {
|
||||
this.langCode = langCode;
|
||||
@@ -124,9 +173,9 @@ class Persist extends EventEmitter {
|
||||
/**
|
||||
* Persist a ship build in local storage.
|
||||
*
|
||||
* @param {string} shipId The unique id for a model of ship
|
||||
* @param {string} name The name of the build
|
||||
* @param {string} code The serialized code
|
||||
* @param {String} shipId The unique id for a model of ship
|
||||
* @param {String} name The name of the build
|
||||
* @param {String} code The serialized code
|
||||
*/
|
||||
saveBuild(shipId, name, code) {
|
||||
if (!this.builds[shipId]) {
|
||||
@@ -134,16 +183,16 @@ class Persist extends EventEmitter {
|
||||
}
|
||||
this.builds[shipId][name] = code;
|
||||
_put(LS_KEY_BUILDS, this.builds);
|
||||
this.emit('buildSaved', shipId, name, code);
|
||||
this.emit('builds');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the serialized code/string for a build. Returns null if a
|
||||
* build is not found.
|
||||
*
|
||||
* @param {string} shipId The unique id for a model of ship
|
||||
* @param {string} name The name of the build
|
||||
* @return {string} The serialized build string.
|
||||
* @param {String} shipId The unique id for a model of ship
|
||||
* @param {String} name The name of the build
|
||||
* @return {String} The serialized build string.
|
||||
*/
|
||||
getBuild(shipId, name) {
|
||||
if (this.builds[shipId] && this.builds[shipId][name]) {
|
||||
@@ -154,7 +203,7 @@ class Persist extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Get all builds (object) or builds for a specific ship (array)
|
||||
* @param {string} shipId Optional Ship Id
|
||||
* @param {String} shipId Optional Ship Id
|
||||
* @return {Object | Array} Object if Ship Id is not provided
|
||||
*/
|
||||
getBuilds(shipId) {
|
||||
@@ -166,7 +215,7 @@ class Persist extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Get an array of all builds names for a ship
|
||||
* @param {string} shipId Ship Id
|
||||
* @param {String} shipId Ship Id
|
||||
* @return {Array} Array of string or empty array
|
||||
*/
|
||||
getBuildsNamesFor(shipId) {
|
||||
@@ -179,8 +228,8 @@ class Persist extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Check if a build has been saved
|
||||
* @param {string} shipId Ship Id
|
||||
* @param {string} name Build name
|
||||
* @param {String} shipId Ship Id
|
||||
* @param {String} name Build name
|
||||
* @return {Boolean} True if the build exists
|
||||
*/
|
||||
hasBuild(shipId, name) {
|
||||
@@ -199,8 +248,8 @@ class Persist extends EventEmitter {
|
||||
* Delete a build from local storage. It will also delete the ship build collection if
|
||||
* it becomes empty
|
||||
*
|
||||
* @param {string} shipId The unique id for a model of ship
|
||||
* @param {string} name The name of the build
|
||||
* @param {String} shipId The unique id for a model of ship
|
||||
* @param {String} name The name of the build
|
||||
*/
|
||||
deleteBuild(shipId, name) {
|
||||
if (this.builds[shipId][name]) {
|
||||
@@ -220,14 +269,14 @@ class Persist extends EventEmitter {
|
||||
}
|
||||
}
|
||||
_put(LS_KEY_COMPARISONS, this.comparisons);
|
||||
this.emit('buildDeleted', shipId, name);
|
||||
this.emit('builds');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist a comparison in localstorage.
|
||||
*
|
||||
* @param {string} name The name of the comparison
|
||||
* @param {String} name The name of the comparison
|
||||
* @param {array} builds Array of builds
|
||||
* @param {array} facets Array of facet indices
|
||||
*/
|
||||
@@ -240,13 +289,13 @@ class Persist extends EventEmitter {
|
||||
builds: builds.map(b => { return { shipId: b.id || b.shipId, buildName: b.buildName }; })
|
||||
};
|
||||
_put(LS_KEY_COMPARISONS, this.comparisons);
|
||||
this.emit('comparisons', this.comparisons);
|
||||
this.emit('comparisons');
|
||||
}
|
||||
|
||||
/**
|
||||
* [getComparison description]
|
||||
* @param {string} name [description]
|
||||
* @return {object} Object containing array of facets and ship id + build names
|
||||
* Get a comparison
|
||||
* @param {String} name Comparison name
|
||||
* @return {Object} Object containing array of facets and ship id + build names
|
||||
*/
|
||||
getComparison(name) {
|
||||
if (this.comparisons[name]) {
|
||||
@@ -265,7 +314,7 @@ class Persist extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Check if a comparison has been saved
|
||||
* @param {string} name Comparison name
|
||||
* @param {String} name Comparison name
|
||||
* @return {Boolean} True if a comparison has been saved
|
||||
*/
|
||||
hasComparison(name) {
|
||||
@@ -282,13 +331,13 @@ class Persist extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Removes the comparison from localstorage.
|
||||
* @param {string} name Comparison name
|
||||
* @param {String} name Comparison name
|
||||
*/
|
||||
deleteComparison(name) {
|
||||
if (this.comparisons[name]) {
|
||||
delete this.comparisons[name];
|
||||
_put(LS_KEY_COMPARISONS, this.comparisons);
|
||||
this.emit('comparisons', this.comparisons);
|
||||
this.emit('comparisons');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,8 +347,8 @@ class Persist extends EventEmitter {
|
||||
deleteAll() {
|
||||
this.builds = {};
|
||||
this.comparisons = {};
|
||||
_delete(LS_KEY_BUILDS);
|
||||
_delete(LS_KEY_COMPARISONS);
|
||||
_put(LS_KEY_BUILDS, {});
|
||||
_put(LS_KEY_COMPARISONS, {});
|
||||
this.emit('deletedAll');
|
||||
}
|
||||
|
||||
@@ -318,20 +367,20 @@ class Persist extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Get the saved insurance type
|
||||
* @return {string} The name of the saved insurance type of null
|
||||
* @return {String} The name of the saved insurance type of null
|
||||
*/
|
||||
getInsurance() {
|
||||
return this.insurance.toLowerCase();
|
||||
return this.insurance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist selected insurance type
|
||||
* @param {string} insurance Insurance type name
|
||||
* @param {String} insurance Insurance type name
|
||||
*/
|
||||
setInsurance(insurance) {
|
||||
this.insurance = insurance.toLowerCase();
|
||||
_put(LS_KEY_INSURANCE, insurance);
|
||||
this.emit('insurance', insurance);
|
||||
_put(LS_KEY_INSURANCE, this.insurance);
|
||||
this.emit('insurance', this.insurance);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -341,7 +390,7 @@ class Persist extends EventEmitter {
|
||||
setShipDiscount(shipDiscount) {
|
||||
this.discounts[0] = shipDiscount;
|
||||
_put(LS_KEY_DISCOUNTS, this.discounts);
|
||||
this.emit('discounts', this.discounts);
|
||||
this.emit('discounts');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -359,7 +408,7 @@ class Persist extends EventEmitter {
|
||||
setModuleDiscount(moduleDiscount) {
|
||||
this.discounts[1] = moduleDiscount;
|
||||
_put(LS_KEY_DISCOUNTS, this.discounts);
|
||||
this.emit('discounts', this.discounts);
|
||||
this.emit('discounts');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -389,7 +438,7 @@ class Persist extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Retrieve the last router state from local storage
|
||||
* @return {object} state State object containing state name and params
|
||||
* @return {Object} state State object containing state name and params
|
||||
*/
|
||||
getState() {
|
||||
return this.state;
|
||||
@@ -397,7 +446,7 @@ class Persist extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Save the current router state to localstorage
|
||||
* @param {object} state State object containing state name and params
|
||||
* @param {Object} state State object containing state name and params
|
||||
*/
|
||||
setState(state) {
|
||||
this.state = state;
|
||||
@@ -433,4 +482,5 @@ class Persist extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
// Export an instance as the default to use as a singleton
|
||||
export default new Persist();
|
||||
|
||||
@@ -19,7 +19,7 @@ export function slotName(translate, slot) {
|
||||
* @param {Function} translate Translate function
|
||||
* @param {Object} a Slot object
|
||||
* @param {Object} b Slot object
|
||||
* @return {number} 1, 0, -1
|
||||
* @return {Number} 1, 0, -1
|
||||
*/
|
||||
export function nameComparator(translate, a, b) {
|
||||
return translate(a.name || a.grp).localeCompare(translate(b.name || b.grp));
|
||||
@@ -97,7 +97,14 @@ const PROP_BLACKLIST = {
|
||||
M: 1,
|
||||
P: 1,
|
||||
mass: 1,
|
||||
cost: 1
|
||||
cost: 1,
|
||||
recover: 1,
|
||||
weaponcapacity: 1,
|
||||
weaponrecharge: 1,
|
||||
enginecapacity: 1,
|
||||
enginerecharge: 1,
|
||||
systemcapacity: 1,
|
||||
systemrecharge: 1
|
||||
};
|
||||
|
||||
const TERM_LOOKUP = {
|
||||
@@ -127,10 +134,10 @@ const UNIT_LOOKUP = {
|
||||
|
||||
/**
|
||||
* Determine the appropriate class based on diff value
|
||||
* @param {number} a Potential Module (cannot be null)
|
||||
* @param {number} b Currently mounted module (optional - null)
|
||||
* @param {boolean} negative A positive diff has a negative implication
|
||||
* @return {string} CSS Class name
|
||||
* @param {Number} a Potential Module (cannot be null)
|
||||
* @param {Number} b Currently mounted module (optional - null)
|
||||
* @param {Boolean} negative A positive diff has a negative implication
|
||||
* @return {String} CSS Class name
|
||||
*/
|
||||
function diffClass(a, b, negative) {
|
||||
if (b === undefined || a === b) {
|
||||
@@ -144,8 +151,8 @@ function diffClass(a, b, negative) {
|
||||
/**
|
||||
* Determine the displayable diff of a module's proprty
|
||||
* @param {Function} format Formatter
|
||||
* @param {number} mVal Potential Module property value
|
||||
* @param {number} mmVal Currently mounted module property value
|
||||
* @param {Number} mVal Potential Module property value
|
||||
* @param {Number} mmVal Currently mounted module property value
|
||||
* @return {string | React.Component} Component to be rendered
|
||||
*/
|
||||
function diff(format, mVal, mmVal) {
|
||||
@@ -208,7 +215,30 @@ export function diffDetails(language, m, mm) {
|
||||
newShield = this.calcShieldStrengthWith(m);
|
||||
}
|
||||
}
|
||||
propDiffs.push(<div key='shields'>{`${translate('shields')}: `}<span className={newShield > shield ? 'secondary' : 'warning'}>{diff(formats.int, newShield, shield)}{units.MJ}</span></div>);
|
||||
let sgDiffClass = Math.round((newShield - shield) * 100) / 100 == 0 ? 'muted' : (newShield > shield ? 'secondary' : 'warning');
|
||||
|
||||
propDiffs.push(<div key='shields'>{`${translate('shields')}: `}<span className={sgDiffClass}>{diff(formats.int, newShield, shield)}{units.MJ}</span></div>);
|
||||
}
|
||||
|
||||
if (m.grp == 'pd') {
|
||||
propDiffs.push(<div key='wep'>
|
||||
{`${translate('WEP')}: `}
|
||||
<span className={diffClass(m.weaponcapacity, mm.weaponcapacity)}>{m.weaponcapacity}{units.MJ}</span>
|
||||
{' / '}
|
||||
<span className={diffClass(m.weaponrecharge, mm.weaponrecharge)}>{m.weaponrecharge}{units.MW}</span>
|
||||
</div>);
|
||||
propDiffs.push(<div key='sys'>
|
||||
{`${translate('SYS')}: `}
|
||||
<span className={diffClass(m.systemcapacity, mm.systemcapacity)}>{m.systemcapacity}{units.MJ}</span>
|
||||
{' / '}
|
||||
<span className={diffClass(m.systemrecharge, mm.systemrecharge)}>{m.systemrecharge}{units.MW}</span>
|
||||
</div>);
|
||||
propDiffs.push(<div key='eng'>
|
||||
{`${translate('ENG')}: `}
|
||||
<span className={diffClass(m.enginecapacity, mm.enginecapacity)}>{m.enginecapacity}{units.MJ}</span>
|
||||
{' / '}
|
||||
<span className={diffClass(m.enginerecharge, mm.enginerecharge)}>{m.enginerecharge}{units.MW}</span>
|
||||
</div>);
|
||||
}
|
||||
|
||||
if (m.grp == 'fd' || massDiff || capDiff) {
|
||||
|
||||
21
src/app/utils/UrlGenerators.js
Normal file
21
src/app/utils/UrlGenerators.js
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
/**
|
||||
* Generates a URL for the outiffing page
|
||||
* @param {String} shipId Ship Id
|
||||
* @param {String} code [optional] Serliazed build code
|
||||
* @param {String} buildName [optional] Build name
|
||||
* @return {String} URL
|
||||
*/
|
||||
export function outfitURL(shipId, code, buildName) {
|
||||
let parts = ['/outfit/', shipId];
|
||||
|
||||
if (code) {
|
||||
parts.push('/', code);
|
||||
}
|
||||
|
||||
if (buildName) {
|
||||
parts.push('?bn=', encodeURIComponent(buildName));
|
||||
}
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,397 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
|
||||
<metadata></metadata>
|
||||
<defs>
|
||||
<font id="orbitronregular" horiz-adv-x="1695" >
|
||||
<font-face units-per-em="2048" ascent="1536" descent="-512" />
|
||||
<missing-glyph horiz-adv-x="557" />
|
||||
<glyph horiz-adv-x="0" />
|
||||
<glyph unicode="
" horiz-adv-x="681" />
|
||||
<glyph unicode=" " horiz-adv-x="557" />
|
||||
<glyph unicode="	" horiz-adv-x="557" />
|
||||
<glyph unicode=" " horiz-adv-x="557" />
|
||||
<glyph unicode="!" horiz-adv-x="450" d="M119 0v168h168v-168h-168zM119 416v1059h168v-1059h-168z" />
|
||||
<glyph unicode=""" horiz-adv-x="761" d="M121 1188v287h168v-287h-168zM438 1188v287h168v-287h-168z" />
|
||||
<glyph unicode="#" horiz-adv-x="1632" d="M66 350v168h260l145 451h-340v168h393l115 336h166l-117 -336h535l114 336h166l-117 -336h197v-168h-248l-145 -451h326v-168h-379l-109 -350h-168l111 350h-533l-108 -350h-168l110 350h-206zM494 518h530l145 451h-532z" />
|
||||
<glyph unicode="$" horiz-adv-x="1613" d="M70 248v59h168v-59q0 -33 23.5 -56.5t55.5 -23.5h414v487h-414q-102 0 -174.5 72t-72.5 176v324q0 104 72.5 177t174.5 73h414v233h168v-233h412q104 0 177 -73t73 -177v-58h-170v58q0 33 -24 56.5t-56 23.5h-412v-484h412q104 0 177 -72.5t73 -175.5v-327 q0 -102 -73 -175t-177 -73h-412v-233h-168v233h-414q-102 0 -174.5 72.5t-72.5 175.5zM238 903q0 -33 23.5 -56.5t55.5 -23.5h414v484h-414q-33 0 -56 -23.5t-23 -56.5v-324zM899 168h412q33 0 56.5 23.5t23.5 56.5v327q0 33 -23.5 56.5t-56.5 23.5h-412v-487z" />
|
||||
<glyph unicode="%" horiz-adv-x="1978" d="M98 1053v172q0 104 73 177t177 73h193q104 0 177 -73t73 -177v-172q0 -102 -73 -175t-177 -73h-193q-104 0 -177 72.5t-73 175.5zM223 1010q0 -33 23.5 -56.5t56.5 -23.5h283q33 0 56.5 23.5t23.5 56.5v258q0 33 -24 56.5t-56 23.5h-283q-33 0 -56.5 -24t-23.5 -56v-258z M281 0v221l1491 1256v-220zM1219 246v174q0 102 71.5 175t175.5 73h195q102 0 175 -73t73 -175v-174q0 -102 -73 -175t-175 -73h-195q-104 0 -175.5 72.5t-71.5 175.5zM1343 203q0 -33 23.5 -56.5t56.5 -23.5h281q33 0 56.5 23.5t23.5 56.5v260q0 33 -23.5 56.5t-56.5 23.5 h-281q-33 0 -56.5 -23.5t-23.5 -56.5v-260z" />
|
||||
<glyph unicode="&" horiz-adv-x="1921" d="M109 248v440q0 45 55 89t147 44q-117 88 -116 197v205q0 104 72.5 177t174.5 73h842q84 0 154.5 -62.5t95.5 -146.5v-113h-170v70q0 33 -23.5 56t-56.5 23h-842q-33 0 -56.5 -23.5t-23.5 -55.5v-285l1068 -539v285h167v-340l275 -162v-147l-281 157q-35 -86 -99.5 -138 t-141.5 -52h-994q-102 0 -174.5 72.5t-72.5 175.5zM276 248q0 -33 23.5 -56.5t56.5 -23.5h994q23 0 45 20.5t37 53.5l-1045 526h-31q-33 0 -56.5 -23.5t-23.5 -56.5v-440z" />
|
||||
<glyph unicode="'" horiz-adv-x="458" d="M121 1188v287h168v-287h-168z" />
|
||||
<glyph unicode="(" horiz-adv-x="567" d="M106 248v977q0 104 73 177t175 73h60v-170h-60q-33 0 -56.5 -24t-23.5 -56v-977q0 -33 23.5 -56.5t56.5 -23.5h60v-168h-60q-102 0 -175 72.5t-73 175.5z" />
|
||||
<glyph unicode=")" horiz-adv-x="569" d="M115 0v168h57q33 0 56.5 23.5t23.5 56.5v977q0 33 -23.5 56.5t-56.5 23.5h-57v170h57q102 0 176 -73t74 -177v-977q0 -102 -74 -175t-176 -73h-57z" />
|
||||
<glyph unicode="*" horiz-adv-x="1005" d="M51 1096l49 161l301 -100v316h168v-314l297 98l56 -161l-299 -97l184 -256q-25 -18 -67 -48.5t-68 -49.5l-187 252l-184 -252q-31 23 -74 52.5t-63 43.5l184 258z" />
|
||||
<glyph unicode="+" horiz-adv-x="886" d="M35 522v168h311v314h168v-314h320v-168h-320v-317h-168v317h-311z" />
|
||||
<glyph unicode="," horiz-adv-x="395" d="M111 168h168v-201q0 -82 -47.5 -146.5t-120.5 -88.5v436z" />
|
||||
<glyph unicode="-" horiz-adv-x="1058" d="M121 522v168h799v-168h-799z" />
|
||||
<glyph unicode="." horiz-adv-x="438" d="M111 0v168h168v-168h-168z" />
|
||||
<glyph unicode="/" horiz-adv-x="1067" d="M12 0v217l1037 1258v-213z" />
|
||||
<glyph unicode="0" horiz-adv-x="1708" d="M117 248v977q0 104 72 177t176 73h993q102 0 175 -73t73 -177v-977q0 -102 -73 -175t-175 -73h-993q-104 0 -176 72.5t-72 175.5zM285 371l1130 934h-1050q-33 0 -56.5 -24t-23.5 -56v-854zM305 168h1053q33 0 56.5 23.5t23.5 56.5v854z" />
|
||||
<glyph unicode="1" horiz-adv-x="800" d="M2 1014l387 461h229v-1475h-169v1286q-29 -37 -114 -137t-114 -135h-219z" />
|
||||
<glyph unicode="2" horiz-adv-x="1699" d="M117 0v553q0 104 71.5 176t176.5 72h993q33 0 56.5 23.5t23.5 56.5v346q0 33 -23.5 56.5t-56.5 23.5h-993q-33 0 -56.5 -23.5t-23.5 -56.5v-58h-168v58q0 104 72 177t176 73h993q102 0 175 -73t73 -177v-346q0 -102 -73 -175t-175 -73h-993q-33 0 -56.5 -23.5 t-23.5 -56.5v-305q0 -33 23.5 -56.5t56.5 -23.5h1241v-168h-1489z" />
|
||||
<glyph unicode="3" horiz-adv-x="1691" d="M109 248v33h170v-33q0 -33 23.5 -56.5t55.5 -23.5h994q33 0 56.5 23.5t23.5 56.5v342q0 33 -24 56.5t-56 23.5h-961v168h895q33 0 56.5 23.5t23.5 56.5v309q0 33 -23.5 56.5t-56.5 23.5h-928q-33 0 -56 -23.5t-23 -56.5v-53h-170v53q0 104 72.5 177t176.5 73h928 q104 0 176 -73t72 -177v-309q0 -54 -25 -107l9 -37q82 -76 81 -184v-342q0 -102 -72.5 -175t-174.5 -73h-994q-104 0 -176.5 72.5t-72.5 175.5z" />
|
||||
<glyph unicode="4" horiz-adv-x="1495" d="M12 381v188l1008 908h188v-928h230v-168h-230v-381h-168v381h-1028zM227 549h813v659z" />
|
||||
<glyph unicode="5" horiz-adv-x="1699" d="M117 248v59h168v-59q0 -33 23.5 -56.5t56.5 -23.5h993q33 0 56.5 23.5t23.5 56.5v348q0 33 -23.5 56.5t-56.5 23.5h-1241v801h1489v-170h-1241q-33 0 -56.5 -23.5t-23.5 -56.5v-301q0 -33 23.5 -57.5t56.5 -24.5h993q102 0 175 -72t73 -176v-348q0 -102 -73 -175 t-175 -73h-993q-104 0 -176 72.5t-72 175.5z" />
|
||||
<glyph unicode="6" horiz-adv-x="1679" d="M117 248v979q0 104 71.5 177t176.5 73h983v-170h-983q-33 0 -56.5 -23.5t-23.5 -56.5v-295q0 -33 23.5 -57.5t56.5 -24.5h993q102 0 175 -71.5t73 -176.5v-354q0 -102 -73 -175t-175 -73h-993q-104 0 -176 72.5t-72 175.5zM285 248q0 -33 23.5 -56.5t56.5 -23.5h993 q33 0 56.5 23.5t23.5 56.5v354q0 33 -23.5 56.5t-56.5 23.5h-1073v-434z" />
|
||||
<glyph unicode="7" horiz-adv-x="1351" d="M6 1307v170h983q102 0 175 -73t73 -177v-1227h-168v1227q0 33 -23.5 56.5t-56.5 23.5h-983z" />
|
||||
<glyph unicode="8" horiz-adv-x="1708" d="M117 248v348q0 84 61 164q-61 84 -61 166v301q0 104 71.5 177t176.5 73h993q82 0 152.5 -62.5t95.5 -146.5v-342q0 -82 -62 -166q61 -80 62 -164v-348q0 -102 -73 -175t-175 -73h-993q-104 0 -176 72.5t-72 175.5zM285 248q0 -33 23.5 -56.5t56.5 -23.5h993 q33 0 56.5 23.5t23.5 56.5v348q0 33 -23.5 56.5t-56.5 23.5h-993q-33 0 -56.5 -23.5t-23.5 -56.5v-348zM285 901q0 -33 23.5 -56.5t56.5 -23.5h993q33 0 56.5 23.5t23.5 56.5v324q0 33 -23.5 56.5t-56.5 23.5h-993q-33 0 -56.5 -24t-23.5 -56v-324z" />
|
||||
<glyph unicode="9" d="M104 872v355q0 104 73 177t177 73h994q102 0 174.5 -73t72.5 -177v-979q0 -102 -72.5 -175t-174.5 -73h-994q-80 0 -145.5 47t-89.5 121h1229q33 0 56 23.5t23 56.5v297q0 33 -23 56.5t-56 23.5h-994q-104 0 -177 72.5t-73 174.5zM274 872q0 -33 24 -56t56 -23h1073v434 q0 33 -23.5 56.5t-55.5 23.5h-994q-33 0 -56.5 -23.5t-23.5 -56.5v-355z" />
|
||||
<glyph unicode=":" horiz-adv-x="438" d="M111 0v168h168v-168h-168zM111 1036v168h168v-168h-168z" />
|
||||
<glyph unicode=";" horiz-adv-x="395" d="M104 168h168v-201q0 -82 -47 -146.5t-121 -88.5v436zM104 1036v168h168v-168h-168z" />
|
||||
<glyph unicode="<" horiz-adv-x="968" d="M10 508v199l824 473v-195l-656 -379l656 -379v-194z" />
|
||||
<glyph unicode="=" horiz-adv-x="1306" d="M121 309v168h1059v-168h-1059zM121 719v168h1059v-168h-1059z" />
|
||||
<glyph unicode=">" horiz-adv-x="972" d="M121 29v194l655 379l-655 379v195l823 -474v-198z" />
|
||||
<glyph unicode="?" horiz-adv-x="1388" d="M63 1303v172l1059 -2q102 0 175 -73t73 -177v-398q0 -102 -72.5 -174.5t-175.5 -72.5h-561q-33 0 -56.5 -24t-23.5 -56v-86h-168v86q0 104 73 175.5t175 71.5h561q33 0 56.5 24t23.5 56v398q0 33 -23.5 56.5t-56.5 23.5h-1059zM313 0v168h168v-168h-168z" />
|
||||
<glyph unicode="@" horiz-adv-x="1701" d="M117 248v977q0 104 71.5 177t176.5 73h993q102 0 175 -73t73 -177v-832h-842q-104 0 -176 73t-72 177v172q0 102 72 175t176 73h194q102 0 175 -72.5t73 -175.5v-295h232v705q0 33 -23.5 56.5t-56.5 23.5h-993q-33 0 -56.5 -24t-23.5 -56v-977q0 -33 23.5 -56.5 t56.5 -23.5h1241v-168h-1241q-104 0 -176 72.5t-72 175.5zM641 600q0 -33 23.5 -56.5t56.5 -23.5h360v338q0 33 -23.5 56.5t-56.5 23.5h-280q-33 0 -56.5 -23.5t-23.5 -56.5v-258z" />
|
||||
<glyph unicode="A" horiz-adv-x="1712" d="M119 0v1229q0 102 72 174t174 72h983q102 0 173.5 -72t71.5 -174v-1229h-166v539h-1142v-539h-166zM285 705h1142v524q0 33 -23.5 56.5t-55.5 23.5h-983q-33 0 -56.5 -23.5t-23.5 -56.5v-524z" />
|
||||
<glyph unicode="B" horiz-adv-x="1703" d="M121 0v1475h1165q102 0 174 -72t72 -174v-314q0 -55 -27 -108l8 -35q82 -76 82 -182v-344q0 -102 -71.5 -174t-173.5 -72h-1229zM287 246q0 -33 23.5 -56.5t56.5 -23.5h983q33 0 56.5 23.5t23.5 56.5v344q0 33 -24 56.5t-56 23.5h-983q-33 0 -56.5 -23.5t-23.5 -56.5 v-344zM287 915q0 -33 23.5 -56t56.5 -23h919q33 0 55.5 23.5t22.5 55.5v314q0 33 -22.5 56.5t-55.5 23.5h-919q-33 0 -56.5 -23.5t-23.5 -56.5v-314z" />
|
||||
<glyph unicode="C" horiz-adv-x="1683" d="M115 246v983q0 102 71.5 174t173.5 72h1225v-166h-1225q-33 0 -56 -23.5t-23 -56.5v-983q0 -33 23.5 -56.5t55.5 -23.5h1225v-166h-1225q-102 0 -173.5 71.5t-71.5 174.5z" />
|
||||
<glyph unicode="D" horiz-adv-x="1708" d="M119 0v1475h1229q102 0 173.5 -72t71.5 -174v-983q0 -102 -71.5 -174t-173.5 -72h-1229zM285 246q0 -33 23.5 -56.5t56.5 -23.5h983q33 0 56 23.5t23 56.5v983q0 33 -23.5 56.5t-55.5 23.5h-983q-33 0 -56.5 -23.5t-23.5 -56.5v-983z" />
|
||||
<glyph unicode="E" horiz-adv-x="1568" d="M119 0v1475h1345v-166h-1179v-488h948v-168h-948v-487h1179v-166h-1345z" />
|
||||
<glyph unicode="F" horiz-adv-x="1480" d="M119 0v1475h1345v-166h-1179v-488h948v-168h-948v-653h-166z" />
|
||||
<glyph unicode="G" horiz-adv-x="1699" d="M115 246v983q0 102 71.5 174t173.5 72h983q102 0 174 -72t72 -174v-60h-166v60q0 33 -23.5 56.5t-56.5 23.5h-983q-33 0 -56 -23.5t-23 -56.5v-983q0 -33 23.5 -56.5t55.5 -23.5h983q33 0 56.5 23.5t23.5 56.5v364h-364v168h530v-532q0 -102 -71.5 -174t-174.5 -72h-983 q-102 0 -173.5 71.5t-71.5 174.5z" />
|
||||
<glyph unicode="H" horiz-adv-x="1742" d="M117 0v1475h166v-654h1177v654h168v-1475h-168v653h-1177v-653h-166z" />
|
||||
<glyph unicode="I" horiz-adv-x="450" d="M117 0v1475h166v-1475h-166z" />
|
||||
<glyph unicode="J" horiz-adv-x="1597" d="M8 246v116h166v-116q0 -33 23.5 -56.5t56.5 -23.5h983q33 0 56.5 23.5t23.5 56.5v1229h166v-1229q0 -102 -72 -174t-174 -72h-983q-102 0 -174 71.5t-72 174.5z" />
|
||||
<glyph unicode="K" horiz-adv-x="1632" d="M117 0v1475h168v-654h475l549 654h213l-617 -738l619 -737h-215l-549 653h-475v-653h-168z" />
|
||||
<glyph unicode="L" horiz-adv-x="1595" d="M117 0v1477h166v-1311h1308v-166h-1474z" />
|
||||
<glyph unicode="M" horiz-adv-x="1900" d="M115 0v1475h227l612 -730l613 730h227v-1475h-166v1286l-674 -801l-673 801v-1286h-166z" />
|
||||
<glyph unicode="N" horiz-adv-x="1703" d="M115 0v1475h227l1081 -1287v1287h166v-1475h-227l-1081 1286v-1286h-166z" />
|
||||
<glyph unicode="O" d="M111 246v983q0 102 71.5 174t173.5 72h983q102 0 174 -72t72 -174v-983q0 -102 -71.5 -174t-174.5 -72h-983q-102 0 -173.5 71.5t-71.5 174.5zM276 246q0 -33 23.5 -56.5t56.5 -23.5h983q33 0 56.5 23.5t23.5 56.5v983q0 33 -23.5 56.5t-56.5 23.5h-983 q-33 0 -56.5 -23.5t-23.5 -56.5v-983z" />
|
||||
<glyph unicode="P" horiz-adv-x="1619" d="M115 0v1473h1228q102 0 174 -72t72 -174v-432q0 -102 -71.5 -174t-174.5 -72h-983q-18 0 -79 8v-557h-166zM281 795q0 -33 23.5 -55.5t55.5 -22.5h983q33 0 56.5 22.5t23.5 55.5v432q0 33 -23.5 56.5t-56.5 23.5h-983q-33 0 -56 -23.5t-23 -56.5v-432z" />
|
||||
<glyph unicode="Q" horiz-adv-x="1810" d="M111 246v983q0 102 71.5 174t173.5 72h983q102 0 174 -72t72 -174v-983q0 -19 -8 -80h201v-166h-1422q-102 0 -173.5 71.5t-71.5 174.5zM276 246q0 -33 23.5 -56.5t56.5 -23.5h983q33 0 56.5 23.5t23.5 56.5v983q0 33 -23.5 56.5t-56.5 23.5h-983q-33 0 -56.5 -23.5 t-23.5 -56.5v-983z" />
|
||||
<glyph unicode="R" horiz-adv-x="1689" d="M113 0v1473h1228q102 0 174 -72t72 -174v-432q0 -102 -71.5 -174t-174.5 -72h-225l463 -549h-219l-461 549h-541q-18 0 -79 8v-557h-166zM279 795q0 -33 23 -55.5t56 -22.5h983q33 0 55.5 22.5t22.5 55.5v432q0 33 -22.5 56.5t-55.5 23.5h-983q-33 0 -56 -23.5t-23 -56.5 v-432z" />
|
||||
<glyph unicode="S" horiz-adv-x="1683" d="M104 246v57h166v-57q0 -33 23.5 -56.5t56.5 -23.5h983q33 0 56.5 23.5t23.5 56.5v329q0 33 -23.5 55.5t-56.5 22.5h-983q-102 0 -174 72t-72 174v330q0 102 72 174t174 72h983q102 0 174 -72t72 -174v-58h-166v58q0 33 -23.5 56.5t-56.5 23.5h-983q-33 0 -56.5 -23.5 t-23.5 -56.5v-330q0 -33 23.5 -55.5t56.5 -22.5h983q102 0 174 -71.5t72 -174.5v-329q0 -102 -71.5 -174t-174.5 -72h-983q-102 0 -174 71.5t-72 174.5z" />
|
||||
<glyph unicode="T" horiz-adv-x="1554" d="M41 1309v166h1475v-166h-654v-1309h-166v1309h-655z" />
|
||||
<glyph unicode="U" d="M111 246v1229h165v-1229q0 -33 24 -56.5t56 -23.5h983q33 0 56.5 23.5t23.5 56.5v1229h166v-1229q0 -102 -71.5 -174t-174.5 -72h-983q-102 0 -173.5 71.5t-71.5 174.5z" />
|
||||
<glyph unicode="V" horiz-adv-x="2054" d="M72 1475h194l756 -1313l754 1313h194l-852 -1475h-192z" />
|
||||
<glyph unicode="W" horiz-adv-x="2414" d="M72 1475h176l432 -1184l430 1184h201l432 -1184l428 1184h180l-536 -1475h-146l-459 1260l-458 -1260h-144z" />
|
||||
<glyph unicode="X" horiz-adv-x="1662" d="M94 0l619 737l-619 738h213l514 -609l512 609h213l-618 -738l620 -737h-215l-512 608l-514 -608h-213z" />
|
||||
<glyph unicode="Y" horiz-adv-x="1650" d="M35 1475h190l572 -721l565 721h194l-677 -920v-555h-166v555z" />
|
||||
<glyph unicode="Z" horiz-adv-x="1681" d="M104 0v227l1287 1082h-1287v166h1475v-228l-1286 -1081h1286v-166h-1475z" />
|
||||
<glyph unicode="[" horiz-adv-x="563" d="M111 0v1477h307v-170h-139v-1139h139v-168h-307z" />
|
||||
<glyph unicode="\" horiz-adv-x="1064" d="M10 1272v213l1037 -1266v-217z" />
|
||||
<glyph unicode="]" horiz-adv-x="565" d="M104 0v168h138v1139h-138v170h308v-1477h-308z" />
|
||||
<glyph unicode="_" d="M111 0h1491v-168h-1491v168z" />
|
||||
<glyph unicode="`" horiz-adv-x="436" d="M66 2071h165l70 -285h-164z" />
|
||||
<glyph unicode="a" horiz-adv-x="1421" d="M106 248v430h1041v262q0 33 -23.5 56.5t-56.5 23.5h-961v168h961q104 0 177 -73t73 -175v-940h-963q-102 0 -175 72.5t-73 175.5zM274 248q0 -33 24 -56.5t56 -23.5h793v342h-873v-262z" />
|
||||
<glyph unicode="b" horiz-adv-x="1366" d="M111 0v1577h168v-389h794q102 0 175 -73t73 -175v-692q0 -102 -72.5 -175t-175.5 -73h-962zM279 248q0 -33 24.5 -56.5t56.5 -23.5h713q33 0 56.5 23.5t23.5 56.5v692q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -57 -23.5t-24 -56.5v-692z" />
|
||||
<glyph unicode="c" horiz-adv-x="1423" d="M104 248v692q0 102 73 175t175 73h959v-168h-959q-33 0 -56.5 -23.5t-23.5 -56.5v-692q0 -33 23.5 -56.5t56.5 -23.5h963v-168h-963q-102 0 -175 72.5t-73 175.5z" />
|
||||
<glyph unicode="d" horiz-adv-x="1366" d="M47 248v692q0 102 73 175t177 73h793v389h167v-1577h-960q-104 0 -177 72.5t-73 175.5zM217 248q0 -33 23.5 -56.5t56.5 -23.5h713q33 0 56.5 23.5t23.5 56.5v692q0 33 -24 56.5t-56 23.5h-713q-33 0 -56.5 -23.5t-23.5 -56.5v-692z" />
|
||||
<glyph unicode="e" horiz-adv-x="1417" d="M104 248v692q0 102 73 175t175 73h713q104 0 177 -73t73 -175v-430h-1043v-262q0 -33 23.5 -56.5t56.5 -23.5h963v-168h-963q-102 0 -175 72.5t-73 175.5zM272 678h873v262q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -56.5 -23.5t-23.5 -56.5v-262z" />
|
||||
<glyph unicode="f" horiz-adv-x="833" d="M109 0v1329q0 104 71.5 176t175.5 72h435v-168h-435q-33 0 -56.5 -23.5t-23.5 -56.5v-141h515v-168h-515v-1020h-167z" />
|
||||
<glyph unicode="g" horiz-adv-x="1398" d="M84 248v692q0 102 72.5 175t175.5 73h712q104 0 176 -73t72 -175v-1159q0 -104 -71.5 -177t-176.5 -73h-741v170h741q33 0 56.5 23.5t23.5 56.5v219h-792q-102 0 -175 72.5t-73 175.5zM252 248q0 -33 23.5 -56.5t56.5 -23.5h712q33 0 56.5 23.5t23.5 56.5v692 q0 33 -23.5 56.5t-56.5 23.5h-712q-33 0 -56.5 -23.5t-23.5 -56.5v-692z" />
|
||||
<glyph unicode="h" horiz-adv-x="1368" d="M111 0v1577h168v-389h794q102 0 175 -73t73 -175v-940h-168v940q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -57 -23.5t-24 -56.5v-940h-168z" />
|
||||
<glyph unicode="i" horiz-adv-x="425" d="M106 0v1188h168v-1188h-168zM106 1409v168h168v-168h-168z" />
|
||||
<glyph unicode="j" horiz-adv-x="489" d="M-383 -328h502q33 0 56.5 23.5t23.5 56.5v1436h170v-1436q0 -104 -73 -177t-177 -73h-502v170zM199 1409v168h170v-168h-170z" />
|
||||
<glyph unicode="k" horiz-adv-x="1323" d="M111 0v1577h168v-899h327l467 510h221l-538 -594l536 -594h-219l-467 510h-327v-510h-168z" />
|
||||
<glyph unicode="l" horiz-adv-x="618" d="M106 248v1329h168v-1329q0 -33 24 -56.5t56 -23.5h201v-168h-201q-102 0 -175 72.5t-73 175.5z" />
|
||||
<glyph unicode="m" horiz-adv-x="2002" d="M111 0v1188h1568q104 0 176 -73t72 -175v-940h-166v940q0 33 -24.5 56.5t-57.5 23.5h-493q-33 0 -56.5 -23.5t-23.5 -56.5v-940h-170v940q0 33 -23.5 56.5t-56.5 23.5h-496q-33 0 -56 -23.5t-23 -56.5v-940h-170z" />
|
||||
<glyph unicode="n" horiz-adv-x="1425" d="M111 0v1188h962q102 0 175 -73t73 -175v-940h-168v940q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -57 -23.5t-24 -56.5v-940h-168z" />
|
||||
<glyph unicode="o" horiz-adv-x="1417" d="M104 248v692q0 102 73 175t175 73h713q104 0 177 -73t73 -175v-692q0 -102 -73 -175t-177 -73h-713q-102 0 -175 72.5t-73 175.5zM272 248q0 -33 23.5 -56.5t56.5 -23.5h713q33 0 56.5 23.5t23.5 56.5v692q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -56.5 -23.5 t-23.5 -56.5v-692z" />
|
||||
<glyph unicode="p" horiz-adv-x="1359" d="M111 -471v1659h962q102 0 175 -73t73 -175v-692q0 -102 -72.5 -175t-175.5 -73h-794v-471h-168zM279 248q0 -33 24.5 -56.5t56.5 -23.5h713q33 0 56.5 23.5t23.5 56.5v692q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -57 -23.5t-24 -56.5v-692z" />
|
||||
<glyph unicode="q" horiz-adv-x="1359" d="M41 248v692q0 102 72.5 175t177.5 73h960v-1659h-168v471h-792q-104 0 -177 72.5t-73 175.5zM211 248q0 -33 23.5 -56.5t56.5 -23.5h713q33 0 56 23.5t23 56.5v692q0 33 -23.5 56.5t-55.5 23.5h-713q-33 0 -56.5 -23.5t-23.5 -56.5v-692z" />
|
||||
<glyph unicode="r" horiz-adv-x="1048" d="M106 0v940q0 102 73 175t175 73h668v-168h-668q-33 0 -56.5 -23.5t-23.5 -56.5v-940h-168z" />
|
||||
<glyph unicode="s" horiz-adv-x="1404" d="M98 248v16h168v-16q0 -33 23.5 -56.5t56.5 -23.5h713q33 0 56.5 23.5t23.5 56.5v182q0 33 -23.5 56.5t-56.5 23.5h-713q-102 0 -175 72.5t-73 175.5v182q0 102 73 175t175 73h713q104 0 177 -73t73 -175v-16h-170v16q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -56.5 -23.5 t-23.5 -56.5v-182q0 -33 23.5 -56.5t56.5 -23.5h713q104 0 177 -73t73 -175v-182q0 -102 -73 -175t-177 -73h-713q-102 0 -175 72.5t-73 175.5z" />
|
||||
<glyph unicode="t" horiz-adv-x="839" d="M109 248v1329h167v-389h515v-168h-515v-772q0 -33 24 -56.5t56 -23.5h435v-168h-435q-104 0 -175.5 72.5t-71.5 175.5z" />
|
||||
<glyph unicode="u" horiz-adv-x="1423" d="M109 248v940h167v-940q0 -33 24 -56.5t56 -23.5h713q33 0 57.5 23.5t24.5 56.5v940h168v-940q0 -102 -72.5 -175t-177.5 -73h-713q-102 0 -174.5 72.5t-72.5 175.5z" />
|
||||
<glyph unicode="v" horiz-adv-x="1617" d="M43 1188h197l557 -1024l557 1024h196l-657 -1188h-195z" />
|
||||
<glyph unicode="w" horiz-adv-x="2193" d="M72 1188h180l360 -885l383 885h222l407 -883l336 883h182l-448 -1188h-146l-442 981l-420 -981h-147z" />
|
||||
<glyph unicode="x" horiz-adv-x="1417" d="M94 0l496 604l-496 584h221l385 -451l387 451h220l-496 -584l498 -604h-222l-387 475l-385 -475h-221z" />
|
||||
<glyph unicode="y" horiz-adv-x="1402" d="M86 248v936h168v-936q0 -33 23.5 -56.5t56.5 -23.5h713q33 0 56 23.5t23 56.5v936h168v-1405q0 -104 -71.5 -177t-175.5 -73h-742v170h742q33 0 56 23.5t23 56.5v221h-792q-102 0 -175 72.5t-73 175.5z" />
|
||||
<glyph unicode="z" horiz-adv-x="1429" d="M111 0v229l1026 791h-1026v168h1210v-230l-1026 -790h1026v-168h-1210z" />
|
||||
<glyph unicode="{" horiz-adv-x="591" d="M47 641v197l96 51v336q0 104 73 177t175 73h60v-170h-60q-33 0 -56.5 -24t-23.5 -56v-351l-172 -137l172 -141v-348q0 -33 23.5 -56.5t56.5 -23.5h60v-168h-60q-102 0 -175 72.5t-73 175.5v334q-12 8 -47 29.5t-49 29.5z" />
|
||||
<glyph unicode="|" horiz-adv-x="438" d="M111 -236v1960h168v-1960h-168z" />
|
||||
<glyph unicode="}" horiz-adv-x="591" d="M104 0v168h60q33 0 56.5 23.5t23.5 56.5v346l172 141l-172 135v355q0 33 -23.5 56.5t-56.5 23.5h-60v170h60q102 0 175 -73t73 -177v-336q16 -8 51 -29.5t43 -25.5v-195l-94 -59v-332q0 -102 -73 -175t-175 -73h-60z" />
|
||||
<glyph unicode="~" horiz-adv-x="827" d="M49 631v76q55 18 127 18q82 0 219 -68.5t209 -76.5h4q66 0 135 32v-84q-72 -27 -135 -26q-72 0 -209 72.5t-223 72.5q-80 0 -127 -16z" />
|
||||
<glyph unicode="¡" horiz-adv-x="430" d="M109 0v1059h167v-1059h-167zM109 1321v170h167v-170h-167z" />
|
||||
<glyph unicode="¢" horiz-adv-x="1302" d="M68 248v713q0 104 71.5 176.5t175.5 72.5h273v277h168v-277h516v-170h-516v-872h516v-168h-516v-236h-168v236h-273q-104 0 -175.5 72.5t-71.5 175.5zM236 248q0 -33 23.5 -56.5t55.5 -23.5h273v872h-273q-33 0 -56 -23.5t-23 -55.5v-713z" />
|
||||
<glyph unicode="£" horiz-adv-x="1503" d="M80 0v168h215v494h-215v167h215v396q0 104 72.5 177t177.5 73h620q104 0 177 -73t73 -177v-31h-170v31q0 33 -23.5 56.5t-56.5 23.5h-620q-33 0 -56.5 -24t-23.5 -56v-396h721v-167h-721v-494h950v-168h-1335z" />
|
||||
<glyph unicode="¨" horiz-adv-x="788" d="M111 1786v168h170v-168h-170zM479 1786v168h168v-168h-168z" />
|
||||
<glyph unicode="­" horiz-adv-x="1058" d="M121 522v168h799v-168h-799z" />
|
||||
<glyph unicode="°" horiz-adv-x="899" d="M92 1069v172q0 104 73 177t177 73h193q104 0 176.5 -72.5t72.5 -177.5v-172q0 -102 -72.5 -175t-176.5 -73h-193q-104 0 -177 73t-73 175zM217 1026q0 -33 23.5 -56.5t56.5 -23.5h283q33 0 56 23.5t23 56.5v258q0 33 -23.5 56.5t-55.5 23.5h-283q-33 0 -56.5 -23.5 t-23.5 -56.5v-258z" />
|
||||
<glyph unicode="´" horiz-adv-x="436" d="M68 1786l71 285h162l-70 -285h-163z" />
|
||||
<glyph unicode="¶" horiz-adv-x="1705" d="M115 803v436q0 104 71.5 177t175.5 73h1242v-1489h-168v555h-283v-555h-168v555h-623q-104 0 -175.5 72.5t-71.5 175.5zM283 803q0 -33 23.5 -56.5t55.5 -23.5h623v596h-623q-33 0 -56 -23.5t-23 -56.5v-436zM1153 723h283v596h-283v-596z" />
|
||||
<glyph unicode="¸" horiz-adv-x="436" d="M68 -350l71 284h162l-70 -284h-163z" />
|
||||
<glyph unicode="¿" horiz-adv-x="1382" d="M39 248v397q0 104 71.5 177t176.5 73h559q33 0 56.5 23.5t23.5 56.5v98h168v-98q0 -104 -72 -177t-176 -73h-559q-33 0 -56.5 -23.5t-23.5 -56.5v-397q0 -33 23.5 -56.5t56.5 -23.5h1040q0 -59 4 -105.5t8 -62.5l4 -16l-1056 16q-104 0 -176 72.5t-72 175.5zM926 1321 v170h168v-170h-168z" />
|
||||
<glyph unicode="À" horiz-adv-x="1712" d="M119 0v1229q0 102 72 174t174 72h983q102 0 173.5 -72t71.5 -174v-1229h-166v539h-1142v-539h-166zM285 705h1142v524q0 33 -23.5 56.5t-55.5 23.5h-983q-33 0 -56.5 -23.5t-23.5 -56.5v-524zM705 2056h165l70 -284h-164z" />
|
||||
<glyph unicode="Á" horiz-adv-x="1712" d="M119 0v1229q0 102 72 174t174 72h983q102 0 173.5 -72t71.5 -174v-1229h-166v539h-1142v-539h-166zM285 705h1142v524q0 33 -23.5 56.5t-55.5 23.5h-983q-33 0 -56.5 -23.5t-23.5 -56.5v-524zM705 1769l71 285h162l-70 -285h-163z" />
|
||||
<glyph unicode="Â" horiz-adv-x="1712" d="M119 0v1229q0 102 72 174t174 72h983q102 0 173.5 -72t71.5 -174v-1229h-166v539h-1142v-539h-166zM285 705h1142v524q0 33 -23.5 56.5t-55.5 23.5h-983q-33 0 -56.5 -23.5t-23.5 -56.5v-524zM606 1769l146 240h120l146 -240h-86l-119 175l-123 -175h-84z" />
|
||||
<glyph unicode="Ã" horiz-adv-x="1712" d="M119 0v1229q0 102 72 174t174 72h983q102 0 173.5 -72t71.5 -174v-1229h-166v539h-1142v-539h-166zM285 705h1142v524q0 33 -23.5 56.5t-55.5 23.5h-983q-33 0 -56.5 -23.5t-23.5 -56.5v-524zM496 1911v96q55 18 127 18q82 0 219 -68.5t209 -78.5h4q61 0 135 35v-105 q-78 -29 -135 -28q-72 0 -209 73.5t-223 73.5q-80 0 -127 -16z" />
|
||||
<glyph unicode="Ä" horiz-adv-x="1712" d="M119 0v1229q0 102 72 174t174 72h983q102 0 173.5 -72t71.5 -174v-1229h-166v539h-1142v-539h-166zM285 705h1142v524q0 33 -23.5 56.5t-55.5 23.5h-983q-33 0 -56.5 -23.5t-23.5 -56.5v-524zM571 1751v168h170v-168h-170zM940 1751v168h168v-168h-168z" />
|
||||
<glyph unicode="Å" horiz-adv-x="1712" d="M119 0v1229q0 102 72 174t174 72h983q102 0 173.5 -72t71.5 -174v-1229h-166v539h-1142v-539h-166zM285 705h1142v524q0 33 -23.5 56.5t-55.5 23.5h-983q-33 0 -56.5 -23.5t-23.5 -56.5v-524zM688 1790v111q0 45 33 76.5t78 31.5h114q45 0 78 -31.5t33 -76.5v-111 q0 -45 -33 -77t-78 -32h-114q-45 0 -78 32t-33 77zM770 1784q0 -21 18 -21h136q18 0 18 21v123q0 20 -18 20h-136q-18 0 -18 -20v-123z" />
|
||||
<glyph unicode="Æ" horiz-adv-x="2816" d="M111 0v1229q0 102 71.5 174t173.5 72h2409v-166h-1180v-488h948v-168h-948v-487h1180v-166h-1346v539h-1143v-539h-165zM276 705h1143v524q0 33 -23.5 56.5t-56.5 23.5h-983q-33 0 -56.5 -23.5t-23.5 -56.5v-524z" />
|
||||
<glyph unicode="Ç" horiz-adv-x="1683" d="M115 246v983q0 102 71.5 174t173.5 72h1225v-166h-1225q-33 0 -56 -23.5t-23 -56.5v-983q0 -33 23.5 -56.5t55.5 -23.5h1225v-166h-661l-70 -285h-164l72 285h-402q-102 0 -173.5 71.5t-71.5 174.5z" />
|
||||
<glyph unicode="È" horiz-adv-x="1568" d="M119 0v1475h1345v-166h-1179v-488h948v-168h-948v-487h1179v-166h-1345zM633 2056h166l69 -284h-163z" />
|
||||
<glyph unicode="É" horiz-adv-x="1568" d="M119 0v1475h1345v-166h-1179v-488h948v-168h-948v-487h1179v-166h-1345zM633 1772l72 284h161l-69 -284h-164z" />
|
||||
<glyph unicode="Ê" horiz-adv-x="1568" d="M119 0v1475h1345v-166h-1179v-488h948v-168h-948v-487h1179v-166h-1345zM535 1769l145 240h121l145 -240h-86l-119 175l-123 -175h-83z" />
|
||||
<glyph unicode="Ë" horiz-adv-x="1568" d="M119 0v1475h1345v-166h-1179v-488h948v-168h-948v-487h1179v-166h-1345zM500 1772v167h170v-167h-170zM868 1772v167h168v-167h-168z" />
|
||||
<glyph unicode="Ì" horiz-adv-x="450" d="M74 2056h166l69 -284h-164zM117 0v1475h166v-1475h-166z" />
|
||||
<glyph unicode="Í" horiz-adv-x="450" d="M74 1772l71 284h162l-69 -284h-164zM117 0v1475h166v-1475h-166z" />
|
||||
<glyph unicode="Î" horiz-adv-x="450" d="M-25 1769l146 240h121l145 -240h-86l-119 175l-123 -175h-84zM117 0v1475h166v-1475h-166z" />
|
||||
<glyph unicode="Ï" horiz-adv-x="450" d="M-59 1772v167h170v-167h-170zM117 0v1475h166v-1475h-166zM309 1772v167h168v-167h-168z" />
|
||||
<glyph unicode="Ñ" horiz-adv-x="1703" d="M115 0v1475h227l1081 -1287v1287h166v-1475h-227l-1081 1286v-1286h-166zM492 1911v96q55 18 126 18q82 0 219.5 -68.5t209.5 -78.5h4q61 0 135 35v-105q-78 -29 -135 -28q-72 0 -209 73.5t-224 73.5q-80 0 -126 -16z" />
|
||||
<glyph unicode="Ò" d="M111 246v983q0 102 71.5 174t173.5 72h983q102 0 174 -72t72 -174v-983q0 -102 -71.5 -174t-174.5 -72h-983q-102 0 -173.5 71.5t-71.5 174.5zM276 246q0 -33 23.5 -56.5t56.5 -23.5h983q33 0 56.5 23.5t23.5 56.5v983q0 33 -23.5 56.5t-56.5 23.5h-983 q-33 0 -56.5 -23.5t-23.5 -56.5v-983zM696 2056h166l70 -284h-164z" />
|
||||
<glyph unicode="Ó" d="M111 246v983q0 102 71.5 174t173.5 72h983q102 0 174 -72t72 -174v-983q0 -102 -71.5 -174t-174.5 -72h-983q-102 0 -173.5 71.5t-71.5 174.5zM276 246q0 -33 23.5 -56.5t56.5 -23.5h983q33 0 56.5 23.5t23.5 56.5v983q0 33 -23.5 56.5t-56.5 23.5h-983 q-33 0 -56.5 -23.5t-23.5 -56.5v-983zM696 1772l72 284h162l-70 -284h-164z" />
|
||||
<glyph unicode="Ô" d="M111 246v983q0 102 71.5 174t173.5 72h983q102 0 174 -72t72 -174v-983q0 -102 -71.5 -174t-174.5 -72h-983q-102 0 -173.5 71.5t-71.5 174.5zM276 246q0 -33 23.5 -56.5t56.5 -23.5h983q33 0 56.5 23.5t23.5 56.5v983q0 33 -23.5 56.5t-56.5 23.5h-983 q-33 0 -56.5 -23.5t-23.5 -56.5v-983zM598 1769l145 240h121l146 -240h-86l-119 175l-123 -175h-84z" />
|
||||
<glyph unicode="Õ" d="M111 246v983q0 102 71.5 174t173.5 72h983q102 0 174 -72t72 -174v-983q0 -102 -71.5 -174t-174.5 -72h-983q-102 0 -173.5 71.5t-71.5 174.5zM276 246q0 -33 23.5 -56.5t56.5 -23.5h983q33 0 56.5 23.5t23.5 56.5v983q0 33 -23.5 56.5t-56.5 23.5h-983 q-33 0 -56.5 -23.5t-23.5 -56.5v-983zM487 1911v96q55 18 127 18q82 0 219 -68.5t209 -78.5h5q61 0 135 35v-105q-78 -29 -135 -28q-72 0 -209.5 73.5t-223.5 73.5q-80 0 -127 -16z" />
|
||||
<glyph unicode="Ö" d="M111 246v983q0 102 71.5 174t173.5 72h983q102 0 174 -72t72 -174v-983q0 -102 -71.5 -174t-174.5 -72h-983q-102 0 -173.5 71.5t-71.5 174.5zM276 246q0 -33 23.5 -56.5t56.5 -23.5h983q33 0 56.5 23.5t23.5 56.5v983q0 33 -23.5 56.5t-56.5 23.5h-983 q-33 0 -56.5 -23.5t-23.5 -56.5v-983zM563 1772v167h170v-167h-170zM932 1772v167h168v-167h-168z" />
|
||||
<glyph unicode="×" horiz-adv-x="1118" d="M109 1036h221l215 -295l217 295h217l-324 -420l324 -421h-217l-217 294l-215 -294h-219l323 421z" />
|
||||
<glyph unicode="Ù" d="M111 246v1229h165v-1229q0 -33 24 -56.5t56 -23.5h983q33 0 56.5 23.5t23.5 56.5v1229h166v-1229q0 -102 -71.5 -174t-174.5 -72h-983q-102 0 -173.5 71.5t-71.5 174.5zM696 2056h166l70 -284h-164z" />
|
||||
<glyph unicode="Ú" d="M111 246v1229h165v-1229q0 -33 24 -56.5t56 -23.5h983q33 0 56.5 23.5t23.5 56.5v1229h166v-1229q0 -102 -71.5 -174t-174.5 -72h-983q-102 0 -173.5 71.5t-71.5 174.5zM696 1772l72 284h162l-70 -284h-164z" />
|
||||
<glyph unicode="Û" d="M111 246v1229h165v-1229q0 -33 24 -56.5t56 -23.5h983q33 0 56.5 23.5t23.5 56.5v1229h166v-1229q0 -102 -71.5 -174t-174.5 -72h-983q-102 0 -173.5 71.5t-71.5 174.5zM598 1769l145 240h121l146 -240h-86l-119 175l-123 -175h-84z" />
|
||||
<glyph unicode="Ü" d="M111 246v1229h165v-1229q0 -33 24 -56.5t56 -23.5h983q33 0 56.5 23.5t23.5 56.5v1229h166v-1229q0 -102 -71.5 -174t-174.5 -72h-983q-102 0 -173.5 71.5t-71.5 174.5zM563 1772v167h170v-167h-170zM932 1772v167h168v-167h-168z" />
|
||||
<glyph unicode="Ý" horiz-adv-x="1650" d="M35 1475h190l572 -721l565 721h194l-677 -920v-555h-166v555zM674 1772l71 284h162l-69 -284h-164z" />
|
||||
<glyph unicode="ß" horiz-adv-x="1705" d="M117 0v1241q0 104 71.5 177t176.5 73h993q82 0 152.5 -62.5t95.5 -146.5v-350q0 -82 -62 -166q61 -80 62 -164v-354q0 -102 -73 -175t-175 -73h-776v168h776q33 0 56.5 23.5t23.5 56.5v354q0 33 -23.5 56.5t-56.5 23.5h-776v145h776q33 0 56.5 23.5t23.5 56.5v332 q0 33 -23.5 56.5t-56.5 23.5h-993q-33 0 -56.5 -23.5t-23.5 -56.5v-1239h-168z" />
|
||||
<glyph unicode="à" horiz-adv-x="1484" d="M106 248v430h1041v262q0 33 -23.5 56.5t-56.5 23.5h-961v168h961q104 0 177 -73t73 -175v-940h-963q-102 0 -175 72.5t-73 175.5zM274 248q0 -33 24 -56.5t56 -23.5h793v342h-873v-262zM590 1847h166l69 -284h-163z" />
|
||||
<glyph unicode="á" horiz-adv-x="1484" d="M106 248v430h1041v262q0 33 -23.5 56.5t-56.5 23.5h-961v168h961q104 0 177 -73t73 -175v-940h-963q-102 0 -175 72.5t-73 175.5zM274 248q0 -33 24 -56.5t56 -23.5h793v342h-873v-262zM590 1563l72 284h161l-69 -284h-164z" />
|
||||
<glyph unicode="â" horiz-adv-x="1484" d="M106 248v430h1041v262q0 33 -23.5 56.5t-56.5 23.5h-961v168h961q104 0 177 -73t73 -175v-940h-963q-102 0 -175 72.5t-73 175.5zM274 248q0 -33 24 -56.5t56 -23.5h793v342h-873v-262zM492 1563l145 239h121l145 -239h-86l-119 174l-123 -174h-83z" />
|
||||
<glyph unicode="ã" horiz-adv-x="1484" d="M106 248v430h1041v262q0 33 -23.5 56.5t-56.5 23.5h-961v168h961q104 0 177 -73t73 -175v-940h-963q-102 0 -175 72.5t-73 175.5zM274 248q0 -33 24 -56.5t56 -23.5h793v342h-873v-262zM381 1700v96q55 18 127 19q82 0 219 -69t209 -79h4q61 0 135 35v-105 q-78 -29 -135 -28q-72 0 -209 73.5t-223 73.5q-80 0 -127 -16z" />
|
||||
<glyph unicode="ä" horiz-adv-x="1484" d="M106 248v430h1041v262q0 33 -23.5 56.5t-56.5 23.5h-961v168h961q104 0 177 -73t73 -175v-940h-963q-102 0 -175 72.5t-73 175.5zM274 248q0 -33 24 -56.5t56 -23.5h793v342h-873v-262zM457 1563v168h170v-168h-170zM825 1563v168h168v-168h-168z" />
|
||||
<glyph unicode="å" horiz-adv-x="1484" d="M106 248v430h1041v262q0 33 -23.5 56.5t-56.5 23.5h-961v168h961q104 0 177 -73t73 -175v-940h-963q-102 0 -175 72.5t-73 175.5zM274 248q0 -33 24 -56.5t56 -23.5h793v342h-873v-262zM573 1579v111q0 45 33 76.5t78 31.5h115q45 0 77.5 -31.5t32.5 -76.5v-111 q0 -45 -32.5 -77t-77.5 -32h-115q-45 0 -78 32t-33 77zM655 1573q1 -21 19 -21h135q18 0 18 21v123q0 20 -18 20h-135q-18 0 -19 -20v-123z" />
|
||||
<glyph unicode="æ" horiz-adv-x="2412" d="M109 248v430h1040v262q0 33 -23.5 56.5t-56.5 23.5h-960v168h2000q104 0 176 -73t72 -175v-430h-1040v-270q2 -31 25.5 -51.5t54.5 -20.5h960v-168h-2001q-104 0 -175.5 72.5t-71.5 175.5zM276 248q0 -33 24 -56.5t56 -23.5h793v342h-873v-262zM1317 678h872v262 q0 33 -23.5 56.5t-56.5 23.5h-712q-33 0 -56.5 -23.5t-23.5 -56.5v-262z" />
|
||||
<glyph unicode="ç" horiz-adv-x="1419" d="M104 248v692q0 102 73 175t175 73h959v-168h-959q-33 0 -56.5 -23.5t-23.5 -56.5v-692q0 -33 23.5 -56.5t56.5 -23.5h963v-168h-524l-70 -285h-164l72 285h-277q-102 0 -175 72.5t-73 175.5z" />
|
||||
<glyph unicode="è" horiz-adv-x="1306" d="M104 248v692q0 102 73 175t175 73h713q104 0 177 -73t73 -175v-430h-1043v-262q0 -33 23.5 -56.5t56.5 -23.5h963v-168h-963q-102 0 -175 72.5t-73 175.5zM272 678h873v262q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -56.5 -23.5t-23.5 -56.5v-262zM502 1847h166l69 -284 h-164z" />
|
||||
<glyph unicode="é" horiz-adv-x="1306" d="M104 248v692q0 102 73 175t175 73h713q104 0 177 -73t73 -175v-430h-1043v-262q0 -33 23.5 -56.5t56.5 -23.5h963v-168h-963q-102 0 -175 72.5t-73 175.5zM272 678h873v262q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -56.5 -23.5t-23.5 -56.5v-262zM502 1565l71 284h162 l-69 -284h-164z" />
|
||||
<glyph unicode="ê" horiz-adv-x="1306" d="M104 248v692q0 102 73 175t175 73h713q104 0 177 -73t73 -175v-430h-1043v-262q0 -33 23.5 -56.5t56.5 -23.5h963v-168h-963q-102 0 -175 72.5t-73 175.5zM272 678h873v262q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -56.5 -23.5t-23.5 -56.5v-262zM403 1563l146 239h121 l145 -239h-86l-119 174l-123 -174h-84z" />
|
||||
<glyph unicode="ë" horiz-adv-x="1306" d="M104 248v692q0 102 73 175t175 73h713q104 0 177 -73t73 -175v-430h-1043v-262q0 -33 23.5 -56.5t56.5 -23.5h963v-168h-963q-102 0 -175 72.5t-73 175.5zM272 678h873v262q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -56.5 -23.5t-23.5 -56.5v-262zM369 1563v168h170v-168 h-170zM737 1563v168h168v-168h-168z" />
|
||||
<glyph unicode="ì" horiz-adv-x="438" d="M33 1849h166l69 -284h-164zM115 0v1188h168v-1188h-168z" />
|
||||
<glyph unicode="í" horiz-adv-x="438" d="M100 1563l72 284h162l-70 -284h-164zM104 0v1188h168v-1188h-168z" />
|
||||
<glyph unicode="î" horiz-adv-x="438" d="M-31 1563l146 239h121l145 -239h-86l-119 174l-123 -174h-84zM104 0v1188h168v-1188h-168z" />
|
||||
<glyph unicode="ï" horiz-adv-x="438" d="M-66 1567v168h170v-168h-170zM113 0v1188h168v-1188h-168zM303 1567v168h168v-168h-168z" />
|
||||
<glyph unicode="ñ" horiz-adv-x="1449" d="M111 0v1188h962q102 0 175 -73t73 -175v-940h-168v940q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -57 -23.5t-24 -56.5v-940h-168zM365 1698v96q55 18 127 18q82 0 219 -68.5t209 -78.5h4q61 0 135 35v-105q-78 -29 -135 -28q-72 0 -209 73.5t-223 73.5q-80 0 -127 -16z " />
|
||||
<glyph unicode="ò" horiz-adv-x="1417" d="M104 248v692q0 102 73 175t175 73h713q104 0 177 -73t73 -175v-692q0 -102 -73 -175t-177 -73h-713q-102 0 -175 72.5t-73 175.5zM272 248q0 -33 23.5 -56.5t56.5 -23.5h713q33 0 56.5 23.5t23.5 56.5v692q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -56.5 -23.5 t-23.5 -56.5v-692zM557 1849h166l70 -284h-164z" />
|
||||
<glyph unicode="ó" horiz-adv-x="1417" d="M104 248v692q0 102 73 175t175 73h713q104 0 177 -73t73 -175v-692q0 -102 -73 -175t-177 -73h-713q-102 0 -175 72.5t-73 175.5zM272 248q0 -33 23.5 -56.5t56.5 -23.5h713q33 0 56.5 23.5t23.5 56.5v692q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -56.5 -23.5 t-23.5 -56.5v-692zM557 1565l72 284h162l-70 -284h-164z" />
|
||||
<glyph unicode="ô" horiz-adv-x="1417" d="M104 248v692q0 102 73 175t175 73h713q104 0 177 -73t73 -175v-692q0 -102 -73 -175t-177 -73h-713q-102 0 -175 72.5t-73 175.5zM272 248q0 -33 23.5 -56.5t56.5 -23.5h713q33 0 56.5 23.5t23.5 56.5v692q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -56.5 -23.5 t-23.5 -56.5v-692zM459 1563l145 239h121l145 -239h-86l-118 174l-123 -174h-84z" />
|
||||
<glyph unicode="õ" horiz-adv-x="1417" d="M104 248v692q0 102 73 175t175 73h713q104 0 177 -73t73 -175v-692q0 -102 -73 -175t-177 -73h-713q-102 0 -175 72.5t-73 175.5zM272 248q0 -33 23.5 -56.5t56.5 -23.5h713q33 0 56.5 23.5t23.5 56.5v692q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -56.5 -23.5 t-23.5 -56.5v-692zM348 1698v96q55 18 127 18q82 0 219 -68.5t209 -78.5h4q61 0 135 35v-105q-78 -29 -135 -28q-72 0 -209 73.5t-223 73.5q-80 0 -127 -16z" />
|
||||
<glyph unicode="ö" horiz-adv-x="1417" d="M104 248v692q0 102 73 175t175 73h713q104 0 177 -73t73 -175v-692q0 -102 -73 -175t-177 -73h-713q-102 0 -175 72.5t-73 175.5zM272 248q0 -33 23.5 -56.5t56.5 -23.5h713q33 0 56.5 23.5t23.5 56.5v692q0 33 -23.5 56.5t-56.5 23.5h-713q-33 0 -56.5 -23.5 t-23.5 -56.5v-692zM424 1567v168h170v-168h-170zM793 1567v168h168v-168h-168z" />
|
||||
<glyph unicode="÷" horiz-adv-x="1040" d="M37 522v168h971v-168h-971zM440 2v168h168v-168h-168zM440 1018v168h168v-168h-168z" />
|
||||
<glyph unicode="ù" horiz-adv-x="1423" d="M109 248v940h167v-940q0 -33 24 -56.5t56 -23.5h713q33 0 57.5 23.5t24.5 56.5v940h168v-940q0 -102 -72.5 -175t-177.5 -73h-713q-102 0 -174.5 72.5t-72.5 175.5zM559 1849h166l70 -284h-164z" />
|
||||
<glyph unicode="ú" horiz-adv-x="1423" d="M109 248v940h167v-940q0 -33 24 -56.5t56 -23.5h713q33 0 57.5 23.5t24.5 56.5v940h168v-940q0 -102 -72.5 -175t-177.5 -73h-713q-102 0 -174.5 72.5t-72.5 175.5zM559 1565l72 284h162l-70 -284h-164z" />
|
||||
<glyph unicode="û" horiz-adv-x="1423" d="M109 248v940h167v-940q0 -33 24 -56.5t56 -23.5h713q33 0 57.5 23.5t24.5 56.5v940h168v-940q0 -102 -72.5 -175t-177.5 -73h-713q-102 0 -174.5 72.5t-72.5 175.5zM461 1565l145 239h121l145 -239h-86l-118 174l-123 -174h-84z" />
|
||||
<glyph unicode="ü" horiz-adv-x="1423" d="M109 248v940h167v-940q0 -33 24 -56.5t56 -23.5h713q33 0 57.5 23.5t24.5 56.5v940h168v-940q0 -102 -72.5 -175t-177.5 -73h-713q-102 0 -174.5 72.5t-72.5 175.5zM426 1563v168h170v-168h-170zM795 1563v168h168v-168h-168z" />
|
||||
<glyph unicode="ý" horiz-adv-x="1409" d="M86 248v936h168v-936q0 -33 23.5 -56.5t56.5 -23.5h713q33 0 56 23.5t23 56.5v936h168v-1405q0 -104 -71.5 -177t-175.5 -73h-742v170h742q33 0 56 23.5t23 56.5v221h-792q-102 0 -175 72.5t-73 175.5zM553 1565l72 284h161l-69 -284h-164z" />
|
||||
<glyph unicode="ÿ" horiz-adv-x="1409" d="M86 248v936h168v-936q0 -33 23.5 -56.5t56.5 -23.5h713q33 0 56 23.5t23 56.5v936h168v-1405q0 -104 -71.5 -177t-175.5 -73h-742v170h742q33 0 56 23.5t23 56.5v221h-792q-102 0 -175 72.5t-73 175.5zM420 1567v168h170v-168h-170zM788 1567v168h168v-168h-168z" />
|
||||
<glyph unicode="Œ" horiz-adv-x="2813" d="M109 246v983q0 102 71.5 174t173.5 72h2409v-166h-1180v-488h948v-168h-948v-487h1180v-166h-2409q-102 0 -173.5 71.5t-71.5 174.5zM274 246q0 -33 24 -56.5t56 -23.5h983q33 0 56.5 23.5t23.5 56.5v983q0 33 -23.5 56.5t-56.5 23.5h-983q-33 0 -56.5 -23.5t-23.5 -56.5 v-983z" />
|
||||
<glyph unicode="œ" horiz-adv-x="2410" d="M106 248v692q0 102 72 175t176 73h1753q104 0 176 -73t72 -175v-430h-1040v-262q0 -33 23.5 -56.5t56.5 -23.5h960v-168h-2001q-104 0 -176 72.5t-72 175.5zM274 248q0 -33 24 -56.5t56 -23.5h713q33 0 56.5 23.5t23.5 56.5v692q0 33 -23.5 56.5t-56.5 23.5h-713 q-33 0 -56.5 -23.5t-23.5 -56.5v-692zM1315 678h872v262q0 33 -23.5 56.5t-56.5 23.5h-712q-33 0 -56.5 -23.5t-23.5 -56.5v-262z" />
|
||||
<glyph unicode="Ÿ" horiz-adv-x="1650" d="M35 1475h190l572 -721l565 721h194l-677 -920v-555h-166v555zM541 1772v167h170v-167h-170zM909 1772v167h168v-167h-168z" />
|
||||
<glyph unicode="ˆ" horiz-adv-x="614" d="M57 1784l146 239h121l145 -239h-86l-119 174l-123 -174h-84z" />
|
||||
<glyph unicode="˜" horiz-adv-x="825" d="M47 1925v96q55 18 127 19q82 0 219 -69t209 -79h4q61 0 135 35v-104q-78 -29 -135 -29q-72 0 -209 74t-223 74q-80 0 -127 -17z" />
|
||||
<glyph unicode=" " horiz-adv-x="1035" />
|
||||
<glyph unicode=" " horiz-adv-x="2071" />
|
||||
<glyph unicode=" " horiz-adv-x="1035" />
|
||||
<glyph unicode=" " horiz-adv-x="2071" />
|
||||
<glyph unicode=" " horiz-adv-x="690" />
|
||||
<glyph unicode=" " horiz-adv-x="517" />
|
||||
<glyph unicode=" " horiz-adv-x="345" />
|
||||
<glyph unicode=" " horiz-adv-x="345" />
|
||||
<glyph unicode=" " horiz-adv-x="258" />
|
||||
<glyph unicode=" " horiz-adv-x="414" />
|
||||
<glyph unicode=" " horiz-adv-x="115" />
|
||||
<glyph unicode="‐" horiz-adv-x="1058" d="M121 522v168h799v-168h-799z" />
|
||||
<glyph unicode="‑" horiz-adv-x="1058" d="M121 522v168h799v-168h-799z" />
|
||||
<glyph unicode="‒" horiz-adv-x="1058" d="M121 522v168h799v-168h-799z" />
|
||||
<glyph unicode="–" horiz-adv-x="1449" d="M111 520v168h1230v-168h-1230z" />
|
||||
<glyph unicode="—" horiz-adv-x="1683" d="M111 520v168h1476v-168h-1476z" />
|
||||
<glyph unicode="‘" horiz-adv-x="344" d="M68 1030v201q0 82 47 147.5t123 87.5v-436h-170z" />
|
||||
<glyph unicode="’" horiz-adv-x="339" d="M111 1040v437h168v-201q0 -82 -47.5 -147.5t-120.5 -88.5z" />
|
||||
<glyph unicode="“" horiz-adv-x="743" d="M96 1028v201q0 82 47 147.5t123 87.5v-436h-170zM467 1028v201q0 82 46 147.5t122 87.5v-436h-168z" />
|
||||
<glyph unicode="”" horiz-adv-x="743" d="M111 1040v437h170v-201q0 -82 -47.5 -147.5t-122.5 -88.5zM481 1040v437h168v-201q0 -82 -47 -147.5t-121 -88.5z" />
|
||||
<glyph unicode="•" horiz-adv-x="759" d="M295 723v43q0 59 57 59h51q59 0 60 -59v-43q0 -57 -60 -57h-51q-57 0 -57 57z" />
|
||||
<glyph unicode="…" horiz-adv-x="1175" d="M111 0v168h168v-168h-168zM498 0v168h168v-168h-168zM883 0v168h170v-168h-170z" />
|
||||
<glyph unicode=" " horiz-adv-x="414" />
|
||||
<glyph unicode=" " horiz-adv-x="517" />
|
||||
<glyph unicode="€" horiz-adv-x="1636" d="M72 471v168h215v211h-215v170h215v207q0 104 72.5 177t177.5 73h1022v-170h-1022q-33 0 -56.5 -23.5t-23.5 -56.5v-207h868v-170h-868v-211h868v-168h-868v-223q0 -33 23.5 -56.5t56.5 -23.5h1022v-168h-1022q-104 0 -177 72.5t-73 175.5v223h-215z" />
|
||||
<glyph unicode="◼" horiz-adv-x="1187" d="M0 0v1188h1188v-1188h-1188z" />
|
||||
<hkern u1="?" u2="v" k="2" />
|
||||
<hkern u1="A" u2="Y" k="27" />
|
||||
<hkern u1="A" u2="W" k="63" />
|
||||
<hkern u1="B" u2="Y" k="63" />
|
||||
<hkern u1="B" u2="V" k="100" />
|
||||
<hkern u1="D" u2="Z" k="55" />
|
||||
<hkern u1="D" u2="V" k="59" />
|
||||
<hkern u1="E" u2="O" k="63" />
|
||||
<hkern u1="E" u2="M" k="59" />
|
||||
<hkern u1="F" u2="y" k="41" />
|
||||
<hkern u1="F" u2="T" k="-20" />
|
||||
<hkern u1="F" u2="R" k="-10" />
|
||||
<hkern u1="F" u2="J" k="492" />
|
||||
<hkern u1="G" u2="W" k="55" />
|
||||
<hkern u1="K" u2="H" k="59" />
|
||||
<hkern u1="K" u2="A" k="61" />
|
||||
<hkern u1="L" u2="Y" k="367" />
|
||||
<hkern u1="L" u2="W" k="264" />
|
||||
<hkern u1="L" u2="V" k="473" />
|
||||
<hkern u1="M" u2="c" k="20" />
|
||||
<hkern u1="O" u2="X" k="80" />
|
||||
<hkern u1="O" u2="W" k="55" />
|
||||
<hkern u1="O" u2="V" k="55" />
|
||||
<hkern u1="P" u2="v" k="-2" />
|
||||
<hkern u1="P" u2="d" k="-20" />
|
||||
<hkern u1="P" u2="J" k="383" />
|
||||
<hkern u1="P" u2="A" k="-20" />
|
||||
<hkern u1="R" u2="W" k="39" />
|
||||
<hkern u1="R" u2="V" k="39" />
|
||||
<hkern u1="S" u2="Y" k="20" />
|
||||
<hkern u1="S" u2="W" k="51" />
|
||||
<hkern u1="S" u2="N" k="39" />
|
||||
<hkern u1="T" u2="z" k="211" />
|
||||
<hkern u1="T" u2="y" k="186" />
|
||||
<hkern u1="T" u2="w" k="170" />
|
||||
<hkern u1="T" u2="u" k="207" />
|
||||
<hkern u1="T" u2="s" k="248" />
|
||||
<hkern u1="T" u2="o" k="252" />
|
||||
<hkern u1="V" u2="s" k="82" />
|
||||
<hkern u1="V" u2="o" k="61" />
|
||||
<hkern u1="V" u2="S" k="41" />
|
||||
<hkern u1="V" u2="O" k="68" />
|
||||
<hkern u1="V" u2="A" k="76" />
|
||||
<hkern u1="W" u2="u" k="41" />
|
||||
<hkern u1="W" u2="o" k="51" />
|
||||
<hkern u1="W" u2="i" k="20" />
|
||||
<hkern u1="W" u2="e" k="72" />
|
||||
<hkern u1="W" u2="a" k="31" />
|
||||
<hkern u1="W" u2="O" k="47" />
|
||||
<hkern u1="W" u2="A" k="55" />
|
||||
<hkern u1="X" u2="B" k="72" />
|
||||
<hkern u1="Y" u2="u" k="199" />
|
||||
<hkern u1="Y" u2="s" k="240" />
|
||||
<hkern u1="Y" u2="p" k="203" />
|
||||
<hkern u1="Y" u2="o" k="244" />
|
||||
<hkern u1="Y" u2="i" k="39" />
|
||||
<hkern u1="Y" u2="e" k="244" />
|
||||
<hkern u1="Y" u2="a" k="199" />
|
||||
<hkern u1="Y" u2="S" k="72" />
|
||||
<hkern u1="Z" u2="Y" k="20" />
|
||||
<hkern u1="a" u2="z" k="51" />
|
||||
<hkern u1="a" u2="x" k="31" />
|
||||
<hkern u1="a" u2="w" k="61" />
|
||||
<hkern u1="a" u2="v" k="41" />
|
||||
<hkern u1="a" u2="s" k="41" />
|
||||
<hkern u1="a" u2="r" k="10" />
|
||||
<hkern u1="a" u2="n" k="31" />
|
||||
<hkern u1="a" u2="m" k="41" />
|
||||
<hkern u1="a" u2="l" k="20" />
|
||||
<hkern u1="a" u2="c" k="20" />
|
||||
<hkern u1="a" u2="b" k="51" />
|
||||
<hkern u1="b" u2="d" k="-61" />
|
||||
<hkern u1="c" u2="z" k="41" />
|
||||
<hkern u1="c" u2="u" k="41" />
|
||||
<hkern u1="c" u2="t" k="51" />
|
||||
<hkern u1="c" u2="k" k="41" />
|
||||
<hkern u1="c" u2="h" k="31" />
|
||||
<hkern u1="c" u2="e" k="51" />
|
||||
<hkern u1="d" u2="u" k="47" />
|
||||
<hkern u1="d" u2="o" k="45" />
|
||||
<hkern u1="d" u2="e" k="41" />
|
||||
<hkern u1="e" u2="x" k="35" />
|
||||
<hkern u1="e" u2="w" k="61" />
|
||||
<hkern u1="e" u2="v" k="41" />
|
||||
<hkern u1="e" u2="r" k="10" />
|
||||
<hkern u1="e" u2="l" k="41" />
|
||||
<hkern u1="e" u2="e" k="31" />
|
||||
<hkern u1="e" u2="c" k="41" />
|
||||
<hkern u1="f" u2="u" k="-20" />
|
||||
<hkern u1="f" u2="t" k="-10" />
|
||||
<hkern u1="f" u2="," k="371" />
|
||||
<hkern u1="g" u2="w" k="41" />
|
||||
<hkern u1="g" u2="u" k="51" />
|
||||
<hkern u1="g" u2="s" k="43" />
|
||||
<hkern u1="g" u2="r" k="31" />
|
||||
<hkern u1="g" u2="o" k="41" />
|
||||
<hkern u1="g" u2="e" k="31" />
|
||||
<hkern u1="g" u2="c" k="31" />
|
||||
<hkern u1="g" u2="a" k="31" />
|
||||
<hkern u1="h" u2="i" k="-10" />
|
||||
<hkern u1="h" u2="f" k="-20" />
|
||||
<hkern u1="h" u2="a" k="-10" />
|
||||
<hkern u1="i" u2="x" k="84" />
|
||||
<hkern u1="i" u2="v" k="10" />
|
||||
<hkern u1="i" u2="q" k="31" />
|
||||
<hkern u1="i" u2="p" k="51" />
|
||||
<hkern u1="i" u2="n" k="31" />
|
||||
<hkern u1="i" u2="f" k="96" />
|
||||
<hkern u1="i" u2="c" k="31" />
|
||||
<hkern u1="i" u2="a" k="72" />
|
||||
<hkern u1="j" u2="e" k="41" />
|
||||
<hkern u1="k" u2="e" k="10" />
|
||||
<hkern u1="l" u2="’" k="178" />
|
||||
<hkern u1="l" u2="w" k="133" />
|
||||
<hkern u1="l" u2="v" k="121" />
|
||||
<hkern u1="l" u2="o" k="20" />
|
||||
<hkern u1="l" u2="m" k="20" />
|
||||
<hkern u1="l" u2="e" k="31" />
|
||||
<hkern u1="l" u2="d" k="-10" />
|
||||
<hkern u1="l" u2="a" k="20" />
|
||||
<hkern u1="m" u2="w" k="51" />
|
||||
<hkern u1="m" u2="m" k="27" />
|
||||
<hkern u1="m" u2="k" k="27" />
|
||||
<hkern u1="n" u2="x" k="51" />
|
||||
<hkern u1="n" u2="p" k="20" />
|
||||
<hkern u1="n" u2="o" k="31" />
|
||||
<hkern u1="n" u2="c" k="41" />
|
||||
<hkern u1="n" u2="b" k="41" />
|
||||
<hkern u1="o" u2="x" k="72" />
|
||||
<hkern u1="o" u2="w" k="72" />
|
||||
<hkern u1="o" u2="v" k="27" />
|
||||
<hkern u1="o" u2="u" k="47" />
|
||||
<hkern u1="o" u2="t" k="47" />
|
||||
<hkern u1="o" u2="s" k="41" />
|
||||
<hkern u1="o" u2="r" k="20" />
|
||||
<hkern u1="o" u2="o" k="41" />
|
||||
<hkern u1="o" u2="n" k="31" />
|
||||
<hkern u1="o" u2="l" k="41" />
|
||||
<hkern u1="o" u2="a" k="20" />
|
||||
<hkern u1="p" u2="s" k="-20" />
|
||||
<hkern u1="p" u2="i" k="-31" />
|
||||
<hkern u1="p" u2="d" k="-51" />
|
||||
<hkern u1="q" u2="u" k="51" />
|
||||
<hkern u1="r" u2="o" k="10" />
|
||||
<hkern u1="r" u2="." k="41" />
|
||||
<hkern u1="r" u2="," k="498" />
|
||||
<hkern u1="s" u2="z" k="47" />
|
||||
<hkern u1="s" u2="x" k="76" />
|
||||
<hkern u1="s" u2="v" k="41" />
|
||||
<hkern u1="s" u2="u" k="41" />
|
||||
<hkern u1="s" u2="n" k="47" />
|
||||
<hkern u1="s" u2="k" k="31" />
|
||||
<hkern u1="s" u2="e" k="31" />
|
||||
<hkern u1="s" u2="c" k="39" />
|
||||
<hkern u1="t" u2="z" k="-10" />
|
||||
<hkern u1="t" u2="i" k="-10" />
|
||||
<hkern u1="t" u2="h" k="-10" />
|
||||
<hkern u1="t" u2="d" k="-41" />
|
||||
<hkern u1="t" u2="b" k="-10" />
|
||||
<hkern u1="u" u2="z" k="31" />
|
||||
<hkern u1="u" u2="x" k="31" />
|
||||
<hkern u1="u" u2="t" k="31" />
|
||||
<hkern u1="u" u2="s" k="31" />
|
||||
<hkern u1="u" u2="p" k="31" />
|
||||
<hkern u1="u" u2="n" k="-10" />
|
||||
<hkern u1="u" u2="m" k="41" />
|
||||
<hkern u1="u" u2="f" k="20" />
|
||||
<hkern u1="u" u2="e" k="31" />
|
||||
<hkern u1="u" u2="d" k="-20" />
|
||||
<hkern u1="u" u2="c" k="41" />
|
||||
<hkern u1="u" u2="b" k="31" />
|
||||
<hkern u1="v" u2="s" k="51" />
|
||||
<hkern u1="v" u2="o" k="59" />
|
||||
<hkern u1="v" u2="e" k="61" />
|
||||
<hkern u1="w" u2="s" k="31" />
|
||||
<hkern u1="w" u2="r" k="41" />
|
||||
<hkern u1="w" u2="o" k="41" />
|
||||
<hkern u1="w" u2="e" k="41" />
|
||||
<hkern u1="x" u2="e" k="100" />
|
||||
<hkern u1="y" u2="o" k="47" />
|
||||
<hkern u1="y" u2="n" k="31" />
|
||||
<hkern u1="y" u2="l" k="-20" />
|
||||
<hkern u1="y" u2="e" k="41" />
|
||||
<hkern u1="y" u2="c" k="41" />
|
||||
<hkern u1="y" u2="a" k="47" />
|
||||
<hkern u1="z" u2="z" k="41" />
|
||||
<hkern u1="z" u2="t" k="41" />
|
||||
<hkern u1="z" u2="l" k="41" />
|
||||
<hkern u1="z" u2="e" k="51" />
|
||||
<hkern u1="z" u2="a" k="51" />
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
Before Width: | Height: | Size: 45 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
@@ -9,6 +9,7 @@
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="shortcut icon" href=/favicon2.ico>
|
||||
<link rel="icon" sizes="152x152 192x192" type="image/png" href="/192x192.png">
|
||||
|
||||
<!-- Apple/iOS headers -->
|
||||
|
||||
@@ -108,6 +108,7 @@ h1 {
|
||||
font-family: @fTitle;
|
||||
color: @primary;
|
||||
font-size: 1.6em;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
|
||||
@@ -41,8 +41,9 @@ svg {
|
||||
|
||||
}
|
||||
|
||||
.label {
|
||||
.label, .text-tip {
|
||||
text-transform: capitalize;
|
||||
fill: @fg;
|
||||
}
|
||||
|
||||
.metric {
|
||||
@@ -55,13 +56,16 @@ svg {
|
||||
|
||||
.label {
|
||||
font-size: 0.75em;
|
||||
fill: @fg;
|
||||
}
|
||||
|
||||
.text-tip {
|
||||
font-size: 0.8em
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
fill: @bgBlack;
|
||||
stroke: @secondary;
|
||||
stroke-width: 1px;
|
||||
font-size: 0.75em
|
||||
font-size: 0.8em
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table {
|
||||
.build-section {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
width: 45%;
|
||||
@@ -114,11 +114,20 @@
|
||||
width: 100%;
|
||||
});
|
||||
|
||||
thead {
|
||||
display: block;
|
||||
h1 {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
font-size: 1em;
|
||||
color: #000;
|
||||
padding: 2px 0.4em 0;
|
||||
background-color: #c06400;
|
||||
text-transform: uppercase;
|
||||
line-height: 1.3em;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
tbody {
|
||||
div {
|
||||
display: block;
|
||||
font-size: 0.8em;
|
||||
width: 100%;
|
||||
@@ -129,6 +138,10 @@
|
||||
max-height: 8em;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td {
|
||||
cursor: pointer;
|
||||
vertical-align: top;
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
@font-face {
|
||||
font-family: 'Orbitron-Regular';
|
||||
src: url('../fonts/orbitron-regular-webfont.eot');
|
||||
src: url('../fonts/orbitron-regular-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/orbitron-regular-webfont.woff2') format('woff2'),
|
||||
url('../fonts/orbitron-regular-webfont.woff') format('woff'),
|
||||
url('../fonts/orbitron-regular-webfont.ttf') format('truetype'),
|
||||
url('../fonts/orbitron-regular-webfont.svg#orbitronregular') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Eurostile';
|
||||
@@ -23,4 +12,4 @@
|
||||
}
|
||||
|
||||
@fStandard: 'Eurostile', Helvetica, sans-serif;
|
||||
@fTitle: 'Orbitron-Regular', Arial, sans-serif;
|
||||
@fTitle: 'Eurostile', Arial, sans-serif;
|
||||
|
||||
@@ -47,7 +47,7 @@ header {
|
||||
padding : 0 1em;
|
||||
cursor: pointer;
|
||||
color: @warning;
|
||||
text-transform: capitalize;
|
||||
text-transform: uppercase;
|
||||
// Less than 600px screen width: hide text
|
||||
|
||||
&.disabled {
|
||||
|
||||
@@ -25,8 +25,8 @@ CopyDirPlugin.prototype.apply = function(compiler) {
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
app: path.resolve(__dirname, 'src/app/index'),
|
||||
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
|
||||
app: ['babel-polyfill', path.resolve(__dirname, 'src/app/index')],
|
||||
lib: ['babel-polyfill', 'd3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['', '.js', '.jsx', '.json', '.less'],
|
||||
@@ -39,9 +39,9 @@ module.exports = {
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, 'build'),
|
||||
filename: '[name].[hash:6].js',
|
||||
chunkFilename: '[name].[hash:6]',
|
||||
publicPath: process.env.CDN || '/'
|
||||
filename: '[name].[chunkhash:6].js',
|
||||
chunkFilename: '[name].[chunkhash:6]',
|
||||
publicPath: '/'
|
||||
},
|
||||
plugins: [
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
@@ -50,7 +50,7 @@ module.exports = {
|
||||
},
|
||||
'screw-ie8': true
|
||||
}),
|
||||
new webpack.optimize.CommonsChunkPlugin('lib', 'lib.[hash:6].js'),
|
||||
new webpack.optimize.CommonsChunkPlugin('lib', 'lib.[chunkhash:6].js'),
|
||||
new HtmlWebpackPlugin({
|
||||
inject: false,
|
||||
appCache: 'coriolis.appcache',
|
||||
|
||||
Reference in New Issue
Block a user