mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-08 22:33:24 +00:00
React port nearing completetion. Adding tests
This commit is contained in:
@@ -10,6 +10,8 @@ Coriolis was created using assets and imagery from Elite: Dangerous, with the pe
|
|||||||
|
|
||||||
## Contributing
|
## 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.
|
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
|
### Feature Requests, Suggestions & Bugs
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"Miner": "25A5A5A4D4A5A5C0s0s24242l2l---04054a1q02022o27"
|
"Miner": "25A5A5A4D4A5A5C0s0s24242l2l---04054a1q02022o27"
|
||||||
},
|
},
|
||||||
"cobra_mk_iii": {
|
"cobra_mk_iii": {
|
||||||
"Example": "24A4A4A3D3A3A4C0s0s2d2d0m0445032b2o2753.AwRj4yKA.CwBhEYyrKhmMQ===",
|
"Example": "24A4A4A3D3A3A4C0s0s2d2d0m0445032b2o2753.AwRj4yKA.CwBhEYyrKhmMQ==="
|
||||||
},
|
},
|
||||||
"imperial_clipper": {
|
"imperial_clipper": {
|
||||||
"Cargo": "03A5D5A5D4D5D4C--0s0s----0605450302020101",
|
"Cargo": "03A5D5A5D4D5D4C--0s0s----0605450302020101",
|
||||||
|
|||||||
@@ -1,196 +0,0 @@
|
|||||||
xdescribe('Import Controller', function() {
|
|
||||||
beforeEach(module('app'));
|
|
||||||
|
|
||||||
var importController, $rootScope, $stateParams, scope;
|
|
||||||
|
|
||||||
var eventStub = {
|
|
||||||
preventDefault: function(){ },
|
|
||||||
stopPropagation: function(){ }
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(inject(function(_$rootScope_, $controller) {
|
|
||||||
$rootScope = _$rootScope_;
|
|
||||||
$rootScope.discounts = {
|
|
||||||
ship: 1,
|
|
||||||
components: 1
|
|
||||||
};
|
|
||||||
$stateParams = { };
|
|
||||||
scope = $rootScope.$new();
|
|
||||||
scope.$parent.dismiss = function() {};
|
|
||||||
|
|
||||||
var store = {};
|
|
||||||
|
|
||||||
spyOn(localStorage, 'getItem').and.callFake(function (key) {
|
|
||||||
return store[key];
|
|
||||||
});
|
|
||||||
spyOn(localStorage, 'setItem').and.callFake(function (key, value) {
|
|
||||||
return store[key] = value + '';
|
|
||||||
});
|
|
||||||
spyOn(localStorage, 'clear').and.callFake(function () {
|
|
||||||
store = {};
|
|
||||||
});
|
|
||||||
|
|
||||||
importController = $controller('ImportController', { $rootScope: $rootScope, $scope: scope, $stateParams: $stateParams });
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('Import Backup', function() {
|
|
||||||
|
|
||||||
it('imports a valid backup', function() {
|
|
||||||
var importData = __json__['fixtures/valid-backup'];
|
|
||||||
scope.importString = angular.toJson(importData);
|
|
||||||
scope.validateImport();
|
|
||||||
expect(scope.importValid).toBeTruthy();
|
|
||||||
expect(scope.errorMsg).toEqual(null);
|
|
||||||
scope.process();
|
|
||||||
expect(scope.processed).toBeTruthy();
|
|
||||||
scope.import();
|
|
||||||
expect(angular.fromJson(localStorage.getItem('builds'))).toEqual(importData.builds);
|
|
||||||
expect(angular.fromJson(localStorage.getItem('comparisons'))).toEqual(importData.comparisons);
|
|
||||||
expect(localStorage.getItem('insurance')).toEqual(importData.insurance);
|
|
||||||
expect(angular.fromJson(localStorage.getItem('discounts'))).toEqual(importData.discounts);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('imports an old valid backup', function() {
|
|
||||||
var importData = __json__['fixtures/old-valid-export'];
|
|
||||||
scope.importString = angular.toJson(importData);
|
|
||||||
scope.validateImport();
|
|
||||||
expect(scope.importValid).toBeTruthy();
|
|
||||||
expect(scope.errorMsg).toEqual(null);
|
|
||||||
scope.process();
|
|
||||||
expect(scope.processed).toBeTruthy();
|
|
||||||
scope.import();
|
|
||||||
expect(angular.fromJson(localStorage.getItem('builds'))).toEqual(importData.builds);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('catches an invalid backup', function() {
|
|
||||||
var importData = __json__['fixtures/valid-backup'];
|
|
||||||
|
|
||||||
scope.importString = 'null';
|
|
||||||
scope.validateImport();
|
|
||||||
expect(scope.importValid).toBeFalsy();
|
|
||||||
expect(scope.errorMsg).toEqual('Must be an object or array!');
|
|
||||||
|
|
||||||
scope.importString = '{ "builds": "Should not be a string" }';
|
|
||||||
scope.validateImport();
|
|
||||||
expect(scope.importValid).toBeFalsy();
|
|
||||||
expect(scope.errorMsg).toEqual('builds must be an object!');
|
|
||||||
|
|
||||||
scope.importString = angular.toJson(importData).replace('anaconda', 'invalid_ship');
|
|
||||||
scope.validateImport();
|
|
||||||
expect(scope.importValid).toBeFalsy();
|
|
||||||
expect(scope.errorMsg).toEqual('"invalid_ship" is not a valid Ship Id!');
|
|
||||||
|
|
||||||
scope.importString = angular.toJson(importData).replace('Dream', '');
|
|
||||||
scope.validateImport();
|
|
||||||
expect(scope.importValid).toBeFalsy();
|
|
||||||
expect(scope.errorMsg).toEqual('Imperial Clipper build "" must be a string at least 1 character long!');
|
|
||||||
|
|
||||||
invalidImportData = angular.copy(importData);
|
|
||||||
invalidImportData.builds.asp = null; // Remove Asp Miner build used in comparison
|
|
||||||
scope.importString = angular.toJson(invalidImportData);
|
|
||||||
scope.validateImport();
|
|
||||||
expect(scope.importValid).toBeFalsy();
|
|
||||||
expect(scope.errorMsg).toEqual('asp build "Miner" data is missing!');
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Import Detailed Build', function() {
|
|
||||||
|
|
||||||
it('imports a valid v1 build', function() {
|
|
||||||
var importData = __json__['fixtures/anaconda-test-detailed-export-v1'];
|
|
||||||
scope.importString = angular.toJson(importData);
|
|
||||||
scope.validateImport();
|
|
||||||
expect(scope.importValid).toBeTruthy();
|
|
||||||
expect(scope.errorMsg).toEqual(null);
|
|
||||||
scope.process();
|
|
||||||
expect(scope.processed).toBeTruthy();
|
|
||||||
scope.import();
|
|
||||||
expect(angular.fromJson(localStorage.getItem('builds'))).toEqual({
|
|
||||||
anaconda: { 'Test': '48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.Iw18ZlA=.Aw18ZlA=' }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('imports a valid v2 build', function() {
|
|
||||||
var importData = __json__['fixtures/anaconda-test-detailed-export-v2'];
|
|
||||||
scope.importString = angular.toJson(importData);
|
|
||||||
scope.validateImport();
|
|
||||||
expect(scope.importValid).toBeTruthy();
|
|
||||||
expect(scope.errorMsg).toEqual(null);
|
|
||||||
scope.process();
|
|
||||||
expect(scope.processed).toBeTruthy();
|
|
||||||
scope.import();
|
|
||||||
expect(angular.fromJson(localStorage.getItem('builds'))).toEqual({
|
|
||||||
anaconda: { 'Test': '48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA' }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('catches an invalid build', function() {
|
|
||||||
var importData = __json__['fixtures/anaconda-test-detailed-export-v2'];
|
|
||||||
scope.importString = angular.toJson(importData).replace('components', 'comps');
|
|
||||||
scope.validateImport();
|
|
||||||
expect(scope.importValid).toBeFalsy();
|
|
||||||
expect(scope.errorMsg).toEqual('Anaconda Build "Test": Invalid data');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Import Detaild Builds Array', function() {
|
|
||||||
|
|
||||||
it('imports all builds', function() {
|
|
||||||
var importData = __json__['fixtures/valid-detailed-export'];
|
|
||||||
var expectedBuilds = __json__['fixtures/expected-builds'];
|
|
||||||
scope.importString = angular.toJson(importData);
|
|
||||||
scope.validateImport();
|
|
||||||
expect(scope.importValid).toBeTruthy();
|
|
||||||
expect(scope.errorMsg).toEqual(null);
|
|
||||||
scope.process();
|
|
||||||
expect(scope.processed).toBeTruthy();
|
|
||||||
scope.import();
|
|
||||||
var builds = angular.fromJson(localStorage.getItem('builds'));
|
|
||||||
for (var s in builds) {
|
|
||||||
for (var b in builds[s]) {
|
|
||||||
expect(builds[s][b]).toEqual(expectedBuilds[s][b]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Import E:D Shipyard Builds', function() {
|
|
||||||
|
|
||||||
it('imports a valid builds', function() {
|
|
||||||
var imports = __json__['fixtures/ed-shipyard-import-valid'];
|
|
||||||
|
|
||||||
for (var i = 0; i < imports.length; i++ ) {
|
|
||||||
scope.importString = imports[i].buildText;
|
|
||||||
scope.validateImport();
|
|
||||||
expect(scope.importValid).toBeTruthy();
|
|
||||||
expect(scope.errorMsg).toEqual(null, 'Build #' + i + ': ' + imports[i].buildName);
|
|
||||||
|
|
||||||
if (scope.importValid) { // No point in carrying out other assertions
|
|
||||||
scope.process();
|
|
||||||
expect(scope.processed).toBeTruthy();
|
|
||||||
scope.import();
|
|
||||||
var allBuilds = angular.fromJson(localStorage.getItem('builds'));
|
|
||||||
var shipBuilds = allBuilds ? allBuilds[imports[i].shipId] : null;
|
|
||||||
var build = shipBuilds ? shipBuilds[imports[i].buildName] : null;
|
|
||||||
expect(build).toEqual(imports[i].buildCode, 'Build #' + i + ': ' + imports[i].buildName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('catches invalid builds', function() {
|
|
||||||
var imports = __json__['fixtures/ed-shipyard-import-invalid'];
|
|
||||||
for (var i = 0; i < imports.length; i++ ) {
|
|
||||||
scope.importString = imports[i].buildText;
|
|
||||||
scope.validateImport();
|
|
||||||
expect(scope.importValid).toBeFalsy();
|
|
||||||
expect(scope.errorMsg).toEqual(imports[i].errorMsg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
218
__tests__/test-import.js
Normal file
218
__tests__/test-import.js
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
jest.dontMock('../src/app/stores/Persist');
|
||||||
|
jest.dontMock('../src/app/components/TranslatedComponent');
|
||||||
|
jest.dontMock('../src/app/components/ModalImport');
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import TU from 'react-testutils-additions';
|
||||||
|
import Utils from './testUtils';
|
||||||
|
import Persist from '../src/app/stores/Persist';
|
||||||
|
import { getLanguage } from '../src/app/i18n/Language';
|
||||||
|
|
||||||
|
describe('Import Controller', function() {
|
||||||
|
|
||||||
|
const Persist = require('../src/app/stores/Persist').default;
|
||||||
|
const ModalImport = require('../src/app/components/ModalImport').default;
|
||||||
|
const mockContext = {
|
||||||
|
language: getLanguage('en'),
|
||||||
|
sizeRatio: 1,
|
||||||
|
openMenu: jest.genMockFunction(),
|
||||||
|
closeMenu: jest.genMockFunction(),
|
||||||
|
showModal: jest.genMockFunction(),
|
||||||
|
hideModal: jest.genMockFunction(),
|
||||||
|
tooltip: jest.genMockFunction(),
|
||||||
|
termtip: jest.genMockFunction(),
|
||||||
|
onWindowResize: jest.genMockFunction()
|
||||||
|
};
|
||||||
|
|
||||||
|
let modal, render, ContextProvider = Utils.createContextProvider(mockContext);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear saved builds, and reset React DOM
|
||||||
|
*/
|
||||||
|
function reset() {
|
||||||
|
Persist.deleteAll();
|
||||||
|
render = TU.renderIntoDocument(<ContextProvider><ModalImport /></ContextProvider>);
|
||||||
|
modal = TU.findRenderedComponentWithType(render, ModalImport);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulate user import text entry / paste
|
||||||
|
* @param {string} text Import text / raw data
|
||||||
|
*/
|
||||||
|
function pasteText(text) {
|
||||||
|
let textarea = TU.findRenderedDOMComponentWithTag(render, 'textarea');
|
||||||
|
TU.Simulate.change(textarea, { target: { value: text } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulate click on Proceed button
|
||||||
|
*/
|
||||||
|
function clickProceed() {
|
||||||
|
let proceedButton = TU.findRenderedDOMComponentWithId(render, 'proceed');
|
||||||
|
TU.Simulate.click(proceedButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulate click on Import button
|
||||||
|
*/
|
||||||
|
function clickImport() {
|
||||||
|
let importButton = TU.findRenderedDOMComponentWithId(render, 'import');
|
||||||
|
TU.Simulate.click(importButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Import Backup', function() {
|
||||||
|
|
||||||
|
beforeEach(reset);
|
||||||
|
|
||||||
|
it('imports a valid backup', function() {
|
||||||
|
let importData = require('./fixtures/valid-backup');
|
||||||
|
let importString = JSON.stringify(importData);
|
||||||
|
|
||||||
|
expect(modal.state.importValid).toEqual(false);
|
||||||
|
expect(modal.state.errorMsg).toEqual(null);
|
||||||
|
pasteText(importString);
|
||||||
|
expect(modal.state.importValid).toBe(true);
|
||||||
|
expect(modal.state.errorMsg).toEqual(null);
|
||||||
|
expect(modal.state.builds).toEqual(importData.builds);
|
||||||
|
expect(modal.state.comparisons).toEqual(importData.comparisons);
|
||||||
|
expect(modal.state.discounts).toEqual(importData.discounts);
|
||||||
|
expect(modal.state.insurance).toBe(importData.insurance.toLowerCase());
|
||||||
|
clickProceed();
|
||||||
|
expect(modal.state.processed).toBe(true);
|
||||||
|
expect(modal.state.errorMsg).toEqual(null);
|
||||||
|
clickImport();
|
||||||
|
expect(Persist.getBuilds()).toEqual(importData.builds);
|
||||||
|
expect(Persist.getComparisons()).toEqual(importData.comparisons);
|
||||||
|
expect(Persist.getInsurance()).toEqual(importData.insurance.toLowerCase());
|
||||||
|
expect(Persist.getShipDiscount()).toEqual(importData.discounts[0]);
|
||||||
|
expect(Persist.getModuleDiscount()).toEqual(importData.discounts[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('imports an old valid backup', function() {
|
||||||
|
let importData = require('./fixtures/old-valid-export');
|
||||||
|
let importStr = JSON.stringify(importData);
|
||||||
|
|
||||||
|
pasteText(importStr);
|
||||||
|
expect(modal.state.builds).toEqual(importData.builds);
|
||||||
|
expect(modal.state.importValid).toBe(true);
|
||||||
|
expect(modal.state.errorMsg).toEqual(null);
|
||||||
|
clickProceed();
|
||||||
|
expect(modal.state.processed).toBeTruthy();
|
||||||
|
clickImport();
|
||||||
|
expect(Persist.getBuilds()).toEqual(importData.builds);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('catches an invalid backup', function() {
|
||||||
|
let importData = require('./fixtures/valid-backup');
|
||||||
|
let invalidImportData = Object.assign({}, importData);
|
||||||
|
invalidImportData.builds.asp = null; // Remove Asp Miner build used in comparison
|
||||||
|
|
||||||
|
pasteText('"this is not valid"');
|
||||||
|
expect(modal.state.importValid).toBeFalsy();
|
||||||
|
expect(modal.state.errorMsg).toEqual('Must be an object or array!');
|
||||||
|
pasteText('{ "builds": "Should not be a string" }');
|
||||||
|
expect(modal.state.importValid).toBeFalsy();
|
||||||
|
expect(modal.state.errorMsg).toEqual('builds must be an object!');
|
||||||
|
pasteText(JSON.stringify(importData).replace('anaconda', 'invalid_ship'));
|
||||||
|
expect(modal.state.importValid).toBeFalsy();
|
||||||
|
expect(modal.state.errorMsg).toEqual('"invalid_ship" is not a valid Ship Id!');
|
||||||
|
pasteText(JSON.stringify(importData).replace('Dream', ''));
|
||||||
|
expect(modal.state.importValid).toBeFalsy();
|
||||||
|
expect(modal.state.errorMsg).toEqual('Imperial Clipper build "" must be a string at least 1 character long!');
|
||||||
|
pasteText(JSON.stringify(invalidImportData));
|
||||||
|
expect(modal.state.importValid).toBeFalsy();
|
||||||
|
expect(modal.state.errorMsg).toEqual('asp build "Miner" data is missing!');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Import Detailed Build', function() {
|
||||||
|
|
||||||
|
beforeEach(reset);
|
||||||
|
|
||||||
|
it('imports a valid v3 build', function() {
|
||||||
|
let importData = require('./fixtures/anaconda-test-detailed-export-v3');
|
||||||
|
pasteText(JSON.stringify(importData));
|
||||||
|
|
||||||
|
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' }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('catches an invalid build', function() {
|
||||||
|
let importData = require('./fixtures/anaconda-test-detailed-export-v3');
|
||||||
|
pasteText(JSON.stringify(importData).replace('components', 'comps'));
|
||||||
|
|
||||||
|
expect(modal.state.importValid).toBeFalsy();
|
||||||
|
expect(modal.state.errorMsg).toEqual('Anaconda Build "Test": Invalid data');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Import Detaild Builds Array', function() {
|
||||||
|
|
||||||
|
beforeEach(reset);
|
||||||
|
|
||||||
|
it('imports all builds', function() {
|
||||||
|
let importData = require('./fixtures/valid-detailed-export');
|
||||||
|
let expectedBuilds = require('./fixtures/expected-builds');
|
||||||
|
|
||||||
|
pasteText(JSON.stringify(importData));
|
||||||
|
expect(modal.state.importValid).toBeTruthy();
|
||||||
|
expect(modal.state.errorMsg).toEqual(null);
|
||||||
|
clickProceed();
|
||||||
|
expect(modal.state.processed).toBeTruthy();
|
||||||
|
clickImport();
|
||||||
|
|
||||||
|
let builds = Persist.getBuilds();
|
||||||
|
|
||||||
|
for (let s in builds) {
|
||||||
|
for (let b in builds[s]) {
|
||||||
|
expect(builds[s][b]).toEqual(expectedBuilds[s][b]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Import E:D Shipyard Builds', function() {
|
||||||
|
|
||||||
|
it('imports a valid builds', function() {
|
||||||
|
let imports = require('./fixtures/ed-shipyard-import-valid');
|
||||||
|
|
||||||
|
for (let i = 0; i < imports.length; i++ ) {
|
||||||
|
reset();
|
||||||
|
pasteText(imports[i].buildText);
|
||||||
|
expect(modal.state.importValid).toBeTruthy();
|
||||||
|
expect(modal.state.errorMsg).toEqual(null, 'Build #' + i + ': ' + imports[i].buildName);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('catches invalid builds', function() {
|
||||||
|
let imports = require('./fixtures/ed-shipyard-import-invalid');
|
||||||
|
|
||||||
|
for (let i = 0; i < imports.length; i++ ) {
|
||||||
|
reset();
|
||||||
|
pasteText(imports[i].buildText);
|
||||||
|
expect(modal.state.importValid).toBeFalsy();
|
||||||
|
expect(modal.state.errorMsg).toEqual(imports[i].errorMsg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
24
__tests__/testUtils.js
Normal file
24
__tests__/testUtils.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const TestUtils = {
|
||||||
|
createContextProvider: function(context) {
|
||||||
|
var _contextTypes = {};
|
||||||
|
|
||||||
|
Object.keys(context).forEach(function(key) {
|
||||||
|
_contextTypes[key] = React.PropTypes.any;
|
||||||
|
});
|
||||||
|
|
||||||
|
return React.createClass({
|
||||||
|
displayName: 'ContextProvider',
|
||||||
|
childContextTypes: _contextTypes,
|
||||||
|
getChildContext() { return context; },
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return React.Children.only(this.props.children);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default TestUtils;
|
||||||
@@ -5,6 +5,7 @@ var config = require('./webpack.config.dev');
|
|||||||
new WebpackDevServer(webpack(config), {
|
new WebpackDevServer(webpack(config), {
|
||||||
publicPath: config.output.publicPath,
|
publicPath: config.output.publicPath,
|
||||||
hot: true,
|
hot: true,
|
||||||
|
headers: { "Access-Control-Allow-Origin": "*" },
|
||||||
historyApiFallback: {
|
historyApiFallback: {
|
||||||
rewrites: [
|
rewrites: [
|
||||||
// For some reason connect-history-api-fallback does not allow '.' in the URL for history fallback...
|
// For some reason connect-history-api-fallback does not allow '.' in the URL for history fallback...
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
"<rootDir>/node_modules/react",
|
"<rootDir>/node_modules/react",
|
||||||
"<rootDir>/node_modules/react-dom",
|
"<rootDir>/node_modules/react-dom",
|
||||||
"<rootDir>/node_modules/react-addons-test-utils",
|
"<rootDir>/node_modules/react-addons-test-utils",
|
||||||
|
"<rootDir>/node_modules/react-testutils-additions",
|
||||||
"<rootDir>/node_modules/fbjs",
|
"<rootDir>/node_modules/fbjs",
|
||||||
"<rootDir>/node_modules/fbemitter",
|
"<rootDir>/node_modules/fbemitter",
|
||||||
"<rootDir>/node_modules/classnames",
|
"<rootDir>/node_modules/classnames",
|
||||||
@@ -44,7 +45,8 @@
|
|||||||
"<rootDir>/node_modules/coriolis-data",
|
"<rootDir>/node_modules/coriolis-data",
|
||||||
"<rootDir>/src/app/shipyard",
|
"<rootDir>/src/app/shipyard",
|
||||||
"<rootDir>/src/app/i18n",
|
"<rootDir>/src/app/i18n",
|
||||||
"<rootDir>/src/app/utils"
|
"<rootDir>/src/app/utils",
|
||||||
|
"<rootDir>/__tests__"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -56,7 +58,7 @@
|
|||||||
"babel-preset-es2015": "^6.3.13",
|
"babel-preset-es2015": "^6.3.13",
|
||||||
"babel-preset-react": "^6.3.13",
|
"babel-preset-react": "^6.3.13",
|
||||||
"babel-preset-stage-0": "^6.3.13",
|
"babel-preset-stage-0": "^6.3.13",
|
||||||
"css-loader": "^0.23.0",
|
"css-loader": "^0.23.0",
|
||||||
"eslint": "2.0.0-beta.1",
|
"eslint": "2.0.0-beta.1",
|
||||||
"eslint-plugin-react": "^3.15.0",
|
"eslint-plugin-react": "^3.15.0",
|
||||||
"expose-loader": "^0.7.1",
|
"expose-loader": "^0.7.1",
|
||||||
@@ -69,6 +71,7 @@
|
|||||||
"less": "^2.5.3",
|
"less": "^2.5.3",
|
||||||
"less-loader": "^2.2.1",
|
"less-loader": "^2.2.1",
|
||||||
"react-addons-test-utils": "^0.14.6",
|
"react-addons-test-utils": "^0.14.6",
|
||||||
|
"react-testutils-additions": "^0.16.0",
|
||||||
"rimraf": "^2.4.3",
|
"rimraf": "^2.4.3",
|
||||||
"style-loader": "^0.13.0",
|
"style-loader": "^0.13.0",
|
||||||
"url-loader": "^0.5.6",
|
"url-loader": "^0.5.6",
|
||||||
|
|||||||
@@ -238,13 +238,11 @@ export default class Coriolis extends React.Component {
|
|||||||
* @return {React.Component} The main app
|
* @return {React.Component} The main app
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
return (
|
return <div onClick={this._closeMenu}>
|
||||||
<div onClick={this._closeMenu}>
|
<Header appCacheUpdate={this.state.appCacheUpdate} currentMenu={this.state.currentMenu} />
|
||||||
<Header appCacheUpdate={this.state.appCacheUpdate} currentMenu={this.state.currentMenu} />
|
{ this.state.page ? <this.state.page currentMenu={this.state.currentMenu} /> : <NotFoundPage/> }
|
||||||
{ this.state.page ? <this.state.page currentMenu={this.state.currentMenu} /> : <NotFoundPage/> }
|
{ this.state.modal }
|
||||||
{ this.state.modal }
|
{ this.state.tooltip }
|
||||||
{ this.state.tooltip }
|
</div>;
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import cn from 'classnames';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the current window location equals the link
|
* Returns true if the current window location equals the link
|
||||||
|
* @param {string} href URL/Href
|
||||||
* @return {boolean} If matches
|
* @return {boolean} If matches
|
||||||
*/
|
*/
|
||||||
function isActive(href) {
|
function isActive(href) {
|
||||||
|
|||||||
@@ -613,6 +613,7 @@ export default class CostSection extends TranslatedComponent {
|
|||||||
if (nextProps.ship != this.props.ship || nextProps.code != this.props.code) {
|
if (nextProps.ship != this.props.ship || nextProps.code != this.props.code) {
|
||||||
this._updateAmmoCosts(nextProps.ship);
|
this._updateAmmoCosts(nextProps.ship);
|
||||||
this._updateRetrofit(nextProps.ship, retrofitShip);
|
this._updateRetrofit(nextProps.ship, retrofitShip);
|
||||||
|
this.setState({ total: nextProps.ship.totalCost });
|
||||||
this._sortCost(nextProps.ship);
|
this._sortCost(nextProps.ship);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ export default class Header extends TranslatedComponent {
|
|||||||
<br/>
|
<br/>
|
||||||
<span className='cap ptr' onClick={this._toggleTooltips} >
|
<span className='cap ptr' onClick={this._toggleTooltips} >
|
||||||
{translate('tooltips')}
|
{translate('tooltips')}
|
||||||
<div className={cn({ disabled: !tips, 'primary-disabled': tips })} style={{ marginLeft: '0.5em', display: 'inline-block' }}>{(tips ? '✔' : '✖')}</div>
|
<div className={cn({ disabled: !tips, 'primary-disabled': tips })} style={{ marginLeft: '0.5em', display: 'inline-block' }}>{(tips ? '✓' : '✗')}</div>
|
||||||
</span>
|
</span>
|
||||||
<br/>
|
<br/>
|
||||||
{translate('insurance')}
|
{translate('insurance')}
|
||||||
|
|||||||
@@ -88,10 +88,13 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
y0 = func(x0),
|
y0 = func(x0),
|
||||||
tips = this.tipContainer,
|
tips = this.tipContainer,
|
||||||
yTotal = 0,
|
yTotal = 0,
|
||||||
flip = (xPos / innerWidth > 0.65),
|
flip = (xPos / innerWidth > 0.60),
|
||||||
tipWidth = 0,
|
tipWidth = 0,
|
||||||
tipHeightPx = tips.selectAll('rect').node().getBoundingClientRect().height;
|
tipHeightPx = tips.selectAll('rect').node().getBoundingClientRect().height;
|
||||||
|
|
||||||
|
|
||||||
|
xPos = xScale(x0); // Clamp xPos
|
||||||
|
|
||||||
tips.selectAll('text.label.y').text(function(d, i) {
|
tips.selectAll('text.label.y').text(function(d, i) {
|
||||||
let yVal = series ? y0[series[i]] : y0;
|
let yVal = series ? y0[series[i]] : y0;
|
||||||
yTotal += yVal;
|
yTotal += yVal;
|
||||||
@@ -110,7 +113,7 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
tips.attr('transform', 'translate(' + xPos + ',' + tipY + ')');
|
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').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.label.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).style('text-anchor', flip ? 'end' : 'start');
|
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));
|
this.markersContainer.selectAll('circle').attr('cx', xPos).attr('cy', (d, i) => yScale(series ? y0[series[i]] : y0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,9 +139,10 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
* @param {SyntheticEvent} e Event
|
* @param {SyntheticEvent} e Event
|
||||||
*/
|
*/
|
||||||
_showTip(e) {
|
_showTip(e) {
|
||||||
this._moveTip(e);
|
e.preventDefault();
|
||||||
this.tipContainer.style('display', null);
|
this.tipContainer.style('display', null);
|
||||||
this.markersContainer.style('display', null);
|
this.markersContainer.style('display', null);
|
||||||
|
this._moveTip(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -146,13 +150,16 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
* @param {SyntheticEvent} e Event
|
* @param {SyntheticEvent} e Event
|
||||||
*/
|
*/
|
||||||
_moveTip(e) {
|
_moveTip(e) {
|
||||||
this._tooltip(Math.round(e.clientX - e.target.getBoundingClientRect().left));
|
let clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
||||||
|
this._tooltip(Math.round(clientX - e.currentTarget.getBoundingClientRect().left));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide tooltip
|
* Hide tooltip
|
||||||
|
* @param {SyntheticEvent} e Event
|
||||||
*/
|
*/
|
||||||
_hideTip() {
|
_hideTip(e) {
|
||||||
|
e.preventDefault();
|
||||||
this.tipContainer.style('display', 'none');
|
this.tipContainer.style('display', 'none');
|
||||||
this.markersContainer.style('display', 'none');
|
this.markersContainer.style('display', 'none');
|
||||||
}
|
}
|
||||||
@@ -238,8 +245,8 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
<tspan className='metric'>{` (${yUnit})`}</tspan>
|
<tspan className='metric'>{` (${yUnit})`}</tspan>
|
||||||
</text>
|
</text>
|
||||||
</g>
|
</g>
|
||||||
<g ref={(g) => this.tipContainer = d3.select(g)} className='tooltip' style={{ display: 'none' }}>
|
<g ref={(g) => this.tipContainer = d3.select(g)} style={{ display: 'none' }}>
|
||||||
<rect className='tip' style={{ height: tipHeight + 'em' }}></rect>
|
<rect className='tooltip' height={tipHeight + 'em'}></rect>
|
||||||
{detailElems}
|
{detailElems}
|
||||||
</g>
|
</g>
|
||||||
<g ref={(g) => this.markersContainer = d3.select(g)} style={{ display: 'none' }}>
|
<g ref={(g) => this.markersContainer = d3.select(g)} style={{ display: 'none' }}>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import TranslatedComponent from './TranslatedComponent';
|
|||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
import { Ships } from 'coriolis-data';
|
import { Ships } from 'coriolis-data';
|
||||||
import Ship from '../shipyard/Ship';
|
import Ship from '../shipyard/Ship';
|
||||||
import { ModuleNameToGroup } from '../shipyard/Constants';
|
import { ModuleNameToGroup, Insurance } from '../shipyard/Constants';
|
||||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||||
import { fromDetailedBuild } from '../shipyard/Serializer';
|
import { fromDetailedBuild } from '../shipyard/Serializer';
|
||||||
import { Download } from './SvgIcons';
|
import { Download } from './SvgIcons';
|
||||||
@@ -146,8 +146,14 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
if (importData.discounts instanceof Array && importData.discounts.length == 2) {
|
if (importData.discounts instanceof Array && importData.discounts.length == 2) {
|
||||||
this.setState({ discounts: importData.discounts });
|
this.setState({ discounts: importData.discounts });
|
||||||
}
|
}
|
||||||
if (typeof importData.insurance == 'string' && importData.insurance.length > 3) {
|
if (typeof importData.insurance == 'string') {
|
||||||
this.setState({ insurance: importData.insurance });
|
let insurance = importData.insurance.toLowerCase();
|
||||||
|
|
||||||
|
if (Insurance[insurance] !== undefined) {
|
||||||
|
this.setState({ insurance });
|
||||||
|
} else {
|
||||||
|
throw 'Invalid insurance type: ' + insurance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +226,7 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
|
|
||||||
if (!slot) { throw 'No hardpoint slot available for: "' + line + '"'; }
|
if (!slot) { throw 'No hardpoint slot available for: "' + line + '"'; }
|
||||||
|
|
||||||
group = ModuleNameToGroup[name.trim()];
|
group = ModuleNameToGroup[name.toLowerCase()];
|
||||||
|
|
||||||
let hp = ModuleUtils.findHardpoint(group, cl, rating, group ? null : name, mount, missile);
|
let hp = ModuleUtils.findHardpoint(group, cl, rating, group ? null : name, mount, missile);
|
||||||
|
|
||||||
@@ -238,7 +244,7 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
|
|
||||||
if (ship.standard[standardIndex].maxClass < cl) { throw name + ' exceeds max class for the ' + ship.name; }
|
if (ship.standard[standardIndex].maxClass < cl) { throw name + ' exceeds max class for the ' + ship.name; }
|
||||||
|
|
||||||
ship.use(ship.standard[standardIndex], cl + rating, ModuleUtils.standard(standardIndex, cl + rating), true);
|
ship.use(ship.standard[standardIndex], ModuleUtils.standard(standardIndex, cl + rating), true);
|
||||||
} else {
|
} else {
|
||||||
throw 'Unknown component: "' + line + '"';
|
throw 'Unknown component: "' + line + '"';
|
||||||
}
|
}
|
||||||
@@ -249,13 +255,13 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
|
|
||||||
if (!slot) { throw 'No internal slot available for: "' + line + '"'; }
|
if (!slot) { throw 'No internal slot available for: "' + line + '"'; }
|
||||||
|
|
||||||
group = ModuleNameToGroup[name.trim()];
|
group = ModuleNameToGroup[name.toLowerCase()];
|
||||||
|
|
||||||
let intComp = ModuleUtils.findInternal(group, cl, rating, group ? null : name);
|
let intComp = ModuleUtils.findInternal(group, cl, rating, group ? null : name);
|
||||||
|
|
||||||
if (!intComp) { throw 'Unknown component: "' + line + '"'; }
|
if (!intComp) { throw 'Unknown component: "' + line + '"'; }
|
||||||
|
|
||||||
ship.use(slot, intComp.id, intComp);
|
ship.use(slot, intComp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,7 +278,7 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
_validateImport(event) {
|
_validateImport(event) {
|
||||||
let importData = null;
|
let importData = null;
|
||||||
let importString = event.target.value;
|
let importString = event.target.value.trim();
|
||||||
this.setState({
|
this.setState({
|
||||||
builds: null,
|
builds: null,
|
||||||
comparisons: null,
|
comparisons: null,
|
||||||
@@ -334,7 +340,7 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.comparisons) {
|
if (this.state.comparisons) {
|
||||||
let comparisons = this.state.comparisons;
|
comparisons = this.state.comparisons;
|
||||||
for (let name in comparisons) {
|
for (let name in comparisons) {
|
||||||
comparisons[name].useName = name;
|
comparisons[name].useName = name;
|
||||||
}
|
}
|
||||||
@@ -371,8 +377,9 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.discounts) {
|
if (this.state.discounts && this.state.discounts.length == 2) {
|
||||||
Persist.setDiscount(this.state.discounts);
|
Persist.setShipDiscount(this.state.discounts[0]);
|
||||||
|
Persist.setModuleDiscount(this.state.discounts[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.insurance) {
|
if (this.state.insurance) {
|
||||||
@@ -384,11 +391,11 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Capture build name changes
|
* Capture build name changes
|
||||||
* @param {Object} build Build import object
|
* @param {Object} item Build/Comparison import object
|
||||||
* @param {SyntheticEvent} e Event
|
* @param {SyntheticEvent} e Event
|
||||||
*/
|
*/
|
||||||
_changeBuildName(build, e) {
|
_changeName(item, e) {
|
||||||
build.useName = e.target.value;
|
item.useName = e.target.value;
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,7 +422,7 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
importStage = (
|
importStage = (
|
||||||
<div>
|
<div>
|
||||||
<textarea className='cb json' onChange={this._validateImport} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
|
<textarea className='cb json' onChange={this._validateImport} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
|
||||||
<button className='l cap' onClick={this._process} disabled={!state.importValid} >{translate('proceed')}</button>
|
<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 className='l warning' style={{ marginLeft:'3em' }}>{state.errorMsg}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -426,14 +433,14 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
|
|
||||||
for (let name in state.comparisons) {
|
for (let name in state.comparisons) {
|
||||||
let comparison = state.comparisons[name];
|
let comparison = state.comparisons[name];
|
||||||
let hasComparison = Persist.hasComparison(name);
|
let hasComparison = Persist.hasComparison(comparison.useName);
|
||||||
comparisonRows.push(
|
comparisonRows.push(
|
||||||
<tr key={name} className='cb'>
|
<tr key={name} className='cb'>
|
||||||
<td>
|
<td>
|
||||||
<input type='text' value={comparison.useName}/>
|
<input type='text' onChange={this._changeName.bind(this, comparison)} value={comparison.useName}/>
|
||||||
</td>
|
</td>
|
||||||
<td style={{ textAlign:'center' }} className={ cn('cap', { warning: hasComparison, disabled: comparison.useName == '' }) }>
|
<td style={{ textAlign:'center' }} className={ cn('cap', { warning: hasComparison, disabled: comparison.useName == '' }) }>
|
||||||
{translate(comparison.useName == '' ? 'skip' : (hasComparison ? 'overwrite' : 'create'))}>
|
{translate(comparison.useName == '' ? 'skip' : (hasComparison ? 'overwrite' : 'create'))}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
@@ -467,7 +474,7 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
buildRows.push(
|
buildRows.push(
|
||||||
<tr key={shipId + buildName} className='cb'>
|
<tr key={shipId + buildName} className='cb'>
|
||||||
<td>{Ships[shipId].properties.name}</td>
|
<td>{Ships[shipId].properties.name}</td>
|
||||||
<td><input type='text' onChange={this._changeBuildName.bind(this, b)} value={b.useName}/></td>
|
<td><input type='text' onChange={this._changeName.bind(this, b)} value={b.useName}/></td>
|
||||||
<td style={{ textAlign: 'center' }} className={cn('cap', { warning: hasBuild, disabled: b.useName == '' })}>
|
<td style={{ textAlign: 'center' }} className={cn('cap', { warning: hasBuild, disabled: b.useName == '' })}>
|
||||||
{translate(b.useName == '' ? 'skip' : (hasBuild ? 'overwrite' : 'create'))}
|
{translate(b.useName == '' ? 'skip' : (hasBuild ? 'overwrite' : 'create'))}
|
||||||
</td>
|
</td>
|
||||||
@@ -491,7 +498,7 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{comparisonTable}
|
{comparisonTable}
|
||||||
<button className='cl l' onClick={this._import}><Download/> {translate('import')}</button>
|
<button id='import' className='cl l' onClick={this._import}><Download/> {translate('import')}</button>
|
||||||
{edit}
|
{edit}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -130,9 +130,9 @@ export default class PowerManagement extends TranslatedComponent {
|
|||||||
<td className='ptr le shorten cap' onClick={toggleEnabled}>{slotName(translate, slot)}</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 className='ptr' onClick={toggleEnabled}><u>{translate(slot.type)}</u></td>
|
||||||
<td>
|
<td>
|
||||||
<span className='flip ptr' onClick={this._priority.bind(this, slot, -1)}>►</span>
|
<span className='flip ptr btn' onClick={this._priority.bind(this, slot, -1)}>►</span>
|
||||||
{' ' + (slot.priority + 1) + ' '}
|
{' ' + (slot.priority + 1) + ' '}
|
||||||
<span className='ptr' onClick={this._priority.bind(this, slot, 1)}>►</span>
|
<span className='ptr btn' onClick={this._priority.bind(this, slot, 1)}>►</span>
|
||||||
</td>
|
</td>
|
||||||
<td className='ri ptr' style={{ width: '3.25em' }} onClick={toggleEnabled}>{pwr(m.power)}</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>
|
<td className='ri ptr' style={{ width: '3em' }} onClick={toggleEnabled}><u>{pct(m.power / ship.powerAvailable)}</u></td>
|
||||||
|
|||||||
@@ -32,29 +32,35 @@ export default class Slider extends React.Component {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* On Mouse down handler
|
* On Mouse down handler
|
||||||
* @param {SyntheticEvent} e Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
down(e) {
|
down(event) {
|
||||||
let rect = e.currentTarget.getBoundingClientRect();
|
if (this.move) {
|
||||||
this.move = this._updatePercent.bind(this, rect.left, rect.width);
|
this.up(event);
|
||||||
this.move(e);
|
} else {
|
||||||
document.addEventListener('mousemove', this.move);
|
let rect = event.currentTarget.getBoundingClientRect();
|
||||||
document.addEventListener('mouseup', this.up);
|
this.move = this._updatePercent.bind(this, rect.left, rect.width);
|
||||||
|
this.move(event);
|
||||||
|
document.addEventListener('mousemove', this.move, true);
|
||||||
|
document.addEventListener('mouseup', this.up, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On Mouse up handler
|
* On Mouse up handler
|
||||||
|
* @param {Event} event DOM Event
|
||||||
*/
|
*/
|
||||||
up() {
|
up(event) {
|
||||||
document.removeEventListener('mousemove', this.move);
|
document.removeEventListener('mousemove', this.move, true);
|
||||||
document.removeEventListener('mouseup', this.up);
|
document.removeEventListener('mouseup', this.up, true);
|
||||||
|
this.move = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the slider percentage
|
* Update the slider percentage
|
||||||
* @param {number} left Slider left position
|
* @param {number} left Slider left position
|
||||||
* @param {number} width Slider width
|
* @param {number} width Slider width
|
||||||
* @param {Event} event Event
|
* @param {Event} event DOM Event
|
||||||
*/
|
*/
|
||||||
_updatePercent(left, width, event) {
|
_updatePercent(left, width, event) {
|
||||||
this.props.onChange(Math.min(Math.max((event.clientX - left) / width, 0), 1));
|
this.props.onChange(Math.min(Math.max((event.clientX - left) / width, 0), 1));
|
||||||
@@ -88,7 +94,7 @@ export default class Slider extends React.Component {
|
|||||||
<rect className='primary' style={{ opacity: 0.3 }} y='0.25em' rx='0.3em' ry='0.3em' width='100%' height='0.7em' />
|
<rect className='primary' style={{ opacity: 0.3 }} y='0.25em' rx='0.3em' ry='0.3em' width='100%' height='0.7em' />
|
||||||
<rect className='primary-disabled'y='0.45em' rx='0.15em' ry='0.15em' width={pctStr} height='0.3em' />
|
<rect className='primary-disabled'y='0.45em' rx='0.15em' ry='0.15em' width={pctStr} height='0.3em' />
|
||||||
<circle className='primary' r='0.6em' cy='0.6em' cx={pctStr} />
|
<circle className='primary' r='0.6em' cy='0.6em' cx={pctStr} />
|
||||||
<rect width='100%' height='100%' fillOpacity='0' onMouseDown={this.down}/>
|
<rect width='100%' height='100%' fillOpacity='0' onMouseDown={this.down} onClick={this.click} />
|
||||||
{axisGroup}
|
{axisGroup}
|
||||||
</svg>;
|
</svg>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -328,10 +328,10 @@ export default class OutfittingPage extends Page {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='group half'>
|
<div className='group half'>
|
||||||
<table style={{ width: '100%', lineHeight: '1em' }}>
|
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||||
<tbody >
|
<tbody >
|
||||||
<tr>
|
<tr>
|
||||||
<td style={{ verticalAlign: 'top', padding:0 }}><Fuel className='xl primary-disabled' /></td>
|
<td style={{ verticalAlign: 'top', padding: 0, width: '2.5em' }}><Fuel className='xl primary-disabled' /></td>
|
||||||
<td><Slider axis={true} onChange={this._fuelChange} axisUnit={translate('T')} percent={fuelLevel} max={fuelCapacity} /></td>
|
<td><Slider axis={true} onChange={this._fuelChange} axisUnit={translate('T')} percent={fuelLevel} max={fuelCapacity} /></td>
|
||||||
<td className='primary' style={{ width: '10em', verticalAlign: 'top', fontSize: '0.9em' }}>{formats.f2(fuelLevel * fuelCapacity)}{units.T} {formats.pct1(fuelLevel)}</td>
|
<td className='primary' style={{ width: '10em', verticalAlign: 'top', fontSize: '0.9em' }}>{formats.f2(fuelLevel * fuelCapacity)}{units.T} {formats.pct1(fuelLevel)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -415,12 +415,10 @@ export default class Ship {
|
|||||||
this.updatePower()
|
this.updatePower()
|
||||||
.updateJumpStats()
|
.updateJumpStats()
|
||||||
.updateShieldStrength()
|
.updateShieldStrength()
|
||||||
.updateTopSpeed()
|
.updateTopSpeed();
|
||||||
.updatePowerPrioritesString()
|
|
||||||
.updatePowerEnabledString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this.updatePowerPrioritesString().updatePowerEnabledString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -570,12 +568,12 @@ export default class Ship {
|
|||||||
if (newPriority >= 0 && newPriority < this.priorityBands.length) {
|
if (newPriority >= 0 && newPriority < this.priorityBands.length) {
|
||||||
let oldPriority = slot.priority;
|
let oldPriority = slot.priority;
|
||||||
slot.priority = newPriority;
|
slot.priority = newPriority;
|
||||||
|
this.updatePowerPrioritesString();
|
||||||
|
|
||||||
if (slot.enabled) { // Only update power if the slot is enabled
|
if (slot.enabled) { // Only update power if the slot is enabled
|
||||||
let usage = powerUsageType(slot, slot.m);
|
let usage = powerUsageType(slot, slot.m);
|
||||||
this.priorityBands[oldPriority][usage] -= slot.m.power;
|
this.priorityBands[oldPriority][usage] -= slot.m.power;
|
||||||
this.priorityBands[newPriority][usage] += slot.m.power;
|
this.priorityBands[newPriority][usage] += slot.m.power;
|
||||||
this.updatePowerPrioritesString();
|
|
||||||
this.updatePower();
|
this.updatePower();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ let LS;
|
|||||||
|
|
||||||
// Safe check to determine if localStorage is enabled
|
// Safe check to determine if localStorage is enabled
|
||||||
try {
|
try {
|
||||||
localStorage.setItem('s', 1);
|
localStorage.setItem('test_string', 1);
|
||||||
localStorage.removeItem('s');
|
localStorage.removeItem('test_string');
|
||||||
LS = localStorage;
|
LS = localStorage;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
LS = null;
|
LS = null;
|
||||||
@@ -38,7 +38,7 @@ function _put(key, value) {
|
|||||||
* @return {string} The stored string
|
* @return {string} The stored string
|
||||||
*/
|
*/
|
||||||
function _getString(key) {
|
function _getString(key) {
|
||||||
return LS.getItem(key);
|
return LS ? LS.getItem(key) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,7 +80,7 @@ class Persist extends EventEmitter {
|
|||||||
this.comparisons = comparisonJson ? comparisonJson : {};
|
this.comparisons = comparisonJson ? comparisonJson : {};
|
||||||
this.buildCount = Object.keys(this.builds).length;
|
this.buildCount = Object.keys(this.builds).length;
|
||||||
this.langCode = _getString(LS_KEY_LANG) || 'en';
|
this.langCode = _getString(LS_KEY_LANG) || 'en';
|
||||||
this.insurance = _getString(LS_KEY_INSURANCE);
|
this.insurance = _getString(LS_KEY_INSURANCE) || 'standard';
|
||||||
this.discounts = _get(LS_KEY_DISCOUNTS) || [1, 1];
|
this.discounts = _get(LS_KEY_DISCOUNTS) || [1, 1];
|
||||||
this.costTab = _getString(LS_KEY_COST_TAB);
|
this.costTab = _getString(LS_KEY_COST_TAB);
|
||||||
this.state = _get(LS_KEY_STATE);
|
this.state = _get(LS_KEY_STATE);
|
||||||
@@ -129,16 +129,13 @@ class Persist extends EventEmitter {
|
|||||||
* @param {string} code The serialized code
|
* @param {string} code The serialized code
|
||||||
*/
|
*/
|
||||||
saveBuild(shipId, name, code) {
|
saveBuild(shipId, name, code) {
|
||||||
if (LS) {
|
if (!this.builds[shipId]) {
|
||||||
if (!this.builds[shipId]) {
|
this.builds[shipId] = {};
|
||||||
this.builds[shipId] = {};
|
|
||||||
}
|
|
||||||
let newBuild = !this.builds[shipId][name];
|
|
||||||
this.builds[shipId][name] = code;
|
|
||||||
_put(LS_KEY_BUILDS, this.builds);
|
|
||||||
this.emit('buildSaved', shipId, name, code);
|
|
||||||
}
|
}
|
||||||
};
|
this.builds[shipId][name] = code;
|
||||||
|
_put(LS_KEY_BUILDS, this.builds);
|
||||||
|
this.emit('buildSaved', shipId, name, code);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the serialized code/string for a build. Returns null if a
|
* Get the serialized code/string for a build. Returns null if a
|
||||||
@@ -153,7 +150,7 @@ class Persist extends EventEmitter {
|
|||||||
return this.builds[shipId][name];
|
return this.builds[shipId][name];
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all builds (object) or builds for a specific ship (array)
|
* Get all builds (object) or builds for a specific ship (array)
|
||||||
@@ -225,7 +222,7 @@ class Persist extends EventEmitter {
|
|||||||
_put(LS_KEY_COMPARISONS, this.comparisons);
|
_put(LS_KEY_COMPARISONS, this.comparisons);
|
||||||
this.emit('buildDeleted', shipId, name);
|
this.emit('buildDeleted', shipId, name);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persist a comparison in localstorage.
|
* Persist a comparison in localstorage.
|
||||||
@@ -244,7 +241,7 @@ class Persist extends EventEmitter {
|
|||||||
};
|
};
|
||||||
_put(LS_KEY_COMPARISONS, this.comparisons);
|
_put(LS_KEY_COMPARISONS, this.comparisons);
|
||||||
this.emit('comparisons', this.comparisons);
|
this.emit('comparisons', this.comparisons);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [getComparison description]
|
* [getComparison description]
|
||||||
@@ -256,7 +253,7 @@ class Persist extends EventEmitter {
|
|||||||
return this.comparisons[name];
|
return this.comparisons[name];
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all saved comparisons
|
* Get all saved comparisons
|
||||||
@@ -293,7 +290,7 @@ class Persist extends EventEmitter {
|
|||||||
_put(LS_KEY_COMPARISONS, this.comparisons);
|
_put(LS_KEY_COMPARISONS, this.comparisons);
|
||||||
this.emit('comparisons', this.comparisons);
|
this.emit('comparisons', this.comparisons);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete all builds and comparisons from localStorage
|
* Delete all builds and comparisons from localStorage
|
||||||
@@ -304,7 +301,7 @@ class Persist extends EventEmitter {
|
|||||||
_delete(LS_KEY_BUILDS);
|
_delete(LS_KEY_BUILDS);
|
||||||
_delete(LS_KEY_COMPARISONS);
|
_delete(LS_KEY_COMPARISONS);
|
||||||
this.emit('deletedAll');
|
this.emit('deletedAll');
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all saved data and settings
|
* Get all saved data and settings
|
||||||
@@ -317,25 +314,25 @@ class Persist extends EventEmitter {
|
|||||||
data[LS_KEY_INSURANCE] = this.getInsurance();
|
data[LS_KEY_INSURANCE] = this.getInsurance();
|
||||||
data[LS_KEY_DISCOUNTS] = this.discounts;
|
data[LS_KEY_DISCOUNTS] = this.discounts;
|
||||||
return data;
|
return data;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the saved insurance type
|
* 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() {
|
getInsurance() {
|
||||||
return this.insurance;
|
return this.insurance.toLowerCase();
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persist selected insurance type
|
* Persist selected insurance type
|
||||||
* @param {string} insurance Insurance type name
|
* @param {string} insurance Insurance type name
|
||||||
*/
|
*/
|
||||||
setInsurance(insurance) {
|
setInsurance(insurance) {
|
||||||
this.insurance = insurance;
|
this.insurance = insurance.toLowerCase();
|
||||||
_put(LS_KEY_INSURANCE, insurance);
|
_put(LS_KEY_INSURANCE, insurance);
|
||||||
this.emit('insurance', insurance);
|
this.emit('insurance', insurance);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persist selected ship discount
|
* Persist selected ship discount
|
||||||
@@ -345,7 +342,7 @@ class Persist extends EventEmitter {
|
|||||||
this.discounts[0] = shipDiscount;
|
this.discounts[0] = shipDiscount;
|
||||||
_put(LS_KEY_DISCOUNTS, this.discounts);
|
_put(LS_KEY_DISCOUNTS, this.discounts);
|
||||||
this.emit('discounts', this.discounts);
|
this.emit('discounts', this.discounts);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the saved ship discount
|
* Get the saved ship discount
|
||||||
@@ -363,7 +360,7 @@ class Persist extends EventEmitter {
|
|||||||
this.discounts[1] = moduleDiscount;
|
this.discounts[1] = moduleDiscount;
|
||||||
_put(LS_KEY_DISCOUNTS, this.discounts);
|
_put(LS_KEY_DISCOUNTS, this.discounts);
|
||||||
this.emit('discounts', this.discounts);
|
this.emit('discounts', this.discounts);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the saved ship discount
|
* Get the saved ship discount
|
||||||
@@ -371,7 +368,7 @@ class Persist extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
getModuleDiscount() {
|
getModuleDiscount() {
|
||||||
return this.discounts[1];
|
return this.discounts[1];
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persist selected cost tab
|
* Persist selected cost tab
|
||||||
@@ -380,7 +377,7 @@ class Persist extends EventEmitter {
|
|||||||
setCostTab(tabName) {
|
setCostTab(tabName) {
|
||||||
this.costTab = tabName;
|
this.costTab = tabName;
|
||||||
_put(LS_KEY_COST_TAB, tabName);
|
_put(LS_KEY_COST_TAB, tabName);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the saved discount
|
* Get the saved discount
|
||||||
@@ -388,7 +385,7 @@ class Persist extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
getCostTab() {
|
getCostTab() {
|
||||||
return this.costTab;
|
return this.costTab;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the last router state from local storage
|
* Retrieve the last router state from local storage
|
||||||
@@ -396,7 +393,7 @@ class Persist extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
getState() {
|
getState() {
|
||||||
return this.state;
|
return this.state;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the current router state to localstorage
|
* Save the current router state to localstorage
|
||||||
@@ -405,7 +402,7 @@ class Persist extends EventEmitter {
|
|||||||
setState(state) {
|
setState(state) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
_put(LS_KEY_STATE, state);
|
_put(LS_KEY_STATE, state);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the last router state from local storage
|
* Retrieve the last router state from local storage
|
||||||
@@ -413,7 +410,7 @@ class Persist extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
getSizeRatio() {
|
getSizeRatio() {
|
||||||
return this.sizeRatio;
|
return this.sizeRatio;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the current size ratio to localstorage
|
* Save the current size ratio to localstorage
|
||||||
@@ -425,7 +422,7 @@ class Persist extends EventEmitter {
|
|||||||
_put(LS_KEY_SIZE_RATIO, sizeRatio);
|
_put(LS_KEY_SIZE_RATIO, sizeRatio);
|
||||||
this.emit('sizeRatio', sizeRatio);
|
this.emit('sizeRatio', sizeRatio);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if localStorage is enabled/active
|
* Check if localStorage is enabled/active
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<html {%= o.htmlWebpackPlugin.options.appCache ? 'manifest=/' + o.htmlWebpackPlugin.options.appCache : '' %} >
|
<html {%= o.htmlWebpackPlugin.options.appCache ? 'manifest=/' + o.htmlWebpackPlugin.options.appCache : '' %} >
|
||||||
<head>
|
<head>
|
||||||
<title>Coriolis</title>
|
<title>Coriolis</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
<link rel="stylesheet" href="{%= o.htmlWebpackPlugin.files.css[0] %}">
|
<link rel="stylesheet" href="{%= o.htmlWebpackPlugin.files.css[0] %}">
|
||||||
<!-- Standard headers -->
|
<!-- Standard headers -->
|
||||||
<meta name="description" content="A ship builder, outfitting and comparison tool for Elite Dangerous">
|
<meta name="description" content="A ship builder, outfitting and comparison tool for Elite Dangerous">
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ svg {
|
|||||||
fill: @fg;
|
fill: @fg;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tip {
|
.tooltip {
|
||||||
fill: @bgBlack;
|
fill: @bgBlack;
|
||||||
stroke: @secondary;
|
stroke: @secondary;
|
||||||
stroke-width: 1px;
|
stroke-width: 1px;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
transform:translate(-50%,-50%);
|
transform:translate(-50%,-50%);
|
||||||
-webkit-transform:translate(-50%,-50%);
|
-webkit-transform:translate(-50%,-50%);
|
||||||
width: 800px;
|
width: 800px;
|
||||||
max-height: 100%;
|
max-height: 90%;
|
||||||
padding: 2em;
|
padding: 2em;
|
||||||
background-color: @bgBlack;
|
background-color: @bgBlack;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|||||||
@@ -235,13 +235,9 @@
|
|||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
table tbody tr td {
|
span.btn {
|
||||||
&:nth-child(4) {
|
vertical-align: middle;
|
||||||
span {
|
font-size: 1.6em;
|
||||||
vertical-align: middle;
|
|
||||||
font-size: 1.6em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user