From ed637addb8908c15afe732a9f1070b2eae9ea4b8 Mon Sep 17 00:00:00 2001 From: Colin McLeod Date: Fri, 13 Nov 2015 17:43:45 -0800 Subject: [PATCH 01/18] Initial Commit for React --- .gitmodules | 3 - app/index.html | 20 +- app/js/Coriolis.jsx | 55 +++ app/js/Router.js | 333 +++++++++++++ app/js/Serializer.js | 255 ++++++++++ app/js/components/Header.jsx | 118 +++++ .../directive-area-chart.js | 0 .../directive-bar-chart.js | 0 .../directive-comparison-table.js | 0 .../directive-component-select.js | 0 .../directive-header.js | 0 .../directive-line-chart.js | 0 .../directive-loader.js | 0 .../directive-power-bands.js | 0 .../directive-slider.js | 0 .../directive-slot-hardpoint.js | 0 .../directive-slot-internal.js | 0 app/js/config.js | 81 --- app/js/controllers/controller-shipyard.js | 59 --- app/js/directives/directive-context-menu.js | 14 - app/js/factory-utils.js | 65 --- app/js/i18n/Language.js | 52 ++ app/js/i18n/de.js | 438 +++++++++-------- app/js/i18n/en.js | 447 ++++++++--------- app/js/i18n/es.js | 418 ++++++++-------- app/js/i18n/fr.js | 398 ++++++++------- app/js/i18n/it.js | 260 +++++----- app/js/i18n/languages.js | 23 - app/js/i18n/ru.js | 460 +++++++++--------- app/js/{app.js => old-app.js} | 0 app/js/pages/NotFoundPage.jsx | 12 + app/js/pages/ShipyardPage.jsx | 180 +++++++ .../controller-comparison.js | 0 .../controller-delete.js | 0 .../controller-error.js | 0 .../controller-export.js | 0 .../controller-import.js | 0 .../{controllers => pages}/controller-link.js | 0 .../controller-modal.js | 0 .../controller-outfit.js | 0 app/js/provider-locale-format.js | 36 -- app/js/service-persist.js | 314 ------------ app/js/service-serializer.js | 245 ---------- app/js/shipyard/Calculations.js | 84 ++++ app/js/shipyard/Constants.js | 183 +++++++ app/js/shipyard/ModuleSet.js | 139 ++++++ app/js/shipyard/ModuleUtils.js | 175 +++++++ app/js/shipyard/{factory-ship.js => Ship.js} | 393 ++++++++------- app/js/shipyard/factory-component-set.js | 145 ------ app/js/shipyard/module-shipyard.js | 252 ---------- app/js/shipyard/service-components.js | 181 ------- app/js/stores/Persist.js | 308 ++++++++++++ app/js/utils/BBCode.js | 43 ++ app/js/utils/ShortenUrl.js | 10 + app/less/app.less | 2 +- app/views/_header.html | 91 ---- app/views/page-shipyard.html | 87 ---- bower.json | 61 --- data | 1 - package.json | 12 +- 60 files changed, 3362 insertions(+), 3091 deletions(-) delete mode 100644 .gitmodules create mode 100644 app/js/Coriolis.jsx create mode 100644 app/js/Router.js create mode 100755 app/js/Serializer.js create mode 100644 app/js/components/Header.jsx rename app/js/{directives => components}/directive-area-chart.js (100%) rename app/js/{directives => components}/directive-bar-chart.js (100%) rename app/js/{directives => components}/directive-comparison-table.js (100%) rename app/js/{directives => components}/directive-component-select.js (100%) rename app/js/{directives => components}/directive-header.js (100%) rename app/js/{directives => components}/directive-line-chart.js (100%) rename app/js/{directives => components}/directive-loader.js (100%) rename app/js/{directives => components}/directive-power-bands.js (100%) rename app/js/{directives => components}/directive-slider.js (100%) rename app/js/{directives => components}/directive-slot-hardpoint.js (100%) rename app/js/{directives => components}/directive-slot-internal.js (100%) delete mode 100755 app/js/config.js delete mode 100755 app/js/controllers/controller-shipyard.js delete mode 100644 app/js/directives/directive-context-menu.js delete mode 100755 app/js/factory-utils.js create mode 100644 app/js/i18n/Language.js delete mode 100644 app/js/i18n/languages.js rename app/js/{app.js => old-app.js} (100%) create mode 100644 app/js/pages/NotFoundPage.jsx create mode 100755 app/js/pages/ShipyardPage.jsx rename app/js/{controllers => pages}/controller-comparison.js (100%) rename app/js/{controllers => pages}/controller-delete.js (100%) rename app/js/{controllers => pages}/controller-error.js (100%) rename app/js/{controllers => pages}/controller-export.js (100%) rename app/js/{controllers => pages}/controller-import.js (100%) rename app/js/{controllers => pages}/controller-link.js (100%) rename app/js/{controllers => pages}/controller-modal.js (100%) rename app/js/{controllers => pages}/controller-outfit.js (100%) delete mode 100644 app/js/provider-locale-format.js delete mode 100755 app/js/service-persist.js delete mode 100755 app/js/service-serializer.js create mode 100644 app/js/shipyard/Calculations.js create mode 100755 app/js/shipyard/Constants.js create mode 100755 app/js/shipyard/ModuleSet.js create mode 100755 app/js/shipyard/ModuleUtils.js rename app/js/shipyard/{factory-ship.js => Ship.js} (52%) delete mode 100755 app/js/shipyard/factory-component-set.js delete mode 100755 app/js/shipyard/module-shipyard.js delete mode 100755 app/js/shipyard/service-components.js create mode 100644 app/js/stores/Persist.js create mode 100644 app/js/utils/BBCode.js create mode 100644 app/js/utils/ShortenUrl.js delete mode 100755 app/views/_header.html delete mode 100755 app/views/page-shipyard.html delete mode 100755 bower.json delete mode 160000 data diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 16fd4b05..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "data"] - path = data - url = https://github.com/cmmcleod/coriolis-data.git diff --git a/app/index.html b/app/index.html index 4dd2af92..4094d64b 100755 --- a/app/index.html +++ b/app/index.html @@ -1,7 +1,7 @@ - + - Coriolis + Coriolis @@ -55,22 +55,15 @@
<%= svgContent %>
- -
- -
- +
- - + + <% if (uaTracking) { %> <% } %> - - \ No newline at end of file + diff --git a/app/js/Coriolis.jsx b/app/js/Coriolis.jsx new file mode 100644 index 00000000..fe00af05 --- /dev/null +++ b/app/js/Coriolis.jsx @@ -0,0 +1,55 @@ +import { Component } from 'react'; +import Router from 'Router'; +import ShipyardPage from 'pages/ShipyardPage'; +import NotFoundPage from 'pages/NotFoundPage'; +import Header from '../components/Header'; + +class Coriolis extends Component { + + constructor(props) { + super(props); + this.setPage = this.setPage.bind(this); + this.state.standAlone = isStandAlone(); + window.onerror = errorPage.bind(this); + + Router('/', () => this.setPage()); + // Router('/outfitting/:ship', outfitting); + // Router('/outfitting/:ship/:code', outfitting); + // Router('/compare/:name', compare); + // Router('/comparison/:code', comparison); + // Router('/settings', settings); + Router('*', () => this.setPage(null)); + + if (window.applicationCache) { + // Listen for appcache updated event, present refresh to update view + window.applicationCache.addEventListener('updateready', () => { + if (window.applicationCache.status == window.applicationCache.UPDATEREADY) { + // Browser downloaded a new app cache. + this.setState({appCacheUpdate: true}); + } + }, false); + } + + Router.start(); + console.log('Root page created'); + } + + setPage(page) { + this.setState({ page: page }); + } + + onError(msg, scriptUrl, line, col, errObj) { + this.setPage(
Some errors occured!!
); + } + + render() { + return ( +
+ {/*
*/} + {this.state.page || } +
+ ); + } +} + +ReactDOM.render(, document.getElementById('coriolis')); diff --git a/app/js/Router.js b/app/js/Router.js new file mode 100644 index 00000000..f4132029 --- /dev/null +++ b/app/js/Router.js @@ -0,0 +1,333 @@ +import Persist from 'stores/Persist'; + +function isStandAlone() { + try { + return window.navigator.standalone || (window.external && window.external.msIsSiteMode && window.external.msIsSiteMode()); + } catch (ex) { + return false; + } +} + +/** + * Register `path` with callback `fn()`, + * or route `path`, or `Router.start()`. + * + * Router('*', fn); + * Router('/user/:id', load, user); + * Router('/user/' + user.id, { some: 'thing' }); + * Router('/user/' + user.id); + * Router(); + * + * @param {String} path + * @param {Function} fn... + * @api public + */ +function Router(path, fn) { + var route = new Route(path); + for (var i = 1; i < arguments.length; ++i) { + Router.callbacks.push(route.middleware(arguments[i])); + } +} + +/** + * Callback functions. + */ + +Router.callbacks = []; + +/** + * Bind with the given `options`. + * + * Options: + * + * - `click` bind to click events [true] + * - `popstate` bind to popstate [true] + * - `dispatch` perform initial dispatch [true] + * + * @param {Object} options + * @api public + */ + +Router.start = function(){ + window.addEventListener('popstate', onpopstate, false); + + if (isStandAlone()) { + var state = Persist.getState(); + // If a previous state has been stored, load that state + if (state && state.name && state.params) { + Router(this.props.initialPath || '/'); + //$state.go(state.name, state.params, { location: 'replace' }); + } else { + Router('/'); + } + } else { + var url = location.pathname + location.search + location.hash; + Router.replace(url, null, true, dispatch); + } +}; + +/** + * Show `path` with optional `state` object. + * + * @param {String} path + * @param {Object} state + * @return {Context} + * @api public + */ +Router.go = function(path, state) { + gaTrack(path); + var ctx = new Context(path, state); + if (false !== dispatch) Router.dispatch(ctx); + if (!ctx.unhandled) ctx.pushState(); + return ctx; +}; + +/** + * Replace `path` with optional `state` object. + * + * @param {String} path + * @param {Object} state + * @return {Context} + * @api public + */ + +Router.replace = function(path, state, init, dispatch) { + gaTrack(path); + var ctx = new Context(path, state); + ctx.init = init; + if (null == dispatch) dispatch = true; + if (dispatch) Router.dispatch(ctx); + ctx.save(); + return ctx; +}; + +/** + * Dispatch the given `ctx`. + * + * @param {Object} ctx + * @api private + */ + +Router.dispatch = function(ctx){ + var i = 0; + + function next() { + var fn = Router.callbacks[i++]; + if (!fn) return unhandled(ctx); + fn(ctx, next); + } + + next(); +}; + +/** + * Unhandled `ctx`. When it's not the initial + * popstate then redirect. If you wish to handle + * 404s on your own use `Router('*', callback)`. + * + * @param {Context} ctx + * @api private + */ + +function unhandled(ctx) { + var current = window.location.pathname + window.location.search; + if (current == ctx.canonicalPath) return; + window.location = ctx.canonicalPath; +} + +/** + * Initialize a new "request" `Context` + * with the given `path` and optional initial `state`. + * + * @param {String} path + * @param {Object} state + * @api public + */ + +function Context(path, state) { + var i = path.indexOf('?'); + + this.canonicalPath = path; + this.path = path || '/'; + this.title = document.title; + this.state = state || {}; + this.state.path = path; + this.querystring = ~i ? path.slice(i + 1) : ''; + this.pathname = ~i ? path.slice(0, i) : path; + this.params = []; + + // fragment + this.hash = ''; + if (!~this.path.indexOf('#')) return; + var parts = this.path.split('#'); + this.path = parts[0]; + this.hash = parts[1] || ''; + this.querystring = this.querystring.split('#')[0]; +} + +/** + * Expose `Context`. + */ + +Router.Context = Context; + +/** + * Push state. + * + * @api private + */ + +Context.prototype.pushState = function(){ + history.pushState(this.state, this.title, this.canonicalPath); +}; + +/** + * Save the context state. + * + * @api public + */ + +Context.prototype.save = function(){ + history.replaceState(this.state, this.title, this.canonicalPath); +}; + +/** + * Initialize `Route` with the given HTTP `path`, + * and an array of `callbacks` and `options`. + * + * Options: + * + * - `sensitive` enable case-sensitive routes + * - `strict` enable strict matching for trailing slashes + * + * @param {String} path + * @param {Object} options. + * @api private + */ + +function Route(path, options) { + options = options || {}; + this.path = path; + this.method = 'GET'; + this.regexp = pathtoRegexp(path + , this.keys = [] + , options.sensitive + , options.strict); +} + +/** + * Expose `Route`. + */ + +Router.Route = Route; + +/** + * Return route middleware with + * the given callback `fn()`. + * + * @param {Function} fn + * @return {Function} + * @api public + */ + +Route.prototype.middleware = function(fn){ + var self = this; + return function(ctx, next){ + if (self.match(ctx.path, ctx.params)) return fn(ctx, next); + next(); + }; +}; + +/** + * Check if this route matches `path`, if so + * populate `params`. + * + * @param {String} path + * @param {Array} params + * @return {Boolean} + * @api private + */ + +Route.prototype.match = function(path, params){ + var keys = this.keys + , qsIndex = path.indexOf('?') + , pathname = ~qsIndex ? path.slice(0, qsIndex) : path + , m = this.regexp.exec(decodeURIComponent(pathname)); + + if (!m) return false; + + for (var i = 1, len = m.length; i < len; ++i) { + var key = keys[i - 1]; + + var val = 'string' == typeof m[i] + ? decodeURIComponent(m[i]) + : m[i]; + + if (key) { + params[key.name] = undefined !== params[key.name] + ? params[key.name] + : val; + } else { + params.push(val); + } + } + + return true; +}; + +function gaTrack(path) { + if (window.ga) { + window.ga('send', 'pageview', { page: path }); + } +} + +/** + * Normalize the given path string, + * returning a regular expression. + * + * An empty array should be passed, + * which will contain the placeholder + * key names. For example "/user/:id" will + * then contain ["id"]. + * + * @param {String|RegExp|Array} path + * @param {Array} keys + * @param {Boolean} sensitive + * @param {Boolean} strict + * @return {RegExp} + * @api private + */ + +function pathtoRegexp(path, keys, sensitive, strict) { + if (path instanceof RegExp) return path; + if (path instanceof Array) path = '(' + path.join('|') + ')'; + path = path + .concat(strict ? '' : '/?') + .replace(/\/\(/g, '(?:/') + .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){ + keys.push({ name: key, optional: !! optional }); + slash = slash || ''; + return '' + + (optional ? '' : slash) + + '(?:' + + (optional ? slash : '') + + (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')' + + (optional || ''); + }) + .replace(/([\/.])/g, '\\$1') + .replace(/\*/g, '(.*)'); + return new RegExp('^' + path + '$', sensitive ? '' : 'i'); +} + +/** + * Handle "populate" events. + */ + +function onpopstate(e) { + if (e.state) { + var path = e.state.path; + Router.replace(path, e.state); + } +} + +export default Router; diff --git a/app/js/Serializer.js b/app/js/Serializer.js new file mode 100755 index 00000000..33644d6c --- /dev/null +++ b/app/js/Serializer.js @@ -0,0 +1,255 @@ +import { ModuleGroupToName, MountMap } from './Constants'; +import Ships from './Ships'; +import Ship from './Ship'; +import ModuleUtils from './ModuleUtils'; +import LZString from 'LZString'; + +/** + * Service managing seralization and deserialization of models for use in URLs and persistene. + */ + +/** + * Utility function to retrieve a safe string for selected component for a slot. + * Used for serialization to code only. + * TODO: comment on binding + * @private + * @param {object} slot The slot object. + * @return {string} The id of the selected component or '-' if none selected + */ +function mapGroup(slot) { + export function enabled.push(slot.enabled ? 1 : 0); + export function priorities.push(slot.priority); + + return slot.id === null ? '-' : slot.id; +} + +function decodeToArray(code, arr, codePos) { + for (let i = 0; i < arr.length; i++) { + if (code.charAt(codePos) == '-') { + arr[i] = 0; + codePos++; + } else { + arr[i] = code.substring(codePos, codePos + 2); + codePos += 2; + } + } + return codePos; +} + +function slotToSchema(slot) { + if (slot.c) { + let o = { + class: slot.c.class, + rating: slot.c.rating, + enabled: Boolean(slot.enabled), + priority: slot.priority + 1, + group: ModuleGroupToName[slot.c.grp] + }; + + if (slot.c.name) { + o.name = slot.c.name; + } + if (slot.c.mode) { + o.mount = MountMap[slot.c.mode]; + } + if (slot.c.missile) { + o.missile = slot.c.missile; + } + return o; + } + return null; +} + + +/** + * Serializes the ships selected components for all slots to a URL friendly string. + * @param {Ship} ship The ship to be serialized. + * @return {string} Encoded string of components + */ +export function fromShip(ship) { + let power = { + enabled: [ship.cargoHatch.enabled ? 1 : 0], + priorities: [ship.cargoHatch.priority] + }; + + let data = [ + ship.bulkheads.id, + _.map(ship.standard, mapGroup, power), + _.map(ship.hardpoints, mapGroup, power), + _.map(ship.internal, mapGroup, power), + '.', + LZString.compressToBase64(power.enabled.join('')).replace(/\//g, '-'), + '.', + LZString.compressToBase64(power.priorities.join('')).replace(/\//g, '-') + ]; + + return _.flatten(data).join(''); +}; + +/** + * Updates an existing ship instance's slots with components determined by the + * code. + * + * @param {Ship} ship The ship instance to be updated + * @param {string} dataString The string to deserialize + */ +export function toShip(ship, dataString) { + var standard = new Array(ship.standard.length), + hardpoints = new Array(ship.hardpoints.length), + internal = new Array(ship.internal.length), + parts = dataString.split('.'), + priorities = null, + enabled = null, + code = parts[0]; + + if (parts[1]) { + enabled = LZString.decompressFromBase64(parts[1].replace(/-/g, '/')).split(''); + } + + if (parts[2]) { + priorities = LZString.decompressFromBase64(parts[2].replace(/-/g, '/')).split(''); + } + + decodeToArray(code, internal, decodeToArray(code, hardpoints, decodeToArray(code, standard, 1))); + + ship.buildWith( + { + bulkheads: code.charAt(0) * 1, + standard: standard, + hardpoints: hardpoints, + internal: internal + }, + priorities, + enabled + ); +}; + +export function toDetailedBuild(buildName, ship, code) { + var standard = ship.standard, + hardpoints = ship.hardpoints, + internal = ship.internal; + + var data = { + $schema: 'http://cdn.coriolis.io/schemas/ship-loadout/2.json#', + name: buildName, + ship: ship.name, + references: [{ + name: 'Coriolis.io', + url: $state.href('outfit', { shipId: ship.id, code: code, bn: buildName }, { absolute: true }), + code: code, + shipId: ship.id + }], + components: { + standard: { + bulkheads: ship.bulkheads.c.name, + cargoHatch: { enabled: Boolean(ship.cargoHatch.enabled), priority: ship.cargoHatch.priority + 1 }, + powerPlant: { class: standard[0].c.class, rating: standard[0].c.rating, enabled: Boolean(standard[0].enabled), priority: standard[0].priority + 1 }, + thrusters: { class: standard[1].c.class, rating: standard[1].c.rating, enabled: Boolean(standard[1].enabled), priority: standard[1].priority + 1 }, + frameShiftDrive: { class: standard[2].c.class, rating: standard[2].c.rating, enabled: Boolean(standard[2].enabled), priority: standard[2].priority + 1 }, + lifeSupport: { class: standard[3].c.class, rating: standard[3].c.rating, enabled: Boolean(standard[3].enabled), priority: standard[3].priority + 1 }, + powerDistributor: { class: standard[4].c.class, rating: standard[4].c.rating, enabled: Boolean(standard[4].enabled), priority: standard[4].priority + 1 }, + sensors: { class: standard[5].c.class, rating: standard[5].c.rating, enabled: Boolean(standard[5].enabled), priority: standard[5].priority + 1 }, + fuelTank: { class: standard[6].c.class, rating: standard[6].c.rating, enabled: Boolean(standard[6].enabled), priority: standard[6].priority + 1 } + }, + hardpoints: _.map(_.filter(hardpoints, function(slot) { return slot.maxClass > 0; }), slotToSchema), + utility: _.map(_.filter(hardpoints, function(slot) { return slot.maxClass === 0; }), slotToSchema), + internal: _.map(internal, slotToSchema) + }, + stats: {} + }; + + for (var stat in ship) { + if (!isNaN(ship[stat])) { + data.stats[stat] = Math.round(ship[stat] * 100) / 100; + } + } + + return data; +}; + +export function fromDetailedBuild(detailedBuild) { + var shipId = _.findKey(ShipsDB, { properties: { name: detailedBuild.ship } }); + + if (!shipId) { + throw 'No such ship: ' + detailedBuild.ship; + } + + var comps = detailedBuild.components; + var standard = comps.standard; + var priorities = [ standard.cargoHatch && standard.cargoHatch.priority !== undefined ? standard.cargoHatch.priority - 1 : 0 ]; + var enabled = [ standard.cargoHatch && standard.cargoHatch.enabled !== undefined ? standard.cargoHatch.enabled : true ]; + var shipData = ShipsDB[shipId]; + var ship = new Ship(shipId, shipData.properties, shipData.slots); + var bulkheads = ModuleUtils.bulkheadIndex(standard.bulkheads); + + if (bulkheads < 0) { + throw 'Invalid bulkheads: ' + standard.bulkheads; + } + + var standardIds = _.map( + ['powerPlant', 'thrusters', 'frameShiftDrive', 'lifeSupport', 'powerDistributor', 'sensors', 'fuelTank'], + function(c) { + if (!standard[c].class || !standard[c].rating) { + throw 'Invalid value for ' + c; + } + priorities.push(standard[c].priority === undefined ? 0 : standard[c].priority - 1); + enabled.push(standard[c].enabled === undefined ? true : standard[c].enabled); + return standard[c].class + standard[c].rating; + } + ); + + var internal = _.map(comps.internal, function(c) { return c ? ModuleUtils.findInternalId(c.group, c.class, c.rating, c.name) : 0; }); + + var hardpoints = _.map(comps.hardpoints, function(c) { + return c ? ModuleUtils.findHardpointId(c.group, c.class, c.rating, c.name, MountMap[c.mount], c.missile) : 0; + }).concat(_.map(comps.utility, function(c) { + return c ? ModuleUtils.findHardpointId(c.group, c.class, c.rating, c.name, MountMap[c.mount]) : 0; + })); + + // The ordering of these arrays must match the order in which they are read in Ship.buildWith + priorities = priorities.concat(_.map(comps.hardpoints, function(c) { return (!c || c.priority === undefined) ? 0 : c.priority - 1; }), + _.map(comps.utility, function(c) { return (!c || c.priority === undefined) ? 0 : c.priority - 1; }), + _.map(comps.internal, function(c) { return (!c || c.priority === undefined) ? 0 : c.priority - 1; })); + enabled = enabled.concat(_.map(comps.hardpoints, function(c) { return (!c || c.enabled === undefined) ? true : c.enabled * 1; }), + _.map(comps.utility, function(c) { return (!c || c.enabled === undefined) ? true : c.enabled * 1; }), + _.map(comps.internal, function(c) { return (!c || c.enabled === undefined) ? true : c.enabled * 1; })); + + ship.buildWith({ bulkheads: bulkheads, standard: standardIds, hardpoints: hardpoints, internal: internal }, priorities, enabled); + + return ship; +}; + +export function toDetailedExport(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); + export function toShip(ship, code); + data.push(export function toDetailedBuild(buildName, ship, code)); + } + } + return data; +}; + +export function fromComparison(name, builds, facets, predicate, desc) { + var shipBuilds = []; + + builds.forEach(function(b) { + shipBuilds.push({ s: b.id, n: b.buildName, c: export function fromShip(b) }); + }.bind(this)); + + return LZString.compressToBase64(angular.toJson({ + n: name, + b: shipBuilds, + f: facets, + p: predicate, + d: desc ? 1 : 0 + })).replace(/\//g, '-'); +}; + +export function toComparison(code) { + return angular.fromJson(LZString.decompressFromBase64(code.replace(/-/g, '/'))); +}; diff --git a/app/js/components/Header.jsx b/app/js/components/Header.jsx new file mode 100644 index 00000000..6f3fcda2 --- /dev/null +++ b/app/js/components/Header.jsx @@ -0,0 +1,118 @@ +import { Component } from 'react'; + +export default class Header extends Component { + + + render() { + let openedMenu = this.state.openedMenu; + if (this.props.appCacheUpdate) { + return
{ 'PHRASE_UPDATE_RDY' | translate }
; + } + + return ( +
+ + + + + + + + + +
+ ); + } + + getShipsMenu() { + return (); + } + + getBuildsMenu() { + return (); + } + + getComparisonsMenu() { + return (); + } + + getSettingsMenu() { + return (); + } + +} \ No newline at end of file diff --git a/app/js/directives/directive-area-chart.js b/app/js/components/directive-area-chart.js similarity index 100% rename from app/js/directives/directive-area-chart.js rename to app/js/components/directive-area-chart.js diff --git a/app/js/directives/directive-bar-chart.js b/app/js/components/directive-bar-chart.js similarity index 100% rename from app/js/directives/directive-bar-chart.js rename to app/js/components/directive-bar-chart.js diff --git a/app/js/directives/directive-comparison-table.js b/app/js/components/directive-comparison-table.js similarity index 100% rename from app/js/directives/directive-comparison-table.js rename to app/js/components/directive-comparison-table.js diff --git a/app/js/directives/directive-component-select.js b/app/js/components/directive-component-select.js similarity index 100% rename from app/js/directives/directive-component-select.js rename to app/js/components/directive-component-select.js diff --git a/app/js/directives/directive-header.js b/app/js/components/directive-header.js similarity index 100% rename from app/js/directives/directive-header.js rename to app/js/components/directive-header.js diff --git a/app/js/directives/directive-line-chart.js b/app/js/components/directive-line-chart.js similarity index 100% rename from app/js/directives/directive-line-chart.js rename to app/js/components/directive-line-chart.js diff --git a/app/js/directives/directive-loader.js b/app/js/components/directive-loader.js similarity index 100% rename from app/js/directives/directive-loader.js rename to app/js/components/directive-loader.js diff --git a/app/js/directives/directive-power-bands.js b/app/js/components/directive-power-bands.js similarity index 100% rename from app/js/directives/directive-power-bands.js rename to app/js/components/directive-power-bands.js diff --git a/app/js/directives/directive-slider.js b/app/js/components/directive-slider.js similarity index 100% rename from app/js/directives/directive-slider.js rename to app/js/components/directive-slider.js diff --git a/app/js/directives/directive-slot-hardpoint.js b/app/js/components/directive-slot-hardpoint.js similarity index 100% rename from app/js/directives/directive-slot-hardpoint.js rename to app/js/components/directive-slot-hardpoint.js diff --git a/app/js/directives/directive-slot-internal.js b/app/js/components/directive-slot-internal.js similarity index 100% rename from app/js/directives/directive-slot-internal.js rename to app/js/components/directive-slot-internal.js diff --git a/app/js/config.js b/app/js/config.js deleted file mode 100755 index 243e6e8d..00000000 --- a/app/js/config.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * 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) { - // Use HTML5 push and replace state if possible - $locationProvider.html5Mode({ enabled: true, requireBase: false }); - - /** - * Set up all states and their routes. - */ - $stateProvider - .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 - }, - templateUrl: 'views/page-outfit.html', - controller: 'OutfitController', - resolve: { - shipId: ['$stateParams', function($p) { // Ensure ship exists before loading controller - if (!ships[$p.shipId]) { - throw { type: 'no-ship', message: $p.shipId }; - } - }] - }, - sticky: true - }) - .state('compare', { - url: '/compare/:name', - params: { - name: { value: null, squash: true } - }, - templateUrl: 'views/page-comparison.html', - controller: 'ComparisonController', - sticky: true - }) - .state('comparison', { - url: '/comparison/:code', - templateUrl: 'views/page-comparison.html', - controller: 'ComparisonController', - 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 }) - - // 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, description: 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'); - - /** - * 404 Handler - Keep current URL/ do not redirect, change to error state. - */ - $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 }); - return $location.path; - }); - - /** - * Global Error Handler. Decorates the existing error handler such that it - * redirects uncaught errors to the error page. - * - */ - $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 }); - $delegate(err, cause); - }; - }]); - -}]); diff --git a/app/js/controllers/controller-shipyard.js b/app/js/controllers/controller-shipyard.js deleted file mode 100755 index 3ef910a4..00000000 --- a/app/js/controllers/controller-shipyard.js +++ /dev/null @@ -1,59 +0,0 @@ - angular.module('app').controller('ShipyardController', ['$rootScope', '$scope', 'ShipsDB', 'Ship', 'Components', function($rootScope, $scope, ShipsDB, Ship, Components) { - $rootScope.title = 'Coriolis - Shipyard'; - $scope.shipPredicate = 'properties.name'; - $scope.shipDesc = false; - - function countHp(slot) { - this.hp[slot.maxClass]++; - this.hpCount++; - } - - function countInt(slot) { - var crEligible = !slot.eligible || slot.eligible.cr; - this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment - this.intCount++; - this.maxCargo += crEligible ? Components.findInternal('cr', slot.maxClass, 'E').capacity : 0; - } - - function shipSummary(shipId, shipData) { - var summary = angular.copy(shipData.properties); - var ship = new Ship(shipId, shipData.properties, shipData.slots); - summary.id = s; - summary.hpCount = 0; - summary.intCount = 0; - summary.maxCargo = 0; - summary.hp = [0, 0, 0, 0, 0]; // Utility, Small, Medium, Large, Huge - summary.int = [0, 0, 0, 0, 0, 0, 0, 0]; // Sizes 1 - 8 - // Build Ship - ship.buildWith(shipData.defaults); // Populate with stock/default components - ship.hardpoints.forEach(countHp.bind(summary)); // Count Hardpoints by class - ship.internal.forEach(countInt.bind(summary)); // Count Internal Compartments by class - summary.retailCost = ship.totalCost; // Record Stock/Default/retail cost - ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range - summary.maxJumpRange = ship.unladenRange; // Record Jump Range - ship.optimizeMass({ th: ship.standard[1].maxClass + 'A' }); // Optmize mass with Max Thrusters - summary.topSpeed = ship.topSpeed; - summary.topBoost = ship.topBoost; - - return summary; - } - - /* Initialization */ - - if (!$rootScope.shipsOverview) { // Only generate this once - $rootScope.shipsOverview = []; - for (var s in ShipsDB) { - $scope.shipsOverview.push(shipSummary(s, ShipsDB[s])); - } - } - - /** - * Sort ships - * @param {object} key Sort predicate - */ - $scope.sortShips = function(key) { - $scope.shipDesc = $scope.shipPredicate == key ? !$scope.shipDesc : $scope.shipDesc; - $scope.shipPredicate = key; - }; - -}]); diff --git a/app/js/directives/directive-context-menu.js b/app/js/directives/directive-context-menu.js deleted file mode 100644 index 0004033d..00000000 --- a/app/js/directives/directive-context-menu.js +++ /dev/null @@ -1,14 +0,0 @@ -angular.module('app').directive('contextMenu', ['$parse', function($parse) { - return function(scope, element, attrs) { - var fn = $parse(attrs.contextMenu); - - element.bind('contextmenu', function(e) { - if (!e.shiftKey) { - scope.$apply(function() { - e.preventDefault(); - fn(scope, { $event: e }); - }); - } - }); - }; -}]); diff --git a/app/js/factory-utils.js b/app/js/factory-utils.js deleted file mode 100755 index 9eeba68d..00000000 --- a/app/js/factory-utils.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * BBCode Generator functions for embedding in the Elite Dangerous Forums - */ -angular.module('app').factory('Utils', ['$window', '$state', '$http', '$q', '$translate', '$rootScope', function($window, $state, $http, $q, $translate, $rootScope) { - - var shortenAPI = 'https://www.googleapis.com/urlshortener/v1/url?key='; - - function shortenUrl(url) { - if ($window.navigator.onLine) { - return $http.post(shortenAPI + GAPI_KEY, { longUrl: url }).then(function(response) { - return response.data.id; - }); - } else { - return $q.reject({ statusText: 'Not Online' }); - } - } - - function comparisonBBCode(facets, builds, link) { - var colCount = 2, b, i, j, k, f, fl, p, pl, l = []; - - for (i = 0; i < facets.length; i++) { - if (facets[i].active) { - f = facets[i]; - p = f.props; - - if (p.length == 1) { - l.push('[th][B][COLOR=#FF8C0D]', $translate.instant(f.title).toUpperCase(), '[/COLOR][/B][/th]'); - colCount++; - } else { - for (j = 0; j < p.length; j++) { - l.push('[th][B][COLOR=#FF8C0D]', $translate.instant(f.title).toUpperCase(), '\n', $translate.instant(f.lbls[j]).toUpperCase(), '[/COLOR][/B][/th]'); - colCount++; - } - } - } - } - l.push('[/tr]\n'); - - for (i = 0; i < builds.length; i++) { - b = builds[i]; - //var href = $state.href('outfit',{shipId: b.id, code: b.code, bn: b.buildName}, {absolute: true}); - l.push('[tr][td]', b.name, '[/td][td]', b.buildName, '[/td]'); - - for (j = 0, fl = facets.length; j < fl; j++) { - if (facets[j].active) { - f = facets[j]; - p = f.props; - for (k = 0, pl = p.length; k < pl; k++) { - l.push('[td="align: right"]', $rootScope[f.fmt](b[p[k]]), ' [size=-2]', $translate.instant(f.unit), '[/size][/td]'); - } - } - } - l.push('[/tr]\n'); - } - l.push('[tr][td="align: center, colspan:', colCount, '"][size=-3]\n[url=', link, ']Interactive Comparison at Coriolis.io[/url][/td][/tr]\n[/size][/table]'); - l.unshift('[table="width:', colCount * 90, ',align: center"]\n[tr][th][B][COLOR=#FF8C0D]Ship[/COLOR][/B][/th][th][B][COLOR="#FF8C0D"]Build[/COLOR][/B][/th]'); - return l.join(''); - } - - return { - comparisonBBCode: comparisonBBCode, - shortenUrl: shortenUrl - }; - -}]); diff --git a/app/js/i18n/Language.js b/app/js/i18n/Language.js new file mode 100644 index 00000000..8966bb67 --- /dev/null +++ b/app/js/i18n/Language.js @@ -0,0 +1,52 @@ +import EN from 'en'; +import DE from 'de'; +import ES from 'es'; +import FR from 'fr'; +import IT from 'it'; +import RU from 'RU'; +import d3 from 'd3'; + +let fallbackTerms = EN.terms; +let currentLanguage; +let currentTerms; +let format = { + rPct: d3.format('%') +}; + +export format; + +export function setLanguage(langCode) { + let lang; + + switch (langCode) { + case 'de': lang = DE; break; + case 'es': lang = ES; break; + case 'fr': lang = FR; break; + case 'it': lang = IT; break; + case 'ru': lang = RU; break; + default: lang = EN; + } + + currentTerms = lang.terms; + d3Locale = d3.locale(lang.formats); + + format.gen = d3Locale.numberFormat('n'); + format.crd = d3Locale.numberFormat(',.0f'); + format.pwr = d3Locale.numberFormat(',.2f'); + format.round = (d) => format.gen(d3.round(d, 2)); + format.pct = d3Locale.numberFormat('.2%'); + format.pct1 = d3Locale.numberFormat('.1%'); +} + +export const Languages = { + en: 'English', + de: 'Deutsh', + it: 'Italiano', + es: 'Español', + fr: 'Français', + ru: 'ру́сский' +}; + +export function term(t) { + return currentTerms[t] || fallbackTerms[t]; +} diff --git a/app/js/i18n/de.js b/app/js/i18n/de.js index 4acc3b46..6b16da08 100644 --- a/app/js/i18n/de.js +++ b/app/js/i18n/de.js @@ -1,220 +1,218 @@ -angular.module('app').config(['$translateProvider', 'localeFormatProvider', function($translateProvider, localeFormatProvider) { - // Declare number format settings - localeFormatProvider.addFormat('de', { - decimal: ',', - thousands: '.', - grouping: [3], - currency: ['', ' €'], - dateTime: '%A, der %e. %B %Y, %X', - date: '%d.%m.%Y', - time: '%H:%M:%S', - periods: ['AM', 'PM'], // unused - days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'], - shortDays: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'], - months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], - shortMonths: ['Jan', 'Feb', 'Mrz', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'] - }); - $translateProvider.translations('de', { - PHRASE_EXPORT_DESC: 'Ein detaillierter JSON-Export Ihrer Konfiguration für die Verwendung in anderen Websites und Tools', - 'A-Rated': 'A-Klasse', - about: 'Über', - action: 'Aktion', - added: 'Hinzugefügt', - Advanced: 'Verbessert', - 'Advanced Discovery Scanner': 'Fortgeschrittener Aufklärungsscanner', - agility: 'Manövrierbarkeit', - ammo: 'Munition', - PHRASE_CONFIRMATION: 'Sind Sie sicher?', - armour: 'Panzerung', - am: 'Automatische Feldwartungs-Einheit', - available: 'Verfügbar', - backup: 'Sicherungsdatei', - 'Basic Discovery Scanner': 'Einfacher Aufklärungsscanner', - bl: 'Strahlenlaser', - beta: 'Beta', - bins: 'Behälter', - boost: 'Boost', - build: 'Ausstattung', - 'build name': 'Ausstattungsname', - builds: 'Ausstattungen', - bh: 'Rumpfhüllenverstärkung', - ul: 'Salvenlaser', - buy: 'Kaufen', - cancel: 'Abbrechen', - c: 'Kanone', - capital: 'Kapital', - cargo: 'Fracht', - 'Cargo Hatch': 'Frachtluke', - cr: 'Frachtgestell', - cs: 'Frachtscanner', - cells: 'Zellen', - 'Chaff Launcher': 'Düppel-Werfer', - close: 'Schließen', - cc: 'Krallensteuerung: Sammler', - compare: 'Vergleichen', - 'compare all': 'Alles Vergleichen', - comparison: 'Vergleich', - comparisons: 'Vergleiche', - component: 'Komponente', - cost: 'Preis', - costs: 'Kosten', - cm: 'Gegenmaßnahme', - create: 'Erstellen', - 'create new': 'Neu Erstellen', - Cytoscrambler: 'Zytostreuer', - damage: 'Schaden', - delete: 'Löschen', - 'delete all': 'Alles Löschen', - dep: 'Ausg', - deployed: 'Ausgefahren', - 'detailed export': 'Detailierter Export', - 'Detailed Surface Scanner': 'Detailoberflächenscanner', - disabled: 'Deaktiviert', - discount: 'Rabatt', - Distruptor: 'Disruptor', - dc: 'Standard-Landecomputer', - done: 'Fertig', - 'edit data': 'Bearbeiten', - efficiency: 'Effizienz', - 'Electronic Countermeasure': 'Elektronische Gegenmaßnahme', - empty: 'leer', - Enforcer: 'Vollstrecker', - ENG: 'ANT', - 'enter name': 'Namen eingeben', - export: 'Export', - fixed: 'Fixiert', - forum: 'Forum', - fc: 'Splitterkanone', - fd: 'Frameshiftantrieb', - ws: 'Frameshift-Sogwolkenscanner', - FSD: 'FSA', - fi: 'FSA-Unterbrecher', - fuel: 'Treibstoff', - fs: 'Treibstoffsammler', - ft: 'Treibstofftank', - fx: 'Krallensteuerung Treibstoffstransfer', - 'full tank': 'Tank voll', - Gimballed: 'Kardanisch', - H: 'R', - hardpoints: 'Waffenaufhängungen', - hb: 'Krallen-Steuereinheit (Ladelukenöffner)', - 'Heat Sink Launcher': 'Kühlkörperwerfer', - huge: 'Riesig', - hull: 'Hülle', - hr: 'Rumpfhüllenverstärkung (Paket)', - 'Imperial Hammer': 'Imperialer Hammer', - import: 'Importieren', - 'import all': 'Alles Importieren', - insurance: 'Versicherung', - 'Intermediate Discovery Scanner': 'Mittlerer Aufklärungsscanner', - 'internal compartments': 'Innenbereichskabine', - 'jump range': 'Sprungreichweite', - jumps: 'Sprünge', - kw: 'Tötungsbefehl-Scanner', - L: 'G', - laden: 'Beladen', - language: 'Sprache', - large: 'Groß', - limpets: 'Krallen', - ls: 'Lebenserhaltung', - 'Lightweight Alloy': 'Leichte Legierung', - 'lock factor': 'Massensperrefaktor', - LS: 'Ls', - LY: 'Lj', - mass: 'Masse', - 'max mass': 'maximale Masse', - medium: 'Mittel', - 'Military Grade Composite': 'Militär-Komposit', - nl: 'Minenwerfer', - 'Mining Lance': 'Lanzenabbaulaser', - ml: 'Abbaulaser', - 'Mirrored Surface Composite': 'Gespiegelte-Oberfläche-Komposit', - mr: 'Raketenbatterie', - mc: 'Mehrfachgeschütz', - 'net cost': 'Nettokosten', - no: 'Nein', - PHRASE_NO_BUILDS: 'Keine Konfigurationen zum Vergleich ausgewählt!', - PHRASE_NO_RETROCH: 'Keine Umrüständerungen', - none: 'Nichts', - 'none created': 'Leer', - off: 'Aus', - on: 'An', - 'optimal mass': 'optimale Masse', - 'optimize mass': 'Masse optimieren', - overwrite: 'Überschreiben', - Pacifier: 'Friedensstifter', - 'Pack-Hound': 'Schwarmwerfer', - PHRASE_IMPORT: 'JSON hier einfügen oder importieren', - pen: 'Durchdr.', - penetration: 'Durchdringung', - permalink: 'Permalink', - pa: 'Plasmabeschleuniger', - 'Point Defence': 'Punktverteidigung', - power: 'Energie', - pd: 'Energieverteiler', - pp: 'Kraftwerk', - pri: 'Prio', - priority: 'Priorität', - psg: 'Prismaschildgenerator', - proceed: 'Fortfahren', - pc: 'Krallensteuerung: Erzsucher', - pl: 'Impulslaser', - PWR: 'En', - rg: 'Schienenkanone', - range: 'Reichweite', - rate: 'Rate', - 'Reactive Surface Composite': 'Reaktive-Oberfläche-Komposit', - recharge: 'Aufladen', - rf: 'Raffinerie', - 'refuel time': 'Auftankzeit', - 'Reinforced Alloy': 'Verstärkte Legierung', - reload: 'Aktualisieren', - rename: 'Umbenennen', - repair: 'Reparieren', - reset: 'Zurücksetzen', - ret: 'Eing', - retracted: 'Eingefahren', - 'retrofit costs': 'Änderungskosten', - 'retrofit from': 'Nachrüsten von', - ROF: 'Kad', - S: 'K', - save: 'Speichern', - sc: 'Scanner', - PHRASE_SELECT_BUILDS: 'Ausstattung zum Vergleich auswählen', - sell: 'Verkaufen', - s: 'Sensoren', - settings: 'Einstellungen', - sb: 'Schild-Booster', - scb: 'Schildzellenbank', - sg: 'Schildgenerator', - shields: 'Schilde', - ship: 'Schiff', - ships: 'Schiffe', - shortened: 'Gekürzt', - size: 'Größe', - skip: 'Überspringen', - small: 'Klein', - speed: 'Geschwindigkeit', - standard: 'Standard', - 'Standard Docking Computer': 'Standard-Landecomputer', - Stock: 'Standard', - T: 't', - T_LOAD: 'T-Lad', - 'The Retributor': 'Retributor', - t: 'Schubdüsen', - time: 'Dauer', - tp: 'Torpedoaufhängung', - total: 'Gesamt', - 'total range': 'Maximale Reichweite', - turret: 'Geschützturm', - type: 'Typ', - U: 'W', - unladen: 'Unbeladen', - PHRASE_UPDATE_RDY: 'Update verfügbar! Klicken zum Aktualisieren', - utility: 'Werkzeug', - 'utility mounts': 'Werkzeug-Steckplätze', - WEP: 'WAF', - yes: 'Ja', - PHRASE_BACKUP_DESC: 'Export aller Coriolis-Daten, um sie zu sichern oder oder um sie zu einem anderen Browser/Gerät zu übertragen.' - }); -}]); +export const formats = { + decimal: ',', + thousands: '.', + grouping: [3], + currency: ['', ' €'], + dateTime: '%A, der %e. %B %Y, %X', + date: '%d.%m.%Y', + time: '%H:%M:%S', + periods: ['AM', 'PM'], // unused + days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'], + shortDays: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'], + months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], + shortMonths: ['Jan', 'Feb', 'Mrz', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'] +}; + +export const terms = { + PHRASE_EXPORT_DESC: 'Ein detaillierter JSON-Export Ihrer Konfiguration für die Verwendung in anderen Websites und Tools', + 'A-Rated': 'A-Klasse', + about: 'Über', + action: 'Aktion', + added: 'Hinzugefügt', + Advanced: 'Verbessert', + 'Advanced Discovery Scanner': 'Fortgeschrittener Aufklärungsscanner', + agility: 'Manövrierbarkeit', + ammo: 'Munition', + PHRASE_CONFIRMATION: 'Sind Sie sicher?', + armour: 'Panzerung', + am: 'Automatische Feldwartungs-Einheit', + available: 'Verfügbar', + backup: 'Sicherungsdatei', + 'Basic Discovery Scanner': 'Einfacher Aufklärungsscanner', + bl: 'Strahlenlaser', + beta: 'Beta', + bins: 'Behälter', + boost: 'Boost', + build: 'Ausstattung', + 'build name': 'Ausstattungsname', + builds: 'Ausstattungen', + bh: 'Rumpfhüllenverstärkung', + ul: 'Salvenlaser', + buy: 'Kaufen', + cancel: 'Abbrechen', + c: 'Kanone', + capital: 'Kapital', + cargo: 'Fracht', + 'Cargo Hatch': 'Frachtluke', + cr: 'Frachtgestell', + cs: 'Frachtscanner', + cells: 'Zellen', + 'Chaff Launcher': 'Düppel-Werfer', + close: 'Schließen', + cc: 'Krallensteuerung: Sammler', + compare: 'Vergleichen', + 'compare all': 'Alles Vergleichen', + comparison: 'Vergleich', + comparisons: 'Vergleiche', + component: 'Komponente', + cost: 'Preis', + costs: 'Kosten', + cm: 'Gegenmaßnahme', + create: 'Erstellen', + 'create new': 'Neu Erstellen', + Cytoscrambler: 'Zytostreuer', + damage: 'Schaden', + delete: 'Löschen', + 'delete all': 'Alles Löschen', + dep: 'Ausg', + deployed: 'Ausgefahren', + 'detailed export': 'Detailierter Export', + 'Detailed Surface Scanner': 'Detailoberflächenscanner', + disabled: 'Deaktiviert', + discount: 'Rabatt', + Distruptor: 'Disruptor', + dc: 'Standard-Landecomputer', + done: 'Fertig', + 'edit data': 'Bearbeiten', + efficiency: 'Effizienz', + 'Electronic Countermeasure': 'Elektronische Gegenmaßnahme', + empty: 'leer', + Enforcer: 'Vollstrecker', + ENG: 'ANT', + 'enter name': 'Namen eingeben', + export: 'Export', + fixed: 'Fixiert', + forum: 'Forum', + fc: 'Splitterkanone', + fd: 'Frameshiftantrieb', + ws: 'Frameshift-Sogwolkenscanner', + FSD: 'FSA', + fi: 'FSA-Unterbrecher', + fuel: 'Treibstoff', + fs: 'Treibstoffsammler', + ft: 'Treibstofftank', + fx: 'Krallensteuerung Treibstoffstransfer', + 'full tank': 'Tank voll', + Gimballed: 'Kardanisch', + H: 'R', + hardpoints: 'Waffenaufhängungen', + hb: 'Krallen-Steuereinheit (Ladelukenöffner)', + 'Heat Sink Launcher': 'Kühlkörperwerfer', + huge: 'Riesig', + hull: 'Hülle', + hr: 'Rumpfhüllenverstärkung (Paket)', + 'Imperial Hammer': 'Imperialer Hammer', + import: 'Importieren', + 'import all': 'Alles Importieren', + insurance: 'Versicherung', + 'Intermediate Discovery Scanner': 'Mittlerer Aufklärungsscanner', + 'internal compartments': 'Innenbereichskabine', + 'jump range': 'Sprungreichweite', + jumps: 'Sprünge', + kw: 'Tötungsbefehl-Scanner', + L: 'G', + laden: 'Beladen', + language: 'Sprache', + large: 'Groß', + limpets: 'Krallen', + ls: 'Lebenserhaltung', + 'Lightweight Alloy': 'Leichte Legierung', + 'lock factor': 'Massensperrefaktor', + LS: 'Ls', + LY: 'Lj', + mass: 'Masse', + 'max mass': 'maximale Masse', + medium: 'Mittel', + 'Military Grade Composite': 'Militär-Komposit', + nl: 'Minenwerfer', + 'Mining Lance': 'Lanzenabbaulaser', + ml: 'Abbaulaser', + 'Mirrored Surface Composite': 'Gespiegelte-Oberfläche-Komposit', + mr: 'Raketenbatterie', + mc: 'Mehrfachgeschütz', + 'net cost': 'Nettokosten', + no: 'Nein', + PHRASE_NO_BUILDS: 'Keine Konfigurationen zum Vergleich ausgewählt!', + PHRASE_NO_RETROCH: 'Keine Umrüständerungen', + none: 'Nichts', + 'none created': 'Leer', + off: 'Aus', + on: 'An', + 'optimal mass': 'optimale Masse', + 'optimize mass': 'Masse optimieren', + overwrite: 'Überschreiben', + Pacifier: 'Friedensstifter', + 'Pack-Hound': 'Schwarmwerfer', + PHRASE_IMPORT: 'JSON hier einfügen oder importieren', + pen: 'Durchdr.', + penetration: 'Durchdringung', + permalink: 'Permalink', + pa: 'Plasmabeschleuniger', + 'Point Defence': 'Punktverteidigung', + power: 'Energie', + pd: 'Energieverteiler', + pp: 'Kraftwerk', + pri: 'Prio', + priority: 'Priorität', + psg: 'Prismaschildgenerator', + proceed: 'Fortfahren', + pc: 'Krallensteuerung: Erzsucher', + pl: 'Impulslaser', + PWR: 'En', + rg: 'Schienenkanone', + range: 'Reichweite', + rate: 'Rate', + 'Reactive Surface Composite': 'Reaktive-Oberfläche-Komposit', + recharge: 'Aufladen', + rf: 'Raffinerie', + 'refuel time': 'Auftankzeit', + 'Reinforced Alloy': 'Verstärkte Legierung', + reload: 'Aktualisieren', + rename: 'Umbenennen', + repair: 'Reparieren', + reset: 'Zurücksetzen', + ret: 'Eing', + retracted: 'Eingefahren', + 'retrofit costs': 'Änderungskosten', + 'retrofit from': 'Nachrüsten von', + ROF: 'Kad', + S: 'K', + save: 'Speichern', + sc: 'Scanner', + PHRASE_SELECT_BUILDS: 'Ausstattung zum Vergleich auswählen', + sell: 'Verkaufen', + s: 'Sensoren', + settings: 'Einstellungen', + sb: 'Schild-Booster', + scb: 'Schildzellenbank', + sg: 'Schildgenerator', + shields: 'Schilde', + ship: 'Schiff', + ships: 'Schiffe', + shortened: 'Gekürzt', + size: 'Größe', + skip: 'Überspringen', + small: 'Klein', + speed: 'Geschwindigkeit', + standard: 'Standard', + 'Standard Docking Computer': 'Standard-Landecomputer', + Stock: 'Standard', + T: 't', + T_LOAD: 'T-Lad', + 'The Retributor': 'Retributor', + t: 'Schubdüsen', + time: 'Dauer', + tp: 'Torpedoaufhängung', + total: 'Gesamt', + 'total range': 'Maximale Reichweite', + turret: 'Geschützturm', + type: 'Typ', + U: 'W', + unladen: 'Unbeladen', + PHRASE_UPDATE_RDY: 'Update verfügbar! Klicken zum Aktualisieren', + utility: 'Werkzeug', + 'utility mounts': 'Werkzeug-Steckplätze', + WEP: 'WAF', + yes: 'Ja', + PHRASE_BACKUP_DESC: 'Export aller Coriolis-Daten, um sie zu sichern oder oder um sie zu einem anderen Browser/Gerät zu übertragen.' +}; diff --git a/app/js/i18n/en.js b/app/js/i18n/en.js index ac01137d..afc3ae08 100644 --- a/app/js/i18n/en.js +++ b/app/js/i18n/en.js @@ -1,217 +1,230 @@ -angular.module('app').config(['$translateProvider', function($translateProvider) { - $translateProvider.translations('en', { - PHRASE_EXPORT_DESC: 'A detailed JSON export of your build for use in other sites and tools', - 'A-Rated': 'A-Rated', - about: 'about', - action: 'action', - added: 'added', - Advanced: 'Advanced', - 'Advanced Discovery Scanner': 'Advanced Discovery Scanner', - agility: 'agility', - alpha: 'alpha', - ammo: 'ammo', - PHRASE_CONFIRMATION: 'Are You Sure?', - armour: 'armour', - am: 'Auto Field-Maintenance Unit', - available: 'available', - backup: 'backup', - 'Basic Discovery Scanner': 'Basic Discovery Scanner', - bl: 'Beam Laser', - beta: 'beta', - bins: 'bins', - boost: 'boost', - build: 'build', - 'build name': 'Build Name', - builds: 'builds', - bh: 'bulkheads', - ul: 'Burst Laser', - buy: 'buy', - cancel: 'cancel', - c: 'Cannon', - capital: 'capital', - cargo: 'cargo', - 'Cargo Hatch': 'Cargo Hatch', - cr: 'Cargo Rack', - cs: 'Cargo Scanner', - cells: 'cells', - 'Chaff Launcher': 'Chaff Launcher', - close: 'close', - cc: 'Collector Limpet Controller', - compare: 'compare', - 'compare all': 'compare all', - comparison: 'comparison', - comparisons: 'comparisons', - component: 'component', - cost: 'cost', - costs: 'costs', - cm: 'Countermeasure', - CR: 'CR', - create: 'create', - 'create new': 'create new', - credits: 'credits', - Cytoscrambler: 'Cytoscrambler', - damage: 'damage', - delete: 'delete', - 'delete all': 'delete all', - dep: 'dep', - deployed: 'deployed', - 'detailed export': 'detailed export', - 'Detailed Surface Scanner': 'Detailed Surface Scanner', - disabled: 'disabled', - discount: 'discount', - Distruptor: 'Distruptor', - dc: 'Docking Computer', - done: 'done', - DPS: 'DPS', - 'edit data': 'edit data', - efficiency: 'efficiency', - 'Electronic Countermeasure': 'Electronic Countermeasure', - empty: 'empty', - Enforcer: 'Enforcer', - ENG: 'ENG', - 'enter name': 'Enter Name', - EPS: 'EPS', - export: 'export', - fixed: 'fixed', - forum: 'forum', - fc: 'Fragment Cannon', - fd: 'Frame Shift Drive', - ws: 'Frame Shift Wake Scanner', - FSD: 'FSD', - fi: 'FSD Interdictor', - fuel: 'fuel', - fs: 'Fuel Scoop', - ft: 'Fuel Tank', - fx: 'Fuel Transfer Limpet Controller', - 'full tank': 'full tank', - Gimballed: 'Gimballed', - H: 'H', - hardpoints: 'hardpoints', - hb: 'Hatch Breaker Limpet Controller', - 'Heat Sink Launcher': 'Heat Sink Launcher', - huge: 'huge', - hull: 'hull', - hr: 'Hull Reinforcement Package', - 'Imperial Hammer': 'Imperial Hammer', - import: 'import', - 'import all': 'import all', - insurance: 'insurance', - 'Intermediate Discovery Scanner': 'Intermediate Discovery Scanner', - 'internal compartments': 'internal compartments', - 'jump range': 'jump range', - jumps: 'jumps', - kw: 'Kill Warrant Scanner', - L: 'L', - laden: 'laden', - language: 'language', - large: 'large', - 'limpets': 'Limpets', - ls: 'life support', - 'Lightweight Alloy': 'Lightweight Alloy', - 'lock factor': 'lock factor', - LS: 'Ls', - LY: 'LY', - M: 'M', - 'm/s': 'm/s', - mass: 'mass', - max: 'max', - 'max mass': 'max mass', - medium: 'medium', - 'Military Grade Composite': 'Military Grade Composite', - nl: 'Mine Launcher', - 'Mining Lance': 'Mining Lance', - ml: 'Mining Laser', - 'Mirrored Surface Composite': 'Mirrored Surface Composite', - mr: 'Missile Rack', - mc: 'Multi-cannon', - 'net cost': 'net cost', - no: 'no', - PHRASE_NO_BUILDS: 'No builds added to comparison!', - PHRASE_NO_RETROCH: 'No Retrofitting changes', - none: 'none', - 'none created': 'none created', - off: 'off', - on: 'on', - optimal: 'optimal', - 'optimal mass': 'optimal mass', - 'optimize mass': 'optimize mass', - overwrite: 'overwrite', - Pacifier: 'Pacifier', - 'Pack-Hound': 'Pack-Hound', - PHRASE_IMPORT: 'Paste JSON or import here', - pen: 'pen', - penetration: 'penetration', - permalink: 'permalink', - pa: 'Plasma Accelerator', - 'Point Defence': 'Point Defence', - power: 'power', - pd: 'power distributor', - pp: 'power plant', - pri: 'pri', - priority: 'priority', - psg: 'Prismatic Shield Generator', - proceed: 'proceed', - pc: 'Prospector Limpet Controller', - pl: 'Pulse Laser', - PWR: 'PWR', - rg: 'Rail Gun', - range: 'range', - rate: 'rate', - 'Reactive Surface Composite': 'Reactive Surface Composite', - recharge: 'recharge', - rf: 'Refinery', - 'refuel time': 'refuel time', - 'Reinforced Alloy': 'Reinforced Alloy', - reload: 'reload', - rename: 'rename', - repair: 'repair', - reset: 'reset', - ret: 'ret', - retracted: 'retracted', - 'retrofit costs': 'retrofit costs', - 'retrofit from': 'retrofit from', - ROF: 'ROF', - S: 'S', - save: 'save', - sc: 'scanner', - PHRASE_SELECT_BUILDS: 'Select Builds to Compare', - sell: 'sell', - s: 'sensors', - settings: 'settings', - sb: 'Shield Booster', - scb: 'Shield Cell Bank', - sg: 'Shield Generator', - shields: 'shields', - ship: 'ship', - ships: 'ships', - shortened: 'shortened', - size: 'size', - skip: 'skip', - small: 'small', - speed: 'speed', - standard: 'standard', - 'Standard Docking Computer': 'Standard Docking Computer', - Stock: 'Stock', - SYS: 'SYS', - T: 'T', - T_LOAD: 't-load', - 'The Retributor': 'The Retributor', - t: 'thrusters', - time: 'time', - tp: 'Torpedo Pylon', - total: 'total', - 'total range': 'total range', - turret: 'turret', - type: 'type', - U: 'U', - unladen: 'unladen', - PHRASE_UPDATE_RDY: 'Update Available! Click to Refresh', - URL: 'URL', - utility: 'utility', - 'utility mounts': 'utility mounts', - version: 'version', - WEP: 'WEP', - yes: 'yes', - PHRASE_BACKUP_DESC: 'Backup of all Coriolis data to save or transfer to another browser/device' - }); -}]); +export const formats = { + decimal: '.', + thousands: ',', + grouping: [3], + currency: ['$', ''], + dateTime: '%a %b %e %X %Y', + date: '%m/%d/%Y', + time: '%H:%M:%S', + periods: ['AM', 'PM'], + days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] +}; + +export const terms = { + PHRASE_EXPORT_DESC: 'A detailed JSON export of your build for use in other sites and tools', + 'A-Rated': 'A-Rated', + about: 'about', + action: 'action', + added: 'added', + Advanced: 'Advanced', + 'Advanced Discovery Scanner': 'Advanced Discovery Scanner', + agility: 'agility', + alpha: 'alpha', + ammo: 'ammo', + PHRASE_CONFIRMATION: 'Are You Sure?', + armour: 'armour', + am: 'Auto Field-Maintenance Unit', + available: 'available', + backup: 'backup', + 'Basic Discovery Scanner': 'Basic Discovery Scanner', + bl: 'Beam Laser', + beta: 'beta', + bins: 'bins', + boost: 'boost', + build: 'build', + 'build name': 'Build Name', + builds: 'builds', + bh: 'bulkheads', + ul: 'Burst Laser', + buy: 'buy', + cancel: 'cancel', + c: 'Cannon', + capital: 'capital', + cargo: 'cargo', + 'Cargo Hatch': 'Cargo Hatch', + cr: 'Cargo Rack', + cs: 'Cargo Scanner', + cells: 'cells', + 'Chaff Launcher': 'Chaff Launcher', + close: 'close', + cc: 'Collector Limpet Controller', + compare: 'compare', + 'compare all': 'compare all', + comparison: 'comparison', + comparisons: 'comparisons', + component: 'component', + cost: 'cost', + costs: 'costs', + cm: 'Countermeasure', + CR: 'CR', + create: 'create', + 'create new': 'create new', + credits: 'credits', + Cytoscrambler: 'Cytoscrambler', + damage: 'damage', + delete: 'delete', + 'delete all': 'delete all', + dep: 'dep', + deployed: 'deployed', + 'detailed export': 'detailed export', + 'Detailed Surface Scanner': 'Detailed Surface Scanner', + disabled: 'disabled', + discount: 'discount', + Distruptor: 'Distruptor', + dc: 'Docking Computer', + done: 'done', + DPS: 'DPS', + 'edit data': 'edit data', + efficiency: 'efficiency', + 'Electronic Countermeasure': 'Electronic Countermeasure', + empty: 'empty', + Enforcer: 'Enforcer', + ENG: 'ENG', + 'enter name': 'Enter Name', + EPS: 'EPS', + export: 'export', + fixed: 'fixed', + forum: 'forum', + fc: 'Fragment Cannon', + fd: 'Frame Shift Drive', + ws: 'Frame Shift Wake Scanner', + FSD: 'FSD', + fi: 'FSD Interdictor', + fuel: 'fuel', + fs: 'Fuel Scoop', + ft: 'Fuel Tank', + fx: 'Fuel Transfer Limpet Controller', + 'full tank': 'full tank', + Gimballed: 'Gimballed', + H: 'H', + hardpoints: 'hardpoints', + hb: 'Hatch Breaker Limpet Controller', + 'Heat Sink Launcher': 'Heat Sink Launcher', + huge: 'huge', + hull: 'hull', + hr: 'Hull Reinforcement Package', + 'Imperial Hammer': 'Imperial Hammer', + import: 'import', + 'import all': 'import all', + insurance: 'insurance', + 'Intermediate Discovery Scanner': 'Intermediate Discovery Scanner', + 'internal compartments': 'internal compartments', + 'jump range': 'jump range', + jumps: 'jumps', + kw: 'Kill Warrant Scanner', + L: 'L', + laden: 'laden', + language: 'language', + large: 'large', + 'limpets': 'Limpets', + ls: 'life support', + 'Lightweight Alloy': 'Lightweight Alloy', + 'lock factor': 'lock factor', + LS: 'Ls', + LY: 'LY', + M: 'M', + 'm/s': 'm/s', + mass: 'mass', + max: 'max', + 'max mass': 'max mass', + medium: 'medium', + 'Military Grade Composite': 'Military Grade Composite', + nl: 'Mine Launcher', + 'Mining Lance': 'Mining Lance', + ml: 'Mining Laser', + 'Mirrored Surface Composite': 'Mirrored Surface Composite', + mr: 'Missile Rack', + mc: 'Multi-cannon', + 'net cost': 'net cost', + no: 'no', + PHRASE_NO_BUILDS: 'No builds added to comparison!', + PHRASE_NO_RETROCH: 'No Retrofitting changes', + none: 'none', + 'none created': 'none created', + off: 'off', + on: 'on', + optimal: 'optimal', + 'optimal mass': 'optimal mass', + 'optimize mass': 'optimize mass', + overwrite: 'overwrite', + Pacifier: 'Pacifier', + 'Pack-Hound': 'Pack-Hound', + PHRASE_IMPORT: 'Paste JSON or import here', + pen: 'pen', + penetration: 'penetration', + permalink: 'permalink', + pa: 'Plasma Accelerator', + 'Point Defence': 'Point Defence', + power: 'power', + pd: 'power distributor', + pp: 'power plant', + pri: 'pri', + priority: 'priority', + psg: 'Prismatic Shield Generator', + proceed: 'proceed', + pc: 'Prospector Limpet Controller', + pl: 'Pulse Laser', + PWR: 'PWR', + rg: 'Rail Gun', + range: 'range', + rate: 'rate', + 'Reactive Surface Composite': 'Reactive Surface Composite', + recharge: 'recharge', + rf: 'Refinery', + 'refuel time': 'refuel time', + 'Reinforced Alloy': 'Reinforced Alloy', + reload: 'reload', + rename: 'rename', + repair: 'repair', + reset: 'reset', + ret: 'ret', + retracted: 'retracted', + 'retrofit costs': 'retrofit costs', + 'retrofit from': 'retrofit from', + ROF: 'ROF', + S: 'S', + save: 'save', + sc: 'scanner', + PHRASE_SELECT_BUILDS: 'Select Builds to Compare', + sell: 'sell', + s: 'sensors', + settings: 'settings', + sb: 'Shield Booster', + scb: 'Shield Cell Bank', + sg: 'Shield Generator', + shields: 'shields', + ship: 'ship', + ships: 'ships', + shortened: 'shortened', + size: 'size', + skip: 'skip', + small: 'small', + speed: 'speed', + standard: 'standard', + 'Standard Docking Computer': 'Standard Docking Computer', + Stock: 'Stock', + SYS: 'SYS', + T: 'T', + T_LOAD: 't-load', + 'The Retributor': 'The Retributor', + t: 'thrusters', + time: 'time', + tp: 'Torpedo Pylon', + total: 'total', + 'total range': 'total range', + turret: 'turret', + type: 'type', + U: 'U', + unladen: 'unladen', + PHRASE_UPDATE_RDY: 'Update Available! Click to Refresh', + URL: 'URL', + utility: 'utility', + 'utility mounts': 'utility mounts', + version: 'version', + WEP: 'WEP', + yes: 'yes', + PHRASE_BACKUP_DESC: 'Backup of all Coriolis data to save or transfer to another browser/device' +}; diff --git a/app/js/i18n/es.js b/app/js/i18n/es.js index ccc5bbac..1a128cc1 100644 --- a/app/js/i18n/es.js +++ b/app/js/i18n/es.js @@ -1,212 +1,208 @@ -angular.module('app').config(['$translateProvider', 'localeFormatProvider', function($translateProvider, localeFormatProvider) { +export const formats = { + decimal: ',', + thousands: '.', + grouping: [3], + currency: ['', ' €'], + dateTime: '%A, %e de %B de %Y, %X', + date: '%d/%m/%Y', + time: '%H:%M:%S', + periods: ['AM', 'PM'], + days: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'], + shortDays: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'], + months: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'], + shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'] +}; - // Declare number format settings - localeFormatProvider.addFormat('es', { - decimal: ',', - thousands: '.', - grouping: [3], - currency: ['', ' €'], - dateTime: '%A, %e de %B de %Y, %X', - date: '%d/%m/%Y', - time: '%H:%M:%S', - periods: ['AM', 'PM'], - days: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'], - shortDays: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'], - months: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'], - shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'] - }); - - $translateProvider.translations('es', { - 'PHRASE_EXPORT_DESC': 'Una detallada exportaci\u00f3n JSON de tu construcci\u00f3n para usarlo en otros sitios web y herramientas', - 'A-Rated': 'Calidad-A', - 'about': 'Acerca', - 'action': 'Acci\u00f3n', - 'added': 'A\u00f1adido', - 'Advanced Discovery Scanner': 'Esc\u00e1ner de exploraci\u00f3n avanzado', - 'agility': 'Maniobrabilidad', - 'alpha': 'Alfa', - 'ammo': 'Munici\u00f3n', - 'PHRASE_CONFIRMATION': '\u00bfEst\u00e1s seguro?', - 'armour': 'Blindaje', - 'am': 'Unidad de auto-reparaciones', - 'available': 'Disponible', - 'backup': 'Reserva', - 'Basic Discovery Scanner': 'Esc\u00e1ner de exploraci\u00f3n b\u00e1sico', - 'bl': 'L\u00e1ser de haz', - 'bins': 'contenedores', - 'boost': 'incrementar', - 'build': 'Construcci\u00f3n', - 'build name': 'Nombre de la construcci\u00f3n', - 'builds': 'Construcciones', - 'bh': 'mamparos', - 'ul': 'Laser de r\u00e1fagas', - 'buy': 'Comprar', - 'cancel': 'Cancelar', - 'c': 'Ca\u00f1\u00f3n', - 'capital': 'capital', - 'cargo': 'Carga', - 'Cargo Hatch': 'Compuerta de carga', - 'cr': 'Compartimento de carga', - 'cs': 'Esc\u00e1ner de carga', - 'cells': 'celdas', - 'Chaff Launcher': 'Lanzador de birutas', - 'close': 'Cerrar', - 'cc': 'Controlador de Drones de Recogida', - 'compare': 'Comparar', - 'compare all': 'comparar todas', - 'comparison': 'Comparativa', - 'comparisons': 'Comparativas', - 'component': 'Componente', - 'cost': 'Coste', - 'costs': 'Costes', - 'cm': 'Contramedidas', - 'create': 'Crear', - 'create new': 'Crear nuevo', - 'credits': 'Cr\u00e9ditos', - 'damage': 'Da\u00f1o', - 'delete': 'Borrar', - 'delete all': 'Borrar todo', - 'dep': 'desp', - 'deployed': 'Desplegado', - 'detailed export': 'Exportacion detallada', - 'Detailed Surface Scanner': 'Escaner de exploraci\u00f3n detallada', - 'disabled': 'Desactivado', - 'discount': 'Descuento', - 'dc': 'Ordenador de aterrizaje', - 'done': 'Hecho', - 'DPS': 'DPS (Da\u00f1o Por Segundo)', - 'edit data': 'Editar datos', - 'efficiency': 'Eficiencia', - 'Electronic Countermeasure': 'Contramedidas electr\u00f3nicas', - 'empty': 'Vac\u00edo', - 'ENG': 'MOT', - 'enter name': 'Introduce el nombre', - 'export': 'exportar', - 'fixed': 'fijo', - 'forum': 'Foro', - 'fc': 'Ca\u00f1\u00f3n de fragmentaci\u00f3n', - 'fd': 'Motor de salto', - 'ws': 'Esc\u00e1ner de Salto', - 'fi': 'Interdictor FSD', - 'fuel': 'Combustible', - 'fs': 'Recolector de Combustible', - 'ft': 'Tanque de combustible', - 'fx': 'Sistema de Transferencia de Combustilble', - 'full tank': 'Tanque lleno', - 'Gimballed': 'Card\u00e1n', - 'H': 'E', - 'hardpoints': 'Montura de armas', - 'hb': 'Controlador de Apertura de Bah\u00eda de Carga', - 'Heat Sink Launcher': 'Eyector de Acumulador de Calor', - 'huge': 'enorme', - 'hull': 'Casco', - 'hr': 'Sistema de Casco Reforzado', - 'import': 'Importar', - 'import all': 'Importar todo', - 'insurance': 'Seguro', - 'Intermediate Discovery Scanner': 'Esc\u00e1ner de exploraci\u00f3n media', - 'internal compartments': 'Compartimentos internos', - 'jump range': 'Rango de salto', - 'jumps': 'Saltos', - 'kw': 'Esc\u00e1ner Detector de Recompensas', - 'L': 'G', - 'laden': 'Cargada', - 'language': 'Idioma', - 'large': 'Grande', - 'ls': 'Soporte vital', - 'Lightweight Alloy': 'Aleaci\u00f3n ligera', - 'limpets': 'Drones', - 'lock factor': 'factor de bloqueo', - 'mass': 'Masa', - 'max': 'm\u00e1x', - 'max mass': 'Masa m\u00e1xima', - 'medium': 'medio', - 'Military Grade Composite': 'Blindaje Militar', - 'nl': 'Lanzaminas', - 'Mining Lance': 'Lanza de miner\u00eda', - 'ml': 'L\u00e1ser de miner\u00eda', - 'Mirrored Surface Composite': 'Blindaje Reflectante', - 'mr': 'Bah\u00eda de Misiles', - 'mc': 'Ca\u00f1\u00f3n m\u00faltiple', - 'net cost': 'Coste neto', - 'PHRASE_NO_BUILDS': '\u00a1No se a\u00f1adieron plantillas para comparaci\u00f3n!', - 'PHRASE_NO_RETROCH': 'No hay cambios en los ajutes', - 'none': 'Nada', - 'none created': 'Nada creado', - 'off': 'apagado', - 'on': 'encendido', - 'optimal': '\u00f3ptimo', - 'optimal mass': 'masa \u00f3ptima', - 'optimize mass': 'optimizar masa', - 'overwrite': 'Sobreescribir', - 'PHRASE_IMPORT': 'Pega el JSON o imp\u00f3rtalo aqu\u00ed', - 'penetration': 'penetraci\u00f3n', - 'permalink': 'enlace permanente', - 'pa': 'Acelerador de Plasma', - 'Point Defence': 'Punto de Defensa', - 'power': 'energ\u00eda', - 'pd': 'distribuidor de energ\u00eda', - 'pp': 'Planta de Energ\u00eda', - 'priority': 'prioridad', - 'proceed': 'Proceder', - 'pc': 'Controlador de drones de prospecci\u00f3n', - 'pl': 'L\u00e1ser de Pulso', - 'PWR': 'POT', - 'rg': 'Ca\u00f1\u00f3n de Riel', - 'range': 'rango', - 'rate': 'ratio', - 'Reactive Surface Composite': 'Blindaje Reactivo', - 'recharge': 'recargar', - 'rf': 'Refineria', - 'refuel time': 'Tiempo para repostar', - 'Reinforced Alloy': 'Armadura reforzada', - 'reload': 'Recargar', - 'rename': 'Renombrar', - 'repair': 'Reparar', - 'reset': 'Reiniciar', - 'ret': 'PLE', - 'retracted': 'plegadas', - 'retrofit costs': 'costes de equipamiento', - 'retrofit from': 'equipamiento desde', - 'ROF': 'RDF', - 'S': 'P', - 'save': 'guardar', - 'sc': 'sc\u00e1ner', - 'PHRASE_SELECT_BUILDS': 'Selecciona equipamientos para comparar', - 'sell': 'Vender', - 's': 'Sensores', - 'settings': 'Configuraci\u00f3n', - 'sb': 'Potenciador de Escudos', - 'scb': 'C\u00e9lula de Energ\u00eda de Escudos', - 'sg': 'Generador de escudos', - 'shields': 'Escudos', - 'ship': 'Nave ', - 'ships': 'Naves', - 'shortened': 'Abreviado', - 'size': 'Tama\u00f1o', - 'skip': 'omitir', - 'small': 'Peque\u00f1o', - 'speed': 'velocidad', - 'standard': 'est\u00e1ndar', - 'Standard Docking Computer': 'Computador de Atraque Est\u00e1ndar', - 'Stock': 'De serie', - 'SYS': 'SIS', - 'T_LOAD': 'c-t\u00e9rmica', - 't': 'Propulsores', - 'time': 'Tiempo', - 'tp': 'Anclaje de torpedo', - 'total': 'Total', - 'total range': 'Rango total', - 'turret': 'torreta', - 'type': 'Tipo', - 'unladen': 'Sin carga', - 'PHRASE_UPDATE_RDY': 'Actualizacion disponible! Haz click para recargar', - 'URL': 'Enlace', - 'utility': 'utilidad', - 'utility mounts': 'monturas de utilidad', - 'version': 'Versi\u00f3n', - 'WEP': 'ARM', - 'yes': 'si', - 'PHRASE_BACKUP_DESC': 'Copia de seguridad de todos los datos de Coriolis para guardarlos o transferirlos a otro navegador\/dispositivo' - }); -}]); +export const terms = { + 'PHRASE_EXPORT_DESC': 'Una detallada exportaci\u00f3n JSON de tu construcci\u00f3n para usarlo en otros sitios web y herramientas', + 'A-Rated': 'Calidad-A', + 'about': 'Acerca', + 'action': 'Acci\u00f3n', + 'added': 'A\u00f1adido', + 'Advanced Discovery Scanner': 'Esc\u00e1ner de exploraci\u00f3n avanzado', + 'agility': 'Maniobrabilidad', + 'alpha': 'Alfa', + 'ammo': 'Munici\u00f3n', + 'PHRASE_CONFIRMATION': '\u00bfEst\u00e1s seguro?', + 'armour': 'Blindaje', + 'am': 'Unidad de auto-reparaciones', + 'available': 'Disponible', + 'backup': 'Reserva', + 'Basic Discovery Scanner': 'Esc\u00e1ner de exploraci\u00f3n b\u00e1sico', + 'bl': 'L\u00e1ser de haz', + 'bins': 'contenedores', + 'boost': 'incrementar', + 'build': 'Construcci\u00f3n', + 'build name': 'Nombre de la construcci\u00f3n', + 'builds': 'Construcciones', + 'bh': 'mamparos', + 'ul': 'Laser de r\u00e1fagas', + 'buy': 'Comprar', + 'cancel': 'Cancelar', + 'c': 'Ca\u00f1\u00f3n', + 'capital': 'capital', + 'cargo': 'Carga', + 'Cargo Hatch': 'Compuerta de carga', + 'cr': 'Compartimento de carga', + 'cs': 'Esc\u00e1ner de carga', + 'cells': 'celdas', + 'Chaff Launcher': 'Lanzador de birutas', + 'close': 'Cerrar', + 'cc': 'Controlador de Drones de Recogida', + 'compare': 'Comparar', + 'compare all': 'comparar todas', + 'comparison': 'Comparativa', + 'comparisons': 'Comparativas', + 'component': 'Componente', + 'cost': 'Coste', + 'costs': 'Costes', + 'cm': 'Contramedidas', + 'create': 'Crear', + 'create new': 'Crear nuevo', + 'credits': 'Cr\u00e9ditos', + 'damage': 'Da\u00f1o', + 'delete': 'Borrar', + 'delete all': 'Borrar todo', + 'dep': 'desp', + 'deployed': 'Desplegado', + 'detailed export': 'Exportacion detallada', + 'Detailed Surface Scanner': 'Escaner de exploraci\u00f3n detallada', + 'disabled': 'Desactivado', + 'discount': 'Descuento', + 'dc': 'Ordenador de aterrizaje', + 'done': 'Hecho', + 'DPS': 'DPS (Da\u00f1o Por Segundo)', + 'edit data': 'Editar datos', + 'efficiency': 'Eficiencia', + 'Electronic Countermeasure': 'Contramedidas electr\u00f3nicas', + 'empty': 'Vac\u00edo', + 'ENG': 'MOT', + 'enter name': 'Introduce el nombre', + 'export': 'exportar', + 'fixed': 'fijo', + 'forum': 'Foro', + 'fc': 'Ca\u00f1\u00f3n de fragmentaci\u00f3n', + 'fd': 'Motor de salto', + 'ws': 'Esc\u00e1ner de Salto', + 'fi': 'Interdictor FSD', + 'fuel': 'Combustible', + 'fs': 'Recolector de Combustible', + 'ft': 'Tanque de combustible', + 'fx': 'Sistema de Transferencia de Combustilble', + 'full tank': 'Tanque lleno', + 'Gimballed': 'Card\u00e1n', + 'H': 'E', + 'hardpoints': 'Montura de armas', + 'hb': 'Controlador de Apertura de Bah\u00eda de Carga', + 'Heat Sink Launcher': 'Eyector de Acumulador de Calor', + 'huge': 'enorme', + 'hull': 'Casco', + 'hr': 'Sistema de Casco Reforzado', + 'import': 'Importar', + 'import all': 'Importar todo', + 'insurance': 'Seguro', + 'Intermediate Discovery Scanner': 'Esc\u00e1ner de exploraci\u00f3n media', + 'internal compartments': 'Compartimentos internos', + 'jump range': 'Rango de salto', + 'jumps': 'Saltos', + 'kw': 'Esc\u00e1ner Detector de Recompensas', + 'L': 'G', + 'laden': 'Cargada', + 'language': 'Idioma', + 'large': 'Grande', + 'ls': 'Soporte vital', + 'Lightweight Alloy': 'Aleaci\u00f3n ligera', + 'limpets': 'Drones', + 'lock factor': 'factor de bloqueo', + 'mass': 'Masa', + 'max': 'm\u00e1x', + 'max mass': 'Masa m\u00e1xima', + 'medium': 'medio', + 'Military Grade Composite': 'Blindaje Militar', + 'nl': 'Lanzaminas', + 'Mining Lance': 'Lanza de miner\u00eda', + 'ml': 'L\u00e1ser de miner\u00eda', + 'Mirrored Surface Composite': 'Blindaje Reflectante', + 'mr': 'Bah\u00eda de Misiles', + 'mc': 'Ca\u00f1\u00f3n m\u00faltiple', + 'net cost': 'Coste neto', + 'PHRASE_NO_BUILDS': '\u00a1No se a\u00f1adieron plantillas para comparaci\u00f3n!', + 'PHRASE_NO_RETROCH': 'No hay cambios en los ajutes', + 'none': 'Nada', + 'none created': 'Nada creado', + 'off': 'apagado', + 'on': 'encendido', + 'optimal': '\u00f3ptimo', + 'optimal mass': 'masa \u00f3ptima', + 'optimize mass': 'optimizar masa', + 'overwrite': 'Sobreescribir', + 'PHRASE_IMPORT': 'Pega el JSON o imp\u00f3rtalo aqu\u00ed', + 'penetration': 'penetraci\u00f3n', + 'permalink': 'enlace permanente', + 'pa': 'Acelerador de Plasma', + 'Point Defence': 'Punto de Defensa', + 'power': 'energ\u00eda', + 'pd': 'distribuidor de energ\u00eda', + 'pp': 'Planta de Energ\u00eda', + 'priority': 'prioridad', + 'proceed': 'Proceder', + 'pc': 'Controlador de drones de prospecci\u00f3n', + 'pl': 'L\u00e1ser de Pulso', + 'PWR': 'POT', + 'rg': 'Ca\u00f1\u00f3n de Riel', + 'range': 'rango', + 'rate': 'ratio', + 'Reactive Surface Composite': 'Blindaje Reactivo', + 'recharge': 'recargar', + 'rf': 'Refineria', + 'refuel time': 'Tiempo para repostar', + 'Reinforced Alloy': 'Armadura reforzada', + 'reload': 'Recargar', + 'rename': 'Renombrar', + 'repair': 'Reparar', + 'reset': 'Reiniciar', + 'ret': 'PLE', + 'retracted': 'plegadas', + 'retrofit costs': 'costes de equipamiento', + 'retrofit from': 'equipamiento desde', + 'ROF': 'RDF', + 'S': 'P', + 'save': 'guardar', + 'sc': 'sc\u00e1ner', + 'PHRASE_SELECT_BUILDS': 'Selecciona equipamientos para comparar', + 'sell': 'Vender', + 's': 'Sensores', + 'settings': 'Configuraci\u00f3n', + 'sb': 'Potenciador de Escudos', + 'scb': 'C\u00e9lula de Energ\u00eda de Escudos', + 'sg': 'Generador de escudos', + 'shields': 'Escudos', + 'ship': 'Nave ', + 'ships': 'Naves', + 'shortened': 'Abreviado', + 'size': 'Tama\u00f1o', + 'skip': 'omitir', + 'small': 'Peque\u00f1o', + 'speed': 'velocidad', + 'standard': 'est\u00e1ndar', + 'Standard Docking Computer': 'Computador de Atraque Est\u00e1ndar', + 'Stock': 'De serie', + 'SYS': 'SIS', + 'T_LOAD': 'c-t\u00e9rmica', + 't': 'Propulsores', + 'time': 'Tiempo', + 'tp': 'Anclaje de torpedo', + 'total': 'Total', + 'total range': 'Rango total', + 'turret': 'torreta', + 'type': 'Tipo', + 'unladen': 'Sin carga', + 'PHRASE_UPDATE_RDY': 'Actualizacion disponible! Haz click para recargar', + 'URL': 'Enlace', + 'utility': 'utilidad', + 'utility mounts': 'monturas de utilidad', + 'version': 'Versi\u00f3n', + 'WEP': 'ARM', + 'yes': 'si', + 'PHRASE_BACKUP_DESC': 'Copia de seguridad de todos los datos de Coriolis para guardarlos o transferirlos a otro navegador\/dispositivo' +}; diff --git a/app/js/i18n/fr.js b/app/js/i18n/fr.js index aa82e502..f93f14d0 100644 --- a/app/js/i18n/fr.js +++ b/app/js/i18n/fr.js @@ -1,200 +1,198 @@ -angular.module('app').config(['$translateProvider', 'localeFormatProvider', function($translateProvider, localeFormatProvider) { - // Declare number format settings - localeFormatProvider.addFormat('fr', { - decimal: ',', - thousands: '.', - grouping: [3], - currency: ['', ' €'], - dateTime: '%A, le %e %B %Y, %X', - date: '%d/%m/%Y', - time: '%H:%M:%S', - periods: ['AM', 'PM'], // unused - days: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'], - shortDays: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'], - months: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'], - shortMonths: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'] - }); - $translateProvider.translations('fr', { - PHRASE_EXPORT_DESC: 'Export détaillé en JSON de votre configuration pour utilisation sur d\'autres sites et outils', - 'A-Rated': 'Classe-A ', - about: 'à propos', - added: 'ajouté', - Advanced: 'Avancé', - 'Advanced Discovery Scanner': 'Détecteur découverte avancé', - agility: 'manœuvrabilité', - ammo: 'munitions', - PHRASE_CONFIRMATION: 'Êtes-vous sûr ?', - armour: 'Armure', - am: 'Unité de maintenance de terrain auto', - available: 'Disponibilité', - backup: 'sauvegarde', - 'Basic Discovery Scanner': 'Détecteur découverte simple', - bl: 'Rayon Laser', - bins: 'bennes', - build: 'Configuration', - 'build name': 'Nom de la configuration', - builds: 'Configurations', - bh: 'Coque', - ul: 'Laser à rafale', - buy: 'Acheter', - cancel: 'Annuler', - c: 'Canon', - cargo: 'Soute', - 'Cargo Hatch': 'Écoutille de soute', - cr: 'Compartiment de soute', - cs: 'Détecteur de cargaison', - cells: 'Cellules', - 'Chaff Launcher': 'Lanceur de paillettes', - close: 'fermer', - cc: 'Contrôleur de Collecteur', - compare: 'comparer', - 'compare all': 'tout comparer', - comparison: 'comparaison', - comparisons: 'comparaisons', - component: 'composant', - cost: 'coût', - costs: 'coûts', - cm: 'Contre-mesure', - create: 'Créer', - 'create new': 'Créer nouveau', - credits: 'crédits', - damage: 'Dégâts', - delete: 'supprimer', - 'delete all': 'tout supprimer', - dep: 'depl', - deployed: 'déployé', - 'detailed export': 'export détaillé', - 'Detailed Surface Scanner': 'Détecteur surface détaillé', - disabled: 'désactivé', - discount: 'réduction', - Distruptor: 'Disrupteur', - dc: 'Ordinateur d\'appontage', - done: 'Valider', - 'edit data': 'Editer donnée', - efficiency: 'efficacité', - 'Electronic Countermeasure': 'Contre-mesures électroniques', - empty: 'Vide', - 'enter name': 'Entrer un nom', - fixed: 'fixé', - fc: 'Canon à fragmentation', - fd: 'Réacteur FSD', - ws: 'Détecteur de sillage FSD', - fi: 'Intercepteur de réacteur FSD', - fuel: 'carburant', - fs: 'Récupérateur de carburant', - ft: 'Réservoir de carburant', - fx: 'Contrôleur de ravitailleur', - 'full tank': 'Réservoir plein', - Gimballed: 'Point', - hardpoints: 'Points d\'emport', - hb: 'Contrôle de patelle perce-soute', - 'Heat Sink Launcher': 'Éjecteur de dissipateur thermique', - huge: 'Très grand', - hull: 'Coque', - hr: 'Ensemble de mesures permettant de renforcer la coque', - 'Imperial Hammer': 'Marteau impérial', - import: 'Importer', - 'import all': 'Importer tout', - insurance: 'Assurance', - 'Intermediate Discovery Scanner': 'Détecteur découverte intermédiaire', - 'internal compartments': 'compartiments internes', - 'jump range': 'Distance de saut', - jumps: 'Sauts', - kw: 'Détecteur d\'avis de recherche', - laden: 'chargé', - language: 'Langue', - large: 'large', - ls: 'Systèmes de survie', - 'Lightweight Alloy': 'alliage léger', - 'limpets': 'Patelles', - 'lock factor': 'facteur inhibition de masse', - LS: 'SL', - LY: 'AL', - mass: 'Masse', - 'max mass': 'masse max', - 'Military Grade Composite': 'Composite militaire', - nl: 'Lance-mines', - 'Mining Lance': 'Lance de minage', - ml: 'Laser minier', - 'Mirrored Surface Composite': 'Composite à surface miroir', - mr: 'Batterie de missiles', - mc: 'Canon multiple', - 'net cost': 'coûts nets', - no: 'non', - PHRASE_NO_BUILDS: 'Aucune configuration ajoutée pour comparaison', - PHRASE_NO_RETROCH: 'Configuration non modifiée', - none: 'aucun', - 'none created': 'Rien de créé', - off: 'éteint', - on: 'allumé', - 'optimal mass': 'masse optimale', - 'optimize mass': 'optimiser masse', - overwrite: 'remplacer', - Pacifier: 'Pacificateur', - PHRASE_IMPORT: 'Coller ici votre JSON à importer', - pen: 'pén.', - penetration: 'pénétration', - permalink: 'lien durable', - pa: 'accélérateur plasma', - 'Point Defence': 'Défense ponctuelle', - power: 'énergie', - pd: 'Répartiteur de puissance', - pp: 'Générateur', - priority: 'priorité', - psg: 'générateur de bouclier prisme', - proceed: 'continuer', - pc: 'Contrôleur de prospecteur', - pl: 'Laser à impulsion', - PWR: 'P', - rg: 'Canon électrique', - range: 'portée', - rate: 'cadence', - 'Reactive Surface Composite': 'Composite à surface réactive', - recharge: 'recharger', - rf: 'Raffinerie', - 'refuel time': 'Temps de remplissage', - 'Reinforced Alloy': 'alliage renforcé', - reload: 'recharger', - rename: 'renommer', - repair: 'réparer', - reset: 'Réinitialisation', - ret: 'esc', - retracted: 'escamoté', - 'retrofit costs': 'Valeur de rachat', - 'retrofit from': 'Racheter de', - ROF: 'cadence', - save: 'sauvegarder', - sc: 'scanner', - PHRASE_SELECT_BUILDS: 'Sélectionner les configurations à comparer', - sell: 'vendre', - s: 'Capteurs', - settings: 'paramètres', - sb: 'Survolteur de bouclier', - scb: 'Réserve de cellules d\'énergie', - sg: 'Générateur de bouclier', - shields: 'boucliers', - ship: 'vaisseau', - ships: 'vaisseaux', - shortened: 'raccourci', - size: 'taille', - skip: 'Suivant', - small: 'petit', - speed: 'vitesse', - 'Standard Docking Computer': 'Ordinateur d\'appontage standard', - Stock: 'de base', - T_LOAD: 'Charge thermique', - 'The Retributor': 'Le Rétributeur', - t: 'propulseurs', - time: 'temps', - tp: 'Tube lance-torpille', - 'total range': 'Distance maximale', - turret: 'tourelle', - unladen: 'Non chargé', - PHRASE_UPDATE_RDY: 'Mise à jour disponible ! Cliquez ici pour mettre à jour', - utility: 'utilitaire', - 'utility mounts': 'Support utilitaire', - WEP: 'ARM', - yes: 'oui', - PHRASE_BACKUP_DESC: 'Exportation détaillée des données de Coriolis pour l\'utilisation dans d\'autres sites et outils' - }); -}]); +export const formats = { + decimal: ',', + thousands: '.', + grouping: [3], + currency: ['', ' €'], + dateTime: '%A, le %e %B %Y, %X', + date: '%d/%m/%Y', + time: '%H:%M:%S', + periods: ['AM', 'PM'], // unused + days: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'], + shortDays: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'], + months: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'], + shortMonths: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'] +}; + +export const terms = { + PHRASE_EXPORT_DESC: 'Export détaillé en JSON de votre configuration pour utilisation sur d\'autres sites et outils', + 'A-Rated': 'Classe-A ', + about: 'à propos', + added: 'ajouté', + Advanced: 'Avancé', + 'Advanced Discovery Scanner': 'Détecteur découverte avancé', + agility: 'manœuvrabilité', + ammo: 'munitions', + PHRASE_CONFIRMATION: 'Êtes-vous sûr ?', + armour: 'Armure', + am: 'Unité de maintenance de terrain auto', + available: 'Disponibilité', + backup: 'sauvegarde', + 'Basic Discovery Scanner': 'Détecteur découverte simple', + bl: 'Rayon Laser', + bins: 'bennes', + build: 'Configuration', + 'build name': 'Nom de la configuration', + builds: 'Configurations', + bh: 'Coque', + ul: 'Laser à rafale', + buy: 'Acheter', + cancel: 'Annuler', + c: 'Canon', + cargo: 'Soute', + 'Cargo Hatch': 'Écoutille de soute', + cr: 'Compartiment de soute', + cs: 'Détecteur de cargaison', + cells: 'Cellules', + 'Chaff Launcher': 'Lanceur de paillettes', + close: 'fermer', + cc: 'Contrôleur de Collecteur', + compare: 'comparer', + 'compare all': 'tout comparer', + comparison: 'comparaison', + comparisons: 'comparaisons', + component: 'composant', + cost: 'coût', + costs: 'coûts', + cm: 'Contre-mesure', + create: 'Créer', + 'create new': 'Créer nouveau', + credits: 'crédits', + damage: 'Dégâts', + delete: 'supprimer', + 'delete all': 'tout supprimer', + dep: 'depl', + deployed: 'déployé', + 'detailed export': 'export détaillé', + 'Detailed Surface Scanner': 'Détecteur surface détaillé', + disabled: 'désactivé', + discount: 'réduction', + Distruptor: 'Disrupteur', + dc: 'Ordinateur d\'appontage', + done: 'Valider', + 'edit data': 'Editer donnée', + efficiency: 'efficacité', + 'Electronic Countermeasure': 'Contre-mesures électroniques', + empty: 'Vide', + 'enter name': 'Entrer un nom', + fixed: 'fixé', + fc: 'Canon à fragmentation', + fd: 'Réacteur FSD', + ws: 'Détecteur de sillage FSD', + fi: 'Intercepteur de réacteur FSD', + fuel: 'carburant', + fs: 'Récupérateur de carburant', + ft: 'Réservoir de carburant', + fx: 'Contrôleur de ravitailleur', + 'full tank': 'Réservoir plein', + Gimballed: 'Point', + hardpoints: 'Points d\'emport', + hb: 'Contrôle de patelle perce-soute', + 'Heat Sink Launcher': 'Éjecteur de dissipateur thermique', + huge: 'Très grand', + hull: 'Coque', + hr: 'Ensemble de mesures permettant de renforcer la coque', + 'Imperial Hammer': 'Marteau impérial', + import: 'Importer', + 'import all': 'Importer tout', + insurance: 'Assurance', + 'Intermediate Discovery Scanner': 'Détecteur découverte intermédiaire', + 'internal compartments': 'compartiments internes', + 'jump range': 'Distance de saut', + jumps: 'Sauts', + kw: 'Détecteur d\'avis de recherche', + laden: 'chargé', + language: 'Langue', + large: 'large', + ls: 'Systèmes de survie', + 'Lightweight Alloy': 'alliage léger', + 'limpets': 'Patelles', + 'lock factor': 'facteur inhibition de masse', + LS: 'SL', + LY: 'AL', + mass: 'Masse', + 'max mass': 'masse max', + 'Military Grade Composite': 'Composite militaire', + nl: 'Lance-mines', + 'Mining Lance': 'Lance de minage', + ml: 'Laser minier', + 'Mirrored Surface Composite': 'Composite à surface miroir', + mr: 'Batterie de missiles', + mc: 'Canon multiple', + 'net cost': 'coûts nets', + no: 'non', + PHRASE_NO_BUILDS: 'Aucune configuration ajoutée pour comparaison', + PHRASE_NO_RETROCH: 'Configuration non modifiée', + none: 'aucun', + 'none created': 'Rien de créé', + off: 'éteint', + on: 'allumé', + 'optimal mass': 'masse optimale', + 'optimize mass': 'optimiser masse', + overwrite: 'remplacer', + Pacifier: 'Pacificateur', + PHRASE_IMPORT: 'Coller ici votre JSON à importer', + pen: 'pén.', + penetration: 'pénétration', + permalink: 'lien durable', + pa: 'accélérateur plasma', + 'Point Defence': 'Défense ponctuelle', + power: 'énergie', + pd: 'Répartiteur de puissance', + pp: 'Générateur', + priority: 'priorité', + psg: 'générateur de bouclier prisme', + proceed: 'continuer', + pc: 'Contrôleur de prospecteur', + pl: 'Laser à impulsion', + PWR: 'P', + rg: 'Canon électrique', + range: 'portée', + rate: 'cadence', + 'Reactive Surface Composite': 'Composite à surface réactive', + recharge: 'recharger', + rf: 'Raffinerie', + 'refuel time': 'Temps de remplissage', + 'Reinforced Alloy': 'alliage renforcé', + reload: 'recharger', + rename: 'renommer', + repair: 'réparer', + reset: 'Réinitialisation', + ret: 'esc', + retracted: 'escamoté', + 'retrofit costs': 'Valeur de rachat', + 'retrofit from': 'Racheter de', + ROF: 'cadence', + save: 'sauvegarder', + sc: 'scanner', + PHRASE_SELECT_BUILDS: 'Sélectionner les configurations à comparer', + sell: 'vendre', + s: 'Capteurs', + settings: 'paramètres', + sb: 'Survolteur de bouclier', + scb: 'Réserve de cellules d\'énergie', + sg: 'Générateur de bouclier', + shields: 'boucliers', + ship: 'vaisseau', + ships: 'vaisseaux', + shortened: 'raccourci', + size: 'taille', + skip: 'Suivant', + small: 'petit', + speed: 'vitesse', + 'Standard Docking Computer': 'Ordinateur d\'appontage standard', + Stock: 'de base', + T_LOAD: 'Charge thermique', + 'The Retributor': 'Le Rétributeur', + t: 'propulseurs', + time: 'temps', + tp: 'Tube lance-torpille', + 'total range': 'Distance maximale', + turret: 'tourelle', + unladen: 'Non chargé', + PHRASE_UPDATE_RDY: 'Mise à jour disponible ! Cliquez ici pour mettre à jour', + utility: 'utilitaire', + 'utility mounts': 'Support utilitaire', + WEP: 'ARM', + yes: 'oui', + PHRASE_BACKUP_DESC: 'Exportation détaillée des données de Coriolis pour l\'utilisation dans d\'autres sites et outils' +}; diff --git a/app/js/i18n/it.js b/app/js/i18n/it.js index 8c5248f7..c8f76e5d 100644 --- a/app/js/i18n/it.js +++ b/app/js/i18n/it.js @@ -1,133 +1,129 @@ -angular.module('app').config(['$translateProvider', 'localeFormatProvider', function($translateProvider, localeFormatProvider) { +export const formats = { + decimal: ',', + thousands: '.', + grouping: [3], + currency: ['€', ''], + dateTime: '%A %e %B %Y, %X', + date: '%d/%m/%Y', + time: '%H:%M:%S', + periods: ['AM', 'PM'], // unused + days: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato'], + shortDays: ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'], + months: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'], + shortMonths: ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'] +}; - // Declare number format settings - localeFormatProvider.addFormat('es', { - decimal: ',', - thousands: '.', - grouping: [3], - currency: ['€', ''], - dateTime: '%A %e %B %Y, %X', - date: '%d/%m/%Y', - time: '%H:%M:%S', - periods: ['AM', 'PM'], // unused - days: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato'], - shortDays: ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'], - months: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'], - shortMonths: ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'] - }); - - $translateProvider.translations('it', { - PHRASE_EXPORT_DESC: 'Un export dettagliato in formato JSON della tua configurazione per essere usato in altri siti o tools', - 'A-Rated': 'Classe A', - about: 'Info su Coriolis', - action: 'azione', - added: 'aggiunto', - Advanced: 'Avanzato', - agility: 'agilità', - ammo: 'munizioni', - PHRASE_CONFIRMATION: 'Sei sicuro ?', - armour: 'armatura', - available: 'disponibile', - bins: 'contenitore', - build: 'configurazione', - 'build name': 'Nome Configurazione', - builds: 'configurazioni', - buy: 'compra', - cancel: 'cancella', - cells: 'celle', - close: 'chiudi', - compare: 'confronta', - 'compare all': 'confronta tutti', - comparison: 'comparazione', - comparisons: 'comparazioni', - component: 'componente', - cost: 'costo', - costs: 'costi', - cm: 'Contromisure', - create: 'crea', - 'create new': 'crea nuovo', - credits: 'crediti', - damage: 'danno', - delete: 'elimina', - 'delete all': 'elimina tutto', - dep: 'dep', - deployed: 'deployed', - 'detailed export': 'esportazione dettagliata', - disabled: 'disabilita', - discount: 'sconto', - done: 'fatto', - 'edit data': 'modifica i dati', - efficiency: 'efficenza', - empty: 'vuoto', - Enforcer: 'Rinforzatore', - 'enter name': 'Inserisci un nome', - export: 'esporta', - fixed: 'fissi', - fuel: 'carburante', - 'full tank': 'Serbatoio Pieno', - huge: 'enorme', - hull: 'corazza', - import: 'importa', - 'import all': 'importa tutto', - insurance: 'assicurazione', - 'internal compartments': 'compartimenti interni', - 'jump range': 'distanza di salto', - jumps: 'salti', - laden: 'carico', - language: 'lingua', - large: 'largo', - mass: 'massa', - max: 'massimo', - 'max mass': 'massa massimale', - medium: 'medio', - 'net cost': 'costo netto', - PHRASE_NO_BUILDS: 'nessuna configurazione è stata aggiunta per la comparazione!', - PHRASE_NO_RETROCH: 'Nessun cambiamento di Retrofitting', - none: 'nessuno', - 'none created': 'nessuno creato', - optimal: 'ottimale', - 'optimal mass': 'massa ottimale', - 'optimize mass': 'ottimizza la massa', - overwrite: 'sovrasscrivi', - PHRASE_IMPORT: 'Incolla un JSON o importalo qua', - penetration: 'penetrazione', - power: 'potenza', - priority: 'priorità', - proceed: 'procedi', - range: 'distanza', - rate: 'rateo', - recharge: 'ricarica', - reload: 'ricarica', - rename: 'rinomina', - repair: 'ripara', - reset: 'resetta', - retracted: 'retratti', - 'retrofit costs': 'costi di retrofit', - 'retrofit from': 'retrofit da', - save: 'salva', - sell: 'vendi', - settings: 'impostazioni', - shields: 'scudi', - ship: 'nave', - ships: 'navi', - shortened: 'accorciato', - size: 'grandezza', - skip: 'salta', - small: 'piccolo', - speed: 'velocità', - Stock: 'appena comprata', - t: 'thrusters', - time: 'tempo', - total: 'totale', - 'total range': 'distanza totale', - turret: 'torrette', - type: 'tipo', - unladen: 'scarico', - PHRASE_UPDATE_RDY: 'Aggiornamenti disponibili ! Clicca per Aggiornare', - utility: 'supporti', - 'utility mounts': 'supporti di utilità', - version: 'versione', - yes: 'sì', - PHRASE_BACKUP_DESC: 'Esportazione di tutti i dati su Coriolis per salvarli o trasferirli in un altro Browser/dispositivo' - }); -}]); +export const terms = { + PHRASE_EXPORT_DESC: 'Un export dettagliato in formato JSON della tua configurazione per essere usato in altri siti o tools', + 'A-Rated': 'Classe A', + about: 'Info su Coriolis', + action: 'azione', + added: 'aggiunto', + Advanced: 'Avanzato', + agility: 'agilità', + ammo: 'munizioni', + PHRASE_CONFIRMATION: 'Sei sicuro ?', + armour: 'armatura', + available: 'disponibile', + bins: 'contenitore', + build: 'configurazione', + 'build name': 'Nome Configurazione', + builds: 'configurazioni', + buy: 'compra', + cancel: 'cancella', + cells: 'celle', + close: 'chiudi', + compare: 'confronta', + 'compare all': 'confronta tutti', + comparison: 'comparazione', + comparisons: 'comparazioni', + component: 'componente', + cost: 'costo', + costs: 'costi', + cm: 'Contromisure', + create: 'crea', + 'create new': 'crea nuovo', + credits: 'crediti', + damage: 'danno', + delete: 'elimina', + 'delete all': 'elimina tutto', + dep: 'dep', + deployed: 'deployed', + 'detailed export': 'esportazione dettagliata', + disabled: 'disabilita', + discount: 'sconto', + done: 'fatto', + 'edit data': 'modifica i dati', + efficiency: 'efficenza', + empty: 'vuoto', + Enforcer: 'Rinforzatore', + 'enter name': 'Inserisci un nome', + export: 'esporta', + fixed: 'fissi', + fuel: 'carburante', + 'full tank': 'Serbatoio Pieno', + huge: 'enorme', + hull: 'corazza', + import: 'importa', + 'import all': 'importa tutto', + insurance: 'assicurazione', + 'internal compartments': 'compartimenti interni', + 'jump range': 'distanza di salto', + jumps: 'salti', + laden: 'carico', + language: 'lingua', + large: 'largo', + mass: 'massa', + max: 'massimo', + 'max mass': 'massa massimale', + medium: 'medio', + 'net cost': 'costo netto', + PHRASE_NO_BUILDS: 'nessuna configurazione è stata aggiunta per la comparazione!', + PHRASE_NO_RETROCH: 'Nessun cambiamento di Retrofitting', + none: 'nessuno', + 'none created': 'nessuno creato', + optimal: 'ottimale', + 'optimal mass': 'massa ottimale', + 'optimize mass': 'ottimizza la massa', + overwrite: 'sovrasscrivi', + PHRASE_IMPORT: 'Incolla un JSON o importalo qua', + penetration: 'penetrazione', + power: 'potenza', + priority: 'priorità', + proceed: 'procedi', + range: 'distanza', + rate: 'rateo', + recharge: 'ricarica', + reload: 'ricarica', + rename: 'rinomina', + repair: 'ripara', + reset: 'resetta', + retracted: 'retratti', + 'retrofit costs': 'costi di retrofit', + 'retrofit from': 'retrofit da', + save: 'salva', + sell: 'vendi', + settings: 'impostazioni', + shields: 'scudi', + ship: 'nave', + ships: 'navi', + shortened: 'accorciato', + size: 'grandezza', + skip: 'salta', + small: 'piccolo', + speed: 'velocità', + Stock: 'appena comprata', + t: 'thrusters', + time: 'tempo', + total: 'totale', + 'total range': 'distanza totale', + turret: 'torrette', + type: 'tipo', + unladen: 'scarico', + PHRASE_UPDATE_RDY: 'Aggiornamenti disponibili ! Clicca per Aggiornare', + utility: 'supporti', + 'utility mounts': 'supporti di utilità', + version: 'versione', + yes: 'sì', + PHRASE_BACKUP_DESC: 'Esportazione di tutti i dati su Coriolis per salvarli o trasferirli in un altro Browser/dispositivo' +}; diff --git a/app/js/i18n/languages.js b/app/js/i18n/languages.js deleted file mode 100644 index ad57247a..00000000 --- a/app/js/i18n/languages.js +++ /dev/null @@ -1,23 +0,0 @@ -angular.module('app').config(['$translateProvider', function($translateProvider) { - $translateProvider - .useSanitizeValueStrategy('escapeParameters') - .useStorage('Persist') - .fallbackLanguage('en') // Use English as default/fallback language - .registerAvailableLanguageKeys(['en', 'de', 'es', 'fr', 'it', 'ru'], { - 'en*': 'en', - 'de*': 'de', - 'es*': 'es', - 'fr*': 'fr', - 'it*': 'it', - 'ru*': 'ru' - }) - .determinePreferredLanguage(); -}]) -.value('Languages', { - en: 'English', - de: 'Deutsh', - it: 'Italiano', - es: 'Español', - fr: 'Français', - ru: 'ру́сский' -}); diff --git a/app/js/i18n/ru.js b/app/js/i18n/ru.js index e6b61363..905a41f0 100644 --- a/app/js/i18n/ru.js +++ b/app/js/i18n/ru.js @@ -1,234 +1,230 @@ -angular.module('app').config(['$translateProvider', 'localeFormatProvider', function($translateProvider, localeFormatProvider) { +export const formats = { + decimal: ',', + thousands: '\xa0', + grouping: [3], + currency: ['', ' руб.'], + dateTime: '%A, %e %B %Y г. %X', + date: '%d.%m.%Y', + time: '%H:%M:%S', + periods: ['AM', 'PM'], + days: ['воскресенье', 'понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота'], + shortDays: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'], + months: ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'], + shortMonths: ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'] +}; - // Declare number format settings - localeFormatProvider.addFormat('ru', { - decimal: ',', - thousands: '\xa0', - grouping: [3], - currency: ['', ' руб.'], - dateTime: '%A, %e %B %Y г. %X', - date: '%d.%m.%Y', - time: '%H:%M:%S', - periods: ['AM', 'PM'], - days: ['воскресенье', 'понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота'], - shortDays: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'], - months: ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'], - shortMonths: ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'] - }); +export const terms = { + PHRASE_EXPORT_DESC: 'Подробный экспорта JSON вашего телосложения для использования в других местах и инструментов', + 'A-Rated': 'А-Класса', + about: 'О сайте', + action: 'Действие', + added: 'Добавлено', + Advanced: 'Продвинутый', + 'Advanced Discovery Scanner': 'Продвинутый астросканер', + agility: 'Маневренность', + alpha: 'Альфа', + ammo: 'Боекомплект', + PHRASE_CONFIRMATION: 'Вы уверены?', + armour: 'Броня', + am: 'Ремонтный модуль', + available: 'доступно', + backup: 'Резервная копия', + 'Basic Discovery Scanner': 'Стандартный исследовательский сканер', + bl: 'Лучевой лазер', + beta: 'Бета', + bins: 'контейнеры', + boost: 'форсаж', + build: 'cборка', + 'build name': 'название сборки', + builds: 'cборки', + bh: 'Корпус', + ul: 'Мультиимпульсный лазер', + buy: 'купить', + cancel: 'отменить', + c: 'Пушка', + capital: 'Крупный', + cargo: 'Груз', + 'Cargo Hatch': 'Грузовой люк', + cr: 'Грузовой отсек', + cs: 'Сканер груза', + cells: 'Ячейки', + 'Chaff Launcher': 'Постановщик помех', + close: 'закрыть', + cc: 'Контроллер "дрон-сборщик"', + compare: 'сравнить ', + 'compare all': 'сравнить все', + comparison: 'сравнение', + comparisons: 'сравнения', + component: 'Компонент', + cost: 'Стоимость', + costs: 'Расходы', + cm: 'Контрмеры', + CR: 'кр.', + create: 'создать', + 'create new': 'Создать новый', + credits: 'Кредиты', + Cytoscrambler: 'сайтоскрамблер', + damage: 'Урон', + delete: 'Удалить', + 'delete all': 'Удалить все', + dep: 'Вып', + deployed: 'Открыты', + 'detailed export': 'Подробный экспорт', + 'Detailed Surface Scanner': 'Подробный сканер поверхности', + disabled: 'Отключено', + discount: 'Скидка', + Distruptor: 'Дисраптор', + dc: 'Стыковочный компьютер', + done: 'готово', + DPS: 'УВС', + 'edit data': 'Редактирование', + efficiency: 'Эффективность', + 'Electronic Countermeasure': 'Электронная противомера', + empty: 'пусто', + Enforcer: 'Энфорсер', + ENG: 'ДВГ', + 'enter name': 'Введите имя', + EPS: 'ЭВС', + export: 'Экспорт', + fixed: 'Фиксированое', + forum: 'Форум', + fc: 'Осколочное Орудие', + fd: 'Двигатель FSD', + ws: 'FSD Сканнер', - $translateProvider.translations('ru', { - PHRASE_EXPORT_DESC: 'Подробный экспорта JSON вашего телосложения для использования в других местах и инструментов', - 'A-Rated': 'А-Класса', - about: 'О сайте', - action: 'Действие', - added: 'Добавлено', - Advanced: 'Продвинутый', - 'Advanced Discovery Scanner': 'Продвинутый астросканер', - agility: 'Маневренность', - alpha: 'Альфа', - ammo: 'Боекомплект', - PHRASE_CONFIRMATION: 'Вы уверены?', - armour: 'Броня', - am: 'Ремонтный модуль', - available: 'доступно', - backup: 'Резервная копия', - 'Basic Discovery Scanner': 'Стандартный исследовательский сканер', - bl: 'Лучевой лазер', - beta: 'Бета', - bins: 'контейнеры', - boost: 'форсаж', - build: 'cборка', - 'build name': 'название сборки', - builds: 'cборки', - bh: 'Корпус', - ul: 'Мультиимпульсный лазер', - buy: 'купить', - cancel: 'отменить', - c: 'Пушка', - capital: 'Крупный', - cargo: 'Груз', - 'Cargo Hatch': 'Грузовой люк', - cr: 'Грузовой отсек', - cs: 'Сканер груза', - cells: 'Ячейки', - 'Chaff Launcher': 'Постановщик помех', - close: 'закрыть', - cc: 'Контроллер "дрон-сборщик"', - compare: 'сравнить ', - 'compare all': 'сравнить все', - comparison: 'сравнение', - comparisons: 'сравнения', - component: 'Компонент', - cost: 'Стоимость', - costs: 'Расходы', - cm: 'Контрмеры', - CR: 'кр.', - create: 'создать', - 'create new': 'Создать новый', - credits: 'Кредиты', - Cytoscrambler: 'сайтоскрамблер', - damage: 'Урон', - delete: 'Удалить', - 'delete all': 'Удалить все', - dep: 'Вып', - deployed: 'Открыты', - 'detailed export': 'Подробный экспорт', - 'Detailed Surface Scanner': 'Подробный сканер поверхности', - disabled: 'Отключено', - discount: 'Скидка', - Distruptor: 'Дисраптор', - dc: 'Стыковочный компьютер', - done: 'готово', - DPS: 'УВС', - 'edit data': 'Редактирование', - efficiency: 'Эффективность', - 'Electronic Countermeasure': 'Электронная противомера', - empty: 'пусто', - Enforcer: 'Энфорсер', - ENG: 'ДВГ', - 'enter name': 'Введите имя', - EPS: 'ЭВС', - export: 'Экспорт', - fixed: 'Фиксированое', - forum: 'Форум', - fc: 'Осколочное Орудие', - fd: 'Двигатель FSD', - ws: 'FSD Сканнер', - - fi: 'Перехватчик FSD', - fuel: 'Топливо', - fs: 'Топливосборщик', - ft: 'Топливный бак', - fx: 'Контроллер Дрона-заправщика', - 'full tank': 'Полный бак', - Gimballed: 'Шарнирное', - H: 'O', - hardpoints: 'Орудийные порты', - hb: 'Контроллер "дрон-взломщик"', - 'Heat Sink Launcher': 'Теплоотводная ПУ', - huge: 'огромный', - hull: 'Корпус', - hr: 'Набор усиления корпуса', - 'Imperial Hammer': 'Имперский Молот', - import: 'импортировать ', - 'import all': 'импортировать все', - insurance: 'Страховка', - 'Intermediate Discovery Scanner': 'Средний исследовательский сканер', - 'internal compartments': 'внутренние отсеки', - 'jump range': 'Дальность прыжка', - jumps: 'Прыжков', - kw: 'Полицейский сканер', - L: 'б', - laden: 'Груженый', - language: 'Язык', - large: 'большой', - ls: 'Система жизнеобеспечения', - 'Lightweight Alloy': 'Легкий сплав', - 'limpets': 'Дроны', - 'lock factor': 'Масс. блок', - LS: 'Св.сек', - LY: 'Св.лет', - M: 'С', - 'm/s': 'м/с', - mass: 'Масса', - max: 'Макс', - 'max mass': 'Максимальная масса', - medium: 'Средний', - 'Military Grade Composite': 'Военный композит', - nl: 'Минноукладчик', - 'Mining Lance': 'Бурильная сулица', - ml: 'Бурильный лазер', - 'Mirrored Surface Composite': 'Зеркальный композит', - mr: 'Ракетная установка', - mc: 'Многоствольное орудие', - 'net cost': 'разница в цене', - no: 'Нет', - PHRASE_NO_BUILDS: 'Нечего сравнивать', - PHRASE_NO_RETROCH: 'нет ранних версий сборки\конфигурации', - none: 'ни один', - 'none created': 'не создано', - off: 'выкл', - on: 'вкл', - optimal: 'Оптимальный', - 'optimal mass': 'Оптимальная масса', - 'optimize mass': 'Оптимизировать массу', - overwrite: 'перезаписать', - Pacifier: 'Миротворец', - 'Pack-Hound': 'Ракета "Гончая"', - PHRASE_IMPORT: 'Для импорта вставьте код в эту форму', - pen: 'ПБ', - penetration: 'Пробитие', - permalink: 'Постоянная ссылка', - pa: 'Ускоритель плазмы', - 'Point Defence': 'Противоракетная защита', - power: 'Мощность', - pd: 'Распределитель энергии', - pp: 'Реактор', - pri: 'Осн', - priority: 'Приоритет', - psg: 'Генератор призматического щита', - proceed: 'продолжить', - pc: 'Контроллер "Дрон-исследователь"', - pl: 'Импульсный лазер', - PWR: 'Эн', - rg: 'Рельсотрон', - range: 'Дальность', - rate: 'скорость', - 'Reactive Surface Composite': 'Динамическая защита', - recharge: 'Перезарядка', - rf: 'Переработка', - 'refuel time': 'Время дозаправки', - 'Reinforced Alloy': 'Усиленный сплав', - reload: 'Перезарядить', - rename: 'Переименовать', - repair: 'Починка', - reset: 'Сброс', - ret: 'Убр.', - retracted: 'Убрано', - 'retrofit costs': 'цена модификации', - 'retrofit from': 'модификация от', - ROF: 'В/сек', - S: 'М', - save: 'Сохранить', - sc: 'Сканер', - PHRASE_SELECT_BUILDS: 'Выберите конфигурацию для сравнения', - sell: 'Продать', - s: 'Сенсоры', - settings: 'Настройки', - sb: 'Усилитель щита', - scb: 'Батареи перезарядки щита', - sg: 'Генератор щита', - shields: 'Щиты', - ship: 'Корабль', - ships: 'Корабли', - shortened: 'Укороченный', - size: 'размер', - skip: 'пропустить', - small: 'Малый', - speed: 'скорость', - standard: 'Стандартный', - 'Standard Docking Computer': 'Стандартный стыковочный компьютер', - Stock: 'Стандартная комплектация', - SYS: 'СИС', - T: 'Т', - T_LOAD: 'Тепл.', - 'The Retributor': '"Возмездие"', - t: 'Двигатели', - time: 'Время', - tp: 'Торпедный аппарат', - total: 'Всего', - 'total range': 'Общий радиус', - turret: 'Туррель', - type: 'Тип', - U: 'В', - unladen: 'Пустой', - PHRASE_UPDATE_RDY: 'Доступно обновление. Нажмите для обновления.', - URL: 'Ссылка', - utility: 'Вспомогательное', - 'utility mounts': 'Вспомогательное оборудование', - version: 'Версия', - WEP: 'ОРУ', - yes: 'Да', - PHRASE_BACKUP_DESC: 'Сохраните все данные перед переносом в другой браузер или устройство' - }); -}]); + fi: 'Перехватчик FSD', + fuel: 'Топливо', + fs: 'Топливосборщик', + ft: 'Топливный бак', + fx: 'Контроллер Дрона-заправщика', + 'full tank': 'Полный бак', + Gimballed: 'Шарнирное', + H: 'O', + hardpoints: 'Орудийные порты', + hb: 'Контроллер "дрон-взломщик"', + 'Heat Sink Launcher': 'Теплоотводная ПУ', + huge: 'огромный', + hull: 'Корпус', + hr: 'Набор усиления корпуса', + 'Imperial Hammer': 'Имперский Молот', + import: 'импортировать ', + 'import all': 'импортировать все', + insurance: 'Страховка', + 'Intermediate Discovery Scanner': 'Средний исследовательский сканер', + 'internal compartments': 'внутренние отсеки', + 'jump range': 'Дальность прыжка', + jumps: 'Прыжков', + kw: 'Полицейский сканер', + L: 'б', + laden: 'Груженый', + language: 'Язык', + large: 'большой', + ls: 'Система жизнеобеспечения', + 'Lightweight Alloy': 'Легкий сплав', + 'limpets': 'Дроны', + 'lock factor': 'Масс. блок', + LS: 'Св.сек', + LY: 'Св.лет', + M: 'С', + 'm/s': 'м/с', + mass: 'Масса', + max: 'Макс', + 'max mass': 'Максимальная масса', + medium: 'Средний', + 'Military Grade Composite': 'Военный композит', + nl: 'Минноукладчик', + 'Mining Lance': 'Бурильная сулица', + ml: 'Бурильный лазер', + 'Mirrored Surface Composite': 'Зеркальный композит', + mr: 'Ракетная установка', + mc: 'Многоствольное орудие', + 'net cost': 'разница в цене', + no: 'Нет', + PHRASE_NO_BUILDS: 'Нечего сравнивать', + PHRASE_NO_RETROCH: 'нет ранних версий сборки\конфигурации', + none: 'ни один', + 'none created': 'не создано', + off: 'выкл', + on: 'вкл', + optimal: 'Оптимальный', + 'optimal mass': 'Оптимальная масса', + 'optimize mass': 'Оптимизировать массу', + overwrite: 'перезаписать', + Pacifier: 'Миротворец', + 'Pack-Hound': 'Ракета "Гончая"', + PHRASE_IMPORT: 'Для импорта вставьте код в эту форму', + pen: 'ПБ', + penetration: 'Пробитие', + permalink: 'Постоянная ссылка', + pa: 'Ускоритель плазмы', + 'Point Defence': 'Противоракетная защита', + power: 'Мощность', + pd: 'Распределитель энергии', + pp: 'Реактор', + pri: 'Осн', + priority: 'Приоритет', + psg: 'Генератор призматического щита', + proceed: 'продолжить', + pc: 'Контроллер "Дрон-исследователь"', + pl: 'Импульсный лазер', + PWR: 'Эн', + rg: 'Рельсотрон', + range: 'Дальность', + rate: 'скорость', + 'Reactive Surface Composite': 'Динамическая защита', + recharge: 'Перезарядка', + rf: 'Переработка', + 'refuel time': 'Время дозаправки', + 'Reinforced Alloy': 'Усиленный сплав', + reload: 'Перезарядить', + rename: 'Переименовать', + repair: 'Починка', + reset: 'Сброс', + ret: 'Убр.', + retracted: 'Убрано', + 'retrofit costs': 'цена модификации', + 'retrofit from': 'модификация от', + ROF: 'В/сек', + S: 'М', + save: 'Сохранить', + sc: 'Сканер', + PHRASE_SELECT_BUILDS: 'Выберите конфигурацию для сравнения', + sell: 'Продать', + s: 'Сенсоры', + settings: 'Настройки', + sb: 'Усилитель щита', + scb: 'Батареи перезарядки щита', + sg: 'Генератор щита', + shields: 'Щиты', + ship: 'Корабль', + ships: 'Корабли', + shortened: 'Укороченный', + size: 'размер', + skip: 'пропустить', + small: 'Малый', + speed: 'скорость', + standard: 'Стандартный', + 'Standard Docking Computer': 'Стандартный стыковочный компьютер', + Stock: 'Стандартная комплектация', + SYS: 'СИС', + T: 'Т', + T_LOAD: 'Тепл.', + 'The Retributor': '"Возмездие"', + t: 'Двигатели', + time: 'Время', + tp: 'Торпедный аппарат', + total: 'Всего', + 'total range': 'Общий радиус', + turret: 'Туррель', + type: 'Тип', + U: 'В', + unladen: 'Пустой', + PHRASE_UPDATE_RDY: 'Доступно обновление. Нажмите для обновления.', + URL: 'Ссылка', + utility: 'Вспомогательное', + 'utility mounts': 'Вспомогательное оборудование', + version: 'Версия', + WEP: 'ОРУ', + yes: 'Да', + PHRASE_BACKUP_DESC: 'Сохраните все данные перед переносом в другой браузер или устройство' +}; diff --git a/app/js/app.js b/app/js/old-app.js similarity index 100% rename from app/js/app.js rename to app/js/old-app.js diff --git a/app/js/pages/NotFoundPage.jsx b/app/js/pages/NotFoundPage.jsx new file mode 100644 index 00000000..0dabb937 --- /dev/null +++ b/app/js/pages/NotFoundPage.jsx @@ -0,0 +1,12 @@ +import { Component } from 'react'; + +export default class ShipyardPage extends Component { + + constructor(props) { + super(props); + } + + render() { + return
Page {this.props.path} Not Found
; + } +} diff --git a/app/js/pages/ShipyardPage.jsx b/app/js/pages/ShipyardPage.jsx new file mode 100755 index 00000000..5d697aea --- /dev/null +++ b/app/js/pages/ShipyardPage.jsx @@ -0,0 +1,180 @@ +import { Component } from 'react'; +import { Ships, Components } from 'coriolis-data'; +import cn from 'classnames'; +import Ship from 'Ship'; + +function countHp(slot) { + this.hp[slot.maxClass]++; + this.hpCount++; +} + +function countInt(slot) { + var crEligible = !slot.eligible || slot.eligible.cr; + this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment + this.intCount++; + this.maxCargo += crEligible ? Components.findInternal('cr', slot.maxClass, 'E').capacity : 0; +} + +function shipSummary(shipId, shipData) { + let summary = { + id: shipId, + hpCount: 0, + intCount: 0, + maxCargo: 0, + hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge + int: [0, 0, 0, 0, 0, 0, 0, 0] // Sizes 1 - 8 + }; + Object.assign(summary, shipData.properties); + let ship = new Ship(shipId, shipData.properties, shipData.slots); + + // Build Ship + ship.buildWith(shipData.defaults); // Populate with stock/default components + ship.hardpoints.forEach(countHp.bind(summary)); // Count Hardpoints by class + ship.internal.forEach(countInt.bind(summary)); // Count Internal Compartments by class + summary.retailCost = ship.totalCost; // Record Stock/Default/retail cost + ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range + summary.maxJumpRange = ship.unladenRange; // Record Jump Range + ship.optimizeMass({ th: ship.standard[1].maxClass + 'A' }); // Optmize mass with Max Thrusters + summary.topSpeed = ship.topSpeed; + summary.topBoost = ship.topBoost; + + return summary; +} + +let shipSummaries = []; + +for (var s in Ships) { + shipSummaries.push(shipSummary(s, Ships[s])); +} + +export default class ShipyardPage extends Component { + + constructor(props) { + super(props); + this.state = { + title: 'Coriolis - Shipyard', + shipPredicate: 'properties.name', + shipDesc = false + }; + } + + shouldComponentUpdate(nextProps, nextState) { + // Only on language change. Context? + return false; + } + + /** + * Sort ships + * @param {object} key Sort predicate + */ + _sortShips(shipPredicate, shipPredicateIndex) { + let shipDesc = this.state.shipPredicate == shipPredicate ? !this.state.shipDesc : this.state.shipDesc; + this.setState({ shipPredicate, shipDesc, shipPredicateIndex }); + }; + + render() { + let sortShips = this._sortShips.bind(this); + let shipPredicate = this.state.shipPredicate; + let shipPredicateIndex = this.state.shipPredicateIndex; + let shipRows = []; + + // Sort shipsOverview + shipSummaries.sort((a, b) => { + let valA = a[shipPredicate], valB = b[shipPredicate]; + + if (shipPredicateIndex != undefined) { + valA = valA[shipPredicateIndex]; + valB = valB[shipPredicateIndex]; + } + + return this.state.shipDesc ? (valA > valB) : (valB > valA); + }); + + for (s of shipSummaries) { + shipRows.push( + + {s.name} + {s.manufacturer} + {SZM[s.class] | translate} + {{fCrd(s.speed)}} m/s + {{fCrd(s.boost)}} m/s + {s.baseArmour} + {{fCrd(s.baseShieldStrength)}} Mj + {{fCrd(s.topSpeed)}} m/s + {{fCrd(s.topBoost)}} m/s + {{fRound(s.maxJumpRange)}} LY + {{fCrd(s.maxCargo)}} T + {s.hp[1]} + {s.hp[2]} + {s.hp[3]} + {s.hp[4]} + {s.hp[0]} + {s.int[0]} + {s.int[1]} + {s.int[2]} + {s.int[3]} + {s.int[4]} + {s.int[5]} + {s.int[6]} + {s.int[7]} + {fCrd(s.hullMass)} T + {s.masslock} + {fCrd(s.retailCost)} CR + + ); + } + + return ( +
+
+ + + + + + + + + + + + + + + + {/* Base */} + + + + + {/* Max */} + + + + + {/* Hardpoints */} + + + + + + {/* Internal */} + + + + + + + + + + + + {shipRows} + +
12345678
+
+
+ ); + } +} diff --git a/app/js/controllers/controller-comparison.js b/app/js/pages/controller-comparison.js similarity index 100% rename from app/js/controllers/controller-comparison.js rename to app/js/pages/controller-comparison.js diff --git a/app/js/controllers/controller-delete.js b/app/js/pages/controller-delete.js similarity index 100% rename from app/js/controllers/controller-delete.js rename to app/js/pages/controller-delete.js diff --git a/app/js/controllers/controller-error.js b/app/js/pages/controller-error.js similarity index 100% rename from app/js/controllers/controller-error.js rename to app/js/pages/controller-error.js diff --git a/app/js/controllers/controller-export.js b/app/js/pages/controller-export.js similarity index 100% rename from app/js/controllers/controller-export.js rename to app/js/pages/controller-export.js diff --git a/app/js/controllers/controller-import.js b/app/js/pages/controller-import.js similarity index 100% rename from app/js/controllers/controller-import.js rename to app/js/pages/controller-import.js diff --git a/app/js/controllers/controller-link.js b/app/js/pages/controller-link.js similarity index 100% rename from app/js/controllers/controller-link.js rename to app/js/pages/controller-link.js diff --git a/app/js/controllers/controller-modal.js b/app/js/pages/controller-modal.js similarity index 100% rename from app/js/controllers/controller-modal.js rename to app/js/pages/controller-modal.js diff --git a/app/js/controllers/controller-outfit.js b/app/js/pages/controller-outfit.js similarity index 100% rename from app/js/controllers/controller-outfit.js rename to app/js/pages/controller-outfit.js diff --git a/app/js/provider-locale-format.js b/app/js/provider-locale-format.js deleted file mode 100644 index 598cd813..00000000 --- a/app/js/provider-locale-format.js +++ /dev/null @@ -1,36 +0,0 @@ - -angular.module('app').provider('localeFormat', function localeFormatProvider() { - var formats = { - en: { - decimal: '.', - thousands: ',', - grouping: [3], - currency: ['$', ''], - dateTime: '%a %b %e %X %Y', - date: '%m/%d/%Y', - time: '%H:%M:%S', - periods: ['AM', 'PM'], - days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], - shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], - months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], - shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] - } - }; - - function LocaleFormat(formatMap) { - this.formatMap = formatMap; - - this.get = function(lang) { - return this.formatMap[lang] ? this.formatMap[lang] : this.formatMap.en; - }; - } - - this.addFormat = function(langCode, formatDetails) { - formats[langCode] = formatDetails; - }; - - this.$get = [function() { - return new LocaleFormat(formats); - }]; - -}); diff --git a/app/js/service-persist.js b/app/js/service-persist.js deleted file mode 100755 index bcca430c..00000000 --- a/app/js/service-persist.js +++ /dev/null @@ -1,314 +0,0 @@ -/** - * [description] - */ -angular.module('app').service('Persist', ['$window', 'lodash', function($window, _) { - var LS_KEY_BUILDS = 'builds'; - var LS_KEY_COMPARISONS = 'comparisons'; - var LS_KEY_LANG = 'NG_TRANSLATE_LANG_KEY'; - var LS_KEY_COST_TAB = 'costTab'; - var LS_KEY_INSURANCE = 'insurance'; - var LS_KEY_DISCOUNTS = 'discounts'; - var localStorage = $window.localStorage; - var buildJson = null; - var comparisonJson = null; - - // Safe check to determine if localStorage is enabled - try { - localStorage.setItem('s', 1); - localStorage.removeItem('s'); - buildJson = localStorage.getItem(LS_KEY_BUILDS); - comparisonJson = localStorage.getItem(LS_KEY_COMPARISONS); - this.lsEnabled = true; - } catch(e) { - this.lsEnabled = false; - } - - this.builds = buildJson ? angular.fromJson(buildJson) : {}; - this.comparisons = comparisonJson ? angular.fromJson(comparisonJson) : {}; - var buildCount = Object.keys(this.builds).length; - - this.state = { - buildCount: buildCount, - hasBuilds: buildCount > 0, - hasComparisons: Object.keys(this.comparisons).length > 0 - }; - - this.put = function(name, value) { - if (!this.lsEnabled) { - return; - } - localStorage.setItem(name, value); - }; - - this.get = function(name) { - return this.lsEnabled ? localStorage.getItem(name) : null; - }; - - this.getLangCode = function() { - return this.lsEnabled ? localStorage.getItem(LS_KEY_LANG) : null; - }; - - /** - * Persist a ship build in local storage. - * - * @param {string} shipId The unique id for a model of ship - * @param {string} name The name of the build - * @param {string} code The serialized code - */ - this.saveBuild = function(shipId, name, code) { - if (!this.lsEnabled) { - return; - } - - if (!this.builds[shipId]) { - this.builds[shipId] = {}; - } - - if (!this.builds[shipId][name]) { - this.state.buildCount++; - this.state.hasBuilds = true; - } - - this.builds[shipId][name] = code; - // Persist updated build collection to localStorage - localStorage.setItem(LS_KEY_BUILDS, angular.toJson(this.builds)); - }; - - /** - * Get the serialized code/string for a build. Returns null if a - * build is not found. - * - * @param {string} shipId The unique id for a model of ship - * @param {string} name The name of the build - * @return {string} The serialized build string. - */ - this.getBuild = function(shipId, name) { - if (this.builds[shipId] && this.builds[shipId][name]) { - return this.builds[shipId][name]; - } - return null; - }; - - /** - * Delete a build from local storage. It will also delete the ship build collection if - * it becomes empty - * - * @param {string} shipId The unique id for a model of ship - * @param {string} name The name of the build - */ - this.deleteBuild = function(shipId, name) { - if (this.lsEnabled && this.builds[shipId][name]) { - delete this.builds[shipId][name]; - if (Object.keys(this.builds[shipId]).length === 0) { - delete this.builds[shipId]; - this.state.buildCount--; - this.state.hasBuilds = this.state.buildCount > 0; - } - // Persist updated build collection to localStorage - localStorage.setItem(LS_KEY_BUILDS, angular.toJson(this.builds)); - // Check if the build was used in existing comparisons - var comps = this.comparisons; - for (var c in comps) { - for (var i = 0; i < comps[c].builds.length; i++) { // For all builds in the current comparison - if (comps[c].builds[i].shipId == shipId && comps[c].builds[i].buildName == name) { - comps[c].builds.splice(i, 1); - break; // A build is unique ber comparison - } - } - } - localStorage.setItem(LS_KEY_COMPARISONS, angular.toJson(this.comparisons)); - } - }; - - /** - * Persist a comparison in localstorage. - * - * @param {string} name The name of the comparison - * @param {array} builds Array of builds - * @param {array} facets Array of facet indices - */ - this.saveComparison = function(name, builds, facets) { - if (!this.lsEnabled) { - return; - } - - if (!this.comparisons[name]) { - this.comparisons[name] = {}; - } - this.comparisons[name] = { - facets: facets, - 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; - }; - - /** - * [getComparison description] - * @param {string} name [description] - * @return {object} Object containing array of facets and ship id + build names - */ - this.getComparison = function(name) { - if (this.comparisons[name]) { - return this.comparisons[name]; - } - return null; - }; - - /** - * Removes the comparison from localstorage. - * @param {string} name Comparison name - */ - this.deleteComparison = function(name) { - if (this.lsEnabled && this.comparisons[name]) { - delete this.comparisons[name]; - localStorage.setItem(LS_KEY_COMPARISONS, angular.toJson(this.comparisons)); - this.state.hasComparisons = Object.keys(this.comparisons).length > 0; - } - }; - - /** - * Delete all builds and comparisons from localStorage - */ - this.deleteAll = function() { - angular.copy({}, this.builds); // Empty object but keep original instance - angular.copy({}, this.comparisons); - this.state.hasBuilds = false; - this.state.buildCount = 0; - - if (this.lsEnabled) { - localStorage.removeItem(LS_KEY_BUILDS); - localStorage.removeItem(LS_KEY_COMPARISONS); - } - }; - - this.getAll = function() { - var data = {}; - data[LS_KEY_BUILDS] = this.builds; - data[LS_KEY_COMPARISONS] = this.comparisons; - data[LS_KEY_INSURANCE] = this.getInsurance(); - data[LS_KEY_DISCOUNTS] = this.getDiscount(); - - return data; - }; - - /** - * Get the saved insurance type - * @return {string} The name of the saved insurance type of null - */ - this.getInsurance = function() { - if (this.lsEnabled) { - return localStorage.getItem(LS_KEY_INSURANCE); - } - return null; - }; - - /** - * Persist selected insurance type - * @param {string} name Insurance type name - */ - this.setInsurance = function(name) { - if (this.lsEnabled) { - return localStorage.setItem(LS_KEY_INSURANCE, name); - } - }; - - /** - * Persist selected discount - * @param {number} val Discount value/amount - */ - this.setDiscount = function(val) { - if (this.lsEnabled) { - return localStorage.setItem(LS_KEY_DISCOUNTS, angular.toJson(val)); - } - }; - - /** - * Get the saved discount - * @return {number} val Discount value/amount - */ - this.getDiscount = function() { - if (this.lsEnabled) { - return angular.fromJson(localStorage.getItem(LS_KEY_DISCOUNTS)); - } - return null; - }; - - /** - * Persist selected cost tab - * @param {number} val Discount value/amount - */ - this.setCostTab = function(tabName) { - if (this.lsEnabled) { - return localStorage.setItem(LS_KEY_COST_TAB, tabName); - } - }; - - /** - * Get the saved discount - * @return {number} val Discount value/amount - */ - this.getCostTab = function() { - if (this.lsEnabled) { - return localStorage.getItem(LS_KEY_COST_TAB); - } - return null; - }; - - /** - * Retrieve the last router state from local storage - * @return {object} state State object containing state name and params - */ - this.getState = function() { - if (this.lsEnabled) { - var state = localStorage.getItem('state'); - if (state) { - return angular.fromJson(state); - } - } - return null; - }; - - /** - * Save the current router state to localstorage - * @param {object} state State object containing state name and params - */ - this.setState = function(state) { - if (this.lsEnabled) { - localStorage.setItem('state', angular.toJson(state)); - } - }; - - /** - * Retrieve the last router state from local storage - * @return {number} size Ratio - */ - this.getSizeRatio = function() { - if (this.lsEnabled) { - var ratio = localStorage.getItem('sizeRatio'); - if (!isNaN(ratio) && ratio > 0.6) { - return ratio; - } - } - return 1; - }; - - /** - * Save the current size ratio to localstorage - * @param {number} sizeRatio - */ - this.setSizeRatio = function(sizeRatio) { - if (this.lsEnabled) { - localStorage.setItem('sizeRatio', sizeRatio); - } - }; - - /** - * Check if localStorage is enabled/active - * @return {Boolean} True if localStorage is enabled - */ - this.isEnabled = function() { - return this.lsEnabled; - }; - -}]); diff --git a/app/js/service-serializer.js b/app/js/service-serializer.js deleted file mode 100755 index f9f2101d..00000000 --- a/app/js/service-serializer.js +++ /dev/null @@ -1,245 +0,0 @@ -/** - * Service managing seralization and deserialization of models for use in URLs and persistene. - */ -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. - * @param {Ship} ship The ship to be serialized. - * @return {string} Encoded string of components - */ - this.fromShip = function(ship) { - var power = { - enabled: [ship.cargoHatch.enabled ? 1 : 0], - priorities: [ship.cargoHatch.priority] - }; - - var data = [ - ship.bulkheads.id, - _.map(ship.standard, mapGroup, power), - _.map(ship.hardpoints, mapGroup, power), - _.map(ship.internal, mapGroup, power), - '.', - LZString.compressToBase64(power.enabled.join('')).replace(/\//g, '-'), - '.', - LZString.compressToBase64(power.priorities.join('')).replace(/\//g, '-') - ]; - - return _.flatten(data).join(''); - }; - - /** - * Updates an existing ship instance's slots with components determined by the - * code. - * - * @param {Ship} ship The ship instance to be updated - * @param {string} dataString The string to deserialize - */ - this.toShip = function(ship, dataString) { - var standard = new Array(ship.standard.length), - hardpoints = new Array(ship.hardpoints.length), - internal = new Array(ship.internal.length), - parts = dataString.split('.'), - priorities = null, - enabled = null, - code = parts[0]; - - if (parts[1]) { - enabled = LZString.decompressFromBase64(parts[1].replace(/-/g, '/')).split(''); - } - - if (parts[2]) { - priorities = LZString.decompressFromBase64(parts[2].replace(/-/g, '/')).split(''); - } - - decodeToArray(code, internal, decodeToArray(code, hardpoints, decodeToArray(code, standard, 1))); - - ship.buildWith( - { - bulkheads: code.charAt(0) * 1, - standard: standard, - hardpoints: hardpoints, - internal: internal - }, - priorities, - enabled - ); - }; - - this.toDetailedBuild = function(buildName, ship, code) { - var standard = ship.standard, - hardpoints = ship.hardpoints, - internal = ship.internal; - - var data = { - $schema: 'http://cdn.coriolis.io/schemas/ship-loadout/2.json#', - name: buildName, - ship: ship.name, - references: [{ - name: 'Coriolis.io', - url: $state.href('outfit', { shipId: ship.id, code: code, bn: buildName }, { absolute: true }), - code: code, - shipId: ship.id - }], - components: { - standard: { - bulkheads: ship.bulkheads.c.name, - cargoHatch: { enabled: Boolean(ship.cargoHatch.enabled), priority: ship.cargoHatch.priority + 1 }, - powerPlant: { class: standard[0].c.class, rating: standard[0].c.rating, enabled: Boolean(standard[0].enabled), priority: standard[0].priority + 1 }, - thrusters: { class: standard[1].c.class, rating: standard[1].c.rating, enabled: Boolean(standard[1].enabled), priority: standard[1].priority + 1 }, - frameShiftDrive: { class: standard[2].c.class, rating: standard[2].c.rating, enabled: Boolean(standard[2].enabled), priority: standard[2].priority + 1 }, - lifeSupport: { class: standard[3].c.class, rating: standard[3].c.rating, enabled: Boolean(standard[3].enabled), priority: standard[3].priority + 1 }, - powerDistributor: { class: standard[4].c.class, rating: standard[4].c.rating, enabled: Boolean(standard[4].enabled), priority: standard[4].priority + 1 }, - sensors: { class: standard[5].c.class, rating: standard[5].c.rating, enabled: Boolean(standard[5].enabled), priority: standard[5].priority + 1 }, - fuelTank: { class: standard[6].c.class, rating: standard[6].c.rating, enabled: Boolean(standard[6].enabled), priority: standard[6].priority + 1 } - }, - hardpoints: _.map(_.filter(hardpoints, function(slot) { return slot.maxClass > 0; }), slotToSchema), - utility: _.map(_.filter(hardpoints, function(slot) { return slot.maxClass === 0; }), slotToSchema), - internal: _.map(internal, slotToSchema) - }, - stats: {} - }; - - for (var stat in ship) { - if (!isNaN(ship[stat])) { - data.stats[stat] = Math.round(ship[stat] * 100) / 100; - } - } - - 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 standard = comps.standard; - var priorities = [ standard.cargoHatch && standard.cargoHatch.priority !== undefined ? standard.cargoHatch.priority - 1 : 0 ]; - var enabled = [ standard.cargoHatch && standard.cargoHatch.enabled !== undefined ? standard.cargoHatch.enabled : true ]; - var shipData = ShipsDB[shipId]; - var ship = new Ship(shipId, shipData.properties, shipData.slots); - var bulkheads = Components.bulkheadIndex(standard.bulkheads); - - if (bulkheads < 0) { - throw 'Invalid bulkheads: ' + standard.bulkheads; - } - - var standardIds = _.map( - ['powerPlant', 'thrusters', 'frameShiftDrive', 'lifeSupport', 'powerDistributor', 'sensors', 'fuelTank'], - function(c) { - if (!standard[c].class || !standard[c].rating) { - throw 'Invalid value for ' + c; - } - priorities.push(standard[c].priority === undefined ? 0 : standard[c].priority - 1); - enabled.push(standard[c].enabled === undefined ? true : standard[c].enabled); - return standard[c].class + 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; - }).concat(_.map(comps.utility, function(c) { - return c ? Components.findHardpointId(c.group, c.class, c.rating, c.name, MountMap[c.mount]) : 0; - })); - - // The ordering of these arrays must match the order in which they are read in Ship.buildWith - priorities = priorities.concat(_.map(comps.hardpoints, function(c) { return (!c || c.priority === undefined) ? 0 : c.priority - 1; }), - _.map(comps.utility, function(c) { return (!c || c.priority === undefined) ? 0 : c.priority - 1; }), - _.map(comps.internal, function(c) { return (!c || c.priority === undefined) ? 0 : c.priority - 1; })); - enabled = enabled.concat(_.map(comps.hardpoints, function(c) { return (!c || c.enabled === undefined) ? true : c.enabled * 1; }), - _.map(comps.utility, function(c) { return (!c || c.enabled === undefined) ? true : c.enabled * 1; }), - _.map(comps.internal, function(c) { return (!c || c.enabled === undefined) ? true : c.enabled * 1; })); - - ship.buildWith({ bulkheads: bulkheads, standard: standardIds, hardpoints: hardpoints, internal: internal }, priorities, enabled); - - return ship; - }; - - 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 = []; - - builds.forEach(function(b) { - shipBuilds.push({ s: b.id, n: b.buildName, c: this.fromShip(b) }); - }.bind(this)); - - return LZString.compressToBase64(angular.toJson({ - n: name, - b: shipBuilds, - f: facets, - p: predicate, - d: desc ? 1 : 0 - })).replace(/\//g, '-'); - }; - - this.toComparison = function(code) { - return angular.fromJson(LZString.decompressFromBase64(code.replace(/-/g, '/'))); - }; - - /** - * Utility function to retrieve a safe string for selected component for a slot. - * Used for serialization to code only. - * - * @private - * @param {object} slot The slot object. - * @return {string} The id of the selected component or '-' if none selected - */ - function mapGroup(slot) { - this.enabled.push(slot.enabled ? 1 : 0); - this.priorities.push(slot.priority); - - return slot.id === null ? '-' : slot.id; - } - - function decodeToArray(code, arr, codePos) { - for (var i = 0; i < arr.length; i++) { - if (code.charAt(codePos) == '-') { - arr[i] = 0; - codePos++; - } else { - arr[i] = code.substring(codePos, codePos + 2); - codePos += 2; - } - } - return codePos; - } - - function slotToSchema(slot) { - if (slot.c) { - var o = { class: slot.c.class, rating: slot.c.rating, enabled: Boolean(slot.enabled), priority: slot.priority + 1, group: GroupMap[slot.c.grp] }; - if (slot.c.name) { - o.name = slot.c.name; - } - if (slot.c.mode) { - o.mount = MountMap[slot.c.mode]; - } - if (slot.c.missile) { - o.missile = slot.c.missile; - } - return o; - } - return null; - } - - -}]); diff --git a/app/js/shipyard/Calculations.js b/app/js/shipyard/Calculations.js new file mode 100644 index 00000000..c8c4f751 --- /dev/null +++ b/app/js/shipyard/Calculations.js @@ -0,0 +1,84 @@ + +/** + * Calculate the maximum single jump range based on mass and a specific FSD + * + * @param {number} mass Mass of a ship: laden, unlanden, partially laden, etc + * @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass + * @param {number} fuel Optional - The fuel consumed during the jump (must be less than the drives max fuel per jump) + * @return {number} Distance in Light Years + */ +export function jumpRange(mass, fsd, fuel) { + return Math.pow(Math.min(fuel === undefined ? fsd.maxfuel : fuel, fsd.maxfuel) / fsd.fuelmul, 1 / fsd.fuelpower ) * fsd.optmass / mass; +} + +/** + * Calculate the total range based on mass and a specific FSD, and all fuel available + * + * @param {number} mass Mass of a ship: laden, unlanden, partially laden, etc + * @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass + * @param {number} fuel The total fuel available + * @return {number} Distance in Light Years + */ +export function totalRange(mass, fsd, fuel) { + var fuelRemaining = fuel % fsd.maxfuel; // Fuel left after making N max jumps + var jumps = Math.floor(fuel / fsd.maxfuel); + mass += fuelRemaining; + // Going backwards, start with the last jump using the remaining fuel + var totalRange = fuelRemaining > 0 ? Math.pow(fuelRemaining / fsd.fuelmul, 1 / fsd.fuelpower ) * fsd.optmass / mass : 0; + // For each max fuel jump, calculate the max jump range based on fuel mass left in the tank + for (var j = 0; j < jumps; j++) { + mass += fsd.maxfuel; + totalRange += Math.pow(fsd.maxfuel / fsd.fuelmul, 1 / fsd.fuelpower ) * fsd.optmass / mass; + } + return totalRange; +}; + +/** + * Calculate the a ships shield strength based on mass, shield generator and shield boosters used. + * + * @param {number} mass Current mass of the ship + * @param {number} shields Base Shield strength MJ for ship + * @param {object} sg The shield generator used + * @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any) + * @return {number} Approximate shield strengh in MJ + */ +export function shieldStrength(mass, shields, sg, multiplier) { + var opt; + if (mass < sg.minmass) { + return shields * multiplier * sg.minmul; + } + if (mass > sg.maxmass) { + return shields * multiplier * sg.maxmul; + } + if (mass < sg.optmass) { + opt = (sg.optmass - mass) / (sg.optmass - sg.minmass); + opt = 1 - Math.pow(1 - opt, 0.87); + return shields * multiplier * ((opt * sg.minmul) + ((1 - opt) * sg.optmul)); + } else { + opt = (sg.optmass - mass) / (sg.maxmass - sg.optmass); + opt = -1 + Math.pow(1 + opt, 2.425); + return shields * multiplier * ( (-1 * opt * sg.maxmul) + ((1 + opt) * sg.optmul) ); + } +} + +/** + * Calculate the a ships speed based on mass, and thrusters. + * + * @param {number} mass Current mass of the ship + * @param {number} baseSpeed Base speed m/s for ship + * @param {number} baseBoost Base boost speed m/s for ship + * @param {object} thrusters The Thrusters used + * @param {number} pipSpeed Speed pip multiplier + * @return {object} Approximate speed by pips + */ +export function speed(mass, baseSpeed, baseBoost, thrusters, pipSpeed) { + var multiplier = mass > thrusters.maxmass ? 0 : ((1 - thrusters.M) + (thrusters.M * Math.pow(3 - (2 * Math.max(0.5, mass / thrusters.optmass)), thrusters.P))); + var speed = baseSpeed * multiplier; + + return { + '0 Pips': speed * (1 - (pipSpeed * 4)), + '2 Pips': speed * (1 - (pipSpeed * 2)), + '4 Pips': speed, + 'boost': baseBoost * multiplier + }; +} diff --git a/app/js/shipyard/Constants.js b/app/js/shipyard/Constants.js new file mode 100755 index 00000000..4a83212f --- /dev/null +++ b/app/js/shipyard/Constants.js @@ -0,0 +1,183 @@ + +export const ArmourMultiplier = [ + 1, // Lightweight + 1.4, // Reinforced + 1.945, // Military + 1.945, // Mirrored + 1.945 // Reactive +]; + +export const SizeMap = ['', 'small', 'medium', 'large', 'capital']; + +// Map to lookup group labels/names for component grp, used for JSON Serialization +const ModuleGroupToName = { + // Standard + pp: 'Power Plant', + t: 'Thrusters', + fsd: 'Frame Shift Drive', + ls: 'Life Support', + pd: 'Power Distributor', + s: 'Sensors', + ft: 'Fuel Tank', + + // Internal + fs: 'Fuel Scoop', + sc: 'Scanner', + am: 'Auto Field-Maintenance Unit', + cr: 'Cargo Rack', + fi: 'Frame Shift Drive Interdictor', + hb: 'Hatch Breaker Limpet Controller', + hr: 'Hull Reinforcement Package', + rf: 'Refinery', + scb: 'Shield Cell Bank', + sg: 'Shield Generator', + psg: 'Prismatic Shield Generator', + dc: 'Docking Computer', + fx: 'Fuel Transfer Limpet Controller', + pc: 'Prospector Limpet Controller', + cc: 'Collector Limpet Controller', + + // Hard Points + bl: 'Beam Laser', + ul: 'Burst Laser', + c: 'Cannon', + cs: 'Cargo Scanner', + cm: 'Countermeasure', + fc: 'Fragment Cannon', + ws: 'Frame Shift Wake Scanner', + kw: 'Kill Warrant Scanner', + nl: 'Mine Launcher', + ml: 'Mining Laser', + mr: 'Missile Rack', + pa: 'Plasma Accelerator', + mc: 'Multi-cannon', + pl: 'Pulse Laser', + rg: 'Rail Gun', + sb: 'Shield Booster', + tp: 'Torpedo Pylon' +}; + +let GrpNameToCodeMap = {}; + +for (let grp in ModuleGroupToName) { + GrpNameToCodeMap[ModuleGroupToName[grp]] = grp; +} + +export ModuleGroupToName; +export const ModuleNameToGroup = GrpNameToCodeMap; + +export const MountMap = { + 'F': 'Fixed', + 'G': 'Gimballed', + 'T': 'Turret', + 'Fixed': 'F', + 'Gimballed': 'G', + 'Turret': 'T' +}; + +export const BulkheadNames = [ + 'Lightweight Alloy', + 'Reinforced Alloy', + 'Military Grade Composite', + 'Mirrored Surface Composite', + 'Reactive Surface Composite' +]; + +/** + * Array of all Ship properties (facets) organized into groups + * used for ship comparisons. + * + * @type {Array} + */ +export const ShipFacets = [ + { // 0 + title: 'agility', + props: ['agility'], + unit: '', + fmt: 'fCrd' + }, + { // 1 + title: 'speed', + props: ['topSpeed', 'topBoost'], + lbls: ['thrusters', 'boost'], + unit: 'm/s', + fmt: 'fCrd' + }, + { // 2 + title: 'armour', + props: ['armour'], + unit: '', + fmt: 'fCrd' + }, + { // 3 + title: 'shields', + props: ['shieldStrength'], + unit: 'MJ', + fmt: 'fRound' + }, + { // 4 + title: 'jump range', + props: ['unladenRange', 'fullTankRange', 'ladenRange'], + lbls: ['max', 'full tank', 'laden'], + unit: 'LY', + fmt: 'fRound' + }, + { // 5 + title: 'mass', + props: ['unladenMass', 'ladenMass'], + lbls: ['unladen', 'laden'], + unit: 'T', + fmt: 'fRound' + }, + { // 6 + title: 'cargo', + props: ['cargoCapacity'], + unit: 'T', + fmt: 'fRound' + }, + { // 7 + title: 'fuel', + props: ['fuelCapacity'], + unit: 'T', + fmt: 'fRound' + }, + { // 8 + title: 'power', + props: ['powerRetracted', 'powerDeployed', 'powerAvailable'], + lbls: ['retracted', 'deployed', 'available'], + unit: 'MW', + fmt: 'fPwr' + }, + { // 9 + title: 'cost', + props: ['totalCost'], + unit: 'CR', + fmt: 'fCrd' + }, + { // 10 + title: 'total range', + props: ['unladenTotalRange', 'ladenTotalRange'], + lbls: ['unladen', 'laden'], + unit: 'LY', + fmt: 'fRound' + }, + { // 11 + title: 'DPS', + props: ['totalDps'], + lbls: ['DPS'], + unit: '', + fmt: 'fRound' + } +]; + /** + * Set of all available / theoretical discounts + */ +export const Discounts = { + '0%': 1, + '5%': 0.95, + '10%': 0.90, + '15%': 0.85, + '20%': 0.80, + '25%': 0.75 +}; + diff --git a/app/js/shipyard/ModuleSet.js b/app/js/shipyard/ModuleSet.js new file mode 100755 index 00000000..361562cf --- /dev/null +++ b/app/js/shipyard/ModuleSet.js @@ -0,0 +1,139 @@ + + +function filter(arr, maxClass, minClass, mass) { + return arr.filter(m => m.class <= maxClass && m.class >= minClass && (m.maxmass === undefined || mass <= m.maxmass)); +} + +function filterToArray(data, maxClass, minClass, mass) { + let arr = []; + + for (let id in data) { + let m = data[id]; + if (m.class <= maxClass && m.class >= minClass && (m.maxmass === undefined || mass <= m.maxmass)) { + arr.push(m); + } + } + + return arr; +} + +export default class ModuleSet { + + constructor(modules, mass, maxStandardArr, maxInternal, maxHardPoint) { + this.mass = mass; + this.standard = {}; + this.internal = {}; + this.hardpoints = {}; + this.hpClass = {}; + this.intClass = {}; + + this.standard[0] = filterToArray(modules.standard[0], maxStandardArr[0], 0, mass); // Power Plant + this.standard[2] = filterToArray(modules.standard[2], maxStandardArr[2], 0, mass); // FSD + this.standard[4] = filterToArray(modules.standard[4], maxStandardArr[4], 0, mass); // Power Distributor + this.standard[6] = filterToArray(modules.standard[6], maxStandardArr[6], 0, mass); // Fuel Tank + + // Thrusters, filter modules by class only (to show full list of ratings for that class) + let ths = modules.standard[1]; + let minThrusterClass = Object.keys(modules.standard[1]).reduce( + (clazz, thId) => (ths[thId].maxmass >= mass && ths[thId].class < clazz) ? ths[thId].class : clazz, + maxStandardArr[1] + ); + this.standard[1] = filterToArray(modules.standard[1], maxStandardArr[1], minThrusterClass, 0); // Thrusters + + // Slots where module class must be equal to slot class + this.standard[3] = filterToArray(modules.standard[3], maxStandardArr[3], maxStandardArr[3], 0); // Life Supprt + this.standard[5] = filterToArray(modules.standard[5], maxStandardArr[5], maxStandardArr[5], mass); // Sensors + + for (let h in modules.hardpoints) { + this.hardpoints[h] = filter(modules.hardpoints[h], maxHardPoint, 0, mass); + } + + for (let g in modules.internal) { + this.internal[g] = filter(modules.internal[g], maxInternal, 0, mass); + } + } + + /** + * Determine the modules that areeligible for an internal slot + * @param {integer} c The max class module that can be mounted in the slot + * @param {Object} eligible) The map of eligible internal groups + * @return {object} A map of all eligible modules by group + */ + getInts(c, eligible) { + let o = {}; + for (let key in this.internal) { + if (eligible && !eligible[key]) { + continue; + } + let data = filter(this.internal[key], c, 0, this.mass); + if (data.length) { // If group is not empty + o[key] = data; + } + } + return o; + } + + /** + * Determining the modules that are eligible for an hardpoint slot + * @param {integer} c The max class module that can be mounted in the slot + * @param {Object} eligible) The map of eligible hardpoint groups + * @return {object} A map of all eligible modules by group + */ + getHps(c, eligible) { + var o = {}; + for (var key in this.hardpoints) { + if (eligible && !eligible[key]) { + continue; + } + var data = filter(this.hardpoints[key], c, c ? 1 : 0, this.mass); + if (data.length) { // If group is not empty + o[key] = data; + } + } + return o; + } + + lightestPowerDist(boostEnergy) { + var pd = this.standard[4][0]; + + for (let p of this.standard[4]) { + if (p.mass < pd.mass && p.enginecapacity >= boostEnergy) { + pd = p; + } + } + return pd.class + pd.rating; + }; + + lightestThruster(ladenMass) { + var th = this.standard[1][0]; + + for (t of this.standard[1]) { + if (t.mass < th.mass && t.maxmass >= ladenMass) { + th = t; + } + } + return th.class + th.rating; + }; + + lightestShieldGenerator(hullMass) { + var sg = this.internal.sg[0]; + + for (let s of this.internal.sg) { + if (s.mass < sg.mass && s.minmass <= hullMass && s.maxmass > hullMass) { + sg = s; + } + } + return sg.id; + }; + + lightestPowerPlant(powerUsed, rating) { + var pp = this.standard[0][0]; + + for (let p of this.standard[0]) { + if (p.mass < pp.mass && p.pGen >= powerUsed) { + pp = p; + } + } + return pp.class + (pp.rating != 'D' || rating == 'A' ? 'A' : 'D'); // Use A rated if C,E + } +} diff --git a/app/js/shipyard/ModuleUtils.js b/app/js/shipyard/ModuleUtils.js new file mode 100755 index 00000000..e54c6480 --- /dev/null +++ b/app/js/shipyard/ModuleUtils.js @@ -0,0 +1,175 @@ +import { ModuleNameToGroup, BulkheadNames } from 'Constants'; +import ModuleSet from 'ModuleSet'; +import { Ships, Modules } from 'coriolis-data'; + +export function cargoHatch() { + return { name: 'Cargo Hatch', class: 1, rating: 'H', power: 0.6 }; + }; + +export function standard(typeIndex, componentId) { + return Modules.standard[typeIndex][componentId]; + }; + +export function hardpoints(id) { + for (let n in Modules.hardpoints) { + let group = Modules.hardpoints[n]; + for (let i = 0; i < group.length; i++) { + if (group[i].id == id) { + return group[i]; + } + } + } + return null; + }; + +export function internal(id) { + for (let n in Modules.internal) { + let group = Modules.internal[n]; + for (let i = 0; i < group.length; i++) { + if (group[i].id == id) { + return group[i]; + } + } + } + return null; + }; + +/** + * Finds an internal Component based on Class, Rating, Group and/or name. + * At least one ofGroup name or unique component name must be provided + * + * @param {string} groupName [Optional] Full name or abbreviated name for component group + * @param {integer} clss Component Class + * @param {string} rating Component Rating + * @param {string} name [Optional] Long/unique name for component -e.g. 'Advanced Discover Scanner' + * @return {String} The id of the component if found, null if not found + */ +export function findInternal(groupName, clss, rating, name) { + let groups = {}; + + if (groupName) { + if (Modules.internal[groupName]) { + groups[groupName] = Modules.internal[groupName]; + } else { + let grpCode = ModuleNameToGroup[groupName]; + if (grpCode && Modules.internal[grpCode]) { + groups[grpCode] = Modules.internal[grpCode]; + } + } + } else if (name) { + groups = Modules.internal; + } + + for (let g in groups) { + let group = groups[g]; + for (let 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]; + } + } + } + + return null; +} + +/** + * Finds an internal Component ID based on Class, Rating, Group and/or name. + * At least one ofGroup name or unique component name must be provided + * + * @param {string} groupName [Optional] Full name or abbreviated name for component group + * @param {integer} clss Component Class + * @param {string} rating Component Rating + * @param {string} name [Optional] Long/unique name for component -e.g. 'Advanced Discover Scanner' + * @return {String} The id of the component if found, null if not found + */ +export function findInternalId(groupName, clss, rating, name) { + let i = this.findInternal(groupName, clss, rating, name); + return i ? i.id : 0; +} + +/** + * Finds a hardpoint Component based on Class, Rating, Group and/or name. + * At least one ofGroup name or unique component name must be provided + * + * @param {string} groupName [Optional] Full name or abbreviated name for component group + * @param {integer} clss Component Class + * @param {string} rating [Optional] Component Rating + * @param {string} name [Optional] Long/unique name for component -e.g. 'Heat Sink Launcher' + * @param {string} mount Mount type - [F]ixed, [G]imballed, [T]urret + * @param {string} missile [Optional] Missile type - [D]umbfire, [S]eeker + * @return {String} The id of the component if found, null if not found + */ +export function findHardpoint(groupName, clss, rating, name, mount, missile) { + let groups = {}; + + if (groupName) { + if (Modules.hardpoints[groupName]) { + groups[groupName] = Modules.hardpoints[groupName]; + } else { + let grpCode = ModuleNameToGroup[groupName]; + if (grpCode && Modules.hardpoints[grpCode]) { + groups[grpCode] = Modules.hardpoints[grpCode]; + } + } + } else if (name) { + groups = Modules.hardpoints; + } + + for (let g in groups) { + let group = groups[g]; + for (let i = 0, l = group.length; i < l; i++) { + if (group[i].class == clss && (!rating || group[i].rating == rating) && group[i].mount == mount + && ((!name && !group[i].name) || group[i].name == name) + && ((!missile && !group[i].missile) || group[i].missile == missile) + ) { + return group[i]; + } + } + } + + return null; +} + +/** + * Finds a hardpoint Component ID based on Class, Rating, Group and/or name. + * At least one of Group name or unique component name must be provided + * + * @param {string} groupName [Optional] Full name or abbreviated name for component group + * @param {integer} clss Component Class + * @param {string} rating Component Rating + * @param {string} name [Optional] Long/unique name for component -e.g. 'Heat Sink Launcher' + * @param {string} mount Mount type - [F]ixed, [G]imballed, [T]urret + * @param {string} missile [Optional] Missile type - [D]umbfire, [S]eeker + * @return {String} The id of the component if found, null if not found + */ +export function findHardpointId(groupName, clss, rating, name, mount, missile) { + let h = this.findHardpoint(groupName, clss, rating, name, mount, missile); + return h ? h.id : 0; +} + +/** + * Looks up the bulkhead component for a specific ship and bulkhead + * @param {string} shipId Unique ship Id/Key + * @param {number} bulkheadsId Id/Index for the specified bulkhead + * @return {object} The bulkhead component object + */ +export function bulkheads(shipId, bulkheadsId) { + return Modules.bulkheads[shipId][bulkheadsId]; +} + +export function bulkheadIndex(bulkheadName) { + return Bulkheads.indexOf(bulkheadName); +} + +/** + * Creates a new ModuleSet that contains all available components + * that the specified ship is eligible to use. + * + * @param {string} shipId Unique ship Id/Key + * @return {ModuleSet} The set of components the ship can install + */ +export function forShip(shipId) { + let ship = Ships[shipId]; + let maxInternal = isNaN(ship.slots.internal[0]) ? ship.slots.internal[0].class : ship.slots.internal[0]; + return new ModuleSet(Modules, ship.minMassFilter || ship.properties.hullMass + 5, ship.slots.standard, maxInternal, ship.slots.hardpoints[0]); +} diff --git a/app/js/shipyard/factory-ship.js b/app/js/shipyard/Ship.js similarity index 52% rename from app/js/shipyard/factory-ship.js rename to app/js/shipyard/Ship.js index b0d5e999..4ebe5938 100755 --- a/app/js/shipyard/factory-ship.js +++ b/app/js/shipyard/Ship.js @@ -1,71 +1,74 @@ -angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', 'calcJumpRange', 'calcTotalRange', 'calcSpeed', 'lodash', 'ArmourMultiplier', function(Components, calcShieldStrength, calcJumpRange, calcTotalRange, calcSpeed, _, ArmourMultiplier) { +import { ArmourMultiplier } from 'Constants'; +import Calc from 'Calculations'; +import ModuleUtils from 'ModuleUtils'; - /** - * Returns the power usage type of a slot and it's particular component - * @param {object} slot The Slot - * @param {object} component The component in the slot - * @return {string} The key for the power usage type - */ - function powerUsageType(slot, component) { - if (component) { - if (component.passive) { - return 'retracted'; - } +const UNIQUE_MODULES = ['psg', 'sg', 'rf', 'fs']; + +/** + * Returns the power usage type of a slot and it's particular modul + * @param {object} slot The Slot + * @param {object} modul The modul in the slot + * @return {string} The key for the power usage type + */ +function powerUsageType(slot, modul) { + if (modul) { + if (modul.passive) { + return 'retracted'; } - return slot.cat != 1 ? 'retracted' : 'deployed'; } + return slot.cat != 1 ? 'retracted' : 'deployed'; +} + +/** + * Ship model used to track all ship ModuleUtils and properties. + */ +export default class Ship { /** - * Ship model used to track all ship components and properties. - * * @param {string} id Unique ship Id / Key * @param {object} properties Basic ship properties such as name, manufacturer, mass, etc * @param {object} slots Collection of slot groups (standard/standard, internal, hardpoints) with their max class size. */ - function Ship(id, properties, slots) { + constructor(id, properties, slots) { this.id = id; - this.cargoHatch = { c: Components.cargoHatch(), type: 'SYS' }; + this.cargoHatch = { m: ModuleUtils.cargoHatch(), type: 'SYS' }; this.bulkheads = { incCost: true, maxClass: 8 }; - this.availCS = Components.forShip(id); + this.availCS = ModuleUtils.forShip(id); for (var p in properties) { this[p] = properties[p]; } // Copy all base properties from shipData for (var slotType in slots) { // Initialize all slots var slotGroup = slots[slotType]; var group = this[slotType] = []; // Initialize Slot group (Standard, Hardpoints, Internal) - for (var i = 0; i < slotGroup.length; i++) { - if (typeof slotGroup[i] == 'object') { - group.push({ id: null, c: null, incCost: true, maxClass: slotGroup[i].class, eligible: slotGroup[i].eligible }); + for (let slot of slotGroup) { + if (typeof slot == 'object') { + group.push({ id: null, m: null, incCost: true, maxClass: slot.class, eligible: slot.eligible }); } else { - group.push({ id: null, c: null, incCost: true, maxClass: slotGroup[i] }); + group.push({ id: null, m: null, incCost: true, maxClass: slot }); } } } // Make a Ship 'slot'/item similar to other slots - this.c = { incCost: true, type: 'SHIP', discountedCost: this.hullCost, c: { name: this.name, cost: this.hullCost } }; - - this.costList = _.union(this.internal, this.standard, this.hardpoints); - this.costList.push(this.bulkheads); // Add The bulkheads - this.costList.unshift(this.c); // Add the ship itself to the list - - this.powerList = _.union(this.internal, this.hardpoints); - this.powerList.unshift(this.cargoHatch); - this.powerList.unshift(this.standard[1]); // Add Thrusters - this.powerList.unshift(this.standard[5]); // Add Sensors - this.powerList.unshift(this.standard[4]); // Add Power Distributor - this.powerList.unshift(this.standard[3]); // Add Life Support - this.powerList.unshift(this.standard[2]); // Add FSD - this.powerList.unshift(this.standard[0]); // Add Power Plant - + this.m = { incCost: true, type: 'SHIP', discountedCost: this.hullCost, m: { name: this.name, cost: this.hullCost } }; + this.costList = this.internal.concat(this.m, this.standard, this.hardpoints, this.bulkheads); + this.powerList = this.internal.concat( + this.cargoHatch, + this.standard[0], // Add Power Plant + this.standard[2], // Add FSD + this.standard[1], // Add Thrusters + this.standard[4], // Add Power Distributor + this.standard[5], // Add Sensors + this.standard[3], // Add Life Support + this.hardpoints + ]; this.shipCostMultiplier = 1; - this.componentCostMultiplier = 1; - + this.modulCostMultiplier = 1; this.priorityBands = [ - { deployed: 0, retracted: 0, retOnly: 0 }, - { deployed: 0, retracted: 0, retOnly: 0 }, - { deployed: 0, retracted: 0, retOnly: 0 }, - { deployed: 0, retracted: 0, retOnly: 0 }, - { deployed: 0, retracted: 0, retOnly: 0 } + { deployed: 0, retracted: 0, }, + { deployed: 0, retracted: 0, }, + { deployed: 0, retracted: 0, }, + { deployed: 0, retracted: 0, }, + { deployed: 0, retracted: 0, } ]; } @@ -73,58 +76,49 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', // GETTERS // //*********// - Ship.prototype.getAvailableComponents = function() { + getAvailableModules() { return this.availCS; - }; + } - Ship.prototype.getSlotStatus = function(slot, deployed) { - if (!slot.c) { // Empty Slot + getSlotStatus(slot, deployed) { + if (!slot.m) { // Empty Slot return 0; // No Status (Not possible to be active in this state) } else if (!slot.enabled) { return 1; // Disabled } else if (deployed) { return this.priorityBands[slot.priority].deployedSum >= this.powerAvailable ? 2 : 3; // Offline : Online // Active hardpoints have no retracted status - } else if ((slot.cat === 1 && !slot.c.passive)) { + } else if ((slot.cat === 1 && !slot.m.passive)) { return 0; // No Status (Not possible to be active in this state) } return this.priorityBands[slot.priority].retractedSum >= this.powerAvailable ? 2 : 3; // Offline : Online - }; + } -/** + /** * Calculate jump range using the installed FSD and the * specified mass which can be more or less than ships actual mass * @param {number} mass Mass in tons * @param {number} fuel Fuel available in tons * @return {number} Jump range in Light Years */ - Ship.prototype.getJumpRangeForMass = function(mass, fuel) { - return calcJumpRange(mass, this.standard[2].c, fuel); - }; + getJumpRangeForMass(mass, fuel) { + return Calc.jumpRange(mass, this.standard[2].m, fuel); + } /** - * Find an internal slot that has an installed component of the specific group. + * Find an internal slot that has an installed modul of the specific group. * - * @param {string} group Component group/type + * @param {string} group Module group/type * @return {number} The index of the slot in ship.internal */ - Ship.prototype.findInternalByGroup = function(group) { + findInternalByGroup(group) { var index; if (group == 'sg' || group == 'psg') { - index = _.findIndex(this.internal, function(slot) { - return slot.c && (slot.c.grp == 'sg' || slot.c.grp == 'psg'); - }); + return this.internal.find(slot => slot.m && (slot.m.grp == 'sg' || slot.m.grp == 'psg'); } else { - index = _.findIndex(this.internal, function(slot) { - return slot.c && slot.c.grp == group; - }); + return this.internal.find(slot => slot.m && slot.m.grp == group); } - - if (index !== -1) { - return this.internal[index]; - } - return null; - }; + } //**********************// // Mutate / Update Ship // @@ -133,32 +127,32 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', /** * Recalculate all item costs and total based on discounts. * @param {number} shipCostMultiplier Ship cost multiplier discount (e.g. 0.9 === 10% discount) - * @param {number} componentCostMultiplier Component cost multiplier discount (e.g. 0.75 === 25% discount) + * @param {number} modulCostMultiplier Module cost multiplier discount (e.g. 0.75 === 25% discount) */ - Ship.prototype.applyDiscounts = function(shipCostMultiplier, componentCostMultiplier) { + applyDiscounts(shipCostMultiplier, modulCostMultiplier) { var total = 0; var costList = this.costList; for (var i = 0, l = costList.length; i < l; i++) { var item = costList[i]; - if (item.c && item.c.cost) { - item.discountedCost = item.c.cost * (item.type == 'SHIP' ? shipCostMultiplier : componentCostMultiplier); + if (item.m && item.m.cost) { + item.discountedCost = item.m.cost * (item.type == 'SHIP' ? shipCostMultiplier : modulCostMultiplier); if (item.incCost) { total += item.discountedCost; } } } this.shipCostMultiplier = shipCostMultiplier; - this.componentCostMultiplier = componentCostMultiplier; + this.modulCostMultiplier = modulCostMultiplier; this.totalCost = total; return this; - }; + } /** - * Builds/Updates the ship instance with the components[comps] passed in. - * @param {object} comps Collection of components used to build the ship + * Builds/Updates the ship instance with the ModuleUtils[comps] passed in. + * @param {object} comps Collection of ModuleUtils used to build the ship */ - Ship.prototype.buildWith = function(comps, priorities, enabled) { + buildWith(comps, priorities, enabled) { var internal = this.internal, standard = this.standard, hps = this.hardpoints, @@ -173,11 +167,11 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', this.armourAdded = 0; this.armourMultiplier = 1; this.shieldMultiplier = 1; - this.totalCost = this.c.incCost ? this.c.discountedCost : 0; + this.totalCost = this.m.incCost ? this.m.discountedCost : 0; this.unladenMass = this.hullMass; this.totalDps = 0; - this.bulkheads.c = null; + this.bulkheads.m = null; this.useBulkhead(comps && comps.bulkheads ? comps.bulkheads : 0, true); this.cargoHatch.priority = priorities ? priorities[0] * 1 : 0; this.cargoHatch.enabled = enabled ? enabled[0] * 1 : true; @@ -185,11 +179,10 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', for (i = 0, l = this.priorityBands.length; i < l; i++) { this.priorityBands[i].deployed = 0; this.priorityBands[i].retracted = 0; - this.priorityBands[i].retOnly = 0; } if (this.cargoHatch.enabled) { - bands[this.cargoHatch.priority].retracted += this.cargoHatch.c.power; + bands[this.cargoHatch.priority].retracted += this.cargoHatch.m.power; } for (i = 0; i < cl; i++) { @@ -197,11 +190,11 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', standard[i].enabled = enabled ? enabled[i + 1] * 1 : true; standard[i].priority = priorities && priorities[i + 1] ? priorities[i + 1] * 1 : 0; standard[i].type = 'SYS'; - standard[i].c = standard[i].id = null; // Resetting 'old' component if there was one + standard[i].m = standard[i].id = null; // Resetting 'old' modul if there was one standard[i].discountedCost = 0; if (comps) { - this.use(standard[i], comps.standard[i], Components.standard(i, comps.standard[i]), true); + this.use(standard[i], comps.standard[i], ModuleUtils.standard(i, comps.standard[i]), true); } } @@ -214,11 +207,11 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', hps[i].enabled = enabled ? enabled[cl + i] * 1 : true; hps[i].priority = priorities && priorities[cl + i] ? priorities[cl + i] * 1 : 0; hps[i].type = hps[i].maxClass ? 'WEP' : 'SYS'; - hps[i].c = hps[i].id = null; // Resetting 'old' component if there was one + hps[i].m = hps[i].id = null; // Resetting 'old' modul if there was one hps[i].discountedCost = 0; if (comps && comps.hardpoints[i] !== 0) { - this.use(hps[i], comps.hardpoints[i], Components.hardpoints(comps.hardpoints[i]), true); + this.use(hps[i], comps.hardpoints[i], ModuleUtils.hardpoints(comps.hardpoints[i]), true); } } @@ -229,11 +222,11 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', internal[i].enabled = enabled ? enabled[cl + i] * 1 : true; internal[i].priority = priorities && priorities[cl + i] ? priorities[cl + i] * 1 : 0; internal[i].type = 'SYS'; - internal[i].id = internal[i].c = null; // Resetting 'old' component if there was one + internal[i].id = internal[i].m = null; // Resetting 'old' modul if there was one internal[i].discountedCost = 0; if (comps && comps.internal[i] !== 0) { - this.use(internal[i], comps.internal[i], Components.internal(comps.internal[i]), true); + this.use(internal[i], comps.internal[i], ModuleUtils.internal(comps.internal[i]), true); } } @@ -246,85 +239,85 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', } return this; - }; + } - Ship.prototype.emptyHardpoints = function() { + emptyHardpoints() { for (var i = this.hardpoints.length; i--; ) { this.use(this.hardpoints[i], null, null); } return this; - }; + } - Ship.prototype.emptyInternal = function() { + emptyInternal() { for (var i = this.internal.length; i--; ) { this.use(this.internal[i], null, null); } return this; - }; + } - Ship.prototype.emptyUtility = function() { + emptyUtility() { for (var i = this.hardpoints.length; i--; ) { if (!this.hardpoints[i].maxClass) { this.use(this.hardpoints[i], null, null); } } return this; - }; + } - Ship.prototype.emptyWeapons = function() { + emptyWeapons() { for (var i = this.hardpoints.length; i--; ) { if (this.hardpoints[i].maxClass) { this.use(this.hardpoints[i], null, null); } } return this; - }; + } /** * Optimize for the lower mass build that can still boost and power the ship * without power management. - * @param {object} c Standard Component overrides + * @param {object} m Standard Module overrides */ - Ship.prototype.optimizeMass = function(c) { - return this.emptyHardpoints().emptyInternal().useLightestStandard(c); - }; + optimizeMass(m) { + return this.emptyHardpoints().emptyInternal().useLightestStandard(m); + } - Ship.prototype.setCostIncluded = function(item, included) { - if (item.incCost != included && item.c) { + setCostIncluded(item, included) { + if (item.incCost != included && item.m) { this.totalCost += included ? item.discountedCost : -item.discountedCost; } item.incCost = included; return this; - }; + } - Ship.prototype.setSlotEnabled = function(slot, enabled) { + setSlotEnabled(slot, enabled) { if (slot.enabled != enabled) { // Enabled state is changing slot.enabled = enabled; - if (slot.c) { - this.priorityBands[slot.priority][powerUsageType(slot, slot.c)] += enabled ? slot.c.power : -slot.c.power; + if (slot.m) { + this.priorityBands[slot.priority][powerUsageType(slot, slot.m)] += enabled ? slot.m.power : -slot.m.power; - if (slot.c.grp == 'sg' || slot.c.grp == 'psg') { + if (slot.m.grp == 'sg' || slot.m.grp == 'psg') { this.updateShieldStrength(); - } else if (slot.c.grp == 'sb') { - this.shieldMultiplier += slot.c.shieldmul * (enabled ? 1 : -1); + } else if (slot.m.grp == 'sb') { + this.shieldMultiplier += slot.m.shieldmul * (enabled ? 1 : -1); this.updateShieldStrength(); - } else if (slot.c.dps) { - this.totalDps += slot.c.dps * (enabled ? 1 : -1); + } else if (slot.m.dps) { + this.totalDps += slot.m.dps * (enabled ? 1 : -1); } this.updatePower(); } } return this; - }; + } /** - * Updates the ship's cumulative and aggregated stats based on the component change. + * Updates the ship's cumulative and aggregated stats based on the modul change. */ - Ship.prototype.updateStats = function(slot, n, old, preventUpdate) { + updateStats(slot, n, old, preventUpdate) { var powerChange = slot == this.standard[0]; - if (old) { // Old component now being removed + if (old) { // Old modul now being removed switch (old.grp) { case 'ft': this.fuelCapacity -= old.capacity; @@ -341,7 +334,7 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', } if (slot.incCost && old.cost) { - this.totalCost -= old.cost * this.componentCostMultiplier; + this.totalCost -= old.cost * this.modulCostMultiplier; } if (old.power && slot.enabled) { @@ -372,7 +365,7 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', } if (slot.incCost && n.cost) { - this.totalCost += n.cost * this.componentCostMultiplier; + this.totalCost += n.cost * this.modulCostMultiplier; } if (n.power && slot.enabled) { @@ -398,82 +391,82 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', this.updateShieldStrength(); } return this; - }; + } - Ship.prototype.updatePower = function() { + updatePower() { var bands = this.priorityBands; var prevRetracted = 0, prevDeployed = 0; for (var i = 0, l = bands.length; i < l; i++) { var band = bands[i]; - prevRetracted = band.retractedSum = prevRetracted + band.retracted + band.retOnly; + prevRetracted = band.retractedSum = prevRetracted + band.retracted; prevDeployed = band.deployedSum = prevDeployed + band.deployed + band.retracted; } - this.powerAvailable = this.standard[0].c.pGen; + this.powerAvailable = this.standard[0].m.pGen; this.powerRetracted = prevRetracted; this.powerDeployed = prevDeployed; return this; }; - Ship.prototype.updateTopSpeed = function() { - var speeds = calcSpeed(this.unladenMass + this.fuelCapacity, this.speed, this.boost, this.standard[1].c, this.pipSpeed); + updateTopSpeed() { + var speeds = Calc.speed(this.unladenMass + this.fuelCapacity, this.speed, this.boost, this.standard[1].m, this.pipSpeed); this.topSpeed = speeds['4 Pips']; this.topBoost = speeds.boost; return this; - }; + } - Ship.prototype.updateShieldStrength = function() { + updateShieldStrength() { var sgSlot = this.findInternalByGroup('sg'); // Find Shield Generator slot Index if any - this.shieldStrength = sgSlot && sgSlot.enabled ? calcShieldStrength(this.hullMass, this.baseShieldStrength, sgSlot.c, this.shieldMultiplier) : 0; + this.shieldStrength = sgSlot && sgSlot.enabled ? Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sgSlot.m, this.shieldMultiplier) : 0; return this; - }; + } /** * Jump Range and total range calculations */ - Ship.prototype.updateJumpStats = function() { - var fsd = this.standard[2].c; // Frame Shift Drive; - this.unladenRange = calcJumpRange(this.unladenMass + fsd.maxfuel, fsd, this.fuelCapacity); // Include fuel weight for jump - this.fullTankRange = calcJumpRange(this.unladenMass + this.fuelCapacity, fsd, this.fuelCapacity); // Full Tanke - this.ladenRange = calcJumpRange(this.ladenMass, fsd, this.fuelCapacity); - this.unladenTotalRange = calcTotalRange(this.unladenMass, fsd, this.fuelCapacity); - this.ladenTotalRange = calcTotalRange(this.unladenMass + this.cargoCapacity, fsd, this.fuelCapacity); + updateJumpStats() { + var fsd = this.standard[2].m; // Frame Shift Drive; + this.unladenRange = Calc.jumpRange(this.unladenMass + fsd.maxfuel, fsd, this.fuelCapacity); // Include fuel weight for jump + this.fullTankRange = Calc.jumpRange(this.unladenMass + this.fuelCapacity, fsd, this.fuelCapacity); // Full Tanke + this.ladenRange = Calc.jumpRange(this.ladenMass, fsd, this.fuelCapacity); + this.unladenTotalRange = Calc.totalRange(this.unladenMass, fsd, this.fuelCapacity); + this.ladenTotalRange = Calc.totalRange(this.unladenMass + this.cargoCapacity, fsd, this.fuelCapacity); this.maxJumpCount = Math.ceil(this.fuelCapacity / fsd.maxfuel); return this; - }; + } /** - * Update a slot with a the component if the id is different from the current id for this slot. - * Has logic handling components that you may only have 1 of (Shield Generator or Refinery). + * Update a slot with a the modul if the id is different from the current id for this slot. + * Has logic handling ModuleUtils that you may only have 1 of (Shield Generator or Refinery). * - * @param {object} slot The component slot - * @param {string} id Unique ID for the selected component - * @param {object} component Properties for the selected component + * @param {object} slot The modul slot + * @param {string} id Unique ID for the selected module + * @param {object} modul Properties for the selected module * @param {boolean} preventUpdate If true, do not update aggregated stats */ - Ship.prototype.use = function(slot, id, component, preventUpdate) { - if (slot.id != id) { // Selecting a different component - // Slot is an internal slot, is not being emptied, and the selected component group/type must be of unique - if (slot.cat == 2 && component && _.includes(['psg', 'sg', 'rf', 'fs'], component.grp)) { + use(slot, id, modul, preventUpdate) { + if (slot.id != id) { // Selecting a different modul + // Slot is an internal slot, is not being emptied, and the selected modul group/type must be of unique + if (slot.cat == 2 && modul && UNIQUE_MODULES.includes(modul.grp)) { // Find another internal slot that already has this type/group installed - var similarSlot = this.findInternalByGroup(component.grp); - // If another slot has an installed component with of the same type + var similarSlot = this.findInternalByGroup(modul.grp); + // If another slot has an installed modul with of the same type if (!preventUpdate && similarSlot && similarSlot !== slot) { - this.updateStats(similarSlot, null, similarSlot.c); - similarSlot.id = similarSlot.c = null; // Empty the slot + this.updateStats(similarSlot, null, similarSlot.m); + similarSlot.id = similarSlot.m = null; // Empty the slot similarSlot.discountedCost = 0; } } - var oldComponent = slot.c; + var oldModule = slot.m; slot.id = id; - slot.c = component; - slot.discountedCost = (component && component.cost) ? component.cost * this.componentCostMultiplier : 0; - this.updateStats(slot, component, oldComponent, preventUpdate); + slot.m = modul; + slot.discountedCost = (modul && modul.cost) ? modul.cost * this.modulCostMultiplier : 0; + this.updateStats(slot, modul, oldModule, preventUpdate); } return this; - }; + } /** * [useBulkhead description] @@ -481,98 +474,98 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', * @param {[type]} preventUpdate [description] * @return {[type]} [description] */ - Ship.prototype.useBulkhead = function(index, preventUpdate) { - var oldBulkhead = this.bulkheads.c; + useBulkhead(index, preventUpdate) { + var oldBulkhead = this.bulkheads.m; this.bulkheads.id = index; - this.bulkheads.c = Components.bulkheads(this.id, index); - this.bulkheads.discountedCost = this.bulkheads.c.cost * this.componentCostMultiplier; + this.bulkheads.m = ModuleUtils.bulkheads(this.id, index); + this.bulkheads.discountedCost = this.bulkheads.m.cost * this.modulCostMultiplier; this.armourMultiplier = ArmourMultiplier[index]; - this.updateStats(this.bulkheads, this.bulkheads.c, oldBulkhead, preventUpdate); + this.updateStats(this.bulkheads, this.bulkheads.m, oldBulkhead, preventUpdate); return this; - }; + } /** * [useStandard description] * @param {[type]} rating [description] * @return {[type]} [description] */ - Ship.prototype.useStandard = function(rating) { + useStandard(rating) { for (var i = this.standard.length - 1; i--; ) { // All except Fuel Tank var id = this.standard[i].maxClass + rating; - this.use(this.standard[i], id, Components.standard(i, id)); + this.use(this.standard[i], id, ModuleUtils.standard(i, id)); } return this; - }; + } /** - * Use the lightest standard components unless otherwise specified - * @param {object} c Component overrides + * Use the lightest standard ModuleUtils unless otherwise specified + * @param {object} m Module overrides */ - Ship.prototype.useLightestStandard = function(c) { - c = c || {}; + useLightestStandard(m) { + m = m || {}; var standard = this.standard, - pd = c.pd || this.availCS.lightestPowerDist(this.boostEnergy), // Find lightest Power Distributor that can still boost; - fsd = c.fsd || standard[2].maxClass + 'A', - ls = c.ls || standard[3].maxClass + 'D', - s = c.s || standard[5].maxClass + 'D', + pd = m.pd || this.availCS.lightestPowerDist(this.boostEnergy), // Find lightest Power Distributor that can still boost; + fsd = m.fsd || standard[2].maxClass + 'A', + ls = m.ls || standard[3].maxClass + 'D', + s = m.s || standard[5].maxClass + 'D', updated; this.useBulkhead(0) - .use(standard[2], fsd, Components.standard(2, fsd)) // FSD - .use(standard[3], ls, Components.standard(3, ls)) // Life Support - .use(standard[5], s, Components.standard(5, s)) // Sensors - .use(standard[4], pd, Components.standard(4, pd)); // Power Distributor + .use(standard[2], fsd, ModuleUtils.standard(2, fsd)) // FSD + .use(standard[3], ls, ModuleUtils.standard(3, ls)) // Life Support + .use(standard[5], s, ModuleUtils.standard(5, s)) // Sensors + .use(standard[4], pd, ModuleUtils.standard(4, pd)); // Power Distributor - // Thrusters and Powerplant must be determined after all other components are mounted + // Thrusters and Powerplant must be determined after all other ModuleUtils are mounted // Loop at least once to determine absolute lightest PD and TH do { updated = false; // Find lightest Thruster that still works for the ship at max mass - var th = c.th || this.availCS.lightestThruster(this.ladenMass); + var th = m.th || this.availCS.lightestThruster(this.ladenMass); if (th != standard[1].id) { - this.use(standard[1], th, Components.standard(1, th)); + this.use(standard[1], th, ModuleUtils.standard(1, th)); updated = true; } // Find lightest Power plant that can power the ship - var pp = c.pp || this.availCS.lightestPowerPlant(Math.max(this.powerRetracted, this.powerDeployed), c.ppRating); + var pp = m.pp || this.availCS.lightestPowerPlant(Math.max(this.powerRetracted, this.powerDeployed), m.ppRating); if (pp != standard[0].id) { - this.use(standard[0], pp, Components.standard(0, pp)); + this.use(standard[0], pp, ModuleUtils.standard(0, pp)); updated = true; } } while (updated); return this; - }; + } - Ship.prototype.useUtility = function(group, rating, clobber) { - var component = Components.findHardpoint(group, 0, rating); + useUtility(group, rating, clobber) { + var modul = ModuleUtils.findHardpoint(group, 0, rating); for (var i = this.hardpoints.length; i--; ) { - if ((clobber || !this.hardpoints[i].c) && !this.hardpoints[i].maxClass) { - this.use(this.hardpoints[i], component.id, component); + if ((clobber || !this.hardpoints[i].m) && !this.hardpoints[i].maxClass) { + this.use(this.hardpoints[i], modul.id, modul); } } return this; - }; + } - Ship.prototype.useWeapon = function(group, mount, clobber, missile) { + useWeapon(group, mount, clobber, missile) { var hps = this.hardpoints; for (var i = hps.length; i--; ) { if (hps[i].maxClass) { - var size = hps[i].maxClass, component; + var size = hps[i].maxClass, modul; do { - component = Components.findHardpoint(group, size, null, null, mount, missile); - if ((clobber || !hps[i].c) && component) { - this.use(hps[i], component.id, component); + modul = ModuleUtils.findHardpoint(group, size, null, null, mount, missile); + if ((clobber || !hps[i].m) && modul) { + this.use(hps[i], modul.id, modul); break; } - } while (!component && (--size > 0)); + } while (!modul && (--size > 0)); } } return this; - }; + } /** * Will change the priority of the specified slot if the new priority is valid @@ -580,21 +573,19 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', * @param {number} newPriority The new priority to be set * @return {boolean} Returns true if the priority was changed (within range) */ - Ship.prototype.changePriority = function(slot, newPriority) { + changePriority(slot, newPriority) { if (newPriority >= 0 && newPriority < this.priorityBands.length) { var oldPriority = slot.priority; slot.priority = newPriority; if (slot.enabled) { // Only update power if the slot is enabled - var usage = powerUsageType(slot, slot.c); - this.priorityBands[oldPriority][usage] -= slot.c.power; - this.priorityBands[newPriority][usage] += slot.c.power; + var usage = powerUsageType(slot, slot.m); + this.priorityBands[oldPriority][usage] -= slot.m.power; + this.priorityBands[newPriority][usage] += slot.m.power; this.updatePower(); } return true; } return false; - }; - - return Ship; -}]); + } +} diff --git a/app/js/shipyard/factory-component-set.js b/app/js/shipyard/factory-component-set.js deleted file mode 100755 index d58ff7a1..00000000 --- a/app/js/shipyard/factory-component-set.js +++ /dev/null @@ -1,145 +0,0 @@ -angular.module('shipyard').factory('ComponentSet', ['lodash', function(_) { - - function filter(data, maxClass, minClass, mass) { - return _.filter(data, function(c) { - return c.class <= maxClass && c.class >= minClass && (c.maxmass === undefined || mass <= c.maxmass); - }); - } - - function getKey(maxClass, eligible) { - if (eligible) { - return maxClass + Object.keys(eligible).join('-'); - } - return maxClass; - } - - function ComponentSet(components, mass, maxStandardArr, maxInternal, maxHardPoint) { - this.mass = mass; - this.standard = {}; - this.internal = {}; - this.hardpoints = {}; - this.hpClass = {}; - this.intClass = {}; - - this.standard[0] = filter(components.standard[0], maxStandardArr[0], 0, mass); // Power Plant - this.standard[2] = filter(components.standard[2], maxStandardArr[2], 0, mass); // FSD - this.standard[4] = filter(components.standard[4], maxStandardArr[4], 0, mass); // Power Distributor - this.standard[6] = filter(components.standard[6], maxStandardArr[6], 0, mass); // Fuel Tank - - // Thrusters, filter components by class only (to show full list of ratings for that class) - var minThrusterClass = _.reduce(components.standard[1], function(minClass, thruster) { - return (thruster.maxmass >= mass && thruster.class < minClass) ? thruster.class : minClass; - }, maxStandardArr[1]); - this.standard[1] = filter(components.standard[1], maxStandardArr[1], minThrusterClass, 0); // Thrusters - - // Slots where component class must be equal to slot class - this.standard[3] = filter(components.standard[3], maxStandardArr[3], maxStandardArr[3], 0); // Life Supprt - this.standard[5] = filter(components.standard[5], maxStandardArr[5], maxStandardArr[5], mass); // Sensors - - for (var h in components.hardpoints) { - this.hardpoints[h] = filter(components.hardpoints[h], maxHardPoint, 0, mass); - } - - for (var g in components.internal) { - this.internal[g] = filter(components.internal[g], maxInternal, 0, mass); - } - - /** - * Create a memoized function for determining the components that are - * eligible for an internal slot - * @param {integer} c The max class component that can be mounted in the slot - * @param {Object} eligible) The map of eligible internal groups - * @return {object} A map of all eligible components by group - */ - this.getInts = _.memoize( - function(c, eligible) { - var o = {}; - for (var key in this.internal) { - if (eligible && !eligible[key]) { - continue; - } - var data = filter(this.internal[key], c, 0, this.mass); - if (data.length) { // If group is not empty - o[key] = data; - } - } - return o; - }, - getKey - ); - - /** - * Create a memoized function for determining the components that are - * eligible for an hardpoint slot - * @param {integer} c The max class component that can be mounted in the slot - * @param {Object} eligible) The map of eligible hardpoint groups - * @return {object} A map of all eligible components by group - */ - this.getHps = _.memoize( - function(c, eligible) { - var o = {}; - for (var key in this.hardpoints) { - if (eligible && !eligible[key]) { - continue; - } - var data = filter(this.hardpoints[key], c, c ? 1 : 0, this.mass); - if (data.length) { // If group is not empty - o[key] = data; - } - } - return o; - }, - getKey - ); - } - - ComponentSet.prototype.lightestPowerDist = function(boostEnergy) { - var pds = this.standard[4]; - var pd = pds[0]; - - for (var i = 1; i < pds.length; i++) { - if (pds[i].mass < pd.mass && pds[i].enginecapacity >= boostEnergy) { - pd = pds[i]; - } - } - return pd.class + pd.rating; - }; - - ComponentSet.prototype.lightestThruster = function(ladenMass) { - var ths = this.standard[1]; - var th = ths[0]; - - for (var i = 1; i < ths.length; i++) { - if (ths[i].mass < th.mass && ths[i].maxmass >= ladenMass) { - th = ths[i]; - } - } - return th.class + th.rating; - }; - - ComponentSet.prototype.lightestShieldGenerator = function(hullMass) { - var sg = null; - - _.forEach(this.internal.sg, function(s) { - if (sg == null || (s.mass < sg.mass && s.minmass <= hullMass && s.maxmass > hullMass)) { - sg = s; - } - }); - return sg.id; - }; - - ComponentSet.prototype.lightestPowerPlant = function(powerUsed, rating) { - var pps = this.standard[0]; - var pp = null; - - for (var i = 0; i < pps.length; i++) { - if (pp == null || (pps[i].mass < pp.mass && pps[i].pGen >= powerUsed)) { - pp = pps[i]; - } - } - return pp.class + (pp.rating != 'D' || rating == 'A' ? 'A' : 'D'); // Use A rated if C,E - }; - - return ComponentSet; - -}]); diff --git a/app/js/shipyard/module-shipyard.js b/app/js/shipyard/module-shipyard.js deleted file mode 100755 index f348a102..00000000 --- a/app/js/shipyard/module-shipyard.js +++ /dev/null @@ -1,252 +0,0 @@ -/** - * This module contains all of the logic and models corresponding to - * information or behavoir in Elite Dangerous. - * - * This file contains values and functions that can be reused across the app. - * - * @requires ngLodash - */ -angular.module('shipyard', ['ngLodash']) - // Create 'angularized' references to DB. This will aid testing - .constant('ShipsDB', DB.ships) - .constant('ComponentsDB', DB.components) - .constant('ArmourMultiplier', [ - 1, // Lightweight - 1.4, // Reinforced - 1.945, // Military - 1.945, // Mirrored - 1.945 // Reactive - ]) - .constant('SizeMap', ['', 'small', 'medium', 'large', 'capital']) - // Map to lookup group labels/names for component grp, used for JSON Serialization - .constant('GroupMap', { - // Standard - pp: 'Power Plant', - t: 'Thrusters', - fsd: 'Frame Shift Drive', - ls: 'Life Support', - pd: 'Power Distributor', - s: 'Sensors', - ft: 'Fuel Tank', - - // Internal - fs: 'Fuel Scoop', - sc: 'Scanner', - am: 'Auto Field-Maintenance Unit', - cr: 'Cargo Rack', - fi: 'Frame Shift Drive Interdictor', - hb: 'Hatch Breaker Limpet Controller', - hr: 'Hull Reinforcement Package', - rf: 'Refinery', - scb: 'Shield Cell Bank', - sg: 'Shield Generator', - psg: 'Prismatic Shield Generator', - dc: 'Docking Computer', - fx: 'Fuel Transfer Limpet Controller', - pc: 'Prospector Limpet Controller', - cc: 'Collector Limpet Controller', - - // Hard Points - bl: 'Beam Laser', - ul: 'Burst Laser', - c: 'Cannon', - cs: 'Cargo Scanner', - cm: 'Countermeasure', - fc: 'Fragment Cannon', - ws: 'Frame Shift Wake Scanner', - kw: 'Kill Warrant Scanner', - nl: 'Mine Launcher', - ml: 'Mining Laser', - mr: 'Missile Rack', - pa: 'Plasma Accelerator', - mc: 'Multi-cannon', - pl: 'Pulse Laser', - rg: 'Rail Gun', - sb: 'Shield Booster', - tp: 'Torpedo Pylon' - }) - .constant('MountMap', { - 'F': 'Fixed', - 'G': 'Gimballed', - 'T': 'Turret', - 'Fixed': 'F', - 'Gimballed': 'G', - 'Turret': 'T' - }) - /** - * Array of all Ship properties (facets) organized into groups - * used for ship comparisons. - * - * @type {Array} - */ - .constant('ShipFacets', [ - { // 0 - title: 'agility', - props: ['agility'], - unit: '', - fmt: 'fCrd' - }, - { // 1 - title: 'speed', - props: ['topSpeed', 'topBoost'], - lbls: ['thrusters', 'boost'], - unit: 'm/s', - fmt: 'fCrd' - }, - { // 2 - title: 'armour', - props: ['armour'], - unit: '', - fmt: 'fCrd' - }, - { // 3 - title: 'shields', - props: ['shieldStrength'], - unit: 'MJ', - fmt: 'fRound' - }, - { // 4 - title: 'jump range', - props: ['unladenRange', 'fullTankRange', 'ladenRange'], - lbls: ['max', 'full tank', 'laden'], - unit: 'LY', - fmt: 'fRound' - }, - { // 5 - title: 'mass', - props: ['unladenMass', 'ladenMass'], - lbls: ['unladen', 'laden'], - unit: 'T', - fmt: 'fRound' - }, - { // 6 - title: 'cargo', - props: ['cargoCapacity'], - unit: 'T', - fmt: 'fRound' - }, - { // 7 - title: 'fuel', - props: ['fuelCapacity'], - unit: 'T', - fmt: 'fRound' - }, - { // 8 - title: 'power', - props: ['powerRetracted', 'powerDeployed', 'powerAvailable'], - lbls: ['retracted', 'deployed', 'available'], - unit: 'MW', - fmt: 'fPwr' - }, - { // 9 - title: 'cost', - props: ['totalCost'], - unit: 'CR', - fmt: 'fCrd' - }, - { // 10 - title: 'total range', - props: ['unladenTotalRange', 'ladenTotalRange'], - lbls: ['unladen', 'laden'], - unit: 'LY', - fmt: 'fRound' - }, - { // 11 - title: 'DPS', - props: ['totalDps'], - lbls: ['DPS'], - unit: '', - fmt: 'fRound' - } - ]) - /** - * Set of all available / theoretical discounts - */ - .constant('Discounts', { - '0%': 1, - '5%': 0.95, - '10%': 0.90, - '15%': 0.85, - '20%': 0.80, - '25%': 0.75 - }) - /** - * Calculate the maximum single jump range based on mass and a specific FSD - * - * @param {number} mass Mass of a ship: laden, unlanden, partially laden, etc - * @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass - * @param {number} fuel Optional - The fuel consumed during the jump (must be less than the drives max fuel per jump) - * @return {number} Distance in Light Years - */ - .value('calcJumpRange', function(mass, fsd, fuel) { - return Math.pow(Math.min(fuel === undefined ? fsd.maxfuel : fuel, fsd.maxfuel) / fsd.fuelmul, 1 / fsd.fuelpower ) * fsd.optmass / mass; - }) - /** - * Calculate the total range based on mass and a specific FSD, and all fuel available - * - * @param {number} mass Mass of a ship: laden, unlanden, partially laden, etc - * @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass - * @param {number} fuel The total fuel available - * @return {number} Distance in Light Years - */ - .value('calcTotalRange', function(mass, fsd, fuel) { - var fuelRemaining = fuel % fsd.maxfuel; // Fuel left after making N max jumps - var jumps = Math.floor(fuel / fsd.maxfuel); - mass += fuelRemaining; - // Going backwards, start with the last jump using the remaining fuel - var totalRange = fuelRemaining > 0 ? Math.pow(fuelRemaining / fsd.fuelmul, 1 / fsd.fuelpower ) * fsd.optmass / mass : 0; - // For each max fuel jump, calculate the max jump range based on fuel mass left in the tank - for (var j = 0; j < jumps; j++) { - mass += fsd.maxfuel; - totalRange += Math.pow(fsd.maxfuel / fsd.fuelmul, 1 / fsd.fuelpower ) * fsd.optmass / mass; - } - return totalRange; - }) - /** - * Calculate the a ships shield strength based on mass, shield generator and shield boosters used. - * - * @param {number} mass Current mass of the ship - * @param {number} shields Base Shield strength MJ for ship - * @param {object} sg The shield generator used - * @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any) - * @return {number} Approximate shield strengh in MJ - */ - .value('calcShieldStrength', function(mass, shields, sg, multiplier) { - var opt; - if (mass < sg.minmass) { - return shields * multiplier * sg.minmul; - } - if (mass > sg.maxmass) { - return shields * multiplier * sg.maxmul; - } - if (mass < sg.optmass) { - opt = (sg.optmass - mass) / (sg.optmass - sg.minmass); - opt = 1 - Math.pow(1 - opt, 0.87); - return shields * multiplier * ((opt * sg.minmul) + ((1 - opt) * sg.optmul)); - } else { - opt = (sg.optmass - mass) / (sg.maxmass - sg.optmass); - opt = -1 + Math.pow(1 + opt, 2.425); - return shields * multiplier * ( (-1 * opt * sg.maxmul) + ((1 + opt) * sg.optmul) ); - } - }) - /** - * Calculate the a ships speed based on mass, and thrusters. - * - * @param {number} mass Current mass of the ship - * @param {number} baseSpeed Base speed m/s for ship - * @param {number} baseBoost Base boost speed m/s for ship - * @param {object} thrusters The Thrusters used - * @param {number} pipSpeed Speed pip multiplier - * @return {object} Approximate speed by pips - */ - .value('calcSpeed', function(mass, baseSpeed, baseBoost, thrusters, pipSpeed) { - var multiplier = mass > thrusters.maxmass ? 0 : ((1 - thrusters.M) + (thrusters.M * Math.pow(3 - (2 * Math.max(0.5, mass / thrusters.optmass)), thrusters.P))); - var speed = baseSpeed * multiplier; - - return { - '0 Pips': speed * (1 - (pipSpeed * 4)), - '2 Pips': speed * (1 - (pipSpeed * 2)), - '4 Pips': speed, - 'boost': baseBoost * multiplier - }; - }); diff --git a/app/js/shipyard/service-components.js b/app/js/shipyard/service-components.js deleted file mode 100755 index f4c540d6..00000000 --- a/app/js/shipyard/service-components.js +++ /dev/null @@ -1,181 +0,0 @@ -angular.module('shipyard').service('Components', ['lodash', 'ComponentsDB', 'ShipsDB', 'ComponentSet', 'GroupMap', function(_, C, Ships, ComponentSet, GroupMap) { - - var GrpNameToCodeMap = {}; - - for (var grp in GroupMap) { - GrpNameToCodeMap[GroupMap[grp]] = grp; - } - - this.cargoHatch = function() { - return { name: 'Cargo Hatch', class: 1, rating: 'H', power: 0.6 }; - }; - - this.standard = function(typeIndex, componentId) { - return C.standard[typeIndex][componentId]; - }; - - this.hardpoints = function(id) { - for (var n in C.hardpoints) { - var group = C.hardpoints[n]; - for (var i = 0; i < group.length; i++) { - if (group[i].id == id) { - return group[i]; - } - } - } - return null; - }; - - this.internal = function(id) { - for (var n in C.internal) { - var group = C.internal[n]; - for (var i = 0; i < group.length; i++) { - if (group[i].id == id) { - return group[i]; - } - } - } - return null; - }; - - /** - * Finds an internal Component based on Class, Rating, Group and/or name. - * At least one ofGroup name or unique component name must be provided - * - * @param {string} groupName [Optional] Full name or abbreviated name for component group - * @param {integer} clss Component Class - * @param {string} rating Component Rating - * @param {string} name [Optional] Long/unique name for component -e.g. 'Advanced Discover Scanner' - * @return {String} The id of the component if found, null if not found - */ - this.findInternal = function(groupName, clss, rating, name) { - var groups = {}; - - if (groupName) { - if (C.internal[groupName]) { - groups[groupName] = C.internal[groupName]; - } else { - var grpCode = GrpNameToCodeMap[groupName]; - if (grpCode && C.internal[grpCode]) { - groups[grpCode] = C.internal[grpCode]; - } - } - } else if (name) { - groups = C.internal; - } - - for (var g in groups) { - var group = groups[g]; - 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]; - } - } - } - - return null; - }; - - /** - * Finds an internal Component ID based on Class, Rating, Group and/or name. - * At least one ofGroup name or unique component name must be provided - * - * @param {string} groupName [Optional] Full name or abbreviated name for component group - * @param {integer} clss Component Class - * @param {string} rating Component Rating - * @param {string} name [Optional] Long/unique name for component -e.g. 'Advanced Discover Scanner' - * @return {String} The id of the component if found, null if not found - */ - this.findInternalId = function(groupName, clss, rating, name) { - var i = this.findInternal(groupName, clss, rating, name); - return i ? i.id : 0; - }; - - /** - * Finds a hardpoint Component based on Class, Rating, Group and/or name. - * At least one ofGroup name or unique component name must be provided - * - * @param {string} groupName [Optional] Full name or abbreviated name for component group - * @param {integer} clss Component Class - * @param {string} rating [Optional] Component Rating - * @param {string} name [Optional] Long/unique name for component -e.g. 'Heat Sink Launcher' - * @param {string} mode Mount mode/type - [F]ixed, [G]imballed, [T]urret - * @param {string} missile [Optional] Missile type - [D]umbfire, [S]eeker - * @return {String} The id of the component if found, null if not found - */ - this.findHardpoint = function(groupName, clss, rating, name, mode, missile) { - var groups = {}; - - if (groupName) { - if (C.hardpoints[groupName]) { - groups[groupName] = C.hardpoints[groupName]; - } else { - var grpCode = GrpNameToCodeMap[groupName]; - if (grpCode && C.hardpoints[grpCode]) { - groups[grpCode] = C.hardpoints[grpCode]; - } - } - } else if (name) { - groups = C.hardpoints; - } - - for (var g in groups) { - var group = groups[g]; - for (var i = 0, l = group.length; i < l; i++) { - if (group[i].class == clss && (!rating || 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]; - } - } - } - - return null; - }; - - /** - * Finds a hardpoint Component ID based on Class, Rating, Group and/or name. - * At least one of Group name or unique component name must be provided - * - * @param {string} groupName [Optional] Full name or abbreviated name for component group - * @param {integer} clss Component Class - * @param {string} rating Component Rating - * @param {string} name [Optional] Long/unique name for component -e.g. 'Heat Sink Launcher' - * @param {string} mode Mount mode/type - [F]ixed, [G]imballed, [T]urret - * @param {string} missile [Optional] Missile type - [D]umbfire, [S]eeker - * @return {String} The id of the component if found, null if not found - */ - this.findHardpointId = function(groupName, clss, rating, name, mode, missile) { - var h = this.findHardpoint(groupName, clss, rating, name, mode, missile); - return h ? h.id : 0; - }; - - /** - * Looks up the bulkhead component for a specific ship and bulkhead - * @param {string} shipId Unique ship Id/Key - * @param {number} bulkheadsId Id/Index for the specified bulkhead - * @return {object} The bulkhead component object - */ - this.bulkheads = function(shipId, bulkheadsId) { - 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. - * - * @param {string} shipId Unique ship Id/Key - * @return {ComponentSet} The set of components the ship can install - */ - this.forShip = function(shipId) { - var ship = Ships[shipId]; - var maxInternal = isNaN(ship.slots.internal[0]) ? ship.slots.internal[0].class : ship.slots.internal[0]; - return new ComponentSet(C, ship.minMassFilter || ship.properties.hullMass + 5, ship.slots.standard, maxInternal, ship.slots.hardpoints[0]); - }; - -}]); diff --git a/app/js/stores/Persist.js b/app/js/stores/Persist.js new file mode 100644 index 00000000..94cda260 --- /dev/null +++ b/app/js/stores/Persist.js @@ -0,0 +1,308 @@ +import { EventEmitter } from 'fbemitter'; + +const LS_KEY_BUILDS = 'builds'; +const LS_KEY_COMPARISONS = 'comparisons'; +const LS_KEY_LANG = 'NG_TRANSLATE_LANG_KEY'; +const LS_KEY_COST_TAB = 'costTab'; +const LS_KEY_INSURANCE = 'insurance'; +const LS_KEY_DISCOUNTS = 'discounts'; +const LS_KEY_DISCOUNTS = 'state'; +const LS_KEY_SIZE_RATIO = 'sizeRatio'; + +let LS; + +// Safe check to determine if localStorage is enabled +try { + localStorage.setItem('s', 1); + localStorage.removeItem('s'); + LS = localStorage; +} catch(e) { + LS = null; +} + +function _put(key, value) { + if (LS) { + LS.setItem(key, typeof value != "string" ? JSON.stringify(value) : value); + } +} + +function _getString(key) { + return LS.getItem(key) : null; +} + +function _get(key) { + let str = _getString(key); + return str ? JSON.parse(str) : null; +} + +function _delete(key) { + if (LS) { + LS.removeItem(key); + } +} + + +/** + * [description] + */ +class Persist extends EventEmitter { + + constructor() { + let buildJson = _get(LS_KEY_BUILDS); + let comparisonJson = _get(LS_KEY_COMPARISONS); + + this.builds = buildJson ? JSON.parse(buildJson) : {}; + this.comparisons = comparisonJson ? JSON.parse(comparisonJson) : {}; + this.buildCount = Object.keys(this.builds).length; + this.langCode = _getString(LS_KEY_LANG) || 'en'; + this.insurance = _getString(LS_KEY_INSURANCE); + this.discount = _getString(LS_KEY_DISCOUNTS); + this.costTab = _getString(LS_KEY_COST_TAB); + this.state = _get(LS_KEY_STATE); + this.sizeRatio = _get(LS_KEY_SIZE_RATIO) || 1; + } + + getLangCode() { + return this.langCode; + }; + + setLangCode(langCode) { + this.langCode = langCode; + _put(LS_KEY_LANG, langCode); + this.emit('language', langCode); + } + + /** + * Persist a ship build in local storage. + * + * @param {string} shipId The unique id for a model of ship + * @param {string} name The name of the build + * @param {string} code The serialized code + */ + saveBuild(shipId, name, code) { + if (LS) { + if (!this.builds[shipId]) { + this.builds[shipId] = {}; + } + let newBuild = !this.builds[shipId][name]; + this.builds[shipId][name] = code; + _put(LS_KEY_BUILDS, this.builds); + if (newBuild) { + this.emit('builds', this.builds); + } + } + }; + + /** + * Get the serialized code/string for a build. Returns null if a + * build is not found. + * + * @param {string} shipId The unique id for a model of ship + * @param {string} name The name of the build + * @return {string} The serialized build string. + */ + getBuild(shipId, name) { + if (this.builds[shipId] && this.builds[shipId][name]) { + return this.builds[shipId][name]; + } + return null; + }; + + getBuilds() { + return this.builds; + } + + /** + * Delete a build from local storage. It will also delete the ship build collection if + * it becomes empty + * + * @param {string} shipId The unique id for a model of ship + * @param {string} name The name of the build + */ + deleteBuild(shipId, name) { + if (this.builds[shipId][name]) { + delete this.builds[shipId][name]; + if (Object.keys(this.builds[shipId]).length === 0) { + delete this.builds[shipId]; + } + _put(LS_KEY_BUILDS, this.builds); + // Check if the build was used in existing comparisons + var comps = this.comparisons; + for (var c in comps) { + for (var i = 0; i < comps[c].builds.length; i++) { // For all builds in the current comparison + if (comps[c].builds[i].shipId == shipId && comps[c].builds[i].buildName == name) { + comps[c].builds.splice(i, 1); + break; // A build is unique per comparison + } + } + } + _put(LS_KEY_COMPARISONS, this.comparisons); + this.emit('builds', this.builds); + } + }; + + /** + * Persist a comparison in localstorage. + * + * @param {string} name The name of the comparison + * @param {array} builds Array of builds + * @param {array} facets Array of facet indices + */ + saveComparison(name, builds, facets) { + if (!this.comparisons[name]) { + this.comparisons[name] = {}; + } + this.comparisons[name] = { + facets: facets, + builds: builds.map(b => { shipId: b.id || b.shipId, buildName: b.buildName }) + }; + _put(LS_KEY_COMPARISONS, this.comparisons); + this.emit('comparisons', this.comparisons); + }; + + /** + * [getComparison description] + * @param {string} name [description] + * @return {object} Object containing array of facets and ship id + build names + */ + getComparison(name) { + if (this.comparisons[name]) { + return this.comparisons[name]; + } + return null; + }; + + getComparisons() { + return this.comparisons; + } + + /** + * Removes the comparison from localstorage. + * @param {string} name Comparison name + */ + deleteComparison(name) { + if (this.comparisons[name]) { + delete this.comparisons[name]; + _put(LS_KEY_COMPARISONS, this.comparisons); + this.emit('comparisons', this.comparisons); + } + }; + + /** + * Delete all builds and comparisons from localStorage + */ + deleteAll() { + this.builds = {}; + this.comparisons = {}; + _delete(LS_KEY_BUILDS); + _delete(LS_KEY_COMPARISONS); + this.emit('deletedall'); + }; + + getAll() { + var data = {}; + data[LS_KEY_BUILDS] = this.getBuilds(); + data[LS_KEY_COMPARISONS] = this.getComparisons(); + data[LS_KEY_INSURANCE] = this.getInsurance(); + data[LS_KEY_DISCOUNTS] = this.getDiscount(); + return data; + }; + + /** + * Get the saved insurance type + * @return {string} The name of the saved insurance type of null + */ + getInsurance() { + return this.insurance; + }; + + /** + * Persist selected insurance type + * @param {string} name Insurance type name + */ + setInsurance(insurance) { + this.insurance = insurance + _put(LS_KEY_INSURANCE, insurance); + this.emit('insurance', insurance); + }; + + /** + * Persist selected discount + * @param {number} val Discount value/amount + */ + setDiscount(discount) { + this.discount = discount; + _put(LS_KEY_DISCOUNTS, discount); + this.emit('discount', discount); + }; + + /** + * Get the saved discount + * @return {number} val Discount value/amount + */ + getDiscount() { + return this.discount; + }; + + /** + * Persist selected cost tab + * @param {number} val Discount value/amount + */ + setCostTab(tabName) { + this.costTab = tabName; + _put(LS_KEY_COST_TAB, tabName); + }; + + /** + * Get the saved discount + * @return {number} val Discount value/amount + */ + getCostTab() { + return this.costTab; + }; + + /** + * Retrieve the last router state from local storage + * @return {object} state State object containing state name and params + */ + getState() { + return this.state; + }; + + /** + * Save the current router state to localstorage + * @param {object} state State object containing state name and params + */ + setState(state) { + this.state = state; + _put(LS_KEY_STATE, state); + }; + + /** + * Retrieve the last router state from local storage + * @return {number} size Ratio + */ + getSizeRatio() { + return this.sizeRatio; + }; + + /** + * Save the current size ratio to localstorage + * @param {number} sizeRatio + */ + setSizeRatio(sizeRatio) { + this.sizeRatio = sizeRatio; + _put(LS_KEY_SIZE_RATIO, sizeRatio); + this.emit('sizeRatio', sizeRatio); + }; + + /** + * Check if localStorage is enabled/active + * @return {Boolean} True if localStorage is enabled + */ + isEnabled() { + return LS != null; + } +} + +export default new Persist(); diff --git a/app/js/utils/BBCode.js b/app/js/utils/BBCode.js new file mode 100644 index 00000000..0bee4c53 --- /dev/null +++ b/app/js/utils/BBCode.js @@ -0,0 +1,43 @@ +import { term, format } from '../i18n/Language'; + +export default function comparisonBBCode(facets, builds, link) { + var colCount = 2, b, i, j, k, f, fl, p, pl, l = []; + + for (i = 0; i < facets.length; i++) { + if (facets[i].active) { + f = facets[i]; + p = f.props; + + if (p.length == 1) { + l.push('[th][B][COLOR=#FF8C0D]', term(f.title).toUpperCase(), '[/COLOR][/B][/th]'); + colCount++; + } else { + for (j = 0; j < p.length; j++) { + l.push('[th][B][COLOR=#FF8C0D]', term(f.title).toUpperCase(), '\n', term(f.lbls[j]).toUpperCase(), '[/COLOR][/B][/th]'); + colCount++; + } + } + } + } + l.push('[/tr]\n'); + + for (i = 0; i < builds.length; i++) { + b = builds[i]; + //var href = $state.href('outfit',{shipId: b.id, code: b.code, bn: b.buildName}, {absolute: true}); + l.push('[tr][td]', b.name, '[/td][td]', b.buildName, '[/td]'); + + for (j = 0, fl = facets.length; j < fl; j++) { + if (facets[j].active) { + f = facets[j]; + p = f.props; + for (k = 0, pl = p.length; k < pl; k++) { + l.push('[td="align: right"]', format[f.fmt](b[p[k]]), ' [size=-2]', term(f.unit), '[/size][/td]'); + } + } + } + l.push('[/tr]\n'); + } + l.push('[tr][td="align: center, colspan:', colCount, '"][size=-3]\n[url=', link, ']Interactive Comparison at Coriolis.io[/url][/td][/tr]\n[/size][/table]'); + l.unshift('[table="width:', colCount * 90, ',align: center"]\n[tr][th][B][COLOR=#FF8C0D]Ship[/COLOR][/B][/th][th][B][COLOR="#FF8C0D"]Build[/COLOR][/B][/th]'); + return l.join(''); +} diff --git a/app/js/utils/ShortenUrl.js b/app/js/utils/ShortenUrl.js new file mode 100644 index 00000000..22cc498e --- /dev/null +++ b/app/js/utils/ShortenUrl.js @@ -0,0 +1,10 @@ + +export default function shortenUrl(url) { + /*if (window.navigator.onLine) { + return $http.post(shortenAPI + GAPI_KEY, { longUrl: url }).then(function(response) { + return response.data.id; + }); + } else { + return $q.reject({ statusText: 'Not Online' }); + }*/ +} diff --git a/app/less/app.less b/app/less/app.less index 522ec240..f2ce9669 100755 --- a/app/less/app.less +++ b/app/less/app.less @@ -45,7 +45,7 @@ div, a, li { -webkit-tap-highlight-color: rgba(0,0,0,0); } -#main { +#coriolis { margin: 0; padding: 0.5em 0.5em; width: 100%; diff --git a/app/views/_header.html b/app/views/_header.html deleted file mode 100755 index 0a9abc64..00000000 --- a/app/views/_header.html +++ /dev/null @@ -1,91 +0,0 @@ - - -
- - - - - - - - - - -
\ No newline at end of file diff --git a/app/views/page-shipyard.html b/app/views/page-shipyard.html deleted file mode 100755 index 14a205ce..00000000 --- a/app/views/page-shipyard.html +++ /dev/null @@ -1,87 +0,0 @@ -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{fCrd(s.speed)}} m/s{{fCrd(s.boost)}} m/s{{fCrd(s.baseShieldStrength)}} Mj{{fCrd(s.topSpeed)}} m/s{{fCrd(s.topBoost)}} m/s{{fRound(s.maxJumpRange)}} LY{{fCrd(s.maxCargo)}} T{{fCrd(s.hullMass)}} T{{fCrd(s.retailCost)}} CR
-
- - -
\ No newline at end of file diff --git a/bower.json b/bower.json deleted file mode 100755 index 1dfea4f5..00000000 --- a/bower.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "coriolis_shipyard", - "authors": [ - "Colin McLeod " - ], - "description": "Coriolis Shipyard for Elite Dangerous", - "main": "app/app.js", - "keywords": [ - "elite", - "shipyard" - ], - "license": "MIT", - "private": true, - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "test", - "tests" - ], - "dependencies": { - "d3": "~3.5.5", - "ng-lodash": "~0.2.0", - "ui-router-extras": "0.0.13", - "angular-ui-router": "^0.2.15", - "d3-tip": "~0.6.7", - "ng-sortable": "~1.2.1", - "lz-string": "~1.4.4", - "angular": "~1.4.0", - "angular-translate": "~2.7.2" - }, - "overrides": { - "angular": { - "main": "angular.min.js" - }, - "angular-ui-router": { - "main": "release/angular-ui-router.min.js" - }, - "angular-translate": { - "main": "angular-translate.min.js" - }, - "d3": { - "main": "d3.min.js" - }, - "ng-lodash": { - "main": "build/ng-lodash.min.js" - }, - "ui-router-extras": { - "main": [ - "release/modular/ct-ui-router-extras.core.min.js", - "release/modular/ct-ui-router-extras.sticky.min.js" - ] - }, - "ng-sortable": { - "main": "dist/ng-sortable.min.js" - } - }, - "resolutions": { - "angular": "~1.4.0" - } -} diff --git a/data b/data deleted file mode 160000 index 5350fb5e..00000000 --- a/data +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5350fb5e077f3ef51c43b5bbf467bdd383408af0 diff --git a/package.json b/package.json index fedddc7e..d9b6e733 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "coriolis_shipyard", - "version": "1.9.0", + "version": "2.0.0-alpha", "repository": { "type": "git", "url": "https://github.com/cmmcleod/coriolis" @@ -11,14 +11,11 @@ "engine": "node >= 0.12.2", "license": "MIT", "devDependencies": { - "angular-mocks": "1.4.x", "async": "0.9.x", "del": "1.2.x", "gulp": "3.9.x", - "gulp-angular-templatecache": "1.6.x", "gulp-concat": "2.5.x", "gulp-eslint": "0.13.x", - "gulp-htmlmin": "1.1.x", "gulp-jasmine": "2.0.x", "gulp-jsonlint": "1.1.x", "gulp-less": "3.0.x", @@ -40,9 +37,14 @@ "karma-json-fixtures-preprocessor": "0.0.4", "karma-mocha-reporter": "1.0.x", "karma-phantomjs-launcher": "0.2.x", - "main-bower-files": "2.8.x", "phantomjs": "1.9.x", "run-sequence": "1.1.x", "uglify-js": "2.4.x" + }, + "dependencies": { + "classnames": "^2.2.0", + "fbemitter": "^2.0.0", + "react": "^0.14.2", + "react-dom": "^0.14.2" } } From 79224f4f9a6f2c5df2510bd35196734fea49d20d Mon Sep 17 00:00:00 2001 From: Colin McLeod Date: Sun, 29 Nov 2015 18:44:59 -0800 Subject: [PATCH 02/18] more react changes, incomplete --- .babelrc | 20 + .eslintrc | 21 + .gitignore | 5 - .travis.yml | 2 - app/icons/bin.svg | 7 - app/icons/cogs.svg | 6 - app/icons/coriolis.svg | 8 - app/icons/download.svg | 6 - app/icons/eddb.svg | 28 -- app/icons/embed.svg | 4 - app/icons/equalizer.svg | 3 - app/icons/floppy-disk.svg | 3 - app/icons/fuel.svg | 3 - app/icons/github-mark.svg | 3 - app/icons/hammer.svg | 6 - app/icons/infinite.svg | 5 - app/icons/info.svg | 8 - app/icons/link.svg | 7 - app/icons/mount-F.svg | 7 - app/icons/mount-G.svg | 4 - app/icons/mount-T.svg | 5 - app/icons/no-power.svg | 3 - app/icons/notification.svg | 6 - app/icons/power.svg | 3 - app/icons/question.svg | 6 - app/icons/rocket.svg | 6 - app/icons/spinner11.svg | 3 - app/icons/station-coriolis.svg | 6 - app/icons/station-ocellus.svg | 7 - app/icons/station-orbis.svg | 6 - app/icons/station-outpost.svg | 7 - app/icons/stats-bars.svg | 6 - app/icons/switch.svg | 4 - app/icons/upload.svg | 6 - app/icons/warning.svg | 8 - app/js/Coriolis.jsx | 55 --- app/js/components/Header.jsx | 118 ----- .../components/directive-component-select.js | 90 ---- app/js/components/directive-header.js | 114 ----- app/js/components/directive-loader.js | 9 - app/js/components/directive-slot-hardpoint.js | 13 - app/js/components/directive-slot-internal.js | 13 - app/js/i18n/Language.js | 52 --- app/js/old-app.js | 114 ----- app/js/pages/NotFoundPage.jsx | 12 - app/js/pages/ShipyardPage.jsx | 180 ------- app/js/pages/controller-delete.js | 7 - app/js/pages/controller-error.js | 29 -- app/js/pages/controller-export.js | 19 - app/js/pages/controller-import.js | 316 ------------- app/js/pages/controller-link.js | 16 - app/js/pages/controller-modal.js | 14 - app/js/utils/ShortenUrl.js | 10 - app/less/background-images.less | 8 - app/less/fonts.less | 26 -- app/views/_modal.html | 3 - app/views/_slot-hardpoint.html | 17 - app/views/_slot-internal.html | 22 - app/views/modal-about.html | 29 -- app/views/modal-delete.html | 4 - app/views/modal-export.html | 6 - app/views/modal-import.html | 46 -- app/views/modal-link.html | 9 - app/views/page-comparison.html | 74 --- app/views/page-error.html | 21 - devServer.js | 27 ++ gulpfile.js | 281 ----------- nginx.conf | 58 --- package.json | 64 +-- scripts/json-to-db.js | 90 ---- src/app/Coriolis.jsx | 110 +++++ {app/js => src/app}/Router.js | 24 +- src/app/components/ActiveLink.jsx | 20 + src/app/components/AvailableModulesMenu.jsx | 103 ++++ src/app/components/HardpointSlot.jsx | 38 ++ src/app/components/HardpointsSlotSection.jsx | 87 ++++ src/app/components/Header.jsx | 251 ++++++++++ src/app/components/InternalSlot.jsx | 38 ++ src/app/components/InternalSlotSection.jsx | 69 +++ src/app/components/Link.jsx | 31 ++ src/app/components/ModalDeleteAll.jsx | 22 + src/app/components/ModalExport.jsx | 49 ++ src/app/components/ModalImport.jsx | 439 ++++++++++++++++++ src/app/components/ModalPermalink.jsx | 41 ++ src/app/components/ShipSummaryTable.jsx | 94 ++++ src/app/components/Slot.jsx | 61 +++ src/app/components/SlotSection.jsx | 73 +++ src/app/components/StandardSlot.jsx | 60 +++ src/app/components/StandardSlotSection.jsx | 167 +++++++ src/app/components/SvgIcons.jsx | 302 ++++++++++++ src/app/components/TranslatedComponent.jsx | 24 + src/app/components/UtilitySlotSection.jsx | 64 +++ .../app}/components/directive-area-chart.js | 0 .../app}/components/directive-bar-chart.js | 0 .../components/directive-comparison-table.js | 0 .../app}/components/directive-line-chart.js | 0 .../app}/components/directive-power-bands.js | 0 .../app}/components/directive-slider.js | 0 src/app/i18n/Language.jsx | 72 +++ {app/js => src/app}/i18n/de.js | 0 {app/js => src/app}/i18n/en.js | 0 {app/js => src/app}/i18n/es.js | 0 {app/js => src/app}/i18n/fr.js | 0 {app/js => src/app}/i18n/it.js | 0 {app/js => src/app}/i18n/ru.js | 0 src/app/index.js | 6 + src/app/pages/AboutPage.jsx | 41 ++ src/app/pages/ComparisonPage.jsx | 216 +++++++++ src/app/pages/ErrorPage.jsx | 61 +++ src/app/pages/NotFoundPage.jsx | 16 + src/app/pages/OutfittingPage.jsx | 132 ++++++ src/app/pages/Page.jsx | 36 ++ src/app/pages/ShipyardPage.jsx | 235 ++++++++++ .../app}/pages/controller-comparison.js | 0 .../js => src/app}/pages/controller-outfit.js | 0 {app/js => src/app}/shipyard/Calculations.js | 0 {app/js => src/app}/shipyard/Constants.js | 3 +- {app/js => src/app}/shipyard/ModuleSet.js | 10 +- {app/js => src/app}/shipyard/ModuleUtils.js | 47 +- src/app/shipyard/Modules.js | 96 ++++ {app/js => src/app/shipyard}/Serializer.js | 14 +- {app/js => src/app}/shipyard/Ship.js | 99 ++-- src/app/shipyard/Ships.js | 59 +++ {app/js => src/app}/stores/Persist.js | 71 ++- {app/js => src/app}/utils/BBCode.js | 0 src/app/utils/InterfaceEvents.js | 39 ++ src/app/utils/ShortenUrl.js | 19 + src/app/utils/shallowEqual.js | 27 ++ {app => src}/fonts/eurostile.eot | Bin {app => src}/fonts/eurostile.svg | 0 {app => src}/fonts/eurostile.ttf | Bin {app => src}/fonts/eurostile.woff | Bin {app => src}/fonts/eurostile.woff2 | Bin .../fonts/orbitron-regular-webfont.eot | Bin .../fonts/orbitron-regular-webfont.svg | 0 .../fonts/orbitron-regular-webfont.ttf | Bin .../fonts/orbitron-regular-webfont.woff | Bin .../fonts/orbitron-regular-webfont.woff2 | Bin {app => src}/images/logo/144x144.png | Bin {app => src}/images/logo/192x192.png | Bin {app => src}/images/logo/72x72.png | Bin {app => src}/images/logo/96x96.png | Bin .../logo/apple-touch-icon-precomposed.png | Bin {app => src}/images/logo/apple-touch-icon.png | Bin {app => src}/images/logo/browserconfig.xml | 0 {app => src}/images/logo/favicon.ico | Bin {app => src}/images/logo/manifest.json | 0 {app => src}/images/logo/mstile-144x144.png | Bin {app => src}/images/logo/mstile-150x150.png | Bin {app => src}/images/logo/mstile-310x150.png | Bin {app => src}/images/logo/mstile-310x310.png | Bin {app => src}/images/logo/mstile-70x70.png | Bin {app => src}/images/splash/1024x748.png | Bin {app => src}/images/splash/1136x640.png | Bin {app => src}/images/splash/1242x2148.png | Bin {app => src}/images/splash/1280x720.png | Bin {app => src}/images/splash/1334x750.png | Bin {app => src}/images/splash/1536x2008.png | Bin {app => src}/images/splash/200x320.png | Bin {app => src}/images/splash/2048x1496.png | Bin {app => src}/images/splash/2208x1242.png | Bin {app => src}/images/splash/320x200.png | Bin {app => src}/images/splash/320x460.png | Bin {app => src}/images/splash/320x480.png | Bin {app => src}/images/splash/480x320.png | Bin {app => src}/images/splash/480x800.png | Bin {app => src}/images/splash/640x1096.png | Bin {app => src}/images/splash/640x920.png | Bin {app => src}/images/splash/720x1280.png | Bin {app => src}/images/splash/750x1294.png | Bin {app => src}/images/splash/768x1004.png | Bin {app => src}/images/splash/800x480.png | Bin {app => src}/images/splash/960x640.png | Bin {app => src}/index.html | 15 +- {app => src}/less/app.less | 16 +- {app => src}/less/buttons.less | 0 {app => src}/less/chart-tooltip.less | 0 {app => src}/less/charts.less | 0 {app => src}/less/colors.less | 0 {app => src}/less/comparison.less | 0 {app => src}/less/error.less | 0 src/less/fonts.less | 26 ++ {app => src}/less/header.less | 1 + {app => src}/less/icons.less | 0 {app => src}/less/list.less | 0 {app => src}/less/loader.less | 2 +- {app => src}/less/modal.less | 3 - {app => src}/less/outfit.less | 0 {app => src}/less/responsive.less | 0 {app => src}/less/select.less | 0 {app => src}/less/shipyard.less | 0 {app => src}/less/slider.less | 0 {app => src}/less/slot.less | 0 {app => src}/less/sortable.less | 1 + {app => src}/less/table.less | 0 {app => src}/less/utilities.less | 0 {app => src}/schemas/ship-loadout/1.json | 0 {app => src}/schemas/ship-loadout/2.json | 0 {app => src}/views/page-outfit.html | 91 ---- webpack.config.dev.js | 41 ++ webpack.config.prod.js | 57 +++ 201 files changed, 3594 insertions(+), 2329 deletions(-) create mode 100644 .babelrc create mode 100644 .eslintrc delete mode 100755 app/icons/bin.svg delete mode 100755 app/icons/cogs.svg delete mode 100755 app/icons/coriolis.svg delete mode 100755 app/icons/download.svg delete mode 100644 app/icons/eddb.svg delete mode 100755 app/icons/embed.svg delete mode 100644 app/icons/equalizer.svg delete mode 100755 app/icons/floppy-disk.svg delete mode 100755 app/icons/fuel.svg delete mode 100755 app/icons/github-mark.svg delete mode 100755 app/icons/hammer.svg delete mode 100755 app/icons/infinite.svg delete mode 100755 app/icons/info.svg delete mode 100755 app/icons/link.svg delete mode 100755 app/icons/mount-F.svg delete mode 100755 app/icons/mount-G.svg delete mode 100755 app/icons/mount-T.svg delete mode 100644 app/icons/no-power.svg delete mode 100755 app/icons/notification.svg delete mode 100644 app/icons/power.svg delete mode 100755 app/icons/question.svg delete mode 100755 app/icons/rocket.svg delete mode 100755 app/icons/spinner11.svg delete mode 100644 app/icons/station-coriolis.svg delete mode 100644 app/icons/station-ocellus.svg delete mode 100644 app/icons/station-orbis.svg delete mode 100644 app/icons/station-outpost.svg delete mode 100755 app/icons/stats-bars.svg delete mode 100755 app/icons/switch.svg delete mode 100755 app/icons/upload.svg delete mode 100755 app/icons/warning.svg delete mode 100644 app/js/Coriolis.jsx delete mode 100644 app/js/components/Header.jsx delete mode 100755 app/js/components/directive-component-select.js delete mode 100755 app/js/components/directive-header.js delete mode 100644 app/js/components/directive-loader.js delete mode 100755 app/js/components/directive-slot-hardpoint.js delete mode 100755 app/js/components/directive-slot-internal.js delete mode 100644 app/js/i18n/Language.js delete mode 100755 app/js/old-app.js delete mode 100644 app/js/pages/NotFoundPage.jsx delete mode 100755 app/js/pages/ShipyardPage.jsx delete mode 100755 app/js/pages/controller-delete.js delete mode 100755 app/js/pages/controller-error.js delete mode 100755 app/js/pages/controller-export.js delete mode 100755 app/js/pages/controller-import.js delete mode 100755 app/js/pages/controller-link.js delete mode 100755 app/js/pages/controller-modal.js delete mode 100644 app/js/utils/ShortenUrl.js delete mode 100755 app/less/background-images.less delete mode 100755 app/less/fonts.less delete mode 100755 app/views/_modal.html delete mode 100755 app/views/_slot-hardpoint.html delete mode 100755 app/views/_slot-internal.html delete mode 100755 app/views/modal-about.html delete mode 100755 app/views/modal-delete.html delete mode 100755 app/views/modal-export.html delete mode 100755 app/views/modal-import.html delete mode 100755 app/views/modal-link.html delete mode 100755 app/views/page-comparison.html delete mode 100755 app/views/page-error.html create mode 100644 devServer.js delete mode 100755 gulpfile.js delete mode 100755 nginx.conf delete mode 100755 scripts/json-to-db.js create mode 100644 src/app/Coriolis.jsx rename {app/js => src/app}/Router.js (93%) create mode 100644 src/app/components/ActiveLink.jsx create mode 100644 src/app/components/AvailableModulesMenu.jsx create mode 100644 src/app/components/HardpointSlot.jsx create mode 100644 src/app/components/HardpointsSlotSection.jsx create mode 100644 src/app/components/Header.jsx create mode 100644 src/app/components/InternalSlot.jsx create mode 100644 src/app/components/InternalSlotSection.jsx create mode 100644 src/app/components/Link.jsx create mode 100644 src/app/components/ModalDeleteAll.jsx create mode 100644 src/app/components/ModalExport.jsx create mode 100644 src/app/components/ModalImport.jsx create mode 100644 src/app/components/ModalPermalink.jsx create mode 100644 src/app/components/ShipSummaryTable.jsx create mode 100644 src/app/components/Slot.jsx create mode 100644 src/app/components/SlotSection.jsx create mode 100644 src/app/components/StandardSlot.jsx create mode 100644 src/app/components/StandardSlotSection.jsx create mode 100644 src/app/components/SvgIcons.jsx create mode 100644 src/app/components/TranslatedComponent.jsx create mode 100644 src/app/components/UtilitySlotSection.jsx rename {app/js => src/app}/components/directive-area-chart.js (100%) rename {app/js => src/app}/components/directive-bar-chart.js (100%) rename {app/js => src/app}/components/directive-comparison-table.js (100%) rename {app/js => src/app}/components/directive-line-chart.js (100%) rename {app/js => src/app}/components/directive-power-bands.js (100%) rename {app/js => src/app}/components/directive-slider.js (100%) create mode 100644 src/app/i18n/Language.jsx rename {app/js => src/app}/i18n/de.js (100%) rename {app/js => src/app}/i18n/en.js (100%) rename {app/js => src/app}/i18n/es.js (100%) rename {app/js => src/app}/i18n/fr.js (100%) rename {app/js => src/app}/i18n/it.js (100%) rename {app/js => src/app}/i18n/ru.js (100%) create mode 100644 src/app/index.js create mode 100644 src/app/pages/AboutPage.jsx create mode 100644 src/app/pages/ComparisonPage.jsx create mode 100644 src/app/pages/ErrorPage.jsx create mode 100644 src/app/pages/NotFoundPage.jsx create mode 100644 src/app/pages/OutfittingPage.jsx create mode 100644 src/app/pages/Page.jsx create mode 100755 src/app/pages/ShipyardPage.jsx rename {app/js => src/app}/pages/controller-comparison.js (100%) rename {app/js => src/app}/pages/controller-outfit.js (100%) rename {app/js => src/app}/shipyard/Calculations.js (100%) rename {app/js => src/app}/shipyard/Constants.js (98%) rename {app/js => src/app}/shipyard/ModuleSet.js (95%) rename {app/js => src/app}/shipyard/ModuleUtils.js (88%) create mode 100644 src/app/shipyard/Modules.js rename {app/js => src/app/shipyard}/Serializer.js (95%) mode change 100755 => 100644 rename {app/js => src/app}/shipyard/Ship.js (85%) create mode 100644 src/app/shipyard/Ships.js rename {app/js => src/app}/stores/Persist.js (81%) rename {app/js => src/app}/utils/BBCode.js (100%) create mode 100644 src/app/utils/InterfaceEvents.js create mode 100644 src/app/utils/ShortenUrl.js create mode 100644 src/app/utils/shallowEqual.js rename {app => src}/fonts/eurostile.eot (100%) rename {app => src}/fonts/eurostile.svg (100%) rename {app => src}/fonts/eurostile.ttf (100%) rename {app => src}/fonts/eurostile.woff (100%) rename {app => src}/fonts/eurostile.woff2 (100%) rename {app => src}/fonts/orbitron-regular-webfont.eot (100%) rename {app => src}/fonts/orbitron-regular-webfont.svg (100%) rename {app => src}/fonts/orbitron-regular-webfont.ttf (100%) rename {app => src}/fonts/orbitron-regular-webfont.woff (100%) rename {app => src}/fonts/orbitron-regular-webfont.woff2 (100%) rename {app => src}/images/logo/144x144.png (100%) rename {app => src}/images/logo/192x192.png (100%) rename {app => src}/images/logo/72x72.png (100%) rename {app => src}/images/logo/96x96.png (100%) rename {app => src}/images/logo/apple-touch-icon-precomposed.png (100%) rename {app => src}/images/logo/apple-touch-icon.png (100%) rename {app => src}/images/logo/browserconfig.xml (100%) rename {app => src}/images/logo/favicon.ico (100%) rename {app => src}/images/logo/manifest.json (100%) rename {app => src}/images/logo/mstile-144x144.png (100%) rename {app => src}/images/logo/mstile-150x150.png (100%) rename {app => src}/images/logo/mstile-310x150.png (100%) rename {app => src}/images/logo/mstile-310x310.png (100%) rename {app => src}/images/logo/mstile-70x70.png (100%) rename {app => src}/images/splash/1024x748.png (100%) rename {app => src}/images/splash/1136x640.png (100%) rename {app => src}/images/splash/1242x2148.png (100%) rename {app => src}/images/splash/1280x720.png (100%) rename {app => src}/images/splash/1334x750.png (100%) rename {app => src}/images/splash/1536x2008.png (100%) rename {app => src}/images/splash/200x320.png (100%) rename {app => src}/images/splash/2048x1496.png (100%) rename {app => src}/images/splash/2208x1242.png (100%) rename {app => src}/images/splash/320x200.png (100%) rename {app => src}/images/splash/320x460.png (100%) rename {app => src}/images/splash/320x480.png (100%) rename {app => src}/images/splash/480x320.png (100%) rename {app => src}/images/splash/480x800.png (100%) rename {app => src}/images/splash/640x1096.png (100%) rename {app => src}/images/splash/640x920.png (100%) rename {app => src}/images/splash/720x1280.png (100%) rename {app => src}/images/splash/750x1294.png (100%) rename {app => src}/images/splash/768x1004.png (100%) rename {app => src}/images/splash/800x480.png (100%) rename {app => src}/images/splash/960x640.png (100%) rename {app => src}/index.html (93%) rename {app => src}/less/app.less (92%) rename {app => src}/less/buttons.less (100%) rename {app => src}/less/chart-tooltip.less (100%) rename {app => src}/less/charts.less (100%) rename {app => src}/less/colors.less (100%) rename {app => src}/less/comparison.less (100%) rename {app => src}/less/error.less (100%) create mode 100755 src/less/fonts.less rename {app => src}/less/header.less (99%) rename {app => src}/less/icons.less (100%) rename {app => src}/less/list.less (100%) rename {app => src}/less/loader.less (96%) rename {app => src}/less/modal.less (96%) rename {app => src}/less/outfit.less (100%) rename {app => src}/less/responsive.less (100%) rename {app => src}/less/select.less (100%) rename {app => src}/less/shipyard.less (100%) rename {app => src}/less/slider.less (100%) rename {app => src}/less/slot.less (100%) rename {app => src}/less/sortable.less (95%) rename {app => src}/less/table.less (100%) rename {app => src}/less/utilities.less (100%) rename {app => src}/schemas/ship-loadout/1.json (100%) rename {app => src}/schemas/ship-loadout/2.json (100%) rename {app => src}/views/page-outfit.html (82%) create mode 100644 webpack.config.dev.js create mode 100644 webpack.config.prod.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..a3a2c1d4 --- /dev/null +++ b/.babelrc @@ -0,0 +1,20 @@ +{ + "stage": 0, + "env": { + "development": { + "plugins": ["react-transform"], + "extra": { + "react-transform": { + "transforms": [{ + "transform": "react-transform-hmr", + "imports": ["react"], + "locals": ["module"] + }, { + "transform": "react-transform-catch-errors", + "imports": ["react", "redbox-react"] + }] + } + } + } + } +} diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..329eec0b --- /dev/null +++ b/.eslintrc @@ -0,0 +1,21 @@ +{ + "ecmaFeatures": { + "jsx": true, + "modules": true + }, + "env": { + "browser": true, + "node": true + }, + "parser": "babel-eslint", + "rules": { + "quotes": [2, "single"], + "strict": [2, "never"], + "react/jsx-uses-react": 2, + "react/jsx-uses-vars": 2, + "react/react-in-jsx-scope": 2 + }, + "plugins": [ + "react" + ] +} diff --git a/.gitignore b/.gitignore index a06816f3..e5c4dc84 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,5 @@ node_modules -bower_components -bower_components/* build .DS_Store *.log -app/js/db.js -app/db.json nginx.pid -template_cache.js \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 52561dc0..e573baac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,12 +7,10 @@ node_js: cache: directories: - node_modules - - bower_components before_script: - npm install -g gulp - npm install -g bower - - bower install script: - gulp lint - gulp build-prod diff --git a/app/icons/bin.svg b/app/icons/bin.svg deleted file mode 100755 index 4b05b645..00000000 --- a/app/icons/bin.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/app/icons/cogs.svg b/app/icons/cogs.svg deleted file mode 100755 index f065a951..00000000 --- a/app/icons/cogs.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/icons/coriolis.svg b/app/icons/coriolis.svg deleted file mode 100755 index 471b720d..00000000 --- a/app/icons/coriolis.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/icons/download.svg b/app/icons/download.svg deleted file mode 100755 index 10bc0a02..00000000 --- a/app/icons/download.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/icons/eddb.svg b/app/icons/eddb.svg deleted file mode 100644 index 81baeb6a..00000000 --- a/app/icons/eddb.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - diff --git a/app/icons/embed.svg b/app/icons/embed.svg deleted file mode 100755 index 35df51e9..00000000 --- a/app/icons/embed.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/app/icons/equalizer.svg b/app/icons/equalizer.svg deleted file mode 100644 index b1d72f2b..00000000 --- a/app/icons/equalizer.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/app/icons/floppy-disk.svg b/app/icons/floppy-disk.svg deleted file mode 100755 index dabe06b3..00000000 --- a/app/icons/floppy-disk.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/icons/fuel.svg b/app/icons/fuel.svg deleted file mode 100755 index 69ff2074..00000000 --- a/app/icons/fuel.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/icons/github-mark.svg b/app/icons/github-mark.svg deleted file mode 100755 index 4aca53bf..00000000 --- a/app/icons/github-mark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/icons/hammer.svg b/app/icons/hammer.svg deleted file mode 100755 index 3bac473c..00000000 --- a/app/icons/hammer.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/icons/infinite.svg b/app/icons/infinite.svg deleted file mode 100755 index 7eb3a117..00000000 --- a/app/icons/infinite.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/icons/info.svg b/app/icons/info.svg deleted file mode 100755 index 7571aede..00000000 --- a/app/icons/info.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/app/icons/link.svg b/app/icons/link.svg deleted file mode 100755 index 8b24b301..00000000 --- a/app/icons/link.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/app/icons/mount-F.svg b/app/icons/mount-F.svg deleted file mode 100755 index 7e5fd98d..00000000 --- a/app/icons/mount-F.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/icons/mount-G.svg b/app/icons/mount-G.svg deleted file mode 100755 index f57ffd33..00000000 --- a/app/icons/mount-G.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/icons/mount-T.svg b/app/icons/mount-T.svg deleted file mode 100755 index bb301efe..00000000 --- a/app/icons/mount-T.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/icons/no-power.svg b/app/icons/no-power.svg deleted file mode 100644 index 00f7a9b5..00000000 --- a/app/icons/no-power.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/icons/notification.svg b/app/icons/notification.svg deleted file mode 100755 index 2c9c22fd..00000000 --- a/app/icons/notification.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/icons/power.svg b/app/icons/power.svg deleted file mode 100644 index 08d19243..00000000 --- a/app/icons/power.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/icons/question.svg b/app/icons/question.svg deleted file mode 100755 index 4eec41ab..00000000 --- a/app/icons/question.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/icons/rocket.svg b/app/icons/rocket.svg deleted file mode 100755 index af2684f1..00000000 --- a/app/icons/rocket.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/icons/spinner11.svg b/app/icons/spinner11.svg deleted file mode 100755 index b6588c71..00000000 --- a/app/icons/spinner11.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/icons/station-coriolis.svg b/app/icons/station-coriolis.svg deleted file mode 100644 index 7287b5a2..00000000 --- a/app/icons/station-coriolis.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/icons/station-ocellus.svg b/app/icons/station-ocellus.svg deleted file mode 100644 index 9e037fee..00000000 --- a/app/icons/station-ocellus.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/icons/station-orbis.svg b/app/icons/station-orbis.svg deleted file mode 100644 index 1acf2a16..00000000 --- a/app/icons/station-orbis.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/icons/station-outpost.svg b/app/icons/station-outpost.svg deleted file mode 100644 index bd9309f3..00000000 --- a/app/icons/station-outpost.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/icons/stats-bars.svg b/app/icons/stats-bars.svg deleted file mode 100755 index 5ff267a5..00000000 --- a/app/icons/stats-bars.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/icons/switch.svg b/app/icons/switch.svg deleted file mode 100755 index a3949c0d..00000000 --- a/app/icons/switch.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/app/icons/upload.svg b/app/icons/upload.svg deleted file mode 100755 index 45e3ae6c..00000000 --- a/app/icons/upload.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/icons/warning.svg b/app/icons/warning.svg deleted file mode 100755 index d7e2c9f8..00000000 --- a/app/icons/warning.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/app/js/Coriolis.jsx b/app/js/Coriolis.jsx deleted file mode 100644 index fe00af05..00000000 --- a/app/js/Coriolis.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import { Component } from 'react'; -import Router from 'Router'; -import ShipyardPage from 'pages/ShipyardPage'; -import NotFoundPage from 'pages/NotFoundPage'; -import Header from '../components/Header'; - -class Coriolis extends Component { - - constructor(props) { - super(props); - this.setPage = this.setPage.bind(this); - this.state.standAlone = isStandAlone(); - window.onerror = errorPage.bind(this); - - Router('/', () => this.setPage()); - // Router('/outfitting/:ship', outfitting); - // Router('/outfitting/:ship/:code', outfitting); - // Router('/compare/:name', compare); - // Router('/comparison/:code', comparison); - // Router('/settings', settings); - Router('*', () => this.setPage(null)); - - if (window.applicationCache) { - // Listen for appcache updated event, present refresh to update view - window.applicationCache.addEventListener('updateready', () => { - if (window.applicationCache.status == window.applicationCache.UPDATEREADY) { - // Browser downloaded a new app cache. - this.setState({appCacheUpdate: true}); - } - }, false); - } - - Router.start(); - console.log('Root page created'); - } - - setPage(page) { - this.setState({ page: page }); - } - - onError(msg, scriptUrl, line, col, errObj) { - this.setPage(
Some errors occured!!
); - } - - render() { - return ( -
- {/*
*/} - {this.state.page || } -
- ); - } -} - -ReactDOM.render(, document.getElementById('coriolis')); diff --git a/app/js/components/Header.jsx b/app/js/components/Header.jsx deleted file mode 100644 index 6f3fcda2..00000000 --- a/app/js/components/Header.jsx +++ /dev/null @@ -1,118 +0,0 @@ -import { Component } from 'react'; - -export default class Header extends Component { - - - render() { - let openedMenu = this.state.openedMenu; - if (this.props.appCacheUpdate) { - return
{ 'PHRASE_UPDATE_RDY' | translate }
; - } - - return ( -
- - - - - - - - - -
- ); - } - - getShipsMenu() { - return (); - } - - getBuildsMenu() { - return (); - } - - getComparisonsMenu() { - return (); - } - - getSettingsMenu() { - return (); - } - -} \ No newline at end of file diff --git a/app/js/components/directive-component-select.js b/app/js/components/directive-component-select.js deleted file mode 100755 index dba65e9e..00000000 --- a/app/js/components/directive-component-select.js +++ /dev/null @@ -1,90 +0,0 @@ -angular.module('app').directive('componentSelect', ['$translate', function($translate) { - - // Generting the HTML in this manner is MUCH faster than using an angular template. - - function appendGroup(list, opts, cid, mass, checkWarning) { - var prevClass = null, prevRating = null; - for (var i = 0; i < opts.length; i++) { - var o = opts[i]; - var id = o.id || (o.class + o.rating); // Standard components' ID is their class and rating - - if (i > 0 && opts.length > 3 && o.class != prevClass && (o.rating != prevRating || o.mode) && o.grp != 'pa') { - list.push('
'); - } - - list.push('
  • '); - - if (o.mode) { - list.push(' '); - } - - list.push('', o.class, o.rating); - - if (o.missile) { - list.push('/' + o.missile); - } - - - if (o.name) { - list.push(' ' + $translate.instant(o.name)); - } - - list.push('
  • '); - prevClass = o.class; - prevRating = o.rating; - } - } - - return { - restrict: 'A', - scope: { - opts: '=', // Component Options object - groups: '=', // Groups of Component Options - mass: '=', // Current ship mass - s: '=', // Current Slot - warning: '=' // Check warning function - }, - link: function(scope, element) { - var list = []; - var cid = scope.s.id; // Slot's current component id - var component = scope.s.c; // Slot's Current Component (may be null/undefined) - var opts = scope.opts; - var groups = scope.groups; - var mass = (scope.mass ? scope.mass : 0) - (component && component.mass ? component.mass : 0); // Mass minus the currently selected component - - if (groups) { - // At present time slots with grouped options (Hardpoints and Internal) can be empty - list.push('
    ', $translate.instant('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('
    ', $translate.instant(g), '
      '); - appendGroup(list, grp, cid, mass, scope.warning); - list.push('
    '); - } - } else { - list.push('
      '); - appendGroup(list, opts, cid, mass, scope.warning); - list.push('
    '); - } - - element.html(list.join('')); - // If groups are present and a component is already selectd - if (groups && component && component.grp) { - var groupElement = angular.element(document.getElementById(component.grp)); - var parentElem = element[0].parentElement; - parentElem.scrollTop = groupElement[0].offsetTop; // Scroll to currently selected group - } - } - }; -}]); diff --git a/app/js/components/directive-header.js b/app/js/components/directive-header.js deleted file mode 100755 index eab4601b..00000000 --- a/app/js/components/directive-header.js +++ /dev/null @@ -1,114 +0,0 @@ -angular.module('app').directive('shipyardHeader', ['lodash', '$window', '$rootScope', '$state', 'Persist', 'Serializer', 'ShipsDB', function(_, $window, $rootScope, $state, Persist, Serializer, ships) { - - - return { - restrict: 'E', - templateUrl: 'views/_header.html', - scope: true, - link: function(scope) { - scope.openedMenu = null; - scope.ships = ships; - scope.allBuilds = Persist.builds; - scope.buildsList = Object.keys(scope.allBuilds).sort(); - scope.allComparisons = Object.keys(Persist.comparisons).sort(); - scope.bs = Persist.state; - - var win = angular.element($window); // Angularized window object for event triggering - var insIndex = _.findIndex($rootScope.insurance.opts, 'name', Persist.getInsurance()); - var savedDiscounts = Persist.getDiscount() || [1, 1]; - $rootScope.insurance.current = $rootScope.insurance.opts[insIndex != -1 ? insIndex : 0]; - $rootScope.discounts.ship = savedDiscounts[0]; - $rootScope.discounts.components = savedDiscounts[1]; - - /** - * Save selected insurance option - */ - scope.updateInsurance = function() { - Persist.setInsurance($rootScope.insurance.current.name); - }; - - /** - * Save selected discount option - */ - scope.updateDiscount = function() { - Persist.setDiscount([$rootScope.discounts.ship, $rootScope.discounts.components]); - $rootScope.$broadcast('discountChange'); - }; - - scope.backup = function(e) { - e.preventDefault(); - e.stopPropagation(); - scope.openedMenu = null; - $state.go('modal.export', { - title: 'backup', - data: Persist.getAll(), - description: 'PHRASE_BACKUP_DESC' - }); - }; - - scope.detailedExport = function(e) { - e.preventDefault(); - e.stopPropagation(); - scope.openedMenu = null; - $state.go('modal.export', { - title: 'detailed export', - data: Serializer.toDetailedExport(scope.allBuilds), - description: 'PHRASE_EXPORT_DESC' - }); - }; - - scope.cleanedBuildList = function(shipId) { - return Object.keys(scope.allBuilds[shipId]); - }; - - scope.openMenu = function(e, menu) { - e.stopPropagation(); - if (menu == scope.openedMenu) { - scope.openedMenu = null; - return; - } - - if ((menu == 'comp' || menu == 'b') && !scope.bs.hasBuilds) { - scope.openedMenu = null; - return; - } - - if (menu == 'comp') { - scope.allComparisons = Object.keys(Persist.comparisons).sort(); - } - - scope.openedMenu = menu; - }; - - // 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; - }); - - scope.textSizeChange = function(size) { - if (size != $rootScope.sizeRatio) { - $rootScope.sizeRatio = size; - document.getElementById('main').style.fontSize = size + 'em'; - Persist.setSizeRatio(size); - win.triggerHandler('resize'); - } - }; - - scope.resetTextSize = function() { - if ($rootScope.sizeRatio != 1) { - scope.textSizeChange(1); - scope.$broadcast('reset', 1); - } - }; - - scope.$watchCollection('allBuilds', function() { - scope.buildsList = Object.keys(scope.allBuilds).sort(); - }); - } - }; -}]); diff --git a/app/js/components/directive-loader.js b/app/js/components/directive-loader.js deleted file mode 100644 index b7743337..00000000 --- a/app/js/components/directive-loader.js +++ /dev/null @@ -1,9 +0,0 @@ -angular.module('app').directive('loader', function() { - return { - restrict: 'A', - link: function(scope, element) { - element.addClass('loader'); - element.html(''); - } - }; -}); diff --git a/app/js/components/directive-slot-hardpoint.js b/app/js/components/directive-slot-hardpoint.js deleted file mode 100755 index dbfa50b8..00000000 --- a/app/js/components/directive-slot-hardpoint.js +++ /dev/null @@ -1,13 +0,0 @@ -angular.module('app').directive('slotHardpoint', ['$rootScope', function($r) { - return { - restrict: 'A', - scope: { - hp: '=', - size: '=' - }, - templateUrl: 'views/_slot-hardpoint.html', - link: function(scope) { - scope.$r = $r; - } - }; -}]); diff --git a/app/js/components/directive-slot-internal.js b/app/js/components/directive-slot-internal.js deleted file mode 100755 index afc148d6..00000000 --- a/app/js/components/directive-slot-internal.js +++ /dev/null @@ -1,13 +0,0 @@ -angular.module('app').directive('slotInternal', ['$rootScope', function($r) { - return { - restrict: 'A', - scope: { - c: '=slot', - fuel: '=' - }, - templateUrl: 'views/_slot-internal.html', - link: function(scope) { - scope.$r = $r; - } - }; -}]); diff --git a/app/js/i18n/Language.js b/app/js/i18n/Language.js deleted file mode 100644 index 8966bb67..00000000 --- a/app/js/i18n/Language.js +++ /dev/null @@ -1,52 +0,0 @@ -import EN from 'en'; -import DE from 'de'; -import ES from 'es'; -import FR from 'fr'; -import IT from 'it'; -import RU from 'RU'; -import d3 from 'd3'; - -let fallbackTerms = EN.terms; -let currentLanguage; -let currentTerms; -let format = { - rPct: d3.format('%') -}; - -export format; - -export function setLanguage(langCode) { - let lang; - - switch (langCode) { - case 'de': lang = DE; break; - case 'es': lang = ES; break; - case 'fr': lang = FR; break; - case 'it': lang = IT; break; - case 'ru': lang = RU; break; - default: lang = EN; - } - - currentTerms = lang.terms; - d3Locale = d3.locale(lang.formats); - - format.gen = d3Locale.numberFormat('n'); - format.crd = d3Locale.numberFormat(',.0f'); - format.pwr = d3Locale.numberFormat(',.2f'); - format.round = (d) => format.gen(d3.round(d, 2)); - format.pct = d3Locale.numberFormat('.2%'); - format.pct1 = d3Locale.numberFormat('.1%'); -} - -export const Languages = { - en: 'English', - de: 'Deutsh', - it: 'Italiano', - es: 'Español', - fr: 'Français', - ru: 'ру́сский' -}; - -export function term(t) { - return currentTerms[t] || fallbackTerms[t]; -} diff --git a/app/js/old-app.js b/app/js/old-app.js deleted file mode 100755 index 7b9441b5..00000000 --- a/app/js/old-app.js +++ /dev/null @@ -1,114 +0,0 @@ -angular.module('app', ['ui.router', 'ct.ui.router.extras.sticky', 'ui.sortable', 'shipyard', 'ngLodash', 'app.templates', 'pascalprecht.translate']) -.run(['$rootScope', '$location', '$window', '$document', '$state', '$translate', 'localeFormat', 'Persist', 'Discounts', 'Languages', 'SizeMap', -function($rootScope, $location, $window, $doc, $state, $translate, localeFormat, Persist, Discounts, Languages, SizeMap) { - // App is running as a standalone web app on tablet/mobile - var isStandAlone; - // This was causing issues on Windows phones ($window.external was causing Angular js to throw an exception). Backup is to try this and set isStandAlone to false if this fails. - try { - isStandAlone = $window.navigator.standalone || ($window.external && $window.external.msIsSiteMode && $window.external.msIsSiteMode()); - } catch (ex) { - isStandAlone = false; - } - - // Redirect any state transition errors to the error controller/state - $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 - }); - - // Track on Google analytics if available - $rootScope.$on('$stateChangeSuccess', function(e, to, toParams, from, fromParams) { - $rootScope.prevState = { name: from.name, params: fromParams }; - - if (to.url) { // Only track states that have a URL - if ($window.ga) { - ga('send', 'pageview', { page: $location.path() }); - } - - if (isStandAlone) { - // Persist the current state - Persist.setState({ name: to.name, params: toParams }); - } - } - }); - - $rootScope.language = { - opts: Languages, - current: Languages[Persist.getLangCode()] ? Persist.getLangCode() : 'en' - }; - $rootScope.localeFormat = d3.locale(localeFormat.get($rootScope.language.current)); - updateNumberFormat(); - - // Global Reference variables - $rootScope.insurance = { opts: [{ name: 'standard', pct: 0.05 }, { name: 'alpha', pct: 0.025 }, { name: 'beta', pct: 0.0375 }] }; - $rootScope.discounts = { opts: Discounts }; - $rootScope.sizeRatio = Persist.getSizeRatio(); - $rootScope.SZM = SizeMap; - $rootScope.title = 'Coriolis'; - - $rootScope.changeLanguage = function() { - $translate.use($rootScope.language.current); - $rootScope.localeFormat = d3.locale(localeFormat.get($rootScope.language.current)); - updateNumberFormat(); - $rootScope.$broadcast('languageChanged', $rootScope.language.current); - }; - - // Formatters - $rootScope.fRPct = d3.format('%'); - $rootScope.fTime = function(d) { return Math.floor(d / 60) + ':' + ('00' + Math.floor(d % 60)).substr(-2, 2); }; - - function updateNumberFormat() { - var locale = $rootScope.localeFormat; - var fGen = $rootScope.fGen = locale.numberFormat('n'); - $rootScope.fCrd = locale.numberFormat(',.0f'); - $rootScope.fPwr = locale.numberFormat(',.2f'); - $rootScope.fRound = function(d) { return fGen(d3.round(d, 2)); }; - $rootScope.fPct = locale.numberFormat('.2%'); - $rootScope.f1Pct = locale.numberFormat('.1%'); - } - - /** - * Returns the name of the component mounted in the specified slot - * @param {Object} slot The slot object - * @return {String} The component name - */ - $rootScope.cName = function(slot) { - return $translate.instant(slot.c ? slot.c.name ? slot.c.name : slot.c.grp : null); - }; - - // Global Event Listeners - $doc.bind('keyup', function(e) { - if (e.keyCode == 27) { // Escape Key - $rootScope.$broadcast('close', e); - $rootScope.$apply(); - } else { - $rootScope.$broadcast('keyup', e); - } - }); - - $rootScope.bgClicked = function(e) { - $rootScope.$broadcast('close', e); - }; - - if ($window.applicationCache) { - // Listen for appcache updated event, present refresh to update view - $window.applicationCache.addEventListener('updateready', function() { - if ($window.applicationCache.status == $window.applicationCache.UPDATEREADY) { - // Browser downloaded a new app cache. - $rootScope.appCacheUpdate = true; - $rootScope.$apply(); - } - }, false); - } - - 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' }); - } else { - $state.go('shipyard', null, { location: 'replace' }); // Default to home page - } - } - -}]); diff --git a/app/js/pages/NotFoundPage.jsx b/app/js/pages/NotFoundPage.jsx deleted file mode 100644 index 0dabb937..00000000 --- a/app/js/pages/NotFoundPage.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Component } from 'react'; - -export default class ShipyardPage extends Component { - - constructor(props) { - super(props); - } - - render() { - return
    Page {this.props.path} Not Found
    ; - } -} diff --git a/app/js/pages/ShipyardPage.jsx b/app/js/pages/ShipyardPage.jsx deleted file mode 100755 index 5d697aea..00000000 --- a/app/js/pages/ShipyardPage.jsx +++ /dev/null @@ -1,180 +0,0 @@ -import { Component } from 'react'; -import { Ships, Components } from 'coriolis-data'; -import cn from 'classnames'; -import Ship from 'Ship'; - -function countHp(slot) { - this.hp[slot.maxClass]++; - this.hpCount++; -} - -function countInt(slot) { - var crEligible = !slot.eligible || slot.eligible.cr; - this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment - this.intCount++; - this.maxCargo += crEligible ? Components.findInternal('cr', slot.maxClass, 'E').capacity : 0; -} - -function shipSummary(shipId, shipData) { - let summary = { - id: shipId, - hpCount: 0, - intCount: 0, - maxCargo: 0, - hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge - int: [0, 0, 0, 0, 0, 0, 0, 0] // Sizes 1 - 8 - }; - Object.assign(summary, shipData.properties); - let ship = new Ship(shipId, shipData.properties, shipData.slots); - - // Build Ship - ship.buildWith(shipData.defaults); // Populate with stock/default components - ship.hardpoints.forEach(countHp.bind(summary)); // Count Hardpoints by class - ship.internal.forEach(countInt.bind(summary)); // Count Internal Compartments by class - summary.retailCost = ship.totalCost; // Record Stock/Default/retail cost - ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range - summary.maxJumpRange = ship.unladenRange; // Record Jump Range - ship.optimizeMass({ th: ship.standard[1].maxClass + 'A' }); // Optmize mass with Max Thrusters - summary.topSpeed = ship.topSpeed; - summary.topBoost = ship.topBoost; - - return summary; -} - -let shipSummaries = []; - -for (var s in Ships) { - shipSummaries.push(shipSummary(s, Ships[s])); -} - -export default class ShipyardPage extends Component { - - constructor(props) { - super(props); - this.state = { - title: 'Coriolis - Shipyard', - shipPredicate: 'properties.name', - shipDesc = false - }; - } - - shouldComponentUpdate(nextProps, nextState) { - // Only on language change. Context? - return false; - } - - /** - * Sort ships - * @param {object} key Sort predicate - */ - _sortShips(shipPredicate, shipPredicateIndex) { - let shipDesc = this.state.shipPredicate == shipPredicate ? !this.state.shipDesc : this.state.shipDesc; - this.setState({ shipPredicate, shipDesc, shipPredicateIndex }); - }; - - render() { - let sortShips = this._sortShips.bind(this); - let shipPredicate = this.state.shipPredicate; - let shipPredicateIndex = this.state.shipPredicateIndex; - let shipRows = []; - - // Sort shipsOverview - shipSummaries.sort((a, b) => { - let valA = a[shipPredicate], valB = b[shipPredicate]; - - if (shipPredicateIndex != undefined) { - valA = valA[shipPredicateIndex]; - valB = valB[shipPredicateIndex]; - } - - return this.state.shipDesc ? (valA > valB) : (valB > valA); - }); - - for (s of shipSummaries) { - shipRows.push( - - {s.name} - {s.manufacturer} - {SZM[s.class] | translate} - {{fCrd(s.speed)}} m/s - {{fCrd(s.boost)}} m/s - {s.baseArmour} - {{fCrd(s.baseShieldStrength)}} Mj - {{fCrd(s.topSpeed)}} m/s - {{fCrd(s.topBoost)}} m/s - {{fRound(s.maxJumpRange)}} LY - {{fCrd(s.maxCargo)}} T - {s.hp[1]} - {s.hp[2]} - {s.hp[3]} - {s.hp[4]} - {s.hp[0]} - {s.int[0]} - {s.int[1]} - {s.int[2]} - {s.int[3]} - {s.int[4]} - {s.int[5]} - {s.int[6]} - {s.int[7]} - {fCrd(s.hullMass)} T - {s.masslock} - {fCrd(s.retailCost)} CR - - ); - } - - return ( -
    -
    - - - - - - - - - - - - - - - - {/* Base */} - - - - - {/* Max */} - - - - - {/* Hardpoints */} - - - - - - {/* Internal */} - - - - - - - - - - - - {shipRows} - -
    12345678
    -
    -
    - ); - } -} diff --git a/app/js/pages/controller-delete.js b/app/js/pages/controller-delete.js deleted file mode 100755 index 5aec906e..00000000 --- a/app/js/pages/controller-delete.js +++ /dev/null @@ -1,7 +0,0 @@ -angular.module('app').controller('DeleteController', ['$scope', 'Persist', function($scope, Persist) { - $scope.deleteAll = function() { - Persist.deleteAll(); - $scope.$parent.dismiss(); - }; - -}]); diff --git a/app/js/pages/controller-error.js b/app/js/pages/controller-error.js deleted file mode 100755 index b1f19cf1..00000000 --- a/app/js/pages/controller-error.js +++ /dev/null @@ -1,29 +0,0 @@ -angular.module('app') -.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'; - $scope.browser = $window.navigator.userAgent; - - switch ($scope.type) { - case 404: - $scope.msgPre = 'Page'; - $scope.msgHighlight = $scope.path; - $scope.msgPost = 'Not Found'; - break; - case 'no-ship': - $scope.msgPre = 'Ship'; - $scope.msgHighlight = $p.message; - $scope.msgPost = 'does not exist'; - break; - case 'build-fail': - $scope.msgPre = 'Build Failure!'; - $scope.details = $p.details; - break; - default: - $scope.msgPre = 'Uh, Jameson, we have a problem..'; - $scope.errorMessage = $p.message; - $scope.details = $p.details; - } - -}]); diff --git a/app/js/pages/controller-export.js b/app/js/pages/controller-export.js deleted file mode 100755 index d2ade80c..00000000 --- a/app/js/pages/controller-export.js +++ /dev/null @@ -1,19 +0,0 @@ -angular.module('app').controller('ExportController', ['$scope', '$stateParams', function($scope, $stateParams) { - - $scope.title = $stateParams.title || 'Export'; - $scope.description = $stateParams.description; - - if ($stateParams.promise) { - $scope.export = 'Generating...'; - $stateParams.promise.then(function(data) { - $scope.export = (typeof data === 'object') ? angular.toJson(data, true) : data; - }); - } else { - $scope.export = angular.toJson($stateParams.data, true); - } - - $scope.onTextClick = function($event) { - $event.target.select(); - }; - -}]); diff --git a/app/js/pages/controller-import.js b/app/js/pages/controller-import.js deleted file mode 100755 index 10ced797..00000000 --- a/app/js/pages/controller-import.js +++ /dev/null @@ -1,316 +0,0 @@ -angular.module('app').controller('ImportController', ['lodash', '$rootScope', '$scope', '$stateParams', 'ShipsDB', 'Ship', 'Components', 'GroupMap', 'Persist', 'Serializer', function(_, $rootScope, $scope, $stateParams, Ships, Ship, Components, GroupMap, Persist, Serializer) { - $scope.importValid = false; - $scope.importString = null; - $scope.errorMsg = null; - $scope.canEdit = true; - $scope.builds = $stateParams.obj || null; - $scope.ships = Ships; - - var textBuildRegex = new RegExp('^\\[([\\w \\-]+)\\]\n'); - var lineRegex = new RegExp('^([\\dA-Z]{1,2}): (\\d)([A-I])[/]?([FGT])?([SD])? ([\\w\\- ]+)'); - var mountMap = { 'H': 4, 'L': 3, 'M': 2, 'S': 1, 'U': 0 }; - var standardMap = { 'RB': 0, 'TM': 1, 'FH': 2, 'EC': 3, 'PC': 4, 'SS': 5, 'FS': 6 }; - var bhMap = { 'lightweight alloy': 0, 'reinforced alloy': 1, 'military grade composite': 2, 'mirrored surface composite': 3, 'reactive surface composite': 4 }; - - function isEmptySlot(slot) { - return slot.maxClass == this && slot.c === null; - } - - function equalsIgnoreCase(str) { - return str.toLowerCase() == this.toLowerCase(); - } - - function validateBuild(shipId, code, name) { - var shipData = Ships[shipId]; - - if (!shipData) { - throw '"' + shipId + '" is not a valid Ship Id!'; - } - if (typeof name != 'string' || name.length == 0) { - throw shipData.properties.name + ' build "' + name + '" must be a string at least 1 character 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!'; - } - - if (!detailedBuild.name.trim()) { - throw 'Build Name must be a string at least 1 character long!'; - } - - try { - 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) { - for (var compName in importData.comparisons) { - var comparison = importData.comparisons[compName]; - for (var i = 0, l = comparison.builds.length; i < l; i++) { - var build = comparison.builds[i]; - if (!importData.builds[build.shipId] || !importData.builds[build.shipId][build.buildName]) { - throw build.shipId + ' build "' + build.buildName + '" data is missing!'; - } - } - } - $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; - } - - function importTextBuild(buildStr) { - var buildName = textBuildRegex.exec(buildStr)[1].trim(); - var shipName = buildName.toLowerCase(); - var shipId = null; - - for (var sId in Ships) { - if (Ships[sId].properties.name.toLowerCase() == shipName) { - shipId = sId; - break; - } - } - - if (!shipId) { throw 'No such ship found: "' + buildName + '"'; } - - var lines = buildStr.split('\n'); - var ship = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots); - ship.buildWith(null); - - for (var i = 1; i < lines.length; i++) { - var line = lines[i].trim(); - - if (!line) { continue; } - if (line.substring(0, 3) == '---') { break; } - - var parts = lineRegex.exec(line); - - if (!parts) { throw 'Error parsing: "' + line + '"'; } - - var typeSize = parts[1]; - var cl = parts[2]; - var rating = parts[3]; - var mount = parts[4]; - var missile = parts[5]; - var name = parts[6].trim(); - var slot, group; - - if (isNaN(typeSize)) { // Standard or Hardpoint - if (typeSize.length == 1) { // Hardpoint - var slotClass = mountMap[typeSize]; - - if (cl > slotClass) { throw cl + rating + ' ' + name + ' exceeds slot size: "' + line + '"'; } - - slot = _.find(ship.hardpoints, isEmptySlot, slotClass); - - if (!slot) { throw 'No hardpoint slot available for: "' + line + '"'; } - - group = _.find(GroupMap, equalsIgnoreCase, name); - - var hp = Components.findHardpoint(group, cl, rating, group ? null : name, mount, missile); - - if (!hp) { throw 'Unknown component: "' + line + '"'; } - - ship.use(slot, hp.id, hp, true); - - } else if (typeSize == 'BH') { - var bhId = bhMap[name.toLowerCase()]; - - if (bhId === undefined) { throw 'Unknown bulkhead: "' + line + '"'; } - - ship.useBulkhead(bhId, true); - - } else if (standardMap[typeSize] != undefined) { - var standardIndex = standardMap[typeSize]; - - if (ship.standard[standardIndex].maxClass < cl) { throw name + ' exceeds max class for the ' + ship.name; } - - ship.use(ship.standard[standardIndex], cl + rating, Components.standard(standardIndex, cl + rating), true); - - } else { - throw 'Unknown component: "' + line + '"'; - } - } else { - if (cl > typeSize) { throw cl + rating + ' ' + name + ' exceeds slot size: "' + line + '"'; } - - slot = _.find(ship.internal, isEmptySlot, typeSize); - - if (!slot) { throw 'No internal slot available for: "' + line + '"'; } - - group = _.find(GroupMap, equalsIgnoreCase, name); - - var intComp = Components.findInternal(group, cl, rating, group ? null : name); - - if (!intComp) { throw 'Unknown component: "' + line + '"'; } - - ship.use(slot, intComp.id, intComp); - } - } - - var builds = {}; - builds[shipId] = {}; - builds[shipId]['Imported ' + buildName] = Serializer.fromShip(ship); - $scope.builds = builds; - } - - $scope.validateImport = function() { - var importData = null; - var importString = $scope.importString.trim(); - $scope.importValid = false; - $scope.errorMsg = null; - $scope.builds = $scope.discounts = $scope.comparisons = $scope.insurance = null; - - if (!importString) { return; } - - - try { - if (textBuildRegex.test(importString)) { // E:D Shipyard build text - importTextBuild(importString); - } else { // JSON Build data - importData = angular.fromJson($scope.importString); - - if (!importData || typeof importData != 'object') { - throw 'Must be an object or array!'; - } - - if (importData instanceof Array) { // Must be detailed export json - importDetailedArray(importData); - } else if (importData.ship && typeof importData.name !== undefined) { // Using JSON from a single ship build export - importDetailedArray([importData]); // Convert to array with singleobject - } else { // Using Backup JSON - importBackup(importData); - } - } - } catch (e) { - $scope.errorMsg = (typeof e == 'string') ? e : 'Cannot Parse the data!'; - return; - } - - $scope.importValid = true; - }; - - $scope.hasBuild = function(shipId, name) { - return Persist.getBuild(shipId, name) !== null; - }; - - $scope.hasComparison = function(name) { - return Persist.getComparison(name) !== null; - }; - - $scope.process = function() { - 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() { - - 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(); - }; - - /* Initialization */ - - if ($scope.builds) { // If import is passed an build object - $scope.canEdit = false; - $scope.process(); - } - - -}]); diff --git a/app/js/pages/controller-link.js b/app/js/pages/controller-link.js deleted file mode 100755 index 59c4690c..00000000 --- a/app/js/pages/controller-link.js +++ /dev/null @@ -1,16 +0,0 @@ -angular.module('app').controller('LinkController', ['$scope', 'Utils', '$stateParams', function($scope, Utils, $stateParams) { - $scope.url = $stateParams.url; - $scope.shortenedUrl = 'Shortening...'; - - $scope.onTextClick = function($event) { - $event.target.select(); - }; - - Utils.shortenUrl($scope.url) - .then(function(url) { - $scope.shortenedUrl = url; - }, function(e) { - $scope.shortenedUrl = 'Error - ' + e.statusText; - }); - -}]); diff --git a/app/js/pages/controller-modal.js b/app/js/pages/controller-modal.js deleted file mode 100755 index c73135ad..00000000 --- a/app/js/pages/controller-modal.js +++ /dev/null @@ -1,14 +0,0 @@ -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 }); - } else { - $state.go('shipyard'); - } - }; - - $scope.$on('close', $scope.dismiss); - -}]); diff --git a/app/js/utils/ShortenUrl.js b/app/js/utils/ShortenUrl.js deleted file mode 100644 index 22cc498e..00000000 --- a/app/js/utils/ShortenUrl.js +++ /dev/null @@ -1,10 +0,0 @@ - -export default function shortenUrl(url) { - /*if (window.navigator.onLine) { - return $http.post(shortenAPI + GAPI_KEY, { longUrl: url }).then(function(response) { - return response.data.id; - }); - } else { - return $q.reject({ statusText: 'Not Online' }); - }*/ -} diff --git a/app/less/background-images.less b/app/less/background-images.less deleted file mode 100755 index 295dd50e..00000000 --- a/app/less/background-images.less +++ /dev/null @@ -1,8 +0,0 @@ - -.deep-space { - background-image: url(images/deep-space-1920x1080.jpg); -} - -.docking-bay { - background-image: url(images/bay-1920x1080.jpg); -} \ No newline at end of file diff --git a/app/less/fonts.less b/app/less/fonts.less deleted file mode 100755 index f8c0b0f4..00000000 --- a/app/less/fonts.less +++ /dev/null @@ -1,26 +0,0 @@ -@font-face { - font-family: 'Orbitron-Regular'; - src: url('fonts/orbitron-regular-webfont.eot'); - src: url('fonts/orbitron-regular-webfont.eot?#iefix') format('embedded-opentype'), - url('fonts/orbitron-regular-webfont.woff2') format('woff2'), - url('fonts/orbitron-regular-webfont.woff') format('woff'), - url('fonts/orbitron-regular-webfont.ttf') format('truetype'), - url('fonts/orbitron-regular-webfont.svg#orbitronregular') format('svg'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: 'Eurostile'; - src: url('fonts/eurostile.eot'); - src: url('fonts/eurostile.eot?#iefix') format('embedded-opentype'), - url('fonts/eurostile.woff2') format('woff2'), - url('fonts/eurostile.woff') format('woff'), - url('fonts/eurostile.ttf') format('truetype'), - url('fonts/eurostile.svg#euro_capsregular') format('svg'); - font-weight: normal; - font-style: normal; -} - -@fStandard: 'Eurostile', Helvetica, sans-serif; -@fTitle: 'Orbitron-Regular', Arial, sans-serif; diff --git a/app/views/_modal.html b/app/views/_modal.html deleted file mode 100755 index 946cf418..00000000 --- a/app/views/_modal.html +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/app/views/_slot-hardpoint.html b/app/views/_slot-hardpoint.html deleted file mode 100755 index 14d211fc..00000000 --- a/app/views/_slot-hardpoint.html +++ /dev/null @@ -1,17 +0,0 @@ -
    {{['U','S','M','L','H'][hp.maxClass] | translate}}
    -
    -
    - {{hp.c.class}}{{hp.c.rating}}/{{hp.c.mode}}{{hp.c.missile}} {{hp.c.name || hp.c.grp | translate}} -
    {{hp.c.mass}} T
    -
    -
    {{'damage' | translate}}: {{hp.c.damage}} ({{$r.fCrd(hp.c.ssdam)}} MJ)
    -
    {{'DPS' | translate}}: {{hp.c.dps}} ({{$r.fCrd(hp.c.mjdps)}} MJ)
    -
    {{'T_LOAD' | translate}}: {{hp.c.thermload}}
    -
    {{'type' | translate}}: {{hp.c.type}}
    -
    {{'ROF' | translate}}: {{hp.c.rof}}/s
    -
    {{'pen' | translate}}: {{hp.c.armourpen}}
    -
    +{{$r.fRPct(hp.c.shieldmul)}}
    -
    {{hp.c.range}} km
    -
    {{'ammo' | translate}}: {{$r.fCrd(hp.c.clip)}}+{{$r.fCrd(hp.c.ammo)}}
    -
    -
    diff --git a/app/views/_slot-internal.html b/app/views/_slot-internal.html deleted file mode 100755 index 4f9b7a60..00000000 --- a/app/views/_slot-internal.html +++ /dev/null @@ -1,22 +0,0 @@ -
    -
    -
    -
    {{c.c.class}}{{c.c.rating}} {{c.c.name || c.c.grp | translate}}
    -
    {{c.c.mass || c.c.capacity || '0'}}
    -
    -
    {{'optimal mass' | translate}}: {{c.c.optmass}}
    -
    {{'max mass' | translate}}: {{c.c.maxmass}}
    -
    {{c.c.bins}}
    -
    {{'rate' | translate}}: {{c.c.rate}} kg/s   {{'refuel time' | translate}}: {{$r.fTime(fuel * 1000 / c.c.rate)}}
    -
    {{'ammo' | translate}}: {{$r.fCrd(c.c.ammo)}}
    -
    {{'cells' | translate}}: {{c.c.cells}}
    -
    {{'recharge' | translate}}: {{c.c.recharge}} MJ   {{'total' | translate}}: {{c.c.cells * c.c.recharge}} MJ
    -
    {{'repair' | translate}}: {{c.c.repair}}
    -
    {{'range' | translate}} {{c.c.range}} km
    -
    {{'time' | translate}}: {{$r.fTime(c.c.time)}}
    -
    {{'max' | translate}}: {{(c.c.maximum)}}
    -
    {{c.c.rangeLS}}
    -
    -
    {{'range' | translate}}: {{c.c.rangeRating}}
    -
    +{{c.c.armouradd}}
    -
    diff --git a/app/views/modal-about.html b/app/views/modal-about.html deleted file mode 100755 index 5e03eb47..00000000 --- a/app/views/modal-about.html +++ /dev/null @@ -1,29 +0,0 @@ -

    Coriolis

    -

    The Coriolis project was inspired by E:D Shipyard and, of course, -Elite Dangerous. 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 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.

    - - -

    - Coriolis is an open source project. Checkout the list of upcoming features and to-do list on github. - Any and all contributions and feedback are welcome. If you encounter any bugs please report them and provide as much detail as possible. -

    - -
    - - - - -
    - -

    Help keep the lights on! Donations will be used to cover costs of running and maintaining Coriolis. Thanks for helping!

    - - \ No newline at end of file diff --git a/app/views/modal-delete.html b/app/views/modal-delete.html deleted file mode 100755 index f132277b..00000000 --- a/app/views/modal-delete.html +++ /dev/null @@ -1,4 +0,0 @@ -

    -

    - - \ No newline at end of file diff --git a/app/views/modal-export.html b/app/views/modal-export.html deleted file mode 100755 index 2828f7e1..00000000 --- a/app/views/modal-export.html +++ /dev/null @@ -1,6 +0,0 @@ -

    -
    -
    - -
    - \ No newline at end of file diff --git a/app/views/modal-import.html b/app/views/modal-import.html deleted file mode 100755 index fe92aace..00000000 --- a/app/views/modal-import.html +++ /dev/null @@ -1,46 +0,0 @@ -

    -
    - - -
    {{errorMsg}}
    -
    - -
    - - - - - - - - - - - - - - - -
    {{ships[shipId].properties.name}} - - -
    - - - - - - - - - -
    - - -
    - - - -
    - - \ No newline at end of file diff --git a/app/views/modal-link.html b/app/views/modal-link.html deleted file mode 100755 index 726033c2..00000000 --- a/app/views/modal-link.html +++ /dev/null @@ -1,9 +0,0 @@ -

    -
    -

    - -

    -

    - -

    - diff --git a/app/views/page-comparison.html b/app/views/page-comparison.html deleted file mode 100755 index d6ade87f..00000000 --- a/app/views/page-comparison.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - -
    - - - - - - -
    -

    - -
    -
      -
    • -
      -
    • -
    -
    - -
    -
    -
    - -
    -

    {{f.title | translate}}

    -
    - - \ No newline at end of file diff --git a/app/views/page-error.html b/app/views/page-error.html deleted file mode 100755 index e1c47d9b..00000000 --- a/app/views/page-error.html +++ /dev/null @@ -1,21 +0,0 @@ -
    -

    - {{msgPre}} - {{msgHighlight}} - {{msgPost}} -

    - -
    -
    - Create an issue on Github - if this keeps happening. Add these details -
    -
    -
    Browser:
    {{browser}}
    -
    Path:
    {{path}}
    -
    Error:
    {{type}}
    -
    Message:
    {{errorMessage}}
    -
    Details:
    {{details}}
    -
    -
    -
    diff --git a/devServer.js b/devServer.js new file mode 100644 index 00000000..4ef236a0 --- /dev/null +++ b/devServer.js @@ -0,0 +1,27 @@ +var path = require('path'); +var express = require('express'); +var webpack = require('webpack'); +var config = require('./webpack.config.dev'); + +var app = express(); +var compiler = webpack(config); + +app.use(require('webpack-dev-middleware')(compiler, { + noInfo: true, + publicPath: config.output.publicPath +})); + +app.use(require('webpack-hot-middleware')(compiler)); + +app.get('*', function(req, res) { + res.sendFile(path.join(__dirname, 'src/index.html')); +}); + +app.listen(3300, 'localhost', function(err) { + if (err) { + console.log(err); + return; + } + + console.log('Listening at http://localhost:3000'); +}); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100755 index 3a61d18a..00000000 --- a/gulpfile.js +++ /dev/null @@ -1,281 +0,0 @@ -// Build / Built-in dependencies -var gulp = require('gulp'), - exec = require('child_process').exec, - pkg = require('./package.json'); - -// Package.json / Gulp Dependencies -var appCache = require("gulp-manifest"), - concat = require('gulp-concat'), - del = require('del'), - eslint = require('gulp-eslint'); - gutil = require('gulp-util'), - htmlmin = require('gulp-htmlmin'), - jsonlint = require("gulp-jsonlint"), - karma = require('karma').server, - less = require('gulp-less'), - mainBowerFiles = require('main-bower-files'), - minifyCSS = require('gulp-minify-css'), - revAll = require('gulp-rev-all'), - runSequence = require('run-sequence'), - sourcemaps = require('gulp-sourcemaps'), - svgstore = require('gulp-svgstore'), - svgmin = require('gulp-svgmin'), - template = require('gulp-template'), - templateCache = require('gulp-angular-templatecache'), - uglify = require('gulp-uglify'); - -var cdnHostStr = ''; - -gulp.task('less', function() { - return gulp.src('app/less/app.less') - .pipe(less({paths: ['less/app.less']}).on('error',function(e){ - console.log('File:', e.fileName); - console.log('Line:', e.lineNumber); - console.log('Message:', e.message); - this.emit('end'); - })) - .pipe(minifyCSS()) - .pipe(gulp.dest('build')); -}); - -gulp.task('js-lint', function() { - return gulp.src(['app/js/**/*.js', '!app/js/template_cache.js', '!app/js/db.js']) - .pipe(eslint({ - globals: { angular:1, DB:1, d3:1, ga:1, GAPI_KEY:1, LZString: 1 }, - rules: { - quotes: [2, 'single'], - strict: 'global', - eqeqeq: 'smart', - 'space-after-keywords': [2, 'always'], - 'no-use-before-define': 'no-func', - 'space-before-function-paren': [2, 'never'], - 'space-before-blocks': [2, 'always'], - 'object-curly-spacing': [2, "always"], - 'brace-style': [2, '1tbs', { allowSingleLine: true }], - 'no-control-regex': false - }, - envs: ['browser'] - })) - .pipe(eslint.format()) - .pipe(eslint.failAfterError()); -}); - -gulp.task('json-lint', function() { - return gulp.src(['data/**/*.json' , 'app/schemas/**/*.json']) - .pipe(jsonlint()) - .pipe(jsonlint.reporter()) - .pipe(jsonlint.failAfterError()); -}); - -gulp.task('bower', function(){ - return gulp.src(mainBowerFiles()) - .pipe(uglify({mangle: false, compress: false}).on('error',function(e){ - console.log('Bower File:', e.fileName); - console.log('Line:', e.lineNumber); - console.log('Message:', e.message); - })) - .pipe(concat('lib.js')) - .pipe(gulp.dest('build')); -}); - -gulp.task('html2js', function() { - return gulp.src('app/views/**/*.html') - .pipe(htmlmin({ - 'collapseBooleanAttributes': true, - 'collapseWhitespace': true, - 'removeAttributeQuotes': true, - 'removeComments': true, - 'removeEmptyAttributes': true, - 'removeRedundantAttributes': true, - 'removeScriptTypeAttributes': true, - 'removeStyleLinkTypeAttributes': true - }).on('error',function(e){ - console.log('File:', e.fileName); - console.log('Message:',e.message); - })) - .pipe(templateCache({ - 'module': 'app.templates', - 'standalone': true, - 'root': 'views', - 'filename': 'template_cache.js' - })) - .pipe(gulp.dest('app/js')) -}); - -gulp.task('jsonToDB', function(cb) { - exec('node scripts/json-to-db.js', cb); -}); - -gulp.task('js', function() { - return gulp.src([ - 'app/js/db.js', - 'app/js/**/module-*.js', - 'app/js/template_cache.js', - 'app/js/app.js', - 'app/js/**/*.js' - ]) - .pipe(sourcemaps.init()) - .pipe(uglify({mangle: false}).on('error',function(e){ - console.log('File:', e.fileName); - console.log('Line:', e.lineNumber); - console.log('Message:', e.message); - this.emit('end'); - })) - .pipe(concat('app.js')) - .pipe(sourcemaps.write('.')) - .pipe(gulp.dest('build')); -}); - -gulp.task('copy', function() { - return gulp.src(['app/images/**','app/fonts/**','app/db.json', 'app/schemas/**'], {base: 'app/'}) - .pipe(gulp.dest('build')); -}); - -gulp.task('generateIndexHTML', function(done) { - // Generate minified inline svg of all icons for svg spriting - gulp.src('app/icons/*.svg') - .pipe(svgmin()) - .pipe(svgstore({ inlineSvg: true })) - .pipe(gutil.buffer(function(err, files) { - var svgIconsContent = files[0].contents.toString(); - gulp.src('app/index.html') - .pipe(template({ - version: pkg.version, - date : new Date().toISOString().slice(0, 10), - uaTracking: process.env.CORIOLIS_UA_TRACKING || false, - svgContent: svgIconsContent, - gapiKey: process.env.CORIOLIS_GAPI_KEY - })) - .pipe(htmlmin({ - 'collapseBooleanAttributes': true, - 'collapseWhitespace': true, - 'removeAttributeQuotes': true, - 'removeComments': true, - 'removeEmptyAttributes': true, - 'removeRedundantAttributes': true, - 'removeScriptTypeAttributes': true, - 'removeStyleLinkTypeAttributes': true - }).on('error',function(e){ - console.log('File:', e.fileName); - console.log('Message:',e.message); - })) - .pipe(gulp.dest('build')); - done(); - })); -}); - -gulp.task('serve', function(cb) { - exec('nginx -p $(pwd) -c nginx.conf', function (err, stdout, stderr) { - if (stderr) { - console.warn(stderr); - console.warn('Is NGINX already running?\n'); - } - cb(); - }); -}); - -// Windows command to launch nginx serv -gulp.task('serve-win', function(cb) { - exec('nginx -p %cd% -c nginx.conf', function (err, stdout, stderr) { - if (stderr) { - console.warn(stderr); - console.warn('Is NGINX already running?\n'); - } - cb(); - }); -}); - -gulp.task('serve-stop', function(cb) { - exec('kill -QUIT $(cat nginx.pid)', function (err, stdout, stderr) { - if (stderr) console.log(stderr); else cb(err); - }); -}); - -gulp.task('watch', function() { - gulp.watch(['app/index.html','app/icons/*.svg'], ['generateIndexHTML']); - gulp.watch(['app/images/**','app/fonts/**', 'app/db.json', 'app/schemas/**'], ['copy']); - gulp.watch('app/less/*.less', ['less']); - gulp.watch('app/views/**/*', ['html2js']); - gulp.watch('app/js/**/*.js', ['js']); - gulp.watch('data/**/*.json', ['jsonToDB']); - gulp.watch(['build/**', '!**/*.appcache'], ['appcache']); -}); - -gulp.task('cache-bust', function(done) { - var rev_all = new revAll({ prefix: cdnHostStr, dontRenameFile: ['.html','.json'] }); - var stream = gulp.src('build/**') - .pipe(rev_all.revision()) - .pipe(gulp.dest('build')) - .pipe(rev_all.manifestFile()) - .pipe(gulp.dest('build')); - - stream.on('end', function() { - var manifest = require('./build/rev-manifest.json'); - var arr = []; - for(var origFileName in manifest) { - if(origFileName != manifest[origFileName]) { // For all files busted/renamed - arr.push('./build/' + origFileName); // Add the original filename to the list - } - } - del(arr, done); // Delete all originals files the were not busted/renamed - }); - stream.on('error', done); -}); - -gulp.task('appcache', function(done) { - // Since using a CDN manually build file list rather than using appCache mechanisms - gulp.src(['build/**', '!build/index.html', '!**/*.json', '!**/logo/*', '!**/*.map','!**/*.appcache']) - .pipe(gutil.buffer(function(err, stream) { - var files = []; - for (var i = 0; i < stream.length; i++) { - if (!stream[i].isNull()) { - files.push(cdnHostStr + '/' + stream[i].relative); - } - } - - gulp.src([]) - .pipe(appCache({ - preferOnline: true, - cache: files, - filename: 'coriolis.appcache', - timestamp: true - })) - .pipe(gulp.dest('build')) - .on('end', done); - })); -}); - -gulp.task('upload', function(done) { - exec([ - "rsync -e 'ssh -i ", process.env.CORIOLIS_PEM, "' -a --delete build/ ", process.env.CORIOLIS_USER, "@", process.env.CORIOLIS_HOST, ":~/www" - ].join(''), - done - ); -}); - -gulp.task('test', function (done) { - karma.start({ - configFile: __dirname + '/test/karma.conf.js', - singleRun: true - }, function(exitStatus) { - done(exitStatus ? new gutil.PluginError('karma', { message: 'Unit tests failed!' }) : undefined); - }); -}); - -gulp.task('lint', ['js-lint', 'json-lint']); - -gulp.task('clean', function (done) { del(['build'], done); }); - -gulp.task('build', function (done) { runSequence('clean', ['html2js','jsonToDB'], ['generateIndexHTML','bower','js','less','copy'], done); }); -gulp.task('build-cache', function (done) { runSequence('build', 'appcache', done); }); -gulp.task('build-prod', function (done) { runSequence('build', 'cache-bust', 'appcache', done); }); - -gulp.task('dev', function (done) { runSequence('build-cache', 'serve','watch', done); }); - -gulp.task('deploy', function (done) { - cdnHostStr = '//cdn.' + process.env.CORIOLIS_HOST; - runSequence('build-prod', 'upload', done); -}); - -gulp.task('default', ['dev']); - diff --git a/nginx.conf b/nginx.conf deleted file mode 100755 index d9fa6a33..00000000 --- a/nginx.conf +++ /dev/null @@ -1,58 +0,0 @@ -worker_processes 2; -error_log ./nginx.error.log; -worker_rlimit_nofile 8192; -pid nginx.pid; - -events { - worker_connections 1024; - multi_accept on; -} - -http { - - access_log off; - charset UTF-8; - - types { - text/html html htm shtml; - text/css css; - text/xml xml rss; - image/gif gif; - image/jpeg jpeg jpg; - application/x-javascript js; - text/plain txt; - image/png png; - image/svg+xml svg; - image/x-icon ico; - application/pdf pdf; - text/cache-manifest appcache; - } - - gzip on; - gzip_vary on; - gzip_proxied any; - gzip_comp_level 6; - gzip_buffers 16 8k; - gzip_http_version 1.1; - gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; - - server { - listen 3300; - server_name localhost; - root ./build/; - index index.html; - - location ~* \.(?:manifest|appcache|html?|xml|json|css|js|map|jpg|jpeg|gif|png|ico|svg|eot|ttf|woff|woff2)$ { - expires -1; - add_header Access-Control-Allow-Origin *; - add_header Access-Control-Allow-Credentials true; - add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; - add_header Access-Control-Allow-Headers 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; - access_log off; - } - - location / { - try_files $uri $uri/ /index.html =404; - } - } -} diff --git a/package.json b/package.json index d9b6e733..9ac152b2 100644 --- a/package.json +++ b/package.json @@ -10,41 +10,45 @@ "private": true, "engine": "node >= 0.12.2", "license": "MIT", + "scripts": { + "clean": "rimraf build", + "build:prod": "NODE_ENV=production webpack --config webpack.config.prod.js", + "build": "npm run clean && npm run build:prod", + "start": "node devServer.js", + "lint": "eslint src" + }, "devDependencies": { - "async": "0.9.x", - "del": "1.2.x", - "gulp": "3.9.x", - "gulp-concat": "2.5.x", - "gulp-eslint": "0.13.x", - "gulp-jasmine": "2.0.x", - "gulp-jsonlint": "1.1.x", - "gulp-less": "3.0.x", - "gulp-manifest": "0.0.6", - "gulp-minify-css": "1.1.x", - "gulp-rev-all": "0.8.18", - "gulp-sourcemaps": "1.5.x", - "gulp-svgmin": "1.1.x", - "gulp-svgstore": "5.0.x", - "gulp-template": "3.0.x", - "gulp-uglify": "1.2.x", - "gulp-util": "3.0.x", - "jasmine-core": "2.3.x", - "jsen": "^0.6.0", - "json-concat": "0.0.x", - "karma": "0.12.x", - "karma-fixture": "^0.2.5", - "karma-jasmine": "0.3.x", - "karma-json-fixtures-preprocessor": "0.0.4", - "karma-mocha-reporter": "1.0.x", - "karma-phantomjs-launcher": "0.2.x", - "phantomjs": "1.9.x", - "run-sequence": "1.1.x", - "uglify-js": "2.4.x" + "babel-core": "^5.4.7", + "babel-eslint": "^3.1.9", + "babel-loader": "^5.1.2", + "babel-plugin-react-transform": "^1.1.1", + "css-loader": "^0.23.0", + "eslint": "^1.3.1", + "eslint-plugin-react": "^2.3.0", + "express": "^4.13.3", + "file-loader": "^0.8.4", + "install": "^0.3.0", + "json-loader": "^0.5.3", + "less": "^2.5.3", + "less-loader": "^2.2.1", + "npm": "^3.4.0", + "react-transform-catch-errors": "^1.0.0", + "react-transform-hmr": "^1.0.0", + "redbox-react": "^1.0.1", + "rimraf": "^2.4.3", + "style-loader": "^0.13.0", + "url-loader": "^0.5.6", + "webpack": "^1.9.6", + "webpack-dev-middleware": "^1.2.0", + "webpack-hot-middleware": "^2.0.0" }, "dependencies": { "classnames": "^2.2.0", + "d3": "^3.5.9", "fbemitter": "^2.0.0", + "lz-string": "^1.4.4", "react": "^0.14.2", - "react-dom": "^0.14.2" + "react-dom": "^0.14.2", + "superagent": "^1.4.0" } } diff --git a/scripts/json-to-db.js b/scripts/json-to-db.js deleted file mode 100755 index 67df0606..00000000 --- a/scripts/json-to-db.js +++ /dev/null @@ -1,90 +0,0 @@ -var fs = require('fs'); -var UglifyJS = require('uglify-js'); -var jsonConcat = require('json-concat'); -var async = require('async'); -var db_filename = './app/js/db.js'; - -async.parallel([ - function(cb) { jsonConcat({ dest: null, src: './data/ships' }, done.bind(cb)); }, - function(cb) { - var standard = [ - JSON.parse(fs.readFileSync('./data/components/standard/power_plant.json', 'utf8')), - JSON.parse(fs.readFileSync('./data/components/standard/thrusters.json', 'utf8')), - JSON.parse(fs.readFileSync('./data/components/standard/frame_shift_drive.json', 'utf8')), - JSON.parse(fs.readFileSync('./data/components/standard/life_support.json', 'utf8')), - JSON.parse(fs.readFileSync('./data/components/standard/power_distributor.json', 'utf8')), - JSON.parse(fs.readFileSync('./data/components/standard/sensors.json', 'utf8')), - JSON.parse(fs.readFileSync('./data/components/standard/fuel_tank.json', 'utf8')) - ]; - cb(null, standard); - }, - function(cb) { jsonConcat({ dest: null, src: './data/components/hardpoints' }, done.bind(cb)); }, - function(cb) { jsonConcat({ dest: null, src: './data/components/internal' }, done.bind(cb)); }, - function(cb) { jsonConcat({ dest: null, src: ['./data/components/bulkheads.json'] }, done.bind(cb)); } - ], writeDB); - -function done(err, json) { this(err,json); } - -function writeDB(err, arr) { - var ships = {}, internal = {}, hardpoints = {}; - var shipOrder = Object.keys(arr[0]).sort(function(a,b) { return arr[0][a].properties.name < arr[0][b].properties.name ? -1 : 1; }); - var internalOrder = Object.keys(arr[3]).sort(); - var hpOrder = [ - "pl", - "ul", - "bl", - "mc", - "c", - "fc", - "rg", - "pa", - "mr", - "tp", - "nl", - "ml", - "cs", - "cm", - "ws", - "kw", - "sb" - ]; - - for (var i = 0; i < internalOrder.length; i++) { - internal[internalOrder[i]] = arr[3][internalOrder[i]]; - } - - for (var j = 0; j < hpOrder.length; j++) { - hardpoints[hpOrder[j]] = arr[2][hpOrder[j]]; - } - - for (var s = 0; s < shipOrder.length; s++) { - ships[shipOrder[s]] = arr[0][shipOrder[s]]; - } - - try { - var db = { - ships: ships, - components: { - standard: arr[1], - hardpoints: hardpoints, - internal: internal, - bulkheads: arr[4] - } - }; - } - catch (e) { - console.error(arguments); - exit(0); - } - - var ast = UglifyJS.parse('var DB = ' + JSON.stringify(db)); - var code = ast.print_to_string({beautify: true, indent_level: 2}); - - fs.open(db_filename, 'w', function() { - fs.writeFile(db_filename, code, function(err) {}); - }); - - fs.open('./app/db.json', 'w', function() { - fs.writeFile('./app/db.json', JSON.stringify(db), function(err) {}); - }); -} diff --git a/src/app/Coriolis.jsx b/src/app/Coriolis.jsx new file mode 100644 index 00000000..466cd826 --- /dev/null +++ b/src/app/Coriolis.jsx @@ -0,0 +1,110 @@ +import React from 'react'; +import Router from './Router'; +import { getLanguage } from './i18n/Language'; +import Persist from './stores/Persist'; +import InterfaceEvents from './utils/InterfaceEvents'; + +import Header from './components/Header'; +import AboutPage from './pages/AboutPage'; +import NotFoundPage from './pages/NotFoundPage'; +import OutfittingPage from './pages/OutfittingPage'; +import ShipyardPage from './pages/ShipyardPage'; + +export default class Coriolis extends React.Component { + + static childContextTypes = { + language: React.PropTypes.object.isRequired, + route: React.PropTypes.object + }; + + constructor() { + super(); + this._setPage = this._setPage.bind(this); + + this.state = { + page: null, + language: getLanguage(Persist.getLangCode()), + route: null + }; + + Router('', (r) => this._setPage(ShipyardPage, r)); + // Router('/', (ctx) => this._setPage(ShipyardPage, ctx)); + Router('/outfitting/:ship', (r) => this._setPage(OutfittingPage, r)); + Router('/outfitting/:ship/:code', (r) => this._setPage(OutfittingPage, r)); + // Router('/compare/:name', compare); + // Router('/comparison/:code', comparison); + // Router('/settings', settings); + Router('/about', (r) => this._setPage(AboutPage, r)); + Router('*', (r) => this._setPage(null, r)); + } + + _setPage(page, route) { + this.setState({ page, route }); + } + + _onError(msg, scriptUrl, line, col, errObj) { + this._setPage(
    Some errors occured!!
    ); + } + + _onLanguageChange(lang) { + this.setState({ language: getLanguage(Persist.getLangCode()) }); + } + + _keyDown(e) { + switch (e.keyCode) { + case 27: + InterfaceEvents.closeAll(); + this._hideModal(); + break; + } + } + + _showModal(content) { + let modal =
    this._hideModal() }>{content}
    ; + this.setState({ modal }); + } + + _hideModal() { + if (this.state.modal) { + this.setState({ modal: null }); + } + } + + getChildContext() { + return { + language: this.state.language, + route: this.state.route + }; + } + + componentWillMount() { + // Listen for appcache updated event, present refresh to update view + if (window.applicationCache) { + window.applicationCache.addEventListener('updateready', () => { + if (window.applicationCache.status == window.applicationCache.UPDATEREADY) { + this.setState({appCacheUpdate: true}); // Browser downloaded a new app cache. + } + }, false); + } + + window.onerror = this._onError.bind(this); + document.addEventListener('keydown', this._keyDown.bind(this)); + Persist.addListener('language', this._onLanguageChange.bind(this)); + Persist.addListener('language', this._onLanguageChange.bind(this)); + InterfaceEvents.addListener('showModal', this._showModal.bind(this)); + InterfaceEvents.addListener('hideModal', this._hideModal.bind(this)); + + Router.start(); + } + + + render() { + return ( +
    +
    + {this.state.page? : } + {this.state.modal} +
    + ); + } +} diff --git a/app/js/Router.js b/src/app/Router.js similarity index 93% rename from app/js/Router.js rename to src/app/Router.js index f4132029..c5929c7d 100644 --- a/app/js/Router.js +++ b/src/app/Router.js @@ -1,4 +1,5 @@ -import Persist from 'stores/Persist'; +import Persist from './stores/Persist'; +import InterfaceEvents from './utils/InterfaceEvents'; function isStandAlone() { try { @@ -35,19 +36,6 @@ function Router(path, fn) { Router.callbacks = []; -/** - * Bind with the given `options`. - * - * Options: - * - * - `click` bind to click events [true] - * - `popstate` bind to popstate [true] - * - `dispatch` perform initial dispatch [true] - * - * @param {Object} options - * @api public - */ - Router.start = function(){ window.addEventListener('popstate', onpopstate, false); @@ -62,7 +50,7 @@ Router.start = function(){ } } else { var url = location.pathname + location.search + location.hash; - Router.replace(url, null, true, dispatch); + Router.replace(url, null, true, true); } }; @@ -76,8 +64,9 @@ Router.start = function(){ */ Router.go = function(path, state) { gaTrack(path); + InterfaceEvents.closeAll(); var ctx = new Context(path, state); - if (false !== dispatch) Router.dispatch(ctx); + Router.dispatch(ctx); if (!ctx.unhandled) ctx.pushState(); return ctx; }; @@ -95,8 +84,7 @@ Router.replace = function(path, state, init, dispatch) { gaTrack(path); var ctx = new Context(path, state); ctx.init = init; - if (null == dispatch) dispatch = true; - if (dispatch) Router.dispatch(ctx); + if (dispatch !== false) Router.dispatch(ctx); ctx.save(); return ctx; }; diff --git a/src/app/components/ActiveLink.jsx b/src/app/components/ActiveLink.jsx new file mode 100644 index 00000000..dfb5228c --- /dev/null +++ b/src/app/components/ActiveLink.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import Link from './Link'; +import cn from 'classnames'; + +export default class ActiveLink extends Link { + + isActive = () => { + return encodeURI(this.props.href) == (window.location.pathname + window.location.search); + } + + render() { + let className = this.props.className; + if (this.isActive()) { + className = cn(className, 'active'); + } + + return {this.props.children} + } + +} \ No newline at end of file diff --git a/src/app/components/AvailableModulesMenu.jsx b/src/app/components/AvailableModulesMenu.jsx new file mode 100644 index 00000000..95bb4bc5 --- /dev/null +++ b/src/app/components/AvailableModulesMenu.jsx @@ -0,0 +1,103 @@ +import React from 'react'; +import { findDOMNode } from 'react-dom'; +import TranslatedComponent from './TranslatedComponent'; +import cn from 'classnames'; +import { MountFixed, MountGimballed, MountTurret } from './SvgIcons'; + +export default class AvailableModulesMenu extends TranslatedComponent { + + static propTypes = { + modules: React.PropTypes.oneOfType([ React.PropTypes.object, React.PropTypes.array ]).isRequired, + onSelect: React.PropTypes.func.isRequired, + m: React.PropTypes.object, + shipMass: React.PropTypes.number, + warning: React.PropTypes.func + }; + + static defaultProps = { + shipMass: 0 + }; + + buildGroup(translate, mountedModule, warningFunc, mass, onSelect, grp, modules) { + let prevClass = null, prevRating = null; + let elems = []; + + for (let i = 0; i < modules.length; i++) { + let m = modules[i]; + let classRating = m.class + m.rating; + let mount = null; + let classes = cn(m.name ? 'lc' : 'c', { + active: mountedModule && mountedModule.id === m.id, + warning: warningFunc && warningFunc(m), + disabled: m.maxmass && (mass + (m.mass ? m.mass : 0)) > m.maxmass + }); + + switch(m.mode) { + case 'F': mount = ; break; + case 'G': mount = ; break; + case 'T': mount = ; break; + } + + if (i > 0 && modules.length > 3 && m.class != prevClass && (m.rating != prevRating || m.mode) && m.grp != 'pa') { + elems.push(
    ); + } + + elems.push( +
  • + {mount} + {(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')} +
  • + ); + prevClass = m.class; + prevRating = m.rating; + } + + return
      {elems}
    ; + } + + componentDidMount() { + let m = this.props.m + + if (!(this.props.modules instanceof Array) && m && m.grp) { + findDOMNode(this).scrollTop = this.refs[m.grp].offsetTop; // Scroll to currently selected group + } + } + + render() { + let translate = this.context.language.translate; + let m = this.props.m; + let modules = this.props.modules; + let list; + let buildGroup = this.buildGroup.bind( + null, + translate, + m, + this.props.warning, + this.props.shipMass - (m && m.mass ? m.mass : 0), + this.props.onSelect + ); + + if (modules instanceof Array) { + console.log(modules[0].grp, modules); + list = buildGroup(modules[0].grp, modules); + } else { + console.log('menu object') + list = []; + // At present time slots with grouped options (Hardpoints and Internal) can be empty + list.push(
    {translate('empty')}
    ); + for (let g in modules) { + let grp = modules[g]; + let grpCode = grp[Object.keys(grp)[0]].grp; // Nasty operation to get the grp property of the first/any single component + list.push(
    {translate(g)}
    ); + list.push(buildGroup(g, modules[g])); + } + } + + return ( +
    e.stopPropagation() }> + {list} +
    + ); + } + +} diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx new file mode 100644 index 00000000..6c4dfab6 --- /dev/null +++ b/src/app/components/HardpointSlot.jsx @@ -0,0 +1,38 @@ +import React from 'react'; +import Slot from './Slot'; + +export default class HardpointSlot extends Slot { + + getClassNames() { + return this.props.size > 0 ? 'hardpoint' : null; + } + + getSize(translate){ + return translate(['U','S','M','L','H'][this.props.size]); + } + + getSlotDetails(m, translate, formats, u) { + if (m) { + let classRating = `${m.class}${m.rating}${m.mode ? '/' + m.mode : ''}${m.missile ? m.missile : ''}`; + return ( +
    +
    {classRating + ' ' + translate(m.name || m.grp)}
    +
    {m.mass}{u.T}
    +
    + { m.damage ?
    {translate('damage')}: {m.damage} { m.ssdam ? ({formats.int(m.ssdam)} {u.MJ}) : null }
    : null } + { m.dps ?
    {translate('DPS')}: {m.dps} { m.mjdps ? ({formats.int(m.mjdps)} {u.MJ}) : null }
    : null } + { m.thermload ?
    {translate('T_LOAD')}: {m.thermload}
    : null } + { m.type ?
    {translate('type')}: {m.type}
    : null } + { m.rof ?
    {translate('ROF')}: {m.rof}{u.ps}
    : null } + { m.armourpen ?
    {translate('pen')}: {m.armourpen}
    : null } + { m.shieldmul ?
    +{formats.rPct(m.shieldmul)}
    : null } + { m.range ?
    {m.range} km
    : null } + { m.ammo >= 0 ?
    {translate('ammo')}: {formats.int(m.clip)}+{formats.int(m.ammo)}
    : null } +
    +
    + ); + } else { + return
    {translate('empty')}
    ; + } + } +} diff --git a/src/app/components/HardpointsSlotSection.jsx b/src/app/components/HardpointsSlotSection.jsx new file mode 100644 index 00000000..b4e3f386 --- /dev/null +++ b/src/app/components/HardpointsSlotSection.jsx @@ -0,0 +1,87 @@ +import React from 'react'; +import SlotSection from './SlotSection'; +import HardpointSlot from './HardpointSlot'; +import cn from 'classnames'; +import { MountFixed, MountGimballed, MountTurret } from '../components/SvgIcons'; + +export default class HardpointsSlotSection extends SlotSection { + + constructor(props, context) { + super(props, context, 'hardpoints', 'hardpoints'); + + this._empty = this._empty.bind(this); + } + + _empty() { + + } + + _fill(grp, mount) { + + } + + _getSlots() { + let slots = []; + let hardpoints = this.props.ship.hardpoints; + let availableModules = this.props.ship.getAvailableModules(); + let currentMenu = this.state.currentMenu; + + for (let i = 0, l = hardpoints.length; i < l; i++) { + let h = hardpoints[i]; + if (h.maxClass) { + slots.push(); + } + } + + return slots; + } + + _getSectionMenu(translate) { + let _fill = this._fill; + + return
    e.stopPropagation()}> +
      +
    • {translate('empty all')}
    • +
    +
    {translate('pl')}
    +
      +
    • +
    • +
    • +
    +
    {translate('ul')}
    +
      +
    • +
    • +
    • +
    +
    {translate('bl')}
    +
      +
    • +
    • +
    • +
    +
    {translate('mc')}
    +
      +
    • +
    • +
    • +
    +
    {translate('c')}
    +
      +
    • +
    • +
    • +
    +
    ; + } + +} diff --git a/src/app/components/Header.jsx b/src/app/components/Header.jsx new file mode 100644 index 00000000..875bbb99 --- /dev/null +++ b/src/app/components/Header.jsx @@ -0,0 +1,251 @@ +import React from 'react'; +import TranslatedComponent from './TranslatedComponent'; +import Link from './Link'; +import ActiveLink from './ActiveLink'; +import cn from 'classnames'; +import { Cogs, CoriolisLogo, Hammer, Rocket, StatsBars } from './SvgIcons'; +import Ships from '../shipyard/Ships'; +import InterfaceEvents from '../utils/InterfaceEvents'; +import Persist from '../stores/Persist'; +import ModalDeleteAll from './ModalDeleteAll'; + +export default class Header extends TranslatedComponent { + + constructor(props) { + super(props); + this.state = { + openedMenu: null + }; + + this._close = this._close.bind(this); + this.shipOrder = Object.keys(Ships).sort(); + } + + _close() { + this.setState({ openedMenu: null }); + } + + _setInsurance(e) { + e.stopPropagation(); + Persist.setInsurance('Beta'); // TODO: get insurance name + } + + _setModuleDiscount(e) { + e.stopPropagation(); + Persist.setModuleDiscount(0); // TODO: get module discount + } + + _setShipDiscount(e) { + e.stopPropagation(); + Persist.setShipDiscount(0); // TODO: get ship discount + } + + _showDeleteAll(e) { + e.preventDefault(); + InterfaceEvents.showModal(); + this._close(); + }; + + _showBackup(e) { + e.preventDefault(); + /*$state.go('modal.export', { + title: 'backup', + data: Persist.getAll(), + description: 'PHRASE_BACKUP_DESC' + });*/ + // TODO: implement modal + }; + + _showDetailedExport(e){ + e.preventDefault(); + e.stopPropagation(); + + /*$state.go('modal.export', { + title: 'detailed export', + data: Serializer.toDetailedExport(Persist.getBuilds()), + description: 'PHRASE_EXPORT_DESC' + });*/ + // TODO: implement modal + } + + _setTextSize(size) { + Persist.setSizeRatio(size); // TODO: implement properly + } + + _resetTextSize() { + Persist.setSizeRatio(1); + } + + _openMenu(event, openedMenu) { + event.stopPropagation(); + + if (this.state.openedMenu == openedMenu) { + openedMenu = null; + } + + this.setState({ openedMenu }); + } + + _getShipsMenu() { + let shipList = []; + + for (let s in Ships) { + shipList.push({Ships[s].properties.name}); + } + + return ( +
    e.stopPropagation() }> + {shipList} +
    + ); + } + + _getBuildsMenu() { + let builds = Persist.getBuilds(); + let buildList = []; + for (let shipId of this.shipOrder) { + if (builds[shipId]) { + let shipBuilds = []; + let buildNameOrder = Object.keys(builds[shipId]).sort(); + for (let buildName of buildNameOrder) { + let href = ['/outfitting/', shipId, '/', builds[shipId][buildName], '?bn=', buildName].join(''); + shipBuilds.push(
  • {buildName}
  • ); + } + buildList.push(
      {Ships[shipId].properties.name}{shipBuilds}
    ); + } + } + + return ( +
    e.stopPropagation() }> +
    {buildList}
    +
    + ); + } + + _getComparisonsMenu() { + let comparisons; + let translate = this.context.language.translate; + + if (Persist.hasComparisons()) { + let comparisons = []; + let comps = Object.keys(Persist.getComparisons()).sort(); + console.log(comps); + + for (let name of comps) { + comparisons.push({name}); + } + } else { + comparisons = {translate('none created')}; + } + + return ( +
    e.stopPropagation() } style={{ whiteSpace: 'nowrap' }}> + {comparisons} +
    + {translate('compare all')} + {translate('create new')} +
    + ); + } + + _getSettingsMenu() { + let translate = this.context.language.translate; + + return ( +
    e.stopPropagation() }> +
      + {translate('language')} +
    • +

    +
      + {translate('insurance')} +
    • +

    +
      + {translate('ship')} {translate('discount')} +
    • +

    +
      + {translate('component')} {translate('discount')} +
    • +
    +
    + +
    + + + + + + + + + + + +
    AA
    +
    + {translate('about')} +
    + ); + } + + componentWillMount() { + this.closeAllListener = InterfaceEvents.addListener('closeAll', this._close); + } + + componentWillUnmount() { + this.closeAllListener.remove(); + } + + render() { + let translate = this.context.language.translate; + let openedMenu = this.state.openedMenu; + let hasBuilds = Persist.hasBuilds(); + + if (this.props.appCacheUpdate) { + return
    { 'PHRASE_UPDATE_RDY' | translate }
    ; + } + + return ( +
    + + +
    +
    this._openMenu(e,'s') } > + {' ' + translate('ships')} +
    + {openedMenu == 's' ? this._getShipsMenu() : null} +
    + +
    +
    this._openMenu(e,'b') : null }> + {' ' + translate('builds')} +
    + {openedMenu == 'b' ? this._getBuildsMenu() : null} +
    + +
    +
    this._openMenu(e,'comp') : null }> + {' ' + translate('compare')} +
    + {openedMenu == 'comp' ? this._getComparisonsMenu() : null} +
    + +
    +
    this._openMenu(e,'settings') }> + {translate('settings')} +
    + {openedMenu == 'settings' ? this._getSettingsMenu() : null} +
    +
    + ); + } + +} \ No newline at end of file diff --git a/src/app/components/InternalSlot.jsx b/src/app/components/InternalSlot.jsx new file mode 100644 index 00000000..dcf30582 --- /dev/null +++ b/src/app/components/InternalSlot.jsx @@ -0,0 +1,38 @@ +import React from 'react'; +import Slot from './Slot'; +import { Infinite } from './SvgIcons'; + +export default class InternalSlot extends Slot { + + getSlotDetails(m, translate, formats, u) { + if (m) { + let classRating = m.class + m.rating; + + return ( +
    +
    {classRating + ' ' + translate(m.name || m.grp)}
    +
    {m.mass || m.capacity || 0}{u.T}
    +
    + { m.optmass ?
    {translate('optimal mass') + ': '}{m.optmass}{u.T}
    : null } + { m.maxmass ?
    {translate('max mass') + ': '}{m.maxmass}{u.T}
    : null } + { m.bins ?
    {m.bins + ' '}{translate('bins')}
    : null } + { m.rate ?
    {translate('rate')}: {m.rate}{u.kgs}   {translate('refuel time')}: {formats.time(this.props.fuel * 1000 / m.rate)}
    : null } + { m.ammo ?
    {translate('ammo')}: {formats.gen(m.ammo)}
    : null } + { m.cells ?
    {translate('cells')}: {m.cells}
    : null } + { m.recharge ?
    {translate('recharge')}: {m.recharge} MJ   {translate('total')}: {m.cells * m.recharge}{u.MJ}
    : null } + { m.repair ?
    {translate('repair')}: {m.repair}
    : null } + { m.range ?
    {translate('range')} {m.range}{u.km}
    : null } + { m.time ?
    {translate('time')}: {formats.time(m.time)}
    : null } + { m.maximum ?
    {translate('max')}: {(m.maximum)}
    : null } + { m.rangeLS ?
    {m.rangeLS}{u.Ls}
    : null } + { m.rangeLS === null ?
    {u.Ls}
    : null } + { m.rangeRating ?
    {translate('range')}: {m.rangeRating}
    : null } + { m.armouradd ?
    +{m.armouradd} {translate('armour')}
    : null } +
    +
    + ); + } else { + return
    {translate('empty')}
    ; + } + } +} diff --git a/src/app/components/InternalSlotSection.jsx b/src/app/components/InternalSlotSection.jsx new file mode 100644 index 00000000..28b8c793 --- /dev/null +++ b/src/app/components/InternalSlotSection.jsx @@ -0,0 +1,69 @@ +import React from 'react'; +import SlotSection from './SlotSection'; +import InternalSlot from './InternalSlot'; +import cn from 'classnames'; + + +export default class InternalSlotSection extends SlotSection { + + constructor(props, context) { + super(props, context, 'internal', 'internal compartments'); + + this._empty = this._empty.bind(this); + this._fillWithCargo = this._fillWithCargo.bind(this); + this._fillWithCells = this._fillWithCells.bind(this); + this._fillWithArmor = this._fillWithArmor.bind(this); + } + + _empty() { + + } + + _fillWithCargo() { + + } + + _fillWithCells() { + + } + + _fillWithArmor() { + + } + + _getSlots() { + let slots = []; + let {internal, fuelCapacity, ladenMass } = this.props.ship; + let availableModules = this.props.ship.getAvailableModules(); + let currentMenu = this.state.currentMenu; + + for (let i = 0, l = internal.length; i < l; i++) { + let s = internal[i]; + slots.push(); + } + + return slots; + } + + _getSectionMenu(translate) { + return
    e.stopPropagation()}> +
      +
    • {translate('empty all')}
    • +
    • {translate('cargo')}
    • +
    • {translate('scb')}
    • +
    • {translate('hr')}
    • +
    +
    ; + } + +} diff --git a/src/app/components/Link.jsx b/src/app/components/Link.jsx new file mode 100644 index 00000000..fdf73cb9 --- /dev/null +++ b/src/app/components/Link.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import Router from '../Router'; +import shallowEqual from '../utils/shallowEqual'; + +export default class Link extends React.Component { + + shouldComponentUpdate(nextProps) { + return !shallowEqual(this.props, nextProps); + } + + handler = (event) => { + if (event.getModifierState + && ( event.getModifierState('Shift') + || event.getModifierState('Alt') + || event.getModifierState('Control') + || event.getModifierState('Meta') + || event.button > 1)) { + return; + } + event.nativeEvent && event.preventDefault && event.preventDefault(); + + if (this.props.href) { + Router.go(encodeURI(this.props.href)); + } + } + + render() { + return {this.props.children} + } + +} \ No newline at end of file diff --git a/src/app/components/ModalDeleteAll.jsx b/src/app/components/ModalDeleteAll.jsx new file mode 100644 index 00000000..1bfda0d6 --- /dev/null +++ b/src/app/components/ModalDeleteAll.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import TranslatedComponent from './TranslatedComponent'; +import InterfaceEvents from '../utils/InterfaceEvents'; + +export default class ModalDeleteAll extends TranslatedComponent { + + _deleteAll() { + Persist.deleteAll(); + InterfaceEvents.hideModal(); + } + + render() { + let translate = this.context.language.translate; + + return
    e.stopPropagation()}> +

    {translate('delete all')}

    +

    {translate('PHRASE_CONFIRMATION')}

    + + +
    ; + } +} diff --git a/src/app/components/ModalExport.jsx b/src/app/components/ModalExport.jsx new file mode 100644 index 00000000..db966748 --- /dev/null +++ b/src/app/components/ModalExport.jsx @@ -0,0 +1,49 @@ +import React from 'react'; +import TranslatedComponent from './TranslatedComponent'; +import InterfaceEvents from '../utils/InterfaceEvents'; + +export default class DeleteAllModal extends TranslatedComponent { + + static propTypes = { + title: React.propTypes.string, + promise: : React.propTypes.func, + data: React.propTypes.oneOfType[React.propTypes.string, React.propTypes.object] + }; + + constructor(props) { + super(props); + let exportJson; + + if (props.promise) { + exportJson = 'Generating...'; + } else if(typeof props.data == 'string') { + exportJson = props.data; + } else { + exportJson = JSON.stringify(this.props.data); + } + + this.state = { exportJson }; + } + + componentWillMount(){ + // When promise is done update exportJson accordingly + } + + render() { + let translate = this.context.language.translate; + let description; + + if (this.props.description) { + description =
    {translate(this.props.description)}
    + } + + return
    e.stopPropagation() }> +

    {translate(this.props.title || 'Export')}

    + {description} +
    + +
    + +
    ; + } +} diff --git a/src/app/components/ModalImport.jsx b/src/app/components/ModalImport.jsx new file mode 100644 index 00000000..0b974acb --- /dev/null +++ b/src/app/components/ModalImport.jsx @@ -0,0 +1,439 @@ +import React from 'react'; +import cn from 'classnames'; +import TranslatedComponent from './TranslatedComponent'; +import InterfaceEvents from '../utils/InterfaceEvents'; +import Persist from '../stores/Persist'; +import Ships from '../shipyard/Ships'; +import Ship from '../shipyard/Ship'; +import * as ModuleUtils from '../shipyard/ModuleUtils'; +import { Download } from './SvgIcons'; + + +const textBuildRegex = new RegExp('^\\[([\\w \\-]+)\\]\n'); +const lineRegex = new RegExp('^([\\dA-Z]{1,2}): (\\d)([A-I])[/]?([FGT])?([SD])? ([\\w\\- ]+)'); +const mountMap = { 'H': 4, 'L': 3, 'M': 2, 'S': 1, 'U': 0 }; +const standardMap = { 'RB': 0, 'TM': 1, 'FH': 2, 'EC': 3, 'PC': 4, 'SS': 5, 'FS': 6 }; +const bhMap = { 'lightweight alloy': 0, 'reinforced alloy': 1, 'military grade composite': 2, 'mirrored surface composite': 3, 'reactive surface composite': 4 }; + +function isEmptySlot(slot) { + return slot.maxClass == this && slot.m === null; +} + +function equalsIgnoreCase(str) { + return str.toLowerCase() == this.toLowerCase(); +} + +function validateBuild(shipId, code, name) { + let shipData = Ships[shipId]; + + if (!shipData) { + throw '"' + shipId + '" is not a valid Ship Id!'; + } + if (typeof name != 'string' || name.length == 0) { + throw shipData.properties.name + ' build "' + name + '" must be a string at least 1 character 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) { + let ship; + if (!detailedBuild.name) { + throw 'Build Name missing!'; + } + + if (!detailedBuild.name.trim()) { + throw 'Build Name must be a string at least 1 character long!'; + } + + try { + 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) }; +} + +export default class ModalImport extends TranslatedComponent { + + static propTypes = { + title: React.propTypes.string, + promise: : React.propTypes.func, + data: React.propTypes.oneOfType[React.propTypes.string, React.propTypes.object] + }; + + constructor(props) { + super(props); + + this.state = { + builds: null, + canEdit: true, + comparisons: null, + discounts: null, + errorMsg: null, + importString: null, + importValid: false, + insurance: null + }; + + this._process = this._process.bind(this); + this._importBackup = this._importBackup.bind(this); + } + + _importBackup(importData) { + if (importData.builds && typeof importData.builds == 'object') { + for (let shipId in importData.builds) { + for (let buildName in importData.builds[shipId]) { + validateBuild(shipId, importData.builds[shipId][buildName], buildName); + } + } + this.setState({ builds: importData.builds }); + } else { + throw 'builds must be an object!'; + } + if (importData.comparisons) { + for (let compName in importData.comparisons) { + let comparison = importData.comparisons[compName]; + for (let i = 0, l = comparison.builds.length; i < l; i++) { + let build = comparison.builds[i]; + if (!importData.builds[build.shipId] || !importData.builds[build.shipId][build.buildName]) { + throw build.shipId + ' build "' + build.buildName + '" data is missing!'; + } + } + } + this.setState({ comparisons: importData.comparisons }); + } + if (importData.discounts instanceof Array && importData.discounts.length == 2) { + this.setState({ discounts: importData.discounts }); + } + if (typeof importData.insurance == 'string' && importData.insurance.length > 3) { + this.setState({ insurance: importData.insurance }); + } + } + + _importDetailedArray(importArr) { + let builds = {}; + for (let i = 0, l = importArr.length; i < l; i++) { + let build = this._detailedJsonToBuild(importArr[i]); + if (!builds[build.shipId]) { + builds[build.shipId] = {}; + } + builds[build.shipId][build.name] = build.code; + } + this.setState({ builds }); + } + + _importTextBuild(buildStr) { + let buildName = textBuildRegex.exec(buildStr)[1].trim(); + let shipName = buildName.toLowerCase(); + let shipId = null; + + for (let sId in Ships) { + if (Ships[sId].properties.name.toLowerCase() == shipName) { + shipId = sId; + break; + } + } + + if (!shipId) { + throw 'No such ship found: "' + buildName + '"'; + } + + let lines = buildStr.split('\n'); + let ship = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots); + ship.buildWith(null); + + for (let i = 1; i < lines.length; i++) { + let line = lines[i].trim(); + + if (!line) { continue; } + if (line.substring(0, 3) == '---') { break; } + + let parts = lineRegex.exec(line); + + if (!parts) { throw 'Error parsing: "' + line + '"'; } + + let typeSize = parts[1]; + let cl = parts[2]; + let rating = parts[3]; + let mount = parts[4]; + let missile = parts[5]; + let name = parts[6].trim(); + let slot, group; + + if (isNaN(typeSize)) { // Standard or Hardpoint + if (typeSize.length == 1) { // Hardpoint + let slotClass = mountMap[typeSize]; + + if (cl > slotClass) { throw cl + rating + ' ' + name + ' exceeds slot size: "' + line + '"'; } + + slot = _.find(ship.hardpoints, isEmptySlot, slotClass); + + if (!slot) { throw 'No hardpoint slot available for: "' + line + '"'; } + + group = _.find(GroupMap, equalsIgnoreCase, name); + + let hp = ModuleUtils.findHardpoint(group, cl, rating, group ? null : name, mount, missile); + + if (!hp) { throw 'Unknown component: "' + line + '"'; } + + ship.use(slot, hp, true); + + } else if (typeSize == 'BH') { + let bhId = bhMap[name.toLowerCase()]; + + if (bhId === undefined) { throw 'Unknown bulkhead: "' + line + '"'; } + + ship.useBulkhead(bhId, true); + + } else if (standardMap[typeSize] != undefined) { + let standardIndex = standardMap[typeSize]; + + if (ship.standard[standardIndex].maxClass < cl) { throw name + ' exceeds max class for the ' + ship.name; } + + ship.use(ship.standard[standardIndex], cl + rating, ModuleUtils.standard(standardIndex, cl + rating), true); + + } else { + throw 'Unknown component: "' + line + '"'; + } + } else { + if (cl > typeSize) { throw cl + rating + ' ' + name + ' exceeds slot size: "' + line + '"'; } + + slot = _.find(ship.internal, isEmptySlot, typeSize); + + if (!slot) { throw 'No internal slot available for: "' + line + '"'; } + + group = _.find(GroupMap, equalsIgnoreCase, name); + + let intComp = ModuleUtils.findInternal(group, cl, rating, group ? null : name); + + if (!intComp) { throw 'Unknown component: "' + line + '"'; } + + ship.use(slot, intComp.id, intComp); + } + } + + let builds = {}; + builds[shipId] = {}; + builds[shipId]['Imported ' + buildName] = Serializer.fromShip(ship); + this.setState({ builds }); + } + + _validateImport() { + let importData = null; + let importString = $scope.importString.trim(); + this.setState({ + builds: null, + comparisons: null, + discounts: null, + errorMsg: null, + importValid: false, + insurance: null + }); + + if (!importString) { + return; + } + + try { + if (textBuildRegex.test(importString)) { // E:D Shipyard build text + importTextBuild(importString); + } else { // JSON Build data + importData = angular.fromJson($scope.importString); + + if (!importData || typeof importData != 'object') { + throw 'Must be an object or array!'; + } + + if (importData instanceof Array) { // Must be detailed export json + this._importDetailedArray(importData); + } else if (importData.ship && typeof importData.name !== undefined) { // Using JSON from a single ship build export + this._importDetailedArray([importData]); // Convert to array with singleobject + } else { // Using Backup JSON + this._importBackup(importData); + } + } + } catch (e) { + this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' }); + return; + } + + this.setState({ importValid: true }); + }; + + _process() { + let builds = null, comparisons = null; + + if (this.state.builds) { + builds = $scope.builds; + for (let shipId in builds) { + for (let buildName in builds[shipId]) { + let code = builds[shipId][buildName]; + // Update builds object such that orginal name retained, but can be renamed + builds[shipId][buildName] = { + code: code, + useName: buildName + }; + } + } + } + + if (this.state.comparisons) { + let comparisons = $scope.comparisons; + for (let name in comparisons) { + comparisons[name].useName = name; + } + } + + this.setState({ processed: true, builds, comparisons }); + }; + + _import() { + + if (this.state.builds) { + let builds = this.state.builds; + for (let shipId in builds) { + for (let buildName in builds[shipId]) { + let build = builds[shipId][buildName]; + let name = build.useName.trim(); + if (name) { + Persist.saveBuild(shipId, name, build.code); + } + } + } + } + + if (this.state.comparisons) { + let comparisons = this.state.comparisons; + for (let comp in comparisons) { + let comparison = comparisons[comp]; + let useName = comparison.useName.trim(); + if (useName) { + Persist.saveComparison(useName, comparison.builds, comparison.facets); + } + } + } + + if (this.state.discounts) { + Persist.setDiscount((this.state.discounts); + } + + if (this.state.insurance) { + Persist.setInsurance(this.state.insurance); + } + + InterfaceEvents.hideModal(); + }; + + componentWillMount() { + if (this.props.importingBuilds) { + this.setState({ builds: this.props.importingBuilds, canEdit : false}); + this._process(); + } + } + + render() { + let translate = this.context.language.translate; + let importStage; + + if (this.state.processed) { + importStage = ( +
    + +