+205
@@ -0,0 +1,205 @@
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/webstorm+all,node
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=webstorm+all,node
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# 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
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://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/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
### WebStorm+all ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### WebStorm+all Patch ###
|
||||
# Ignores the whole .idea folder and all .iml files
|
||||
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
|
||||
|
||||
.idea/
|
||||
|
||||
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||
|
||||
*.iml
|
||||
modules.xml
|
||||
.idea/misc.xml
|
||||
*.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
.idea/sonarlint
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/webstorm+all,node
|
||||
|
||||
dist/
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
<!--
|
||||
~ Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||
~ All rights reserved.
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Editor</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
height: 0;
|
||||
user-select: none; /* standard syntax */
|
||||
-webkit-user-select: none; /* webkit (safari, chrome) browsers */
|
||||
-moz-user-select: none; /* mozilla browsers */
|
||||
-khtml-user-select: none; /* webkit (konqueror) browsers */
|
||||
-ms-user-select: none; /* IE10+ */
|
||||
}
|
||||
|
||||
div {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
margin: 8px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<table id="stops">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<label><textarea id="inp" rows="3"></textarea></label>
|
||||
</div>
|
||||
<canvas id="canvas"></canvas>
|
||||
|
||||
<script>
|
||||
Number.prototype.mod = function (n) {
|
||||
return ((this % n) + n) % n;
|
||||
};
|
||||
let stops = [];
|
||||
let i = 0;
|
||||
let canvas = document.getElementById("canvas");
|
||||
canvas.focus()
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
||||
let tbl = document.getElementById("stops");
|
||||
let inp = document.getElementById("inp");
|
||||
inp.addEventListener('input', () => {
|
||||
if (inp.value === null || inp.value === '')
|
||||
return;
|
||||
stops = inp.value.split('\n').map(l => {
|
||||
return {
|
||||
name: l,
|
||||
x: null,
|
||||
y: null
|
||||
}
|
||||
})
|
||||
inp.value = ''
|
||||
i = 0;
|
||||
canvas.focus();
|
||||
draw()
|
||||
})
|
||||
let map = new Image();
|
||||
map.onload = () => {
|
||||
canvas.width = map.width;
|
||||
canvas.height = map.height
|
||||
draw()
|
||||
|
||||
canvas.addEventListener('contextmenu', ev => {
|
||||
// right
|
||||
ev.preventDefault();
|
||||
stops.splice(i, 0, {
|
||||
name: null,
|
||||
x: ev.offsetX,
|
||||
y: ev.offsetY
|
||||
})
|
||||
|
||||
i = (i + 1).mod(stops.length)
|
||||
draw();
|
||||
return false;
|
||||
}, false);
|
||||
|
||||
canvas.addEventListener('click', ev => {
|
||||
// left
|
||||
ev.preventDefault();
|
||||
|
||||
stops[i].x = ev.offsetX
|
||||
stops[i].y = ev.offsetY
|
||||
|
||||
|
||||
i = (i + 1).mod(stops.length)
|
||||
draw();
|
||||
return false;
|
||||
}, false);
|
||||
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.which === 38) {
|
||||
// up
|
||||
i = (i - 1).mod(stops.length)
|
||||
draw()
|
||||
e.preventDefault()
|
||||
} else if (e.which === 40) {
|
||||
// down
|
||||
i = (i + 1).mod(stops.length)
|
||||
draw()
|
||||
e.preventDefault()
|
||||
} else if (e.which === 13) {
|
||||
// enter
|
||||
let output = stops.map(s => `${s.x},${s.y}` + (s.name ? `,,${s.name}` : '')).join('\n')
|
||||
|
||||
console.log(output)
|
||||
}
|
||||
})
|
||||
}
|
||||
map.src = "map.png";
|
||||
|
||||
|
||||
function draw() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.drawImage(map, 0, 0)
|
||||
tbl.getElementsByTagName('tbody')[0].innerHTML = "";
|
||||
let last = null;
|
||||
stops.forEach((s, index) => {
|
||||
const tr = document.createElement('tr');
|
||||
const tdName = document.createElement('td');
|
||||
tdName.innerText = s.name ? s.name : '-';
|
||||
const tdI = document.createElement('td');
|
||||
if (index === i)
|
||||
tdI.innerText = '<';
|
||||
|
||||
|
||||
tr.append(tdName, tdI);
|
||||
|
||||
tbl.getElementsByTagName('tbody')[0].append(tr)
|
||||
|
||||
if (s.y === null && s.x === null)
|
||||
return;
|
||||
if (s.name !== null) {
|
||||
ctx.beginPath()
|
||||
ctx.arc(s.x, s.y, 5, 0, 2 * Math.PI);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = index === i ? '#f00' : '#000'
|
||||
ctx.stroke()
|
||||
ctx.closePath()
|
||||
ctx.fillStyle = '#000'
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText(s.name, s.x, s.y - 10);
|
||||
}
|
||||
if (last) {
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(last.x, last.y)
|
||||
ctx.lineWidth = 3;
|
||||
ctx.lineTo(s.x, s.y)
|
||||
ctx.strokeStyle = '#000'
|
||||
ctx.stroke()
|
||||
ctx.closePath()
|
||||
}
|
||||
last = s;
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
<!--
|
||||
~ Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||
~ All rights reserved.
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
#buttons {
|
||||
position: fixed;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
#settings {
|
||||
position: fixed;
|
||||
margin: 8px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
#bus {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
margin: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="buttons">
|
||||
<button id="minus">-1</button>
|
||||
<button id="play">></button>
|
||||
<button id="real">></button>
|
||||
<button id="plus">+1</button>
|
||||
<button disabled id="display" style="width: 70px">00:00</button>
|
||||
</div>
|
||||
<table id="bus">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 200px">Next Stop</th>
|
||||
<th>Line</th>
|
||||
<th>Offset</th>
|
||||
<th>Delay</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="settings">
|
||||
<label style="display: grid">
|
||||
<input id="mpm" max="10000" min="1" type="number" value="180"> Minutes per Minute
|
||||
</label>
|
||||
<label>
|
||||
<input id="showMap" type="checkbox"> Show Map?
|
||||
</label>
|
||||
</div>
|
||||
<canvas id="canvas"></canvas>
|
||||
</body>
|
||||
</html>
|
||||
Generated
+10198
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "realtime-oepnv-tracker",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "webpack --config webpack/prod.js",
|
||||
"start": "webpack-dev-server --config webpack/base.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.11.6",
|
||||
"@babel/preset-env": "^7.11.5",
|
||||
"babel-loader": "^8.1.0",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"csv-loader": "^3.0.3",
|
||||
"favicons-webpack-plugin": "^4.2.0",
|
||||
"file-loader": "^6.1.0",
|
||||
"html-webpack-plugin": "^4.5.0",
|
||||
"raw-loader": "^4.0.1",
|
||||
"source-map": "^0.7.3",
|
||||
"terser-webpack-plugin": "^4.2.2",
|
||||
"ts-loader": "^8.0.4",
|
||||
"typescript": "^4.0.3",
|
||||
"webpack": "^4.44.2",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.0",
|
||||
"webpack-merge": "^5.1.4",
|
||||
"workbox-webpack-plugin": "^5.1.4"
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
import Line from "./Line";
|
||||
import Coordinate from "./Coordinate";
|
||||
|
||||
export default class Bus {
|
||||
readonly offset: number;
|
||||
readonly line: Line;
|
||||
|
||||
public delay: number = 0;
|
||||
public td: HTMLTableDataCellElement;
|
||||
|
||||
constructor(line: Line, offset: number = 0) {
|
||||
this.line = line;
|
||||
this.offset = offset
|
||||
}
|
||||
|
||||
draw(ctx: CanvasRenderingContext2D, s: number) {
|
||||
|
||||
// @ts-ignore
|
||||
const progress = (this.offset + s - this.delay).mod(this.line.duration);
|
||||
|
||||
|
||||
let position = this.getCurrentPosition(progress);
|
||||
ctx.beginPath()
|
||||
if (this.delay === 0)
|
||||
ctx.fillStyle = '#0f0'
|
||||
else if (this.delay > 0 && this.delay < 5)
|
||||
ctx.fillStyle = '#ff0'
|
||||
else {
|
||||
ctx.fillStyle = '#f00'
|
||||
ctx.fillText("+" + this.delay, position.x, position.y - 3);
|
||||
}
|
||||
ctx.arc(position.x, position.y, 3, 0, 2 * Math.PI);
|
||||
ctx.fill()
|
||||
ctx.closePath()
|
||||
|
||||
if (this.td)
|
||||
this.td.innerText = this.getNextStop(progress);
|
||||
}
|
||||
|
||||
public getNextStop(progress: number = 0): string {
|
||||
let br = 0
|
||||
for (; br < this.line.stopBreaks.length && progress > this.line.stopBreaks[br]; br++) {
|
||||
}
|
||||
|
||||
return this.line.stops[br].name;
|
||||
}
|
||||
|
||||
public getCurrentPosition(progress: number = 0): Coordinate {
|
||||
let br = 0
|
||||
for (; br < this.line.breaks.length && progress > this.line.breaks[br]; br++) {
|
||||
}
|
||||
|
||||
let top = this.line.coords[br];
|
||||
// @ts-ignore
|
||||
let bottomBr = (br - 1).mod(this.line.breaks.length);
|
||||
let bottom = this.line.coords[bottomBr];
|
||||
|
||||
if (progress < this.line.breaks[0])
|
||||
return this.line.coords[0];
|
||||
if (!bottom) {
|
||||
return top;
|
||||
}
|
||||
|
||||
let percentToBr = (progress - this.line.breaks[bottomBr]) / (this.line.breaks[br] - this.line.breaks[bottomBr]);
|
||||
return new Coordinate(
|
||||
bottom.x + percentToBr * (top.x - bottom.x),
|
||||
bottom.y + percentToBr * (top.y - bottom.y)
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
export default class Coordinate {
|
||||
|
||||
readonly x: number;
|
||||
readonly y: number;
|
||||
|
||||
constructor(x: number, y: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
distance(other: Coordinate): number {
|
||||
if (this.equals(other))
|
||||
return 0;
|
||||
if (!other)
|
||||
return 0;
|
||||
|
||||
return Math.sqrt(Math.pow(other.x - this.x, 2) + Math.pow(other.y - this.y, 2))
|
||||
}
|
||||
|
||||
equals(other: Coordinate = null): boolean {
|
||||
if (!other)
|
||||
return false;
|
||||
|
||||
return this.x === other.x && this.y === other.y;
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
import Stop from "./Stop";
|
||||
import Coordinate from "./Coordinate";
|
||||
|
||||
export default class Line {
|
||||
readonly name: String;
|
||||
readonly stops: Stop[] = [];
|
||||
|
||||
readonly breaks: number[] = [];
|
||||
readonly stopBreaks: number[] = [];
|
||||
readonly duration: number = 0;
|
||||
readonly coords: Coordinate[];
|
||||
readonly color: string = '#000';
|
||||
|
||||
constructor(name: String, stops: string, color: string) {
|
||||
this.color = color
|
||||
this.name = name;
|
||||
let coords = stops.split("\n").map(e => {
|
||||
let a = e.split(',');
|
||||
return a.length > 2 ? new Stop(Number.parseFloat(a[0]), Number.parseFloat(a[1]), Number.parseFloat(a[2]), a[3]) : new Coordinate(Number.parseFloat(a[0]), Number.parseFloat(a[1]));
|
||||
});
|
||||
this.coords = coords;
|
||||
|
||||
let lastStop = <Stop>coords[0];
|
||||
let length = 0;
|
||||
let breaks: number[] = [];
|
||||
for (let i = 0; i < coords.length; i++) {
|
||||
if (coords[i] instanceof Stop) {
|
||||
let stop = <Stop>coords[i];
|
||||
this.stops.push(stop)
|
||||
this.stopBreaks.push(stop.minute)
|
||||
|
||||
const td = stop.minute - lastStop.minute
|
||||
if (coords[i - 1])
|
||||
length += coords[i - 1].distance(stop)
|
||||
breaks.map(e => lastStop.minute + (e / length) * td).forEach(e => this.breaks.push(e));
|
||||
|
||||
|
||||
this.breaks.push(stop.minute)
|
||||
|
||||
length = 0;
|
||||
breaks = [];
|
||||
|
||||
lastStop = stop
|
||||
} else {
|
||||
let len = coords[i - 1].distance(coords[i]);
|
||||
length += len;
|
||||
breaks.push(length)
|
||||
}
|
||||
}
|
||||
this.duration = lastStop.minute
|
||||
}
|
||||
|
||||
draw(ctx: CanvasRenderingContext2D) {
|
||||
let last: Coordinate = null;
|
||||
this.coords.forEach(c => {
|
||||
ctx.beginPath()
|
||||
ctx.textAlign = "center";
|
||||
if (c instanceof Stop) {
|
||||
let stop: Stop = c;
|
||||
ctx.arc(c.x, c.y, 5, 0, 2 * Math.PI);
|
||||
ctx.strokeStyle = this.color
|
||||
ctx.fillStyle = '#000'
|
||||
ctx.fillText(stop.name, c.x, c.y - 10);
|
||||
}
|
||||
ctx.stroke()
|
||||
ctx.closePath()
|
||||
if (last) {
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(last.x, last.y)
|
||||
ctx.lineWidth = 3;
|
||||
ctx.lineTo(c.x, c.y)
|
||||
ctx.strokeStyle = this.color
|
||||
ctx.stroke()
|
||||
ctx.closePath()
|
||||
}
|
||||
last = c;
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
import Coordinate from "./Coordinate";
|
||||
|
||||
export default class Stop extends Coordinate {
|
||||
readonly name: string;
|
||||
readonly minute: number;
|
||||
|
||||
constructor(x: number, y: number, minute: number, name: string) {
|
||||
super(x, y);
|
||||
this.minute = minute;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
Number.prototype.mod = function (n) {
|
||||
return ((this % n) + n) % n;
|
||||
};
|
||||
|
||||
import mapSrc from "../map.png";
|
||||
import Bus from "./Bus";
|
||||
import Line from "./Line";
|
||||
import stops201 from './lines/201.csv';
|
||||
import stops204 from './lines/204.csv';
|
||||
import stops205 from './lines/205.csv';
|
||||
import stops206 from './lines/206.csv';
|
||||
|
||||
let line201 = new Line("201", stops201, '#fbce18');
|
||||
let line204 = new Line("204", stops204, '#99207e');
|
||||
let line205 = new Line("205", stops205, '#009fe3');
|
||||
let line206 = new Line("206", stops206, '#1c60b2');
|
||||
console.log(line201);
|
||||
|
||||
let canvas = document.getElementById("canvas");
|
||||
let btnMinus = document.getElementById("minus");
|
||||
let btnPlay = document.getElementById("play");
|
||||
let btnReal = document.getElementById("real");
|
||||
let btnPlus = document.getElementById("plus");
|
||||
let btnDisplay = document.getElementById("display");
|
||||
let tblBus = document.getElementById("bus");
|
||||
let chkMap = document.getElementById("showMap");
|
||||
let inpMpm = document.getElementById("mpm");
|
||||
|
||||
chkMap.addEventListener('change', () => run(0));
|
||||
inpMpm.addEventListener('change', () => mpm = parseInt(inpMpm.value))
|
||||
let ctx = canvas.getContext("2d");
|
||||
let map = new Image();
|
||||
map.onload = () => {
|
||||
canvas.width = map.width;
|
||||
canvas.height = map.height
|
||||
run(0)
|
||||
}
|
||||
map.src = mapSrc;
|
||||
|
||||
let isRunning = false;
|
||||
let realIsRunning = false;
|
||||
let interval;
|
||||
let progress = 0;
|
||||
let mpm = parseInt(inpMpm.value);
|
||||
|
||||
let busses = [
|
||||
new Bus(line201),
|
||||
new Bus(line201, 30),
|
||||
new Bus(line201, 60),
|
||||
new Bus(line201, 90),
|
||||
new Bus(line204),
|
||||
new Bus(line204, 30),
|
||||
new Bus(line204, 60),
|
||||
new Bus(line204, 90),
|
||||
new Bus(line205),
|
||||
new Bus(line205, 30),
|
||||
new Bus(line205, 60),
|
||||
new Bus(line206),
|
||||
new Bus(line206, 30),
|
||||
new Bus(line206, 60),
|
||||
];
|
||||
|
||||
|
||||
busses.forEach(b => {
|
||||
const tr = document.createElement('tr');
|
||||
const tdNext = document.createElement('td');
|
||||
b.td = tdNext;
|
||||
const tdLine = document.createElement('td');
|
||||
tdLine.innerText = b.line.name
|
||||
const tdOffset = document.createElement('td');
|
||||
tdOffset.innerText = b.offset.toString()
|
||||
const tdDelay = document.createElement('td');
|
||||
const inputDelay = document.createElement('input')
|
||||
inputDelay.type = 'number'
|
||||
inputDelay.min = '0'
|
||||
inputDelay.max = '120'
|
||||
inputDelay.value = b.delay.toString()
|
||||
inputDelay.addEventListener('change', () => b.delay = parseInt(inputDelay.value))
|
||||
tdDelay.append(inputDelay)
|
||||
|
||||
tr.append(tdNext, tdLine, tdOffset, tdDelay);
|
||||
|
||||
tblBus.getElementsByTagName('tbody')[0].append(tr)
|
||||
})
|
||||
|
||||
function run(s) {
|
||||
if (s instanceof Date) {
|
||||
const hours = s.getHours()
|
||||
const minutes = s.getMinutes();
|
||||
const seconds = s.getSeconds();
|
||||
|
||||
progress = (60 * hours) + minutes + (seconds / 60);
|
||||
} else
|
||||
progress += s;
|
||||
const mprogress = progress.mod(60);
|
||||
const hours = Math.floor(progress / 60);
|
||||
const minutes = Math.floor(mprogress);
|
||||
const seconds = Math.floor((mprogress * 60).mod(60))
|
||||
btnDisplay.innerText = (hours > 9 ? hours : "0" + hours) + ":" + (minutes > 9 ? minutes : "0" + minutes) + ":" + (seconds > 9 ? seconds : "0" + seconds)
|
||||
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
if (chkMap.checked)
|
||||
ctx.drawImage(map, 0, 0)
|
||||
else {
|
||||
line201.draw(ctx)
|
||||
line204.draw(ctx)
|
||||
line205.draw(ctx)
|
||||
line206.draw(ctx)
|
||||
}
|
||||
busses.forEach(b => b.draw(ctx, progress))
|
||||
}
|
||||
|
||||
btnMinus.addEventListener('click', () => {
|
||||
run(-1);
|
||||
})
|
||||
|
||||
btnPlus.addEventListener('click', () => {
|
||||
run(1);
|
||||
})
|
||||
|
||||
|
||||
btnPlay.addEventListener('click', () => {
|
||||
if (realIsRunning) {
|
||||
clearInterval(interval)
|
||||
btnReal.innerText = '>'
|
||||
realIsRunning = false
|
||||
}
|
||||
|
||||
if (isRunning) {
|
||||
clearInterval(interval)
|
||||
btnPlay.innerText = '>'
|
||||
} else {
|
||||
interval = setInterval(() => {
|
||||
run(mpm / 600);
|
||||
}, 100)
|
||||
btnPlay.innerText = '||'
|
||||
}
|
||||
|
||||
isRunning = !isRunning;
|
||||
})
|
||||
|
||||
|
||||
btnReal.addEventListener('click', () => {
|
||||
if (isRunning) {
|
||||
clearInterval(interval)
|
||||
btnPlay.innerText = '>'
|
||||
isRunning = false
|
||||
}
|
||||
|
||||
if (realIsRunning) {
|
||||
clearInterval(interval)
|
||||
btnReal.innerText = '>'
|
||||
} else {
|
||||
interval = setInterval(() => {
|
||||
run(new Date());
|
||||
}, 100)
|
||||
btnReal.innerText = '||'
|
||||
}
|
||||
|
||||
realIsRunning = !realIsRunning;
|
||||
})
|
||||
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('./service-worker.js').catch(() => {
|
||||
}).then();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
1567,455,11,Roland-Center
|
||||
1553,448
|
||||
1558,431
|
||||
1559,408,12,Obervielander Str.
|
||||
1569,391
|
||||
1566,364
|
||||
1553,351,13,Carl-Hurtzig-Str.
|
||||
1517,303,14,Zum Huchtinger Bhf.
|
||||
1455,310,15,Harriersand
|
||||
1386,325,16,Mittelshuchtinger Dorfstr.
|
||||
1337,332,16,Brüsseler Str.
|
||||
1261,335,17,Varrelgraben
|
||||
1135,338
|
||||
1099,332,19,Kleiner Tannenweg
|
||||
1023,324,20,Bahnhof Heidkrug
|
||||
972,346,21,Sommerweg
|
||||
927,370,22,Masurenstr.
|
||||
892,386,22,Tannenbergstr.
|
||||
845,383,23,Feldstr.
|
||||
794,380,23,Hamburger Weg
|
||||
731,379,24,Am Fuhrenkamp
|
||||
693,382,25,Syker Str.
|
||||
685,372
|
||||
682,349
|
||||
663,339
|
||||
652,341,27,Otto-Jenzok-Str.
|
||||
638,345
|
||||
640,358
|
||||
624,359
|
||||
627,367,28,Museum
|
||||
529,410,33,Bahnhof / ZOB
|
||||
529,410,39,Bahnhof / ZOB
|
||||
501,431,40,Post
|
||||
503,443,43,Markt
|
||||
553,491,45,Parkstr.
|
||||
581,492,47,Arthur-Fitger-Str.
|
||||
612,507,48,Uferweg
|
||||
658,574,49,Karlsbader Str.
|
||||
690,617,50,Amundsenstr.
|
||||
725,662,51,Schollendamm
|
||||
762,715,52,Riedeweg
|
||||
793,746,53,Helgolandstr.
|
||||
766,777,53,Nachtigalstr.
|
||||
739,808,54,Wissmannstr.
|
||||
724,819,55,Bahnhof Annenheide
|
||||
703,864,56,Steller Str.
|
||||
689,915,57,Annenheide
|
||||
689,915,60,Annenheide
|
||||
714,947
|
||||
742,920,61,Feldhusstr.
|
||||
748,907
|
||||
703,864,62,Steller Str.
|
||||
724,819,63,Bahnhof Annenheide
|
||||
739,808,64,Wissmannstr.
|
||||
793,746,65,Helgolandstr.
|
||||
762,715,66,Riedeweg
|
||||
725,662,67,Schollendamm
|
||||
690,617,68,Amundsenstr.
|
||||
658,574,69,Karlsbader Str.
|
||||
612,507,70,Uferweg
|
||||
581,492,71,Arthur-Fitger-Str.
|
||||
553,491,73,Parkstr.
|
||||
503,443,74,Markt
|
||||
501,431,75,Post
|
||||
529,410,78,Bahnhof / ZOB
|
||||
529,410,84,Bahnhof / ZOB
|
||||
627,367,87,Museum
|
||||
624,359
|
||||
640,358
|
||||
638,345
|
||||
652,341,88,Otto-Jenzok-Str.
|
||||
663,339
|
||||
682,349
|
||||
685,372
|
||||
693,382,89,Syker Str.
|
||||
731,379,91,Am Fuhrenkamp
|
||||
794,380,91,Hamburger Weg
|
||||
845,383,92,Feldstr.
|
||||
892,386,93,Tannenbergstr.
|
||||
927,370,94,Masurenstr.
|
||||
972,346,95,Sommerweg
|
||||
1023,324,96,Bahnhof Heidkrug
|
||||
1099,332,97,Kleiner Tannenweg
|
||||
1135,338
|
||||
1261,335,98,Varrelgraben
|
||||
1337,332,99,Brüsseler Str.
|
||||
1386,325,100,Mittelshuchtinger Dorfstr.
|
||||
1455,310,101,Harriersand
|
||||
1517,303,101,Zum Huchtinger Bhf.
|
||||
1553,351,102,Carl-Hurtzig-Str.
|
||||
1566,364
|
||||
1569,391
|
||||
1559,408,103,Obervielander Str.
|
||||
1558,431
|
||||
1553,448
|
||||
1567,455,106,Roland-Center
|
||||
1567,455,120,Roland-Center
|
||||
|
@@ -0,0 +1,81 @@
|
||||
1567,455,23,Roland-Center
|
||||
1542,478,24,Willakedamm
|
||||
1525,509,24,Hohenhorster Weg
|
||||
1473,575,25,Rotterdamer Str.
|
||||
1457,600,26,Friedhof Huchting
|
||||
1441,637,28,Varreler Landstr.
|
||||
1419,631,28,Henstedter Str.
|
||||
1341,606,29,Meenheit
|
||||
1284,642,30,Grüne Str.
|
||||
1188,682,32,Schulstr.
|
||||
1106,675,33,Johann-Eilers-Weg
|
||||
1040,661,34,Mühle Unger
|
||||
992,597,35,Pultern
|
||||
922,513,37,Langenwischstr.
|
||||
882,488,38,An der Riede
|
||||
826,457,39,Kieler Weg
|
||||
784,437,40,Lüneburger Str.
|
||||
759,422,41,Leipziger Weg
|
||||
728,405,41,Pestalozziweg
|
||||
694,384,43,Syker Str.
|
||||
623,399,44,Heinrichstr.
|
||||
530,409,48,Bahnhof / ZOB
|
||||
530,409,54,Bahnhof / ZOB
|
||||
506,429,55,Post
|
||||
506,441,58,Markt
|
||||
555,489,60,Parkstr.
|
||||
582,536,62,Max-Planck-Str.
|
||||
588,572,63,Breslauer Str.
|
||||
540,617,64,Lethestr.
|
||||
506,641,64,Elbinger Str.
|
||||
467,696,65,Brendelweg
|
||||
495,718,66,Ziethenweg
|
||||
474,755,66,Graf-von-Galen-Str.
|
||||
447,800,67,Könzgenstr.
|
||||
428,825,68,Letterhausstr.
|
||||
376,830,69,Schlutterdamm
|
||||
327,937,70,Holzkamper Damm
|
||||
332,1036,71,Abernettistr.
|
||||
293,1055,72,Boelckestr.
|
||||
254,1075,73,Adelheide Kaserne
|
||||
254,1075,74,Adelheide Kaserne
|
||||
293,1055,75,Boelckestr.
|
||||
332,1036,76,Abernettistr.
|
||||
327,937,77,Holzkamper Damm
|
||||
376,830,78,Schlutterdamm
|
||||
428,825,79,Letterhausstr.
|
||||
447,800,80,Könzgenstr.
|
||||
474,755,81,Graf-von-Galen-Str.
|
||||
495,718,81,Ziethenweg
|
||||
467,696,83,Brendelweg
|
||||
506,641,83,Elbinger Str.
|
||||
540,617,84,Lethestr.
|
||||
588,572,85,Breslauer Str.
|
||||
582,536,87,Max-Planck-Str.
|
||||
555,489,88,Parkstr.
|
||||
506,441,89,Markt
|
||||
506,429,90,Post
|
||||
530,409,93,Bahnhof / ZOB
|
||||
530,409,99,Bahnhof / ZOB
|
||||
623,399,101,Heinrichstr.
|
||||
694,384,102,Syker Str.
|
||||
728,405,103,Pestalozziweg
|
||||
759,422,104,Leipziger Weg
|
||||
784,437,105,Lüneburger Str.
|
||||
826,457,106,Kieler Weg
|
||||
882,488,106,An der Riede
|
||||
922,513,107,Langenwischstr.
|
||||
992,597,109,Pultern
|
||||
1040,661,110,Mühle Unger
|
||||
1106,675,111,Johann-Eilers-Weg
|
||||
1188,682,112,Schulstr.
|
||||
1284,642,114,Grüne Str.
|
||||
1341,606,115,Meenheit
|
||||
1419,631,116,Henstedter Str.
|
||||
1441,637,117,Varreler Landstr.
|
||||
1457,600,118,Friedhof Huchting
|
||||
1473,575,119,Rotterdamer Str.
|
||||
1525,509,120,Hohenhorster Weg
|
||||
1542,478,121,Willakedamm
|
||||
1567,455,124,Roland-Center
|
||||
1567,455,150,Roland-Center
|
||||
|
@@ -0,0 +1,59 @@
|
||||
515,881,1,Gothaer Str.
|
||||
467,864,1,Dresdener Str.
|
||||
475,826,2,Blücherweg
|
||||
498,787,3,Jenaer Str.
|
||||
529,750,4,Overbergstr.
|
||||
553,770,5,Jägerstr.
|
||||
588,717,6,Danziger Str.
|
||||
583,682,7,Görlitzer Str.
|
||||
576,654,8,Liegnitzer Str.
|
||||
567,628,9,Königsberger Str.
|
||||
583,571,10,Breslauer Str.
|
||||
578,536,12,Max-Planck-Str.
|
||||
548,492,13,Parkstr.
|
||||
499,446,14,Markt
|
||||
498,432,15,Post
|
||||
529,401,18,Bahnhof / ZOB
|
||||
529,401,24,Bahnhof / ZOB
|
||||
564,357,26,Am Wollepark
|
||||
558,318,27,Thüringer Str.
|
||||
546,260,27,Schönemoorer Str.
|
||||
555,227,28,Hansastr.
|
||||
574,187,29,Nordstr.
|
||||
532,190,30,Lessingstr.
|
||||
526,140,31,Andersenstr.
|
||||
532,106,31,Kleistweg
|
||||
543,78,32,Gorch-Fock-Str.
|
||||
590,83,33,Friedhof
|
||||
644,75,33,Brauereiweg
|
||||
658,47,34,Bungerhof
|
||||
658,47,50,Bungerhof
|
||||
644,75,51,Brauereiweg
|
||||
590,83,52,Friedhof
|
||||
543,78,53,Gorch-Fock-Str.
|
||||
532,106,53,Kleistweg
|
||||
526,140,54,Andersenstr.
|
||||
532,190,55,Lessingstr.
|
||||
574,187,56,Nordstr.
|
||||
555,227,57,Hansastr.
|
||||
546,260,58,Schönemoorer Str.
|
||||
558,318,59,Thüringer Str.
|
||||
564,357,60,Am Wollepark
|
||||
529,401,63,Bahnhof / ZOB
|
||||
529,401,69,Bahnhof / ZOB
|
||||
498,432,70,Post
|
||||
499,446,73,Markt
|
||||
548,492,75,Parkstr.
|
||||
578,536,77,Max-Planck-Str.
|
||||
583,571,78,Breslauer Str.
|
||||
567,628,79,Königsberger Str.
|
||||
576,654,80,Liegnitzer Str.
|
||||
583,682,81,Görlitzer Str.
|
||||
588,717,81,Danziger Str.
|
||||
553,770,82,Jägerstr.
|
||||
529,750,83,Overbergstr.
|
||||
498,787,84,Jenaer Str.
|
||||
525,828,86,Chemnitzer Str.
|
||||
539,875,86,Altenburger Str.
|
||||
515,881,88,Gothaer Str.
|
||||
515,881,90,Gothaer Str.
|
||||
|
@@ -0,0 +1,71 @@
|
||||
658,47,5,Bungerhof
|
||||
644,75,6,Brauereiweg
|
||||
636,85
|
||||
590,83,7,Friedhof
|
||||
543,78,8,Gorch-Fock-Str.
|
||||
498,75,8,Hebbelweg
|
||||
468,68
|
||||
463,75,9,Friedensstr.
|
||||
470,134,10,Scheffelstr.
|
||||
480,198,11,Dwostr.
|
||||
482,216
|
||||
518,239,12,Rilkeweg
|
||||
546,260,13,Schönemoorer Str.
|
||||
554,319,14,Thüringer Str.
|
||||
560,359,15,Am Wollepark
|
||||
566,386
|
||||
563,392
|
||||
548,396
|
||||
527,398,18,Bahnhof / ZOB J
|
||||
527,398,24,Bahnhof / ZOB J
|
||||
509,402
|
||||
493,415
|
||||
494,432,25,Post
|
||||
496,445,28,Markt A
|
||||
474,456
|
||||
450,463
|
||||
442,471,30,Holbeinstr./Graft Therme
|
||||
412,533
|
||||
411,530,32,Berufsschule
|
||||
362,505,33,Feuerbachstr.
|
||||
336,491,34,Schumannstr.
|
||||
299,519,35,Brahmsstr.
|
||||
242,520,36,Mozartstr.
|
||||
220,514,37,Josef-Hospital
|
||||
199,569,38,Hanse-Wissenschaftsk.
|
||||
199,569,48,Hanse-Wissenschaftsk.
|
||||
220,514,49,Josef-Hospital
|
||||
242,520,50,Mozartstr.
|
||||
299,519,51,Brahmsstr.
|
||||
336,491,52,Schumannstr.
|
||||
362,505,53,Feuerbachstr.
|
||||
411,530,54,Berufsschule
|
||||
412,533
|
||||
442,471,55,Holbeinstr./Graft Therme
|
||||
450,463
|
||||
474,456
|
||||
496,445,59,Markt C1
|
||||
494,432,60,Post
|
||||
493,415
|
||||
509,402
|
||||
527,398,63,Bahnhof / ZOB D
|
||||
527,398,69,Bahnhof / ZOB D
|
||||
548,396
|
||||
563,392
|
||||
566,386
|
||||
560,359,71,Am Wollepark
|
||||
554,319,72,Thüringer Str.
|
||||
546,260,73,Schönemoorer Str.
|
||||
518,239,74,Rilkeweg
|
||||
482,216
|
||||
480,198,74,Dwostr.
|
||||
470,134,75,Scheffelstr.
|
||||
463,75,76,Friedensstr.
|
||||
468,68
|
||||
498,75,77,Hebbelweg
|
||||
543,78,77,Gorch-Fock-Str.
|
||||
590,83,78,Friedhof
|
||||
636,85
|
||||
644,75,78,Brauereiweg
|
||||
658,47,79,Bungerhof
|
||||
658,47,90,Bungerhof
|
||||
|
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/",
|
||||
"noImplicitAny": true,
|
||||
"module": "es6",
|
||||
"target": "es5",
|
||||
"jsx": "react",
|
||||
"allowJs": true,
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
const webpack = require("webpack");
|
||||
const path = require("path");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const {CleanWebpackPlugin} = require("clean-webpack-plugin");
|
||||
|
||||
module.exports = {
|
||||
mode: "development",
|
||||
devtool: "eval-source-map",
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: "babel-loader"
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: /\.(ogg|mp3|wav|mpe?g|gif|png|jpe?g|svg|xml)$/i,
|
||||
use: "file-loader"
|
||||
},
|
||||
{
|
||||
test: /\.csv$/i,
|
||||
use: 'raw-loader',
|
||||
},
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js'],
|
||||
},
|
||||
plugins: [
|
||||
new CleanWebpackPlugin({
|
||||
root: path.resolve(__dirname, "../")
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: "./index.html"
|
||||
})
|
||||
]
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
const {merge} = require('webpack-merge');
|
||||
const path = require("path");
|
||||
const base = require("./base");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const WorkboxPlugin = require('workbox-webpack-plugin');
|
||||
const FaviconsWebpackPlugin = require('favicons-webpack-plugin');
|
||||
|
||||
module.exports = merge(base, {
|
||||
mode: "production",
|
||||
output: {
|
||||
filename: "bundle.min.js"
|
||||
},
|
||||
devtool: false,
|
||||
performance: {
|
||||
maxEntrypointSize: 900000,
|
||||
maxAssetSize: 900000
|
||||
},
|
||||
optimization: {
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
output: {
|
||||
comments: false
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new WorkboxPlugin.GenerateSW({
|
||||
clientsClaim: true,
|
||||
skipWaiting: true,
|
||||
}),
|
||||
new FaviconsWebpackPlugin({
|
||||
logo: './map.png',
|
||||
publicPath: "./",
|
||||
favicons: {
|
||||
manifestRelativePaths: true,
|
||||
start_url: "../",
|
||||
appName: 'RT Delbus',
|
||||
appDescription: 'Real-Time Delbus',
|
||||
appShortName: "rt-delbus",
|
||||
developerName: 'Syma.Dev',
|
||||
developerURL: "https://syma.dev",
|
||||
background: '#000',
|
||||
theme_color: '#333',
|
||||
logging: false,
|
||||
icons: {
|
||||
android: true,
|
||||
appleIcon: true,
|
||||
appleStartup: false,
|
||||
coast: false,
|
||||
favicons: true,
|
||||
firefox: true,
|
||||
windows: true,
|
||||
yandex: false
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
Reference in New Issue
Block a user