Massive refactor for 3rd party import

This commit is contained in:
Colin McLeod
2015-07-19 21:32:14 -07:00
parent c796adf40d
commit 03986cb88a
50 changed files with 4278 additions and 156 deletions

View File

@@ -1,88 +1,110 @@
angular.module('app').controller('ImportController', ['$scope', '$stateParams', 'ShipsDB', 'Ship', 'Persist', 'Serializer', function($scope, $stateParams, Ships, Ship, Persist, Serializer) {
angular.module('app').controller('ImportController', ['lodash', '$rootScope', '$scope', '$stateParams', 'ShipsDB', 'Ship', 'Persist', 'Serializer', function(_, $rootScope, $scope, $stateParams, Ships, Ship, Persist, Serializer) {
$scope.jsonValid = false;
$scope.importData = null;
$scope.importJSON = null;
$scope.errorMsg = null;
$scope.canEdit = true;
$scope.builds = $stateParams.obj || null;
$scope.ships = Ships;
$scope.validateJson = function() {
var importObj = null, shipData = null;
$scope.jsonValid = false;
$scope.errorMsg = null;
$scope.builds = null;
function validateBuild(shipId, code, name) {
var shipData = Ships[shipId];
if (!$scope.importData) { return; }
if (!shipData) {
throw '"' + shipId + '" is not a valid Ship Id!';
}
if (typeof name != 'string' || name.length < 3) {
throw shipData.properties.name + ' build "' + name + '" must be a string at least 3 characters long!';
}
if (typeof code != 'string' || code.length < 10) {
throw shipData.properties.name + ' build "' + name + '" is not valid!';
}
try {
Serializer.toShip(new Ship(shipId, shipData.properties, shipData.slots), code);
} catch (e) {
throw shipData.properties.name + ' build "' + name + '" is not valid!';
}
}
function detailedJsonToBuild(detailedBuild) {
var ship;
if (!detailedBuild.name) {
throw 'Build Name missing!';
}
try {
importObj = angular.fromJson($scope.importData);
ship = Serializer.fromDetailedBuild(detailedBuild);
} catch (e) {
throw detailedBuild.ship + ' Build "' + detailedBuild.name + '": Invalid data';
}
return { shipId: ship.id, name: detailedBuild.name, code: Serializer.fromShip(ship) };
}
function importBackup(importData) {
if (importData.builds && typeof importData.builds == 'object') {
for (var shipId in importData.builds) {
for (var buildName in importData.builds[shipId]) {
validateBuild(shipId, importData.builds[shipId][buildName], buildName);
}
}
$scope.builds = importData.builds;
} else {
throw 'builds must be an object!';
}
if (importData.comparisons) {
// TODO: check ship/builds exist for comparison
$scope.comparisons = importData.comparisons;
}
if (importData.discounts instanceof Array && importData.discounts.length == 2) {
$scope.discounts = importData.discounts;
}
if (typeof importData.insurance == 'string' && importData.insurance.length > 3) {
$scope.insurance = importData.insurance;
}
}
function importDetailedArray(importArr) {
var builds = {};
for (var i = 0, l = importArr.length; i < l; i++) {
var build = detailedJsonToBuild(importArr[i]);
if (!builds[build.shipId]) {
builds[build.shipId] = {};
}
builds[build.shipId][build.name] = build.code;
}
$scope.builds = builds;
}
$scope.validateJson = function() {
var importData = null;
$scope.jsonValid = false;
$scope.errorMsg = null;
$scope.builds = $scope.discounts = $scope.comparisons = $scope.insurance = null;
if (!$scope.importJSON) { return; }
try {
importData = angular.fromJson($scope.importJSON);
} catch (e) {
$scope.errorMsg = 'Cannot Parse JSON!';
return;
}
if (typeof importObj != 'object') {
if (!importData || typeof importData != 'object') {
$scope.errorMsg = 'Must be an object or array!';
return;
}
// Using JSON from a simple/shortform/standard export
if (importObj.builds && Object.keys(importObj.builds).length) {
for (var shipId in importObj.builds) {
shipData = Ships[shipId];
if (shipData) {
for (var buildName in importObj.builds[shipId]) {
if (typeof importObj.builds[shipId][buildName] != 'string') {
$scope.errorMsg = shipData.properties.name + ' build "' + buildName + '" must be a string!';
return;
}
try {
// Actually build the ship with the code to ensure it's valid
Serializer.toShip(new Ship(shipId, shipData.properties, shipData.slots), importObj.builds[shipId][buildName]);
} catch (e) {
$scope.errorMsg = shipData.properties.name + ' build "' + buildName + '" is not valid!';
return;
}
}
} else {
$scope.errorMsg = '"' + shipId + '"" is not a valid Ship Id!';
return;
}
$scope.builds = importObj.builds;
try {
if (importData instanceof Array) { // Must be detailed export json
importDetailedArray(importData);
} else if (importData.ship && importData.name) { // Using JSON from a single ship build export
importDetailedArray([importData]); // Convert to array with singleobject
} else { // Using Backup JSON
importBackup(importData);
}
// Using JSON from a detailed export
} else if (importObj.length && importObj[0].references && importObj[0].references.length) {
var builds = {};
for (var i = 0, l = importObj.length; i < l; i++) {
if (typeof importObj[i].name != 'string' || typeof importObj[i].ship != 'string') {
$scope.errorMsg = 'Build [' + i + '] must have a ship and build name!';
return;
}
for (var r = 0, rl = importObj[i].references.length; r < rl; r++) {
var ref = importObj[i].references[r];
if (ref.name == 'Coriolis.io' && ref.code && ref.shipId) {
if (!builds[ref.shipId]) {
builds[ref.shipId] = {};
}
try {
// Actually build the ship with the code to ensure it's valid
shipData = Ships[ref.shipId];
Serializer.toShip(new Ship(ref.shipId, shipData.properties, shipData.slots), ref.code);
} catch (e) {
$scope.errorMsg = importObj[i].ship + ' build "' + importObj[i].name + '" is not valid!';
return;
}
builds[ref.shipId][importObj[i].name] = ref.code;
} else {
$scope.errorMsg = importObj[i].ship + ' build "' + importObj[i].name + '" has an invalid Coriolis reference!';
return;
}
}
}
$scope.builds = builds;
} else {
$scope.errorMsg = 'No builds in data';
} catch (e) {
$scope.errorMsg = e;
return;
}
@@ -93,32 +115,73 @@ angular.module('app').controller('ImportController', ['$scope', '$stateParams',
return Persist.getBuild(shipId, name) !== null;
};
$scope.hasComparison = function(name) {
return Persist.getComparison(name) !== null;
};
$scope.process = function() {
var builds = $scope.builds;
for (var shipId in builds) {
for (var buildName in builds[shipId]) {
var code = builds[shipId][buildName];
// Update builds object such that orginal name retained, but can be renamed
builds[shipId][buildName] = {
code: code,
useName: buildName
};
if ($scope.builds) {
var builds = $scope.builds;
for (var shipId in builds) {
for (var buildName in builds[shipId]) {
var code = builds[shipId][buildName];
// Update builds object such that orginal name retained, but can be renamed
builds[shipId][buildName] = {
code: code,
useName: buildName
};
}
}
}
if ($scope.comparisons) {
var comparisons = $scope.comparisons;
for (var name in comparisons) {
comparisons[name].useName = name;
}
}
$scope.processed = true;
};
$scope.import = function() {
var builds = $scope.builds;
for (var shipId in builds) {
for (var buildName in builds[shipId]) {
var build = builds[shipId][buildName];
var name = build.useName.trim();
if (name) {
Persist.saveBuild(shipId, name, build.code);
if ($scope.builds) {
var builds = $scope.builds;
for (var shipId in builds) {
for (var buildName in builds[shipId]) {
var build = builds[shipId][buildName];
var name = build.useName.trim();
if (name) {
Persist.saveBuild(shipId, name, build.code);
}
}
}
}
if ($scope.comparisons) {
var comparisons = $scope.comparisons;
for (var comp in comparisons) {
var comparison = comparisons[comp];
var useName = comparison.useName.trim();
if (useName) {
Persist.saveComparison(useName, comparison.builds, comparison.facets);
}
}
}
if ($scope.discounts) {
$rootScope.discounts.ship = $scope.discounts[0];
$rootScope.discounts.components = $scope.discounts[1];
$rootScope.$broadcast('discountChange');
Persist.setDiscount($scope.discounts);
}
if ($scope.insurance) {
$rootScope.insurance.current = $scope.insurance;
Persist.setInsurance($scope.insurance);
}
$scope.$parent.dismiss();
};

View File

@@ -219,6 +219,8 @@ angular.module('app').controller('OutfitController', ['$window', '$rootScope', '
if ($scope.buildName) {
$state.go('modal.export', {
title: $scope.buildName + ' Export',
description: 'A detailed JSON export of your build for use in other sites and tools',
data: Serializer.toDetailedBuild($scope.buildName, ship, $scope.code || Serializer.fromShip(ship))
});
}

View File

@@ -33,11 +33,26 @@ angular.module('app').directive('shipyardHeader', ['lodash', '$rootScope', '$sta
$rootScope.$broadcast('discountChange');
};
scope.backup = function(e) {
e.preventDefault();
e.stopPropagation();
scope.openedMenu = null;
$state.go('modal.export', {
title: 'Backup',
data: Persist.getAll(),
description: 'Backup of all Coriolis data to save or transfer to another browser/device'
});
};
scope.detailedExport = function(e) {
e.preventDefault();
e.stopPropagation();
scope.openedMenu = null;
$state.go('modal.export', { data: Serializer.toDetailedExport(scope.allBuilds) });
$state.go('modal.export', {
title: 'Detailed Export',
data: Serializer.toDetailedExport(scope.allBuilds),
description: 'Detailed export of all builds for use with other tools and sites'
});
};
scope.openMenu = function(e, menu) {

View File

@@ -120,7 +120,7 @@ angular.module('app').service('Persist', ['$window', 'lodash', function($window,
}
this.comparisons[name] = {
facets: facets,
builds: _.map(builds, function(b) { return { shipId: b.id, buildName: b.buildName }; })
builds: _.map(builds, function(b) { return { shipId: b.id || b.shipId, buildName: b.buildName }; })
};
localStorage.setItem(LS_KEY_COMPARISONS, angular.toJson(this.comparisons));
this.state.hasComparisons = true;

View File

@@ -1,7 +1,7 @@
/**
* Service managing seralization and deserialization of models for use in URLs and persistene.
*/
angular.module('app').service('Serializer', ['lodash', 'GroupMap', 'MountMap', 'ShipsDB', 'Ship', '$state', function(_, GroupMap, MountMap, ShipsDB, Ship, $state) {
angular.module('app').service('Serializer', ['lodash', 'GroupMap', 'MountMap', 'ShipsDB', 'Ship', 'Components', '$state', function(_, GroupMap, MountMap, ShipsDB, Ship, Components, $state) {
/**
* Serializes the ships selected components for all slots to a URL friendly string.
@@ -108,6 +108,46 @@ angular.module('app').service('Serializer', ['lodash', 'GroupMap', 'MountMap', '
return data;
};
this.fromDetailedBuild = function(detailedBuild) {
var shipId = _.findKey(ShipsDB, { properties: { name: detailedBuild.ship } });
if (!shipId) {
throw 'No such ship: ' + detailedBuild.ship;
}
var comps = detailedBuild.components;
var shipData = ShipsDB[shipId];
var ship = new Ship(shipId, shipData.properties, shipData.slots);
var bulkheads = Components.bulkheadIndex(comps.standard.bulkheads);
if (bulkheads < 0) {
throw 'Invalid bulkheads: ' + comps.standard.bulkheads;
}
var common = _.map(
['powerPlant', 'thrusters', 'frameShiftDrive', 'lifeSupport', 'powerDistributor', 'sensors', 'fuelTank'],
function(c) {
if (!comps.standard[c].class || !comps.standard[c].rating) {
throw 'Invalid value for ' + c;
}
return comps.standard[c].class + comps.standard[c].rating;
}
);
var internal = _.map(comps.internal, function(c) { return c ? Components.findInternalId(c.group, c.class, c.rating, c.name) : 0; });
var hardpoints = _.map(comps.hardpoints, function(c) {
return c ? Components.findHardpointId(c.group, c.class, c.rating, c.name, MountMap[c.mount], c.missile) : 0;
});
hardpoints = hardpoints.concat(_.map(comps.utility, function(c) {
return c ? Components.findHardpointId(c.group, c.class, c.rating, c.name, MountMap[c.mount]) : 0;
}));
ship.buildWith({ bulkheads: bulkheads, common: common, hardpoints: hardpoints, internal: internal });
return ship;
};
this.toDetailedExport = function(builds) {
var data = [];
@@ -180,6 +220,9 @@ angular.module('app').service('Serializer', ['lodash', 'GroupMap', 'MountMap', '
if (slot.c.mode) {
o.mount = MountMap[slot.c.mode];
}
if (slot.c.missile) {
o.missile = slot.c.missile;
}
return o;
}
return null;

View File

@@ -32,7 +32,7 @@ angular.module('shipyard', ['ngLodash'])
// Internal
fs: 'Fuel Scoop',
sc: 'Scanners',
sc: 'Scanner',
am: 'Auto Field-Maintenance Unit',
cr: 'Cargo Rack',
fi: 'FSD Interdictor',
@@ -69,7 +69,10 @@ angular.module('shipyard', ['ngLodash'])
.value('MountMap', {
'F': 'Fixed',
'G': 'Gimballed',
'T': 'Turret'
'T': 'Turret',
'Fixed': 'F',
'Gimballed': 'G',
'Turret': 'T'
})
.value('shipSize', [
'N/A',

View File

@@ -32,6 +32,41 @@ angular.module('shipyard').service('Components', ['lodash', 'ComponentsDB', 'Shi
return null;
};
this.findInternalId = function(groupName, clss, rating, name) {
var group = C.internal[groupName];
if (!group) {
throw 'Invalid internal group: ' + groupName;
}
for (var i = 0, l = group.length; i < l; i++) {
if (group[i].class == clss && group[i].rating == rating && ((!name && !group[i].name) || group[i].name == name)) {
return group[i].id;
}
}
return 0;
};
this.findHardpointId = function(groupName, clss, rating, name, mode, missile) {
var group = C.hardpoints[groupName];
if (!group) {
throw 'Invalid hardpoint group: ' + groupName;
}
for (var i = 0, l = group.length; i < l; i++) {
if (group[i].class == clss && group[i].rating == rating && group[i].mode == mode
&& ((!name && !group[i].name) || group[i].name == name)
&& ((!missile && !group[i].missile) || group[i].missile == missile)
) {
return group[i].id;
}
}
return 0;
};
/**
* Looks up the bulkhead component for a specific ship and bulkhead
* @param {string} shipId Unique ship Id/Key
@@ -42,6 +77,10 @@ angular.module('shipyard').service('Components', ['lodash', 'ComponentsDB', 'Shi
return C.bulkheads[shipId][bulkheadsId];
};
this.bulkheadIndex = function(bulkheadName) {
return ['Lightweight Alloy', 'Reinforced Alloy', 'Military Grade Composite', 'Mirrored Surface Composite', 'Reactive Surface Composite'].indexOf(bulkheadName);
};
/**
* Creates a new ComponentSet that contains all available components
* that the specified ship is eligible to use.

View File

@@ -63,8 +63,8 @@
<hr />
<ul>
Builds & Comparisons
<li><a href="#" class="block" ui-sref="modal.export({data: {builds: allBuilds}})">Export</a></li>
<li><a href="#" class="block" ng-click="detailedExport($event)">3rd Party Export</a></li>
<li><a href="#" class="block" ng-click="backup($event)">Backup</a></li>
<li><a href="#" class="block" ng-click="detailedExport($event)">Detailed Export</a></li>
<li><a href="#" class="block" ui-sref="modal.import">Import</a></li>
<li><a href="#" class="block" ui-sref="modal.delete">Delete All</a></li>
</ul>

View File

@@ -1,24 +1,38 @@
<h2>Import</h2>
<div ng-show="!processed">
<textarea class="cb json" ng-model="importData" ng-change="validateJson()" placeholder="Paste JSON Here"></textarea>
<textarea class="cb json" ng-model="importJSON" ng-change="validateJson()" placeholder="Paste JSON Here"></textarea>
<button class="l" ng-click="process()" ng-disabled="!jsonValid">Proceed</button>
<div class="l warning" style="margin-left:3em;">{{errorMsg}}</div>
</div>
<div ng-show="processed">
<table class="l" style="overflow:hidden;margin: 1em 0; width: 100%;">
<thead><tr><th>Ship</th><th>Build Name</th><th>Action</th></tr></thead>
<thead><tr><th style="text-align:left">Ship</th><th style="text-align:left">Build Name</th><th>Action</th></tr></thead>
<tbody ng-repeat="(shipId,shipBuilds) in builds">
<tr class="cb" ng-repeat="(buildName, b) in shipBuilds">
<td>{{ships[shipId].properties.name}}</td>
<td><input type="text" ng-model="b.useName"/></td>
<td ng-class="{warning: hasBuild(shipId, b.useName) == true, disabled: b.useName == ''}">
<td style="text-align:center" ng-class="{warning: hasBuild(shipId, b.useName) == true, disabled: b.useName == ''}">
<span ng-show="b.useName">{{ hasBuild(shipId, b.useName)? 'Overwrite' : 'Create' }}</span>
<span ng-show="b.useName == ''">Skip</span>
</td>
</tr>
</tbody>
</table>
<table class="l" style="overflow:hidden;margin: 1em 0; width: 100%;" ng-if="comparisons">
<thead><tr><th style="text-align:left">Comparison</th><th>Action</th></tr></thead>
<tbody>
<tr class="cb" ng-repeat="(name, comparison) in comparisons">
<td><input type="text" ng-model="comparison.useName"/></td>
<td style="text-align:center" ng-class="{warning: hasComparison(comparison.useName) == true, disabled: comparison.useName == ''}">
<span ng-show="comparison.useName">{{ hasComparison(comparison.useName)? 'Overwrite' : 'Create' }}</span>
<span ng-show="comparison.useName == ''">Skip</span>
</td>
</tr>
</tbody>
</table>
<button class="cl l" ng-click="import()"><svg class="icon"><use xlink:href="#download"></use></svg> Import</button>
<button class="l" style="margin-left: 2em;" ng-click="processed = false" ng-show="canEdit">Edit JSON</button>
</div>

View File

@@ -1,5 +1,5 @@
{
"Beam Lasers" : [
"Beam Laser": [
{
"id": "0u",
"grp": "bl",

View File

@@ -1,5 +1,5 @@
{
"Burst Lasers": [
"Burst Laser": [
{
"id": "14",
"grp": "ul",

View File

@@ -1,5 +1,5 @@
{
"Cannons": [
"Cannon": [
{
"id": "1q",
"grp": "c",

View File

@@ -1,5 +1,5 @@
{
"Cargo Scanners": [
"Cargo Scanner": [
{
"id": "0d",
"grp": "cs",

View File

@@ -1,5 +1,5 @@
{
"Countermeasures": [
"Countermeasure": [
{
"id": "00",
"grp": "cm",

View File

@@ -1,5 +1,5 @@
{
"Fragment Cannons": [
"Fragment Cannon": [
{
"id": "1t",
"grp": "fc",

View File

@@ -1,5 +1,5 @@
{
"Frame Shift Wake Scanners": [
"Frame Shift Wake Scanner": [
{
"id": "0i",
"grp": "ws",

View File

@@ -1,5 +1,5 @@
{
"Kill Warrant Scanners": [
"Kill Warrant Scanner": [
{
"id": "0n",
"grp": "kw",

View File

@@ -1,5 +1,5 @@
{
"Mine Launchers": [
"Mine Launcher": [
{
"id": "2j",
"grp": "nl",

View File

@@ -1,5 +1,5 @@
{
"Mining Lasers": [
"Mining Laser": [
{
"id": "2l",
"grp": "ml",

View File

@@ -1,5 +1,5 @@
{
"Missile Racks": [
"Missile Rack": [
{
"id": "2f",
"grp": "mr",

View File

@@ -1,5 +1,5 @@
{
"Multi-cannons": [
"Multi-cannon": [
{
"id": "26",
"grp": "mc",

View File

@@ -1,5 +1,5 @@
{
"Plasma Accelerators": [
"Plasma Accelerator": [
{
"id": "1g",
"grp": "pa",

View File

@@ -1,5 +1,5 @@
{
"Pulse Lasers": [
"Pulse Laser": [
{
"id": "1d",
"grp": "pl",

View File

@@ -1,5 +1,5 @@
{
"Rail Guns": [
"Rail Gun": [
{
"id": "29",
"grp": "rg",

View File

@@ -1,5 +1,5 @@
{
"Shield Boosters": [
"Shield Booster": [
{
"id": "08",
"grp": "sb",

View File

@@ -1,5 +1,5 @@
{
"Torpedo Pylons": [
"Torpedo Pylon": [
{
"id": "2h",
"grp": "tp",

View File

@@ -1,5 +1,5 @@
{
"Auto Field-Maintenance Units": [
"Auto Field-Maintenance Unit": [
{
"id": "1f",
"grp": "am",

View File

@@ -1,5 +1,5 @@
{
"Cargo Racks": [
"Cargo Rack": [
{ "id": "00", "grp": "cr", "class": 1, "rating": "E", "cost": 1000, "capacity": 2 },
{ "id": "01", "grp": "cr", "class": 2, "rating": "E", "cost": 3250, "capacity": 4 },
{ "id": "02", "grp": "cr", "class": 3, "rating": "E", "cost": 10563, "capacity": 8 },

View File

@@ -1,5 +1,5 @@
{
"Collector Limpet Controllers": [
"Collector Limpet Controller": [
{ "id": "Cf", "grp":"cc", "class":7, "rating":"E", "cost": 437400, "mass": 32.0, "power":0.41, "range":1.36, "maximum": 4, "time":300 },
{ "id": "Cg", "grp":"cc", "class":7, "rating":"D", "cost": 874800, "mass": 32.0, "power":0.55, "range":1.02, "maximum": 4, "time":600 },
{ "id": "Ch", "grp":"cc", "class":7, "rating":"C", "cost":1749600, "mass": 80.0, "power":0.69, "range":1.70, "maximum": 4, "time":510 },

View File

@@ -1,5 +1,5 @@
{
"Docking Computers": [
"Docking Computer": [
{
"id": "24",
"grp": "dc",

View File

@@ -1,5 +1,5 @@
{
"FSD Interdictors": [
"FSD Interdictor": [
{
"id": "6p",
"grp": "fi",

View File

@@ -1,5 +1,5 @@
{
"Fuel Scoops": [
"Fuel Scoop": [
{
"id": "3q",
"grp": "fs",

View File

@@ -1,5 +1,5 @@
{
"Fuel Transfer Limpet Controllers": [
"Fuel Transfer Limpet Controller": [
{ "id": "Ff", "grp":"fx", "class":7, "rating":"E", "cost": 437400, "mass": 80.0, "power":0.55, "range":1.02, "maximum": 8 },
{ "id": "Fg", "grp":"fx", "class":7, "rating":"D", "cost": 874800, "mass": 32.0, "power":0.41, "range":1.36, "maximum": 8 },
{ "id": "Fh", "grp":"fx", "class":7, "rating":"C", "cost":1749600, "mass": 80.0, "power":0.69, "range":1.70, "maximum": 8 },

View File

@@ -1,5 +1,5 @@
{
"Hatch Breaker Limpet Controllers": [
"Hatch Breaker Limpet Controller": [
{
"id": "7d",
"grp": "hb",

View File

@@ -1,5 +1,5 @@
{
"Hull Reinforcement Packages": [
"Hull Reinforcement Package": [
{
"id": "2e",
"grp": "hr",

View File

@@ -1,5 +1,5 @@
{
"Prismatic Shield Generators": [
"Prismatic Shield Generator": [
{ "id": "p6", "grp": "psg", "class": 1, "rating": "A", "cost": 132195, "mass": 2.5, "power": 2.52, "minmass": 13, "optmass": 25, "maxmass": 63, "minmul": 2.04, "optmul": 1.44, "maxmul": 0.84 },
{ "id": "p5", "grp": "psg", "class": 2, "rating": "A", "cost": 240336, "mass": 5, "power": 3.15, "minmass": 23, "optmass": 55, "maxmass": 138, "minmul": 2.04, "optmul": 1.44, "maxmul": 0.84 },
{ "id": "p4", "grp": "psg", "class": 3, "rating": "A", "cost": 761868, "mass": 10, "power": 3.78, "minmass": 83, "optmass": 165, "maxmass": 413, "minmul": 2.04, "optmul": 1.44, "maxmul": 0.84 },

View File

@@ -1,5 +1,5 @@
{
"Prospector Limpet Controllers": [
"Prospector Limpet Controller": [
{ "id": "Pf", "grp":"pc", "class":7, "rating":"E", "cost": 437400, "mass": 80.0, "power":0.55, "range": 5.10, "maximum": 8 },
{ "id": "Pg", "grp":"pc", "class":7, "rating":"D", "cost": 874800, "mass": 32.0, "power":0.41, "range": 6.80, "maximum": 8 },
{ "id": "Ph", "grp":"pc", "class":7, "rating":"C", "cost":1749600, "mass": 80.0, "power":0.69, "range": 8.50, "maximum": 8 },

View File

@@ -1,5 +1,5 @@
{
"Refineries": [
"Refinery": [
{
"id": "23",
"grp": "rf",

View File

@@ -1,9 +1,9 @@
{
"Scanners": [
"Scanner": [
{
"id": "2f",
"grp": "sc",
"name": "Adv. Discovery Scanner",
"name": "Advanced Discovery Scanner",
"class": 1,
"rating": "C",
"cost": 1545000,
@@ -14,7 +14,7 @@
{
"id": "2g",
"grp": "sc",
"name": "Inter. Discovery Scanner",
"name": "Intermediate Discovery Scanner",
"class": 1,
"rating": "D",
"cost": 505000,

View File

@@ -1,5 +1,5 @@
{
"Shield Cell Banks": [
"Shield Cell Bank": [
{ "id": "65", "grp": "scb", "class": 8, "rating": "E", "cost": 697584, "mass": 160, "power": 1.44, "cells": 5, "rechargeRating": "C", "recharge": 0 },
{ "id": "64", "grp": "scb", "class": 8, "rating": "D", "cost": 1743961, "mass": 64, "power": 1.92, "cells": 3, "rechargeRating": "C", "recharge": 0 },
{ "id": "63", "grp": "scb", "class": 8, "rating": "C", "cost": 4359903, "mass": 160, "power": 2.4, "cells": 4, "rechargeRating": "B", "recharge": 0 },

View File

@@ -1,5 +1,5 @@
{
"Shield Generators": [
"Shield Generator": [
{ "id": "4t", "grp": "sg", "class": 8, "rating": "E", "cost": 2007241, "mass": 160, "power": 2.4, "minmass": 900, "optmass": 1800, "maxmass": 4500, "minmul": 1.3, "optmul": 0.8, "maxmul": 0.3 },
{ "id": "4s", "grp": "sg", "class": 8, "rating": "D", "cost": 6021722, "mass": 64, "power": 3.2, "minmass": 900, "optmass": 1800, "maxmass": 4500, "minmul": 1.4, "optmul": 0.9, "maxmul": 0.4 },
{ "id": "4r", "grp": "sg", "class": 8, "rating": "C", "cost": 18065165, "mass": 160, "power": 4, "minmass": 900, "optmass": 1800, "maxmass": 4500, "minmul": 1.5, "optmul": 1, "maxmul": 0.5 },

View File

@@ -30,23 +30,23 @@ function writeDB(err, arr) {
var shipOrder = Object.keys(arr[0]).sort();
var internalOrder = Object.keys(arr[3]).sort();
var hpOrder = [
"Pulse Lasers",
"Burst Lasers",
"Beam Lasers",
"Multi-cannons",
"Cannons",
"Fragment Cannons",
"Rail Guns",
"Plasma Accelerators",
"Missile Racks",
"Torpedo Pylons",
"Mine Launchers",
"Mining Lasers",
"Cargo Scanners",
"Countermeasures",
"Frame Shift Wake Scanners",
"Kill Warrant Scanners",
"Shield Boosters"
"Pulse Laser",
"Burst Laser",
"Beam Laser",
"Multi-cannon",
"Cannon",
"Fragment Cannon",
"Rail Gun",
"Plasma Accelerator",
"Missile Rack",
"Torpedo Pylon",
"Mine Launcher",
"Mining Laser",
"Cargo Scanner",
"Countermeasure",
"Frame Shift Wake Scanner",
"Kill Warrant Scanner",
"Shield Booster"
];
for (var i = 0; i < internalOrder.length; i++) {

View File

@@ -5,8 +5,8 @@
"references": [
{
"name": "Coriolis.io",
"url": "http://localhost:3300/outfit/anaconda/48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4yo5dig%3D.MwBhEYy6duwEziA?bn=Test",
"code": "48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4yo5dig=.MwBhEYy6duwEziA",
"url": "http://localhost:3300/outfit/anaconda/48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b?bn=Test",
"code": "48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b",
"shipId": "anaconda"
}
],
@@ -205,13 +205,13 @@
"armour": 1185,
"totalDps": 29,
"powerAvailable": 36,
"powerRetracted": 23.33,
"powerDeployed": 34.96,
"powerRetracted": 23.93,
"powerDeployed": 35.56,
"unladenRange": 18.49,
"fullTankRange": 18.12,
"ladenRange": 16.39,
"unladenTotalRange": 74.45,
"ladenTotalRange": 67.16,
"unladenTotalRange": 73.21,
"ladenTotalRange": 66.15,
"maxJumpCount": 4,
"shieldStrength": 833
}

50
test/fixtures/expected-builds.json vendored Normal file
View File

@@ -0,0 +1,50 @@
{
"type_6_transporter": {
"Cargo": "02A4D4A2D2D2D4C-----04040303430101.Iw1-kA==.Aw1-kA==",
"Miner": "03A4D4A2D2D2D4C2l2l---040403451q0101.Iw1-kA==.Aw1-kA==",
"Hopper": "02A4D4A2D1A2D4C1717---030302024300-.Iw1-kA==.Aw1-kA=="
},
"type_7_transport": {
"Cargo": "02A5D5A4D3D3D5C--------0505040403480101.Iw18aQ==.Aw18aQ==",
"Miner": "04D5D5A4D2D3D5C--2l2l----0505041v03450000.Iw18aQ==.Aw18aQ=="
},
"federal_dropship": {
"Cargo": "04D5D5A5D3D4D4C-1717------05040448020201.Iw18aQ==.Aw18aQ=="
},
"asp": {
"Miner": "25A5A5A4D4A5A5C0s0s24242l2l---04054a1q02022o27.Iw18WQ==.Aw18WQ=="
},
"imperial_clipper": {
"Cargo": "03A5D5A5D4D5D4C--0s0s----0605450302020101.Iw18aQ==.Aw18aQ==",
"Dream": "26A6A5A5D6A5A4C0v0v0s0s0404040n4k5n5d2b29292o-.Iw18aQ==.Aw18aQ==",
"Current": "04A6A5A5D4A5A4C----------------.Iw18aQ==.Aw18aQ=="
},
"type_9_heavy": {
"Current": "04A7D6A5D5D4D6C---------0706054a0303020224.Iw18eQ==.Aw18eQ=="
},
"python": {
"Cargo": "04A6D5A4D6D6D5C---------050505040448020201.Iw18eQ==.Aw18eQ==",
"Miner": "06A6A5A4D6A6A5C0v0v0v2m2m0404--050505Ce4a1v02022o.Iw18eQ==.Aw18eQ==",
"Dream": "27A6A5A4D7A6A5C0v0v0v27270404040m5n5n4f2d2d032t0201.Iw18eQ==.Aw18eQ==",
"Missile": "07E6E5E4E7E6E5C2f2g2d2ePh----04044j03---002h.Iw18eQ==.Aw18eQ=="
},
"anaconda": {
"Dream": "48A7A6A5D8A8A5C2c0o0o0o1m1m0q0q0404040l0b0100004k5n5n112d2d040303326b.Iw18ZlA=.Aw18ZlA=",
"Cargo": "04A6D6A5D5D8D5C----------------0605050504040445030301.Iw18ZlA=.Aw18ZlA=",
"Current": "04A6D6A5D5A8D5C----------------0605050504040403034524.Iw18ZlA=.Aw18ZlA=",
"Explorer": "04A6D6A5D5A8D5C--------0202------f7050505040s372f2i4524.Iw18ZlA=.Aw18ZlA=",
"Test": "48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.Iw18ZlA=.Aw18ZlA="
},
"diamondback_explorer": {
"Explorer": "02A4D5A3D3D3D5C---0202--320p432i2f.Iw1-kA==.Aw1-kA=="
},
"vulture": {
"Bounty Hunter": "34A4C4A3D5A4A3C1e1e0404-0l4a5d27662j.Iw19kA==.Aw19kA=="
},
"fer_de_lance": {
"Attack": "25A5C4A4D6A4A3C1r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.Aw18aQ=="
},
"eagle": {
"Figther": "42A3A3A1D2A2A2C0p0p24-40532j.Iw19A===.Aw19A==="
}
}

50
test/fixtures/old-valid-export.json vendored Normal file
View File

@@ -0,0 +1,50 @@
{
"builds": {
"type_6_transporter": {
"Cargo": "02A4D4A2D2D2D4C-----04040303430101",
"Miner": "03A4D4A2D2D2D4C2l2l---040403451q0101",
"Hopper": "02A4D4A2D1A2D4C1717---030302024300-"
},
"type_7_transport": {
"Cargo": "02A5D5A4D3D3D5C--------0505040403480101",
"Miner": "04D5D5A4D2D3D5C--2l2l----0505041v03450000"
},
"federal_dropship": {
"Cargo": "04D5D5A5D3D4D4C-1717------05040448020201"
},
"asp": {
"Miner": "25A5A5A4D4A5A5C0s0s24242l2l---04054a1q02022o27"
},
"imperial_clipper": {
"Cargo": "03A5D5A5D4D5D4C--0s0s----0605450302020101",
"Dream": "26A6A5A5D6A5A4C0v0v0s0s0404040n4k5n5d2b29292o-.AwRj4yWU1I==.CwBhCYy6YRigzLIA",
"Current": "04A6A5A5D4A5A4C----------------.AwRj4yWU1I==.CwBhCYy6YRigzLIA"
},
"type_9_heavy": {
"Current": "04A7D6A5D5D4D6C---------0706054a0303020224.AwRj4yoo.EwBhEYy6dsg="
},
"python": {
"Cargo": "04A6D5A4D6D6D5C---------050505040448020201.Iw18eQ==.Aw18eQ==",
"Miner": "06A6A5A4D6A6A5C0v0v0v2m2m0404--050505Ce4a1v02022o.Iw18eQ==.IwBhBYy6dkCYg===",
"Dream": "27A6A5A4D7A6A5C0v0v0v27270404040m5n5n4f2d2d032t0201.Iw1+gDBxA===.EwBhEYy6e0WEA==="
},
"anaconda": {
"Dream": "48A7A6A5D8A8A5C2c0o0o0o1m1m0q0q0404040l0b0100004k5n5n112d2d040303326b.AwRj4yo5dig=.MwBhCYy6du3ARiA=",
"Cargo": "04A6D6A5D5D8D5C----------------0605050504040445030301.Iw18ZlA=.Aw18ZlA=",
"Current": "04A6D6A5D5A8D5C----------------0605050504040403034524.Iw18ZlA=.Aw18ZlA=",
"Explorer": "04A6D6A5D5A8D5C--------0202------f7050505040s372f2i4524.AwRj4yVKJthA.AwhMIyungRhEA==="
},
"diamondback_explorer": {
"Explorer": "02A4D5A3D3D3D5C---0202--320p432i2f.AwRj4zTI.AwiMIypI"
},
"vulture": {
"Bounty Hunter": "34A4C4A3D5A4A3C1e1e0404-0l4a5d27662j.AwRj4y2I.MwBhBYy6wJmAjLIA"
},
"fer_de_lance": {
"Attack": "25A5C4A4D6A4A3C1r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.CwBhrSu8EZyA"
},
"eagle": {
"Figther": "42A3A3A1D2A2A2C0p0p24-40532j.AwRj49iA.AwgsIkEZigmIA==="
}
}
}

66
test/fixtures/valid-backup.json vendored Normal file
View File

@@ -0,0 +1,66 @@
{
"builds": {
"type_6_transporter": {
"Cargo": "02A4D4A2D2D2D4C-----04040303430101",
"Miner": "03A4D4A2D2D2D4C2l2l---040403451q0101",
"Hopper": "02A4D4A2D1A2D4C1717---030302024300-"
},
"type_7_transport": {
"Cargo": "02A5D5A4D3D3D5C--------0505040403480101",
"Miner": "04D5D5A4D2D3D5C--2l2l----0505041v03450000"
},
"federal_dropship": {
"Cargo": "04D5D5A5D3D4D4C-1717------05040448020201"
},
"asp": {
"Miner": "25A5A5A4D4A5A5C0s0s24242l2l---04054a1q02022o27"
},
"cobra_mk_iii": {
"Example": "24A4A4A3D3A3A4C0s0s2d2d0m0445032b2o2753.AwRj4yKA.CwBhEYyrKhmMQ===",
},
"imperial_clipper": {
"Cargo": "03A5D5A5D4D5D4C--0s0s----0605450302020101",
"Multi-purpose": "26A4A5A5D6A5A4C0v0v272704090j0h064f2c0302020101",
"Current": "05A6D5A5D6A5A4C0v0v27270404050n4m05035d29292o01.AwRj4yrI.AwhMIyuBGNiA",
"Dream": "26A6A5A5D6A5A4C0v0v0s0s04040c0n064f5d2b02022o0d.AwRj49UlmI==.AwiMIyuo"
},
"type_9_heavy": {
"Cargo": "04A6D6A5D4D4D5C---------07064f040303010201.AwRj4yoo.EwBhEYy6dsg="
},
"python": {
"Cargo": "04A6D5A4D6D6D5C---------050505044a03020201",
"Miner": "04A6D5A4D6D6D5C---2m2m----050505044d1v02022o"
},
"anaconda": {
"Dream": "48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404040l0b0100034k5n05050404040303326b.AwRj4yo5dig=.MwBhEYy6duwEziA=",
"Cargo": "03A7D6A5D4D8D5C----------------060505054d040403030301.AwRj4yuqg===.Aw18ZlA="
},
"diamondback_explorer": {
"Explorer": "02A4D5A3D3D3D5C-------320p432i2f.AwRj4zTI.AwiMIypI"
}
},
"comparisons": {
"Test": {
"facets": [ 9, 6, 4, 1, 3, 2 ],
"builds": [
{
"shipId": "anaconda",
"buildName": "Dream"
},
{
"shipId": "asp",
"buildName": "Miner"
},
{
"shipId": "diamondback_explorer",
"buildName": "Explorer"
}
]
}
},
"insurance": "Beta",
"discounts": [
1,
1
]
}

3620
test/fixtures/valid-detailed-export.json vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,140 @@
describe('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.importJSON = angular.toJson(importData);
scope.validateJson();
expect(scope.jsonValid).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.importJSON = angular.toJson(importData);
scope.validateJson();
expect(scope.jsonValid).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.importJSON = 'null';
scope.validateJson();
expect(scope.jsonValid).toBeFalsy();
expect(scope.errorMsg).toEqual('Must be an object or array!');
scope.importJSON = '{ "builds": "Should not be a string" }';
scope.validateJson();
expect(scope.jsonValid).toBeFalsy();
expect(scope.errorMsg).toEqual('builds must be an object!');
scope.importJSON = angular.toJson(importData).replace('anaconda', 'invalid_ship');
scope.validateJson();
expect(scope.jsonValid).toBeFalsy();
expect(scope.errorMsg).toEqual('"invalid_ship" is not a valid Ship Id!');
scope.importJSON = angular.toJson(importData).replace('Dream', '');
scope.validateJson();
expect(scope.jsonValid).toBeFalsy();
expect(scope.errorMsg).toEqual('Imperial Clipper build "" must be a string at least 3 characters long!');
});
});
describe('Import Detailed Build', function() {
it('imports a valid build', function() {
var importData = __json__['fixtures/anaconda-test-detailed-export'];
scope.importJSON = angular.toJson(importData);
scope.validateJson();
expect(scope.jsonValid).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('catches an invalid build', function() {
var importData = __json__['fixtures/anaconda-test-detailed-export'];
scope.importJSON = angular.toJson(importData).replace('components', 'comps');
scope.validateJson();
expect(scope.jsonValid).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.importJSON = angular.toJson(importData);
scope.validateJson();
expect(scope.jsonValid).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]);
}
}
});
});
});

View File

@@ -1,7 +1,7 @@
describe("Outfit Controller", function() {
beforeEach(module('app'));
var outfitController, scope;
var outfitController, $rootScope, $stateParams, scope;
var eventStub = {
preventDefault: function(){ },

View File

@@ -1,18 +1,19 @@
describe("Serializer Service", function() {
beforeEach(module('app'));
var Ship, Serializer;
var Ship,
Serializer,
code = '48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b',
anaconda = DB.ships['anaconda'],
testBuild,
exportData;
beforeEach(inject(function (_Ship_, _Serializer_) {
Ship = _Ship_;
Serializer = _Serializer_;
}));
describe("Detailed Export", function() {
var code = '48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4yo5dig=.MwBhEYy6duwEziA',
anaconda = DB.ships['anaconda'],
testBuild,
exportData;
describe("To Detailed Build", function() {
beforeEach(function() {
testBuild = new Ship('anaconda', anaconda.properties, anaconda.slots);
@@ -37,4 +38,20 @@ describe("Serializer Service", function() {
});
describe("From Detailed Build", function() {
it("builds the ship correctly", function() {
var anacondaTestExport = __json__['fixtures/anaconda-test-detailed-export'];
testBuildA = new Ship('anaconda', anaconda.properties, anaconda.slots);
Serializer.toShip(testBuildA, code);
testBuildB = Serializer.fromDetailedBuild(anacondaTestExport);
for(var p in testBuildB) {
expect(testBuildB[p]).toEqual(testBuildA[p], p + ' does not match');
}
});
});
});