diff --git a/app/js/controllers/controller-export.js b/app/js/controllers/controller-export.js index bc10c3f3..96570287 100755 --- a/app/js/controllers/controller-export.js +++ b/app/js/controllers/controller-export.js @@ -5,7 +5,7 @@ angular.module('app').controller('ExportController', ['$scope', '$stateParams', if ($stateParams.promise) { $scope.export = 'Generating...'; $stateParams.promise.then(function(data) { - $scope.export = data; + $scope.export = (typeof data === 'object') ? angular.toJson(data, true) : data; }); } else { $scope.export = angular.toJson($stateParams.data, true); diff --git a/app/js/controllers/controller-import.js b/app/js/controllers/controller-import.js index 88f10620..43327c74 100755 --- a/app/js/controllers/controller-import.js +++ b/app/js/controllers/controller-import.js @@ -7,7 +7,7 @@ angular.module('app').controller('ImportController', ['$scope', '$stateParams', $scope.ships = Ships; $scope.validateJson = function() { - var importObj = null; + var importObj = null, shipData = null; $scope.jsonValid = false; $scope.errorMsg = null; $scope.builds = null; @@ -22,36 +22,68 @@ angular.module('app').controller('ImportController', ['$scope', '$stateParams', } if (typeof importObj != 'object') { - $scope.errorMsg = 'Must be an object!'; + $scope.errorMsg = 'Must be an object or array!'; return; } - if ((!importObj.builds || !Object.keys(importObj.builds).length)) { - $scope.errorMsg = 'No builds in data'; - return; - } - - for (var shipId in importObj.builds) { - var 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; + // 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; + } } - 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!'; + } else { + $scope.errorMsg = '"' + shipId + '"" is not a valid Ship Id!'; + return; + } + $scope.builds = importObj.builds; + } + + // 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; } } - } else { - $scope.errorMsg = '"' + shipId + '"" is not a valid Ship Id!'; - return; } - $scope.builds = importObj.builds; + $scope.builds = builds; + } else { + $scope.errorMsg = 'No builds in data'; + return; } $scope.jsonValid = true; diff --git a/app/js/controllers/controller-outfit.js b/app/js/controllers/controller-outfit.js index fc123275..c9f8eb3e 100755 --- a/app/js/controllers/controller-outfit.js +++ b/app/js/controllers/controller-outfit.js @@ -219,12 +219,7 @@ angular.module('app').controller('OutfitController', ['$window', '$rootScope', ' if ($scope.buildName) { $state.go('modal.export', { - data: Serializer.toJsonBuild( - $scope.buildName, - ship, - $state.href($state.current.name, $state.params, { absolute: true }), - $scope.code || Serializer.fromShip(ship) - ) + data: Serializer.toDetailedBuild($scope.buildName, ship, $scope.code || Serializer.fromShip(ship)) }); } }; diff --git a/app/js/directives/directive-header.js b/app/js/directives/directive-header.js index d8f5f039..9b2b368a 100755 --- a/app/js/directives/directive-header.js +++ b/app/js/directives/directive-header.js @@ -1,4 +1,4 @@ -angular.module('app').directive('shipyardHeader', ['lodash', '$rootScope', 'Persist', 'ShipsDB', function(_, $rootScope, Persist, ships) { +angular.module('app').directive('shipyardHeader', ['lodash', '$rootScope', '$state', 'Persist', 'Serializer', 'ShipsDB', function(_, $rootScope, $state, Persist, Serializer, ships) { return { restrict: 'E', @@ -18,17 +18,6 @@ angular.module('app').directive('shipyardHeader', ['lodash', '$rootScope', 'Pers $rootScope.discounts.ship = savedDiscounts[0]; $rootScope.discounts.components = savedDiscounts[1]; - // Close menus if a navigation change event occurs - $rootScope.$on('$stateChangeStart', function() { - scope.openedMenu = null; - }); - - // Listen to close event to close opened menus or modals - $rootScope.$on('close', function() { - scope.openedMenu = null; - $rootScope.showAbout = false; - }); - /** * Save selected insurance option */ @@ -44,6 +33,13 @@ angular.module('app').directive('shipyardHeader', ['lodash', '$rootScope', 'Pers $rootScope.$broadcast('discountChange'); }; + scope.detailedExport = function(e) { + e.preventDefault(); + e.stopPropagation(); + scope.openedMenu = null; + $state.go('modal.export', { data: Serializer.toDetailedExport(scope.allBuilds) }); + }; + scope.openMenu = function(e, menu) { e.stopPropagation(); if (menu == scope.openedMenu) { @@ -58,16 +54,15 @@ angular.module('app').directive('shipyardHeader', ['lodash', '$rootScope', 'Pers scope.openedMenu = menu; }; - scope.about = function(e) { - e.preventDefault(); - e.stopPropagation(); + // Close menus if a navigation change event occurs + $rootScope.$on('$stateChangeStart', function() { scope.openedMenu = null; - $rootScope.showAbout = true; - }; + }); - $rootScope.hideAbout = function() { - $rootScope.showAbout = false; - }; + // Listen to close event to close opened menus or modals + $rootScope.$on('close', function() { + scope.openedMenu = null; + }); scope.$watchCollection('allBuilds', function() { scope.buildsList = Object.keys(scope.allBuilds).sort(); diff --git a/app/js/service-serializer.js b/app/js/service-serializer.js index 3b84ad9a..32761c9e 100755 --- a/app/js/service-serializer.js +++ b/app/js/service-serializer.js @@ -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', function(_, GroupMap, MountMap) { +angular.module('app').service('Serializer', ['lodash', 'GroupMap', 'MountMap', 'ShipsDB', 'Ship', '$state', function(_, GroupMap, MountMap, ShipsDB, Ship, $state) { /** * Serializes the ships selected components for all slots to a URL friendly string. @@ -66,18 +66,18 @@ angular.module('app').service('Serializer', ['lodash', 'GroupMap', 'MountMap', f ); }; - this.toJsonBuild = function(buildName, ship, url, code) { + this.toDetailedBuild = function(buildName, ship, code) { var standard = ship.common, hardpoints = ship.hardpoints, internal = ship.internal; var data = { - $schema: 'http://cdn.coriolis.io/schemas/ship-loadout/1-draft.json#', + $schema: 'http://cdn.coriolis.io/schemas/ship-loadout/1.json#', name: buildName, ship: ship.name, references: [{ name: 'Coriolis.io', - url: url, + url: $state.href('outfit', { shipId: ship.id, code: code, bn: buildName }, { absolute: true }), code: code, shipId: ship.id }], @@ -108,6 +108,21 @@ angular.module('app').service('Serializer', ['lodash', 'GroupMap', 'MountMap', f return data; }; + this.toDetailedExport = function(builds) { + var data = []; + + for (var shipId in builds) { + for (var buildName in builds[shipId]) { + var code = builds[shipId][buildName]; + var shipData = ShipsDB[shipId]; + var ship = new Ship(shipId, shipData.properties, shipData.slots); + this.toShip(ship, code); + data.push(this.toDetailedBuild(buildName, ship, code)); + } + } + return data; + }; + this.fromComparison = function(name, builds, facets, predicate, desc) { var shipBuilds = []; diff --git a/app/schemas/ship-loadout/1-draft.json b/app/schemas/ship-loadout/1.json similarity index 100% rename from app/schemas/ship-loadout/1-draft.json rename to app/schemas/ship-loadout/1.json diff --git a/app/views/_header.html b/app/views/_header.html index 8d0d9eb1..ca9cacae 100755 --- a/app/views/_header.html +++ b/app/views/_header.html @@ -64,6 +64,7 @@ diff --git a/app/views/page-outfit.html b/app/views/page-outfit.html index bea2cd15..b1e19797 100644 --- a/app/views/page-outfit.html +++ b/app/views/page-outfit.html @@ -19,9 +19,9 @@ - + diff --git a/test/fixtures/anaconda-test-detailed-export.json b/test/fixtures/anaconda-test-detailed-export.json new file mode 100644 index 00000000..a54bff19 --- /dev/null +++ b/test/fixtures/anaconda-test-detailed-export.json @@ -0,0 +1,218 @@ +{ + "$schema": "http://cdn.coriolis.io/schemas/ship-loadout/1.json#", + "name": "Test", + "ship": "Anaconda", + "references": [ + { + "name": "Coriolis.io", + "url": "http://localhost:3300/outfit/anaconda/48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4yo5dig%3D.MwBhEYy6duwEziA?bn=Test", + "code": "48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4yo5dig=.MwBhEYy6duwEziA", + "shipId": "anaconda" + } + ], + "components": { + "standard": { + "bulkheads": "Reactive Surface Composite", + "powerPlant": { + "class": 8, + "rating": "A" + }, + "thrusters": { + "class": 6, + "rating": "A" + }, + "frameShiftDrive": { + "class": 6, + "rating": "A" + }, + "lifeSupport": { + "class": 5, + "rating": "A" + }, + "powerDistributor": { + "class": 8, + "rating": "A" + }, + "sensors": { + "class": 8, + "rating": "A" + }, + "fuelTank": { + "class": 5, + "rating": "C" + } + }, + "hardpoints": [ + { + "class": 4, + "rating": "A", + "group": "Plasma Accelerator", + "mount": "Fixed" + }, + { + "class": 3, + "rating": "D", + "group": "Beam Laser", + "mount": "Turret" + }, + { + "class": 3, + "rating": "D", + "group": "Beam Laser", + "mount": "Turret" + }, + { + "class": 3, + "rating": "D", + "group": "Beam Laser", + "mount": "Turret" + }, + { + "class": 2, + "rating": "E", + "group": "Cannon", + "mount": "Turret" + }, + { + "class": 2, + "rating": "E", + "group": "Cannon", + "mount": "Turret" + }, + { + "class": 1, + "rating": "F", + "group": "Beam Laser", + "mount": "Turret" + }, + { + "class": 1, + "rating": "F", + "group": "Beam Laser", + "mount": "Turret" + } + ], + "utility": [ + { + "class": 0, + "rating": "A", + "group": "Shield Booster" + }, + { + "class": 0, + "rating": "A", + "group": "Shield Booster" + }, + null, + { + "class": 0, + "rating": "C", + "group": "Kill Warrant Scanner" + }, + { + "class": 0, + "rating": "C", + "group": "Cargo Scanner" + }, + { + "class": 0, + "rating": "F", + "group": "Countermeasure", + "name": "Electronic Countermeasure" + }, + { + "class": 0, + "rating": "I", + "group": "Countermeasure", + "name": "Chaff Launcher" + }, + { + "class": 0, + "rating": "I", + "group": "Countermeasure", + "name": "Point Defence" + } + ], + "internal": [ + { + "class": 7, + "rating": "A", + "group": "Shield Generator" + }, + { + "class": 6, + "rating": "A", + "group": "Shield Cell Bank" + }, + { + "class": 6, + "rating": "E", + "group": "Cargo Rack" + }, + { + "class": 5, + "rating": "D", + "group": "Hull Reinforcement Package" + }, + { + "class": 5, + "rating": "E", + "group": "Cargo Rack" + }, + null, + null, + { + "class": 4, + "rating": "E", + "group": "Cargo Rack" + }, + { + "class": 4, + "rating": "E", + "group": "Cargo Rack" + }, + { + "class": 4, + "rating": "A", + "group": "Fuel Scoop" + }, + { + "class": 2, + "rating": "A", + "group": "FSD Interdictor" + } + ] + }, + "stats": { + "class": 3, + "hullCost": 141889932, + "speed": 180, + "boost": 240, + "agility": 2, + "baseShieldStrength": 350, + "baseArmour": 945, + "hullMass": 400, + "masslock": 23, + "shipCostMultiplier": 1, + "componentCostMultiplier": 1, + "fuelCapacity": 32, + "cargoCapacity": 128, + "ladenMass": 1339.2, + "armourAdded": 240, + "shieldMultiplier": 1.4, + "totalCost": 882362049, + "unladenMass": 1179.2, + "armour": 1185, + "totalDps": 29, + "powerAvailable": 36, + "powerRetracted": 23.33, + "powerDeployed": 34.96, + "unladenRange": 18.49, + "fullTankRange": 18.12, + "ladenRange": 16.39, + "unladenTotalRange": 74.45, + "ladenTotalRange": 67.16, + "maxJumpCount": 4, + "shieldStrength": 833 + } +} diff --git a/test/karma.conf.js b/test/karma.conf.js index df35c777..7c3d4815 100644 --- a/test/karma.conf.js +++ b/test/karma.conf.js @@ -3,7 +3,8 @@ module.exports = function(config) { basePath: '', frameworks: ['jasmine', 'fixture'], preprocessors: { - '../build/schemas/**/*.json': ['json_fixtures'] + '../build/schemas/**/*.json': ['json_fixtures'], + 'fixtures/**/*.json': ['json_fixtures'] }, files: [ '../build/lib*.js', @@ -11,10 +12,11 @@ module.exports = function(config) { '../node_modules/jsen/dist/jsen.js', '../build/app*.js', '../build/schemas/**/*.json', + 'fixtures/**/*.json', 'tests/**/*.js', ], - jsonFixturesPreprocessor: { - stripPrefix: '.*build', + jsonFixturesPreprocessor: { + stripPrefix: '.*(/build/)', variableName: '__json__' }, reporters: ['mocha'], diff --git a/test/tests/test-service-serializer.js b/test/tests/test-service-serializer.js index c4a9ee36..87565490 100644 --- a/test/tests/test-service-serializer.js +++ b/test/tests/test-service-serializer.js @@ -10,7 +10,6 @@ describe("Serializer Service", function() { describe("Detailed Export", function() { var code = '48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4yo5dig=.MwBhEYy6duwEziA', - url = 'http://a.test.url.com', anaconda = DB.ships['anaconda'], testBuild, exportData; @@ -18,22 +17,22 @@ describe("Serializer Service", function() { beforeEach(function() { testBuild = new Ship('anaconda', anaconda.properties, anaconda.slots); Serializer.toShip(testBuild, code); - exportData = Serializer.toJsonBuild('Test Build', testBuild, url, code); + exportData = Serializer.toDetailedBuild('Test', testBuild, code); }); it("conforms to the ship-loadout schema", function() { - var shipLoadoutSchema = __json__['/schemas/ship-loadout/1-draft']; + var shipLoadoutSchema = __json__['schemas/ship-loadout/1']; var validate = jsen(shipLoadoutSchema); var valid = validate(exportData); expect(valid).toBeTruthy(); }); - xit("contains the correct components", function() { - // TODO: implement - }); - - xit("contains the correct stats", function() { - // TODO: implement + it("contains the correct components and stats", function() { + var anacondaTestExport = __json__['fixtures/anaconda-test-detailed-export']; + expect(exportData.components).toEqual(anacondaTestExport.components); + expect(exportData.stats).toEqual(anacondaTestExport.stats); + expect(exportData.ship).toEqual(anacondaTestExport.ship); + expect(exportData.name).toEqual(anacondaTestExport.name); }); });