diff --git a/.jshintignore b/.jshintignore deleted file mode 100644 index 4b71d483..00000000 --- a/.jshintignore +++ /dev/null @@ -1 +0,0 @@ -app/js/db.js \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..52561dc0 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: node_js +notifications: + email: false +sudo: false +node_js: + - "0.12" +cache: + directories: + - node_modules + - bower_components + +before_script: + - npm install -g gulp + - npm install -g bower + - bower install +script: + - gulp lint + - gulp build-prod + - gulp test \ No newline at end of file diff --git a/README.md b/README.md index 3eed4816..8c21a4e7 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[ ![Codeship Status for cmmcleod/coriolis](https://codeship.com/projects/637858c0-f2a5-0132-7af7-5ed004d44c71/status?branch=master)](https://codeship.com/projects/85232) [![Tasks in Ready](https://badge.waffle.io/cmmcleod/coriolis.png?label=ready&title=Ready)](https://waffle.io/cmmcleod/coriolis) [![Tasks in Progress](https://badge.waffle.io/cmmcleod/coriolis.svg?label=in%20progress&title=In%20Progress)](http://waffle.io/cmmcleod/coriolis) +[![Build Status](https://travis-ci.org/cmmcleod/coriolis.svg?branch=master)](https://travis-ci.org/cmmcleod/coriolis) [![Tasks in Ready](https://badge.waffle.io/cmmcleod/coriolis.png?label=ready&title=Ready)](https://waffle.io/cmmcleod/coriolis) [![Tasks in Progress](https://badge.waffle.io/cmmcleod/coriolis.svg?label=in%20progress&title=In%20Progress)](http://waffle.io/cmmcleod/coriolis) @@ -6,11 +6,11 @@ The Coriolis project was inspired by [E:D Shipyard](http://www.edshipyard.com/) and, of course, [Elite Dangerous](http://www.elitedangerous.com). The ultimate goal of Coriolis is to provide rich features to support in-game play and planning while engaging the E:D community to support its development. -Coriolis was created for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments. +Coriolis was created for non-commercial purposes. Coriolis was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments and no employee of Frontier Developments was involved in the making of it. ## Contributing -Please [submit issues](https://github.com/cmmcleod/coriolis/issues), or better yet [pull requests](http://www.elitedangerous.com) 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 @@ -28,12 +28,15 @@ See [Data wiki](https://github.com/cmmcleod/coriolis/wiki/Database) for details ## License -The MIT License +All Data and [associated JSON](https://github.com/cmmcleod/coriolis/tree/master/data) files are intellectual property and copyright of Frontier Developments plc ('Frontier', 'Frontier Developments') and are subject to their +[terms and conditions](https://www.frontierstore.net/terms-and-conditions/). + +The code (Javascript, CSS, HTML, and SVG files only) specificially for Coriolis.io is released under the MIT License. Copyright (c) 2015 Coriolis.io, Colin McLeod Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal +of this software (Javascript, CSS, HTML, and SVG files only), and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is diff --git a/app/icons/cancel-circle.svg b/app/icons/cancel-circle.svg deleted file mode 100755 index dfb2f750..00000000 --- a/app/icons/cancel-circle.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/app/icons/feather.svg b/app/icons/feather.svg new file mode 100644 index 00000000..4d512636 --- /dev/null +++ b/app/icons/feather.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/icons/spinner11.svg b/app/icons/spinner11.svg index 15cb4cdf..b6588c71 100755 --- a/app/icons/spinner11.svg +++ b/app/icons/spinner11.svg @@ -1,6 +1,3 @@ - - - - + diff --git a/app/icons/switch.svg b/app/icons/switch.svg index 4401f3f1..a3949c0d 100755 --- a/app/icons/switch.svg +++ b/app/icons/switch.svg @@ -1,6 +1,4 @@ - - - - + + diff --git a/app/index.html b/app/index.html index 0de22846..6fd111f8 100755 --- a/app/index.html +++ b/app/index.html @@ -63,10 +63,10 @@
- Version <%= version %> - <%= date %> + Version <%= version %> - <%= date %>
- Coriolis Shipyard was created for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments. + Coriolis was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments and no employee of Frontier Developments was involved in the making of it.
diff --git a/app/js/app.js b/app/js/app.js index c24da083..bfa1c99e 100755 --- a/app/js/app.js +++ b/app/js/app.js @@ -1,12 +1,13 @@ angular.module('app', ['ui.router', 'ct.ui.router.extras.sticky', 'ui.sortable', 'shipyard', 'ngLodash', 'app.templates']) -.run(['$rootScope', '$location', '$window', '$document','$state','commonArray','shipPurpose','shipSize','hardPointClass','GroupMap', 'Persist', function ($rootScope, $location, $window, $doc, $state, CArr, shipPurpose, sz, hpc, GroupMap, Persist) { +.run(['$rootScope', '$location', '$window', '$document', '$state', 'commonArray', 'shipPurpose', 'shipSize', 'hardPointClass', 'GroupMap', 'Persist', +function($rootScope, $location, $window, $doc, $state, CArr, shipPurpose, sz, hpc, GroupMap, Persist) { // App is running as a standalone web app on tablet/mobile var isStandAlone = $window.navigator.standalone || ($window.external && $window.external.msIsSiteMode && $window.external.msIsSiteMode()); // Redirect any state transition errors to the error controller/state - $rootScope.$on('$stateChangeError', function(e, toState, toParams, fromState, fromParams, error){ + $rootScope.$on('$stateChangeError', function(e, toState, toParams, fromState, fromParams, error) { e.preventDefault(); - $state.go('error', error, {location:false, reload:true}); // Go to error state, reload the controller, keep the current URL + $state.go('error', error, { location: false, reload: true }); // Go to error state, reload the controller, keep the current URL }); // Track on Google analytics if available @@ -16,12 +17,12 @@ angular.module('app', ['ui.router', 'ct.ui.router.extras.sticky', 'ui.sortable', if (to.url) { // Only track states that have a URL if ($window.ga) { - ga('send', 'pageview', {page: $location.path()}); + ga('send', 'pageview', { page: $location.path() }); } if (isStandAlone) { // Persist the current state - Persist.setState({name: to.name, params: toParams}); + Persist.setState({ name: to.name, params: toParams }); } } }); @@ -32,12 +33,14 @@ angular.module('app', ['ui.router', 'ct.ui.router.extras.sticky', 'ui.sortable', $rootScope.SZ = sz; $rootScope.HPC = hpc; $rootScope.GMAP = GroupMap; - $rootScope.STATUS = ['','DISABLED', 'OFF', 'ON']; - $rootScope.STATUS_CLASS = ['','disabled', 'warning', 'secondary-disabled']; + $rootScope.insurance = { opts: [{ name: 'Standard', pct: 0.05 }, { name: 'Alpha', pct: 0.025 }, { name: 'Beta', pct: 0.035 }] }; + $rootScope.discounts = { opts: [{ name: 'None', pct: 1 }, { name: 'Founders World - 10%', pct: 0.90 }] }; + $rootScope.STATUS = ['', 'DISABLED', 'OFF', 'ON']; + $rootScope.STATUS_CLASS = ['', 'disabled', 'warning', 'secondary-disabled']; $rootScope.title = 'Coriolis'; - $rootScope.cName = function (c) { - return c.c? c.c.name? c.c.name : GroupMap[c.c.grp] : null; + $rootScope.cName = function(c) { + return c.c ? c.c.name ? c.c.name : GroupMap[c.c.grp] : null; }; // Formatters @@ -48,21 +51,21 @@ angular.module('app', ['ui.router', 'ct.ui.router.extras.sticky', 'ui.sortable', $rootScope.fPct = d3.format('.2%'); $rootScope.f1Pct = d3.format('.1%'); $rootScope.fRPct = d3.format('%'); - $rootScope.fTime = function(d) { return Math.floor(d/60) + ":" + ("00" + Math.floor(d%60)).substr(-2,2); }; + $rootScope.fTime = function(d) { return Math.floor(d / 60) + ':' + ('00' + Math.floor(d % 60)).substr(-2, 2); }; if (isStandAlone) { var state = Persist.getState(); // If a previous state has been stored, load that state if (state && state.name && state.params) { - $state.go(state.name, state.params, {location:'replace'}); + $state.go(state.name, state.params, { location: 'replace' }); } else { - $state.go('shipyard', null, {location:'replace'}); // Default to home page + $state.go('shipyard', null, { location: 'replace' }); // Default to home page } } // Global Event Listeners - $doc.bind('keyup', function (e) { - if(e.keyCode == 27) { // Escape Key + $doc.bind('keyup', function(e) { + if (e.keyCode == 27) { // Escape Key $rootScope.$broadcast('close', e); $rootScope.$apply(); } else { @@ -70,7 +73,7 @@ angular.module('app', ['ui.router', 'ct.ui.router.extras.sticky', 'ui.sortable', } }); - $rootScope.bgClicked = function (e) { + $rootScope.bgClicked = function(e) { $rootScope.$broadcast('close', e); }; diff --git a/app/js/config.js b/app/js/config.js index 6da41298..87e1f132 100755 --- a/app/js/config.js +++ b/app/js/config.js @@ -1,9 +1,9 @@ /** * Sets up the routes and handlers before the Angular app is kicked off. */ -angular.module('app').config(['$provide','$stateProvider', '$urlRouterProvider', '$locationProvider', 'ShipsDB', function ($provide, $stateProvider, $urlRouterProvider, $locationProvider, ships) { +angular.module('app').config(['$provide', '$stateProvider', '$urlRouterProvider', '$locationProvider', 'ShipsDB', function($provide, $stateProvider, $urlRouterProvider, $locationProvider, ships) { // Use HTML5 push and replace state if possible - $locationProvider.html5Mode({enabled: true, requireBase: false}); + $locationProvider.html5Mode({ enabled: true, requireBase: false }); /** * Set up all states and their routes. */ @@ -11,13 +11,13 @@ angular.module('app').config(['$provide','$stateProvider', '$urlRouterProvider', .state('outfit', { url: '/outfit/:shipId/:code?bn', params: { - shipId: { value: 'sidewinder', squash: false}, // Allow 'shipId' parameter to default to sidewinder - code: { value: null, squash: true } // Allow 'code' parameter to be empty/optional + shipId: { value: 'sidewinder', squash: false }, // Allow 'shipId' parameter to default to sidewinder + code: { value: null, squash: true } // Allow 'code' parameter to be empty/optional }, templateUrl: 'views/page-outfit.html', controller: 'OutfitController', resolve: { - shipId: ['$stateParams',function ($p) { // Ensure ship exists before loading controller + shipId: ['$stateParams', function($p) { // Ensure ship exists before loading controller if (!ships[$p.shipId]) { throw { type: 'no-ship', message: $p.shipId }; } @@ -28,7 +28,7 @@ angular.module('app').config(['$provide','$stateProvider', '$urlRouterProvider', .state('compare', { url: '/compare/:name', params: { - name: {value: null, squash: true } + name: { value: null, squash: true } }, templateUrl: 'views/page-comparison.html', controller: 'ComparisonController', @@ -41,26 +41,26 @@ angular.module('app').config(['$provide','$stateProvider', '$urlRouterProvider', sticky: true }) .state('shipyard', { url: '/', templateUrl: 'views/page-shipyard.html', controller: 'ShipyardController', sticky: true }) - .state('error', { params: {type:null, message:null, details: null }, templateUrl: 'views/page-error.html', controller: 'ErrorController', sticky: true }) + .state('error', { params: { type: null, message: null, details: null }, templateUrl: 'views/page-error.html', controller: 'ErrorController', sticky: true }) // Modal States and views - .state('modal', { abstract: true, views:{ "modal": { templateUrl: "views/_modal.html", controller: 'ModalController' } } }) - .state('modal.about', { views: { "modal-content": { templateUrl: "views/modal-about.html" } } }) - .state('modal.export', { params: {title:null, data: null, promise: null}, views: { "modal-content": { templateUrl: "views/modal-export.html", controller: 'ExportController' } } }) - .state('modal.import', { params: {obj:null}, views: { "modal-content": { templateUrl: "views/modal-import.html", controller: 'ImportController' } } }) - .state('modal.link', { params: {url:null}, views: { "modal-content": { templateUrl: "views/modal-link.html", controller: 'LinkController' } } }) - .state('modal.delete', { views: { "modal-content": { templateUrl: "views/modal-delete.html", controller: 'DeleteController' } } }); + .state('modal', { abstract: true, views: { 'modal': { templateUrl: 'views/_modal.html', controller: 'ModalController' } } }) + .state('modal.about', { views: { 'modal-content': { templateUrl: 'views/modal-about.html' } } }) + .state('modal.export', { params: { title: null, data: null, promise: null }, views: { 'modal-content': { templateUrl: 'views/modal-export.html', controller: 'ExportController' } } }) + .state('modal.import', { params: { obj: null }, views: { 'modal-content': { templateUrl: 'views/modal-import.html', controller: 'ImportController' } } }) + .state('modal.link', { params: { url: null }, views: { 'modal-content': { templateUrl: 'views/modal-link.html', controller: 'LinkController' } } }) + .state('modal.delete', { views: { 'modal-content': { templateUrl: 'views/modal-delete.html', controller: 'DeleteController' } } }); // Redirects - $urlRouterProvider.when('/outfit','/outfit/sidewinder'); + $urlRouterProvider.when('/outfit', '/outfit/sidewinder'); /** * 404 Handler - Keep current URL/ do not redirect, change to error state. */ - $urlRouterProvider.otherwise(function ($injector, $location) { + $urlRouterProvider.otherwise(function($injector, $location) { // Go to error state, reload the controller, keep the current URL - $injector.get('$state').go('error', { type: 404, message: null, details: null }, {location:false, reload:true}); + $injector.get('$state').go('error', { type: 404, message: null, details: null }, { location: false, reload: true }); return $location.path; }); @@ -69,10 +69,10 @@ angular.module('app').config(['$provide','$stateProvider', '$urlRouterProvider', * redirects uncaught errors to the error page. * */ - $provide.decorator('$exceptionHandler', ['$delegate', '$injector', function ($delegate, $injector) { + $provide.decorator('$exceptionHandler', ['$delegate', '$injector', function($delegate, $injector) { return function(err, cause) { // Go to error state, reload the controller, keep the current URL - $injector.get('$state').go('error', {type:null, message: err.message, details: err.stack }, {location:false, reload:true}); + $injector.get('$state').go('error', { type: null, message: err.message, details: err.stack }, { location: false, reload: true }); $delegate(err, cause); }; }]); diff --git a/app/js/controllers/controller-comparison.js b/app/js/controllers/controller-comparison.js index 3ac392e9..d9914256 100755 --- a/app/js/controllers/controller-comparison.js +++ b/app/js/controllers/controller-comparison.js @@ -1,48 +1,49 @@ -angular.module('app').controller('ComparisonController', ['lodash', '$rootScope', '$filter', '$scope', '$state', '$stateParams', 'Utils', 'ShipFacets', 'ShipsDB', 'Ship', 'Persist', 'Serializer', function (_, $rootScope, $filter, $scope, $state, $stateParams, Utils, ShipFacets, Ships, Ship, Persist, Serializer) { +angular.module('app').controller('ComparisonController', ['lodash', '$rootScope', '$filter', '$scope', '$state', '$stateParams', 'Utils', 'ShipFacets', 'ShipsDB', 'Ship', 'Persist', 'Serializer', function(_, $rootScope, $filter, $scope, $state, $stateParams, Utils, ShipFacets, Ships, Ship, Persist, Serializer) { $rootScope.title = 'Coriolis - Compare'; $scope.predicate = 'name'; // Sort by ship name as default $scope.desc = false; - $scope.facetSortOpts = { containment: '#facet-container', orderChanged: function () { $scope.saved = false; } }; + $scope.facetSortOpts = { containment: '#facet-container', orderChanged: function() { $scope.saved = false; } }; $scope.builds = []; $scope.unusedBuilds = []; $scope.name = $stateParams.name; $scope.compareMode = !$stateParams.code; $scope.importObj = {}; // Used for importing comparison builds (from permalinked comparison) - var defaultFacets = [9,6,4,1,3,2]; // Reverse order of Armour, Shields, Speed, Jump Range, Cargo Capacity, Cost + var defaultFacets = [9, 6, 4, 1, 3, 2]; // Reverse order of Armour, Shields, Speed, Jump Range, Cargo Capacity, Cost var facets = $scope.facets = angular.copy(ShipFacets); + var shipId, buildName, comparisonData; /** * Add an existing build to the comparison. The build must be saved locally. - * @param {string} shipId The unique ship key/id - * @param {string} buildName The build name + * @param {string} id The unique ship key/id + * @param {string} name The build name */ - $scope.addBuild = function (shipId, buildName, code) { - var data = Ships[shipId]; // Get ship properties - code = code? code : Persist.builds[shipId][buildName]; // Retrieve build code if not passed - var b = new Ship(shipId, data.properties, data.slots); // Create a new Ship instance + $scope.addBuild = function(id, name, code) { + var data = Ships[id]; // Get ship properties + code = code ? code : Persist.builds[id][name]; // Retrieve build code if not passed + var b = new Ship(id, data.properties, data.slots); // Create a new Ship instance Serializer.toShip(b, code); // Populate components from code // Extend ship instance and add properties below - b.buildName = buildName; + b.buildName = name; b.code = code; b.pctRetracted = b.powerRetracted / b.powerAvailable; b.pctDeployed = b.powerDeployed / b.powerAvailable; $scope.builds.push(b); // Add ship build to comparison $scope.builds = $filter('orderBy')($scope.builds, $scope.predicate, $scope.desc); // Resort - _.remove($scope.unusedBuilds, function (b) { // Remove from unused builds - return b.id == shipId && b.buildName == buildName; + _.remove($scope.unusedBuilds, function(o) { // Remove from unused builds + return o.id == id && o.buildName == name; }); $scope.saved = false; }; /** * Removes a build from the comparison - * @param {string} shipId The unique ship key/id - * @param {string} buildName The build name + * @param {string} id The unique ship key/id + * @param {string} name The build name */ - $scope.removeBuild = function (shipId, buildName) { - _.remove($scope.builds, function (b) { - if (b.id == shipId && b.buildName == buildName) { - $scope.unusedBuilds.push({id: shipId, buildName: buildName, name: b.name}); // Add build back to unused builds + $scope.removeBuild = function(id, name) { + _.remove($scope.builds, function(s) { + if (s.id == id && s.buildName == name) { + $scope.unusedBuilds.push({ id: id, buildName: name, name: s.name }); // Add build back to unused builds return true; } return false; @@ -54,7 +55,7 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope' * Toggles the selected the set of facets used in the comparison * @param {number} i The index of the facet in facets */ - $scope.toggleFacet = function (i) { + $scope.toggleFacet = function(i) { facets[i].active = !facets[i].active; $scope.tblUpdate = !$scope.tblUpdate; // Simple switch to trigger the table to update $scope.saved = false; @@ -64,12 +65,12 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope' * Click handler for sorting by facets in the table * @param {object} e Event object */ - $scope.handleClick = function (e) { + $scope.handleClick = function(e) { var elem = angular.element(e.target); - if(elem.attr('prop')) { // Get component ID + if (elem.attr('prop')) { // Get component ID $scope.sort(elem.attr('prop')); - } - else if (elem.attr('del')) { // Delete index + + } else if (elem.attr('del')) { // Delete index $scope.removeBuild(elem.attr('del')); } }; @@ -78,8 +79,8 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope' * Sort the comparison array based on the selected facet / ship property * @param {string} key Ship property */ - $scope.sort = function (key) { - $scope.desc = ($scope.predicate == key)? !$scope.desc : $scope.desc; + $scope.sort = function(key) { + $scope.desc = $scope.predicate == key ? !$scope.desc : $scope.desc; $scope.predicate = key; $scope.builds = $filter('orderBy')($scope.builds, $scope.predicate, $scope.desc); }; @@ -94,12 +95,12 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope' } var selectedFacets = []; facets.forEach(function(f) { - if(f.active) { + if (f.active) { selectedFacets.unshift(f.index); } }); Persist.saveComparison($scope.name, $scope.builds, selectedFacets); - $state.go('compare', {name: $scope.name}, {location:'replace', notify:false}); + $state.go('compare', { name: $scope.name }, { location: 'replace', notify: false }); $scope.saved = true; }; @@ -108,7 +109,7 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope' */ $scope.delete = function() { Persist.deleteComparison($scope.name); - $state.go('compare', {name: null}, {location:'replace', reload:true}); + $state.go('compare', { name: null }, { location: 'replace', reload: true }); }; /** @@ -134,7 +135,7 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope' */ $scope.permalink = function(e) { e.stopPropagation(); - $state.go('modal.link', {url: genPermalink()}); + $state.go('modal.link', { url: genPermalink() }); }; /** @@ -147,14 +148,14 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope' e.stopPropagation(); // Make a request to goo.gl to shorten the URL, returns a promise var promise = Utils.shortenUrl( genPermalink()).then( - function (shortUrl) { + function(shortUrl) { return Utils.comparisonBBCode(facets, $scope.builds, shortUrl); }, - function (e) { - return 'Error - ' + e.statusText; + function(err) { + return 'Error - ' + err.statusText; } ); - $state.go('modal.export', {promise: promise, title:'Forum BBCode'}); + $state.go('modal.export', { promise: promise, title: 'Forum BBCode' }); }; /** @@ -164,7 +165,7 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope' function genPermalink() { var selectedFacets = []; facets.forEach(function(f) { - if(f.active) { + if (f.active) { selectedFacets.unshift(f.index); } }); @@ -175,7 +176,7 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope' $scope.predicate, $scope.desc ); - return $state.href('comparison', {code: code}, {absolute:true}); + return $state.href('comparison', { code: code }, { absolute: true }); } /* Event listeners */ @@ -184,7 +185,6 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope' }); /* Initialization */ - var shipId, buildName, comparisonData; if ($scope.compareMode) { if ($scope.name == 'all') { for (shipId in Persist.builds) { @@ -195,13 +195,13 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope' } else { for (shipId in Persist.builds) { for (buildName in Persist.builds[shipId]) { - $scope.unusedBuilds.push({id: shipId, buildName: buildName, name: Ships[shipId].properties.name}); + $scope.unusedBuilds.push({ id: shipId, buildName: buildName, name: Ships[shipId].properties.name }); } } comparisonData = Persist.getComparison($scope.name); if (comparisonData) { defaultFacets = comparisonData.facets; - comparisonData.builds.forEach(function (b) { + comparisonData.builds.forEach(function(b) { $scope.addBuild(b.shipId, b.buildName); }); $scope.saved = true; @@ -214,9 +214,9 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope' $scope.name = comparisonData.n; $scope.predicate = comparisonData.p; $scope.desc = comparisonData.d; - comparisonData.b.forEach(function (build) { + comparisonData.b.forEach(function(build) { $scope.addBuild(build.s, build.n, build.c); - if(!$scope.importObj[build.s]) { + if (!$scope.importObj[build.s]) { $scope.importObj[build.s] = {}; } $scope.importObj[build.s][build.n] = build.c; @@ -226,9 +226,9 @@ angular.module('app').controller('ComparisonController', ['lodash', '$rootScope' } } // Replace fmt with actual format function as defined in rootScope and retain original index - facets.forEach(function(f,i) { f.fmt = $rootScope[f.fmt]; f.index = i; }); + facets.forEach(function(f, i) { f.fmt = $rootScope[f.fmt]; f.index = i; }); // Remove default facets, mark as active, and add them back in selected order - _.pullAt(facets, defaultFacets).forEach(function (f) { f.active = true; facets.unshift(f); }); + _.pullAt(facets, defaultFacets).forEach(function(f) { f.active = true; facets.unshift(f); }); $scope.builds = $filter('orderBy')($scope.builds, $scope.predicate, $scope.desc); -}]); \ No newline at end of file +}]); diff --git a/app/js/controllers/controller-delete.js b/app/js/controllers/controller-delete.js index 2d500916..5aec906e 100755 --- a/app/js/controllers/controller-delete.js +++ b/app/js/controllers/controller-delete.js @@ -1,7 +1,7 @@ -angular.module('app').controller('DeleteController', ['$scope', 'Persist', function ($scope, Persist) { - $scope.deleteAll = function () { +angular.module('app').controller('DeleteController', ['$scope', 'Persist', function($scope, Persist) { + $scope.deleteAll = function() { Persist.deleteAll(); $scope.$parent.dismiss(); }; -}]); \ No newline at end of file +}]); diff --git a/app/js/controllers/controller-error.js b/app/js/controllers/controller-error.js index 4274c89a..b1f19cf1 100755 --- a/app/js/controllers/controller-error.js +++ b/app/js/controllers/controller-error.js @@ -1,5 +1,5 @@ angular.module('app') -.controller('ErrorController', ['$window','$rootScope','$scope','$stateParams', '$location', function ($window, $rootScope, $scope, $p, $location) { +.controller('ErrorController', ['$window', '$rootScope', '$scope', '$stateParams', '$location', function($window, $rootScope, $scope, $p, $location) { $rootScope.title = 'Error'; $scope.path = $location.path(); $scope.type = $p.type || 'unknown'; @@ -21,9 +21,9 @@ angular.module('app') $scope.details = $p.details; break; default: - $scope.msgPre = "Uh, Jameson, we have a problem.."; + $scope.msgPre = 'Uh, Jameson, we have a problem..'; $scope.errorMessage = $p.message; $scope.details = $p.details; } -}]); \ No newline at end of file +}]); diff --git a/app/js/controllers/controller-export.js b/app/js/controllers/controller-export.js index dd9c44f6..bc10c3f3 100755 --- a/app/js/controllers/controller-export.js +++ b/app/js/controllers/controller-export.js @@ -1,18 +1,18 @@ -angular.module('app').controller('ExportController', ['$scope', '$stateParams', function ($scope, $stateParams) { +angular.module('app').controller('ExportController', ['$scope', '$stateParams', function($scope, $stateParams) { $scope.title = $stateParams.title || 'Export'; if ($stateParams.promise) { $scope.export = 'Generating...'; - $stateParams.promise.then(function(data){ + $stateParams.promise.then(function(data) { $scope.export = data; }); } else { $scope.export = angular.toJson($stateParams.data, true); } - $scope.onTextClick = function ($event) { + $scope.onTextClick = function($event) { $event.target.select(); }; -}]); \ No newline at end of file +}]); diff --git a/app/js/controllers/controller-import.js b/app/js/controllers/controller-import.js index f1971a7a..88f10620 100755 --- a/app/js/controllers/controller-import.js +++ b/app/js/controllers/controller-import.js @@ -1,4 +1,4 @@ -angular.module('app').controller('ImportController', ['$scope', '$stateParams', 'ShipsDB', 'Ship', 'Persist', 'Serializer', function ($scope, $stateParams, Ships, Ship, Persist, Serializer) { +angular.module('app').controller('ImportController', ['$scope', '$stateParams', 'ShipsDB', 'Ship', 'Persist', 'Serializer', function($scope, $stateParams, Ships, Ship, Persist, Serializer) { $scope.jsonValid = false; $scope.importData = null; $scope.errorMsg = null; @@ -21,7 +21,7 @@ angular.module('app').controller('ImportController', ['$scope', '$stateParams', return; } - if(typeof importObj != 'object') { + if (typeof importObj != 'object') { $scope.errorMsg = 'Must be an object!'; return; } @@ -48,7 +48,7 @@ angular.module('app').controller('ImportController', ['$scope', '$stateParams', } } } else { - $scope.errorMsg = '"' + shipId + '" is not a valid Ship Id!'; + $scope.errorMsg = '"' + shipId + '"" is not a valid Ship Id!'; return; } $scope.builds = importObj.builds; @@ -57,7 +57,7 @@ angular.module('app').controller('ImportController', ['$scope', '$stateParams', $scope.jsonValid = true; }; - $scope.hasBuild = function (shipId, name) { + $scope.hasBuild = function(shipId, name) { return Persist.getBuild(shipId, name) !== null; }; @@ -98,4 +98,4 @@ angular.module('app').controller('ImportController', ['$scope', '$stateParams', } -}]); \ No newline at end of file +}]); diff --git a/app/js/controllers/controller-link.js b/app/js/controllers/controller-link.js index 75041907..59c4690c 100755 --- a/app/js/controllers/controller-link.js +++ b/app/js/controllers/controller-link.js @@ -1,16 +1,16 @@ -angular.module('app').controller('LinkController', ['$scope', 'Utils', '$stateParams', function ($scope, Utils, $stateParams) { +angular.module('app').controller('LinkController', ['$scope', 'Utils', '$stateParams', function($scope, Utils, $stateParams) { $scope.url = $stateParams.url; $scope.shortenedUrl = 'Shortening...'; - $scope.onTextClick = function ($event) { + $scope.onTextClick = function($event) { $event.target.select(); }; Utils.shortenUrl($scope.url) .then(function(url) { $scope.shortenedUrl = url; - },function(e) { + }, function(e) { $scope.shortenedUrl = 'Error - ' + e.statusText; }); -}]); \ No newline at end of file +}]); diff --git a/app/js/controllers/controller-modal.js b/app/js/controllers/controller-modal.js index 203d6155..c73135ad 100755 --- a/app/js/controllers/controller-modal.js +++ b/app/js/controllers/controller-modal.js @@ -1,9 +1,9 @@ -angular.module('app').controller('ModalController', ['$rootScope','$scope', '$state', function ($rootScope, $scope, $state) { +angular.module('app').controller('ModalController', ['$rootScope', '$scope', '$state', function($rootScope, $scope, $state) { $scope.dismiss = function() { if ($rootScope.prevState) { var state = $rootScope.prevState; - $state.go(state.name, state.params, {location: 'replace', reload: false}); + $state.go(state.name, state.params, { location: 'replace', reload: false }); } else { $state.go('shipyard'); } @@ -11,4 +11,4 @@ angular.module('app').controller('ModalController', ['$rootScope','$scope', '$st $scope.$on('close', $scope.dismiss); -}]); \ No newline at end of file +}]); diff --git a/app/js/controllers/controller-outfit.js b/app/js/controllers/controller-outfit.js index 1159e4a1..91445217 100755 --- a/app/js/controllers/controller-outfit.js +++ b/app/js/controllers/controller-outfit.js @@ -1,4 +1,4 @@ -angular.module('app').controller('OutfitController', ['$window','$rootScope','$scope', '$state', '$stateParams', 'ShipsDB', 'Ship', 'Components', 'Serializer', 'Persist', function ($window, $rootScope, $scope, $state, $p, Ships, Ship, Components, Serializer, Persist) { +angular.module('app').controller('OutfitController', ['$window', '$rootScope', '$scope', '$state', '$stateParams', 'ShipsDB', 'Ship', 'Components', 'Serializer', 'Persist', 'calcTotalRange', 'calcSpeed', function($window, $rootScope, $scope, $state, $p, Ships, Ship, Components, Serializer, Persist, calcTotalRange, calcSpeed) { var data = Ships[$p.shipId]; // Retrieve the basic ship properties, slots and defaults var ship = new Ship($p.shipId, data.properties, data.slots); // Create a new Ship instance var win = angular.element($window); // Angularized window object for event triggering @@ -12,7 +12,7 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s } $scope.buildName = $p.bn; - $rootScope.title = ship.name + ($scope.buildName? ' - ' + $scope.buildName : ''); + $rootScope.title = ship.name + ($scope.buildName ? ' - ' + $scope.buildName : ''); $scope.ship = ship; $scope.pp = ship.common[0]; // Power Plant $scope.th = ship.common[1]; // Thruster @@ -39,8 +39,7 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s $scope.jrSeries = { xMin: 0, xMax: ship.cargoCapacity, - // Slightly higher than actual based bacuse components are excluded - yMax: ship.jumpRangeWithMass(ship.unladenMass), + yMax: ship.unladenRange, yMin: 0, func: function(cargo) { // X Axis is Cargo return ship.jumpRangeWithMass(ship.unladenMass + $scope.fuel + cargo, $scope.fuel); @@ -49,15 +48,59 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s $scope.jrChart = { labels: { xAxis: { - title:'Cargo', + title: 'Cargo', unit: 'T' }, yAxis: { - title:'Jump Range', + title: 'Jump Range', unit: 'LY' } - }, - watch: $scope.fsd + } + }; + + $scope.trSeries = { + xMin: 0, + xMax: ship.cargoCapacity, + yMax: ship.unladenTotalRange, + yMin: 0, + func: function(cargo) { // X Axis is Cargo + return calcTotalRange(ship.unladenMass + cargo, $scope.fsd.c, $scope.fuel); + } + }; + $scope.trChart = { + labels: { + xAxis: { + title: 'Cargo', + unit: 'T' + }, + yAxis: { + title: 'Total Range', + unit: 'LY' + } + } + }; + + $scope.speedSeries = { + xMin: 0, + xMax: ship.cargoCapacity, + yMax: 500, + yMin: 0, + series: ['speed', 'boost'], + func: function(cargo) { // X Axis is Cargo + return calcSpeed(ship.unladenMass + $scope.fuel + cargo, ship.speed, ship.boost, $scope.th.c); + } + }; + $scope.speedChart = { + labels: { + xAxis: { + title: 'Cargo', + unit: 'T' + }, + yAxis: { + title: 'Speed', + unit: 'm/s' + } + } }; /** @@ -83,14 +126,14 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s * @param {[type]} slot The slot object belonging to the ship instance * @param {[type]} e The event object */ - $scope.select = function(type, slot, e) { + $scope.select = function(type, slot, e, id) { e.stopPropagation(); - var id = angular.element(e.target).attr('cpid'); // Get component ID + id = id || angular.element(e.target).attr('cpid'); // Get component ID if (id) { if (id == 'empty') { ship.use(slot, null, null); - } else if(type == 'h') { + } else if (type == 'h') { ship.use(slot, id, Components.hardpoints(id)); } else if (type == 'c') { ship.use(slot, id, Components.common(ship.common.indexOf(slot), id)); @@ -100,8 +143,7 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s ship.useBulkhead(id); } $scope.selectedSlot = null; - $scope.code = Serializer.fromShip(ship); - updateState(); + updateState(Serializer.fromShip(ship)); } }; @@ -111,11 +153,24 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s $scope.reloadBuild = function() { if ($scope.buildName && $scope.savedCode) { Serializer.toShip(ship, $scope.savedCode); // Repopulate with components from last save - $scope.code = $scope.savedCode; - updateState(); + updateState($scope.savedCode); } }; + /** + * Strip ship to D-class and no other components. + */ + $scope.stripBuild = function() { + for (var i = 0, l = ship.common.length - 1; i < l; i++) { // All except Fuel Tank + var id = ship.common[i].maxClass + 'D'; + ship.use(ship.common[i], id, Components.common(i, id)); + } + ship.hardpoints.forEach(function(slot) { ship.use(slot, null, null); }); + ship.internal.forEach(function(slot) { ship.use(slot, null, null); }); + ship.useBulkhead(0); + updateState(Serializer.fromShip(ship)); + }; + /** * Save the current build. Will replace the saved build if there is one * for this ship & with the exact name. @@ -132,7 +187,7 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s if ($scope.code != $scope.savedCode) { Persist.saveBuild(ship.id, $scope.buildName, $scope.code); $scope.savedCode = $scope.code; - updateState(); + updateState($scope.code); } }; @@ -142,13 +197,13 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s */ $scope.deleteBuild = function() { Persist.deleteBuild(ship.id, $scope.buildName); - $state.go('outfit', {shipId: ship.id, code: null, bn: null}, {location:'replace', reload:true}); + $state.go('outfit', { shipId: ship.id, code: null, bn: null }, { location: 'replace', reload: true }); }; /** * On build name change, retrieve the existing saved code if there is one */ - $scope.bnChange = function(){ + $scope.bnChange = function() { $scope.savedCode = Persist.getBuild(ship.id, $scope.buildName); }; @@ -165,13 +220,13 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s * @param {[type]} key [description] * @return {[type]} [description] */ - $scope.sortCost = function (key) { - $scope.costDesc = ($scope.costPredicate == key)? !$scope.costDesc : $scope.costDesc; + $scope.sortCost = function(key) { + $scope.costDesc = $scope.costPredicate == key ? !$scope.costDesc : $scope.costDesc; $scope.costPredicate = key; }; - $scope.sortPwr = function (key) { - $scope.pwrDesc = ($scope.pwrPredicate == key)? !$scope.pwrDesc : $scope.pwrDesc; + $scope.sortPwr = function(key) { + $scope.pwrDesc = $scope.pwrPredicate == key ? !$scope.pwrDesc : $scope.pwrDesc; $scope.pwrPredicate = key; }; @@ -181,49 +236,47 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s */ $scope.togglePwr = function(c) { ship.setSlotEnabled(c, !c.enabled); - $scope.code = Serializer.fromShip(ship); - updateState(); + updateState(Serializer.fromShip(ship)); }; - $scope.incPriority = function (c) { + $scope.incPriority = function(c) { if (ship.changePriority(c, c.priority + 1)) { - $scope.code = Serializer.fromShip(ship); - updateState(); + updateState(Serializer.fromShip(ship)); } }; - $scope.decPriority = function (c) { + $scope.decPriority = function(c) { if (ship.changePriority(c, c.priority - 1)) { - $scope.code = Serializer.fromShip(ship); - updateState(); + updateState(Serializer.fromShip(ship)); } }; - $scope.fuelChange = function (fuel) { + $scope.fuelChange = function(fuel) { $scope.fuel = fuel; win.triggerHandler('render'); }; - $scope.statusRetracted = function (slot) { + $scope.statusRetracted = function(slot) { return ship.getSlotStatus(slot, false); }; - $scope.statusDeployed = function (slot) { + $scope.statusDeployed = function(slot) { return ship.getSlotStatus(slot, true); }; // Utilify functions - function updateState() { - $state.go('outfit', {shipId: ship.id, code: $scope.code, bn: $scope.buildName}, {location:'replace', notify:false}); - $scope.jrSeries.xMax = ship.cargoCapacity; - $scope.jrSeries.yMax = ship.jumpRangeWithMass(ship.unladenMass); - $scope.jrSeries.mass = ship.unladenMass; + function updateState(code) { + $scope.code = code; + $state.go('outfit', { shipId: ship.id, code: $scope.code, bn: $scope.buildName }, { location: 'replace', notify: false }); + $scope.speedSeries.xMax = $scope.trSeries.xMax = $scope.jrSeries.xMax = ship.cargoCapacity; + $scope.jrSeries.yMax = ship.unladenRange; + $scope.trSeries.yMax = ship.unladenTotalRange; win.triggerHandler('pwrchange'); } // Hide any open menu/slot/etc if the background is clicked - $scope.$on('close', function () { + $scope.$on('close', function() { $scope.selectedSlot = null; }); diff --git a/app/js/controllers/controller-shipyard.js b/app/js/controllers/controller-shipyard.js index 78751edc..1cc5477f 100755 --- a/app/js/controllers/controller-shipyard.js +++ b/app/js/controllers/controller-shipyard.js @@ -1,4 +1,4 @@ -angular.module('app').controller('ShipyardController', ['$rootScope', 'ShipsDB', function ($rootScope, ships) { +angular.module('app').controller('ShipyardController', ['$rootScope', 'ShipsDB', function($rootScope, ships) { $rootScope.title = 'Coriolis'; $rootScope.ships = ships; -}]); \ No newline at end of file +}]); diff --git a/app/js/directives/directive-area-chart.js b/app/js/directives/directive-area-chart.js index a7222eb7..0ade7b93 100755 --- a/app/js/directives/directive-area-chart.js +++ b/app/js/directives/directive-area-chart.js @@ -1,9 +1,7 @@ -angular.module('app').directive('areaChart', ['$window', function ($window) { - - +angular.module('app').directive('areaChart', ['$window', function($window) { return { restrict: 'A', - scope:{ + scope: { config: '=', series: '=' }, @@ -11,64 +9,87 @@ angular.module('app').directive('areaChart', ['$window', function ($window) { var series = scope.series, config = scope.config, labels = config.labels, - margin = {top: 15, right: 15, bottom: 35, left: 60}, + margin = { top: 15, right: 15, bottom: 35, left: 60 }, fmt = d3.format('.3r'), fmtLong = d3.format('.2f'), func = series.func, drag = d3.behavior.drag(), dragging = false, // Define Axes - xAxis = d3.svg.axis().outerTickSize(0).orient("bottom").tickFormat(d3.format('.2r')), - yAxis = d3.svg.axis().ticks(6).outerTickSize(0).orient("left").tickFormat(fmt), + xAxis = d3.svg.axis().outerTickSize(0).orient('bottom').tickFormat(d3.format('.2r')), + yAxis = d3.svg.axis().ticks(6).outerTickSize(0).orient('left').tickFormat(fmt), x = d3.scale.linear(), - y = d3.scale.linear(); + y = d3.scale.linear(), + data = []; // Create chart - var svg = d3.select(element[0]).append("svg"); - var vis = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + var svg = d3.select(element[0]).append('svg'); + var vis = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); // Define Area var area = d3.svg.area(); - var gradient = vis.append("defs") - .append("linearGradient") - .attr("id", "gradient") - .attr("x1", "0%").attr("y1", "0%") - .attr("x2", "100%").attr("y2", "100%") - .attr("spreadMethod", "pad"); - gradient.append("stop") - .attr("offset", "0%") - .attr("stop-color", "#ff8c0d") - .attr("stop-opacity", 1); - gradient.append("stop") - .attr("offset", "100%") - .attr("stop-color", "#ff3b00") - .attr("stop-opacity", 1); + var gradient = vis.append('defs') + .append('linearGradient') + .attr('id', 'gradient') + .attr('x1', '0%').attr('y1', '0%') + .attr('x2', '100%').attr('y2', '100%') + .attr('spreadMethod', 'pad'); + gradient.append('stop') + .attr('offset', '0%') + .attr('stop-color', '#ff8c0d') + .attr('stop-opacity', 1); + gradient.append('stop') + .attr('offset', '100%') + .attr('stop-color', '#ff3b00') + .attr('stop-opacity', 1); // Create Y Axis SVG Elements - var yTxt = vis.append("g").attr("class", "y axis") - .append("text") - .attr("transform", "rotate(-90)") - .attr("y", -50) - .attr("dy", ".1em") - .style("text-anchor", "middle") + var yTxt = vis.append('g').attr('class', 'y axis') + .append('text') + .attr('transform', 'rotate(-90)') + .attr('y', -50) + .attr('dy', '.1em') + .style('text-anchor', 'middle') .text(labels.yAxis.title + ' (' + labels.yAxis.unit + ')'); // Create X Axis SVG Elements - var xLbl = vis.append("g").attr("class", "x axis"); - var xTxt = xLbl.append("text") - .attr("y", 30) - .attr("dy", ".1em") - .style("text-anchor", "middle") + var xLbl = vis.append('g').attr('class', 'x axis'); + var xTxt = xLbl.append('text') + .attr('y', 30) + .attr('dy', '.1em') + .style('text-anchor', 'middle') .text(labels.xAxis.title + ' (' + labels.xAxis.unit + ')'); // Create and Add tooltip - var tip = vis.append("g").style("display", "none"); - tip.append("rect").attr("width","4em").attr("height", "2em").attr("x", "0.5em").attr("y","-1em").attr("class","tip"); - tip.append("circle") - .attr("class", "marker") - .attr("r", 4); - tip.append("text").attr("class", 'label x').attr("y", "-0.25em"); - tip.append("text").attr("class", 'label y').attr("y", '0.85em'); + var tip = vis.append('g').style('display', 'none'); + tip.append('rect').attr('width', '4.5em').attr('height', '2em').attr('x', '0.5em').attr('y', '-1em').attr('class', 'tip'); + tip.append('circle') + .attr('class', 'marker') + .attr('r', 4); + tip.append('text').attr('class', 'label x').attr('y', '-0.25em'); + tip.append('text').attr('class', 'label y').attr('y', '0.85em'); + + vis.insert('path', ':first-child') // Area/Path to appear behind everything else + .data([data]) + .attr('class', 'area') + .attr('fill', 'url(#gradient)') + .attr('d', area) + .on('mouseover', showTip) + .on('mouseout', hideTip) + .on('mousemove', moveTip) + .call(drag); + + drag + .on('dragstart', function() { + dragging = true; + moveTip.call(this); + showTip(); + }) + .on('dragend', function() { + dragging = false; + hideTip(); + }) + .on('drag', moveTip); /** * Watch for changes in the series data (mass changes, etc) @@ -80,75 +101,55 @@ angular.module('app').directive('areaChart', ['$window', function ($window) { var width = element[0].parentElement.offsetWidth, height = width * 0.5, w = width - margin.left - margin.right, - h = height - margin.top - margin.bottom, - data = []; + h = height - margin.top - margin.bottom; + + data.length = 0; // Reset Data array if (series.xMax == series.xMin) { var yVal = func(series.xMin); data.push([ series.xMin, yVal ]); data.push([ series.xMin, yVal ]); - area.x(function(d,i) { return i * w; }).y0(h).y1(function(d) { return y(d[1]); }); + area.x(function(d, i) { return i * w; }).y0(h).y1(function(d) { return y(d[1]); }); } else { - for (var d = series.xMin; d <= series.xMax; d += 1) { - data.push([ d, func(d) ]); + for (var val = series.xMin; val <= series.xMax; val += 1) { + data.push([ val, func(val) ]); } area.x(function(d) { return x(d[0]); }).y0(h).y1(function(d) { return y(d[1]); }); } // Update Chart Size - svg.attr("width", width).attr("height", height); - // Update domain and scale for axes; + svg.attr('width', width).attr('height', height); + // Update domain and scale for axes x.range([0, w]).domain([series.xMin, series.xMax]).clamp(true); xAxis.scale(x); - xLbl.attr("transform", "translate(0," + h + ")"); - xTxt.attr("x", w/2); + xLbl.attr('transform', 'translate(0,' + h + ')'); + xTxt.attr('x', w / 2); y.range([h, 0]).domain([series.yMin, series.yMax]); yAxis.scale(y); - yTxt.attr("x", -h/2); - vis.selectAll(".y.axis").call(yAxis); - vis.selectAll(".x.axis").call(xAxis); + yTxt.attr('x', -h / 2); + vis.selectAll('.y.axis').call(yAxis); + vis.selectAll('.x.axis').call(xAxis); - // Remove existing elements - vis.selectAll('path.area').remove(); - - vis.insert("path",':first-child') // Area/Path to appear behind everything else - .datum(data) - .attr("class", "area") - .attr('fill', 'url(#gradient)') - .attr("d", area) - .on('mouseover', showTip) - .on('mouseout', hideTip) - .on('mousemove', moveTip) - .call(drag); - - drag - .on('dragstart', function() { - dragging = true; - moveTip.call(this); - showTip(); - }) - .on("dragend", function() { - dragging = false; - hideTip(); - }) - .on('drag', moveTip); + vis.selectAll('path.area') // Area/Path to appear behind everything else + .data([data]) + .attr('d', area); } function showTip() { - tip.style("display", null); + tip.style('display', null); } function hideTip() { if (!dragging) { - tip.style("display", "none"); + tip.style('display', 'none'); } } function moveTip() { - var xPos = d3.mouse(this)[0], x0 = x.invert(xPos), y0 = func(x0), flip = (x0 / x.domain()[1] > 0.75); - tip.attr("transform", "translate(" + x(x0) + "," + y(y0) + ")"); - tip.selectAll('rect').attr("x", flip? '-4.5em' : "0.5em").style("text-anchor", flip? 'end' : 'start'); - tip.selectAll('text.label').attr("x", flip? "-1em" : "1em").style("text-anchor", flip? 'end' : 'start'); + var xPos = d3.mouse(this)[0], x0 = x.invert(xPos), y0 = func(x0), flip = (x0 / x.domain()[1] > 0.65); + tip.attr('transform', 'translate(' + x(x0) + ',' + y(y0) + ')'); + tip.selectAll('rect').attr('x', flip ? '-5.75em' : '0.5em').style('text-anchor', flip ? 'end' : 'start'); + tip.selectAll('text.label').attr('x', flip ? '-2em' : '1em').style('text-anchor', flip ? 'end' : 'start'); tip.select('text.label.x').text(fmtLong(x0) + ' ' + labels.xAxis.unit); tip.select('text.label.y').text(fmtLong(y0) + ' ' + labels.yAxis.unit); } @@ -159,4 +160,4 @@ angular.module('app').directive('areaChart', ['$window', function ($window) { } }; -}]); \ No newline at end of file +}]); diff --git a/app/js/directives/directive-bar-chart.js b/app/js/directives/directive-bar-chart.js index da5e91a0..eab99af9 100755 --- a/app/js/directives/directive-bar-chart.js +++ b/app/js/directives/directive-bar-chart.js @@ -1,10 +1,10 @@ -angular.module('app').directive('barChart', ['$window', function ($window) { +angular.module('app').directive('barChart', ['$window', function($window) { - function bName (build) { + function bName(build) { return build.buildName + '\n' + build.name; } - var insertLinebreaks = function (d) { + function insertLinebreaks(d) { var el = d3.select(this); var words = d.split('\n'); el.text('').attr('y', -6); @@ -14,11 +14,11 @@ angular.module('app').directive('barChart', ['$window', function ($window) { tspan.attr('x', -9).attr('dy', 12); } } - }; + } return { restrict: 'A', - scope:{ + scope: { data: '=', facet: '=' }, @@ -28,7 +28,7 @@ angular.module('app').directive('barChart', ['$window', function ($window) { fmt = scope.facet.fmt, properties = scope.facet.props, unit = scope.facet.unit, - margin = {top: 10, right: 20, bottom: 35, left: 150}, + margin = { top: 10, right: 20, bottom: 35, left: 150 }, y0 = d3.scale.ordinal(), y1 = d3.scale.ordinal(), x = d3.scale.linear(), @@ -43,7 +43,7 @@ angular.module('app').directive('barChart', ['$window', function ($window) { var tip = d3.tip() .attr('class', 'd3-tip') .html(function(property, propertyIndex) { - return (labels? (labels[propertyIndex] + ': ') : '') + fmt(property.value) + ' ' + unit; + return (labels ? (labels[propertyIndex] + ': ') : '') + fmt(property.value) + ' ' + unit; }); vis.call(tip); @@ -52,13 +52,13 @@ angular.module('app').directive('barChart', ['$window', function ($window) { vis.append('g').attr('class', 'y axis'); vis.selectAll('g.y.axis g text').each(insertLinebreaks); // Create X Axis SVG Elements - var xAxisLbl = vis.append('g') + var xAxisLbl = vis.append('g') .attr('class', 'x axis') .append('text') .attr('y', 30) .attr('dy', '.1em') .style('text-anchor', 'middle') - .text(scope.facet.title + (unit? (' (' + unit + ')') : '')); + .text(scope.facet.title + (unit ? (' (' + unit + ')') : '')); /** @@ -84,11 +84,11 @@ angular.module('app').directive('barChart', ['$window', function ($window) { // Update X & Y Axis x.range([0, w]).domain([0, maxVal]); - y0.domain(data.map(bName)).rangeRoundBands([0, h],0.3); + y0.domain(data.map(bName)).rangeRoundBands([0, h], 0.3); y1.domain(properties).rangeRoundBands([0, y0.rangeBand()]); vis.selectAll('.y.axis').call(yAxis); vis.selectAll('.x.axis').attr('transform', 'translate(0,' + h + ')').call(xAxis); - xAxisLbl.attr('x', w/2); + xAxisLbl.attr('x', w / 2); // Update Y-Axis labels vis.selectAll('g.y.axis g text').each(insertLinebreaks); @@ -102,13 +102,13 @@ angular.module('app').directive('barChart', ['$window', function ($window) { .data(function(build) { var o = []; for (var i = 0; i < properties.length; i++) { - o.push({name: properties[i], value:build[properties[i]]}); + o.push({ name: properties[i], value: build[properties[i]] }); } return o; }) .enter().append('rect') .attr('height', y1.rangeBand()) - .attr('x',0) + .attr('x', 0) .attr('y', function(d) {return y1(d.name); }) .attr('width', function(d) { return x(d.value); }) .on('mouseover', tip.show) @@ -124,4 +124,4 @@ angular.module('app').directive('barChart', ['$window', function ($window) { } }; -}]); \ No newline at end of file +}]); diff --git a/app/js/directives/directive-comparison-table.js b/app/js/directives/directive-comparison-table.js index a47550a1..7219b988 100755 --- a/app/js/directives/directive-comparison-table.js +++ b/app/js/directives/directive-comparison-table.js @@ -1,4 +1,4 @@ -angular.module('app').directive('comparisonTable', ['$state', function ($state) { +angular.module('app').directive('comparisonTable', ['$state', function($state) { function tblHeader(facets) { var r1 = ['ShipBuild']; @@ -8,17 +8,17 @@ angular.module('app').directive('comparisonTable', ['$state', function ($state) var f = facets[i]; var p = f.props; var pl = p.length; - r1.push('' , f.lbls[j], ''); + r2.push('', f.lbls[j], ''); } } - r1.push('>', f.title ,''); + r1.push('>', f.title, ''); } } r1.push(''); @@ -30,16 +30,16 @@ angular.module('app').directive('comparisonTable', ['$state', function ($state) function tblBody(facets, builds) { var body = []; - if(builds.length === 0) { + if (builds.length === 0) { return 'No builds added to comparison!'); - var href = $state.href('outfit',{shipId: b.id, code: b.code, bn: b.buildName}); - body.push('', b.name,''); - body.push('', b.buildName,''); + var href = $state.href('outfit', { shipId: b.id, code: b.code, bn: b.buildName }); + body.push('', b.name, ''); + body.push('', b.buildName, ''); for (var j = 0, fl = facets.length; j < fl; j++) { if (facets[j].active) { @@ -59,13 +59,13 @@ angular.module('app').directive('comparisonTable', ['$state', function ($state) return { restrict: 'A', - link: function (scope, element) { + link: function(scope, element) { var header = angular.element(''); var body = angular.element(''); element.append(header); element.append(body); - var updateAll = function (){ + var updateAll = function() { header.html(tblHeader(scope.facets)); body.html(tblBody(scope.facets, scope.builds)); }; @@ -77,4 +77,4 @@ angular.module('app').directive('comparisonTable', ['$state', function ($state) }); } }; -}]); \ No newline at end of file +}]); diff --git a/app/js/directives/directive-component-select.js b/app/js/directives/directive-component-select.js index 2f0b851b..b4070927 100755 --- a/app/js/directives/directive-component-select.js +++ b/app/js/directives/directive-component-select.js @@ -1,4 +1,4 @@ -angular.module('app').directive('componentSelect', function () { +angular.module('app').directive('componentSelect', function() { // Generting the HTML in this manner is MUCH faster than using an angular template. @@ -8,42 +8,42 @@ angular.module('app').directive('componentSelect', function () { var o = opts[i]; var id = o.id || (o.class + o.rating); // Common components' ID is their class and rating - if(i > 0 && opts.length > 3 && o.class != prevClass && (!o.grp || o.rating != prevRating || o.mode)) { + if (i > 0 && opts.length > 3 && o.class != prevClass && (!o.grp || o.rating != prevRating || o.mode)) { list.push('
'); } - list.push('
  • o.maxmass)? ' disabled"' : '" cpid="', id, '">'); + list.push((o.maxmass && mass > o.maxmass) ? ' disabled"' : '" cpid="', id, '">'); - if(o.mode) { - list.push(' '); + if (o.mode) { + list.push(' '); } list.push(o.class, o.rating); - if(o.missile) { + if (o.missile) { list.push('/' + o.missile); } - if(o.name) { + if (o.name) { list.push(' ' + o.name); } list.push('
  • '); prevClass = o.class; - prevRating= o.rating; + prevRating = o.rating; } } return { restrict: 'A', - scope:{ + scope: { opts: '=', // Component Options object groups: '=', // Groups of Component Options mass: '=', // Current ship unladen mass @@ -57,13 +57,13 @@ angular.module('app').directive('componentSelect', function () { var groups = scope.groups; var mass = scope.mass || 0; - if(groups) { + if (groups) { // At present time slots with grouped options (Hardpoints and Internal) can be empty list.push('
    EMPTY
    '); for (var g in groups) { var grp = groups[g]; var grpCode = grp[Object.keys(grp)[0]].grp; // Nasty operation to get the grp property of the first/any single component - list.push('
    ', g, '