mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 14:45:35 +00:00
Compare commits
161 Commits
feature/wa
...
special-ke
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
934593fe2a | ||
|
|
e4a826592f | ||
|
|
cee4c32551 | ||
|
|
081d8fb86a | ||
|
|
3dfd563d90 | ||
|
|
fd08cd219c | ||
|
|
6a15326d31 | ||
|
|
608ecc51b7 | ||
|
|
fcef26ebbb | ||
|
|
ba6d758ed5 | ||
|
|
43aa3e4e79 | ||
|
|
18f0e060a7 | ||
|
|
c7547e8baf | ||
|
|
ffff242abe | ||
|
|
b44c66b986 | ||
|
|
ae77ec6256 | ||
|
|
4f1e32b154 | ||
|
|
af37c2bfc5 | ||
|
|
5d4ab6f2ad | ||
|
|
0c9db53057 | ||
|
|
b689605ac2 | ||
|
|
baab91e371 | ||
|
|
70e69c7099 | ||
|
|
f4534fd3eb | ||
|
|
93594e1a65 | ||
|
|
b5e449ea54 | ||
|
|
0ff4b849aa | ||
|
|
b99e38043f | ||
|
|
28e3a59473 | ||
|
|
b20290fb60 | ||
|
|
2734beb6f8 | ||
|
|
345eec528c | ||
|
|
7a17e18a76 | ||
|
|
4697677457 | ||
|
|
7d8a5a1368 | ||
|
|
dd7402bd0e | ||
|
|
65592b0fc6 | ||
|
|
0ab66023a6 | ||
|
|
d6fad098ee | ||
|
|
1b5730d337 | ||
|
|
439b615b1b | ||
|
|
a8b30594dc | ||
|
|
9b6b1d328c | ||
|
|
ac2e2e4d69 | ||
|
|
3a5fb31860 | ||
|
|
c610eb8627 | ||
|
|
94980270c4 | ||
|
|
c685e002e3 | ||
|
|
1f665eed9e | ||
|
|
0c4fc1fd9a | ||
|
|
0fc033363e | ||
|
|
fb6e9538bc | ||
|
|
95b98fc4ed | ||
|
|
1840dafed0 | ||
|
|
1ad82b116c | ||
|
|
7bdd17504b | ||
|
|
2d820bb5d5 | ||
|
|
2e14512ed8 | ||
|
|
48ed583c6d | ||
|
|
dd444a17f3 | ||
|
|
2ea63c711e | ||
|
|
6d6d31db25 | ||
|
|
e9273dcb9b | ||
|
|
2bdc4562c6 | ||
|
|
9e8a5323e9 | ||
|
|
8e001063b3 | ||
|
|
dc88fab4c5 | ||
|
|
dfca917e50 | ||
|
|
ef7dfd6ca1 | ||
|
|
435c1b6d45 | ||
|
|
c5c9abe588 | ||
|
|
363735d36b | ||
|
|
2741e7701b | ||
|
|
3be442ea60 | ||
|
|
34cbeca201 | ||
|
|
b37e73ead6 | ||
|
|
ee775521d6 | ||
|
|
5f84aaef1b | ||
|
|
99ac58d999 | ||
|
|
f128a1e87d | ||
|
|
8c0768b451 | ||
|
|
319307136c | ||
|
|
a498452943 | ||
|
|
4b854b8305 | ||
|
|
b400db8216 | ||
|
|
fb811faf5e | ||
|
|
deeb525433 | ||
|
|
1cb88115f6 | ||
|
|
a181791500 | ||
|
|
94eec120da | ||
|
|
48092d4395 | ||
|
|
2457c30b94 | ||
|
|
593f069806 | ||
|
|
a073692632 | ||
|
|
7752d5c9db | ||
|
|
544e5acaef | ||
|
|
9ab35bbaf9 | ||
|
|
98782da200 | ||
|
|
2936364934 | ||
|
|
01e1609a9f | ||
|
|
f85a03a9ae | ||
|
|
2703c2aa23 | ||
|
|
954921c231 | ||
|
|
8bed35a8ba | ||
|
|
9f4ae60577 | ||
|
|
ee3c50e27d | ||
|
|
03020743b3 | ||
|
|
001fed67b7 | ||
|
|
3894915740 | ||
|
|
68fd13e8dc | ||
|
|
fdf16cd959 | ||
|
|
d916c67fe0 | ||
|
|
d8a8e224f4 | ||
|
|
e1c115747c | ||
|
|
e9b6d71606 | ||
|
|
e03e249d2f | ||
|
|
0cfb0b6878 | ||
|
|
600df162aa | ||
|
|
94141aa3c5 | ||
|
|
aca90d7077 | ||
|
|
a66fa8e83f | ||
|
|
194db07057 | ||
|
|
307886d4ae | ||
|
|
bbba048129 | ||
|
|
222173b388 | ||
|
|
ec0d05e081 | ||
|
|
b3be0bd639 | ||
|
|
529d80682c | ||
|
|
934de01803 | ||
|
|
3367580d78 | ||
|
|
fbf59219d0 | ||
|
|
77401a3b3f | ||
|
|
7c587c29aa | ||
|
|
2295dccd82 | ||
|
|
cc4ad6d132 | ||
|
|
8a2d27290a | ||
|
|
eda61a8e06 | ||
|
|
3987c4e681 | ||
|
|
71b90eb6f4 | ||
|
|
4e891f382c | ||
|
|
3d3f9e44b5 | ||
|
|
dd7a133caa | ||
|
|
ae247c4812 | ||
|
|
6c932f96a6 | ||
|
|
0ea25692d3 | ||
|
|
e129e1da39 | ||
|
|
8acd32b0fc | ||
|
|
8e5dd9fb8d | ||
|
|
97ce2828e0 | ||
|
|
f8f99a5aaa | ||
|
|
70cfa58896 | ||
|
|
6e79ced51e | ||
|
|
56571f9c1f | ||
|
|
0b10cac85c | ||
|
|
34c04a6354 | ||
|
|
4e337c4ca1 | ||
|
|
5048b7e094 | ||
|
|
27fbc1ad66 | ||
|
|
4ab376d9ed | ||
|
|
2858ef3e93 | ||
|
|
3215b3942d |
27
.babelrc
27
.babelrc
@@ -1,29 +1,19 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"modules": "commonjs"
|
||||
}
|
||||
],
|
||||
["@babel/preset-env", {"modules": "commonjs"}],
|
||||
"@babel/preset-react"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-syntax-dynamic-import",
|
||||
"@babel/plugin-syntax-import-meta",
|
||||
["@babel/plugin-proposal-class-properties", { "loose": true }],
|
||||
"@babel/plugin-proposal-json-strings",
|
||||
[
|
||||
"@babel/plugin-proposal-decorators",
|
||||
{
|
||||
"legacy": true
|
||||
}
|
||||
],
|
||||
"@babel/plugin-syntax-dynamic-import",
|
||||
"@babel/plugin-syntax-import-meta",
|
||||
[
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
{
|
||||
"loose": true
|
||||
}
|
||||
],
|
||||
"@babel/plugin-proposal-json-strings",
|
||||
"@babel/plugin-proposal-function-sent",
|
||||
"@babel/plugin-proposal-export-namespace-from",
|
||||
"@babel/plugin-proposal-numeric-separator",
|
||||
@@ -37,13 +27,6 @@
|
||||
"proposal": "minimal"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@babel/plugin-transform-runtime",
|
||||
{
|
||||
"helpers": true,
|
||||
"regenerator": true
|
||||
}
|
||||
],
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
"@babel/plugin-proposal-do-expressions",
|
||||
"@babel/plugin-proposal-function-bind"
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
@@ -1,45 +0,0 @@
|
||||
worker_processes 1;
|
||||
user nobody nobody;
|
||||
error_log /tmp/error.log;
|
||||
pid /tmp/nginx.pid;
|
||||
|
||||
events {
|
||||
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
sendfile on;
|
||||
client_body_temp_path /tmp/client_body;
|
||||
fastcgi_temp_path /tmp/fastcgi_temp;
|
||||
proxy_temp_path /tmp/proxy_temp;
|
||||
scgi_temp_path /tmp/scgi_temp;
|
||||
uwsgi_temp_path /tmp/uwsgi_temp;
|
||||
access_log /tmp/access.log;
|
||||
error_log /tmp/error.log;
|
||||
|
||||
keepalive_timeout 3000;
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
index index.html;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
autoindex on;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
77
.dockerignore
Normal file
77
.dockerignore
Normal file
@@ -0,0 +1,77 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
### Node template
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"title": "Coriolis",
|
||||
"description": "Coriolis Shipyard for Elite Dangerous",
|
||||
"repository": "https://github.com/EDCD/coriolis",
|
||||
"site": "https://coriolis.edcd.io",
|
||||
"site": "https://coriolis.io",
|
||||
"author": "https://github.com/edcd",
|
||||
"image": "./src/images/logo/192x192.png"
|
||||
}
|
||||
@@ -81,7 +81,7 @@
|
||||
"title": "Coriolis",
|
||||
"description": "Coriolis Shipyard for Elite Dangerous",
|
||||
"repository": "https://github.com/EDCD/coriolis",
|
||||
"site": "https://coriolis.edcd.io",
|
||||
"site": "https://coriolis.io",
|
||||
"author": "https://github.com/edcd",
|
||||
"image": "./src/images/logo/192x192.png"
|
||||
}
|
||||
|
||||
13
.gitlab-ci.yml
Normal file
13
.gitlab-ci.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
image: docker:stable
|
||||
services:
|
||||
- docker:dind
|
||||
|
||||
stages:
|
||||
- Build image
|
||||
|
||||
docker build:
|
||||
stage: Build image
|
||||
script:
|
||||
- img build --build-arg branch=$CI_COMMIT_REF_NAME -t edcd/coriolis:$CI_COMMIT_REF_NAME .
|
||||
- echo "$REGISTRY_PASSWORD" | img login --username "$REGISTRY_USER" --password-stdin
|
||||
- img push edcd/coriolis:$CI_COMMIT_REF_NAME
|
||||
@@ -6,29 +6,29 @@ WORKDIR /src/app
|
||||
RUN mkdir -p /src/app/coriolis
|
||||
RUN mkdir -p /src/app/coriolis-data
|
||||
|
||||
COPY ./coriolis/ /src/app/coriolis
|
||||
COPY ./coriolis-data/ /src/app/coriolis-data
|
||||
RUN apk add --update git
|
||||
|
||||
RUN apk update
|
||||
RUN apk add git
|
||||
COPY . /src/app/coriolis
|
||||
|
||||
RUN npm i -g npm
|
||||
|
||||
# Set up coriolis-data
|
||||
WORKDIR /src/app/coriolis-data
|
||||
RUN git fetch --all
|
||||
RUN git clone https://github.com/EDCD/coriolis-data.git .
|
||||
RUN git checkout ${BRANCH}
|
||||
RUN npm install --no-package-lock
|
||||
RUN npm start
|
||||
|
||||
# Set up coriolis
|
||||
WORKDIR /src/app/coriolis
|
||||
RUN git fetch --all
|
||||
RUN git checkout ${BRANCH}
|
||||
RUN npm install --no-package-lock
|
||||
RUN npm run build
|
||||
|
||||
|
||||
### STAGE 2: Production Environment ###
|
||||
FROM nginx:1.13.12-alpine as web
|
||||
COPY coriolis/.docker/nginx.conf /etc/nginx/nginx.conf
|
||||
FROM fholzer/nginx-brotli as web
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
COPY --from=builder /src/app/coriolis/build /usr/share/nginx/html
|
||||
WORKDIR /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
24
LICENSE.md
Normal file
24
LICENSE.md
Normal file
@@ -0,0 +1,24 @@
|
||||
All Data and [associated JSON](https://github.com/EDCD/coriolis-data) files are intellectual property and copyright of Frontier Developments plc ('Frontier', 'Frontier Developments') and are subject to their
|
||||
[terms and conditions](https://www.frontierstore.net/terms-and-conditions/).
|
||||
|
||||
The code (Javascript, CSS, HTML, and SVG files only) specificially for Coriolis.io is released under the MIT License.
|
||||
|
||||
Copyright (c) 2015 Coriolis.io, Colin McLeod
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software (Javascript, CSS, HTML, and SVG files only), and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -29,6 +29,8 @@ Also see [the documentation site.](https://coriolis.willb.info/)
|
||||
|
||||
See the [Data wiki](https://github.com/cmmcleod/coriolis-data/wiki) for details on structure, etc.
|
||||
|
||||
You can find hosted and compiled versions of these data-jsons under https://coriolis.io/data/ and https://beta.coriolis.io/data/.
|
||||
You might want to load these as depedency instead of reyling on the npm-dependency.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@ version: '2.2'
|
||||
services:
|
||||
coriolis_prod:
|
||||
image: edcd/coriolis:master
|
||||
build:
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
branch: master
|
||||
restart: always
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||
@@ -17,6 +21,10 @@ services:
|
||||
|
||||
coriolis_dev:
|
||||
image: edcd/coriolis:develop
|
||||
build:
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
branch: develop
|
||||
restart: always
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||
@@ -29,6 +37,20 @@ services:
|
||||
- "traefik.basic.port=80"
|
||||
- "traefik.basic.protocol=http"
|
||||
|
||||
coriolis_dw2:
|
||||
image: edcd/coriolis:dw2
|
||||
restart: always
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||
networks:
|
||||
- web
|
||||
labels:
|
||||
- "traefik.docker.network=web"
|
||||
- "traefik.enable=true"
|
||||
- "traefik.basic.frontend.rule=Host:dw2.coriolis.io"
|
||||
- "traefik.basic.port=80"
|
||||
- "traefik.basic.protocol=http"
|
||||
|
||||
networks:
|
||||
web:
|
||||
external: true
|
||||
101
nginx.conf
101
nginx.conf
@@ -1,48 +1,84 @@
|
||||
worker_processes 2;
|
||||
error_log ./nginx.error.log;
|
||||
worker_rlimit_nofile 8192;
|
||||
pid nginx.pid;
|
||||
worker_processes 1;
|
||||
user nobody nobody;
|
||||
error_log /tmp/error.log;
|
||||
pid /tmp/nginx.pid;
|
||||
|
||||
events {
|
||||
|
||||
worker_connections 1024;
|
||||
multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
|
||||
access_log off;
|
||||
charset UTF-8;
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
sendfile on;
|
||||
client_body_temp_path /tmp/client_body;
|
||||
fastcgi_temp_path /tmp/fastcgi_temp;
|
||||
proxy_temp_path /tmp/proxy_temp;
|
||||
scgi_temp_path /tmp/scgi_temp;
|
||||
uwsgi_temp_path /tmp/uwsgi_temp;
|
||||
access_log /tmp/access.log;
|
||||
error_log /tmp/error.log;
|
||||
|
||||
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;
|
||||
}
|
||||
# https://nginx.org/en/docs/http/ngx_http_gzip_module.html
|
||||
# Enable gzip compression.
|
||||
# Default: off
|
||||
gzip off;
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
# Compression level (1-9).
|
||||
# 5 is a perfect compromise between size and CPU usage, offering about
|
||||
# 75% reduction for most ASCII files (almost identical to level 9).
|
||||
# Default: 1
|
||||
gzip_comp_level 5;
|
||||
|
||||
# Don't compress anything that's already small and unlikely to shrink much
|
||||
# if at all (the default is 20 bytes, which is bad as that usually leads to
|
||||
# larger files after gzipping).
|
||||
# Default: 20
|
||||
gzip_min_length 256;
|
||||
|
||||
# Compress data even for clients that are connecting to us via proxies,
|
||||
# identified by the "Via" header (required for CloudFront).
|
||||
# Default: off
|
||||
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;
|
||||
|
||||
# Tell proxies to cache both the gzipped and regular version of a resource
|
||||
# whenever the client's Accept-Encoding capabilities header varies;
|
||||
# Avoids the issue where a non-gzip capable client (which is extremely rare
|
||||
# today) would display gibberish if their proxy gave them the gzipped version.
|
||||
# Default: off
|
||||
gzip_vary on;
|
||||
|
||||
# Compress all output labeled with one of the following MIME-types.
|
||||
# text/html is always compressed by gzip module.
|
||||
# Default: text/html
|
||||
gzip_types *;
|
||||
brotli on;
|
||||
# brotli_static on;
|
||||
brotli_types *;
|
||||
# This should be turned on if you are going to have pre-compressed copies (.gz) of
|
||||
# static files available. If not it should be left off as it will cause extra I/O
|
||||
# for the check. It is best if you enable this in a location{} block for
|
||||
# a specific directory, or on an individual server{} level.
|
||||
# gzip_static on;
|
||||
keepalive_timeout 3000;
|
||||
server {
|
||||
listen 3301;
|
||||
server_name localhost;
|
||||
root ./build/;
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
index index.html;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
autoindex on;
|
||||
|
||||
location ~* \.(?:manifest|appcache|html?|xml|json|css|js|map|jpg|jpeg|gif|png|ico|svg|eot|ttf|woff|woff2)$ {
|
||||
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 /service-worker.js {
|
||||
expires -1;
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
add_header Access-Control-Allow-Credentials true;
|
||||
@@ -50,10 +86,11 @@ http {
|
||||
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;
|
||||
}
|
||||
location /iframe.html {
|
||||
try_files $uri $uri/ /iframe.html =404;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
19
package.json
19
package.json
@@ -5,7 +5,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/EDCD/coriolis"
|
||||
},
|
||||
"homepage": "https://coriolis.edcd.io",
|
||||
"homepage": "https://coriolis.io",
|
||||
"bugs": "https://github.com/EDCD/coriolis/issues",
|
||||
"private": true,
|
||||
"engine": "node >= 4.8.1",
|
||||
@@ -56,8 +56,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.1.0",
|
||||
"@babel/plugin-proposal-decorators": "^7.1.2",
|
||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
||||
"@babel/plugin-proposal-decorators": "^7.0.0",
|
||||
"@babel/plugin-proposal-do-expressions": "^7.0.0",
|
||||
"@babel/plugin-proposal-export-default-from": "^7.0.0",
|
||||
"@babel/plugin-proposal-export-namespace-from": "^7.0.0",
|
||||
@@ -72,12 +72,12 @@
|
||||
"@babel/plugin-proposal-throw-expressions": "^7.0.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||
"@babel/plugin-syntax-import-meta": "^7.0.0",
|
||||
"@babel/plugin-transform-runtime": "^7.1.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"appcache-webpack-plugin": "^1.4.0",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-jest": "^23.4.2",
|
||||
"babel-jest": "^23.6.0",
|
||||
"babel-loader": "^8.0.0",
|
||||
"copy-webpack-plugin": "^4.5.2",
|
||||
"create-react-class": "^15.6.3",
|
||||
@@ -118,14 +118,11 @@
|
||||
"webpack-cli": "^3.1.1",
|
||||
"webpack-dev-server": "^3.1.9",
|
||||
"webpack-notifier": "^1.6.0",
|
||||
"workbox-webpack-plugin": "^3.6.1",
|
||||
"worker-loader": "^2.0.0"
|
||||
"workbox-webpack-plugin": "^3.6.1"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.0.0",
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"@nozbe/watermelondb": "^0.6.2",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"browserify-zlib-next": "^1.0.1",
|
||||
"classnames": "^2.2.6",
|
||||
"coriolis-data": "../coriolis-data",
|
||||
@@ -133,13 +130,13 @@
|
||||
"detect-browser": "^3.0.1",
|
||||
"fbemitter": "^2.1.1",
|
||||
"lodash": "^4.17.11",
|
||||
"lokijs": "^1.5.5",
|
||||
"lz-string": "^1.4.4",
|
||||
"pako": "^1.0.6",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"react-extras": "^0.7.1",
|
||||
"react-fuzzy": "^0.5.2",
|
||||
"react-ga": "^2.5.3",
|
||||
"react-number-editor": "Athanasius/react-number-editor.git#miggy",
|
||||
"recharts": "^1.2.0",
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Router from './Router';
|
||||
import { register } from 'register-service-worker'
|
||||
import { register } from 'register-service-worker';
|
||||
import { EventEmitter } from 'fbemitter';
|
||||
import { getLanguage } from './i18n/Language';
|
||||
import Persist from './stores/Persist';
|
||||
|
||||
import Announcement from './components/Announcement';
|
||||
import Header from './components/Header';
|
||||
import Tooltip from './components/Tooltip';
|
||||
import ModalExport from './components/ModalExport';
|
||||
@@ -14,7 +15,6 @@ import ModalImport from './components/ModalImport';
|
||||
import ModalPermalink from './components/ModalPermalink';
|
||||
import * as CompanionApiUtils from './utils/CompanionApiUtils';
|
||||
import * as JournalUtils from './utils/JournalUtils';
|
||||
|
||||
import AboutPage from './pages/AboutPage';
|
||||
import NotFoundPage from './pages/NotFoundPage';
|
||||
import OutfittingPage from './pages/OutfittingPage';
|
||||
@@ -72,7 +72,7 @@ export default class Coriolis extends React.Component {
|
||||
route: {},
|
||||
sizeRatio: Persist.getSizeRatio()
|
||||
};
|
||||
this._getAnnouncements()
|
||||
this._getAnnouncements();
|
||||
Router('', (r) => this._setPage(ShipyardPage, r));
|
||||
Router('/import?', (r) => this._importBuild(r));
|
||||
Router('/import/:data', (r) => this._importBuild(r));
|
||||
@@ -105,18 +105,20 @@ export default class Coriolis extends React.Component {
|
||||
}
|
||||
r.params.ship = ship.id;
|
||||
r.params.code = ship.toString();
|
||||
this._setPage(OutfittingPage, r);
|
||||
this._setPage(OutfittingPage, r)
|
||||
} catch (err) {
|
||||
this._onError('Failed to import ship', r.path, 0, 0, err);
|
||||
}
|
||||
}
|
||||
|
||||
_getAnnouncements() {
|
||||
return request.get('https://orbis.zone/api/announcement')
|
||||
.query({showInCoriolis: true})
|
||||
.then(announces => {
|
||||
this.setState({ announcements: announces.body })
|
||||
})
|
||||
async _getAnnouncements() {
|
||||
try {
|
||||
const announces = await request.get('https://orbis.zone/api/announcement')
|
||||
.query({ showInCoriolis: true });
|
||||
this.setState({ announcements: announces.body });
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,27 +353,27 @@ export default class Coriolis extends React.Component {
|
||||
const self = this;
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
register('/service-worker.js', {
|
||||
ready (registration) {
|
||||
console.log('Service worker is active.')
|
||||
ready(registration) {
|
||||
console.log('Service worker is active.');
|
||||
},
|
||||
registered (registration) {
|
||||
console.log('Service worker has been registered.')
|
||||
registered(registration) {
|
||||
console.log('Service worker has been registered.');
|
||||
},
|
||||
cached (registration) {
|
||||
console.log('Content has been cached for offline use.')
|
||||
cached(registration) {
|
||||
console.log('Content has been cached for offline use.');
|
||||
},
|
||||
updatefound (registration) {
|
||||
console.log('New content is downloading.')
|
||||
updatefound(registration) {
|
||||
console.log('New content is downloading.');
|
||||
},
|
||||
updated (registration) {
|
||||
updated(registration) {
|
||||
self.setState({ appCacheUpdate: true });
|
||||
console.log('New content is available; please refresh.')
|
||||
console.log('New content is available; please refresh.');
|
||||
},
|
||||
offline () {
|
||||
console.log('No internet connection found. App is running in offline mode.')
|
||||
offline() {
|
||||
console.log('No internet connection found. App is running in offline mode.');
|
||||
},
|
||||
error (error) {
|
||||
console.error('Error during service worker registration:', error)
|
||||
error(error) {
|
||||
console.error('Error during service worker registration:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -395,7 +397,10 @@ export default class Coriolis extends React.Component {
|
||||
|
||||
return <div style={{ minHeight: '100%' }} onClick={this._closeMenu}
|
||||
className={this.state.noTouch ? 'no-touch' : null}>
|
||||
<Header announcements={this.state.announcements} appCacheUpdate={this.state.appCacheUpdate} currentMenu={currentMenu}/>
|
||||
<Header announcements={this.state.announcements} appCacheUpdate={this.state.appCacheUpdate}
|
||||
currentMenu={currentMenu}/>
|
||||
<div className="announcement-container">{this.state.announcements.map(a => <Announcement
|
||||
text={a.message}/>)}</div>
|
||||
{this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) :
|
||||
<NotFoundPage/>}
|
||||
{this.state.modal}
|
||||
@@ -407,7 +412,8 @@ export default class Coriolis extends React.Component {
|
||||
<br/>
|
||||
<a
|
||||
href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'}
|
||||
target="_blank" rel="noopener noreferrer" title={'Coriolis Commits since' + window.CORIOLIS_DATE}>Commits since last release
|
||||
target="_blank" rel="noopener noreferrer" title={'Coriolis Commits since' + window.CORIOLIS_DATE}>Commits
|
||||
since last release
|
||||
({window.CORIOLIS_DATE})</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import Persist from './stores/Persist';
|
||||
import ReactGA from 'react-ga';
|
||||
ReactGA.initialize('UA-55840909-18');
|
||||
|
||||
let standalone = undefined;
|
||||
|
||||
/**
|
||||
@@ -258,16 +257,8 @@ Route.prototype.match = function(path, params) {
|
||||
* @param {string} path Path to track
|
||||
*/
|
||||
function gaTrack(path) {
|
||||
const match = path.match(/\/outfit\/(.*)(\?code=.*)/);
|
||||
if (match) {
|
||||
if (match[1]) {
|
||||
ReactGA.ga('set', 'contentGroup1', match[1]);
|
||||
}
|
||||
if (match[2]) {
|
||||
ReactGA.ga('set', 'contentGroup2', match[2]);
|
||||
}
|
||||
}
|
||||
ReactGA.pageview(path);
|
||||
const _paq = window._paq || [];
|
||||
_paq.push(['trackPageView']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
export default class Ad extends Component {
|
||||
static propTypes = {
|
||||
client: PropTypes.string,
|
||||
slot: PropTypes.string,
|
||||
format: PropTypes.string,
|
||||
wrapperDivStyle: PropTypes.object
|
||||
};
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
// This code is ran when the component mounts
|
||||
componentDidMount() {
|
||||
|
||||
}
|
||||
|
||||
// an outer div for styling purposes
|
||||
// changed class to ClassName
|
||||
// changed style from string to an object
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={this.props.wrapperDivStyle}>
|
||||
<ins
|
||||
className="adsbygoogle"
|
||||
style={{ display: 'block' }}
|
||||
data-ad-client={this.props.client}
|
||||
data-ad-slot={this.props.slot}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ export default class Announcement extends React.Component {
|
||||
* @return {React.Component} A href element
|
||||
*/
|
||||
render() {
|
||||
return <p>{this.props.text}</p>;
|
||||
return <div className="announcement" >{this.props.text}</div>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import TranslatedComponent from './TranslatedComponent';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
import cn from 'classnames';
|
||||
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||
import FuzzySearch from 'react-fuzzy';
|
||||
|
||||
const PRESS_THRESHOLD = 500; // mouse/touch down threshold
|
||||
|
||||
@@ -72,7 +73,16 @@ const GRPCAT = {
|
||||
'gfsb': 'guardian',
|
||||
'gmrp': 'guardian',
|
||||
'gsc': 'guardian',
|
||||
'ghrp': 'guardian'
|
||||
'ghrp': 'guardian',
|
||||
|
||||
// Mining
|
||||
'scl': 'mining',
|
||||
'pwa': 'mining',
|
||||
'sdm': 'mining',
|
||||
|
||||
// Assists
|
||||
'dc': 'flight assists',
|
||||
'sua': 'flight assists',
|
||||
};
|
||||
// Order here is the order in which items will be shown in the modules menu
|
||||
const CATEGORIES = {
|
||||
@@ -87,9 +97,10 @@ const CATEGORIES = {
|
||||
'rf': ['rf'],
|
||||
'shields': ['sg', 'bsg', 'psg', 'scb'],
|
||||
'structural reinforcement': ['hr', 'mrp'],
|
||||
'dc': ['dc'],
|
||||
'flight assists': ['dc', 'sua'],
|
||||
|
||||
// Hardpoints
|
||||
'lasers': ['pl', 'ul', 'bl', 'ml'],
|
||||
'lasers': ['pl', 'ul', 'bl'],
|
||||
'projectiles': ['mc', 'c', 'fc', 'pa', 'rg'],
|
||||
'ordnance': ['mr', 'tp', 'nl'],
|
||||
// Utilities
|
||||
@@ -101,7 +112,9 @@ const CATEGORIES = {
|
||||
'experimental': ['axmc', 'axmr', 'rfl', 'tbrfl', 'tbsc', 'tbem', 'xs', 'sfn', 'rcpl', 'dtl', 'rsl', 'mahr',],
|
||||
|
||||
// Guardian
|
||||
'guardian': ['gpp', 'gpd', 'gpc', 'ggc', 'gsrp', 'gfsb', 'ghrp', 'gmrp', 'gsc']
|
||||
'guardian': ['gpp', 'gpd', 'gpc', 'ggc', 'gsrp', 'gfsb', 'ghrp', 'gmrp', 'gsc'],
|
||||
|
||||
'mining': ['ml', 'scl', 'pwa', 'sdm', 'abl'],
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -113,7 +126,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
diffDetails: PropTypes.func,
|
||||
m: PropTypes.object,
|
||||
shipMass: PropTypes.number,
|
||||
ship: PropTypes.object.isRequired,
|
||||
warning: PropTypes.func,
|
||||
firstSlotId: PropTypes.string,
|
||||
lastSlotId: PropTypes.string,
|
||||
@@ -121,10 +134,6 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
slotDiv: PropTypes.object
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
shipMass: 0
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
@@ -133,6 +142,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
this._hideDiff = this._hideDiff.bind(this);
|
||||
this._showSearch = this._showSearch.bind(this);
|
||||
this.state = this._initState(props, context);
|
||||
this.slotItems = [];// Array to hold <li> refs.
|
||||
}
|
||||
@@ -145,21 +155,21 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
*/
|
||||
_initState(props, context) {
|
||||
let translate = context.language.translate;
|
||||
let { m, warning, shipMass, onSelect, modules, firstSlotId, lastSlotId } = props;
|
||||
let { m, warning, onSelect, modules, ship } = props;
|
||||
let list, currentGroup;
|
||||
|
||||
let buildGroup = this._buildGroup.bind(
|
||||
this,
|
||||
ship,
|
||||
translate,
|
||||
m,
|
||||
warning,
|
||||
shipMass - (m && m.mass ? m.mass : 0),
|
||||
(m, event) => {
|
||||
this._hideDiff(event);
|
||||
onSelect(m);
|
||||
}
|
||||
);
|
||||
|
||||
let fuzzy = [];
|
||||
if (modules instanceof Array) {
|
||||
list = buildGroup(modules[0].grp, modules);
|
||||
} else {
|
||||
@@ -167,9 +177,11 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
// At present time slots with grouped options (Hardpoints and Internal) can be empty
|
||||
if (m) {
|
||||
let emptyId = 'empty';
|
||||
if(this.firstSlotId == null) this.firstSlotId = emptyId;
|
||||
if (this.firstSlotId == null) this.firstSlotId = emptyId;
|
||||
let keyDown = this._keyDown.bind(this, onSelect);
|
||||
list.push(<div className='empty-c upp' key={emptyId} data-id={emptyId} onClick={onSelect.bind(null, null)} onKeyDown={keyDown} tabIndex="0" ref={slotItem => this.slotItems[emptyId] = slotItem} >{translate('empty')}</div>);
|
||||
list.push(<div className='empty-c upp' key={emptyId} data-id={emptyId} onClick={onSelect.bind(null, null)}
|
||||
onKeyDown={keyDown} tabIndex="0"
|
||||
ref={slotItem => this.slotItems[emptyId] = slotItem}>{translate('empty')}</div>);
|
||||
}
|
||||
|
||||
// Need to regroup the modules by our own categorisation
|
||||
@@ -197,7 +209,8 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
if (categories.length === 1) {
|
||||
// Show category header instead of group header
|
||||
if (m && grp == m.grp) {
|
||||
list.push(<div ref={(elem) => this.groupElem = elem} key={category} className={'select-category upp'}>{translate(category)}</div>);
|
||||
list.push(<div ref={(elem) => this.groupElem = elem} key={category}
|
||||
className={'select-category upp'}>{translate(category)}</div>);
|
||||
} else {
|
||||
list.push(<div key={category} className={'select-category upp'}>{translate(category)}</div>);
|
||||
}
|
||||
@@ -208,43 +221,56 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
categoryHeader = true;
|
||||
}
|
||||
if (m && grp == m.grp) {
|
||||
list.push(<div ref={(elem) => this.groupElem = elem} key={grp} className={'select-group cap'}>{translate(grp)}</div>);
|
||||
list.push(<div ref={(elem) => this.groupElem = elem} key={grp}
|
||||
className={'select-group cap'}>{translate(grp)}</div>);
|
||||
} else {
|
||||
list.push(<div key={grp} className={'select-group cap'}>{translate(grp)}</div>);
|
||||
}
|
||||
}
|
||||
list.push(buildGroup(grp, modules[grp]));
|
||||
for (const i of modules[grp]) {
|
||||
let mount = '';
|
||||
if (i.mount === 'F') {
|
||||
mount = 'Fixed';
|
||||
} else if (i.mount === 'G') {
|
||||
mount = 'Gimballed';
|
||||
} else if (i.mount === 'T') {
|
||||
mount = 'Turreted';
|
||||
}
|
||||
const fuzz = { grp, m: i, name: `${i.class}${i.rating}${mount ? ' ' + mount : ''} ${translate(grp)}` };
|
||||
fuzzy.push(fuzz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let trackingFocus = false;
|
||||
return { list, currentGroup, trackingFocus };
|
||||
return { list, currentGroup, fuzzy, trackingFocus };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate React Components for Module Group
|
||||
* @param {Ship} ship Ship the selection is for
|
||||
* @param {Function} translate Translate function
|
||||
* @param {Object} mountedModule Mounted Module
|
||||
* @param {Function} warningFunc Warning function
|
||||
* @param {number} mass Mass
|
||||
* @param {function} onSelect Select/Mount callback
|
||||
* @param {string} grp Group name
|
||||
* @param {Array} modules Available modules
|
||||
* @param {string} firstSlotId id of first slot item
|
||||
* @param {string} lastSlotId id of last slot item
|
||||
* @return {React.Component} Available Module Group contents
|
||||
*/
|
||||
_buildGroup(translate, mountedModule, warningFunc, mass, onSelect, grp, modules, firstSlotId, lastSlotId) {
|
||||
_buildGroup(ship, translate, mountedModule, warningFunc, onSelect, grp, modules) {
|
||||
let prevClass = null, prevRating = null, prevName;
|
||||
let elems = [];
|
||||
|
||||
const sortedModules = modules.sort(this._moduleOrder);
|
||||
|
||||
|
||||
// Calculate the number of items per class. Used so we don't have long lists with only a few items in each row
|
||||
const tmp = sortedModules.map((v, i) => v['class']).reduce((count, cls) => { count[cls] = ++count[cls] || 1; return count; }, {});
|
||||
const tmp = sortedModules.map((v, i) => v['class']).reduce((count, cls) => {
|
||||
count[cls] = ++count[cls] || 1;
|
||||
return count;
|
||||
}, {});
|
||||
const itemsPerClass = Math.max.apply(null, Object.keys(tmp).map(key => tmp[key]));
|
||||
|
||||
let itemsOnThisRow = 0;
|
||||
@@ -255,10 +281,11 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
prevName = m.name;
|
||||
if (ModuleUtils.isShieldGenerator(m.grp)) {
|
||||
// Shield generators care about maximum hull mass
|
||||
disabled = mass > m.maxmass;
|
||||
} else if (m.maxmass) {
|
||||
// Thrusters care about total mass
|
||||
disabled = mass + m.mass > m.maxmass;
|
||||
disabled = ship.hullMass > m.maxmass;
|
||||
// If the mounted module is experimental as well, we can replace it so
|
||||
// the maximum does not apply
|
||||
} else if (m.experimental && (!mountedModule || !mountedModule.experimental)) {
|
||||
disabled = 4 <= ship.hardpoints.filter(o => o.m && o.m.experimental).length;
|
||||
}
|
||||
let active = mountedModule && mountedModule.id === m.id;
|
||||
let classes = cn(m.name ? 'lc' : 'c', {
|
||||
@@ -297,22 +324,29 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
};
|
||||
}
|
||||
|
||||
switch(m.mount) {
|
||||
case 'F': mount = <MountFixed className={'lg'} />; break;
|
||||
case 'G': mount = <MountGimballed className={'lg'}/>; break;
|
||||
case 'T': mount = <MountTurret className={'lg'}/>; break;
|
||||
switch (m.mount) {
|
||||
case 'F':
|
||||
mount = <MountFixed className={'lg'}/>;
|
||||
break;
|
||||
case 'G':
|
||||
mount = <MountGimballed className={'lg'}/>;
|
||||
break;
|
||||
case 'T':
|
||||
mount = <MountTurret className={'lg'}/>;
|
||||
break;
|
||||
}
|
||||
if (m.name && m.name === prevName) {
|
||||
// elems.push(<br key={'b' + m.grp + i} />);
|
||||
itemsOnThisRow = 0;
|
||||
}
|
||||
if (itemsOnThisRow == 6 || i > 0 && sortedModules.length > 3 && itemsPerClass > 2 && m.class != prevClass && (m.rating != prevRating || m.mount)) {
|
||||
elems.push(<br key={'b' + m.grp + i} />);
|
||||
elems.push(<br key={'b' + m.grp + i}/>);
|
||||
itemsOnThisRow = 0;
|
||||
}
|
||||
let tbIdx = (classes.indexOf('disabled') < 0) ? 0 : undefined;
|
||||
elems.push(
|
||||
<li key={m.id} data-id={m.id} className={classes} {...eventHandlers} tabIndex={tbIdx} ref={slotItem => this.slotItems[m.id] = slotItem}>
|
||||
<li key={m.id} data-id={m.id} className={classes} {...eventHandlers} tabIndex={tbIdx}
|
||||
ref={slotItem => this.slotItems[m.id] = slotItem}>
|
||||
{mount}
|
||||
{(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')}
|
||||
</li>
|
||||
@@ -340,6 +374,40 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate tooltip content for the difference between the
|
||||
* mounted module and the hovered modules
|
||||
*/
|
||||
_showSearch() {
|
||||
if (this.props.modules instanceof Array) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<FuzzySearch
|
||||
list={this.state.fuzzy}
|
||||
keys={['grp', 'name']}
|
||||
tokenize={true}
|
||||
className={'input'}
|
||||
width={'100%'}
|
||||
style={{ padding: 0 }}
|
||||
onSelect={e => this.props.onSelect.bind(null, e.m)()}
|
||||
resultsTemplate={(props, state, styles, clickHandler) => {
|
||||
return state.results.map((val, i) => {
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className={'lc'}
|
||||
onClick={() => clickHandler(i)}
|
||||
>
|
||||
{val.name}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mouse over diff handler
|
||||
* @param {Function} showDiff diff tooltip callback
|
||||
@@ -405,7 +473,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
* @param {Function} select Select module callback
|
||||
* @param {SytheticEvent} event Event
|
||||
*/
|
||||
_keyUp(select,event) {
|
||||
_keyUp(select, event) {
|
||||
// nothing here yet
|
||||
}
|
||||
|
||||
@@ -475,12 +543,13 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
this.slotItems[this.firstSlotId].focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle focus if the component updates
|
||||
*
|
||||
*/
|
||||
componentWillUnmount() {
|
||||
if(this.props.slotDiv) {
|
||||
if (this.props.slotDiv) {
|
||||
this.props.slotDiv.focus();
|
||||
}
|
||||
}
|
||||
@@ -503,9 +572,10 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
<div ref={node => this.node = node}
|
||||
className={cn('select', this.props.className)}
|
||||
onScroll={this._hideDiff}
|
||||
onClick={(e) => e.stopPropagation() }
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onContextMenu={stopCtxPropagation}
|
||||
>
|
||||
{this._showSearch()}
|
||||
{this.state.list}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -52,12 +52,12 @@ export default class Defence extends TranslatedComponent {
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
const { ship, sys, opponentWep } = this.props;
|
||||
const { opponent, sys, opponentWep } = this.props;
|
||||
const { language, tooltip, termtip } = this.context;
|
||||
const { formats, translate, units } = language;
|
||||
const { shield, armour, shielddamage, armourdamage } = this.state;
|
||||
|
||||
const pd = ship.standard[4].m;
|
||||
const pd = opponent.standard[4].m;
|
||||
|
||||
const shieldSourcesData = [];
|
||||
const effectiveShieldData = [];
|
||||
|
||||
@@ -2,12 +2,21 @@ import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import Slot from './Slot';
|
||||
import Persist from '../stores/Persist';
|
||||
import { DamageAbsolute, DamageKinetic, DamageThermal, DamageExplosive, MountFixed, MountGimballed, MountTurret, ListModifications, Modified } from './SvgIcons';
|
||||
import {
|
||||
DamageAbsolute,
|
||||
DamageKinetic,
|
||||
DamageThermal,
|
||||
DamageExplosive,
|
||||
MountFixed,
|
||||
MountGimballed,
|
||||
MountTurret,
|
||||
ListModifications,
|
||||
Modified
|
||||
} from './SvgIcons';
|
||||
import { Modifications } from 'coriolis-data/dist';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
import { blueprintTooltip } from '../utils/BlueprintFunctions';
|
||||
|
||||
|
||||
/**
|
||||
* Hardpoint / Utility Slot
|
||||
*/
|
||||
@@ -27,7 +36,7 @@ export default class HardpointSlot extends Slot {
|
||||
* @return {string} Label
|
||||
*/
|
||||
_getMaxClassLabel(translate) {
|
||||
return translate(['U','S','M','L','H'][this.props.maxClass]);
|
||||
return translate(['U', 'S', 'M', 'L', 'H'][this.props.maxClass]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,38 +75,72 @@ export default class HardpointSlot extends Slot {
|
||||
return <div className={className} draggable='true' onDragStart={drag} onDragEnd={drop}>
|
||||
<div className={'cb'}>
|
||||
<div className={'l'}>
|
||||
{m.mount && m.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} onMouseOut={tooltip.bind(null, null)}><MountFixed /></span> : ''}
|
||||
{m.mount && m.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} onMouseOut={tooltip.bind(null, null)}><MountGimballed /></span> : ''}
|
||||
{m.mount && m.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().K ? <span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().T ? <span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().E ? <span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().A ? <span onMouseOver={termtip.bind(null, 'absolute')} onMouseOut={tooltip.bind(null, null)}><DamageAbsolute /></span> : ''}
|
||||
{classRating} {translate(m.name || m.grp)}{ m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }
|
||||
{m.mount && m.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')}
|
||||
onMouseOut={tooltip.bind(null, null)}><MountFixed/></span> : ''}
|
||||
{m.mount && m.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')}
|
||||
onMouseOut={tooltip.bind(null, null)}><MountGimballed/></span> : ''}
|
||||
{m.mount && m.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')}
|
||||
onMouseOut={tooltip.bind(null, null)}><MountTurret/></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().K ? <span onMouseOver={termtip.bind(null, 'kinetic')}
|
||||
onMouseOut={tooltip.bind(null, null)}><DamageKinetic/></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().T ? <span onMouseOver={termtip.bind(null, 'thermal')}
|
||||
onMouseOut={tooltip.bind(null, null)}><DamageThermal/></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().E ? <span onMouseOver={termtip.bind(null, 'explosive')}
|
||||
onMouseOut={tooltip.bind(null, null)}><DamageExplosive/></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().A ? <span onMouseOver={termtip.bind(null, 'absolute')}
|
||||
onMouseOut={tooltip.bind(null, null)}><DamageAbsolute/></span> : ''}
|
||||
{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span className='r'
|
||||
onMouseOver={termtip.bind(null, modTT)}
|
||||
onMouseOut={tooltip.bind(null, null)}><Modified/></span> : null}
|
||||
</div>
|
||||
|
||||
<div className={'r'}>{formats.round(m.getMass())}{u.T}</div>
|
||||
</div>
|
||||
<div className={'cb'}>
|
||||
{ m.getDps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'dpssdps' : 'dps')} onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}: {formats.round1(m.getDps())} { m.getClip() ? <span>({formats.round1(m.getSDps()) })</span> : null }</div> : null }
|
||||
{ m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'epsseps' : 'eps')} onMouseOut={tooltip.bind(null, null)}>{translate('EPS')}: {formats.round1(m.getEps())}{u.MW} { m.getClip() ? <span>({formats.round1((m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }{u.MW})</span> : null }</div> : null }
|
||||
{ m.getHps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'hpsshps' : 'hps')} onMouseOut={tooltip.bind(null, null)}>{translate('HPS')}: {formats.round1(m.getHps())} { m.getClip() ? <span>({formats.round1((m.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) })</span> : null }</div> : null }
|
||||
{ m.getDps() && m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, 'dpe')} onMouseOut={tooltip.bind(null, null)}>{translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}</div> : null }
|
||||
{ m.getRoF() ? <div className={'l'} onMouseOver={termtip.bind(null, 'rof')} onMouseOut={tooltip.bind(null, null)}>{translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}</div> : null }
|
||||
{ m.getRange() ? <div className={'l'}>{translate('range', m.grp)} {formats.f1(m.getRange() / 1000)}{u.km}</div> : null }
|
||||
{ m.getScanTime() ? <div className={'l'}>{translate('scantime')} {formats.f1(m.getScanTime())}{u.s}</div> : null }
|
||||
{ m.getFalloff() ? <div className={'l'}>{translate('falloff')} {formats.round(m.getFalloff() / 1000)}{u.km}</div> : null }
|
||||
{ m.getShieldBoost() ? <div className={'l'}>+{formats.pct1(m.getShieldBoost())}</div> : null }
|
||||
{ m.getAmmo() ? <div className={'l'}>{translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}</div> : null }
|
||||
{ m.getReload() ? <div className={'l'}>{translate('reload')}: {formats.round(m.getReload())}{u.s}</div> : null }
|
||||
{ m.getShotSpeed() ? <div className={'l'}>{translate('shotspeed')}: {formats.int(m.getShotSpeed())}{u.mps}</div> : null }
|
||||
{ m.getPiercing() ? <div className={'l'}>{translate('piercing')}: {formats.int(m.getPiercing())}</div> : null }
|
||||
{ m.getJitter() ? <div className={'l'}>{translate('jitter')}: {formats.f2(m.getJitter())}°</div> : null }
|
||||
{ showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null }
|
||||
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
||||
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
|
||||
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }
|
||||
{ m && validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
|
||||
{m.getDps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'dpssdps' : 'dps')}
|
||||
onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}: {formats.round1(m.getDps())} {m.getClip() ?
|
||||
<span>({formats.round1(m.getSDps())})</span> : null}</div> : null}
|
||||
{m.getDamage() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getDamage() ? 'shotdmg' : 'shotdmg')}
|
||||
onMouseOut={tooltip.bind(null, null)}>{translate('shotdmg')}: {formats.round1(m.getDamage())}</div> : null}
|
||||
{m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'epsseps' : 'eps')}
|
||||
onMouseOut={tooltip.bind(null, null)}>{translate('EPS')}: {formats.round1(m.getEps())}{u.MW} {m.getClip() ?
|
||||
<span>({formats.round1(m.getEps() * m.getSustainedFactor())}{u.MW})</span> : null}</div> : null}
|
||||
{m.getHps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'hpsshps' : 'hps')}
|
||||
onMouseOut={tooltip.bind(null, null)}>{translate('HPS')}: {formats.round1(m.getHps())} {m.getClip() ?
|
||||
<span>({formats.round1(m.getHps() * m.getSustainedFactor())})</span> : null}</div> : null}
|
||||
{m.getDps() && m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, 'dpe')}
|
||||
onMouseOut={tooltip.bind(null, null)}>{translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}</div> : null}
|
||||
{m.getRoF() ? <div className={'l'} onMouseOver={termtip.bind(null, 'rof')}
|
||||
onMouseOut={tooltip.bind(null, null)}>{translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}</div> : null}
|
||||
{m.getRange() ? <div
|
||||
className={'l'}>{translate('range', m.grp)} {formats.f1(m.getRange() / 1000)}{u.km}</div> : null}
|
||||
{m.getScanTime() ? <div
|
||||
className={'l'}>{translate('scantime')} {formats.f1(m.getScanTime())}{u.s}</div> : null}
|
||||
{m.getFalloff() ? <div
|
||||
className={'l'}>{translate('falloff')} {formats.round(m.getFalloff() / 1000)}{u.km}</div> : null}
|
||||
{m.getShieldBoost() ? <div className={'l'}>+{formats.pct1(m.getShieldBoost())}</div> : null}
|
||||
{m.getAmmo() ? <div
|
||||
className={'l'}>{translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}</div> : null}
|
||||
{m.getReload() ? <div className={'l'}>{translate('wep_reload')}: {formats.round(m.getReload())}{u.s}</div> : null}
|
||||
{m.getShotSpeed() ? <div
|
||||
className={'l'}>{translate('shotspeed')}: {formats.int(m.getShotSpeed())}{u.mps}</div> : null}
|
||||
{m.getPiercing() ? <div className={'l'}>{translate('piercing')}: {formats.int(m.getPiercing())}</div> : null}
|
||||
{m.getJitter() ? <div className={'l'}>{translate('jitter')}: {formats.f2(m.getJitter())}°</div> : null}
|
||||
{m.getScanAngle() ? <div className={'l'}>{translate('scan angle')}: {formats.f2(m.getScanAngle())}°</div> : null}
|
||||
{m.getScanRange() ? <div className={'l'}>{translate('scan range')}: {formats.int(m.getScanRange())}{u.m}</div> : null}
|
||||
{m.getMaxAngle() ? <div className={'l'}>{translate('max angle')}: {formats.f2(m.getMaxAngle())}°</div> : null}
|
||||
{showModuleResistances && m.getExplosiveResistance() ? <div
|
||||
className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null}
|
||||
{showModuleResistances && m.getKineticResistance() ? <div
|
||||
className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null}
|
||||
{showModuleResistances && m.getThermalResistance() ? <div
|
||||
className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null}
|
||||
{m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null}
|
||||
{m && validMods.length > 0 ? <div className='r' tabIndex="0" ref={modButton => this.modButton = modButton}>
|
||||
<button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation}
|
||||
onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}>
|
||||
<ListModifications/></button>
|
||||
</div> : null}
|
||||
</div>
|
||||
</div>;
|
||||
} else {
|
||||
|
||||
@@ -149,10 +149,19 @@ export default class HardpointSlotSection extends SlotSection {
|
||||
<ul>
|
||||
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'pa', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pa-F'] = smRef}>{translate('pa')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('rg')}</div>
|
||||
<ul>
|
||||
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'rg', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rg-F'] = smRef}>{translate('rg')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('nl')}</div>
|
||||
<ul>
|
||||
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'nl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['nl-F'] = smRef}>{translate('nl')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('rfl')}</div>
|
||||
<ul>
|
||||
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'rfl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rfl-F'] = smRef}><MountFixed className='lg'/></li>
|
||||
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'rfl', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rfl-T'] = smRef}><MountTurret className='lg'/></li>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
||||
@@ -349,7 +349,7 @@ export default class Header extends TranslatedComponent {
|
||||
_getShipsMenu() {
|
||||
let shipList = [];
|
||||
|
||||
for (let s in Ships) {
|
||||
for (let s of this.shipOrder) {
|
||||
shipList.push(<ActiveLink key={s} href={outfitURL(s)} className='block'>{Ships[s].properties.name}</ActiveLink>);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ export default class InternalSlot extends Slot {
|
||||
{ m.rangeLS ? <div className={'l'}>{translate('range')}: {m.rangeLS}{u.Ls}</div> : null }
|
||||
{ m.rangeLS === null ? <div className={'l'}>∞{u.Ls}</div> : null }
|
||||
{ m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</div> : null }
|
||||
{ m.maximum ? <div className={'l'}>{translate('max')}: {(m.maximum)}</div> : null }
|
||||
{ m.passengers ? <div className={'l'}>{translate('passengers')}: {m.passengers}</div> : null }
|
||||
{ m.getRegenerationRate() ? <div className='l'>{translate('regen')}: {formats.round1(m.getRegenerationRate())}{u.ps}</div> : null }
|
||||
{ m.getBrokenRegenerationRate() ? <div className='l'>{translate('brokenregen')}: {formats.round1(m.getBrokenRegenerationRate())}{u.ps}</div> : null }
|
||||
|
||||
@@ -13,6 +13,8 @@ import { Download } from './SvgIcons';
|
||||
import { outfitURL } from '../utils/UrlGenerators';
|
||||
import * as CompanionApiUtils from '../utils/CompanionApiUtils';
|
||||
|
||||
const zlib = require('pako');
|
||||
|
||||
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 };
|
||||
@@ -99,6 +101,7 @@ export default class ModalImport extends TranslatedComponent {
|
||||
this.state = {
|
||||
builds: props.builds,
|
||||
canEdit: !props.builds,
|
||||
loadoutEvent: null,
|
||||
comparisons: null,
|
||||
shipDiscount: null,
|
||||
moduleDiscount: null,
|
||||
@@ -111,12 +114,28 @@ export default class ModalImport extends TranslatedComponent {
|
||||
this._process = this._process.bind(this);
|
||||
this._import = this._import.bind(this);
|
||||
this._importBackup = this._importBackup.bind(this);
|
||||
this._importLoadout = this._importLoadout.bind(this);
|
||||
this._importDetailedArray = this._importDetailedArray.bind(this);
|
||||
this._importTextBuild = this._importTextBuild.bind(this);
|
||||
this._importCompanionApiBuild = this._importCompanionApiBuild.bind(this);
|
||||
this._validateImport = this._validateImport.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a Loadout event from Elite: Dangerous journal files
|
||||
* @param {Object} data Loadout event
|
||||
* @throws {string} If import fails
|
||||
*/
|
||||
_importLoadout(data) {
|
||||
if (data && data.Ship && data.Modules) {
|
||||
const deflated = zlib.deflate(JSON.stringify(data), { to: 'string' });
|
||||
let compressed = btoa(deflated);
|
||||
this.setState({loadoutEvent: compressed});
|
||||
} else {
|
||||
throw 'Loadout event must contain Ship and Modules';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a Coriolis backup
|
||||
* @param {Object} importData Backup Data
|
||||
@@ -159,7 +178,7 @@ export default class ModalImport extends TranslatedComponent {
|
||||
}
|
||||
// Check for module discount
|
||||
if (!isNaN(importData.moduleDiscount)) {
|
||||
this.setState({ shipDiscount: importData.moduleDiscount * 1 });
|
||||
this.setState({ moduleDiscount: importData.moduleDiscount * 1 });
|
||||
}
|
||||
|
||||
if (typeof importData.insurance == 'string') {
|
||||
@@ -345,12 +364,14 @@ export default class ModalImport extends TranslatedComponent {
|
||||
} else if (importData.ship && typeof importData.name !== undefined) { // Using JSON from a single ship build export
|
||||
this._importDetailedArray([importData]); // Convert to array with singleobject
|
||||
this.setState({ singleBuild: true });
|
||||
} else if (importData.Modules != null && importData.Modules[0] != null) {
|
||||
this._importLoadout(importData);
|
||||
} else { // Using Backup JSON
|
||||
this._importBackup(importData);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// console.log(e.stack);
|
||||
console.log(e);
|
||||
this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' });
|
||||
return;
|
||||
}
|
||||
@@ -364,6 +385,10 @@ export default class ModalImport extends TranslatedComponent {
|
||||
_process() {
|
||||
let builds = null, comparisons = null;
|
||||
|
||||
if (this.state.loadoutEvent) {
|
||||
return Router.go(`/import?data=${this.state.loadoutEvent}`);
|
||||
}
|
||||
|
||||
// If only importing a single build go straight to the outfitting page
|
||||
if (this.state.singleBuild) {
|
||||
builds = this.state.builds;
|
||||
@@ -480,7 +505,7 @@ export default class ModalImport extends TranslatedComponent {
|
||||
if (!state.processed) {
|
||||
importStage = (
|
||||
<div>
|
||||
<textarea className='cb json' ref={node => this.importField = node} onChange={this._validateImport} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
|
||||
<textarea spellCheck={false} className='cb json' ref={node => this.importField = node} onChange={this._validateImport} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
|
||||
<button id='proceed' className='l cap' onClick={this._process} disabled={!state.importValid} >{translate('proceed')}</button>
|
||||
<div className='l warning' style={{ marginLeft:'3em' }}>{state.errorMsg}</div>
|
||||
</div>
|
||||
|
||||
@@ -23,8 +23,10 @@ export default class ModalOrbis extends TranslatedComponent {
|
||||
this.state = {
|
||||
orbisCreds: Persist.getOrbisCreds(),
|
||||
orbisUrl: '...',
|
||||
ship: this.props.ship,
|
||||
authenticatedStatus: 'Checking...'
|
||||
};
|
||||
this.orbisCategory = this.orbisCategory.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,6 +90,17 @@ export default class ModalOrbis extends TranslatedComponent {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for changing category
|
||||
* @param {SyntheticEvent} e React Event
|
||||
*/
|
||||
orbisCategory(e) {
|
||||
let ship = this.state.ship;
|
||||
let cat = e.target.value;
|
||||
ship.category = cat;
|
||||
this.setState({ship});
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the modal
|
||||
* @return {React.Component} Modal Content
|
||||
@@ -106,6 +119,17 @@ export default class ModalOrbis extends TranslatedComponent {
|
||||
<br/><br/>
|
||||
<a className='button' href="https://orbis.zone/api/auth">Log in / signup to Orbis</a>
|
||||
<br/><br/>
|
||||
<h3>Category</h3>
|
||||
<select onChange={this.orbisCategory}>
|
||||
<option value="">No Category</option>
|
||||
<option>Combat</option>
|
||||
<option>Mining</option>
|
||||
<option>Trading</option>
|
||||
<option>Exploration</option>
|
||||
<option>Passenger Liner</option>
|
||||
<option>PvP</option>
|
||||
</select>
|
||||
<br/><br/>
|
||||
<h3 >{translate('Orbis link')}</h3>
|
||||
<input value={this.state.orbisUrl} readOnly size={25} onFocus={ (e) => e.target.select() }/>
|
||||
<br/><br/>
|
||||
|
||||
@@ -109,17 +109,18 @@ export default class ModalShoppingList extends TranslatedComponent {
|
||||
*/
|
||||
sendToEDEng(event) {
|
||||
event.preventDefault();
|
||||
let translate = this.context.language.translate;
|
||||
const target = event.target;
|
||||
target.disabled = this.state.blueprints.length > 0;
|
||||
if (this.state.blueprints.length === 0) {
|
||||
target.innerText = 'No modded components.';
|
||||
target.innerText = translate('No modded components.');
|
||||
target.disabled = true;
|
||||
setTimeout(() => {
|
||||
target.innerText = 'Send to EDEngineer';
|
||||
target.innerText = translate('Send to EDEngineer');
|
||||
target.disabled = false;
|
||||
}, 3000);
|
||||
} else {
|
||||
target.innerText = 'Sending...';
|
||||
target.innerText = translate('Sending...');
|
||||
}
|
||||
let countSent = 0;
|
||||
let countTotal = this.state.blueprints.length;
|
||||
@@ -139,7 +140,7 @@ export default class ModalShoppingList extends TranslatedComponent {
|
||||
countSent++;
|
||||
if (countSent === countTotal) {
|
||||
target.disabled = false;
|
||||
target.innerText = 'Send to EDEngineer';
|
||||
target.innerText = translate('Send to EDEngineer');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -230,32 +231,32 @@ export default class ModalShoppingList extends TranslatedComponent {
|
||||
this.sendToEDEng = this.sendToEDEng.bind(this);
|
||||
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
|
||||
<h2>{translate('PHRASE_SHOPPING_MATS')}</h2>
|
||||
<label>Grade 1 rolls </label>
|
||||
<label>{translate('Grade 1 rolls ')}</label>
|
||||
<input id={1} type={'number'} min={0} defaultValue={this.state.matsPerGrade[1]} onChange={this.changeHandler} />
|
||||
<br/>
|
||||
<label>Grade 2 rolls </label>
|
||||
<label>{translate('Grade 2 rolls ')}</label>
|
||||
<input id={2} type={'number'} min={0} defaultValue={this.state.matsPerGrade[2]} onChange={this.changeHandler} />
|
||||
<br/>
|
||||
<label>Grade 3 rolls </label>
|
||||
<label>{translate('Grade 3 rolls ')}</label>
|
||||
<input id={3} type={'number'} min={0} value={this.state.matsPerGrade[3]} onChange={this.changeHandler} />
|
||||
<br/>
|
||||
<label>Grade 4 rolls </label>
|
||||
<label>{translate('Grade 4 rolls ')}</label>
|
||||
<input id={4} type={'number'} min={0} value={this.state.matsPerGrade[4]} onChange={this.changeHandler} />
|
||||
<br/>
|
||||
<label>Grade 5 rolls </label>
|
||||
<label>{translate('Grade 5 rolls ')}</label>
|
||||
<input id={5} type={'number'} min={0} value={this.state.matsPerGrade[5]} onChange={this.changeHandler} />
|
||||
<div>
|
||||
<textarea className='cb json' readOnly value={this.state.matsList} />
|
||||
</div>
|
||||
<label hidden={!compatible} className={'l cap'}>CMDR Name </label>
|
||||
<label hidden={!compatible} className={'l cap'}>{translate('CMDR Name')}</label>
|
||||
<br/>
|
||||
<select hidden={!compatible} className={'cmdr-select l cap'} onChange={this.cmdrChangeHandler} defaultValue={this.state.cmdrName}>
|
||||
{this.state.cmdrs.map(e => <option key={e}>{e}</option>)}
|
||||
</select>
|
||||
<br/>
|
||||
<p hidden={!this.state.failed} id={'failed'} className={'l'}>Failed to send to EDEngineer (Launch EDEngineer and make sure the API is started then refresh the page.)</p>
|
||||
<p hidden={compatible} id={'browserbad'} className={'l'}>Sending to EDEngineer is not compatible with Firefox's security settings. Please try again with Chrome.</p>
|
||||
<button className={'l cb dismiss cap'} disabled={!!this.state.failed || !compatible} onClick={this.sendToEDEng}>{translate('Send To EDEngineer')}</button>
|
||||
<p hidden={!this.state.failed} id={'failed'} className={'l'}>{translate('PHRASE_FAIL_EDENGINEER')}</p>
|
||||
<p hidden={compatible} id={'browserbad'} className={'l'}>{translate('PHRASE_FIREFOX_EDENGINEER')}</p>
|
||||
<button className={'l cb dismiss cap'} disabled={!!this.state.failed || !compatible} onClick={this.sendToEDEng}>{translate('Send to EDEngineer')}</button>
|
||||
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import cn from 'classnames';
|
||||
import NumberEditor from 'react-number-editor';
|
||||
import { isValueBeneficial } from '../utils/BlueprintFunctions';
|
||||
import { isChangeValueBeneficial } from '../utils/BlueprintFunctions';
|
||||
import { Modifications } from 'coriolis-data/dist';
|
||||
|
||||
/**
|
||||
* Modification
|
||||
@@ -79,6 +80,7 @@ export default class Modification extends TranslatedComponent {
|
||||
let { translate, formats, units } = this.context.language;
|
||||
let { m, name } = this.props;
|
||||
let modValue = m.getChange(name);
|
||||
let isOverwrite = Modifications.modifications[name].method === 'overwrite';
|
||||
|
||||
if (name === 'damagedist') {
|
||||
// We don't show damage distribution
|
||||
@@ -117,10 +119,10 @@ export default class Modification extends TranslatedComponent {
|
||||
</td>
|
||||
<td style={{ textAlign: 'center' }} className={
|
||||
modValue ?
|
||||
isValueBeneficial(name, modValue) ? 'secondary' : 'warning' :
|
||||
isChangeValueBeneficial(name, modValue) ? 'secondary' : 'warning' :
|
||||
''
|
||||
}>
|
||||
{formats.f2(modValue / 100) || 0}%
|
||||
{formats.f2(modValue / 100) || 0}{isOverwrite ? '' : '%'}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -178,7 +178,7 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
continue;
|
||||
}
|
||||
const classes = cn('button-inline-menu', {
|
||||
active: m.blueprint && m.blueprint.special && m.blueprint.special.edname == specialName
|
||||
active: m.blueprint && m.blueprint.special && m.blueprint.special.key == specialName
|
||||
});
|
||||
if (classes.indexOf('active') >= 0) this.selectedSpecialId = specialName;
|
||||
const close = this._specialSelected.bind(this, specialName);
|
||||
@@ -437,7 +437,7 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
let specialTt;
|
||||
if (m.blueprint && m.blueprint.special) {
|
||||
specialLabel = m.blueprint.special.name;
|
||||
specialTt = specialToolTip(translate, m.blueprint.grades[m.blueprint.grade], m.grp, m, m.blueprint.special.edname);
|
||||
specialTt = specialToolTip(translate, m.blueprint.grades[m.blueprint.grade], m.grp, m, m.blueprint.special.key);
|
||||
} else {
|
||||
specialLabel = translate('PHRASE_SELECT_SPECIAL');
|
||||
}
|
||||
@@ -478,7 +478,7 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
<tbody>
|
||||
{ showRolls ?
|
||||
<tr>
|
||||
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: false }) }> { translate('roll') }: </td>
|
||||
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: false }) }> { translate('mroll') }: </td>
|
||||
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 0 }) } style={{ cursor: 'pointer' }} onClick={_rollWorst} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')} onMouseOut={tooltip.bind(null, null)}> { translate('0%') } </td>
|
||||
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 50 })} style={{ cursor: 'pointer' }} onClick={_rollFifty} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_FIFTY')} onMouseOut={tooltip.bind(null, null)}> { translate('50%') } </td>
|
||||
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 100 })} style={{ cursor: 'pointer' }} onClick={_rollFull} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('100%') } </td>
|
||||
|
||||
@@ -174,7 +174,7 @@ export default class OutfittingSubpages extends TranslatedComponent {
|
||||
<th style={{ width:'25%' }} className={cn({ active: tab == 'power' })} onClick={this._showTab.bind(this, 'power')} >{translate('power and costs')}</th>
|
||||
<th style={{ width:'25%' }} className={cn({ active: tab == 'profiles' })} onClick={this._showTab.bind(this, 'profiles')} >{translate('profiles')}</th>
|
||||
<th style={{ width:'25%' }} className={cn({ active: tab == 'offence' })} onClick={this._showTab.bind(this, 'offence')} >{translate('offence')}</th>
|
||||
<th style={{ width:'25%' }} className={cn({ active: tab == 'defence' })} onClick={this._showTab.bind(this, 'defence')} >{translate('defence')}</th>
|
||||
<th style={{ width:'25%' }} className={cn({ active: tab == 'defence' })} onClick={this._showTab.bind(this, 'defence')} >{translate('tab_defence')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
||||
@@ -50,8 +50,10 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
const speedTooltip = canThrust ? 'TT_SUMMARY_SPEED' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
|
||||
const canBoost = ship.canBoost(cargo, ship.fuelCapacity);
|
||||
const boostTooltip = canBoost ? 'TT_SUMMARY_BOOST' : canThrust ? 'TT_SUMMARY_BOOST_NONFUNCTIONAL' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
|
||||
const canJump = ship.getSlotStatus(ship.standard[2]) == 3;
|
||||
const sgMetrics = Calc.shieldMetrics(ship, pips.sys);
|
||||
const shipBoost = canBoost ? Calc.calcBoost(ship) : 'No Boost';
|
||||
const restingHeat = Math.sqrt(((ship.standard[0].m.pgen * ship.standard[0].m.eff) / ship.heatCapacity) / 0.2);
|
||||
const armourMetrics = Calc.armourMetrics(ship);
|
||||
let shieldColour = 'blue';
|
||||
if (shieldGenerator && shieldGenerator.m.grp === 'psg') {
|
||||
@@ -70,7 +72,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<tr className='main'>
|
||||
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canThrust }) }>{translate('speed')}</th>
|
||||
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canBoost }) }>{translate('boost')}</th>
|
||||
<th colSpan={5}>{translate('jump range')}</th>
|
||||
<th colSpan={5} className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('jump range')}</th>
|
||||
<th rowSpan={2}>{translate('shield')}</th>
|
||||
<th rowSpan={2}>{translate('integrity')}</th>
|
||||
<th rowSpan={2}>{translate('DPS')}</th>
|
||||
@@ -78,20 +80,21 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<th rowSpan={2}>{translate('TTD')}</th>
|
||||
{/* <th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th> */}
|
||||
<th rowSpan={2}>{translate('cargo')}</th>
|
||||
<th rowSpan={2}>{translate('pax')}</th>
|
||||
<th rowSpan={2} onMouseEnter={termtip.bind(null, 'passenger capacity', { cap: 0 })} onMouseLeave={hide}>{translate('pax')}</th>
|
||||
<th rowSpan={2}>{translate('fuel')}</th>
|
||||
<th colSpan={3}>{translate('mass')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th>
|
||||
<th rowSpan={2}>{translate('crew')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_TIME', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost time')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_INTERVAL', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost interval')}</th>
|
||||
<th rowSpan={2}>{translate('resting heat (Beta)')}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className='lft'>{translate('max')}</th>
|
||||
<th>{translate('unladen')}</th>
|
||||
<th>{translate('laden')}</th>
|
||||
<th>{translate('total unladen')}</th>
|
||||
<th>{translate('total laden')}</th>
|
||||
<th className={ cn({ 'lft': true, 'bg-warning-disabled': !canJump }) }>{translate('max')}</th>
|
||||
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('unladen')}</th>
|
||||
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('laden')}</th>
|
||||
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('total unladen')}</th>
|
||||
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('total laden')}</th>
|
||||
<th className='lft'>{translate('hull')}</th>
|
||||
<th>{translate('unladen')}</th>
|
||||
<th>{translate('laden')}</th>
|
||||
@@ -101,11 +104,11 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<tr>
|
||||
<td onMouseEnter={termtip.bind(null, speedTooltip, { cap: 0 })} onMouseLeave={hide}>{ canThrust ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, false))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||
<td onMouseEnter={termtip.bind(null, boostTooltip, { cap: 0 })} onMouseLeave={hide}>{ canBoost ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, true))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||
<td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.standard[2].m.getMaxFuelPerJump(), ship.standard[2].m, ship.standard[2].m.getMaxFuelPerJump(), ship))}{u.LY}</span></td>
|
||||
<td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span></td>
|
||||
<td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span></td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{ f2(Calc.jumpRange(ship.unladenMass + ship.standard[2].m.getMaxFuelPerJump(), ship.standard[2].m, ship.standard[2].m.getMaxFuelPerJump(), ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||
<td className={sgClassNames} onMouseEnter={termtip.bind(null, sgTooltip, { cap: 0 })} onMouseLeave={hide}>{int(ship.shield)}{u.MJ}</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_INTEGRITY', { cap: 0 })} onMouseLeave={hide}>{int(ship.armour)}</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_DPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalDps)}</td>
|
||||
@@ -122,6 +125,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<td>{ship.crew}</td>
|
||||
<td>{ship.masslock}</td>
|
||||
<td>{shipBoost !== 'No Boost' ? formats.time(shipBoost) : 'No Boost'}</td>
|
||||
<td>{formats.pct(restingHeat)}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -156,10 +160,10 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<td>{formats.pct1(ship.shieldThermRes)}</td>
|
||||
<td></td>
|
||||
|
||||
<td>{int(ship && ship.shield > 0 ? ship.shield : 0)}{u.MJ}</td>
|
||||
<td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldExplRes)))) : 0)}{u.MJ}</td>
|
||||
<td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldKinRes)))) : 0)}{u.MJ}</td>
|
||||
<td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldThermRes)))) : 0)}{u.MJ}</td>
|
||||
<td>{int(ship && sgMetrics.summary > 0 ? sgMetrics.summary : 0)}{u.MJ}</td>
|
||||
<td>{int(ship && sgMetrics.summary > 0 ? sgMetrics.summary / sgMetrics.explosive.base : 0)}{u.MJ}</td>
|
||||
<td>{int(ship && sgMetrics.summary ? sgMetrics.summary / sgMetrics.kinetic.base : 0)}{u.MJ}</td>
|
||||
<td>{int(ship && sgMetrics.summary ? sgMetrics.summary / sgMetrics.thermal.base : 0)}{u.MJ}</td>
|
||||
<td></td>
|
||||
<td>{sgMetrics && sgMetrics.recover === Math.Inf ? translate('Never') : formats.time(sgMetrics.recover)}</td>
|
||||
<td>{sgMetrics && sgMetrics.recharge === Math.Inf ? translate('Never') : formats.time(sgMetrics.recharge)}</td>
|
||||
@@ -194,11 +198,11 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<td>{formats.pct1(ship.hullKinRes)}</td>
|
||||
<td>{formats.pct1(ship.hullThermRes)}</td>
|
||||
<td>{formats.pct1(ship.hullCausRes)}</td>
|
||||
<td>{int(ship.armour)}</td>
|
||||
<td>{int(ship.armour * ((1 / (1 - (ship.hullExplRes)))))}</td>
|
||||
<td>{int(ship.armour * ((1 / (1 - (ship.hullKinRes)))))}</td>
|
||||
<td>{int(ship.armour * ((1 / (1 - (ship.hullThermRes)))))}</td>
|
||||
<td>{int(ship.armour * ((1 / (1 - (ship.hullCausRes)))))}</td>
|
||||
<td>{int(armourMetrics.total)}</td>
|
||||
<td>{int(armourMetrics.total / armourMetrics.explosive.total)}</td>
|
||||
<td>{int(armourMetrics.total/ armourMetrics.kinetic.total)}</td>
|
||||
<td>{int(armourMetrics.total / armourMetrics.thermal.total)}</td>
|
||||
<td>{int(armourMetrics.total/ armourMetrics.caustic.total)}</td>
|
||||
<td>{int(armourMetrics.modulearmour)}</td>
|
||||
<td>{int(armourMetrics.moduleprotection * 100) + '%'}</td>
|
||||
|
||||
|
||||
@@ -127,8 +127,8 @@ export default class Slot extends TranslatedComponent {
|
||||
menu = <AvailableModulesMenu
|
||||
className={this._getClassNames()}
|
||||
modules={availableModules()}
|
||||
shipMass={ship.hullMass}
|
||||
m={m}
|
||||
ship={ship}
|
||||
onSelect={onSelect}
|
||||
warning={warning}
|
||||
diffDetails={diffDetails.bind(ship, this.context.language)}
|
||||
|
||||
@@ -195,6 +195,15 @@ export default class SlotSection extends TranslatedComponent {
|
||||
if (targetSlot && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
|
||||
const mCopy = m.clone();
|
||||
this.props.ship.use(targetSlot, mCopy, false);
|
||||
let experimentalNum = this.props.ship.hardpoints
|
||||
.filter(s => s.m && s.m.experimental).length;
|
||||
// Remove the module on the last slot if we now exceed the number of
|
||||
// experimentals allowed
|
||||
if (m.experimental && 4 < experimentalNum) {
|
||||
this.props.ship.updateStats(originSlot, null, originSlot.m);
|
||||
originSlot.m = null; // Empty the slot
|
||||
originSlot.discountedCost = 0;
|
||||
}
|
||||
// Copy power info
|
||||
targetSlot.enabled = originSlot.enabled;
|
||||
targetSlot.priority = originSlot.priority;
|
||||
|
||||
@@ -109,8 +109,8 @@ export default class StandardSlot extends TranslatedComponent {
|
||||
menu = <AvailableModulesMenu
|
||||
className='standard'
|
||||
modules={modules}
|
||||
shipMass={ModuleUtils.isShieldGenerator(m.grp) ? ship.hullMass : ship.unladenMass}
|
||||
m={m}
|
||||
ship={ship}
|
||||
onSelect={onSelect}
|
||||
warning={warning}
|
||||
diffDetails={diffDetails.bind(ship, this.context.language)}
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as IT from './it';
|
||||
import * as RU from './ru';
|
||||
import * as PL from './pl';
|
||||
import * as PT from './pt';
|
||||
import * as CN from './cn';
|
||||
import * as d3 from 'd3';
|
||||
|
||||
let fallbackTerms = EN.terms;
|
||||
@@ -27,6 +28,7 @@ export function getLanguage(langCode) {
|
||||
case 'ru': lang = RU; break;
|
||||
case 'pl': lang = PL; break;
|
||||
case 'pt': lang = PT; break;
|
||||
case 'cn': lang = CN; break;
|
||||
default:
|
||||
lang = EN;
|
||||
}
|
||||
@@ -94,5 +96,6 @@ export const Languages = {
|
||||
fr: 'Français',
|
||||
ru: 'ру́сский',
|
||||
pl: 'polski',
|
||||
pt: 'português'
|
||||
pt: 'português',
|
||||
cn: '中文'
|
||||
};
|
||||
|
||||
16
src/app/i18n/cn.js
Normal file
16
src/app/i18n/cn.js
Normal file
@@ -0,0 +1,16 @@
|
||||
export const formats = {
|
||||
decimal: '.',
|
||||
thousands: ',',
|
||||
grouping: [3],
|
||||
currency: ['¥', ''],
|
||||
dateTime: '%a %b %e %X %Y',
|
||||
date: '%Y年%m月%d日',
|
||||
time: '%H:%M:%S',
|
||||
periods: ['AM', 'PM'],
|
||||
days: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
|
||||
shortDays: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
|
||||
months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
|
||||
shortMonths: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
|
||||
};
|
||||
|
||||
export { default as terms } from './cn.json';
|
||||
405
src/app/i18n/cn.json
Normal file
405
src/app/i18n/cn.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -59,7 +59,7 @@
|
||||
"empty all": "vide tout",
|
||||
"Enter Name": "Entrer nom",
|
||||
"Explorer": "explorateur",
|
||||
"fastest range": "gamme la plus rapide",
|
||||
"farthest range": "gamme la plus rapide",
|
||||
"fuel": "carburant",
|
||||
"fuel level": "niveau de carburant",
|
||||
"full tank": "Réservoir plein",
|
||||
|
||||
@@ -299,7 +299,7 @@
|
||||
"edit data": "Редактирование",
|
||||
"empty all": "пусто все",
|
||||
"Enter Name": "Введите имя",
|
||||
"fastest range": "быстрый диапазон",
|
||||
"farthest range": "быстрый диапазон",
|
||||
"fuel level": "уровень топлива",
|
||||
"full tank": "Полный бак",
|
||||
"internal compartments": "внутренние отсеки",
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Model } from '@nozbe/watermelondb';
|
||||
import { field } from '@nozbe/watermelondb/decorators';
|
||||
|
||||
export default class Build extends Model {
|
||||
static table = 'builds';
|
||||
|
||||
@field('title') title;
|
||||
|
||||
@field('code') code;
|
||||
|
||||
@field('ship_id') ship_id;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Database } from '@nozbe/watermelondb';
|
||||
import LokiJSAdapter from '@nozbe/watermelondb/adapters/lokijs';
|
||||
|
||||
import { coriolisSchema } from './schema';
|
||||
import Build from './Build';
|
||||
// import Post from './model/Post' // ⬅️ You'll import your Models here
|
||||
|
||||
// First, create the adapter to the underlying database:
|
||||
const adapter = new LokiJSAdapter({
|
||||
dbName: 'coriolis', // ⬅️ Give your database a name!
|
||||
schema: coriolisSchema
|
||||
});
|
||||
|
||||
// Then, make a Watermelon database from it!
|
||||
export const database = new Database({
|
||||
adapter,
|
||||
modelClasses: [
|
||||
Build
|
||||
]
|
||||
});
|
||||
|
||||
export const buildsCollection = database.collections.get('builds');
|
||||
@@ -1,15 +0,0 @@
|
||||
import {appSchema, tableSchema} from '@nozbe/watermelondb';
|
||||
|
||||
export const coriolisSchema = appSchema({
|
||||
version: 2,
|
||||
tables: [
|
||||
tableSchema({
|
||||
name: 'builds',
|
||||
columns: [
|
||||
{name: 'title', type: 'string', isIndexed: true},
|
||||
{name: 'code', type: 'string'},
|
||||
{name: 'ship_id', type: 'string'}
|
||||
]
|
||||
})
|
||||
]
|
||||
});
|
||||
@@ -103,7 +103,7 @@ export default class AboutPage extends Page {
|
||||
patreon.com/coriolis_elite
|
||||
</a>
|
||||
, which will be used to keep Coriolis up to date and the servers
|
||||
running. I also run ads, which are also used for development and hosting.
|
||||
running.
|
||||
</p>
|
||||
<form
|
||||
action="https://www.paypal.com/cgi-bin/webscr"
|
||||
|
||||
@@ -38,7 +38,6 @@ import ModalExport from '../components/ModalExport';
|
||||
import ModalPermalink from '../components/ModalPermalink';
|
||||
import ModalShoppingList from '../components/ModalShoppingList';
|
||||
import ModalOrbis from '../components/ModalOrbis';
|
||||
import { autoBind } from 'react-extras';
|
||||
|
||||
/**
|
||||
* Document Title Generator
|
||||
@@ -63,7 +62,14 @@ export default class OutfittingPage extends Page {
|
||||
super(props, context);
|
||||
// window.Perf = Perf;
|
||||
this.state = this._initState(props, context);
|
||||
autoBind(this);
|
||||
this._keyDown = this._keyDown.bind(this);
|
||||
this._exportBuild = this._exportBuild.bind(this);
|
||||
this._pipsUpdated = this._pipsUpdated.bind(this);
|
||||
this._boostUpdated = this._boostUpdated.bind(this);
|
||||
this._cargoUpdated = this._cargoUpdated.bind(this);
|
||||
this._fuelUpdated = this._fuelUpdated.bind(this);
|
||||
this._opponentUpdated = this._opponentUpdated.bind(this);
|
||||
this._engagementRangeUpdated = this._engagementRangeUpdated.bind(this);
|
||||
this._sectionMenuRefs = {};
|
||||
}
|
||||
|
||||
@@ -77,9 +83,9 @@ export default class OutfittingPage extends Page {
|
||||
let params = context.route.params;
|
||||
let shipId = params.ship;
|
||||
let code = params.code;
|
||||
let savedCode = code;
|
||||
let buildName = params.bn;
|
||||
let data = Ships[shipId]; // Retrieve the basic ship properties, slots and defaults
|
||||
let savedCode = Persist.getBuild(shipId, buildName);
|
||||
if (!data) {
|
||||
return { error: { message: 'Ship not found: ' + shipId } };
|
||||
}
|
||||
@@ -138,13 +144,6 @@ export default class OutfittingPage extends Page {
|
||||
};
|
||||
}
|
||||
|
||||
async getBuild() {
|
||||
const build = await Persist.getBuild(this.state.shipId, this.state.buildName);
|
||||
if (build) {
|
||||
this.setState({id: build.id, savedCode: build.code})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle build name change and update state
|
||||
* @param {SyntheticEvent} event React Event
|
||||
@@ -225,9 +224,12 @@ export default class OutfittingPage extends Page {
|
||||
const control = LZString.decompressFromBase64(
|
||||
Utils.fromUrlSafe(parts[4])
|
||||
).split('/');
|
||||
sys = parseFloat(control[0]) || sys;
|
||||
eng = parseFloat(control[1]) || eng;
|
||||
wep = parseFloat(control[2]) || wep;
|
||||
sys = parseFloat(control[0]);
|
||||
eng = parseFloat(control[1]);
|
||||
wep = parseFloat(control[2]);
|
||||
if (sys + eng + wep > 6) {
|
||||
sys = eng = wep = 2;
|
||||
}
|
||||
boost = control[3] == 1 ? true : false;
|
||||
fuel = parseFloat(control[4]) || fuel;
|
||||
cargo = parseInt(control[5]) || cargo;
|
||||
@@ -418,14 +420,13 @@ export default class OutfittingPage extends Page {
|
||||
/**
|
||||
* Save the current build
|
||||
*/
|
||||
async _saveBuild() {
|
||||
const { ship, buildName, newBuildName, shipId, id } = this.state;
|
||||
this.getBuild();
|
||||
_saveBuild() {
|
||||
const { ship, buildName, newBuildName, shipId } = this.state;
|
||||
|
||||
// If this is a stock ship the code won't be set, so ensure that we have it
|
||||
const code = this.state.code || ship.toString();
|
||||
|
||||
const yes = await Persist.saveBuild(id, newBuildName, code, shipId);
|
||||
console.log(yes)
|
||||
Persist.saveBuild(shipId, newBuildName, code);
|
||||
this._updateRoute(shipId, newBuildName, code);
|
||||
|
||||
let opponent, opponentBuild, opponentSys, opponentEng, opponentWep;
|
||||
@@ -662,7 +663,6 @@ export default class OutfittingPage extends Page {
|
||||
* Add listeners when about to mount
|
||||
*/
|
||||
componentWillMount() {
|
||||
this.getBuild()
|
||||
document.addEventListener('keydown', this._keyDown);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,22 +51,16 @@ export default class Page extends React.Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Pages are 'pure' components that only render when props, state, or context changes.
|
||||
* This method performs a shallow comparison to determine change.
|
||||
*
|
||||
* @param {Object} np Next/Incoming properties
|
||||
* @param {Object} ns Next/Incoming state
|
||||
* @param {Object} nc Next/Incoming context
|
||||
* @return {Boolean} True if props, state, or context has changed
|
||||
* Update the window title upon mount
|
||||
*/
|
||||
shouldComponentUpdate(np, ns, nc) {
|
||||
return !shallowEqual(this.props, np) || !shallowEqual(this.state, ns) || !shallowEqual(this.context, nc);
|
||||
componentWillMount() {
|
||||
document.title = this.state.title || 'Coriolis';
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the window title upon mount
|
||||
*/
|
||||
componentWillMount() {
|
||||
componentDidMount() {
|
||||
document.title = this.state.title || 'Coriolis';
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ function shipSummary(shipId, shipData) {
|
||||
id: shipId,
|
||||
hpCount: 0,
|
||||
intCount: 0,
|
||||
beta: shipData.beta,
|
||||
maxCargo: 0,
|
||||
maxPassengers: 0,
|
||||
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
||||
@@ -125,12 +126,14 @@ export default class ShipyardPage extends Page {
|
||||
title: 'Coriolis EDCD Edition - Shipyard',
|
||||
shipPredicate: 'name',
|
||||
shipDesc: true,
|
||||
shipSummaries: ShipyardPage.cachedShipSummaries
|
||||
shipSummaries: ShipyardPage.cachedShipSummaries,
|
||||
compare: {},
|
||||
groupCompared: false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Higlight the current ship in the table
|
||||
* Higlight the current ship in the table on mouse over
|
||||
* @param {String} shipId Ship Id
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
@@ -139,6 +142,24 @@ export default class ShipyardPage extends Page {
|
||||
this.setState({ shipId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle compare highlighting for ships in the table
|
||||
* @param {String} shipId Ship Id
|
||||
*/
|
||||
_toggleCompare(shipId) {
|
||||
let compare = this.state.compare;
|
||||
compare[shipId] = !compare[shipId];
|
||||
this.setState({ compare });
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle grouping of compared ships in the table
|
||||
* @private
|
||||
*/
|
||||
_toggleGroupCompared() {
|
||||
this.setState({groupCompared: !this.state.groupCompared})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update state with the specified sort predicates
|
||||
* @param {String} shipPredicate Sort predicate - property name
|
||||
@@ -168,10 +189,9 @@ export default class ShipyardPage extends Page {
|
||||
* @param {Object} u Localized unit map
|
||||
* @param {Function} fInt Localized integer formatter
|
||||
* @param {Function} fRound Localized round formatter
|
||||
* @param {Boolean} highlight Should this row be highlighted
|
||||
* @return {React.Component} Table Row
|
||||
*/
|
||||
_shipRowElement(s, translate, u, fInt, fRound, highlight) {
|
||||
_shipRowElement(s, translate, u, fInt, fRound) {
|
||||
let noTouch = this.context.noTouch;
|
||||
|
||||
return (
|
||||
@@ -180,9 +200,10 @@ export default class ShipyardPage extends Page {
|
||||
style={{ height: '1.5em' }}
|
||||
className={cn({
|
||||
highlighted: noTouch && this.state.shipId === s.id,
|
||||
alt: highlight
|
||||
comparehighlight: this.state.compare[s.id],
|
||||
})}
|
||||
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
|
||||
onClick={() => this._toggleCompare(s.id)}
|
||||
>
|
||||
<td className="ri">{s.manufacturer}</td>
|
||||
<td className="ri">{fInt(s.retailCost)}</td>
|
||||
@@ -235,7 +256,7 @@ export default class ShipyardPage extends Page {
|
||||
let hide = this.context.tooltip.bind(null, null);
|
||||
let fInt = formats.int;
|
||||
let fRound = formats.round;
|
||||
let { shipSummaries, shipPredicate, shipPredicateIndex } = this.state;
|
||||
let { shipSummaries, shipPredicate, shipPredicateIndex, compare, groupCompared } = this.state;
|
||||
let sortShips = (predicate, index) =>
|
||||
this._sortShips.bind(this, predicate, index);
|
||||
|
||||
@@ -268,6 +289,15 @@ export default class ShipyardPage extends Page {
|
||||
valB = val;
|
||||
}
|
||||
|
||||
if (groupCompared) {
|
||||
if (compare[a.id] && !compare[b.id]) {
|
||||
return -1;
|
||||
}
|
||||
if (!compare[a.id] && compare[b.id]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (valA == valB) {
|
||||
if (a.name > b.name) {
|
||||
return 1;
|
||||
@@ -285,27 +315,13 @@ export default class ShipyardPage extends Page {
|
||||
let shipRows = new Array(shipSummaries.length);
|
||||
let detailRows = new Array(shipSummaries.length);
|
||||
|
||||
let lastShipSortValue = null;
|
||||
let backgroundHighlight = false;
|
||||
|
||||
for (let s of shipSummaries) {
|
||||
let shipSortValue = s[shipPredicate];
|
||||
if (shipPredicateIndex != undefined) {
|
||||
shipSortValue = shipSortValue[shipPredicateIndex];
|
||||
}
|
||||
|
||||
if (shipSortValue != lastShipSortValue) {
|
||||
backgroundHighlight = !backgroundHighlight;
|
||||
lastShipSortValue = shipSortValue;
|
||||
}
|
||||
|
||||
detailRows[i] = this._shipRowElement(
|
||||
s,
|
||||
translate,
|
||||
units,
|
||||
fInt,
|
||||
formats.f1,
|
||||
backgroundHighlight
|
||||
);
|
||||
shipRows[i] = (
|
||||
<tr
|
||||
@@ -313,12 +329,13 @@ export default class ShipyardPage extends Page {
|
||||
style={{ height: '1.5em' }}
|
||||
className={cn({
|
||||
highlighted: noTouch && this.state.shipId === s.id,
|
||||
alt: backgroundHighlight
|
||||
comparehighlight: this.state.compare[s.id],
|
||||
})}
|
||||
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
|
||||
onClick={() => this._toggleCompare(s.id)}
|
||||
>
|
||||
<td className="le">
|
||||
<Link href={'/outfit/' + s.id}>{s.name}</Link>
|
||||
<Link href={'/outfit/' + s.id}>{s.name} {s.beta === true ? '(Beta)' : null}</Link>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
@@ -326,18 +343,10 @@ export default class ShipyardPage extends Page {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="page" style={{ fontSize: sizeRatio + 'em' }}>
|
||||
<div
|
||||
style={{
|
||||
whiteSpace: 'nowrap',
|
||||
margin: '0 auto',
|
||||
fontSize: '0.8em',
|
||||
position: 'relative',
|
||||
display: 'inline-block',
|
||||
maxWidth: '100%'
|
||||
}}
|
||||
>
|
||||
<table style={{ width: '12em', position: 'absolute', zIndex: 1 }}>
|
||||
<div className="page" style={{fontSize: sizeRatio + 'em'}}>
|
||||
<div className="content-wrapper">
|
||||
<div className="shipyard-table-wrapper">
|
||||
<table style={{width: '12em', position: 'absolute', zIndex: 1}} className="shipyard-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="le rgt"> </th>
|
||||
@@ -355,8 +364,8 @@ export default class ShipyardPage extends Page {
|
||||
{shipRows}
|
||||
</tbody>
|
||||
</table>
|
||||
<div style={{ overflowX: 'scroll', maxWidth: '100%' }}>
|
||||
<table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }}>
|
||||
<div style={{ overflowX: 'auto', maxWidth: '100%' }}>
|
||||
<table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }} className="shipyard-table">
|
||||
<thead>
|
||||
<tr className="main">
|
||||
<th
|
||||
@@ -451,10 +460,10 @@ export default class ShipyardPage extends Page {
|
||||
<th className="sortable" onClick={sortShips('maxCargo')}>
|
||||
{translate('cargo')}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('maxPassengers')}>
|
||||
<th className="sortable" onClick={sortShips('maxPassengers')} onMouseEnter={termtip.bind(null, 'passenger capacity')}
|
||||
onMouseLeave={hide}>
|
||||
{translate('pax')}
|
||||
</th>
|
||||
|
||||
<th className="lft" colSpan={7}>
|
||||
{translate('core module classes')}
|
||||
</th>
|
||||
@@ -613,6 +622,10 @@ export default class ShipyardPage extends Page {
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div className="table-tools" >
|
||||
<label><input type="checkbox" checked={this.state.groupCompared} onClick={() => this._toggleGroupCompared()}/>Group highlighted ships</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export function jumpRange(mass, fsd, fuel, ship) {
|
||||
let jumpAddition = 0;
|
||||
if (ship) {
|
||||
for (const module of ship.internal) {
|
||||
if (module && module.m && module.m.grp === 'gfsb') {
|
||||
if (module && module.m && module.m.grp === 'gfsb' && ship.getSlotStatus(module) == 3) {
|
||||
jumpAddition += module.m.getJumpBoost();
|
||||
}
|
||||
}
|
||||
@@ -400,7 +400,7 @@ export function shieldMetrics(ship, sys) {
|
||||
let shieldAddition = 0;
|
||||
if (ship) {
|
||||
for (const module of ship.internal) {
|
||||
if (module && module.m && module.m.grp === 'gsrp') {
|
||||
if (module && module.m && module.m.grp === 'gsrp' && module.enabled) {
|
||||
shieldAddition += module.m.getShieldAddition();
|
||||
}
|
||||
}
|
||||
@@ -465,6 +465,7 @@ export function shieldMetrics(ship, sys) {
|
||||
boosters: boostersStrength,
|
||||
addition: shieldAddition,
|
||||
cells: ship.shieldCells,
|
||||
summary: generatorStrength + boostersStrength + shieldAddition,
|
||||
total: generatorStrength + boostersStrength + ship.shieldCells + shieldAddition,
|
||||
recover,
|
||||
recharge,
|
||||
@@ -573,7 +574,7 @@ export function armourMetrics(ship) {
|
||||
// };
|
||||
// Armour from HRPs and module armour from MRPs
|
||||
for (let slot of ship.internal) {
|
||||
if (slot.m && (slot.m.grp === 'hr' || slot.m.grp === 'ghrp' || slot.m.grp == 'mahr')) {
|
||||
if (slot.m && slot.enabled && (slot.m.grp === 'hr' || slot.m.grp === 'ghrp' || slot.m.grp == 'mahr')) {
|
||||
armourReinforcement += slot.m.getHullReinforcement();
|
||||
// Hull boost for HRPs is applied against the ship's base armour
|
||||
armourReinforcement += ship.baseArmour * slot.m.getModValue('hullboost') / 10000;
|
||||
@@ -585,7 +586,7 @@ export function armourMetrics(ship) {
|
||||
hullThermDmg = hullThermDmg * (1 - slot.m.getThermalResistance());
|
||||
hullCausDmg = hullCausDmg * (1 - slot.m.getCausticResistance());
|
||||
}
|
||||
if (slot.m && (slot.m.grp == 'mrp' || slot.m.grp == 'gmrp')) {
|
||||
if (slot.m && slot.enabled && (slot.m.grp == 'mrp' || slot.m.grp == 'gmrp')) {
|
||||
moduleArmour += slot.m.getIntegrity();
|
||||
moduleProtection = moduleProtection * (1 - slot.m.getProtection());
|
||||
}
|
||||
@@ -1014,7 +1015,10 @@ export function timeToDrainWep(ship, wep) {
|
||||
*/
|
||||
export function timeToDeplete(amount, dps, eps, capacity, recharge) {
|
||||
const drainPerSecond = eps - recharge;
|
||||
if (drainPerSecond <= 0) {
|
||||
// If there is nothing to remove, we're don instantly
|
||||
if (!amount) {
|
||||
return 0;
|
||||
} if (drainPerSecond <= 0) {
|
||||
// Simple result
|
||||
return amount / dps;
|
||||
} else {
|
||||
|
||||
@@ -57,6 +57,7 @@ export const ModuleGroupToName = {
|
||||
ghrp: 'Guardian Hull Reinforcement Package',
|
||||
gmrp: 'Guardian Module Reinforcement Package',
|
||||
mahr: 'Meta Alloy Hull Reinforcement Package',
|
||||
sua: 'Supercruise Assist',
|
||||
|
||||
// Hard Points
|
||||
bl: 'Beam Laser',
|
||||
@@ -94,6 +95,10 @@ export const ModuleGroupToName = {
|
||||
gsc: 'Guardian Shard Cannon',
|
||||
tbem: 'Enzyme Missile Rack',
|
||||
tbrfl: 'Remote Release Flechette Launcher',
|
||||
pwa: 'Pulse Wave Analyser',
|
||||
abl: 'Abrasion Blaster',
|
||||
scl: 'Seismic Charge Launcher',
|
||||
sdm: 'Sub-Surface Displacement Missile',
|
||||
};
|
||||
|
||||
let GrpNameToCodeMap = {};
|
||||
@@ -202,7 +207,7 @@ export const ShipFacets = [
|
||||
i: 9
|
||||
},
|
||||
{ // 10
|
||||
title: 'fastest range',
|
||||
title: 'farthest range',
|
||||
props: ['unladenFastestRange', 'ladenFastestRange'],
|
||||
lbls: ['unladen', 'laden'],
|
||||
unit: 'LY',
|
||||
|
||||
@@ -41,49 +41,29 @@ export default class Module {
|
||||
* @return {object} The value of the modification. If it is a numeric value then it is returned as an integer value scaled so that 1.23% == 123
|
||||
*/
|
||||
getModValue(name, raw) {
|
||||
let baseVal = this[name];
|
||||
let result = this.mods && this.mods[name] ? this.mods[name] : null;
|
||||
|
||||
if ((!raw) && this.blueprint && this.blueprint.special) {
|
||||
// This module has a special effect, see if we need to alter our returned value
|
||||
const modifierActions = Modifications.modifierActions[this.blueprint.special.edname];
|
||||
const modifierActions = Modifications.modifierActions[this.blueprint.special.key];
|
||||
if (modifierActions && modifierActions[name]) {
|
||||
// this special effect modifies our returned value
|
||||
const modification = Modifications.modifications[name];
|
||||
const multiplier = modification.type === 'percentage' ? 10000 : 100;
|
||||
if (name === 'explres' || name === 'kinres' || name === 'thermres' || name === 'causres') {
|
||||
// Resistance modifications in itself are additive, however their
|
||||
// special effects are multiplicative. They affect the overall result
|
||||
// by (special effect resistance) * (damage mult after modification),
|
||||
// i. e. we need to apply the special effect as a multiplier to the
|
||||
// overall result and then calculate the difference.
|
||||
let baseMult = this[name] ? 1 - this[name] : 1;
|
||||
result = (baseMult - (baseMult - result / multiplier) * (1 - modifierActions[name] / 100)) * multiplier;
|
||||
// Apply resistance modding mechanisms to special effects subsequently
|
||||
result = result + modifierActions[name] * (1 - (this[name] + result / multiplier)) * 100;
|
||||
} else if (modification.method === 'additive') {
|
||||
result = result + modifierActions[name] * 100;
|
||||
} else if (modification.method === 'overwrite') {
|
||||
result = modifierActions[name];
|
||||
} else {
|
||||
// rate of fire is special, as it's really burst interval. Handle that here
|
||||
let mod = null;
|
||||
if (name === 'rof') {
|
||||
mod = 1 / (1 + modifierActions[name]) - 1;
|
||||
} else {
|
||||
mod = modifierActions[name];
|
||||
}
|
||||
result = (((1 + result / multiplier) * (1 + mod)) - 1) * multiplier;
|
||||
result = (((1 + result / multiplier) * (1 + modifierActions[name])) - 1) * multiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resistance modding for hull reinforcement packages has additional
|
||||
// diminishing returns implemented. The mod value gets lowered by
|
||||
// the amount of base resistance the hrp has.
|
||||
if (!isNaN(result) && this.grp === 'hr' &&
|
||||
(name === 'kinres' || name === 'thermres' || name === 'explres')) {
|
||||
let baseRes = this[name];
|
||||
result = result * (1 - baseRes);
|
||||
}
|
||||
|
||||
// Sanitise the resultant value to 4dp equivalent
|
||||
return isNaN(result) ? result : Math.round(result);
|
||||
}
|
||||
@@ -103,29 +83,22 @@ export default class Module {
|
||||
}
|
||||
if (valueiswithspecial && this.blueprint && this.blueprint.special) {
|
||||
// This module has a special effect, see if we need to alter the stored value
|
||||
const modifierActions = Modifications.modifierActions[this.blueprint.special.edname];
|
||||
const modifierActions = Modifications.modifierActions[this.blueprint.special.key];
|
||||
if (modifierActions && modifierActions[name]) {
|
||||
// This special effect modifies the value being set, so we need to revert it prior to storing the value
|
||||
const modification = Modifications.modifications[name];
|
||||
if (name === 'explres' || name === 'kinres' || name === 'thermres' || name === 'causres') {
|
||||
// Resistance modifications in itself are additive but their
|
||||
// experimentals are applied multiplicatively therefor we must handle
|
||||
// them differently here (cf. documentation in getModValue).
|
||||
let baseMult = (this[name] ? 1 - this[name] : 1);
|
||||
value = ((baseMult - value / 10000) / (1 - modifierActions[name] / 100) - baseMult) * -10000;
|
||||
let res = (this[name] ? this[name] : 0) + value / 10000;
|
||||
let experimental = modifierActions[name] / 100;
|
||||
value = (experimental - res) / (experimental - 1) - this[name];
|
||||
value *= 10000;
|
||||
// value = ((baseMult - value / 10000) / (1 - modifierActions[name] / 100) - baseMult) * -10000;
|
||||
} else if (modification.method === 'additive') {
|
||||
value = value - modifierActions[name];
|
||||
} else if (modification.method === 'overwrite') {
|
||||
value = null;
|
||||
} else {
|
||||
// rate of fire is special, as it's really burst interval. Handle that here
|
||||
let mod = null;
|
||||
if (name === 'rof') {
|
||||
mod = 1 / (1 + modifierActions[name]) - 1;
|
||||
} else {
|
||||
mod = modifierActions[name];
|
||||
}
|
||||
value = ((value / 10000 + 1) / (1 + mod) - 1) * 10000;
|
||||
value = ((value / 10000 + 1) / (1 + modifierActions[name]) - 1) * 10000;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,6 +117,13 @@ export default class Module {
|
||||
* @return {Number} The value queried
|
||||
*/
|
||||
get(name, modified = true) {
|
||||
if (name == 'rof' && isNaN(this[name])) {
|
||||
let fireint = this['fireint'];
|
||||
if (!isNaN(fireint)) {
|
||||
this['rof'] = 1 / fireint;
|
||||
}
|
||||
}
|
||||
|
||||
let val;
|
||||
if (modified) {
|
||||
val = this._getModifiedValue(name);
|
||||
@@ -177,20 +157,22 @@ export default class Module {
|
||||
baseValue = 0;
|
||||
}
|
||||
modValue = value - baseValue;
|
||||
if (this.grp === 'hr' &&
|
||||
(name === 'kinres' || name === 'thermres' || name === 'explres')) {
|
||||
modValue = modValue / (1 - baseValue);
|
||||
}
|
||||
} else if (name === 'shieldboost' || name === 'hullboost') {
|
||||
modValue = (1 + value) / (1 + baseValue) - 1;
|
||||
} else if (name === 'rof') {
|
||||
let burst = this.get('burst', true) || 1;
|
||||
let burstInt = 1 / (this.get('burstrof', true) / 1);
|
||||
|
||||
let interval = burst / value;
|
||||
let newFireint = (interval - (burst - 1) * burstInt);
|
||||
modValue = newFireint / this['fireint'] - 1;
|
||||
} else { // multiplicative
|
||||
modValue = value / baseValue - 1;
|
||||
modValue = baseValue == 0 ? 0 : value / baseValue - 1;
|
||||
}
|
||||
|
||||
if (modification.type === 'percentage') {
|
||||
modValue = modValue * 10000;
|
||||
} else if (modification.type === 'numeric' && name !== 'burst' &&
|
||||
name !== 'burstrof') {
|
||||
} else if (modification.type === 'numeric') {
|
||||
modValue = modValue * 100;
|
||||
}
|
||||
|
||||
@@ -208,7 +190,14 @@ export default class Module {
|
||||
*/
|
||||
getPretty(name, modified = true, places = 2) {
|
||||
const formattingOptions = STATS_FORMATTING[name];
|
||||
let val = this.get(name, modified) || 0;
|
||||
let val;
|
||||
if (formattingOptions && formattingOptions.synthetic) {
|
||||
val = (this[formattingOptions.synthetic]).call(this, modified);
|
||||
} else {
|
||||
val = this.get(name, modified);
|
||||
}
|
||||
val = val || 0;
|
||||
|
||||
if (formattingOptions && formattingOptions.format.startsWith('pct')) {
|
||||
return 100 * val;
|
||||
}
|
||||
@@ -267,12 +256,17 @@ export default class Module {
|
||||
} else if (name === 'shieldboost' || name === 'hullboost') {
|
||||
result = (1 + result) * (1 + modValue) - 1;
|
||||
} else {
|
||||
// Rate of fire modifiers are special as they actually are modifiers
|
||||
// for fire interval. Translate them accordingly here:
|
||||
if (name == 'rof') {
|
||||
modValue = 1 / (1 + modValue) - 1;
|
||||
}
|
||||
result = result * (1 + modValue);
|
||||
}
|
||||
} else if (name === 'burstrof') {
|
||||
} else if (name === 'burstrof' || name === 'burst') {
|
||||
// Burst and burst rate of fire are special, as it can not exist but
|
||||
// have a modification
|
||||
result = modValue / 100;
|
||||
result = modValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,11 +288,7 @@ export default class Module {
|
||||
formatModifiedValue(name, language, unit, val) {
|
||||
const formattingOptions = STATS_FORMATTING[name];
|
||||
if (val === undefined) {
|
||||
if (formattingOptions && formattingOptions.synthetic) {
|
||||
val = (this[formattingOptions.synthetic]).call(this, true);
|
||||
} else {
|
||||
val = this._getModifiedValue(name);
|
||||
}
|
||||
val = this.getPretty(name, true);
|
||||
}
|
||||
|
||||
val = val || 0;
|
||||
@@ -368,15 +358,19 @@ export default class Module {
|
||||
|
||||
if (formattingOptions && formattingOptions.change) {
|
||||
let changeFormatting = formattingOptions.change;
|
||||
let baseVal = this[name];
|
||||
let baseVal = this[name] || 0;
|
||||
let absVal = this._getModifiedValue(name);
|
||||
if (changeFormatting === 'additive') {
|
||||
val = absVal - baseVal;
|
||||
} else if (changeFormatting === 'multiplicative') {
|
||||
val = absVal / baseVal - 1;
|
||||
}
|
||||
if (Modifications.modifications[name].method === 'overwrite') {
|
||||
val *= 100;
|
||||
} else {
|
||||
val *= 10000;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -589,20 +583,9 @@ export default class Module {
|
||||
* @return {Number} the falloff of this module
|
||||
*/
|
||||
getFalloff(modified = true) {
|
||||
if (!modified) {
|
||||
const range = this.getRange(false);
|
||||
const falloff = this.get('falloff', false);
|
||||
return (falloff > range ? range : falloff);
|
||||
}
|
||||
|
||||
// Falloff from range is mapped to range
|
||||
if (this.mods && this.mods['fallofffromrange']) {
|
||||
if (modified && this.mods && this.mods['fallofffromrange']) {
|
||||
return this.getRange();
|
||||
// Need to find out if we have a focused modification, in which case our
|
||||
// falloff is scaled to range
|
||||
} else if (this.blueprint && this.blueprint.name === 'Focused') {
|
||||
const rangeMod = this.getModValue('range') / 10000;
|
||||
return this.falloff * (1 + rangeMod);
|
||||
// Standard falloff calculation
|
||||
} else {
|
||||
const range = this.getRange();
|
||||
@@ -656,15 +639,6 @@ export default class Module {
|
||||
return this.get('protection', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the delay for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {Number} the delay of this module
|
||||
*/
|
||||
getDelay(modified = true) {
|
||||
return this.get('delay', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the duration for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
@@ -720,8 +694,7 @@ export default class Module {
|
||||
let result = 0;
|
||||
if (this['maxmass']) {
|
||||
result = this['maxmass'];
|
||||
// max mass is only modified for non-shield boosters
|
||||
if (result && modified && this.grp !== 'sg') {
|
||||
if (result && modified) {
|
||||
let mult = this.getModValue('optmass') / 10000;
|
||||
if (mult) { result = result * (1 + mult); }
|
||||
}
|
||||
@@ -810,24 +783,6 @@ export default class Module {
|
||||
return this.get('distdraw', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thermal load for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {Number} the thermal load of this module
|
||||
*/
|
||||
getThermalLoad(modified = true) {
|
||||
return this.get('thermload', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rounds per shot for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {Number} the rounds per shot of this module
|
||||
*/
|
||||
getRoundsPerShot(modified = true) {
|
||||
return this.get('roundspershot', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DPS for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
@@ -851,25 +806,39 @@ export default class Module {
|
||||
return this.getDps(modified) / this.getEps(modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the factor that gets applied when calculating certain "sustained"
|
||||
* values, e.g. `SDPS = this.getSustainedFactor() * DPS`.
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
*/
|
||||
getSustainedFactor(modified = true) {
|
||||
let clipSize = this.getClip(modified);
|
||||
if (clipSize) {
|
||||
// If auto-loader is applied, effective clip size will be nearly doubled
|
||||
// as you get one reload for every two shots fired.
|
||||
if (this.blueprint && this.blueprint.special && this.blueprint.special.key === 'special_auto_loader' && modified) {
|
||||
clipSize += clipSize - 1;
|
||||
}
|
||||
|
||||
let burstSize = this.get('burst', modified) || 1;
|
||||
let rof = this.getRoF(modified);
|
||||
// rof averages burstfire + pause until next interval but for sustained
|
||||
// rof we need to take another burst without pause into account
|
||||
let burstOverhead = (burstSize - 1) / (this.get('burstrof', modified) || 1);
|
||||
let srof = clipSize / ((clipSize - burstSize) / rof + burstOverhead + this.getReload(modified));
|
||||
return srof / rof;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SDPS for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {Number} The SDPS of this module
|
||||
*/
|
||||
getSDps(modified = true) {
|
||||
let dps = this.getDps(modified);
|
||||
if (this.getClip(modified)) {
|
||||
let clipSize = this.getClip(modified);
|
||||
// If auto-loader is applied, effective clip size will be nearly doubled
|
||||
// as you get one reload for every two shots fired.
|
||||
if (this.blueprint && this.blueprint.special && this.blueprint.special.edname === 'special_auto_loader' && modified) {
|
||||
clipSize += clipSize - 1;
|
||||
}
|
||||
let timeToDeplete = clipSize / this.getRoF(modified);
|
||||
return dps * timeToDeplete / (timeToDeplete + this.getReload(modified));
|
||||
} else {
|
||||
return dps;
|
||||
}
|
||||
return this.getDps(modified) * this.getSustainedFactor(modified);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -893,7 +862,7 @@ export default class Module {
|
||||
*/
|
||||
getHps(modified = true) {
|
||||
// HPS is a synthetic value
|
||||
let heat = this.getThermalLoad(modified);
|
||||
let heat = this.get('thermload', modified);
|
||||
// We don't use rpshot here as dist draw is per combined shot
|
||||
let rof = this.getRoF(modified) || 1;
|
||||
|
||||
@@ -930,24 +899,6 @@ export default class Module {
|
||||
return this.get('reload', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the burst size for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {Number} the burst size of this module
|
||||
*/
|
||||
getBurst(modified = true) {
|
||||
return this.get('burst', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the burst rate of fire for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {Number} the burst rate of fire of this module
|
||||
*/
|
||||
getBurstRoF(modified = true) {
|
||||
return this.get('burstrof', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rate of fire for this module.
|
||||
* The rate of fire is a combination value, and needs to take in to account
|
||||
@@ -958,8 +909,8 @@ export default class Module {
|
||||
* @return {Number} the rate of fire for this module
|
||||
*/
|
||||
getRoF(modified = true) {
|
||||
const burst = this.getBurst(modified) || 1;
|
||||
const burstRoF = this.getBurstRoF(modified) || 1;
|
||||
const burst = this.get('burst', modified) || 1;
|
||||
const burstRoF = this.get('burstrof', modified) || 1;
|
||||
const intRoF = this.get('rof', modified);
|
||||
|
||||
return burst / (((burst - 1) / burstRoF) + 1 / intRoF);
|
||||
@@ -1090,4 +1041,31 @@ export default class Module {
|
||||
getHackTime(modified = true) {
|
||||
return this.get('hacktime', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scan range for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {string} the time for this module
|
||||
*/
|
||||
getScanRange(modified = true) {
|
||||
return this.get('scanrange', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scan angle for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {string} the time for this module
|
||||
*/
|
||||
getScanAngle(modified = true) {
|
||||
return this.get('scanangle', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the max angle for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {string} the time for this module
|
||||
*/
|
||||
getMaxAngle(modified = true) {
|
||||
return this.get('maxangle', modified);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,10 @@ export function standard(type, id) {
|
||||
if (!isNaN(type)) {
|
||||
type = StandardArray[type];
|
||||
}
|
||||
let s = Modules.standard[type].find(e => e.id == id || (e.class == id.charAt(0) && e.rating == id.charAt(1)));
|
||||
let s = Modules.standard[type].find(e => e.id === id);
|
||||
if (!s) {
|
||||
s = Modules.standard[type].find(e => (e.class == id.charAt(0) && e.rating == id.charAt(1)));
|
||||
}
|
||||
if (s) {
|
||||
s = new Module({ template: s });
|
||||
}
|
||||
|
||||
@@ -85,12 +85,12 @@ export function toDetailedBuild(buildName, ship) {
|
||||
code = ship.toString();
|
||||
|
||||
let data = {
|
||||
$schema: 'https://coriolis.edcd.io/schemas/ship-loadout/4.json#',
|
||||
$schema: 'https://coriolis.io/schemas/ship-loadout/4.json#',
|
||||
name: buildName,
|
||||
ship: ship.name,
|
||||
references: [{
|
||||
name: 'Coriolis.io',
|
||||
url: 'https://coriolis.edcd.io' + outfitURL(ship.id, code, buildName),
|
||||
url: 'https://coriolis.io' + outfitURL(ship.id, code, buildName),
|
||||
code,
|
||||
shipId: ship.id
|
||||
}],
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Ships, Modifications } from 'coriolis-data/dist';
|
||||
import { chain } from 'lodash';
|
||||
const zlib = require('zlib');
|
||||
|
||||
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh', 'gfsb'];
|
||||
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh', 'gfsb', 'dc'];
|
||||
|
||||
// Constants for modifications struct
|
||||
const SLOT_ID_DONE = -1;
|
||||
@@ -505,6 +505,11 @@ export default class Ship {
|
||||
if (isAbsolute) {
|
||||
m.setPretty(name, value, sentfromui);
|
||||
} else {
|
||||
// Resistance modifiers scale with the base value
|
||||
if (name == 'kinres' || name == 'thermres' || name == 'causres' || name == 'explres') {
|
||||
let baseValue = m.get(name, false);
|
||||
value = (1 - baseValue) * value;
|
||||
}
|
||||
m.setModValue(name, value, sentfromui);
|
||||
}
|
||||
|
||||
@@ -1303,7 +1308,7 @@ export default class Ship {
|
||||
let fsd = this.standard[2].m; // Frame Shift Drive;
|
||||
let { unladenMass, fuelCapacity } = this;
|
||||
this.unladenRange = this.calcUnladenRange(); // Includes fuel weight for jump
|
||||
this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd, this); // Full Tank
|
||||
this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd, fuelCapacity, this); // Full Tank
|
||||
this.ladenRange = this.calcLadenRange(); // Includes full tank and caro
|
||||
this.unladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity, fsd, fuelCapacity, this);
|
||||
this.ladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity + this.cargoCapacity, fsd, fuelCapacity, this);
|
||||
@@ -1501,7 +1506,7 @@ export default class Ship {
|
||||
} else {
|
||||
buffer.writeInt32LE(slotMod.value, curpos);
|
||||
}
|
||||
// const modification = _.find(Modifications.modifications, function(o) { return o.id === slotMod.id; });
|
||||
const modification = _.find(Modifications.modifications, function(o) { return o.id === slotMod.id; });
|
||||
// console.log('ENCODE Slot ' + i + ': ' + modification.name + ' = ' + slotMod.value);
|
||||
curpos += 4;
|
||||
}
|
||||
@@ -1514,6 +1519,7 @@ export default class Ship {
|
||||
}
|
||||
|
||||
this.serialized.modifications = zlib.gzipSync(buffer).toString('base64');
|
||||
// console.log(this.serialized.modifications)
|
||||
} else {
|
||||
this.serialized.modifications = null;
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ export const STATS_FORMATTING = {
|
||||
'ammo': { 'format': 'int', },
|
||||
'boot': { 'format': 'int', 'unit': 'secs' },
|
||||
'brokenregen': { 'format': 'round1', 'unit': 'ps' },
|
||||
'burst': { 'format': 'int' },
|
||||
'burstrof': { 'format': 'round1', 'unit': 'ps' },
|
||||
'burst': { 'format': 'int', 'change': 'additive' },
|
||||
'burstrof': { 'format': 'round1', 'unit': 'ps', 'change': 'additive' },
|
||||
'causres': { 'format': 'pct' },
|
||||
'clip': { 'format': 'int' },
|
||||
'damage': { 'format': 'round' },
|
||||
@@ -61,7 +61,7 @@ export const STATS_FORMATTING = {
|
||||
'ranget': { 'format': 'f1', 'unit': 's' },
|
||||
'regen': { 'format': 'round1', 'unit': 'ps' },
|
||||
'reload': { 'format': 'int', 'unit': 's' },
|
||||
'rof': { 'format': 'round1', 'unit': 'ps' },
|
||||
'rof': { 'format': 'round1', 'unit': 'ps', 'synthetic': 'getRoF', 'higherbetter': true },
|
||||
'angle': { 'format': 'round1', 'unit': 'ang' },
|
||||
'scanrate': { 'format': 'int' },
|
||||
'scantime': { 'format': 'round1', 'unit': 's' },
|
||||
@@ -78,5 +78,6 @@ export const STATS_FORMATTING = {
|
||||
'thermres': { 'format': 'pct' },
|
||||
'wepcap': { 'format': 'round1', 'unit': 'MJ' },
|
||||
'weprate': { 'format': 'round1', 'unit': 'MW' },
|
||||
'jumpboost': { 'format': 'round1', 'unit': 'LY' }
|
||||
'jumpboost': { 'format': 'round1', 'unit': 'LY' },
|
||||
'proberadius': { 'format': 'pct1', 'unit': 'pct' },
|
||||
};
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { EventEmitter } from 'fbemitter';
|
||||
import { Insurance } from '../shipyard/Constants';
|
||||
import { buildsCollection, database } from '../model';
|
||||
import {Q} from '@nozbe/watermelondb';
|
||||
|
||||
const LS_KEY_BUILDS = 'builds';
|
||||
const LS_KEY_COMPARISONS = 'comparisons';
|
||||
@@ -83,7 +81,7 @@ export class Persist extends EventEmitter {
|
||||
localStorage.setItem('test', 'test');
|
||||
localStorage.removeItem('test');
|
||||
LS = localStorage;
|
||||
} catch (e) {
|
||||
} catch(e) {
|
||||
LS = null;
|
||||
}
|
||||
|
||||
@@ -134,7 +132,7 @@ export class Persist extends EventEmitter {
|
||||
let newValue = e.newValue;
|
||||
|
||||
try {
|
||||
switch (e.key) {
|
||||
switch(e.key) {
|
||||
case LS_KEY_BUILDS:
|
||||
this.builds = newValue ? JSON.parse(newValue) : {};
|
||||
this.emit('builds');
|
||||
@@ -251,27 +249,17 @@ export class Persist extends EventEmitter {
|
||||
/**
|
||||
* Persist a ship build in local storage.
|
||||
*
|
||||
* @param {String} id The unique id for the ship or ''
|
||||
* @param {String} shipId The coriolis ship id
|
||||
* @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
|
||||
*/
|
||||
async saveBuild(id, name, code, shipId) {
|
||||
if (id) {
|
||||
const build = await buildsCollection.find(id);
|
||||
if (build) {
|
||||
return build.update(newBuild => {
|
||||
newBuild.title = name;
|
||||
newBuild.body = code;
|
||||
});
|
||||
saveBuild(shipId, name, code) {
|
||||
if (!this.builds[shipId]) {
|
||||
this.builds[shipId] = {};
|
||||
}
|
||||
}
|
||||
|
||||
return await buildsCollection.create(build => {
|
||||
build.title = name;
|
||||
build.ship_id = shipId;
|
||||
build.body = code;
|
||||
});
|
||||
this.builds[shipId][name] = code;
|
||||
_put(LS_KEY_BUILDS, this.builds);
|
||||
this.emit('builds');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,10 +270,11 @@ export class Persist extends EventEmitter {
|
||||
* @param {String} name The name of the build
|
||||
* @return {String} The serialized build string.
|
||||
*/
|
||||
async getBuild(shipId, name) {
|
||||
const build = await buildsCollection.query(Q.where('ship_id', shipId), Q.where('title', name)).fetch();
|
||||
console.log(build);
|
||||
return build;
|
||||
getBuild(shipId, name) {
|
||||
if (this.builds[shipId] && this.builds[shipId][name]) {
|
||||
return this.builds[shipId][name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -294,7 +283,7 @@ export class Persist extends EventEmitter {
|
||||
* @return {Object | Array} Object if Ship Id is not provided
|
||||
*/
|
||||
getBuilds(shipId) {
|
||||
if (shipId && shipId.length > 0) {
|
||||
if(shipId && shipId.length > 0) {
|
||||
return this.builds[shipId];
|
||||
}
|
||||
return this.builds;
|
||||
@@ -373,9 +362,7 @@ export class Persist extends EventEmitter {
|
||||
}
|
||||
this.comparisons[name] = {
|
||||
facets,
|
||||
builds: builds.map(b => {
|
||||
return { shipId: b.id || b.shipId, buildName: b.buildName };
|
||||
})
|
||||
builds: builds.map(b => { return { shipId: b.id || b.shipId, buildName: b.buildName }; })
|
||||
};
|
||||
_put(LS_KEY_COMPARISONS, this.comparisons);
|
||||
this.emit('comparisons');
|
||||
@@ -518,7 +505,6 @@ export class Persist extends EventEmitter {
|
||||
_put(LS_KEY_ROLLS, this.matsPerGrade);
|
||||
this.emit('matsPerGrade');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the saved Mats per grade
|
||||
* @return {Object} # of rolls per grade
|
||||
@@ -570,7 +556,6 @@ export class Persist extends EventEmitter {
|
||||
this.outfittingTab = tabName;
|
||||
_put(LS_KEY_OUTFITTING_TAB, tabName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current outfitting tab
|
||||
* @return {string} the current outfitting tab
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Modifications } from 'coriolis-data/dist';
|
||||
import { STATS_FORMATTING } from '../shipyard/StatsFormatting';
|
||||
|
||||
/**
|
||||
* Generate a tooltip with details of a blueprint's specials
|
||||
@@ -155,7 +156,7 @@ export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
|
||||
|
||||
// We also add in any benefits from specials that aren't covered above
|
||||
if (m.blueprint && m.blueprint.special) {
|
||||
for (const feature in Modifications.modifierActions[m.blueprint.special.edname]) {
|
||||
for (const feature in Modifications.modifierActions[m.blueprint.special.key]) {
|
||||
if (!blueprint.features[feature] && !m.mods.feature) {
|
||||
const featureDef = Modifications.modifications[feature];
|
||||
if (featureDef && !featureDef.hidden) {
|
||||
@@ -280,6 +281,25 @@ export function isValueBeneficial(feature, value) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the change as shown beneficial?
|
||||
* @param {string} feature The name of the feature
|
||||
* @param {number} value The value of the feature as percentage change
|
||||
* @returns True if the value is beneficial
|
||||
*/
|
||||
export function isChangeValueBeneficial(feature, value) {
|
||||
let changeHigherBetter = STATS_FORMATTING[feature].higherbetter;
|
||||
if (changeHigherBetter === undefined) {
|
||||
return isValueBeneficial(feature, value);
|
||||
}
|
||||
|
||||
if (changeHigherBetter) {
|
||||
return value > 0;
|
||||
} else {
|
||||
return value < 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a blueprint with a given name and an optional module
|
||||
* @param {string} name The name of the blueprint
|
||||
@@ -372,9 +392,7 @@ export function getPercent(m) {
|
||||
|
||||
let value = _getValue(m, featureName);
|
||||
let mult;
|
||||
if (featureName == 'shieldboost') {
|
||||
mult = ((1 + value) * (1 + m.shieldboost)) - 1 - m.shieldboost;
|
||||
} else if (Modifications.modifications[featureName].higherbetter) {
|
||||
if (Modifications.modifications[featureName].higherbetter) {
|
||||
// Higher is better, but is this making it better or worse?
|
||||
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
||||
mult = Math.round((value - features[featureName][1]) / (features[featureName][0] - features[featureName][1]) * 100);
|
||||
|
||||
@@ -30,6 +30,8 @@ export const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
|
||||
'Hauler': 'hauler',
|
||||
'Independant_Trader': 'keelback',
|
||||
'Krait_MkII': 'krait_mkii',
|
||||
'Mamba': 'mamba',
|
||||
'Krait_Light': 'krait_phantom',
|
||||
'Orca': 'orca',
|
||||
'Python': 'python',
|
||||
'SideWinder': 'sidewinder',
|
||||
|
||||
@@ -249,6 +249,13 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
|
||||
if (!modifiers) return;
|
||||
let special;
|
||||
if (specialModifications) {
|
||||
if (specialModifications == 'special_plasma_slug') {
|
||||
if (module.symbol.match(/PlasmaAccelerator/i)) {
|
||||
specialModifications = 'special_plasma_slug_pa';
|
||||
} else {
|
||||
specialModifications = 'special_plasma_slug_cooled';
|
||||
}
|
||||
}
|
||||
special = Modifications.specials[specialModifications];
|
||||
}
|
||||
// Add the blueprint definition, grade and special
|
||||
@@ -274,6 +281,9 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
|
||||
if (value === Infinity) {
|
||||
value = modifiers[i].Value * 100;
|
||||
}
|
||||
if (modifiers[i].Label.search('DamageFalloffRange') >= 0) {
|
||||
value = (modifiers[i].Value / module.range - 1) * 100;
|
||||
}
|
||||
if (modifiers[i].Label.search('Resistance') >= 0) {
|
||||
value = (modifiers[i].Value * 100) - (modifiers[i].OriginalValue * 100);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import Persist from '../stores/Persist';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import Module from '../shipyard/Module';
|
||||
|
||||
/**
|
||||
* Determine if a slot on a ship can mount a module of a particular class and group
|
||||
@@ -139,20 +140,21 @@ function diff(format, mVal, mmVal) {
|
||||
export function diffDetails(language, m, mm) {
|
||||
let { formats, translate, units } = language;
|
||||
let propDiffs = [];
|
||||
m = new Module(m);
|
||||
|
||||
// Module-specific items
|
||||
|
||||
if (m.grp === 'pp') {
|
||||
let mPowerGeneration = m.pgen || 0;
|
||||
let mPowerGeneration = m.getPowerGeneration() || 0;
|
||||
let mmPowerGeneration = mm ? mm.getPowerGeneration() : 0;
|
||||
if (mPowerGeneration != mmPowerGeneration) propDiffs.push(<div key='pgen'>{translate('pgen')}: <span className={diffClass(mPowerGeneration, mmPowerGeneration)}>{diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MJ}</span></div>);
|
||||
if (mPowerGeneration != mmPowerGeneration) propDiffs.push(<div key='pgen'>{translate('pgen')}: <span className={diffClass(mPowerGeneration, mmPowerGeneration)}>{diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MW}</span></div>);
|
||||
} else {
|
||||
let mPowerUsage = m.power || 0;
|
||||
let mPowerUsage = m.getPowerUsage() || 0;
|
||||
let mmPowerUsage = mm ? mm.getPowerUsage() || 0 : 0;
|
||||
if (mPowerUsage != mmPowerUsage) propDiffs.push(<div key='power'>{translate('power')}: <span className={diffClass(mPowerUsage, mmPowerUsage, true)}>{diff(formats.round, mPowerUsage, mmPowerUsage)}{units.MJ}</span></div>);
|
||||
if (mPowerUsage != mmPowerUsage) propDiffs.push(<div key='power'>{translate('power')}: <span className={diffClass(mPowerUsage, mmPowerUsage, true)}>{diff(formats.round, mPowerUsage, mmPowerUsage)}{units.MW}</span></div>);
|
||||
}
|
||||
|
||||
let mDps = m.damage * (m.rpshot || 1) * (m.rof || 1);
|
||||
let mDps = m.getDps() || 0;
|
||||
let mmDps = mm ? mm.getDps() || 0 : 0;
|
||||
if (mDps && mDps != mmDps) propDiffs.push(<div key='dps'>{translate('dps')}: <span className={diffClass(mmDps, mDps, true)}>{diff(formats.round, mDps, mmDps)}</span></div>);
|
||||
|
||||
@@ -164,7 +166,7 @@ export function diffDetails(language, m, mm) {
|
||||
|
||||
if (mAffectsShield) {
|
||||
if (m.grp == 'sb') { // Both m and mm must be utility modules if this is true
|
||||
newShield = this.calcShieldStrengthWith(null, m.shieldboost - (mm ? mm.getShieldBoost() || 0 : 0));
|
||||
newShield = this.calcShieldStrengthWith(null, m.getShieldBoost() - (mm ? mm.getShieldBoost() || 0 : 0));
|
||||
} else {
|
||||
newShield = this.calcShieldStrengthWith(m);
|
||||
}
|
||||
@@ -179,7 +181,7 @@ export function diffDetails(language, m, mm) {
|
||||
}
|
||||
|
||||
if (m.grp === 'mrp') {
|
||||
let mProtection = m.protection;
|
||||
let mProtection = m.getProtection();
|
||||
let mmProtection = mm ? mm.getProtection() || 0 : 0;
|
||||
if (mProtection != mmProtection) {
|
||||
propDiffs.push(<div key='protection'>{translate('protection')}: <span className={diffClass(mmProtection, mProtection, true)}>{diff(formats.pct, mProtection, mmProtection)}</span></div>);
|
||||
@@ -187,7 +189,7 @@ export function diffDetails(language, m, mm) {
|
||||
}
|
||||
|
||||
if (m.grp === 'hr') {
|
||||
let mHullReinforcement = m.hullreinforcement;
|
||||
let mHullReinforcement = m.getHullReinforcement();
|
||||
let mmHullReinforcement = mm ? mm.getHullReinforcement() || 0 : 0;
|
||||
if (mHullReinforcement && mHullReinforcement != mmHullReinforcement) propDiffs.push(<div key='hullreinforcement'>{translate('hullreinforcement')}: <span className={diffClass(mmHullReinforcement, mHullReinforcement, true)}>{diff(formats.round, mHullReinforcement, mmHullReinforcement)}</span></div>);
|
||||
}
|
||||
@@ -219,7 +221,7 @@ export function diffDetails(language, m, mm) {
|
||||
let mmCost = mm ? mm.cost : 0;
|
||||
if (mCost != mmCost) propDiffs.push(<div key='cost'>{translate('cost')}: <span className={diffClass(mCost, mmCost, true) }>{formats.int(mCost ? Math.round(mCost * (1 - Persist.getModuleDiscount())) : 0)}{units.CR}</span></div>);
|
||||
|
||||
let mMass = m.mass || 0;
|
||||
let mMass = m.getMass() || 0;
|
||||
let mmMass = mm ? mm.getMass() : 0;
|
||||
if (mMass != mmMass) propDiffs.push(<div key='mass'>{translate('mass')}: <span className={diffClass(mMass, mmMass, true)}>{diff(formats.round, mMass, mmMass)}{units.T}</span></div>);
|
||||
|
||||
@@ -240,7 +242,7 @@ export function diffDetails(language, m, mm) {
|
||||
}
|
||||
}
|
||||
|
||||
let mIntegrity = m.integrity || 0;
|
||||
let mIntegrity = m.getIntegrity() || 0;
|
||||
let mmIntegrity = mm ? mm.getIntegrity() || 0 : 0;
|
||||
if (mIntegrity != mmIntegrity) {
|
||||
propDiffs.push(<div key='integrity'>{translate('integrity')}: <span className={diffClass(mmIntegrity, mIntegrity, true)}>{diff(formats.round, mIntegrity, mmIntegrity)}</span></div>);
|
||||
|
||||
9
src/iframe.html
Normal file
9
src/iframe.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="xdLocalStoragePostMessageApi.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
This is the magical iframe
|
||||
</body>
|
||||
</html>
|
||||
@@ -32,36 +32,20 @@
|
||||
window.CORIOLIS_DATE = '<%- htmlWebpackPlugin.options.date.toISOString().slice(0, 10) %>';
|
||||
window.BUGSNAG_VERSION = '<%- htmlWebpackPlugin.options.version + '-' + htmlWebpackPlugin.options.date.toISOString() %>';
|
||||
</script>
|
||||
<% if (htmlWebpackPlugin.options.uaTracking) { %>
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-55840909-18"></script>
|
||||
<script>
|
||||
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
||||
ga('create', '<%- htmlWebpackPlugin.options.uaTracking %>', 'auto');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
<script async src='https://www.google-analytics.com/analytics.js'></script>
|
||||
<% } %>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'UA-55840909-18');
|
||||
</script>
|
||||
|
||||
|
||||
<!-- Piwik -->
|
||||
<!-- <script type="text/javascript">
|
||||
var _paq = _paq || [];
|
||||
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||
_paq.push(["setCookieDomain", "*.coriolis.edcd.io"]);
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function() {
|
||||
var u="//stats.isadankme.me/";
|
||||
_paq.push(['setTrackerUrl', u+'piwik.php']);
|
||||
_paq.push(['setSiteId', '4']);
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
</script>-->
|
||||
<!-- End Piwik Code -->
|
||||
|
||||
<!-- Bugsnag -->
|
||||
<script src="//d2wy8f7a9ursnm.cloudfront.net/v4/bugsnag.min.js"></script>
|
||||
<script
|
||||
src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-plugins/v1/bugsnag-react.min.js"></script>
|
||||
<script src="https://d2wy8f7a9ursnm.cloudfront.net/v5.0.0/bugsnag.min.js"></script>
|
||||
<script>
|
||||
window.bugsnagClient = bugsnag('ba9fae819372850fb660755341fa6ef5', {appVersion: window.BUGSNAG_VERSION || undefined})
|
||||
window.Bugsnag = window.bugsnagClient
|
||||
|
||||
@@ -171,3 +171,16 @@ footer {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.announcement-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: 10px;
|
||||
justify-content: center;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
|
||||
.announcement {
|
||||
border: 1px @secondary solid;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
@bgDarken: 40%;
|
||||
@disabledDarken: 15%;
|
||||
@bgTransparency: 10%;
|
||||
@bgHighlight: 5%;
|
||||
@fgHighlight: 10%;
|
||||
|
||||
// Foreground colors
|
||||
@fg: #CCC;
|
||||
@@ -21,9 +23,14 @@
|
||||
@bgBlack: #000;
|
||||
@primary-bg: fadeout(darken(@primary, 47%), 15%);
|
||||
@alt-primary-bg: fadeout(darken(@primary, 42%), 15%); // Lighter brown background
|
||||
@secondary-bg: fadeout(darken(@secondary, @bgDarken), @bgTransparency); // Brown background
|
||||
@secondary-bg: fadeout(darken(@secondary, @bgDarken), @bgTransparency); // Blue background
|
||||
@warning-bg: fadeout(darken(@warning, @bgDarken), @bgTransparency); // Dark Red
|
||||
|
||||
@alt-primary-bg-highlighted: lighten(@alt-primary-bg, @bgHighlight);
|
||||
@fg-highlighted: lighten(@fg, @fgHighlight);
|
||||
@primary-darker: darken(@primary, 30%);
|
||||
|
||||
|
||||
|
||||
.fg {
|
||||
color: @fg;
|
||||
|
||||
@@ -54,7 +54,7 @@ textarea {
|
||||
width:100%;
|
||||
min-height: 10em;
|
||||
resize: vertical;
|
||||
user-select: auto;
|
||||
user-select: text;
|
||||
margin:2em 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,15 @@ select {
|
||||
}
|
||||
}
|
||||
|
||||
.react-fuzzy-search > * {
|
||||
padding: 0 !important;
|
||||
color: @primary;
|
||||
& > input {
|
||||
border: 1px solid @primary !important;
|
||||
color: @primary-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.cmdr-select {
|
||||
border: 1px solid @primary;
|
||||
padding: 0.5em 0.5em;
|
||||
|
||||
@@ -50,3 +50,44 @@ a.ship {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.shipyard-table-wrapper {
|
||||
white-space: nowrap;
|
||||
margin: 0 auto;
|
||||
font-size: 0.8em;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
table.shipyard-table{
|
||||
tbody tr.comparehighlight{
|
||||
background-color: @secondary-bg;
|
||||
color: @fg-highlighted;
|
||||
}
|
||||
}
|
||||
|
||||
.shipyard-table-wrapper {
|
||||
border-bottom: 1px solid @primary-darker;
|
||||
}
|
||||
|
||||
.shipyard-table-wrapper div .shipyard-table td:last-child {
|
||||
border-right: 1px solid @primary-darker;
|
||||
}
|
||||
.shipyard-table-wrapper > .shipyard-table td:first-child {
|
||||
border-left: 1px solid @primary-darker;
|
||||
}
|
||||
|
||||
.content-wrapper{
|
||||
display: inline-block;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.table-tools{
|
||||
text-align: left;
|
||||
color: @primary;
|
||||
label{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,10 +52,10 @@ tbody tr {
|
||||
}
|
||||
|
||||
.no-touch &.highlight:hover, .no-touch &.highlighted {
|
||||
background-color: @warning-bg;
|
||||
background-color: @alt-primary-bg-highlighted; //@warning-bg;
|
||||
}
|
||||
|
||||
&.alt {
|
||||
&:nth-child(odd){
|
||||
background-color: @alt-primary-bg;
|
||||
}
|
||||
}
|
||||
@@ -84,3 +84,5 @@ td {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "https://coriolis.edcd.io/schemas/ship-loadout/1.json#",
|
||||
"id": "https://coriolis.io/schemas/ship-loadout/1.json#",
|
||||
"title": "Ship Loadout",
|
||||
"type": "object",
|
||||
"description": "The details for a specific ship build/loadout. DEPRECATED in favor of Version 3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "https://coriolis.edcd.io/schemas/ship-loadout/2.json#",
|
||||
"id": "https://coriolis.io/schemas/ship-loadout/2.json#",
|
||||
"title": "Ship Loadout",
|
||||
"type": "object",
|
||||
"description": "The details for a specific ship build/loadout. DEPRECATED in favor of Version 3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "https://coriolis.edcd.io/schemas/ship-loadout/3.json#",
|
||||
"id": "https://coriolis.io/schemas/ship-loadout/3.json#",
|
||||
"title": "Ship Loadout",
|
||||
"type": "object",
|
||||
"description": "The details for a specific ship build/loadout",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "https://coriolis.edcd.io/schemas/ship-loadout/4.json#",
|
||||
"id": "https://coriolis.io/schemas/ship-loadout/4.json#",
|
||||
"title": "Ship Loadout",
|
||||
"type": "object",
|
||||
"description": "The details for a specific ship build/loadout",
|
||||
|
||||
@@ -2,9 +2,31 @@ console.log('Hello from sw.js');
|
||||
|
||||
if (workbox) {
|
||||
console.log('Yay! Workbox is loaded 🎉');
|
||||
workbox.precaching.precacheAndRoute(self.__precacheManifest);
|
||||
|
||||
workbox.routing.registerNavigationRoute('/index.html');
|
||||
|
||||
workbox.routing.registerRoute(
|
||||
/\.(?:png|jpg|jpeg|svg|gif)$/,
|
||||
new workbox.strategies.CacheFirst({
|
||||
plugins: [
|
||||
new workbox.cacheableResponse.Plugin({
|
||||
statuses: [0, 200]
|
||||
})
|
||||
]
|
||||
})
|
||||
);
|
||||
|
||||
workbox.routing.registerRoute(
|
||||
/\.(?:js|css)$/,
|
||||
new workbox.strategies.StaleWhileRevalidate({
|
||||
cacheName: 'static-resources',
|
||||
})
|
||||
);
|
||||
|
||||
workbox.routing.registerRoute(
|
||||
new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
|
||||
workbox.strategies.cacheFirst({
|
||||
new workbox.strategies.CacheFirst({
|
||||
cacheName: 'google-fonts',
|
||||
plugins: [
|
||||
new workbox.expiration.Plugin({
|
||||
@@ -16,12 +38,6 @@ if (workbox) {
|
||||
]
|
||||
})
|
||||
);
|
||||
|
||||
try {
|
||||
workbox.googleAnalytics.initialize();
|
||||
} catch (e) {
|
||||
console.log('Probably an ad-blocker');
|
||||
}
|
||||
} else {
|
||||
console.log('Boo! Workbox didn\'t load 😬');
|
||||
}
|
||||
@@ -40,23 +56,3 @@ self.addEventListener('message', event => {
|
||||
break;
|
||||
}
|
||||
});
|
||||
const OFFLINE_URL = '/';
|
||||
self.addEventListener('fetch', function(event) {
|
||||
console.log('Handling fetch event for', event.request.url);
|
||||
|
||||
event.respondWith(
|
||||
caches.match(event.request).then(function(response) {
|
||||
if (response) {
|
||||
return response;
|
||||
}
|
||||
|
||||
return fetch(event.request)
|
||||
.then(function(response) {
|
||||
return response;
|
||||
})
|
||||
.catch(function(error) {
|
||||
return caches.match(OFFLINE_URL);
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
1
src/xdLocalStoragePostMessageApi.min.js
vendored
Normal file
1
src/xdLocalStoragePostMessageApi.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";window.XdUtils=window.XdUtils||function(){function a(a,b){var c,d=b||{};for(c in a)a.hasOwnProperty(c)&&(d[c]=a[c]);return d}return{extend:a}}(),function(){function a(a,b){var c=XdUtils.extend(b,l);c.id=a,parent.postMessage(JSON.stringify(c),"*")}function b(b,c){a(b,{key:c,value:localStorage.getItem(c)})}function c(b,c,d){localStorage.setItem(c,d),a(b,{success:localStorage.getItem(c)===d})}function d(b,c){localStorage.removeItem(c),a(b,{})}function e(b,c){a(b,{key:localStorage.key(c)})}function f(b){a(b,{size:JSON.stringify(localStorage).length})}function g(b){a(b,{length:localStorage.length})}function h(b){localStorage.clear(),a(b,{})}function i(a){var i;try{i=JSON.parse(a.data)}catch(a){}i&&i.namespace===k&&("set"===i.action?c(i.id,i.key,i.value):"get"===i.action?b(i.id,i.key):"remove"===i.action?d(i.id,i.key):"key"===i.action?e(i.id,i.key):"size"===i.action?f(i.id):"length"===i.action?g(i.id):"clear"===i.action&&h(i.id))}function j(){var a={namespace:k,id:"iframe-ready"};parent.postMessage(JSON.stringify(a),"*")}var k="cross-domain-local-message",l={namespace:k};window.addEventListener?window.addEventListener("message",i,!1):window.attachEvent("onmessage",i),j()}();
|
||||
@@ -14,7 +14,7 @@ module.exports = {
|
||||
},
|
||||
mode: 'development',
|
||||
entry: {
|
||||
main: './src/app/index.js'
|
||||
main: './src/app/index.js',
|
||||
},
|
||||
resolve: {
|
||||
// When requiring, you don't need to add these extensions
|
||||
@@ -22,24 +22,15 @@ module.exports = {
|
||||
},
|
||||
optimization: {
|
||||
minimize: false,
|
||||
splitChunks: {
|
||||
chunks: 'all'
|
||||
}
|
||||
usedExports: true
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, 'build'),
|
||||
filename: 'app.js',
|
||||
globalObject: 'this',
|
||||
chunkFilename: '[name].bundle.js',
|
||||
publicPath: '/'
|
||||
},
|
||||
node: {
|
||||
fs: 'empty',
|
||||
net: 'empty',
|
||||
tls: 'empty',
|
||||
'crypto': 'empty'
|
||||
},
|
||||
plugins: [
|
||||
new CopyWebpackPlugin(['src/.htaccess']),
|
||||
new CopyWebpackPlugin(['src/.htaccess', 'src/iframe.html', 'src/xdLocalStoragePostMessageApi.min.js']),
|
||||
// new webpack.optimize.CommonsChunkPlugin({
|
||||
// name: 'lib',
|
||||
// filename: 'lib.js'
|
||||
@@ -68,7 +59,6 @@ module.exports = {
|
||||
loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' })
|
||||
},
|
||||
{ test: /\.(js|jsx)$/, loaders: ['babel-loader'], include: path.join(__dirname, 'src') },
|
||||
{ test: /\.worker\.js$/, use: { loader: 'worker-loader' } },
|
||||
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
||||
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
||||
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/octet-stream' },
|
||||
|
||||
@@ -4,34 +4,35 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
const { InjectManifest } = require('workbox-webpack-plugin');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const { BugsnagSourceMapUploaderPlugin } = require('webpack-bugsnag-plugins');
|
||||
const { BugsnagSourceMapUploaderPlugin, BugsnagBuildReporterPlugin } = require('webpack-bugsnag-plugins');
|
||||
const pkgJson = require('./package');
|
||||
const buildDate = new Date();
|
||||
|
||||
module.exports = {
|
||||
devtool: 'source-map',
|
||||
entry: {
|
||||
main: ['./src/app/index.js']
|
||||
main: './src/app/index.js'
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx', '.json', '.less']
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, 'build'),
|
||||
filename: '[name].[hash].js',
|
||||
chunkFilename: '[name].bundle.js',
|
||||
publicPath: '/',
|
||||
globalObject: 'this'
|
||||
},
|
||||
node: { fs: 'empty' },
|
||||
mode: 'production',
|
||||
optimization: {
|
||||
minimize: true,
|
||||
splitChunks: {
|
||||
chunks: 'all'
|
||||
}
|
||||
usedExports: true
|
||||
},
|
||||
plugins: [
|
||||
new CopyWebpackPlugin(['src/.htaccess', { from: 'src/schemas', to: 'schemas' }, {from: 'src/images/logo/*', flatten: true, to: ''}]),
|
||||
new CopyWebpackPlugin(['src/.htaccess', { from: 'src/schemas', to: 'schemas' }, {
|
||||
from: 'src/images/logo/*',
|
||||
flatten: true,
|
||||
to: ''
|
||||
}, 'src/iframe.html', 'src/xdLocalStoragePostMessageApi.min.js']),
|
||||
// new webpack.optimize.CommonsChunkPlugin({
|
||||
// name: 'lib',
|
||||
// filename: 'lib.[chunkhash:6].js'
|
||||
@@ -49,21 +50,28 @@ module.exports = {
|
||||
disable: false,
|
||||
allChunks: true
|
||||
}),
|
||||
// new BugsnagBuildReporterPlugin({
|
||||
// apiKey: 'ba9fae819372850fb660755341fa6ef5',
|
||||
// appVersion: `${pkgJson.version}-${buildDate.toISOString()}`
|
||||
// }, { /* opts */ }),
|
||||
// new BugsnagSourceMapUploaderPlugin({
|
||||
// apiKey: 'ba9fae819372850fb660755341fa6ef5',
|
||||
// overwrite: true,
|
||||
// appVersion: `${pkgJson.version}-${buildDate.toISOString()}`
|
||||
// }),
|
||||
new InjectManifest({
|
||||
swSrc: './src/app/sw.js',
|
||||
swSrc: './src/sw.js',
|
||||
importWorkboxFrom: 'cdn',
|
||||
swDest: 'service-worker.js'
|
||||
}),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{test: /\.worker\.js$/, use: {loader: 'worker-loader'}},
|
||||
{ test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) },
|
||||
{ test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' }) },
|
||||
{
|
||||
test: /\.less$/,
|
||||
loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' })
|
||||
},
|
||||
{ test: /\.(js|jsx)$/, loader: 'babel-loader?cacheDirectory=true', include: path.join(__dirname, 'src') },
|
||||
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
||||
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
||||
|
||||
Reference in New Issue
Block a user