When a train has a 2 minute delay, currently the scheduled time is displayed with strikethrough and the actual time is displayed below. This change adds a +2 after the original time to also have the relative delay, not just the absolute one.
551 lines
16 KiB
JavaScript
Executable file
551 lines
16 KiB
JavaScript
Executable file
var trainNumber
|
|
var date
|
|
var groupIndex = null
|
|
|
|
var yesterday = false
|
|
var showKm = false
|
|
|
|
var trainData = null
|
|
var lastSuccessfulFetch = null
|
|
|
|
/**
|
|
* @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
|
|
*/
|
|
|
|
/**
|
|
* @param {{ rank: string; number: string; operator: string; date: string; groups: Group[]; }} data
|
|
*/
|
|
function onTrainData(data) {
|
|
var title = document.getElementById('title')
|
|
title.textContent = ''
|
|
title.appendChild(document.createTextNode('Train '))
|
|
var rankSpan = document.createElement('span')
|
|
rankSpan.textContent = data.rank
|
|
rankSpan.classList.add(data.rank)
|
|
title.appendChild(rankSpan)
|
|
title.appendChild(document.createTextNode(` ${data.number}`))
|
|
|
|
document.getElementsByTagName('title')[0].textContent = `Train ${data.rank} ${data.number}`
|
|
|
|
document.getElementById('company').textContent = data.operator
|
|
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.href = '#'
|
|
dateHref.classList.add('no-a-custom')
|
|
dateHref.classList.add('items', 'no-a-custom')
|
|
dateHref.addEventListener('click', function (e) {
|
|
e.preventDefault()
|
|
// Implement date switcher
|
|
yesterday = !yesterday
|
|
refresh()
|
|
})
|
|
|
|
document.getElementById('loading').classList.add('hidden')
|
|
|
|
/**
|
|
* @type {Group | null}
|
|
*/
|
|
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 ${data.rank} ${data.number}`
|
|
|
|
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
|
|
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]
|
|
}
|
|
document.getElementById('group-choice').classList.add('hidden')
|
|
document.getElementById('train-info').classList.remove('hidden')
|
|
document.getElementById('train-info').focus()
|
|
|
|
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
|
|
|
|
if (group.status) {
|
|
document.getElementById('status').classList.remove('hidden')
|
|
|
|
var statusDelay = document.getElementById('status-delay')
|
|
while (statusDelay.childNodes.length > 0) {
|
|
statusDelay.childNodes[0].remove()
|
|
}
|
|
var delayString = ''
|
|
var delayMinutes = group.status.delay
|
|
if (delayMinutes === 0) {
|
|
delayString = 'On time'
|
|
statusDelay.appendChild(document.createTextNode(delayString))
|
|
}
|
|
else {
|
|
var early = false
|
|
if (delayMinutes < 0) {
|
|
early = true
|
|
delayMinutes = -delayMinutes
|
|
}
|
|
|
|
if (delayMinutes >= 60) {
|
|
var hours = Math.floor(delayMinutes / 60)
|
|
delayMinutes = delayMinutes % 60
|
|
delayString += hours.toString()
|
|
delayString += ' hour'
|
|
if (hours > 1) {
|
|
delayString += 's'
|
|
}
|
|
}
|
|
if (delayMinutes > 0) {
|
|
if (delayString.length > 0) {
|
|
delayString += ' and '
|
|
}
|
|
delayString += delayMinutes.toString()
|
|
delayString += ' minute'
|
|
if (delayMinutes > 1) {
|
|
delayString += 's'
|
|
}
|
|
}
|
|
delayString += ' '
|
|
statusDelay.appendChild(document.createTextNode(delayString))
|
|
|
|
var kindSpan = document.createElement('span')
|
|
statusDelay.appendChild(kindSpan)
|
|
if (early) {
|
|
kindSpan.textContent = 'early'
|
|
kindSpan.classList.add('early')
|
|
}
|
|
else {
|
|
kindSpan.textContent = 'late'
|
|
kindSpan.classList.add('late')
|
|
}
|
|
}
|
|
|
|
var statusLocation = document.getElementById('status-location')
|
|
while (statusLocation.childNodes.length > 0) {
|
|
statusLocation.childNodes[0].remove()
|
|
}
|
|
var stateString = ''
|
|
if (group.status.state === 'arrival') {
|
|
stateString += 'when arriving at '
|
|
}
|
|
else if (group.status.state === 'departure') {
|
|
stateString += 'when departing from '
|
|
}
|
|
else if (group.status.state === 'passing') {
|
|
stateString += 'while passing through '
|
|
}
|
|
statusLocation.appendChild(document.createTextNode(stateString))
|
|
var stationSpan = document.createElement('span')
|
|
statusLocation.appendChild(stationSpan)
|
|
stationSpan.textContent = group.status.station
|
|
stationSpan.classList.add('station')
|
|
}
|
|
else {
|
|
document.getElementById('status').classList.add('hidden')
|
|
}
|
|
|
|
var stationsDiv = document.getElementById('stations')
|
|
while (stationsDiv.childNodes.length > 0) {
|
|
stationsDiv.childNodes[0].remove()
|
|
}
|
|
|
|
var separator = document.createElement('h4')
|
|
stationsDiv.appendChild(separator)
|
|
separator.textContent = 'Stations'
|
|
|
|
var stationsList = document.createElement('ul')
|
|
stationsDiv.appendChild(stationsList)
|
|
|
|
group.stations.forEach(function (station) {
|
|
var stationItem = document.createElement('li')
|
|
stationsList.appendChild(stationItem)
|
|
stationItem.classList.add('stationItem')
|
|
|
|
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.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)
|
|
stationNameHref.href = stationUrl.toString()
|
|
|
|
if (station.arrival) {
|
|
var stationArrival = document.createElement('div')
|
|
stationItem.appendChild(stationArrival)
|
|
stationArrival.classList.add('arrival')
|
|
|
|
var originalArr = document.createElement('p')
|
|
stationArrival.appendChild(originalArr)
|
|
var originalArrSpan = document.createElement('span')
|
|
originalArr.appendChild(originalArrSpan)
|
|
var arrDate = new Date(station.arrival.scheduleTime)
|
|
originalArrSpan.textContent = arrDate.toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' })
|
|
originalArr.classList.add('pri')
|
|
if (station.arrival.status && station.arrival.status.delay != 0) {
|
|
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.style.marginLeft = '4px'
|
|
|
|
var actualArr = document.createElement('p')
|
|
stationArrival.appendChild(actualArr)
|
|
arrDate.setMinutes(arrDate.getMinutes() + station.arrival.status.delay)
|
|
actualArr.textContent = arrDate.toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' })
|
|
actualArr.classList.add('pri', station.arrival.status.delay > 0 ? 'late' : 'early')
|
|
if (!station.arrival.status.real) {
|
|
actualArr.classList.add('not-real')
|
|
}
|
|
}
|
|
}
|
|
|
|
if (station.departure) {
|
|
var stationDeparture = document.createElement('div')
|
|
stationItem.appendChild(stationDeparture)
|
|
stationDeparture.classList.add('departure')
|
|
|
|
var originalDep = document.createElement('p')
|
|
stationDeparture.appendChild(originalDep)
|
|
var depDate = new Date(station.departure.scheduleTime)
|
|
var originalDepSpan = document.createElement('span')
|
|
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) {
|
|
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.style.marginLeft = '4px'
|
|
|
|
var actualDep = document.createElement('p')
|
|
stationDeparture.appendChild(actualDep)
|
|
depDate.setMinutes(depDate.getMinutes() + station.departure.status.delay)
|
|
actualDep.textContent = depDate.toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' })
|
|
actualDep.classList.add('pri', station.departure.status.delay > 0 ? 'late' : 'early')
|
|
if (!station.departure.status.real) {
|
|
actualDep.classList.add('not-real')
|
|
}
|
|
}
|
|
}
|
|
|
|
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) {
|
|
var stationPlatform = document.createElement('p')
|
|
stationItem.appendChild(stationPlatform)
|
|
stationPlatform.textContent = `platform ${station.platform}`
|
|
stationPlatform.classList.add('thi', 'platform')
|
|
}
|
|
|
|
if (station.notes && station.notes.length > 0) {
|
|
var stationNotes = document.createElement('div')
|
|
stationItem.appendChild(stationNotes)
|
|
stationNotes.classList.add('notes')
|
|
|
|
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 ${note.rank} ${note.number}`
|
|
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 ${note.rank} ${note.number}`
|
|
break
|
|
}
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
lastSuccessfulFetch = new Date()
|
|
}
|
|
|
|
var refreshStopToken = null
|
|
function refresh() {
|
|
function reschedule(timeout) {
|
|
if (refreshStopToken != null) {
|
|
clearTimeout(refreshStopToken)
|
|
}
|
|
refreshStopToken = setTimeout(function () {
|
|
refresh()
|
|
}, timeout || 60000)
|
|
}
|
|
/**
|
|
* @type {Date}
|
|
*/
|
|
var reqDate = new Date(date.valueOf())
|
|
if (yesterday) {
|
|
reqDate.setDate(reqDate.getDate() - 1)
|
|
}
|
|
return fetch(
|
|
`https://scraper.infotren.dcdev.ro/v3/trains/${trainNumber}?date=${reqDate.getFullYear().toString()}-${(reqDate.getMonth() + 1).toString().padStart(2, "0")}-${reqDate.getDate().toString().padStart(2, "0")}`,
|
|
{
|
|
cache: 'no-store',
|
|
},
|
|
).then(function (response) {
|
|
if (!response.ok) {
|
|
// Check in 10 seconds if server returned error
|
|
reschedule(10000)
|
|
return
|
|
}
|
|
return response.json()
|
|
}).then(function (response) {
|
|
if (!response) {
|
|
return
|
|
}
|
|
trainData = response
|
|
onTrainData(response)
|
|
reschedule()
|
|
}).catch(function (e) {
|
|
// Check in 1 second if network error
|
|
reschedule(1000)
|
|
throw e
|
|
})
|
|
}
|
|
|
|
window.addEventListener('unload', function (e) {
|
|
if (refreshStopToken != null) {
|
|
clearTimeout(refreshStopToken)
|
|
}
|
|
})
|
|
|
|
function rsk() {
|
|
refresh()
|
|
}
|
|
|
|
window.addEventListener('popstate', function (e) {
|
|
groupIndex = null
|
|
if (trainData) {
|
|
onTrainData(trainData)
|
|
}
|
|
else {
|
|
refresh()
|
|
}
|
|
})
|
|
|
|
window.addEventListener('load', function (e) {
|
|
if (!new URL(window.location.href).searchParams.has('train')) {
|
|
window.history.back()
|
|
this.setTimeout(function () {
|
|
var url = new URL(window.location.href)
|
|
url.pathname = 'train.html'
|
|
window.location.href = url.toString()
|
|
}, 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
|
|
|
|
document.querySelectorAll('.rsk').forEach(function (rskElem) {
|
|
rskElem.addEventListener('click', function (e) {
|
|
rsk()
|
|
})
|
|
})
|
|
|
|
var content = document.getElementsByClassName('content')[0]
|
|
content.focus()
|
|
content.addEventListener('keydown', function (e) {
|
|
switch (e.key) {
|
|
// case 'ArrowUp':
|
|
// content.scrollBy(0, -50)
|
|
// break
|
|
// case 'ArrowDown':
|
|
// content.scrollBy(0, 50)
|
|
// break
|
|
case 'SoftRight':
|
|
rsk()
|
|
break
|
|
case '1':
|
|
date.setDate(date.getDate() - 1)
|
|
refresh()
|
|
break
|
|
case '3':
|
|
date.setDate(date.getDate() + 1)
|
|
refresh()
|
|
break
|
|
case '7':
|
|
showKm = !showKm
|
|
document.querySelectorAll('.km').forEach(function (kmItem) {
|
|
if (showKm) {
|
|
kmItem.classList.remove('hidden')
|
|
}
|
|
else {
|
|
kmItem.classList.add('hidden')
|
|
}
|
|
})
|
|
break
|
|
default:
|
|
console.log(e.key)
|
|
}
|
|
})
|
|
|
|
refresh()
|
|
|
|
setInterval(function () {
|
|
if (!lastSuccessfulFetch) {
|
|
return
|
|
}
|
|
var millis = new Date() - lastSuccessfulFetch
|
|
var secs = Math.floor(millis / 1000)
|
|
|
|
var timeStr = ''
|
|
if (secs / 3600 >= 1) {
|
|
timeStr += `${Math.floor(secs / 3600)}h`
|
|
secs = secs % 3600
|
|
}
|
|
if (secs / 60 >= 1) {
|
|
timeStr += `${Math.floor(secs / 60)}m`
|
|
secs = secs % 60
|
|
}
|
|
if (secs >= 1) {
|
|
timeStr += `${Math.floor(secs)}s`
|
|
}
|
|
if (!timeStr) {
|
|
document.querySelectorAll('.lsk').forEach(function (elem) {
|
|
elem.textContent = 'Last refreshed now'
|
|
elem.classList.add('last-refreshed')
|
|
})
|
|
}
|
|
else {
|
|
document.querySelectorAll('.lsk').forEach(function (elem) {
|
|
elem.textContent = `Last refreshed ${timeStr} ago`
|
|
elem.classList.add('last-refreshed')
|
|
})
|
|
}
|
|
}, 500)
|
|
|
|
if (this.localStorage && !this.localStorage.getItem('info-yesterday')) {
|
|
this.alert("New feature: You can now view yesterday's train by selecting the date!")
|
|
this.localStorage.setItem('info-yesterday', 'true')
|
|
}
|
|
})
|