Change infotren to infodb
This commit is contained in:
parent
fb54636ce7
commit
14c99d6d0c
31 changed files with 2296 additions and 623 deletions
|
@ -4,9 +4,10 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>About - InfoTren</title>
|
||||
<title>About - InfoDTrain</title>
|
||||
|
||||
<link rel="stylesheet" href="/base.css">
|
||||
<link rel="stylesheet" href="/base.dark.css">
|
||||
|
||||
<script src="/common/worker.js"></script>
|
||||
<script defer src="/common/back.js"></script>
|
||||
|
@ -24,8 +25,8 @@
|
|||
<div class="header-placeholder"></div>
|
||||
|
||||
<div class="content">
|
||||
<h2>InfoTren</h2>
|
||||
<p>KaiOS webapp for <a rel="noreferrer" target="_blank" href="https://infofer.ro" class="items inline">Informatica Feroviară</a> scraper</p>
|
||||
<h2>InfoDTrain</h2>
|
||||
<p>Webapp for <a rel="noreferrer" target="_blank" href="https://v6.db.transport.rest/" class="items inline">Deutsche Bahn API</a></p>
|
||||
|
||||
<div>
|
||||
<h4>Acknowledgements</h4>
|
||||
|
|
55
base.css
55
base.css
|
@ -73,6 +73,13 @@ footer .rsk {
|
|||
}
|
||||
|
||||
header {
|
||||
background-color: white;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
header.embedded {
|
||||
position: fixed;
|
||||
left: env(titlebar-area-x, 0);
|
||||
top: env(titlebar-area-y, 0);
|
||||
|
@ -81,19 +88,6 @@ header {
|
|||
-webkit-app-region: drag;
|
||||
app-region: drag;
|
||||
|
||||
background-color: white;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-placeholder {
|
||||
box-sizing: border-box;
|
||||
height: max(env(titlebar-area-height, 36px), 42px);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
header.embedded {
|
||||
background-color: #0000ff;
|
||||
color: white;
|
||||
}
|
||||
|
@ -108,6 +102,14 @@ header.embedded {
|
|||
|
||||
@media (display-mode: window-controls-overlay) {
|
||||
header {
|
||||
position: fixed;
|
||||
left: env(titlebar-area-x, 0);
|
||||
top: env(titlebar-area-y, 0);
|
||||
width: env(titlebar-area-width, 100%);
|
||||
min-height: env(titlebar-area-height, 36px);
|
||||
-webkit-app-region: drag;
|
||||
app-region: drag;
|
||||
|
||||
background-color: #0000ff;
|
||||
color: white;
|
||||
}
|
||||
|
@ -128,8 +130,13 @@ header.embedded {
|
|||
}
|
||||
|
||||
.header-placeholder {
|
||||
box-sizing: border-box;
|
||||
height: max(env(titlebar-area-height, 36px), 42px);
|
||||
margin-bottom: 2px;
|
||||
|
||||
background-color: #0000ff;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
header .left, header .right {
|
||||
|
@ -206,7 +213,7 @@ p.sec {
|
|||
|
||||
margin: 0 8px;
|
||||
|
||||
color: gray;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
p.thi {
|
||||
|
@ -344,14 +351,26 @@ pre {
|
|||
border-bottom-right-radius: 5%;
|
||||
}
|
||||
|
||||
.IR, .IRN {
|
||||
color: #ff0000 !important;
|
||||
.product-suburban {
|
||||
color: green !important;
|
||||
}
|
||||
|
||||
.IC {
|
||||
color: #00aa00 !important;
|
||||
.product-national, .product-nationalExpress {
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.product-bus {
|
||||
color: purple !important;
|
||||
}
|
||||
|
||||
.product-subway {
|
||||
color: blue !important;
|
||||
}
|
||||
|
||||
.product-tram {
|
||||
color: lightcoral !important;
|
||||
}
|
||||
|
||||
@media print {
|
||||
footer, .no-print {
|
||||
|
|
49
base.dark.css
Normal file
49
base.dark.css
Normal file
|
@ -0,0 +1,49 @@
|
|||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
body {
|
||||
color: white;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
header:not(.embedded) {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: #303030;
|
||||
}
|
||||
|
||||
h4 {
|
||||
color: #a0a0a0;
|
||||
background-color: #0f0f0f;
|
||||
}
|
||||
|
||||
p.sec, p.thi {
|
||||
color: lightgrey;
|
||||
}
|
||||
|
||||
li.items:not(.disabled):hover:not(:focus), a:not(.disabled):hover:not(:focus) {
|
||||
background-color: #0000bb;
|
||||
color: white;
|
||||
}
|
||||
|
||||
a:not(.no-a-custom):not(.no-custom-a):not(:focus):not(:hover) {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.back {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
.product-suburban {
|
||||
color: #33ff33 !important;
|
||||
}
|
||||
|
||||
.product-bus {
|
||||
color: #dd33dd !important;
|
||||
}
|
||||
|
||||
.product-subway {
|
||||
color: #33aaff !important;
|
||||
}
|
||||
}
|
88
common/components.js
Normal file
88
common/components.js
Normal file
|
@ -0,0 +1,88 @@
|
|||
// Adapted from: https://github.com/tsoding/grecha.js/blob/master/grecha.js
|
||||
|
||||
function tag(name) {
|
||||
var result = document.createElement(name);
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
if (child instanceof Node) {
|
||||
result.appendChild(child)
|
||||
} else {
|
||||
result.appendChild(document.createTextNode(child ? child.toString() : ''))
|
||||
}
|
||||
}
|
||||
|
||||
result.att$ = function(name, value) {
|
||||
this.setAttribute(name, value);
|
||||
return this;
|
||||
};
|
||||
|
||||
result.value$ = function(value) {
|
||||
this.value = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
result.checked$ = function(value) {
|
||||
this.checked = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
result.id$ = function(name) {
|
||||
this.id = name;
|
||||
return this;
|
||||
};
|
||||
|
||||
result.class$ = function(name) {
|
||||
this.classList.add(name);
|
||||
return this;
|
||||
};
|
||||
|
||||
result.event$ = function(eventName, handler) {
|
||||
this.addEventListener(eventName, handler);
|
||||
return this;
|
||||
};
|
||||
|
||||
result.onclick$ = function(callback) {
|
||||
this.onclick = callback;
|
||||
return this;
|
||||
};
|
||||
|
||||
result.also$ = function(callback) {
|
||||
callback(this);
|
||||
return this;
|
||||
};
|
||||
|
||||
result.let$ = function(callback) {
|
||||
return callback(this);
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var MUNDANE_TAGS = ["canvas", "h1", "h2", "h3", "h4", "p", "a", "div", "span", "select", "label", "hr"];
|
||||
for (var i = 0; i < MUNDANE_TAGS.length; i++) {
|
||||
(function (tagName) {
|
||||
window[tagName] = function() {
|
||||
var args = [tagName];
|
||||
for (var j = 0; j < arguments.length; j++) {
|
||||
args.push(arguments[j]);
|
||||
}
|
||||
return tag.apply(null, args);
|
||||
}
|
||||
})(MUNDANE_TAGS[i]);
|
||||
}
|
||||
|
||||
function a(href) {
|
||||
var args = ["a"];
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
args.push(arguments[i]);
|
||||
}
|
||||
return tag.apply(null, args).att$("href", href);
|
||||
}
|
||||
|
||||
function img(src) {
|
||||
return tag("img").att$("src", src);
|
||||
}
|
||||
|
||||
function input(type) {
|
||||
return tag("input").att$("type", type);
|
||||
}
|
146
config-route.css
Normal file
146
config-route.css
Normal file
|
@ -0,0 +1,146 @@
|
|||
.itinerary-train {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
grid-template-rows: repeat(auto-fit, auto);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.itinerary-train:not(:last-child) {
|
||||
grid-template-areas:
|
||||
"dep-time dep-station dep-platform"
|
||||
"train train train"
|
||||
"arr-time arrdep-station arr-platform"
|
||||
"dep2-time arrdep-station dep2-platform";
|
||||
}
|
||||
|
||||
.itinerary-train:last-child {
|
||||
grid-template-areas:
|
||||
"train train train"
|
||||
"arr-time arr-station arr-platform";
|
||||
}
|
||||
|
||||
.itinerary-train:only-child {
|
||||
grid-template-areas:
|
||||
"dep-time dep-station dep-platform"
|
||||
"train train train"
|
||||
"arr-time arr-station arr-platform";
|
||||
}
|
||||
|
||||
.itinerary-train .departure.time {
|
||||
grid-area: dep-time;
|
||||
}
|
||||
|
||||
.itinerary-train .departure.platform {
|
||||
grid-area: dep-platform;
|
||||
}
|
||||
|
||||
.itinerary-train .next-departure.time {
|
||||
grid-area: dep2-time;
|
||||
}
|
||||
|
||||
.itinerary-train .next-departure.platform {
|
||||
grid-area: dep2-platform;
|
||||
}
|
||||
|
||||
.itinerary-train .departure.station {
|
||||
grid-area: dep-station;
|
||||
}
|
||||
|
||||
.itinerary-train .train {
|
||||
grid-area: train;
|
||||
}
|
||||
|
||||
.itinerary-train .arrival.time {
|
||||
grid-area: arr-time;
|
||||
}
|
||||
|
||||
.itinerary-train .arrival.platform {
|
||||
grid-area: arr-platform;
|
||||
}
|
||||
|
||||
.itinerary-train:not(:last-child) .arrival.station {
|
||||
grid-area: arrdep-station;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.itinerary-train .arrival.station {
|
||||
grid-area: arr-station;
|
||||
}
|
||||
|
||||
.itinerary-train .time {
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.itinerary-train .platform {
|
||||
margin: 2px;
|
||||
padding: 2px;
|
||||
border: 1px solid black;
|
||||
border-radius: 4px;
|
||||
justify-self: end;
|
||||
min-width: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.itinerary-train .platform.changed {
|
||||
color: red;
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.train .company {
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.walking {
|
||||
font-size: 0.95em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
input#time {
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
div.checkbox {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
div.checkbox p {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
div.checkbox input {
|
||||
flex: 0;
|
||||
}
|
||||
|
||||
.suggestion {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.suggestion :first-child {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.suggestion .star {
|
||||
flex: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.suggestion .star.checked {
|
||||
filter: invert(90%) sepia(49%) saturate(704%) hue-rotate(359deg) brightness(94%) contrast(99%);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.suggestion .star {
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
.suggestion .star.checked {
|
||||
filter: invert(86%) sepia(79%) saturate(2126%) hue-rotate(357deg) brightness(108%) contrast(104%);
|
||||
}
|
||||
}
|
49
config-route.html
Normal file
49
config-route.html
Normal file
|
@ -0,0 +1,49 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Route - InfoDTrain</title>
|
||||
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
|
||||
<link rel="stylesheet" href="/base.css">
|
||||
<link rel="stylesheet" href="/base.dark.css">
|
||||
<link rel="stylesheet" href="config-route.css">
|
||||
<link rel="stylesheet" href="config-route.dark.css">
|
||||
|
||||
<script src="/common/worker.js"></script>
|
||||
<script defer src="/common/back.js"></script>
|
||||
<script defer src="/common/components.js"></script>
|
||||
<script defer src="/common/items.js"></script>
|
||||
<script defer src="/common/trainId.js"></script>
|
||||
<script defer src="config-route.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="left">
|
||||
<img id="back-button" class="back" src="/common/back.svg" alt="Back">
|
||||
</div>
|
||||
<h1>Find Route</h1>
|
||||
<div class="right"></div>
|
||||
</header>
|
||||
<div class="header-placeholder"></div>
|
||||
|
||||
<!-- <h4><label for="stationName">Station Name</label></h4>-->
|
||||
<!-- <input type="search" class="items" name="stationName" id="stationName">-->
|
||||
|
||||
<!-- <h4>Suggestions</h4>-->
|
||||
<!-- <div class="content">-->
|
||||
<!-- <ul id="suggestionsArea"></ul>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<footer>
|
||||
<div class="lsk"></div>
|
||||
<!-- <div class="csk">Search</div>-->
|
||||
<div class="csk"></div>
|
||||
<div class="rsk"></div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
674
config-route.js
Normal file
674
config-route.js
Normal file
|
@ -0,0 +1,674 @@
|
|||
/**
|
||||
* @type {string | null}
|
||||
*/
|
||||
var fromStation = null
|
||||
/**
|
||||
* @type {string | null}
|
||||
*/
|
||||
var toStation = null
|
||||
/**
|
||||
* @type {Date | null}
|
||||
*/
|
||||
var departureDate = null
|
||||
var transitKind = {
|
||||
ice: true,
|
||||
ic: true,
|
||||
re: true,
|
||||
rb: true,
|
||||
s: true,
|
||||
bus: true,
|
||||
ferry: true,
|
||||
u: true,
|
||||
tram: true,
|
||||
}
|
||||
var starred = []
|
||||
|
||||
/**
|
||||
* @type {{id: string, name: string}[]}
|
||||
*/
|
||||
var knownStations = []
|
||||
|
||||
var itineraries = null
|
||||
|
||||
function goToStation(stationId) {
|
||||
var url = new URL(window.location.href)
|
||||
if (!fromStation) {
|
||||
url.searchParams.set('from', stationId)
|
||||
}
|
||||
else if (!toStation) {
|
||||
url.searchParams.set('to', stationId)
|
||||
}
|
||||
// url.searchParams.set('date', new Date().toISOString())
|
||||
window.location.href = url.toString()
|
||||
}
|
||||
|
||||
function setDepartureDate(departureDate) {
|
||||
var url = new URL(window.location.href)
|
||||
url.searchParams.set('departureDate', departureDate.toISOString())
|
||||
window.location.href = url.toString()
|
||||
}
|
||||
|
||||
function searchNormalize(str) {
|
||||
return str
|
||||
.toLowerCase()
|
||||
.replaceAll('ă', 'a')
|
||||
.replaceAll('â', 'a')
|
||||
.replaceAll('î', 'i')
|
||||
.replaceAll('ș', 's')
|
||||
.replaceAll('ț', 't')
|
||||
}
|
||||
|
||||
var focusedElement = null
|
||||
|
||||
var _rebuildDebounce = null
|
||||
var _rebuildRequested = false
|
||||
function rebuildSuggestions() {
|
||||
if (_rebuildDebounce !== null) {
|
||||
_rebuildRequested = true
|
||||
return
|
||||
}
|
||||
|
||||
_rebuildRequested = false
|
||||
_rebuildDebounce = 123
|
||||
|
||||
var suggestionsArea = document.getElementById('suggestionsArea')
|
||||
while (suggestionsArea.childNodes.length > 0) {
|
||||
suggestionsArea.childNodes[0].remove()
|
||||
}
|
||||
|
||||
var suggestions = knownStations.slice()
|
||||
if (suggestions.length === 0) {
|
||||
suggestions = starred.map(function (s) { return JSON.parse(s) })
|
||||
}
|
||||
|
||||
suggestions.forEach(function (suggestion, index) {
|
||||
var suggestionDiv = document.createElement('div')
|
||||
suggestionsArea.appendChild(suggestionDiv)
|
||||
suggestionDiv.classList.add('suggestion')
|
||||
|
||||
var suggestionLi = document.createElement('li')
|
||||
suggestionDiv.appendChild(suggestionLi)
|
||||
|
||||
setTimeout(function () {
|
||||
suggestionLi.classList.add('items')
|
||||
suggestionLi.tabIndex = index + 1
|
||||
suggestionLi.style.padding = '2px 0'
|
||||
|
||||
function onAction(e) {
|
||||
goToStation(JSON.stringify(suggestion))
|
||||
}
|
||||
suggestionLi.addEventListener('click', onAction)
|
||||
suggestionLi.addEventListener('keypress', function (e) {
|
||||
if (e.key == 'Enter') {
|
||||
onAction(e)
|
||||
}
|
||||
})
|
||||
suggestionLi.addEventListener('focus', function (e) {
|
||||
focusedElement = suggestionLi
|
||||
})
|
||||
|
||||
var stationNameP = document.createElement('p')
|
||||
suggestionLi.appendChild(stationNameP)
|
||||
|
||||
stationNameP.textContent = suggestion.name || suggestion.address
|
||||
stationNameP.classList.add('pri', 'stationName')
|
||||
|
||||
if (window.localStorage) {
|
||||
var suggestionLink = document.createElement('a')
|
||||
suggestionDiv.appendChild(suggestionLink)
|
||||
suggestionLink.classList.add('no-custom-a')
|
||||
|
||||
var suggestionStar = document.createElement('object')
|
||||
suggestionLink.appendChild(suggestionStar)
|
||||
suggestionStar.classList.add('star')
|
||||
suggestionStar.type = 'image/svg+xml'
|
||||
function setStar() {
|
||||
if (starred.includes(JSON.stringify(suggestion))) {
|
||||
suggestionStar.data = '/icons/star_full.svg'
|
||||
suggestionStar.classList.add('checked')
|
||||
}
|
||||
else {
|
||||
suggestionStar.data = '/icons/star_empty.svg'
|
||||
suggestionStar.classList.remove('checked')
|
||||
}
|
||||
}
|
||||
suggestionLink.addEventListener('click', function (event) {
|
||||
event.preventDefault()
|
||||
if (starred.includes(JSON.stringify(suggestion))) {
|
||||
starred = starred.filter(function (s) {
|
||||
return s !== suggestion
|
||||
})
|
||||
} else {
|
||||
starred.push(JSON.stringify(suggestion))
|
||||
}
|
||||
setStar()
|
||||
localStorage.setItem('stations/starred', JSON.stringify(starred))
|
||||
})
|
||||
setStar()
|
||||
}
|
||||
|
||||
// var trainCompanyP = document.createElement('p')
|
||||
// suggestionLi.appendChild(trainCompanyP)
|
||||
|
||||
// trainCompanyP.textContent = suggestion.company
|
||||
// trainCompanyP.classList.add('thi')
|
||||
}, 0)
|
||||
})
|
||||
|
||||
setTimeout(function () {
|
||||
_rebuildDebounce = null
|
||||
if (_rebuildRequested) {
|
||||
rebuildSuggestions()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
var fetchAbortController = new AbortController()
|
||||
function reloadSuggestions() {
|
||||
var stationNameInput = document.getElementById('stationName')
|
||||
var stationName = searchNormalize(stationNameInput.value.trim())
|
||||
|
||||
var locationsUrl = new URL('https://v6.db.transport.rest/locations')
|
||||
locationsUrl.searchParams.set('query', stationName)
|
||||
locationsUrl.searchParams.set('limit', '25')
|
||||
locationsUrl.searchParams.set('fuzzy', 'true')
|
||||
locationsUrl.searchParams.set('stops', 'true')
|
||||
locationsUrl.searchParams.set('addresses', 'true')
|
||||
locationsUrl.searchParams.set('poi', 'true')
|
||||
|
||||
fetchAbortController.abort()
|
||||
fetchAbortController = new AbortController()
|
||||
fetch(locationsUrl.toString(), { signal: fetchAbortController.signal })
|
||||
.then(function (response) {
|
||||
if (response.ok) {
|
||||
return response.json()
|
||||
}
|
||||
else {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
.then(function (data) {
|
||||
if (data) {
|
||||
knownStations = Object.values(data)
|
||||
rebuildSuggestions()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef DbJourney
|
||||
* @prop {'journey'} type
|
||||
* @prop {(DbTrip & DbArrDep & {tripId: string})[]} legs
|
||||
* @prop {string} refreshToken
|
||||
* @prop {DbRemark[]} remarks
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {{journeys: DbJourney[]}} data
|
||||
*/
|
||||
function onItineraries(data) {
|
||||
var contentDiv = document.createElement('div')
|
||||
document.body.insertBefore(contentDiv, document.querySelector('footer'))
|
||||
contentDiv.classList.add('content')
|
||||
|
||||
for (var i = 0; i < data.journeys.length; i++) {
|
||||
var itineraryDiv = document.createElement('div')
|
||||
contentDiv.appendChild(itineraryDiv)
|
||||
|
||||
var heading = document.createElement('h4')
|
||||
itineraryDiv.appendChild(heading)
|
||||
heading.textContent = `Itinerary ${i + 1}`
|
||||
|
||||
var trainsDiv = document.createElement('div')
|
||||
itineraryDiv.appendChild(trainsDiv)
|
||||
trainsDiv.classList.add('itinerary-trains')
|
||||
|
||||
data.journeys[i].legs.forEach(function (train, idx) {
|
||||
var last = idx === data.journeys[i].legs.length - 1
|
||||
|
||||
var trainDiv = document.createElement('div')
|
||||
trainsDiv.appendChild(trainDiv)
|
||||
trainDiv.classList.add('itinerary-train')
|
||||
|
||||
if (idx === 0) {
|
||||
var departureTimeP = document.createElement('p')
|
||||
trainDiv.appendChild(departureTimeP)
|
||||
departureTimeP.classList.add('sec', 'departure', 'time')
|
||||
var departureTimePre = document.createElement('pre')
|
||||
departureTimeP.appendChild(departureTimePre)
|
||||
var departure = new Date(train.plannedDeparture)
|
||||
departureTimePre.textContent = departure.toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' })
|
||||
|
||||
var departureHeading = document.createElement('h3')
|
||||
trainDiv.appendChild(departureHeading)
|
||||
departureHeading.classList.add('departure', 'station')
|
||||
if (train.origin.type === 'stop' || train.origin.type === 'station') {
|
||||
var departureLink = document.createElement('a')
|
||||
departureHeading.appendChild(departureLink)
|
||||
departureLink.textContent = train.origin.name
|
||||
departureLink.classList.add('no-custom-a', 'items')
|
||||
var departureUrl = new URL('/view-station.html', window.location.origin)
|
||||
departureUrl.searchParams.set('stationId', train.origin.id)
|
||||
departureLink.href = departureUrl.toString()
|
||||
}
|
||||
else {
|
||||
var departureSpan = document.createElement('span')
|
||||
departureHeading.append(departureSpan)
|
||||
departureSpan.innerText = train.origin.name || train.origin.address
|
||||
}
|
||||
|
||||
if (train.departurePlatform || train.plannedDeparturePlatform) {
|
||||
var departurePlatformP = document.createElement('p')
|
||||
trainDiv.append(departurePlatformP)
|
||||
departurePlatformP.classList.add('sec', 'departure', 'platform')
|
||||
if (train.departurePlatform && train.departurePlatform != train.plannedDeparturePlatform) {
|
||||
departurePlatformP.classList.add('changed')
|
||||
}
|
||||
departurePlatformP.textContent = `${train.departurePlatform || train.plannedDeparturePlatform}`
|
||||
}
|
||||
}
|
||||
|
||||
var trainP = document.createElement('p')
|
||||
trainDiv.appendChild(trainP)
|
||||
trainP.classList.add('pri', 'train')
|
||||
if (!train.walking) {
|
||||
var trainLink = document.createElement('a')
|
||||
trainP.appendChild(trainLink)
|
||||
trainLink.innerText = train.line.name
|
||||
trainLink.classList.add('no-custom-a', 'items')
|
||||
var trainUrl = new URL('/view-train.html', window.location.origin)
|
||||
trainUrl.searchParams.set('tripId', train.tripId)
|
||||
trainLink.href = trainUrl.toString()
|
||||
trainP.appendChild(document.createTextNode(' '))
|
||||
if (train.line.operator) {
|
||||
var trainCompany = document.createElement('span')
|
||||
trainP.appendChild(trainCompany)
|
||||
trainCompany.textContent = '(' + train.line.operator.name + ')'
|
||||
trainCompany.classList.add('company')
|
||||
}
|
||||
}
|
||||
else {
|
||||
var walkingSpan = document.createElement('span')
|
||||
trainP.append(walkingSpan)
|
||||
walkingSpan.classList.add('walking')
|
||||
walkingSpan.innerText = `Walking (${train.distance} m)`
|
||||
}
|
||||
|
||||
var arrivalTimeP = document.createElement('p')
|
||||
trainDiv.appendChild(arrivalTimeP)
|
||||
arrivalTimeP.classList.add('sec', 'arrival', 'time')
|
||||
var arrivalTimePre = document.createElement('pre')
|
||||
arrivalTimeP.appendChild(arrivalTimePre)
|
||||
var arrival = new Date(train.plannedArrival)
|
||||
arrivalTimePre.textContent = arrival.toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' })
|
||||
|
||||
var arrivalHeading = document.createElement('h3')
|
||||
trainDiv.appendChild(arrivalHeading)
|
||||
arrivalHeading.classList.add('arrival', 'station')
|
||||
if (train.destination.type === 'stop' || train.destination.type === 'station') {
|
||||
var arrivalLink = document.createElement('a')
|
||||
arrivalHeading.appendChild(arrivalLink)
|
||||
arrivalLink.textContent = train.destination.name
|
||||
arrivalLink.classList.add('no-custom-a', 'items')
|
||||
var arrivalUrl = new URL('/view-station.html', window.location.origin)
|
||||
arrivalUrl.searchParams.set('stationId', train.destination.id)
|
||||
arrivalLink.href = arrivalUrl.toString()
|
||||
}
|
||||
else {
|
||||
var arrivalSpan = document.createElement('span')
|
||||
arrivalHeading.append(arrivalSpan)
|
||||
arrivalSpan.innerText = train.destination.name || train.origin.address
|
||||
}
|
||||
|
||||
if (train.arrivalPlatform || train.plannedArrivalPlatform) {
|
||||
var arrivalPlatformP = document.createElement('p')
|
||||
trainDiv.append(arrivalPlatformP)
|
||||
arrivalPlatformP.classList.add('sec', 'arrival', 'platform')
|
||||
if (train.arrivalPlatform && train.arrivalPlatform != train.plannedArrivalPlatform) {
|
||||
arrivalPlatformP.classList.add('changed')
|
||||
}
|
||||
arrivalPlatformP.textContent = `${train.arrivalPlatform || train.plannedArrivalPlatform}`
|
||||
}
|
||||
|
||||
if (!last) {
|
||||
var nextTrain = data.journeys[i].legs[idx + 1]
|
||||
var nextDepartureTimeP = document.createElement('p')
|
||||
trainDiv.appendChild(nextDepartureTimeP)
|
||||
nextDepartureTimeP.classList.add('sec', 'next-departure', 'time')
|
||||
var departureTimePre = document.createElement('pre')
|
||||
nextDepartureTimeP.appendChild(departureTimePre)
|
||||
var departure = new Date(nextTrain.plannedDeparture)
|
||||
departureTimePre.textContent = departure.toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' })
|
||||
|
||||
if (nextTrain.departurePlatform || nextTrain.plannedDeparturePlatform) {
|
||||
var departurePlatformP = document.createElement('p')
|
||||
trainDiv.append(departurePlatformP)
|
||||
departurePlatformP.classList.add('sec', 'next-departure', 'platform')
|
||||
if (nextTrain.departurePlatform && nextTrain.departurePlatform != nextTrain.plannedDeparturePlatform) {
|
||||
departurePlatformP.classList.add('changed')
|
||||
}
|
||||
departurePlatformP.textContent = `${nextTrain.departurePlatform || nextTrain.plannedDeparturePlatform}`
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function lsk() {
|
||||
document.getElementById('stationName').focus()
|
||||
}
|
||||
|
||||
function csk() {
|
||||
if (focusedElement == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (focusedElement.id === 'stationName') {
|
||||
goToTrain(document.activeElement.value.trim())
|
||||
}
|
||||
else {
|
||||
focusedElement.click()
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('load', function (e) {
|
||||
var sp = new URL(window.location.href).searchParams
|
||||
fromStation = sp.get('from')
|
||||
var fromJson = JSON.parse(fromStation || 'null')
|
||||
toStation = sp.get('to')
|
||||
var toJson = JSON.parse(toStation || 'null')
|
||||
var departureDateStr = sp.get('departureDate')
|
||||
if (departureDateStr) {
|
||||
departureDate = new Date(departureDateStr)
|
||||
}
|
||||
|
||||
var titleH1 = document.querySelector("header > h1")
|
||||
if (!fromStation) {
|
||||
titleH1.textContent = 'Find Route - From'
|
||||
}
|
||||
else if (!toStation) {
|
||||
titleH1.textContent = 'Find Route - To'
|
||||
}
|
||||
else if (!departureDate) {
|
||||
titleH1.textContent = 'Find Route - Departure Date'
|
||||
}
|
||||
else {
|
||||
// titleH1.textContent = `${fromJson.name || fromJson.address} - ${toJson.name || toJson.address}`
|
||||
titleH1.textContent = 'Find Route - Configure'
|
||||
}
|
||||
|
||||
if (window.localStorage) {
|
||||
var maybeTransitKind = JSON.parse(localStorage.getItem('config-route/transitKind'))
|
||||
if (maybeTransitKind) {
|
||||
transitKind = maybeTransitKind
|
||||
}
|
||||
|
||||
var maybeStarred = JSON.parse(localStorage.getItem('stations/starred'))
|
||||
if (maybeStarred) {
|
||||
starred = maybeStarred
|
||||
}
|
||||
}
|
||||
|
||||
var footer = document.querySelector('footer')
|
||||
|
||||
if (!fromStation || !toStation) {
|
||||
// Build station selection UI
|
||||
document.body.insertBefore(
|
||||
h4(
|
||||
label('Station Name').att$('for', 'stationName'),
|
||||
),
|
||||
footer,
|
||||
)
|
||||
// var stationNameH4 = document.createElement('h4')
|
||||
// document.body.insertBefore(stationNameH4, footer)
|
||||
// var stationNameLabel = document.createElement('label')
|
||||
// stationNameH4.appendChild(stationNameLabel)
|
||||
// stationNameLabel.htmlFor = 'stationName'
|
||||
// stationNameLabel.textContent = 'Station Name'
|
||||
|
||||
document.body.insertBefore(
|
||||
input('search').id$('stationName').att$('name', 'stationName').class$('items'),
|
||||
footer,
|
||||
)
|
||||
// var stationNameInput = document.createElement('input')
|
||||
// document.body.insertBefore(stationNameInput, footer)
|
||||
// stationNameInput.type = 'search'
|
||||
// stationNameInput.classList.add('items')
|
||||
// stationNameInput.name = 'stationName'
|
||||
// stationNameInput.id = 'stationName'
|
||||
|
||||
document.body.insertBefore(h4('Suggestions'), footer)
|
||||
// var suggestionsH4 = document.createElement('h4')
|
||||
// document.body.insertBefore(suggestionsH4, footer)
|
||||
// suggestionsH4.textContent = 'Suggestions'
|
||||
|
||||
var contentDiv = document.createElement('div')
|
||||
document.body.insertBefore(contentDiv, footer)
|
||||
contentDiv.classList.add('content')
|
||||
var suggestionsUl = document.createElement('ul')
|
||||
contentDiv.appendChild(suggestionsUl)
|
||||
suggestionsUl.id = 'suggestionsArea'
|
||||
rebuildSuggestions()
|
||||
|
||||
document.querySelector('.csk').textContent = 'Search'
|
||||
|
||||
var stationName = document.getElementById('stationName')
|
||||
stationName.addEventListener('input', function (e) {
|
||||
reloadSuggestions()
|
||||
})
|
||||
stationName.addEventListener('focus', function (e) {
|
||||
focusedElement = stationName
|
||||
document.getElementsByClassName('lsk')[0].textContent = ''
|
||||
document.getElementsByClassName('csk')[0].textContent = 'Search'
|
||||
})
|
||||
stationName.addEventListener('blur', function (e) {
|
||||
document.getElementsByClassName('lsk')[0].textContent = 'Search'
|
||||
document.getElementsByClassName('csk')[0].textContent = 'Select'
|
||||
})
|
||||
stationName.addEventListener('keypress', function (e) {
|
||||
if (e.key == 'Enter') {
|
||||
goToStation(stationName.value.trim())
|
||||
}
|
||||
})
|
||||
}
|
||||
else if (!departureDate) {
|
||||
var departureDateH4 = document.createElement('h4')
|
||||
document.body.insertBefore(departureDateH4, footer)
|
||||
departureDateH4.textContent = 'Departure Date'
|
||||
|
||||
var contentDiv = document.createElement('div')
|
||||
document.body.insertBefore(contentDiv, footer)
|
||||
contentDiv.classList.add('content')
|
||||
var departureDateUl = document.createElement('ul')
|
||||
contentDiv.appendChild(departureDateUl)
|
||||
departureDateUl.id = 'suggestionsArea'
|
||||
|
||||
for (var i = -1, departureOption = (function () { var d = new Date(); d.setDate(d.getDate() - 1); return d })(); i < 30; i++, departureOption.setDate(departureOption.getDate() + 1)) {
|
||||
var suggestionLi = document.createElement('li')
|
||||
departureDateUl.appendChild(suggestionLi)
|
||||
suggestionLi.classList.add('items')
|
||||
suggestionLi.tabIndex = i + 10
|
||||
// Capture
|
||||
;(function () {
|
||||
var d = new Date(departureOption.getTime())
|
||||
function onAction() {
|
||||
setDepartureDate(d)
|
||||
}
|
||||
suggestionLi.addEventListener('click', onAction)
|
||||
suggestionLi.addEventListener('keypress', function (e) {
|
||||
if (e.key == 'Enter') {
|
||||
onAction(e)
|
||||
}
|
||||
})
|
||||
suggestionLi.addEventListener('focus', function (e) {
|
||||
focusedElement = suggestionLi
|
||||
})
|
||||
if (i === 0) {
|
||||
suggestionLi.focus()
|
||||
}
|
||||
})()
|
||||
var innerP = document.createElement('p')
|
||||
suggestionLi.appendChild(innerP)
|
||||
innerP.classList.add('pri')
|
||||
var innerPre = document.createElement('pre')
|
||||
innerP.appendChild(innerPre)
|
||||
innerPre.textContent = `${departureOption.getDate().toString().padStart(2, '0')}.${(departureOption.getMonth() + 1).toString().padStart(2, '0')}.${departureOption.getFullYear().toString().padStart(4, '0')}`
|
||||
if (i === 0) {
|
||||
innerPre.textContent += ' (today)'
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelector('.csk').textContent = 'Select'
|
||||
}
|
||||
else {
|
||||
// var contentDiv = document.createElement('div')
|
||||
// document.body.insertBefore(contentDiv, footer)
|
||||
// contentDiv.classList.add('content')
|
||||
// contentDiv.style.display = 'flex'
|
||||
// contentDiv.style.flexDirection = 'column'
|
||||
|
||||
function updateSearchLink() {
|
||||
var a = document.getElementById('search-link')
|
||||
var url = new URL(window.location.href)
|
||||
url.pathname = 'route.html'
|
||||
url.searchParams.set('departureDate', departureDate.toISOString())
|
||||
url.searchParams.set('from', JSON.stringify(fromJson))
|
||||
url.searchParams.set('to', JSON.stringify(toJson))
|
||||
url.searchParams.set('transitKind', JSON.stringify(transitKind))
|
||||
a.href = url.toString()
|
||||
}
|
||||
|
||||
function transitKindCheckChanged(event) {
|
||||
transitKind[event.target.id.slice(13)] = event.target.checked
|
||||
if (window.localStorage) {
|
||||
localStorage.setItem('config-route/transitKind', JSON.stringify(transitKind))
|
||||
}
|
||||
updateSearchLink()
|
||||
}
|
||||
|
||||
var contentDiv = div(
|
||||
h4('Route'),
|
||||
p('From').class$('thi'),
|
||||
p(fromJson.name || fromJson.address).class$('pri'),
|
||||
p('To').class$('thi'),
|
||||
p(toJson.name || toJson.address).class$('pri'),
|
||||
// a('', 'Configure via...'),
|
||||
h4('Date and time'),
|
||||
p('Date').class$('thi'),
|
||||
p(departureDate.toDateString()).class$('pri'),
|
||||
p(label('Time').att$('for', 'time')).class$('thi'),
|
||||
p(
|
||||
input('time')
|
||||
.id$('time')
|
||||
.att$('value', departureDate.getHours().toString().padStart(2, '0') + ':' + departureDate.getMinutes().toString().padStart(2, '0'))
|
||||
.event$('input', function(event) {
|
||||
var text = event.target.value
|
||||
var splitted = text.toString().split(':')
|
||||
var h = parseInt(splitted[0])
|
||||
var m = parseInt(splitted[1])
|
||||
departureDate.setHours(h, m)
|
||||
updateSearchLink()
|
||||
}),
|
||||
),
|
||||
h4('Train categories'),
|
||||
div(
|
||||
p(label('ICE, RJX, High speed').class$('product-nationalExpress').att$('for', 'transit-kind-ice')),
|
||||
input('checkbox')
|
||||
.checked$(transitKind.ice)
|
||||
.id$('transit-kind-ice')
|
||||
.class$('transit-kind')
|
||||
.event$('change', transitKindCheckChanged),
|
||||
).class$('checkbox'),
|
||||
div(
|
||||
p(label('IC, EC, RJ').class$('product-national').att$('for', 'transit-kind-ic')),
|
||||
input('checkbox')
|
||||
.checked$(transitKind.ic)
|
||||
.id$('transit-kind-ic')
|
||||
.class$('transit-kind')
|
||||
.event$('change', transitKindCheckChanged),
|
||||
).class$('checkbox'),
|
||||
div(
|
||||
p(label('RE, IRE').class$('product-regionalExpress').att$('for', 'transit-kind-re')),
|
||||
input('checkbox')
|
||||
.checked$(transitKind.re)
|
||||
.id$('transit-kind-re')
|
||||
.class$('transit-kind')
|
||||
.event$('change', transitKindCheckChanged),
|
||||
).class$('checkbox'),
|
||||
div(
|
||||
p(label('RB').class$('product-regional').att$('for', 'transit-kind-rb')),
|
||||
input('checkbox')
|
||||
.checked$(transitKind.rb)
|
||||
.id$('transit-kind-rb')
|
||||
.class$('transit-kind')
|
||||
.event$('change', transitKindCheckChanged),
|
||||
).class$('checkbox'),
|
||||
div(
|
||||
p(label('S-Bahn').class$('product-suburban').att$('for', 'transit-kind-s')),
|
||||
input('checkbox')
|
||||
.checked$(transitKind.s)
|
||||
.id$('transit-kind-s')
|
||||
.class$('transit-kind')
|
||||
.event$('change', transitKindCheckChanged),
|
||||
).class$('checkbox'),
|
||||
div(
|
||||
p(label('U-Bahn').class$('product-subway').att$('for', 'transit-kind-u')),
|
||||
input('checkbox')
|
||||
.checked$(transitKind.u)
|
||||
.id$('transit-kind-u')
|
||||
.class$('transit-kind')
|
||||
.event$('change', transitKindCheckChanged),
|
||||
).class$('checkbox'),
|
||||
div(
|
||||
p(label('Tram, Stadtbahn').class$('product-tram').att$('for', 'transit-kind-tram')),
|
||||
input('checkbox')
|
||||
.checked$(transitKind.tram)
|
||||
.id$('transit-kind-tram')
|
||||
.class$('transit-kind')
|
||||
.event$('change', transitKindCheckChanged),
|
||||
).class$('checkbox'),
|
||||
div(
|
||||
p(label('Bus').class$('product-bus').att$('for', 'transit-kind-bus')),
|
||||
input('checkbox')
|
||||
.checked$(transitKind.bus)
|
||||
.id$('transit-kind-bus')
|
||||
.class$('transit-kind')
|
||||
.event$('change', transitKindCheckChanged),
|
||||
).class$('checkbox'),
|
||||
div(
|
||||
p(label('Ferry').class$('product-ferry').att$('for', 'transit-kind-ferry')),
|
||||
input('checkbox')
|
||||
.checked$(transitKind.ferry)
|
||||
.id$('transit-kind-ferry')
|
||||
.class$('transit-kind')
|
||||
.event$('change', transitKindCheckChanged),
|
||||
).class$('checkbox'),
|
||||
h4('Start search'),
|
||||
a('', 'Search').id$('search-link'),
|
||||
).class$('content')
|
||||
document.body.insertBefore(contentDiv, footer)
|
||||
contentDiv.style.display = 'flex'
|
||||
contentDiv.style.flexDirection = 'column'
|
||||
|
||||
updateSearchLink()
|
||||
}
|
||||
|
||||
document.querySelectorAll('.lsk').forEach(function (lskElem) {
|
||||
lskElem.addEventListener('click', function (e) {
|
||||
lsk()
|
||||
})
|
||||
})
|
||||
document.querySelectorAll('.csk').forEach(function (cskElem) {
|
||||
cskElem.addEventListener('click', function (e) {
|
||||
csk()
|
||||
})
|
||||
})
|
||||
document.body.addEventListener('keydown', function (e) {
|
||||
if (e.key == 'SoftLeft') {
|
||||
lsk()
|
||||
}
|
||||
else if (e.key == 'Enter') {
|
||||
csk()
|
||||
}
|
||||
})
|
||||
})
|
1
icons/star_empty.svg
Normal file
1
icons/star_empty.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m354-247 126-76 126 77-33-144 111-96-146-13-58-136-58 135-146 13 111 97-33 143ZM233-80l65-281L80-550l288-25 112-265 112 265 288 25-218 189 65 281-247-149L233-80Zm247-350Z"/></svg>
|
After Width: | Height: | Size: 276 B |
1
icons/star_full.svg
Normal file
1
icons/star_full.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m233-80 65-281L80-550l288-25 112-265 112 265 288 25-218 189 65 281-247-149L233-80Z"/></svg>
|
After Width: | Height: | Size: 188 B |
17
index.html
17
index.html
|
@ -4,31 +4,36 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>InfoTren</title>
|
||||
<title>InfoDTrain</title>
|
||||
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
|
||||
<link rel="stylesheet" href="/base.css">
|
||||
<link rel="stylesheet" href="/base.dark.css">
|
||||
|
||||
<script src="/common/worker.js"></script>
|
||||
<script src="/common/items.js"></script>
|
||||
<script src="/common/trainId.js"></script>
|
||||
<script src="index.js"></script>
|
||||
|
||||
<script>
|
||||
window.bSupportsGamepadUI = 1;
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="left"></div>
|
||||
<h1>InfoTren</h1>
|
||||
<h1>InfoDTrain</h1>
|
||||
<div class="right"></div>
|
||||
</header>
|
||||
<div class="header-placeholder"></div>
|
||||
|
||||
<div class="content">
|
||||
<ul>
|
||||
<li id="routes-li"><a class="items" href="route.html">Train routes</a></li>
|
||||
<li id="my-train-li"><a class="items" href="train.html">My train</a></li>
|
||||
<li id="station-arr-dep-li"><a class="items" href="station.html">Station departures/arrivals</a></li>
|
||||
<li><a class="items" href="about.html">About</a></li>
|
||||
<li id="routes-li"><a class="items" href="config-route.html" tabstop="0">Train routes</a></li>
|
||||
<li id="my-train-li"><a class="items" href="train.html" tabstop="0">My train</a></li>
|
||||
<li id="station-arr-dep-li"><a class="items" href="station.html" tabstop="0">Station departures/arrivals</a></li>
|
||||
<li><a class="items" href="about.html" tabstop="0">About</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
73
index.js
73
index.js
|
@ -1,59 +1,76 @@
|
|||
window.addEventListener('load', function (e) {
|
||||
if (window.localStorage) {
|
||||
var recentViewTrain = localStorage.getItem('recent/view-train')
|
||||
if (recentViewTrain) {
|
||||
var recentViewTrainStr = localStorage.getItem('recent/view-train')
|
||||
if (recentViewTrainStr) {
|
||||
/**
|
||||
* @property {string} trainNumber
|
||||
* @type {object}
|
||||
* @property {?string} trainNumber
|
||||
* @property {?string} name
|
||||
* @property {string} tripId
|
||||
* @property {string} date
|
||||
* @property {string} $addDate
|
||||
* @property {string | undefined} groupIndex
|
||||
*/
|
||||
recentViewTrain = JSON.parse(recentViewTrain)
|
||||
var recentViewTrain = JSON.parse(recentViewTrainStr)
|
||||
var addDate = new Date(recentViewTrain.$addDate)
|
||||
addDate.setHours(addDate.getHours() + 2) // store recents for 2 hours
|
||||
addDate.setHours(addDate.getHours() + 6) // store recents for 6 hours
|
||||
if (addDate.getTime() > Date.now()) {
|
||||
var recentViewTrainLi = document.createElement('li')
|
||||
var recentViewTrainLink = document.createElement('a')
|
||||
recentViewTrainLi.appendChild(recentViewTrainLink)
|
||||
var recentViewTrainLinkUrl = new URL('/view-train.html', window.location.origin)
|
||||
recentViewTrainLinkUrl.searchParams.append('train', recentViewTrain.trainNumber)
|
||||
recentViewTrainLinkUrl.searchParams.append('tripId', recentViewTrain.tripId)
|
||||
recentViewTrainLinkUrl.searchParams.append('date', recentViewTrain.date)
|
||||
if (recentViewTrain.groupIndex) {
|
||||
recentViewTrainLinkUrl.searchParams.append('groupIndex', recentViewTrain.groupIndex)
|
||||
}
|
||||
recentViewTrainLink.href = recentViewTrainLinkUrl.toString()
|
||||
recentViewTrainLink.classList.add('items')
|
||||
recentViewTrainLink.innerText = `Recent train: ${recentViewTrain.trainNumber}`
|
||||
recentViewTrainLink.innerText = `Recent train: ${recentViewTrain.name || "..."}`
|
||||
if (recentViewTrain.trainNumber) {
|
||||
recentViewTrainLink.innerText = `Recent train: ${recentViewTrain.name || "..."} (${recentViewTrain.trainNumber})`
|
||||
}
|
||||
|
||||
fetch(`https://scraper.infotren.dcdev.ro/v3/trains/${recentViewTrain.trainNumber}?date=${recentViewTrain.date}`)
|
||||
fetch(`https://v6.db.transport.rest/trips/${encodeURI(recentViewTrain.tripId)}`)
|
||||
.then(function (result) {
|
||||
if (result.ok) {
|
||||
return result.json()
|
||||
}
|
||||
else {
|
||||
return Promise.reject('Response not okay')
|
||||
}
|
||||
})
|
||||
.then(function (result) {
|
||||
recentViewTrainLink.innerText = 'Recent train: '
|
||||
trainIdSpan(result.rank, result.number, recentViewTrainLink)
|
||||
if (recentViewTrain.groupIndex !== undefined || result.groups.length === 1) {
|
||||
var group = result.groups[recentViewTrain.groupIndex || 0]
|
||||
if (group.status) {
|
||||
if (group.status.delay === 0) {
|
||||
recentViewTrainLink.appendChild(document.createTextNode(" (on time)"))
|
||||
}
|
||||
else if (group.status.delay > 0) {
|
||||
recentViewTrainLink.appendChild(document.createTextNode(` (${group.status.delay} min late)`))
|
||||
}
|
||||
else {
|
||||
recentViewTrainLink.appendChild(document.createTextNode(` (${-group.status.delay} min early)`))
|
||||
}
|
||||
}
|
||||
}
|
||||
recentViewTrainLink.innerText = `Recent train: ${result.trip.line.name} (${result.trip.line.fahrtNr})`
|
||||
})
|
||||
|
||||
var myTrainLi = document.getElementById("my-train-li")
|
||||
myTrainLi.parentNode.insertBefore(recentViewTrainLi, myTrainLi.nextSibling)
|
||||
}
|
||||
}
|
||||
|
||||
var recentRouteStr = localStorage.getItem('recent/route')
|
||||
if (recentRouteStr) {
|
||||
/**
|
||||
* @type {object}
|
||||
* @property {string} queryParams
|
||||
* @property {string} from
|
||||
* @property {string} to
|
||||
* @property {string} $addDate
|
||||
*/
|
||||
var recentRoute = JSON.parse(recentRouteStr)
|
||||
var addDate = new Date(recentRoute.$addDate)
|
||||
addDate.setHours(addDate.getHours() + 12) // store recents for 6 hours
|
||||
if (addDate.getTime() > Date.now()) {
|
||||
var recentRouteLi = document.createElement('li')
|
||||
var recentRouteLink = document.createElement('a')
|
||||
recentRouteLi.appendChild(recentRouteLink)
|
||||
var recentRouteLinkUrl = new URL('/config-route.html', window.location.origin)
|
||||
recentRouteLinkUrl.search = recentRoute.queryParams
|
||||
recentRouteLink.href = recentRouteLinkUrl.toString()
|
||||
recentRouteLink.classList.add('items')
|
||||
recentRouteLink.innerText = `Recent route: ${recentRoute.from} → ${recentRoute.to}`
|
||||
|
||||
var routesLi = document.getElementById("routes-li")
|
||||
routesLi.parentNode.insertBefore(recentRouteLi, routesLi.nextSibling)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
{
|
||||
"id": "ro.dcdev.infotren.kai",
|
||||
"name": "Info Tren: Romanian Railways",
|
||||
"short_name": "Info Tren",
|
||||
"description": "Web application for Informatica Feroviară scraper, showing data about Romanian Railways",
|
||||
"name": "InfoDTrain",
|
||||
"short_name": "InfoDTrain",
|
||||
"description": "Web application for Deutsche Bahn API",
|
||||
"theme_color": "#0000ff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone",
|
||||
|
@ -27,7 +26,7 @@
|
|||
},
|
||||
{
|
||||
"name": "Train Routes",
|
||||
"url": "/route.html",
|
||||
"url": "/config-route.html",
|
||||
"description": "Plan an itinerary"
|
||||
}
|
||||
],
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"version": "1",
|
||||
"name": "InfoTren",
|
||||
"name": "InfoDTrain",
|
||||
"launch_path": "/index.html",
|
||||
"description": "Frontend for InfoFer scraper",
|
||||
"description": "Frontend for Deutsche Bahn API",
|
||||
"developer": {
|
||||
"name": "Dan Cojocaru",
|
||||
"url": "https://dcdev.ro"
|
||||
|
|
58
route.css
58
route.css
|
@ -1,38 +1,47 @@
|
|||
.itinerary-train {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
grid-template-rows: repeat(auto-fit, auto);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.itinerary-train:not(:last-child) {
|
||||
grid-template-areas:
|
||||
"dep-time dep-station"
|
||||
"train train"
|
||||
"arr-time arrdep-station"
|
||||
"dep2-time arrdep-station";
|
||||
"dep-time dep-station dep-platform"
|
||||
"train train train"
|
||||
"arr-time arrdep-station arr-platform"
|
||||
"dep2-time arrdep-station dep2-platform";
|
||||
}
|
||||
|
||||
.itinerary-train:last-child {
|
||||
grid-template-areas:
|
||||
"train train"
|
||||
"arr-time arr-station";
|
||||
"train train train"
|
||||
"arr-time arr-station arr-platform";
|
||||
}
|
||||
|
||||
.itinerary-train:only-child {
|
||||
grid-template-areas:
|
||||
"dep-time dep-station"
|
||||
"train train"
|
||||
"arr-time arr-station";
|
||||
"dep-time dep-station dep-platform"
|
||||
"train train train"
|
||||
"arr-time arr-station arr-platform";
|
||||
}
|
||||
|
||||
.itinerary-train .departure.time {
|
||||
grid-area: dep-time;
|
||||
}
|
||||
|
||||
.itinerary-train .departure.platform {
|
||||
grid-area: dep-platform;
|
||||
}
|
||||
|
||||
.itinerary-train .next-departure.time {
|
||||
grid-area: dep2-time;
|
||||
}
|
||||
|
||||
.itinerary-train .next-departure.platform {
|
||||
grid-area: dep2-platform;
|
||||
}
|
||||
|
||||
.itinerary-train .departure.station {
|
||||
grid-area: dep-station;
|
||||
}
|
||||
|
@ -45,6 +54,10 @@
|
|||
grid-area: arr-time;
|
||||
}
|
||||
|
||||
.itinerary-train .arrival.platform {
|
||||
grid-area: arr-platform;
|
||||
}
|
||||
|
||||
.itinerary-train:not(:last-child) .arrival.station {
|
||||
grid-area: arrdep-station;
|
||||
align-self: center;
|
||||
|
@ -57,9 +70,34 @@
|
|||
.itinerary-train .time {
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.itinerary-train .platform {
|
||||
margin: 2px;
|
||||
padding: 2px;
|
||||
border: 1px solid black;
|
||||
border-radius: 4px;
|
||||
justify-self: end;
|
||||
min-width: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.itinerary-train .platform.changed {
|
||||
color: red;
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.train .company {
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.walking {
|
||||
font-size: 0.95em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
input#time {
|
||||
margin: 1px;
|
||||
}
|
||||
|
|
11
route.dark.css
Normal file
11
route.dark.css
Normal file
|
@ -0,0 +1,11 @@
|
|||
@media (prefers-color-scheme: dark) {
|
||||
.itinerary-train .platform {
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
.itinerary-train .platform.changed {
|
||||
color: #ff3333;
|
||||
border-color: #ff3333;
|
||||
}
|
||||
|
||||
}
|
|
@ -4,15 +4,18 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Route - InfoTren</title>
|
||||
<title>Route - InfoDTrain</title>
|
||||
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
|
||||
<link rel="stylesheet" href="/base.css">
|
||||
<link rel="stylesheet" href="/base.dark.css">
|
||||
<link rel="stylesheet" href="route.css">
|
||||
<link rel="stylesheet" href="route.dark.css">
|
||||
|
||||
<script src="/common/worker.js"></script>
|
||||
<script defer src="/common/back.js"></script>
|
||||
<script defer src="/common/components.js"></script>
|
||||
<script defer src="/common/items.js"></script>
|
||||
<script defer src="/common/trainId.js"></script>
|
||||
<script defer src="route.js"></script>
|
||||
|
|
383
route.js
383
route.js
|
@ -10,19 +10,32 @@ var toStation = null
|
|||
* @type {Date | null}
|
||||
*/
|
||||
var departureDate = null
|
||||
var transitKind = {
|
||||
ice: true,
|
||||
ic: true,
|
||||
re: true,
|
||||
rb: true,
|
||||
s: true,
|
||||
bus: true,
|
||||
ferry: true,
|
||||
u: true,
|
||||
tram: true,
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {{ name: string, stoppedAtBy: string[] }[]}
|
||||
* @type {{id: string, name: string}[]}
|
||||
*/
|
||||
var knownStations = []
|
||||
|
||||
function goToStation(station) {
|
||||
var itineraries = null
|
||||
|
||||
function goToStation(stationId) {
|
||||
var url = new URL(window.location.href)
|
||||
if (!fromStation) {
|
||||
url.searchParams.set('from', station)
|
||||
url.searchParams.set('from', stationId)
|
||||
}
|
||||
else if (!toStation) {
|
||||
url.searchParams.set('to', station)
|
||||
url.searchParams.set('to', stationId)
|
||||
}
|
||||
// url.searchParams.set('date', new Date().toISOString())
|
||||
window.location.href = url.toString()
|
||||
|
@ -62,41 +75,9 @@ function rebuildSuggestions() {
|
|||
suggestionsArea.childNodes[0].remove()
|
||||
}
|
||||
|
||||
var stationNameInput = document.getElementById('stationName')
|
||||
var stationName = searchNormalize(stationNameInput.value.trim())
|
||||
var suggestions = knownStations.slice()
|
||||
|
||||
var suggestions = []
|
||||
if (!stationName) {
|
||||
suggestions = knownStations.slice()
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < knownStations.length; i++) {
|
||||
if (!searchNormalize(knownStations[i].name).includes(stationName)) {
|
||||
continue
|
||||
}
|
||||
suggestions.push(knownStations[i])
|
||||
}
|
||||
suggestions.sort((s1, s2) => {
|
||||
var s1n = searchNormalize(s1.name);
|
||||
var s2n = searchNormalize(s2.name);
|
||||
|
||||
if (s1n.indexOf(stationName) != s2n.indexOf(stationName)) {
|
||||
return s1n.indexOf(stationName) - s2n.indexOf(stationName);
|
||||
}
|
||||
|
||||
if (s1.stoppedAtBy.length != s2.stoppedAtBy.length) {
|
||||
return s2.stoppedAtBy.length - s1.stoppedAtBy.length;
|
||||
}
|
||||
|
||||
return s1.name.localeCompare(s2.name);
|
||||
})
|
||||
}
|
||||
|
||||
var foundInput = false
|
||||
suggestions.forEach(function (suggestion, index) {
|
||||
if (stationName == searchNormalize(suggestion.name)) {
|
||||
foundInput = true
|
||||
}
|
||||
var suggestionLi = document.createElement('li')
|
||||
suggestionsArea.appendChild(suggestionLi)
|
||||
|
||||
|
@ -106,7 +87,7 @@ function rebuildSuggestions() {
|
|||
suggestionLi.style.padding = '2px 0'
|
||||
|
||||
function onAction(e) {
|
||||
goToStation(suggestion.name)
|
||||
goToStation(JSON.stringify(suggestion))
|
||||
}
|
||||
suggestionLi.addEventListener('click', onAction)
|
||||
suggestionLi.addEventListener('keypress', function (e) {
|
||||
|
@ -121,7 +102,7 @@ function rebuildSuggestions() {
|
|||
var stationNameP = document.createElement('p')
|
||||
suggestionLi.appendChild(stationNameP)
|
||||
|
||||
stationNameP.textContent = suggestion.name
|
||||
stationNameP.textContent = suggestion.name || suggestion.address
|
||||
stationNameP.classList.add('pri', 'stationName')
|
||||
|
||||
// var trainCompanyP = document.createElement('p')
|
||||
|
@ -131,33 +112,6 @@ function rebuildSuggestions() {
|
|||
// trainCompanyP.classList.add('thi')
|
||||
}, 0)
|
||||
})
|
||||
if (!foundInput && stationName) {
|
||||
var suggestionLi = document.createElement('li')
|
||||
suggestionsArea.appendChild(suggestionLi)
|
||||
|
||||
suggestionLi.classList.add('items')
|
||||
suggestionLi.tabIndex = suggestions.length + 2
|
||||
suggestionLi.style.padding = '2px 0'
|
||||
|
||||
function onAction(e) {
|
||||
goToStation(stationNameInput.value.trim())
|
||||
}
|
||||
suggestionLi.addEventListener('click', onAction)
|
||||
suggestionLi.addEventListener('keypress', function (e) {
|
||||
if (e.key == 'Enter') {
|
||||
onAction(e)
|
||||
}
|
||||
})
|
||||
suggestionLi.addEventListener('focus', function (e) {
|
||||
focusedElement = suggestionLi
|
||||
})
|
||||
|
||||
var stationNameP = document.createElement('p')
|
||||
suggestionLi.appendChild(stationNameP)
|
||||
|
||||
stationNameP.textContent = stationNameInput.value.trim()
|
||||
stationNameP.classList.add('pri', 'stationName')
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
_rebuildDebounce = null
|
||||
|
@ -167,31 +121,58 @@ function rebuildSuggestions() {
|
|||
}, 500)
|
||||
}
|
||||
|
||||
var fetchAbortController = new AbortController()
|
||||
function reloadSuggestions() {
|
||||
var stationNameInput = document.getElementById('stationName')
|
||||
var stationName = searchNormalize(stationNameInput.value.trim())
|
||||
|
||||
var locationsUrl = new URL('https://v6.db.transport.rest/locations')
|
||||
locationsUrl.searchParams.set('query', stationName)
|
||||
locationsUrl.searchParams.set('limit', '25')
|
||||
locationsUrl.searchParams.set('fuzzy', 'true')
|
||||
locationsUrl.searchParams.set('stops', 'true')
|
||||
locationsUrl.searchParams.set('addresses', 'true')
|
||||
locationsUrl.searchParams.set('poi', 'true')
|
||||
|
||||
fetchAbortController.abort()
|
||||
fetchAbortController = new AbortController()
|
||||
fetch(locationsUrl.toString(), { signal: fetchAbortController.signal })
|
||||
.then(function (response) {
|
||||
return response.json()
|
||||
})
|
||||
.then(function (data) {
|
||||
if (data) {
|
||||
knownStations = Object.values(data)
|
||||
rebuildSuggestions()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef ItineraryTrain
|
||||
* @property {string} from
|
||||
* @property {string} to
|
||||
* @property {string[]} intermediateStops
|
||||
* @property {string} departureDate
|
||||
* @property {string} arrivalDate
|
||||
* @property {number} km
|
||||
* @property {string} operator
|
||||
* @property {string} trainRank
|
||||
* @property {string} trainNumber
|
||||
* @typedef DbJourney
|
||||
* @prop {'journey'} type
|
||||
* @prop {(DbTrip & DbArrDep & {tripId: string})[]} legs
|
||||
* @prop {string} refreshToken
|
||||
* @prop {DbRemark[]} remarks
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef Itinerary
|
||||
* @property {ItineraryTrain[]} trains
|
||||
*/
|
||||
/**
|
||||
* @param {Itinerary[]} data
|
||||
* @param {{journeys: DbJourney[]}} data
|
||||
*/
|
||||
function onItineraries(data) {
|
||||
var contentDiv = document.createElement('div')
|
||||
var contentDiv = document.getElementById('content-div')
|
||||
if (!contentDiv) {
|
||||
contentDiv = document.createElement('div')
|
||||
document.body.insertBefore(contentDiv, document.querySelector('footer'))
|
||||
contentDiv.classList.add('content')
|
||||
contentDiv.id = 'content-div'
|
||||
}
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
while (contentDiv.childNodes.length > 0) {
|
||||
contentDiv.childNodes[contentDiv.childNodes.length - 1].remove()
|
||||
}
|
||||
|
||||
for (var i = 0; i < data.journeys.length; i++) {
|
||||
var itineraryDiv = document.createElement('div')
|
||||
contentDiv.appendChild(itineraryDiv)
|
||||
|
||||
|
@ -203,8 +184,8 @@ function onItineraries(data) {
|
|||
itineraryDiv.appendChild(trainsDiv)
|
||||
trainsDiv.classList.add('itinerary-trains')
|
||||
|
||||
data[i].trains.forEach(function (train, idx) {
|
||||
var last = idx === data[i].trains.length - 1
|
||||
data.journeys[i].legs.forEach(function (train, idx) {
|
||||
var last = idx === data.journeys[i].legs.length - 1
|
||||
|
||||
var trainDiv = document.createElement('div')
|
||||
trainsDiv.appendChild(trainDiv)
|
||||
|
@ -216,64 +197,127 @@ function onItineraries(data) {
|
|||
departureTimeP.classList.add('sec', 'departure', 'time')
|
||||
var departureTimePre = document.createElement('pre')
|
||||
departureTimeP.appendChild(departureTimePre)
|
||||
var departure = new Date(train.departureDate)
|
||||
var departure = new Date(train.plannedDeparture)
|
||||
departureTimePre.textContent = departure.toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' })
|
||||
|
||||
var departureHeading = document.createElement('h3')
|
||||
trainDiv.appendChild(departureHeading)
|
||||
departureHeading.classList.add('departure', 'station')
|
||||
if (train.origin.type === 'stop' || train.origin.type === 'station') {
|
||||
var departureLink = document.createElement('a')
|
||||
departureHeading.appendChild(departureLink)
|
||||
departureLink.textContent = train.from
|
||||
departureLink.textContent = train.origin.name
|
||||
departureLink.classList.add('no-custom-a', 'items')
|
||||
var departureUrl = new URL('/view-station.html', window.location.origin)
|
||||
departureUrl.searchParams.set('station', train.from)
|
||||
departureUrl.searchParams.set('stationId', train.origin.id)
|
||||
departureLink.href = departureUrl.toString()
|
||||
}
|
||||
else {
|
||||
var departureSpan = document.createElement('span')
|
||||
departureHeading.append(departureSpan)
|
||||
departureSpan.innerText = train.origin.name || train.origin.address
|
||||
}
|
||||
|
||||
if (train.departurePlatform || train.plannedDeparturePlatform) {
|
||||
var departurePlatformP = document.createElement('p')
|
||||
trainDiv.append(departurePlatformP)
|
||||
departurePlatformP.classList.add('sec', 'departure', 'platform')
|
||||
if (train.departurePlatform && train.departurePlatform != train.plannedDeparturePlatform) {
|
||||
departurePlatformP.classList.add('changed')
|
||||
}
|
||||
departurePlatformP.textContent = `${train.departurePlatform || train.plannedDeparturePlatform}`
|
||||
}
|
||||
}
|
||||
|
||||
var trainP = document.createElement('p')
|
||||
trainDiv.appendChild(trainP)
|
||||
trainP.classList.add('pri', 'train')
|
||||
if (!train.walking) {
|
||||
var trainLink = document.createElement('a')
|
||||
trainP.appendChild(trainLink)
|
||||
trainIdSpan(train.trainRank, train.trainNumber, trainLink)
|
||||
trainLink.innerText = train.line.name
|
||||
trainLink.classList.add('no-custom-a', 'items')
|
||||
if (train.line.product) {
|
||||
if (train.line.productName === 'STB' && train.line.name.startsWith('STB U')) {
|
||||
train.line.product = 'subway'
|
||||
}
|
||||
trainLink.classList.add('product-' + train.line.product)
|
||||
}
|
||||
var trainUrl = new URL('/view-train.html', window.location.origin)
|
||||
trainUrl.searchParams.set('train', train.trainNumber)
|
||||
trainUrl.searchParams.set('tripId', train.tripId)
|
||||
trainUrl.searchParams.set('startId', train.origin.id)
|
||||
trainUrl.searchParams.set('stopId', train.destination.id)
|
||||
trainLink.href = trainUrl.toString()
|
||||
trainP.appendChild(document.createTextNode(' '))
|
||||
if (train.line.operator) {
|
||||
var trainCompany = document.createElement('span')
|
||||
trainP.appendChild(trainCompany)
|
||||
trainCompany.textContent = '(' + train.operator + ')'
|
||||
trainCompany.textContent = '(' + train.line.operator.name + ')'
|
||||
trainCompany.classList.add('company')
|
||||
}
|
||||
}
|
||||
else {
|
||||
var walkingSpan = document.createElement('span')
|
||||
trainP.append(walkingSpan)
|
||||
walkingSpan.classList.add('walking')
|
||||
walkingSpan.innerText = `Walking (${train.distance} m)`
|
||||
}
|
||||
|
||||
var arrivalTimeP = document.createElement('p')
|
||||
trainDiv.appendChild(arrivalTimeP)
|
||||
arrivalTimeP.classList.add('sec', 'arrival', 'time')
|
||||
var arrivalTimePre = document.createElement('pre')
|
||||
arrivalTimeP.appendChild(arrivalTimePre)
|
||||
var arrival = new Date(train.arrivalDate)
|
||||
var arrival = new Date(train.plannedArrival)
|
||||
arrivalTimePre.textContent = arrival.toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' })
|
||||
|
||||
var arrivalHeading = document.createElement('h3')
|
||||
trainDiv.appendChild(arrivalHeading)
|
||||
arrivalHeading.classList.add('arrival', 'station')
|
||||
if (train.destination.type === 'stop' || train.destination.type === 'station') {
|
||||
var arrivalLink = document.createElement('a')
|
||||
arrivalHeading.appendChild(arrivalLink)
|
||||
arrivalLink.textContent = train.to
|
||||
arrivalLink.textContent = train.destination.name
|
||||
arrivalLink.classList.add('no-custom-a', 'items')
|
||||
var arrivalUrl = new URL('/view-station.html', window.location.origin)
|
||||
arrivalUrl.searchParams.set('station', train.from)
|
||||
arrivalUrl.searchParams.set('stationId', train.destination.id)
|
||||
arrivalLink.href = arrivalUrl.toString()
|
||||
}
|
||||
else {
|
||||
var arrivalSpan = document.createElement('span')
|
||||
arrivalHeading.append(arrivalSpan)
|
||||
arrivalSpan.innerText = train.destination.name || train.destination.address
|
||||
}
|
||||
|
||||
if (train.arrivalPlatform || train.plannedArrivalPlatform) {
|
||||
var arrivalPlatformP = document.createElement('p')
|
||||
trainDiv.append(arrivalPlatformP)
|
||||
arrivalPlatformP.classList.add('sec', 'arrival', 'platform')
|
||||
if (train.arrivalPlatform && train.arrivalPlatform != train.plannedArrivalPlatform) {
|
||||
arrivalPlatformP.classList.add('changed')
|
||||
}
|
||||
arrivalPlatformP.textContent = `${train.arrivalPlatform || train.plannedArrivalPlatform}`
|
||||
}
|
||||
|
||||
if (!last) {
|
||||
var nextTrain = data.journeys[i].legs[idx + 1]
|
||||
var nextDepartureTimeP = document.createElement('p')
|
||||
trainDiv.appendChild(nextDepartureTimeP)
|
||||
nextDepartureTimeP.classList.add('sec', 'next-departure', 'time')
|
||||
var departureTimePre = document.createElement('pre')
|
||||
nextDepartureTimeP.appendChild(departureTimePre)
|
||||
var departure = new Date(data[i].trains[idx + 1].departureDate)
|
||||
var departure = new Date(nextTrain.plannedDeparture)
|
||||
departureTimePre.textContent = departure.toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' })
|
||||
|
||||
if (nextTrain.departurePlatform || nextTrain.plannedDeparturePlatform) {
|
||||
var departurePlatformP = document.createElement('p')
|
||||
trainDiv.append(departurePlatformP)
|
||||
departurePlatformP.classList.add('sec', 'next-departure', 'platform')
|
||||
if (nextTrain.departurePlatform && nextTrain.departurePlatform != nextTrain.plannedDeparturePlatform) {
|
||||
departurePlatformP.classList.add('changed')
|
||||
}
|
||||
departurePlatformP.textContent = `${nextTrain.departurePlatform || nextTrain.plannedDeparturePlatform}`
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -299,7 +343,9 @@ function csk() {
|
|||
window.addEventListener('load', function (e) {
|
||||
var sp = new URL(window.location.href).searchParams
|
||||
fromStation = sp.get('from')
|
||||
var fromJson = JSON.parse(fromStation || 'null')
|
||||
toStation = sp.get('to')
|
||||
var toJson = JSON.parse(toStation || 'null')
|
||||
var departureDateStr = sp.get('departureDate')
|
||||
if (departureDateStr) {
|
||||
departureDate = new Date(departureDateStr)
|
||||
|
@ -316,30 +362,46 @@ window.addEventListener('load', function (e) {
|
|||
titleH1.textContent = 'Find Route - Departure Date'
|
||||
}
|
||||
else {
|
||||
titleH1.textContent = `${fromStation} - ${toStation}`
|
||||
titleH1.textContent = `${fromJson.name || fromJson.address} - ${toJson.name || toJson.address}`
|
||||
}
|
||||
|
||||
var transitKindJson = JSON.parse(sp.get('transitKind'))
|
||||
if (transitKindJson) {
|
||||
transitKind = transitKindJson
|
||||
}
|
||||
|
||||
var footer = document.querySelector('footer')
|
||||
|
||||
if (!fromStation || !toStation) {
|
||||
// Build station selection UI
|
||||
var stationNameH4 = document.createElement('h4')
|
||||
document.body.insertBefore(stationNameH4, footer)
|
||||
var stationNameLabel = document.createElement('label')
|
||||
stationNameH4.appendChild(stationNameLabel)
|
||||
stationNameLabel.htmlFor = 'stationName'
|
||||
stationNameLabel.textContent = 'Station Name'
|
||||
document.body.insertBefore(
|
||||
h4(
|
||||
label('Station Name').att$('for', 'stationName'),
|
||||
),
|
||||
footer,
|
||||
)
|
||||
// var stationNameH4 = document.createElement('h4')
|
||||
// document.body.insertBefore(stationNameH4, footer)
|
||||
// var stationNameLabel = document.createElement('label')
|
||||
// stationNameH4.appendChild(stationNameLabel)
|
||||
// stationNameLabel.htmlFor = 'stationName'
|
||||
// stationNameLabel.textContent = 'Station Name'
|
||||
|
||||
var stationNameInput = document.createElement('input')
|
||||
document.body.insertBefore(stationNameInput, footer)
|
||||
stationNameInput.type = 'search'
|
||||
stationNameInput.classList.add('items')
|
||||
stationNameInput.name = 'stationName'
|
||||
stationNameInput.id = 'stationName'
|
||||
document.body.insertBefore(
|
||||
input('search').id$('stationName').att$('name', 'stationName').class$('items'),
|
||||
footer,
|
||||
)
|
||||
// var stationNameInput = document.createElement('input')
|
||||
// document.body.insertBefore(stationNameInput, footer)
|
||||
// stationNameInput.type = 'search'
|
||||
// stationNameInput.classList.add('items')
|
||||
// stationNameInput.name = 'stationName'
|
||||
// stationNameInput.id = 'stationName'
|
||||
|
||||
var suggestionsH4 = document.createElement('h4')
|
||||
document.body.insertBefore(suggestionsH4, footer)
|
||||
suggestionsH4.textContent = 'Suggestions'
|
||||
document.body.insertBefore(h4('Suggestions'), footer)
|
||||
// var suggestionsH4 = document.createElement('h4')
|
||||
// document.body.insertBefore(suggestionsH4, footer)
|
||||
// suggestionsH4.textContent = 'Suggestions'
|
||||
|
||||
var contentDiv = document.createElement('div')
|
||||
document.body.insertBefore(contentDiv, footer)
|
||||
|
@ -352,7 +414,7 @@ window.addEventListener('load', function (e) {
|
|||
|
||||
var stationName = document.getElementById('stationName')
|
||||
stationName.addEventListener('input', function (e) {
|
||||
rebuildSuggestions()
|
||||
reloadSuggestions()
|
||||
})
|
||||
stationName.addEventListener('focus', function (e) {
|
||||
focusedElement = stationName
|
||||
|
@ -368,19 +430,6 @@ window.addEventListener('load', function (e) {
|
|||
goToStation(stationName.value.trim())
|
||||
}
|
||||
})
|
||||
|
||||
fetch('https://scraper.infotren.dcdev.ro/v3/stations')
|
||||
.then(function (response) {
|
||||
return response.json()
|
||||
})
|
||||
.then(function (response) {
|
||||
knownStations = response
|
||||
knownStations = knownStations.filter((s) => ![fromStation, toStation].includes(s.name))
|
||||
knownStations.sort(function(a, b) { return b.stoppedAtBy.length - a.stoppedAtBy.length })
|
||||
})
|
||||
.then(function () {
|
||||
rebuildSuggestions()
|
||||
})
|
||||
}
|
||||
else if (!departureDate) {
|
||||
var departureDateH4 = document.createElement('h4')
|
||||
|
@ -394,7 +443,7 @@ window.addEventListener('load', function (e) {
|
|||
contentDiv.appendChild(departureDateUl)
|
||||
departureDateUl.id = 'suggestionsArea'
|
||||
|
||||
for (var i = 0, departureOption = new Date(); i < 30; i++, departureOption.setDate(departureOption.getDate() + 1)) {
|
||||
for (var i = -1, departureOption = (function () { var d = new Date(); d.setDate(d.getDate() - 1); return d })(); i < 30; i++, departureOption.setDate(departureOption.getDate() + 1)) {
|
||||
var suggestionLi = document.createElement('li')
|
||||
departureDateUl.appendChild(suggestionLi)
|
||||
suggestionLi.classList.add('items')
|
||||
|
@ -414,6 +463,9 @@ window.addEventListener('load', function (e) {
|
|||
suggestionLi.addEventListener('focus', function (e) {
|
||||
focusedElement = suggestionLi
|
||||
})
|
||||
if (i === 0) {
|
||||
suggestionLi.focus()
|
||||
}
|
||||
})()
|
||||
var innerP = document.createElement('p')
|
||||
suggestionLi.appendChild(innerP)
|
||||
|
@ -421,6 +473,9 @@ window.addEventListener('load', function (e) {
|
|||
var innerPre = document.createElement('pre')
|
||||
innerP.appendChild(innerPre)
|
||||
innerPre.textContent = `${departureOption.getDate().toString().padStart(2, '0')}.${(departureOption.getMonth() + 1).toString().padStart(2, '0')}.${departureOption.getFullYear().toString().padStart(4, '0')}`
|
||||
if (i === 0) {
|
||||
innerPre.textContent += ' (today)'
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelector('.csk').textContent = 'Select'
|
||||
|
@ -439,11 +494,51 @@ window.addEventListener('load', function (e) {
|
|||
loadingP.classList.add('pri')
|
||||
loadingP.textContent = 'Loading data...'
|
||||
|
||||
var url = new URL('https://v6.db.transport.rest/journeys')
|
||||
if (fromJson.type === 'stop' || fromJson.type === 'station') {
|
||||
url.searchParams.set('from', fromJson.id)
|
||||
}
|
||||
else {
|
||||
if (fromJson.type === 'location') {
|
||||
delete fromJson.id
|
||||
}
|
||||
Object.keys(fromJson).forEach(function (key) {
|
||||
url.searchParams.set(`from.${key}`, fromJson[key])
|
||||
})
|
||||
}
|
||||
if (toJson.type === 'stop' || toJson.type === 'station') {
|
||||
url.searchParams.set('to', toJson.id)
|
||||
}
|
||||
else {
|
||||
if (toJson.type === 'location') {
|
||||
delete toJson.id
|
||||
}
|
||||
Object.keys(toJson).forEach(function (key) {
|
||||
url.searchParams.set(`to.${key}`, toJson[key])
|
||||
})
|
||||
}
|
||||
url.searchParams.set('departure', departureDate.toISOString())
|
||||
url.searchParams.set('results', '20')
|
||||
url.searchParams.set('stopovers', 'true')
|
||||
url.searchParams.set('nationalExpress', transitKind.ice)
|
||||
url.searchParams.set('national', transitKind.ic)
|
||||
url.searchParams.set('regionalExpress', transitKind.re)
|
||||
url.searchParams.set('regional', transitKind.rb)
|
||||
url.searchParams.set('suburban', transitKind.s)
|
||||
url.searchParams.set('bus', transitKind.bus)
|
||||
url.searchParams.set('ferry', transitKind.ferry)
|
||||
url.searchParams.set('subway', transitKind.u)
|
||||
url.searchParams.set('tram', transitKind.tram)
|
||||
|
||||
if (window.localStorage) {
|
||||
this.localStorage.setItem('recent/route', JSON.stringify({
|
||||
$addDate: new Date().toISOString(),
|
||||
from: fromJson.name || fromJson.address,
|
||||
to: toJson.name || toJson.address,
|
||||
queryParams: new URL(window.location.href).search,
|
||||
}))
|
||||
}
|
||||
|
||||
var url = new URL('https://scraper.infotren.dcdev.ro/v3/itineraries')
|
||||
url.searchParams.set('from', fromStation)
|
||||
url.searchParams.set('to', toStation)
|
||||
url.searchParams.set('date', departureDate.toISOString())
|
||||
fetch(url.toString())
|
||||
.then(function (response) {
|
||||
return response.json()
|
||||
|
@ -451,6 +546,34 @@ window.addEventListener('load', function (e) {
|
|||
.then(function (data) {
|
||||
contentDiv.remove()
|
||||
onItineraries(data)
|
||||
itineraries = data
|
||||
function fetchMore() {
|
||||
console.debug(`Got ${itineraries.journeys.length} journeys, fetching more`)
|
||||
var moreUrl = new URL(url.toString())
|
||||
moreUrl.searchParams.delete('departure')
|
||||
moreUrl.searchParams.set('laterThan', itineraries.laterRef)
|
||||
fetch(moreUrl.toString())
|
||||
.then(function (result) {
|
||||
return result.json()
|
||||
})
|
||||
.then(function (data) {
|
||||
if (data.journeys) {
|
||||
itineraries.journeys = itineraries.journeys.concat(data.journeys)
|
||||
itineraries.laterRef = data.laterRef
|
||||
onItineraries(itineraries)
|
||||
|
||||
if (itineraries.laterRef) {
|
||||
var lastJourney = itineraries.journeys[itineraries.journeys.length - 1]
|
||||
var lastLeg = lastJourney.legs[lastJourney.legs.length - 1]
|
||||
var departureDate = new Date(lastLeg.plannedDeparture)
|
||||
if (departureDate.getTime() - Date.now() < 86400000) {
|
||||
setTimeout(fetchMore, 500)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
fetchMore()
|
||||
})
|
||||
.catch(function (e) {
|
||||
loadingP.textContent = 'An error has occured'
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<title>Showcase</title>
|
||||
|
||||
<link rel="stylesheet" href="/base.css">
|
||||
<link rel="stylesheet" href="/base.dark.css">
|
||||
|
||||
<script src="/common/worker.js"></script>
|
||||
<script defer src="/common/back.js"></script>
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Station - InfoTren</title>
|
||||
<title>Station - InfoDTrain</title>
|
||||
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
|
||||
<link rel="stylesheet" href="/base.css">
|
||||
<link rel="stylesheet" href="/base.dark.css">
|
||||
|
||||
<script src="/common/worker.js"></script>
|
||||
<script defer src="/common/back.js"></script>
|
||||
|
|
106
station.js
106
station.js
|
@ -1,9 +1,9 @@
|
|||
var knownStations = []
|
||||
|
||||
function goToStation(station) {
|
||||
function goToStation(stationId) {
|
||||
var url = new URL(window.location.href)
|
||||
url.pathname = 'view-station.html'
|
||||
url.searchParams.set('station', station)
|
||||
url.searchParams.set('stationId', stationId)
|
||||
url.searchParams.set('date', new Date().toISOString())
|
||||
window.location.href = url.toString()
|
||||
}
|
||||
|
@ -36,41 +36,9 @@ function rebuildSuggestions() {
|
|||
suggestionsArea.childNodes[0].remove()
|
||||
}
|
||||
|
||||
var stationNameInput = document.getElementById('stationName')
|
||||
var stationName = searchNormalize(stationNameInput.value.trim())
|
||||
var suggestions = knownStations.slice()
|
||||
|
||||
var suggestions = []
|
||||
if (!stationName) {
|
||||
suggestions = knownStations.slice()
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < knownStations.length; i++) {
|
||||
if (!searchNormalize(knownStations[i].name).includes(stationName)) {
|
||||
continue
|
||||
}
|
||||
suggestions.push(knownStations[i])
|
||||
}
|
||||
suggestions.sort((s1, s2) => {
|
||||
var s1n = searchNormalize(s1.name);
|
||||
var s2n = searchNormalize(s2.name);
|
||||
|
||||
if (s1n.indexOf(stationName) != s2n.indexOf(stationName)) {
|
||||
return s1n.indexOf(stationName) - s2n.indexOf(stationName);
|
||||
}
|
||||
|
||||
if (s1.stoppedAtBy.length != s2.stoppedAtBy.length) {
|
||||
return s2.stoppedAtBy.length - s1.stoppedAtBy.length;
|
||||
}
|
||||
|
||||
return s1.name.localeCompare(s2.name);
|
||||
})
|
||||
}
|
||||
|
||||
var foundInput = false
|
||||
suggestions.forEach(function (suggestion, index) {
|
||||
if (stationName == searchNormalize(suggestion.name)) {
|
||||
foundInput = true
|
||||
}
|
||||
var suggestionLi = document.createElement('li')
|
||||
suggestionsArea.appendChild(suggestionLi)
|
||||
|
||||
|
@ -80,7 +48,7 @@ function rebuildSuggestions() {
|
|||
suggestionLi.style.padding = '2px 0'
|
||||
|
||||
function onAction(e) {
|
||||
goToStation(suggestion.name)
|
||||
goToStation(suggestion.id)
|
||||
}
|
||||
suggestionLi.addEventListener('click', onAction)
|
||||
suggestionLi.addEventListener('keypress', function (e) {
|
||||
|
@ -105,33 +73,6 @@ function rebuildSuggestions() {
|
|||
// trainCompanyP.classList.add('thi')
|
||||
}, 0)
|
||||
})
|
||||
if (!foundInput && stationName) {
|
||||
var suggestionLi = document.createElement('li')
|
||||
suggestionsArea.appendChild(suggestionLi)
|
||||
|
||||
suggestionLi.classList.add('items')
|
||||
suggestionLi.tabIndex = suggestions.length + 2
|
||||
suggestionLi.style.padding = '2px 0'
|
||||
|
||||
function onAction(e) {
|
||||
goToStation(stationNameInput.value.trim())
|
||||
}
|
||||
suggestionLi.addEventListener('click', onAction)
|
||||
suggestionLi.addEventListener('keypress', function (e) {
|
||||
if (e.key == 'Enter') {
|
||||
onAction(e)
|
||||
}
|
||||
})
|
||||
suggestionLi.addEventListener('focus', function (e) {
|
||||
focusedElement = suggestionLi
|
||||
})
|
||||
|
||||
var stationNameP = document.createElement('p')
|
||||
suggestionLi.appendChild(stationNameP)
|
||||
|
||||
stationNameP.textContent = stationNameInput.value.trim()
|
||||
stationNameP.classList.add('pri', 'stationName')
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
_rebuildDebounce = null
|
||||
|
@ -141,6 +82,31 @@ function rebuildSuggestions() {
|
|||
}, 500)
|
||||
}
|
||||
|
||||
var fetchAbortController = new AbortController()
|
||||
function reloadSuggestions() {
|
||||
var stationNameInput = document.getElementById('stationName')
|
||||
var stationName = searchNormalize(stationNameInput.value.trim())
|
||||
|
||||
var locationsUrl = new URL('https://v6.db.transport.rest/locations')
|
||||
locationsUrl.searchParams.set('query', stationName)
|
||||
locationsUrl.searchParams.set('limit', '25')
|
||||
locationsUrl.searchParams.set('fuzzy', 'true')
|
||||
locationsUrl.searchParams.set('stops', 'true')
|
||||
|
||||
fetchAbortController.abort()
|
||||
fetchAbortController = new AbortController()
|
||||
fetch(locationsUrl.toString(), { signal: fetchAbortController.signal })
|
||||
.then(function (response) {
|
||||
return response.json()
|
||||
})
|
||||
.then(function (data) {
|
||||
if (data) {
|
||||
knownStations = Object.values(data)
|
||||
rebuildSuggestions()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function lsk() {
|
||||
document.getElementById('stationName').focus()
|
||||
}
|
||||
|
@ -161,7 +127,7 @@ function csk() {
|
|||
window.addEventListener('load', function (e) {
|
||||
var stationName = document.getElementById('stationName')
|
||||
stationName.addEventListener('input', function (e) {
|
||||
rebuildSuggestions()
|
||||
reloadSuggestions()
|
||||
})
|
||||
stationName.addEventListener('focus', function (e) {
|
||||
focusedElement = stationName
|
||||
|
@ -197,15 +163,5 @@ window.addEventListener('load', function (e) {
|
|||
}
|
||||
})
|
||||
|
||||
fetch('https://scraper.infotren.dcdev.ro/v3/stations')
|
||||
.then(function (response) {
|
||||
return response.json()
|
||||
})
|
||||
.then(function (response) {
|
||||
knownStations = response
|
||||
knownStations.sort(function(a, b) { return b.stoppedAtBy.length - a.stoppedAtBy.length })
|
||||
})
|
||||
.then(function () {
|
||||
rebuildSuggestions()
|
||||
})
|
||||
reloadSuggestions()
|
||||
})
|
||||
|
|
10
sw.js
10
sw.js
|
@ -1,4 +1,4 @@
|
|||
const VERSION = 'v34'
|
||||
const VERSION = 'v14'
|
||||
const API_ORIGIN = 'https://scraper.infotren.dcdev.ro/'
|
||||
const API_TRAINS = `${API_ORIGIN}v3/trains`
|
||||
const API_STATIONS = `${API_ORIGIN}v3/stations`
|
||||
|
@ -20,6 +20,7 @@ const CACHE_FIRST = [
|
|||
|
||||
// Base
|
||||
'/base.css',
|
||||
'/base.dark.css',
|
||||
|
||||
// Pages
|
||||
'/index.html',
|
||||
|
@ -34,6 +35,7 @@ const CACHE_FIRST = [
|
|||
'/view-train.html',
|
||||
'/view-train.js',
|
||||
'/view-train.css',
|
||||
'/view-train.dark.css',
|
||||
|
||||
'/station.html',
|
||||
'/station.js',
|
||||
|
@ -41,10 +43,16 @@ const CACHE_FIRST = [
|
|||
'/view-station.html',
|
||||
'/view-station.js',
|
||||
'/view-station.css',
|
||||
'/view-station.dark.css',
|
||||
|
||||
'/config-route.html',
|
||||
'/config-route.js',
|
||||
'/config-route.css',
|
||||
|
||||
'/route.html',
|
||||
'/route.js',
|
||||
'/route.css',
|
||||
'/route.dark.css',
|
||||
|
||||
// API
|
||||
API_TRAINS,
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Train - InfoTren</title>
|
||||
<title>Train - InfoDTrain</title>
|
||||
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
|
||||
<link rel="stylesheet" href="/base.css">
|
||||
<link rel="stylesheet" href="/base.dark.css">
|
||||
|
||||
<script src="/common/worker.js"></script>
|
||||
<script defer src="/common/back.js"></script>
|
||||
|
|
121
train.js
121
train.js
|
@ -1,9 +1,9 @@
|
|||
var knownTrains = []
|
||||
|
||||
function goToTrain(number) {
|
||||
function goToTrip(tripId) {
|
||||
var url = new URL(window.location.href)
|
||||
url.pathname = 'view-train.html'
|
||||
url.searchParams.set('train', number)
|
||||
url.searchParams.set('tripId', tripId)
|
||||
url.searchParams.set('date', new Date().toISOString())
|
||||
window.location.href = url.toString()
|
||||
}
|
||||
|
@ -26,43 +26,16 @@ function rebuildSuggestions() {
|
|||
suggestionsArea.childNodes[0].remove()
|
||||
}
|
||||
|
||||
var trainNumberInput = document.getElementById('trainNumber')
|
||||
var trainNumber = trainNumberInput.value.trim()
|
||||
|
||||
var suggestions = []
|
||||
if (!trainNumber) {
|
||||
suggestions = knownTrains.slice()
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < knownTrains.length; i++) {
|
||||
if (!knownTrains[i].number.includes(trainNumber)) {
|
||||
continue
|
||||
}
|
||||
suggestions.push(knownTrains[i])
|
||||
}
|
||||
suggestions.sort((s1, s2) => {
|
||||
if (s1.number.indexOf(trainNumber) != s2.number.indexOf(trainNumber)) {
|
||||
return s1.number.indexOf(trainNumber) - s2.number.indexOf(trainNumber);
|
||||
}
|
||||
|
||||
if (s1.number.length != s2.number.length) {
|
||||
return s1.number.length - s2.number.length;
|
||||
}
|
||||
|
||||
return s1.number.localeCompare(s2.number);
|
||||
var suggestions = knownTrains.filter(function (suggestion) {
|
||||
return suggestion.line.name
|
||||
})
|
||||
}
|
||||
|
||||
// Trim the amount of results displayed
|
||||
if (suggestions.length > 100) {
|
||||
suggestions.splice(100)
|
||||
}
|
||||
|
||||
var foundInput = false
|
||||
suggestions.forEach(function (suggestion, index) {
|
||||
if (trainNumber == suggestion.number) {
|
||||
foundInput = true
|
||||
}
|
||||
var suggestionLi = document.createElement('li')
|
||||
suggestionsArea.appendChild(suggestionLi)
|
||||
|
||||
|
@ -72,7 +45,7 @@ function rebuildSuggestions() {
|
|||
suggestionLi.style.padding = '2px 0'
|
||||
|
||||
function onAction(e) {
|
||||
goToTrain(suggestion.number)
|
||||
goToTrip(suggestion.id)
|
||||
}
|
||||
suggestionLi.addEventListener('click', onAction)
|
||||
suggestionLi.addEventListener('keypress', function (e) {
|
||||
|
@ -86,44 +59,22 @@ function rebuildSuggestions() {
|
|||
|
||||
var trainNameP = document.createElement('p')
|
||||
suggestionLi.appendChild(trainNameP)
|
||||
|
||||
trainIdSpan(suggestion.rank, suggestion.number, trainNameP)
|
||||
trainNameP.textContent = `${suggestion.line.name} (${suggestion.line.fahrtNr})`
|
||||
trainNameP.classList.add('pri', 'trainName')
|
||||
|
||||
var trainRouteP = document.createElement('p')
|
||||
suggestionLi.appendChild(trainRouteP)
|
||||
|
||||
trainRouteP.textContent = `${suggestion.origin.name} → ${suggestion.destination.name}`
|
||||
trainRouteP.classList.add('thi')
|
||||
|
||||
var trainCompanyP = document.createElement('p')
|
||||
suggestionLi.appendChild(trainCompanyP)
|
||||
|
||||
trainCompanyP.textContent = suggestion.company
|
||||
trainCompanyP.textContent = suggestion.line.operator.name
|
||||
trainCompanyP.classList.add('thi')
|
||||
}, 0)
|
||||
})
|
||||
if (!foundInput && trainNumber) {
|
||||
var suggestionLi = document.createElement('li')
|
||||
suggestionsArea.appendChild(suggestionLi)
|
||||
|
||||
suggestionLi.classList.add('items')
|
||||
suggestionLi.tabIndex = suggestions.length + 2
|
||||
suggestionLi.style.padding = '2px 0'
|
||||
|
||||
function onAction(e) {
|
||||
goToTrain(trainNumber)
|
||||
}
|
||||
suggestionLi.addEventListener('click', onAction)
|
||||
suggestionLi.addEventListener('keypress', function (e) {
|
||||
if (e.key == 'Enter') {
|
||||
onAction(e)
|
||||
}
|
||||
})
|
||||
suggestionLi.addEventListener('focus', function (e) {
|
||||
focusedElement = suggestionLi
|
||||
})
|
||||
|
||||
var trainNameP = document.createElement('p')
|
||||
suggestionLi.appendChild(trainNameP)
|
||||
|
||||
trainNameP.textContent = `Train ${trainNumber}`
|
||||
trainNameP.classList.add('pri', 'trainName')
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
_rebuildDebounce = null
|
||||
|
@ -133,6 +84,34 @@ function rebuildSuggestions() {
|
|||
}, 500)
|
||||
}
|
||||
|
||||
var fetchAbortController = new AbortController()
|
||||
function reloadSuggestions() {
|
||||
var trainNumberInput = document.getElementById('trainNumber')
|
||||
var trainNumber = trainNumberInput.value.trim()
|
||||
|
||||
var tripsUrl = new URL('https://v6.db.transport.rest/trips')
|
||||
tripsUrl.searchParams.set('query', trainNumber)
|
||||
tripsUrl.searchParams.set('onlyCurrentlyRunning', 'true')
|
||||
|
||||
fetchAbortController.abort()
|
||||
fetchAbortController = new AbortController()
|
||||
fetch(tripsUrl.toString(), { signal: fetchAbortController.signal })
|
||||
.then(function (response) {
|
||||
return response.json()
|
||||
})
|
||||
.then(function (data) {
|
||||
if (data.trips) {
|
||||
knownTrains = data.trips
|
||||
knownTrains.sort(function (s1, s2) {
|
||||
var diff1 = Math.abs(Date.now() - new Date(s1.plannedDeparture).getTime())
|
||||
var diff2 = Math.abs(Date.now() - new Date(s2.plannedDeparture).getTime())
|
||||
return diff1 - diff2
|
||||
})
|
||||
rebuildSuggestions()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function lsk() {
|
||||
document.getElementById('trainNumber').focus()
|
||||
}
|
||||
|
@ -143,7 +122,7 @@ function csk() {
|
|||
}
|
||||
|
||||
if (focusedElement.id === 'trainNumber') {
|
||||
goToTrain(document.activeElement.value.trim())
|
||||
goToTrip(document.activeElement.value.trim())
|
||||
}
|
||||
else {
|
||||
focusedElement.click()
|
||||
|
@ -153,7 +132,7 @@ function csk() {
|
|||
window.addEventListener('load', function (e) {
|
||||
var trainNumber = document.getElementById('trainNumber')
|
||||
trainNumber.addEventListener('input', function (e) {
|
||||
rebuildSuggestions()
|
||||
reloadSuggestions()
|
||||
})
|
||||
trainNumber.addEventListener('focus', function (e) {
|
||||
focusedElement = trainNumber
|
||||
|
@ -166,7 +145,7 @@ window.addEventListener('load', function (e) {
|
|||
})
|
||||
trainNumber.addEventListener('keypress', function (e) {
|
||||
if (e.key == 'Enter') {
|
||||
goToTrain(trainNumber.value.trim())
|
||||
goToTrip(trainNumber.value.trim())
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -189,15 +168,5 @@ window.addEventListener('load', function (e) {
|
|||
}
|
||||
})
|
||||
|
||||
fetch('https://scraper.infotren.dcdev.ro/v2/trains')
|
||||
.then(function (response) {
|
||||
return response.json()
|
||||
})
|
||||
.then(function (response) {
|
||||
knownTrains = response
|
||||
knownTrains.sort(function(a, b) { return a.number - b.number })
|
||||
})
|
||||
.then(function () {
|
||||
rebuildSuggestions()
|
||||
})
|
||||
reloadSuggestions()
|
||||
})
|
||||
|
|
|
@ -53,12 +53,12 @@
|
|||
|
||||
.train-item {
|
||||
display: grid;
|
||||
grid-template-columns: 30px 60px auto 1fr auto;
|
||||
grid-template-columns: 100px auto 1fr auto;
|
||||
grid-template-rows: auto;
|
||||
grid-template-areas:
|
||||
"rank train time terminus platform"
|
||||
"rank train delay terminus platform"
|
||||
"status status status status status";
|
||||
"train time terminus platform"
|
||||
"train delay terminus platform"
|
||||
"status status status status";
|
||||
align-items: center;
|
||||
padding: 4px 0;
|
||||
|
||||
|
@ -106,10 +106,19 @@
|
|||
padding: 1px;
|
||||
margin: 1px;
|
||||
border-radius: 5px;
|
||||
aspect-ratio: 1 / 1;
|
||||
/* aspect-ratio: 1 / 1; */
|
||||
min-width: 22px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.train-item .platform.changed {
|
||||
color: red;
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.status-cancel {
|
||||
text-align: start;
|
||||
}
|
||||
|
|
43
view-station.dark.css
Normal file
43
view-station.dark.css
Normal file
|
@ -0,0 +1,43 @@
|
|||
@media(prefers-color-scheme: dark) {
|
||||
|
||||
.early {
|
||||
color: lightgreen;
|
||||
}
|
||||
|
||||
.late {
|
||||
color: #ff3333;
|
||||
}
|
||||
|
||||
#tabs-arr {
|
||||
border-bottom-color: #bbffbb;
|
||||
}
|
||||
|
||||
#tabs-dep {
|
||||
border-bottom-color: #bbbbff;
|
||||
}
|
||||
|
||||
#arrivals .train-item {
|
||||
background-color: #001a00;
|
||||
}
|
||||
|
||||
#arrivals .train-item:nth-of-type(even) {
|
||||
background-color: #004400;
|
||||
}
|
||||
|
||||
#departures .train-item {
|
||||
background-color: #00002a;
|
||||
}
|
||||
|
||||
#departures .train-item:nth-of-type(even) {
|
||||
background-color: #000066;
|
||||
}
|
||||
|
||||
.train-item.cancelled {
|
||||
background-color: #550000 !important;
|
||||
}
|
||||
|
||||
.train-item .platform {
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
}
|
|
@ -4,12 +4,14 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>View Station - InfoTren</title>
|
||||
<title>View Station - InfoDTrain</title>
|
||||
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
|
||||
<link rel="stylesheet" href="/base.css">
|
||||
<link rel="stylesheet" href="/base.dark.css">
|
||||
<link rel="stylesheet" href="view-station.css">
|
||||
<link rel="stylesheet" href="view-station.dark.css">
|
||||
|
||||
<script src="/common/worker.js"></script>
|
||||
<script defer src="/common/back.js"></script>
|
||||
|
|
127
view-station.js
127
view-station.js
|
@ -1,24 +1,54 @@
|
|||
var station
|
||||
var stationId
|
||||
var date
|
||||
|
||||
var stationData = null
|
||||
var stationData = {
|
||||
departures: [],
|
||||
arrivals: [],
|
||||
}
|
||||
var lastSuccessfulFetch = null
|
||||
|
||||
/**
|
||||
* @typedef StationArrDep
|
||||
* @property {string} tripId
|
||||
* @property {DbStop} stop
|
||||
* @property {string | null} when
|
||||
* @property {string} plannedWhen
|
||||
* @property {number | null} delay
|
||||
* @property {string | null} platform
|
||||
* @property {string | null} plannedPlatform
|
||||
* @property {string | null} prognosisType
|
||||
* @property {string | null} direction
|
||||
* @property {string | null} provenance
|
||||
* @property {DbLine} line
|
||||
* @property {DbRemark[]} remarks
|
||||
* @property {DbStop | null} origin
|
||||
* @property {DbStop | null} destionation
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef StationData
|
||||
* @prop {StationArrDep[]} departures
|
||||
* @prop {StationArrDep[]} arrivals
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {?StationData} data
|
||||
*/
|
||||
function onStationData(data) {
|
||||
if (!data) {
|
||||
if (!data || !data.arrivals && !data?.departures) {
|
||||
return
|
||||
}
|
||||
|
||||
var title = document.getElementById('title')
|
||||
title.textContent = data.stationName
|
||||
title.textContent = (data.departures[0] || data.arrivals[0]).stop.name
|
||||
|
||||
document.getElementById('date').textContent = data.date
|
||||
// document.getElementById('date').textContent = data.date
|
||||
|
||||
document.getElementById('loading').classList.add('hidden')
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} elem
|
||||
* @param {any[]} trains
|
||||
* @param {StationArrDep[]} trains
|
||||
*/
|
||||
function addTrains(elem, trains) {
|
||||
while (elem.childNodes.length > 0) {
|
||||
|
@ -32,26 +62,26 @@ function onStationData(data) {
|
|||
var trainItem = document.createElement('li')
|
||||
trainsList.appendChild(trainItem)
|
||||
trainItem.classList.add('train-item')
|
||||
if (train.status && train.status.cancelled) {
|
||||
trainItem.classList.add('cancelled')
|
||||
}
|
||||
// if (train.status && train.status.cancelled) {
|
||||
// trainItem.classList.add('cancelled')
|
||||
// }
|
||||
|
||||
var timeDiv = document.createElement('p')
|
||||
trainItem.appendChild(timeDiv)
|
||||
timeDiv.classList.add('pri', 'time')
|
||||
var timeDivPre = document.createElement('pre')
|
||||
timeDiv.appendChild(timeDivPre)
|
||||
timeDivPre.textContent = new Date(train.time).toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' })
|
||||
timeDivPre.textContent = new Date(train.plannedWhen).toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' })
|
||||
|
||||
if (train.status && train.status.delay != 0) {
|
||||
if (train.delay && train.delay != 0) {
|
||||
var delayDiv = document.createElement('p')
|
||||
trainItem.appendChild(delayDiv)
|
||||
delayDiv.classList.add('thi', 'delay')
|
||||
delayDiv.textContent = `${train.status.delay} min `
|
||||
delayDiv.textContent = `${train.delay / 60} min `
|
||||
// delayDiv.appendChild(document.createElement('br'))
|
||||
var descSpan = document.createElement('span')
|
||||
delayDiv.appendChild(descSpan)
|
||||
if (train.status.delay > 0) {
|
||||
if (train.delay > 0) {
|
||||
descSpan.classList.add('late')
|
||||
descSpan.textContent = 'late'
|
||||
}
|
||||
|
@ -61,12 +91,12 @@ function onStationData(data) {
|
|||
}
|
||||
}
|
||||
|
||||
var rankDiv = document.createElement('p')
|
||||
trainItem.appendChild(rankDiv)
|
||||
rankDiv.classList.add('sec', 'rank', train.train.rank)
|
||||
var rankDivPre = document.createElement('pre')
|
||||
rankDiv.appendChild(rankDivPre)
|
||||
rankDivPre.textContent = train.train.rank
|
||||
// var rankDiv = document.createElement('p')
|
||||
// trainItem.appendChild(rankDiv)
|
||||
// rankDiv.classList.add('sec', 'rank', train.train.rank)
|
||||
// var rankDivPre = document.createElement('pre')
|
||||
// rankDiv.appendChild(rankDivPre)
|
||||
// rankDivPre.textContent = train.train.rank
|
||||
|
||||
var trainDiv = document.createElement('p')
|
||||
trainItem.appendChild(trainDiv)
|
||||
|
@ -75,32 +105,35 @@ function onStationData(data) {
|
|||
trainDiv.appendChild(trainDivHref)
|
||||
trainDivHref.classList.add('no-a-custom')
|
||||
var trainUrl = new URL('/view-train.html', window.location.origin)
|
||||
trainUrl.searchParams.append('train', train.train.number)
|
||||
trainUrl.searchParams.append('date', train.train.departureDate)
|
||||
trainUrl.searchParams.append('tripId', train.tripId)
|
||||
trainDivHref.href = trainUrl.toString()
|
||||
var trainDivHrefPre = document.createElement('pre')
|
||||
trainDivHref.appendChild(trainDivHrefPre)
|
||||
trainDivHrefPre.textContent = train.train.number
|
||||
trainDivHrefPre.textContent = train.line.name
|
||||
|
||||
var terminusDiv = document.createElement('p')
|
||||
trainItem.appendChild(terminusDiv)
|
||||
terminusDiv.classList.add('pri', 'terminus')
|
||||
terminusDiv.textContent = train.train.terminus
|
||||
terminusDiv.textContent = train.direction
|
||||
|
||||
if (train.status && train.status.platform) {
|
||||
if (train.platform) {
|
||||
var platformDiv = document.createElement('div')
|
||||
trainItem.appendChild(platformDiv)
|
||||
platformDiv.classList.add('thi', 'platform')
|
||||
if (train.platform && train.platform !== train.plannedPlatform) {
|
||||
platformDiv.classList.add('changed')
|
||||
}
|
||||
var platformDivPre = document.createElement('pre')
|
||||
platformDiv.appendChild(platformDivPre)
|
||||
platformDivPre.textContent = train.status.platform
|
||||
platformDivPre.textContent = train.platform || train.plannedPlatform
|
||||
}
|
||||
|
||||
if (train.status && train.status.cancelled) {
|
||||
if (train.cancelled) {
|
||||
trainItem.classList.add('cancelled')
|
||||
var statusDiv = document.createElement('p')
|
||||
trainItem.appendChild(statusDiv)
|
||||
statusDiv.classList.add('sec', 'status')
|
||||
statusDiv.textContent = 'This train is cancelled'
|
||||
statusDiv.classList.add('sec', 'status', 'status-cancel')
|
||||
statusDiv.textContent = 'This journey is cancelled'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -119,34 +152,44 @@ function refresh() {
|
|||
}, timeout || 90000)
|
||||
}
|
||||
var reqDate = new Date(date.valueOf())
|
||||
reqDate.setMinutes(0, 0, 0)
|
||||
return fetch(
|
||||
`https://scraper.infotren.dcdev.ro/v3/stations/${station}?date=${reqDate.toISOString()}`,
|
||||
// reqDate.setHours(0, 0, 0, 0)
|
||||
reqDate.setMinutes(reqDate.getMinutes() - 15)
|
||||
return Promise.all([
|
||||
fetch(
|
||||
`https://v6.db.transport.rest/stops/${stationId}/arrivals?when=${reqDate.toISOString()}&duration=1440`,
|
||||
{
|
||||
cache: 'no-store',
|
||||
},
|
||||
).then(function (response) {
|
||||
if (!response.ok) {
|
||||
),
|
||||
fetch(
|
||||
`https://v6.db.transport.rest/stops/${stationId}/departures?when=${reqDate.toISOString()}&duration=1440`,
|
||||
{
|
||||
cache: 'no-store',
|
||||
},
|
||||
),
|
||||
]).then(function (responses) {
|
||||
if (!responses[0].ok || !responses[1].ok) {
|
||||
// Check in 10 seconds if server returned error
|
||||
reschedule(10000)
|
||||
return
|
||||
}
|
||||
var cacheDate = response.headers.get('SW-Cached-At')
|
||||
return response.json().then(function (data) {
|
||||
var cacheDate = responses[0].headers.get('SW-Cached-At')
|
||||
return Promise.all(responses.map(function (r) {return r.json() })).then(function (data) {
|
||||
data['$cacheDate'] = cacheDate
|
||||
return data
|
||||
})
|
||||
}).then(function (response) {
|
||||
if (!response) {
|
||||
}).then(function (responses) {
|
||||
if (!responses) {
|
||||
return
|
||||
}
|
||||
var cacheDate = response['$cacheDate']
|
||||
var cacheDate = responses['$cacheDate']
|
||||
if (cacheDate) {
|
||||
cacheDate = new Date(cacheDate)
|
||||
}
|
||||
var success = !cacheDate
|
||||
stationData = response
|
||||
onStationData(response)
|
||||
stationData.arrivals = responses[0].arrivals
|
||||
stationData.departures = responses[1].departures
|
||||
onStationData(stationData)
|
||||
// Check in 1 seconds if network error
|
||||
reschedule(success ? undefined : 1000)
|
||||
return success
|
||||
|
@ -185,7 +228,7 @@ function rsk() {
|
|||
}
|
||||
|
||||
window.addEventListener('load', function (e) {
|
||||
if (!new URL(window.location.href).searchParams.has('station')) {
|
||||
if (!new URL(window.location.href).searchParams.has('stationId')) {
|
||||
window.history.back()
|
||||
this.setTimeout(function () {
|
||||
var url = new URL(window.location.href)
|
||||
|
@ -196,7 +239,7 @@ window.addEventListener('load', function (e) {
|
|||
|
||||
var sp = new URL(window.location.href).searchParams
|
||||
|
||||
station = sp.get('station')
|
||||
stationId = sp.get('stationId')
|
||||
date = sp.has('date') ? new Date(sp.get('date')) : new Date()
|
||||
|
||||
// View departures first
|
||||
|
|
|
@ -45,11 +45,29 @@
|
|||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.stationItem.cancelled:nth-of-type(odd) {
|
||||
background-color: #fffafa;
|
||||
}
|
||||
|
||||
.stationItem.cancelled:nth-of-type(even) {
|
||||
background-color: #ffeaea;
|
||||
}
|
||||
|
||||
.stationItem .name {
|
||||
text-align: center;
|
||||
grid-area: name;
|
||||
}
|
||||
|
||||
.stationItem.not-in-journey .name, .stationItem.not-in-journey .arrival, .stationItem.not-in-journey .departure {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.stationItem.cancelled .name {
|
||||
text-decoration: line-through;
|
||||
text-decoration-color: red;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.stationItem .arrival {
|
||||
text-align: start;
|
||||
grid-area: arr;
|
||||
|
@ -90,6 +108,10 @@
|
|||
grid-area: platform;
|
||||
}
|
||||
|
||||
.stationItem .platform.changed {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.stationItem .notes {
|
||||
grid-area: notes;
|
||||
}
|
||||
|
@ -98,8 +120,36 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.remarkItem {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.remarkItem:nth-of-type(even) {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.last-refreshed {
|
||||
font-size: 12px;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.remark-status, .remark-status * {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
.remark-board, .remark-exit {
|
||||
color: blue !important;
|
||||
}
|
||||
|
||||
#actual-map {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#actual-map a {
|
||||
display: initial;
|
||||
padding: initial;
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
|
49
view-train.dark.css
Normal file
49
view-train.dark.css
Normal file
|
@ -0,0 +1,49 @@
|
|||
@media(prefers-color-scheme: dark) {
|
||||
|
||||
.early {
|
||||
color: lightgreen;
|
||||
}
|
||||
|
||||
.late {
|
||||
color: #ff3333;
|
||||
}
|
||||
|
||||
.station {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.stationItem:nth-of-type(even) {
|
||||
background-color: #0f0f0f;
|
||||
}
|
||||
|
||||
.stationItem .arrival .original, .stationItem .departure .original {
|
||||
color: #afafaf;
|
||||
}
|
||||
|
||||
.remark-status, .remark-status * {
|
||||
color: #ff3333 !important;
|
||||
}
|
||||
|
||||
.remark-board, .remark-exit {
|
||||
color: #8888ff !important;
|
||||
}
|
||||
|
||||
.stationItem.not-in-journey .name, .stationItem.not-in-journey .arrival, .stationItem.not-in-journey .departure {
|
||||
color: #aaaaaa;
|
||||
}
|
||||
|
||||
.stationItem.cancelled .name {
|
||||
text-decoration: line-through;
|
||||
text-decoration-color: #ff3333;
|
||||
color: #ff3333;
|
||||
}
|
||||
|
||||
.stationItem .platform.changed {
|
||||
color: #ff3333;
|
||||
}
|
||||
|
||||
.remarkItem:nth-of-type(even) {
|
||||
background-color: #202020;
|
||||
}
|
||||
|
||||
}
|
|
@ -4,13 +4,21 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>View Train - InfoTren</title>
|
||||
<title>View Train - InfoDTrain</title>
|
||||
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
|
||||
<link rel="stylesheet" href="/base.css">
|
||||
<link rel="stylesheet" href="/base.dark.css">
|
||||
<link rel="stylesheet" href="view-train.css">
|
||||
<link rel="stylesheet" href="view-train.dark.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
||||
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
|
||||
crossorigin=""/>
|
||||
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
||||
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
|
||||
crossorigin=""></script>
|
||||
<script src="/common/worker.js"></script>
|
||||
<script defer src="/common/back.js"></script>
|
||||
<script defer src="/common/items.js"></script>
|
||||
|
@ -56,6 +64,10 @@
|
|||
<div id="stations">
|
||||
<h4>Stations</h4>
|
||||
</div>
|
||||
<div id="map">
|
||||
<h4>Map</h4>
|
||||
<a id="load-map-button" class="items" href="">Load map</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
|
|
667
view-train.js
667
view-train.js
|
@ -2,11 +2,23 @@
|
|||
* @type {string}
|
||||
*/
|
||||
var trainNumber
|
||||
/**
|
||||
* @type {string | null}
|
||||
*/
|
||||
var tripId = null
|
||||
/**
|
||||
* @type {Date}
|
||||
*/
|
||||
var date
|
||||
var groupIndex = null
|
||||
/**
|
||||
* @type {string | null}
|
||||
*/
|
||||
var startId = null
|
||||
/**
|
||||
* @type {string | null}
|
||||
*/
|
||||
var stopId = null
|
||||
|
||||
var yesterday = false
|
||||
var showKm = false
|
||||
|
@ -17,71 +29,62 @@ var trainData = null
|
|||
*/
|
||||
var lastSuccessfulFetch = null
|
||||
|
||||
var loadMap = false
|
||||
/**
|
||||
* @typedef ArrDep
|
||||
* @property {string} scheduleTime
|
||||
* @property {?{delay: number, real: boolean}} status
|
||||
*
|
||||
* @typedef Note
|
||||
* @property {string} kind
|
||||
*
|
||||
* @typedef DepartsAsNote
|
||||
* @type {Note}
|
||||
* @property {"departsAs"} kind
|
||||
* @property {string} rank
|
||||
* @property {string} number
|
||||
*
|
||||
* @typedef TrainNumberChangeNote
|
||||
* @type {Note}
|
||||
* @property {"trainNumberChange"} kind
|
||||
* @property {string} rank
|
||||
* @property {string} number
|
||||
*
|
||||
* @typedef DetachingWagonsNote
|
||||
* @type {Note}
|
||||
* @property {"detachingWagons"} kind
|
||||
* @property {string} station
|
||||
*
|
||||
* @typedef ReceivingWagonsNote
|
||||
* @type {Note}
|
||||
* @property {"receivingWagons"} kind
|
||||
* @property {string} station
|
||||
*
|
||||
* @typedef TrainStop
|
||||
* @property {string} name
|
||||
* @property {number} km
|
||||
* @property {?number} stoppingTime
|
||||
* @property {?string} platform
|
||||
* @property {ArrDep} arrival
|
||||
* @property {ArrDep} departure
|
||||
* @property {Note[]} notes
|
||||
*
|
||||
* @typedef Group
|
||||
* @property {{from: string; to: string}} route
|
||||
* @property {{delay: number; station: string; state: "passing" | "arrival" | "departure"} | undefined} status
|
||||
* @property {TrainStop[]} stations
|
||||
* @type {any}
|
||||
*/
|
||||
var leafletMap = null
|
||||
/**
|
||||
* @type {any[]}
|
||||
*/
|
||||
var mapLayers = []
|
||||
|
||||
/**
|
||||
* @param {{ rank: string; number: string; operator: string; date: string; groups: Group[]; }} data
|
||||
* @param {DbTrip & DbArrDep} data
|
||||
* @param {?Date} fetchDate
|
||||
*/
|
||||
function onTrainData(data, fetchDate) {
|
||||
if (loadMap) {
|
||||
document.getElementById('load-map-button').remove()
|
||||
}
|
||||
|
||||
if (window.localStorage) {
|
||||
localStorage.setItem('recent/view-train', JSON.stringify({
|
||||
trainNumber: data.line.fahrtNr,
|
||||
name: data.line.name,
|
||||
tripId: data.id,
|
||||
date: date ? date.toISOString() : undefined,
|
||||
$addDate: new Date().toISOString(),
|
||||
}))
|
||||
}
|
||||
var title = document.getElementById('title')
|
||||
title.textContent = ''
|
||||
title.appendChild(document.createTextNode('Train '))
|
||||
trainIdSpan(data.rank, data.number, title)
|
||||
var lineNameSpan = document.createElement('span')
|
||||
title.appendChild(lineNameSpan)
|
||||
lineNameSpan.textContent = data.line.name
|
||||
if (data.line.product) {
|
||||
if (data.line.productName === 'STB' && data.line.name.startsWith('STB U')) {
|
||||
data.line.product = 'subway'
|
||||
}
|
||||
lineNameSpan.classList.add('product-' + data.line.product)
|
||||
}
|
||||
// trainIdSpan(data.line.productName, data.line.fahrtNr, title)
|
||||
// title.append(' ')
|
||||
title.append(` (${data.line.fahrtNr})`)
|
||||
|
||||
document.getElementsByTagName('title')[0].textContent = `Train ${data.rank} ${data.number}`
|
||||
document.getElementsByTagName('title')[0].textContent = `Train ${data.line.productName} ${data.line.fahrtNr} (${data.line.name})`
|
||||
|
||||
document.getElementById('company').textContent = data.operator
|
||||
if (data.line.operator) {
|
||||
document.getElementById('company').textContent = data.line.operator.name
|
||||
}
|
||||
var dateHref = document.createElement('a')
|
||||
var dateP = document.getElementById('date')
|
||||
while (dateP.childNodes.length > 0) {
|
||||
dateP.childNodes[0].remove()
|
||||
}
|
||||
dateP.appendChild(dateHref)
|
||||
dateHref.textContent = data.date
|
||||
dateHref.textContent = new Date(data.departure || data.plannedDeparture).toDateString()
|
||||
dateHref.href = '#'
|
||||
dateHref.classList.add('no-a-custom')
|
||||
dateHref.classList.add('items', 'no-a-custom')
|
||||
|
@ -105,80 +108,9 @@ function onTrainData(data, fetchDate) {
|
|||
document.getElementById('loading').classList.add('hidden')
|
||||
|
||||
/**
|
||||
* @type {Group | null}
|
||||
* @type {DbTrip & DbArrDep}
|
||||
*/
|
||||
var group = null;
|
||||
if (data.groups.length > 1 && groupIndex == null) {
|
||||
document.getElementById('group-choice').classList.remove('hidden')
|
||||
document.getElementById('group-choice').focus()
|
||||
document.getElementById('train-info').classList.add('hidden')
|
||||
|
||||
document.getElementsByClassName('rsk')[0].textContent = ''
|
||||
document.getElementsByClassName('csk')[0].textContent = 'Select'
|
||||
|
||||
title.textContent = 'Select Group for '
|
||||
trainIdSpan(data.rank, data.number, title)
|
||||
|
||||
var gc = document.getElementById('group-choice')
|
||||
while (gc.childNodes.length > 0) {
|
||||
gc.childNodes[0].remove()
|
||||
}
|
||||
|
||||
for (var i = 0; i < data.groups.length; i++) {
|
||||
var g = data.groups[i]
|
||||
|
||||
var groupLi = document.createElement('li')
|
||||
gc.append(groupLi)
|
||||
groupLi.tabIndex = i
|
||||
groupLi.classList.add('items')
|
||||
if (i === currentIndex) {
|
||||
groupLi.focus()
|
||||
}
|
||||
|
||||
(function (i) {
|
||||
function onAction(e) {
|
||||
var url = new URL(window.location.toString())
|
||||
groupIndex = i
|
||||
if (window.localStorage) {
|
||||
localStorage.setItem('recent/view-train', JSON.stringify({
|
||||
trainNumber: trainNumber,
|
||||
date: date.toISOString(),
|
||||
groupIndex: groupIndex,
|
||||
$addDate: new Date().toISOString(),
|
||||
}))
|
||||
}
|
||||
url.searchParams.append('groupIndex', groupIndex)
|
||||
window.history.pushState({'groupIndex': groupIndex}, '', url.toString( ))
|
||||
onTrainData(data)
|
||||
}
|
||||
groupLi.addEventListener('click', onAction)
|
||||
groupLi.addEventListener('keypress', function (e) {
|
||||
if (e.key == 'Enter') {
|
||||
onAction(e)
|
||||
}
|
||||
})
|
||||
})(i)
|
||||
|
||||
var routeP = document.createElement('p')
|
||||
groupLi.append(routeP)
|
||||
routeP.classList.add('pri')
|
||||
routeP.textContent = `${g.route.from} ➔ ${g.route.to}`
|
||||
|
||||
var groupP = document.createElement('p')
|
||||
groupLi.append(groupP)
|
||||
groupP.classList.add('thi')
|
||||
groupP.textContent = i === 0 ? 'Main train' : `Group ${i}`
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
else if (data.groups.length === 1) {
|
||||
group = data.groups[0]
|
||||
}
|
||||
else {
|
||||
group = data.groups[groupIndex]
|
||||
}
|
||||
var group = data
|
||||
document.getElementById('group-choice').classList.add('hidden')
|
||||
document.getElementById('train-info').classList.remove('hidden')
|
||||
document.getElementById('train-info').focus()
|
||||
|
@ -186,8 +118,8 @@ function onTrainData(data, fetchDate) {
|
|||
document.getElementsByClassName('rsk')[0].textContent = 'Refresh'
|
||||
document.getElementsByClassName('csk')[0].textContent = ''
|
||||
|
||||
document.getElementById('route-from').textContent = group.route.from
|
||||
document.getElementById('route-to').textContent = group.route.to
|
||||
document.getElementById('route-from').textContent = group.origin.name
|
||||
document.getElementById('route-to').textContent = group.destination.name
|
||||
|
||||
if (group.status) {
|
||||
document.getElementById('status').classList.remove('hidden')
|
||||
|
@ -279,21 +211,47 @@ function onTrainData(data, fetchDate) {
|
|||
var stationsList = document.createElement('ul')
|
||||
stationsDiv.appendChild(stationsList)
|
||||
|
||||
group.stations.forEach(function (station) {
|
||||
/**
|
||||
* @type {string[] | null}
|
||||
*/
|
||||
var journeyStations = null
|
||||
if (startId && stopId) {
|
||||
journeyStations = []
|
||||
var include = false
|
||||
for (var si = 0; si < group.stopovers.length; si++) {
|
||||
if (group.stopovers[si].stop.id === startId) {
|
||||
include = true
|
||||
}
|
||||
if (include) {
|
||||
journeyStations.push(group.stopovers[si].stop.id)
|
||||
}
|
||||
if (group.stopovers[si].stop.id === stopId) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group.stopovers.forEach(function (station) {
|
||||
var stationItem = document.createElement('li')
|
||||
stationsList.appendChild(stationItem)
|
||||
stationItem.classList.add('stationItem')
|
||||
if (station.cancelled && !station.arrival && !station.departure) {
|
||||
stationItem.classList.add('cancelled')
|
||||
}
|
||||
if (journeyStations && !journeyStations.includes(station.stop.id)) {
|
||||
stationItem.classList.add('not-in-journey')
|
||||
}
|
||||
|
||||
var stationName = document.createElement('p')
|
||||
stationItem.appendChild(stationName)
|
||||
stationName.classList.add('pri', 'name')
|
||||
var stationNameHref = document.createElement('a')
|
||||
stationName.appendChild(stationNameHref)
|
||||
stationNameHref.textContent = station.name
|
||||
stationNameHref.textContent = station.stop.name
|
||||
stationNameHref.classList.add('items', 'no-a-custom')
|
||||
var stationUrl = new URL('/view-station.html', window.location.origin)
|
||||
stationUrl.searchParams.append('station', station.name)
|
||||
stationUrl.searchParams.append('date', (station.arrival || station.departure).scheduleTime)
|
||||
stationUrl.searchParams.append('stationId', station.stop.id)
|
||||
stationUrl.searchParams.append('date', (station.arrival || station.departure))
|
||||
stationNameHref.href = stationUrl.toString()
|
||||
|
||||
if (station.arrival) {
|
||||
|
@ -305,26 +263,26 @@ function onTrainData(data, fetchDate) {
|
|||
stationArrival.appendChild(originalArr)
|
||||
var originalArrSpan = document.createElement('pre')
|
||||
originalArr.appendChild(originalArrSpan)
|
||||
var arrDate = new Date(station.arrival.scheduleTime)
|
||||
var arrDate = new Date(station.plannedArrival)
|
||||
originalArrSpan.textContent = arrDate.toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' })
|
||||
originalArr.classList.add('pri')
|
||||
if (station.arrival.status && station.arrival.status.delay != 0) {
|
||||
if (station.arrivalDelay) {
|
||||
originalArr.classList.remove('pri')
|
||||
originalArr.classList.add('thi')
|
||||
originalArrSpan.classList.add('original')
|
||||
var delaySpanArr = document.createElement('span')
|
||||
originalArr.appendChild(delaySpanArr)
|
||||
delaySpanArr.textContent = `${station.arrival.status.delay > 0 ? '+' : ''}${station.arrival.status.delay}`;
|
||||
delaySpanArr.classList.add(station.arrival.status.delay > 0 ? 'late' : 'early')
|
||||
delaySpanArr.textContent = `${station.arrivalDelay > 0 ? '+' : ''}${station.arrivalDelay / 60}`;
|
||||
delaySpanArr.classList.add(station.arrivalDelay > 0 ? 'late' : 'early')
|
||||
delaySpanArr.style.marginLeft = '4px'
|
||||
|
||||
var actualArr = document.createElement('p')
|
||||
stationArrival.appendChild(actualArr)
|
||||
arrDate.setMinutes(arrDate.getMinutes() + station.arrival.status.delay)
|
||||
actualArr.classList.add('pri', station.arrival.status.delay > 0 ? 'late' : 'early')
|
||||
if (!station.arrival.status.real) {
|
||||
actualArr.classList.add('not-real')
|
||||
}
|
||||
arrDate.setSeconds(arrDate.getSeconds() + station.arrivalDelay)
|
||||
actualArr.classList.add('pri', station.arrivalDelay > 0 ? 'late' : 'early')
|
||||
// if (!station.arrival.status.real) {
|
||||
// actualArr.classList.add('not-real')
|
||||
// }
|
||||
var actualArrPre = document.createElement('pre')
|
||||
actualArr.appendChild(actualArrPre)
|
||||
actualArrPre.textContent = arrDate.toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' })
|
||||
|
@ -338,88 +296,413 @@ function onTrainData(data, fetchDate) {
|
|||
|
||||
var originalDep = document.createElement('p')
|
||||
stationDeparture.appendChild(originalDep)
|
||||
var depDate = new Date(station.departure.scheduleTime)
|
||||
var depDate = new Date(station.plannedDeparture)
|
||||
var originalDepSpan = document.createElement('pre')
|
||||
originalDep.appendChild(originalDepSpan)
|
||||
originalDepSpan.textContent = depDate.toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' })
|
||||
originalDep.classList.add('pri')
|
||||
if (station.departure.status && station.departure.status.delay != 0) {
|
||||
if (station.departureDelay) {
|
||||
originalDep.classList.remove('pri')
|
||||
originalDep.classList.add('thi')
|
||||
originalDepSpan.classList.add('original')
|
||||
var delaySpanDep = document.createElement('span')
|
||||
originalDep.appendChild(delaySpanDep)
|
||||
delaySpanDep.textContent = `${station.departure.status.delay > 0 ? '+' : ''}${station.departure.status.delay}`;
|
||||
delaySpanDep.classList.add(station.departure.status.delay > 0 ? 'late' : 'early')
|
||||
delaySpanDep.textContent = `${station.departureDelay > 0 ? '+' : ''}${station.departureDelay / 60}`;
|
||||
delaySpanDep.classList.add(station.departureDelay > 0 ? 'late' : 'early')
|
||||
delaySpanDep.style.marginLeft = '4px'
|
||||
|
||||
var actualDep = document.createElement('p')
|
||||
stationDeparture.appendChild(actualDep)
|
||||
depDate.setMinutes(depDate.getMinutes() + station.departure.status.delay)
|
||||
actualDep.classList.add('pri', station.departure.status.delay > 0 ? 'late' : 'early')
|
||||
if (!station.departure.status.real) {
|
||||
actualDep.classList.add('not-real')
|
||||
}
|
||||
depDate.setSeconds(depDate.getSeconds() + station.departureDelay)
|
||||
actualDep.classList.add('pri', station.departureDelay > 0 ? 'late' : 'early')
|
||||
// if (!station.departure.status.real) {
|
||||
// actualDep.classList.add('not-real')
|
||||
// }
|
||||
var actualDepPre = document.createElement('pre')
|
||||
actualDep.appendChild(actualDepPre)
|
||||
actualDepPre.textContent = depDate.toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' })
|
||||
}
|
||||
}
|
||||
|
||||
var stationKm = document.createElement('p')
|
||||
stationItem.appendChild(stationKm)
|
||||
stationKm.textContent = `${station.km} km`
|
||||
stationKm.classList.add('thi', 'km')
|
||||
if (!showKm) {
|
||||
stationKm.classList.add('hidden')
|
||||
}
|
||||
// var stationKm = document.createElement('p')
|
||||
// stationItem.appendChild(stationKm)
|
||||
// stationKm.textContent = `${station.km} km`
|
||||
// stationKm.classList.add('thi', 'km')
|
||||
// if (!showKm) {
|
||||
// stationKm.classList.add('hidden')
|
||||
// }
|
||||
|
||||
if (station.platform) {
|
||||
if (station.arrivalPlatform || station.departurePlatform) {
|
||||
var stationPlatform = document.createElement('p')
|
||||
stationItem.appendChild(stationPlatform)
|
||||
stationPlatform.textContent = `platform ${station.platform}`
|
||||
stationPlatform.textContent = `platform ${station.departurePlatform || station.arrivalPlatform}`
|
||||
stationPlatform.classList.add('thi', 'platform')
|
||||
if (station.departurePlatform && station.departurePlatform !== station.plannedDeparturePlatform) {
|
||||
stationPlatform.classList.add('changed')
|
||||
}
|
||||
}
|
||||
|
||||
if (station.notes && station.notes.length > 0) {
|
||||
var stationNotes = document.createElement('div')
|
||||
stationItem.appendChild(stationNotes)
|
||||
stationNotes.classList.add('notes')
|
||||
if (station.remarks) {
|
||||
station.remarks.forEach(function (remark) {
|
||||
var noteP = document.createElement('p')
|
||||
stationNotes.appendChild(noteP)
|
||||
noteP.classList.add('note', 'thi', `remark-${remark.type}`)
|
||||
noteP.textContent = remark.text
|
||||
})
|
||||
}
|
||||
if (journeyStations && station.stop.id === journeyStations[0]) {
|
||||
var boardNoteP = document.createElement('p')
|
||||
stationNotes.appendChild(boardNoteP)
|
||||
boardNoteP.classList.add('note', 'thi', 'remark-board')
|
||||
boardNoteP.textContent = 'Board here'
|
||||
}
|
||||
if (journeyStations && station.stop.id === journeyStations[journeyStations.length - 1]) {
|
||||
var boardNoteP = document.createElement('p')
|
||||
stationNotes.appendChild(boardNoteP)
|
||||
boardNoteP.classList.add('note', 'thi', 'remark-exit')
|
||||
boardNoteP.textContent = 'Exit here'
|
||||
}
|
||||
// if (station.notes && station.notes.length > 0) {
|
||||
|
||||
station.notes.forEach(function (note) {
|
||||
// station.notes.forEach(function (note) {
|
||||
// var noteP = document.createElement('p')
|
||||
// stationNotes.appendChild(noteP)
|
||||
// noteP.classList.add('note', 'thi')
|
||||
|
||||
// switch (note.kind) {
|
||||
// case 'departsAs': {
|
||||
// noteP.textContent = 'Train departs as '
|
||||
// trainIdSpan(note.rank, note.number, noteP)
|
||||
// break
|
||||
// }
|
||||
// case 'detachingWagons': {
|
||||
// noteP.textContent = `Detaching wagons to ${note.station}`
|
||||
// break
|
||||
// }
|
||||
// case 'receivingWagons': {
|
||||
// noteP.textContent = `Receiving wagons from ${note.station}`
|
||||
// break
|
||||
// }
|
||||
// case 'trainNumberChange': {
|
||||
// noteP.textContent = 'Train changes number to '
|
||||
// trainIdSpan(note.rank, note.number, noteP)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
if (station.cancelled && !station.arrival && !station.departure) {
|
||||
var noteP = document.createElement('p')
|
||||
stationNotes.appendChild(noteP)
|
||||
noteP.classList.add('note', 'thi')
|
||||
noteP.textContent = 'Stop is cancelled'
|
||||
}
|
||||
})
|
||||
|
||||
switch (note.kind) {
|
||||
case 'departsAs': {
|
||||
noteP.textContent = 'Train departs as '
|
||||
trainIdSpan(note.rank, note.number, noteP)
|
||||
break
|
||||
}
|
||||
case 'detachingWagons': {
|
||||
noteP.textContent = `Detaching wagons to ${note.station}`
|
||||
break
|
||||
}
|
||||
case 'receivingWagons': {
|
||||
noteP.textContent = `Receiving wagons from ${note.station}`
|
||||
break
|
||||
}
|
||||
case 'trainNumberChange': {
|
||||
noteP.textContent = 'Train changes number to '
|
||||
trainIdSpan(note.rank, note.number, noteP)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (group.remarks && group.remarks.length > 0) {
|
||||
var remarksSeparator = document.createElement('h4')
|
||||
stationsDiv.appendChild(remarksSeparator)
|
||||
remarksSeparator.textContent = 'Remarks'
|
||||
|
||||
var remarksList = document.createElement('ul')
|
||||
stationsDiv.appendChild(remarksList)
|
||||
remarksList.classList.add('remarks')
|
||||
|
||||
group.remarks.forEach(function (remark) {
|
||||
var remarkItem = document.createElement('li')
|
||||
remarksList.append(remarkItem)
|
||||
remarkItem.classList.add('remarkItem', `remark-${remark.type}`)
|
||||
|
||||
var remarkSummaryP = document.createElement('p')
|
||||
remarkItem.appendChild(remarkSummaryP)
|
||||
remarkSummaryP.classList.add('remark', 'pri')
|
||||
remarkSummaryP.textContent = remark.summary
|
||||
|
||||
var remarkTextP = document.createElement('p')
|
||||
remarkItem.appendChild(remarkTextP)
|
||||
remarkTextP.classList.add('remark', 'sec')
|
||||
remarkTextP.textContent = remark.text
|
||||
})
|
||||
}
|
||||
|
||||
var mapDiv = document.getElementById('map')
|
||||
if (group.polyline) {
|
||||
console.log(group.polyline)
|
||||
|
||||
mapDiv.classList.remove('hidden')
|
||||
|
||||
var actualMap = document.getElementById('actual-map')
|
||||
if (!actualMap) {
|
||||
actualMap = document.createElement('div')
|
||||
actualMap.id = 'actual-map'
|
||||
mapDiv.insertBefore(actualMap, null)
|
||||
}
|
||||
|
||||
if (!leafletMap) {
|
||||
leafletMap = L.map('actual-map', {
|
||||
zoomSnap: 0.5,
|
||||
// zoomDelta: 0.5,
|
||||
}).setView([51.1657, 10.4515], 6)
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||
}).addTo(leafletMap);
|
||||
L.tileLayer('https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png', {
|
||||
attribution: ' Style: <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA 2.0</a> <a href="http://www.openrailwaymap.org/">OpenRailwayMap</a> and OpenStreetMap',
|
||||
minZoom: 2,
|
||||
maxZoom: 19,
|
||||
tileSize: 256
|
||||
}).addTo(leafletMap)
|
||||
}
|
||||
|
||||
if (mapLayers) {
|
||||
mapLayers.forEach(function (layer) { layer.remove() })
|
||||
mapLayers = []
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {[number, number][]}
|
||||
*/
|
||||
var lines = []
|
||||
/**
|
||||
* @type {[number, number][] | null}
|
||||
*/
|
||||
var journeyLines = null
|
||||
include = false
|
||||
var stops = []
|
||||
var minLat = group.polyline.features[0].geometry.coordinates[1]
|
||||
var maxLat = minLat
|
||||
var minLng = group.polyline.features[0].geometry.coordinates[0]
|
||||
var maxLng = minLng
|
||||
for (var pi = 0; pi < group.polyline.features.length; pi++) {
|
||||
var feature = group.polyline.features[pi]
|
||||
var coord = feature.geometry.coordinates
|
||||
lines.push([coord[1], coord[0]])
|
||||
if (journeyStations && include !== -1) {
|
||||
if (feature.properties && feature.properties.id === journeyStations[0]) {
|
||||
minLat = maxLat = coord[1]
|
||||
minLng = maxLng = coord[0]
|
||||
include = true
|
||||
}
|
||||
if (include) {
|
||||
if (!journeyLines) journeyLines = []
|
||||
journeyLines.push([coord[1], coord[0]])
|
||||
}
|
||||
if (feature.properties && feature.properties.id === journeyStations[journeyStations.length - 1]) {
|
||||
include = -1
|
||||
}
|
||||
}
|
||||
if (feature.properties && feature.properties.type == 'stop') {
|
||||
stops.push({
|
||||
coord: [coord[1], coord[0]],
|
||||
id: feature.properties.id,
|
||||
})
|
||||
}
|
||||
|
||||
if (!journeyStations || include === true) {
|
||||
if (minLat > coord[1]) {
|
||||
minLat = coord[1]
|
||||
}
|
||||
if (minLng > coord[0]) {
|
||||
minLng = coord[0]
|
||||
}
|
||||
if (maxLat < coord[1]) {
|
||||
maxLat = coord[1]
|
||||
}
|
||||
if (maxLng < coord[0]) {
|
||||
maxLng = coord[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mapLayers.push(L.polyline(lines, {
|
||||
color: !journeyLines ? '#6666ff' : '#9999dd',
|
||||
opacity: !journeyLines ? 1.0 : 0.8,
|
||||
weight: 4,
|
||||
}).addTo(leafletMap));
|
||||
if (journeyLines) {
|
||||
mapLayers.push(L.polyline(journeyLines, {
|
||||
color: '#6666ff',
|
||||
weight: 2,
|
||||
}).addTo(leafletMap));
|
||||
}
|
||||
|
||||
for (var si = 0; si < stops.length; si++) {
|
||||
var stop = stops[si]
|
||||
mapLayers.push(L.circleMarker(stop.coord, {
|
||||
color: !journeyStations || journeyStations.includes(stop.id) ? '#ffff66' : '#dfdf66',
|
||||
radius: 5,
|
||||
}).addTo(leafletMap))
|
||||
}
|
||||
|
||||
leafletMap.flyToBounds([[minLat, minLng], [maxLat, maxLng]], {
|
||||
duration: 0.2,
|
||||
})
|
||||
|
||||
actualMap.scrollIntoView()
|
||||
}
|
||||
else if (loadMap) {
|
||||
mapDiv.classList.add('hidden')
|
||||
}
|
||||
|
||||
lastSuccessfulFetch = fetchDate || new Date()
|
||||
}
|
||||
|
||||
var refreshStopToken = null
|
||||
|
||||
/**
|
||||
* @typedef DbProducts
|
||||
* @property {boolean} nationalExpress
|
||||
* @property {boolean} national
|
||||
* @property {boolean} regionalExpress
|
||||
* @property {boolean} regional
|
||||
* @property {boolean} suburban
|
||||
* @property {boolean} bus
|
||||
* @property {boolean} ferry
|
||||
* @property {boolean} subway
|
||||
* @property {boolean} tram
|
||||
* @property {boolean} taxi
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef DbLocation
|
||||
* @property {'location'} type
|
||||
* @property {?string} id
|
||||
* @property {number} latitude
|
||||
* @property {number} longitude
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef DbLine
|
||||
* @property {'line'} type
|
||||
* @property {string} id
|
||||
* @property {string} fahrtNr
|
||||
* @property {string} name
|
||||
* @property {boolean} public
|
||||
* @property {string} adminCode
|
||||
* @property {string} productName
|
||||
* @property {string} mode
|
||||
* @property {string} product
|
||||
* @property {{type: 'operator', id: string, name: string}} operator
|
||||
* @property {string} additionalName
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef DbStop
|
||||
* @property {'stop'} type
|
||||
* @property {?string} id
|
||||
* @property {string} name
|
||||
* @property {DbLocation} location
|
||||
* @property {DbProducts} products
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef DbRemark
|
||||
* @property {string} type
|
||||
* @property {string} text
|
||||
* @property {string} code
|
||||
* @property {string} summary
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef DbArrDep
|
||||
* @property {string | null} arrival
|
||||
* @property {string | null} plannedArrival
|
||||
* @property {number | null} arrivalDelay
|
||||
* @property {string | null} arrivalPlatform
|
||||
* @property {string | null} plannedArrivalPlatform
|
||||
* @property {string | null} arrivalPrognosisType
|
||||
* @property {string | null} departure
|
||||
* @property {string | null} plannedDeparture
|
||||
* @property {number | null} departureDelay
|
||||
* @property {string | null} departurePlatform
|
||||
* @property {string | null} plannedDeparturePlatform
|
||||
* @property {string | null} departurePrognosisType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef DbTrip
|
||||
* @property {DbStop} origin
|
||||
* @property {DbStop} destination
|
||||
* @property {DbLine} line
|
||||
* @property {string} direction
|
||||
* @property {DbLocation} location
|
||||
* @property {({stop: DbStop, cancelled?: boolean, remarks?: DbRemark[]} & DbArrDep)[]} stopovers
|
||||
* @property {DbRemark[]} remarks
|
||||
* @property {string} id
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string | null} tripId
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function onTripId(tripId) {
|
||||
if (tripId) {
|
||||
var reqUrl = new URL(`https://v6.db.transport.rest/trips/${encodeURIComponent(tripId)}`)
|
||||
reqUrl.searchParams.append('stopovers', 'true')
|
||||
reqUrl.searchParams.append('remarks', 'true')
|
||||
if (loadMap) {
|
||||
reqUrl.searchParams.append('polyline', 'true')
|
||||
}
|
||||
return fetch(
|
||||
reqUrl.toString(),
|
||||
{
|
||||
cache: 'no-store',
|
||||
},
|
||||
).then(function (response) {
|
||||
if (!response.ok) {
|
||||
// Check in 10 seconds if server returned error
|
||||
return Promise.reject('Response not okay')
|
||||
}
|
||||
var cacheDate = response.headers.get('SW-Cached-At')
|
||||
return response.json().then(function (data) {
|
||||
data['$cacheDate'] = cacheDate
|
||||
return data
|
||||
})
|
||||
}).then(function (response) {
|
||||
if (!response) {
|
||||
return Promise.reject('No JSON response')
|
||||
}
|
||||
var cacheDate = response['$cacheDate']
|
||||
if (cacheDate) {
|
||||
cacheDate = new Date(cacheDate)
|
||||
}
|
||||
var success = !cacheDate
|
||||
trainData = response.trip
|
||||
onTrainData(response.trip, cacheDate)
|
||||
return success
|
||||
})
|
||||
}
|
||||
else {
|
||||
return Promise.reject('No tripId found')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when trips are found
|
||||
* @param {string} wantedTrainNumber
|
||||
* @param {{trips: (DbTrip & DbArrDep)[]}} data
|
||||
* @param {?Date} fetchDate
|
||||
*/
|
||||
function onTripsData(wantedTrainNumber, data, fetchDate) {
|
||||
data.trips.forEach(function (trip) {
|
||||
if (trip.line.fahrtNr == wantedTrainNumber || trip.line.name == wantedTrainNumber) {
|
||||
tripId = trip.id
|
||||
console.group('Found tripId by train number')
|
||||
console.log(`ID: ${trip.id}`)
|
||||
console.log(`Fahrt Nr: ${trip.line.fahrtNr}`)
|
||||
console.log(`Name: ${trip.line.name}`)
|
||||
console.log(`+Name: ${trip.line.additionalName}`)
|
||||
console.groupEnd()
|
||||
}
|
||||
})
|
||||
return onTripId(tripId)
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
|
@ -432,6 +715,9 @@ function refresh() {
|
|||
refresh()
|
||||
}, timeout || 60000)
|
||||
}
|
||||
if (tripId) {
|
||||
return onTripId(tripId)
|
||||
}
|
||||
/**
|
||||
* @type {Date}
|
||||
*/
|
||||
|
@ -440,8 +726,14 @@ function refresh() {
|
|||
reqDate.setDate(reqDate.getDate() - 1)
|
||||
}
|
||||
reqDate.setMinutes(0, 0, 0)
|
||||
var reqUrl = new URL(`https://v6.db.transport.rest/trips`)
|
||||
reqUrl.searchParams.append('query', trainNumber)
|
||||
if (loadMap) {
|
||||
reqUrl.searchParams.append('polyline', 'true')
|
||||
}
|
||||
reqUrl.searchParams.append('when', reqDate.toISOString())
|
||||
return fetch(
|
||||
`https://scraper.infotren.dcdev.ro/v3/trains/${trainNumber}?date=${reqDate.toISOString()}`,
|
||||
reqUrl.toString(),
|
||||
{
|
||||
cache: 'no-store',
|
||||
},
|
||||
|
@ -465,11 +757,12 @@ function refresh() {
|
|||
cacheDate = new Date(cacheDate)
|
||||
}
|
||||
var success = !cacheDate
|
||||
trainData = response
|
||||
onTrainData(response, cacheDate)
|
||||
return onTripsData(trainNumber, response, cacheDate)
|
||||
.then(function () {
|
||||
// Check in 1 seconds if network error
|
||||
reschedule(success ? undefined : 1000)
|
||||
return success
|
||||
})
|
||||
}).catch(function (e) {
|
||||
// Check in 1 second if network error
|
||||
reschedule(1000)
|
||||
|
@ -521,7 +814,9 @@ window.addEventListener('popstate', function (e) {
|
|||
})
|
||||
|
||||
window.addEventListener('load', function (e) {
|
||||
if (!new URL(window.location.href).searchParams.has('train')) {
|
||||
var sp = new URL(window.location.href).searchParams
|
||||
|
||||
if (!sp.has('train') && !sp.has('tripId')) {
|
||||
window.history.back()
|
||||
this.setTimeout(function () {
|
||||
var url = new URL(window.location.href)
|
||||
|
@ -530,11 +825,12 @@ window.addEventListener('load', function (e) {
|
|||
}, 100)
|
||||
}
|
||||
|
||||
var sp = new URL(window.location.href).searchParams
|
||||
|
||||
trainNumber = sp.get('train')
|
||||
date = sp.has('date') ? new Date(sp.get('date')) : new Date()
|
||||
groupIndex = sp.has('groupIndex') ? parseInt(sp.get('groupIndex')) : null
|
||||
tripId = sp.get('tripId')
|
||||
startId = sp.get('startId')
|
||||
stopId = sp.get('stopId')
|
||||
|
||||
if (window.localStorage) {
|
||||
var oldRecent = localStorage.getItem('recent/view-train')
|
||||
|
@ -543,8 +839,8 @@ window.addEventListener('load', function (e) {
|
|||
}
|
||||
localStorage.setItem('recent/view-train', JSON.stringify({
|
||||
trainNumber: trainNumber,
|
||||
tripId: tripId,
|
||||
date: date.toISOString(),
|
||||
groupIndex: oldRecent && oldRecent.trainNumber === trainNumber ? oldRecent.groupIndex : undefined,
|
||||
$addDate: new Date().toISOString(),
|
||||
}))
|
||||
}
|
||||
|
@ -598,6 +894,13 @@ window.addEventListener('load', function (e) {
|
|||
}
|
||||
})
|
||||
|
||||
document.getElementById('load-map-button').addEventListener('click', function (event) {
|
||||
event.preventDefault()
|
||||
event.target.textContent = 'Loading...'
|
||||
loadMap = true;
|
||||
refresh()
|
||||
})
|
||||
|
||||
refresh()
|
||||
|
||||
setInterval(function () {
|
||||
|
|
Loading…
Add table
Reference in a new issue