You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
414 lines
17 KiB
414 lines
17 KiB
/** |
|
* @type {string | null} |
|
*/ |
|
var fromStation = null |
|
/** |
|
* @type {string | null} |
|
*/ |
|
var toStation = null |
|
/** |
|
* @type {Date | null} |
|
*/ |
|
var departureDate = null |
|
/** |
|
* @type {Date | null} |
|
*/ |
|
var arrivalDate = null |
|
var transitKind = { |
|
ice: true, |
|
ic: true, |
|
re: true, |
|
rb: true, |
|
s: true, |
|
bus: true, |
|
ferry: true, |
|
u: true, |
|
tram: true, |
|
} |
|
|
|
var itineraries = null |
|
|
|
var focusedElement = null |
|
|
|
/** |
|
* @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.getElementById('content-div') |
|
if (!contentDiv) { |
|
contentDiv = document.createElement('div') |
|
document.body.insertBefore(contentDiv, document.querySelector('footer')) |
|
contentDiv.classList.add('content') |
|
contentDiv.id = 'content-div' |
|
} |
|
|
|
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) |
|
|
|
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') |
|
if (train.line.product) { |
|
if (train.line.productName === 'STB' && train.line.name.startsWith('STB U')) { |
|
train.line.product = 'subway' |
|
} |
|
if (train.line.adminCode === 'vvs020') { |
|
// Stuttgart Stadtbahn |
|
trainLink.innerText = train.line.name.slice(4) |
|
} else if (train.line.adminCode === '800643') { |
|
// Stuttgart S-Bahn |
|
trainLink.innerText = 'S' + train.line.name.slice(2) |
|
} else if (train.line.adminCode === 'kvv021') { |
|
// Karlsruhe Tram |
|
trainLink.innerText = train.line.name.slice(4) |
|
} |
|
trainLink.classList.add('product-' + train.line.product) |
|
trainLink.classList.add('product-id-' + train.line.id) |
|
trainLink.classList.add('product-adminCode-' + train.line.adminCode) |
|
if (train.line.operator) { |
|
trainLink.classList.add('product-operator-' + train.line.operator.id) |
|
} |
|
} |
|
var trainUrl = new URL('/view-train.html', window.location.origin) |
|
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.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.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(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() { |
|
|
|
} |
|
|
|
function csk() { |
|
if (focusedElement == null) { |
|
return |
|
} |
|
|
|
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 arrivalDateStr = sp.get('arrivalDate') |
|
if (arrivalDateStr) { |
|
arrivalDate = new Date(arrivalDateStr) |
|
} |
|
|
|
var titleH1 = document.querySelector("header > h1") |
|
if (!fromJson || !toJson) { |
|
titleH1.textContent = 'Find Route' |
|
} |
|
else { |
|
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 || (!departureDate && !arrivalDate)) { |
|
// Send to config page |
|
var url = new URL(window.location.href) |
|
url.pathname = '/config-route.html' |
|
window.location.href = url.toString() |
|
} |
|
else { |
|
var contentDiv = document.createElement('div') |
|
document.body.insertBefore(contentDiv, footer) |
|
contentDiv.classList.add('content') |
|
contentDiv.style.display = 'flex' |
|
contentDiv.style.flexDirection = 'column' |
|
contentDiv.style.alignItems = 'center' |
|
contentDiv.style.justifyContent = 'center' |
|
|
|
var loadingP = document.createElement('p') |
|
contentDiv.appendChild(loadingP) |
|
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]) |
|
}) |
|
} |
|
if (departureDate) { |
|
url.searchParams.set('departure', departureDate.toISOString()) |
|
} |
|
if (arrivalDate) { |
|
url.searchParams.set('arrival', arrivalDate.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, |
|
})) |
|
} |
|
|
|
fetch(url.toString()) |
|
.then(function (response) { |
|
return response.json() |
|
}) |
|
.then(function (data) { |
|
contentDiv.remove() |
|
onItineraries(data) |
|
itineraries = data |
|
function fetchMore(timeoutMs) { |
|
console.debug(`Got ${itineraries.journeys.length} journeys, fetching more`) |
|
var moreUrl = new URL(url.toString()) |
|
moreUrl.searchParams.delete('departure') |
|
moreUrl.searchParams.delete('arrival') |
|
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( |
|
function () { |
|
fetchMore((timeoutMs || 500) * 1.5) |
|
}, |
|
timeoutMs || 500, |
|
) |
|
} |
|
} |
|
} |
|
}) |
|
} |
|
|
|
if (departureDate) { |
|
fetchMore() |
|
} |
|
}) |
|
.catch(function (e) { |
|
loadingP.textContent = 'An error has occured' |
|
|
|
var errorP = document.createElement('p') |
|
contentDiv.appendChild(errorP) |
|
errorP.classList.add('sec') |
|
errorP.textContent = e.toString() |
|
|
|
var retryLink = document.createElement('a') |
|
contentDiv.appendChild(retryLink) |
|
retryLink.classList.add('items') |
|
retryLink.textContent = 'Retry' |
|
retryLink.href = '' |
|
}) |
|
} |
|
|
|
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() |
|
} |
|
}) |
|
})
|
|
|