var stationId var date 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 || !data.arrivals && !data?.departures) { return } var title = document.getElementById('title') title.textContent = (data.departures[0] || data.arrivals[0]).stop.name // document.getElementById('date').textContent = data.date document.getElementById('loading').classList.add('hidden') /** * @param {HTMLElement} elem * @param {StationArrDep[]} trains */ function addTrains(elem, trains) { while (elem.childNodes.length > 0) { elem.childNodes[0].remove() } var trainsList = document.createElement('ul') elem.appendChild(trainsList) trains.forEach(function (train, tIdx) { var trainItem = document.createElement('li') trainsList.appendChild(trainItem) trainItem.classList.add('train-item') // 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.plannedWhen).toLocaleTimeString([], { 'hour': '2-digit', 'minute': '2-digit' }) if (train.delay && train.delay != 0) { var delayDiv = document.createElement('p') trainItem.appendChild(delayDiv) delayDiv.classList.add('thi', 'delay') delayDiv.textContent = `${train.delay / 60} min ` // delayDiv.appendChild(document.createElement('br')) var descSpan = document.createElement('span') delayDiv.appendChild(descSpan) if (train.delay > 0) { descSpan.classList.add('late') descSpan.textContent = 'late' } else { descSpan.classList.add('early') descSpan.textContent = 'early' } } // 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) trainDiv.classList.add('pri', 'train') var trainDivHref = document.createElement('a') trainDiv.appendChild(trainDivHref) trainDivHref.classList.add('no-a-custom') var trainUrl = new URL('/view-train.html', window.location.origin) trainUrl.searchParams.append('tripId', train.tripId) trainDivHref.href = trainUrl.toString() var trainDivHrefPre = document.createElement('pre') trainDivHref.appendChild(trainDivHrefPre) trainDivHrefPre.textContent = train.line.name var terminusDiv = document.createElement('p') trainItem.appendChild(terminusDiv) terminusDiv.classList.add('pri', 'terminus') terminusDiv.textContent = train.direction 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.platform || train.plannedPlatform } if (train.cancelled) { trainItem.classList.add('cancelled') var statusDiv = document.createElement('p') trainItem.appendChild(statusDiv) statusDiv.classList.add('sec', 'status', 'status-cancel') statusDiv.textContent = 'This journey is cancelled' } }) } addTrains(document.getElementById('arrivals'), data.arrivals) addTrains(document.getElementById('departures'), data.departures) } var refreshStopToken = null function refresh() { function reschedule(timeout) { if (refreshStopToken != null) { clearTimeout(refreshStopToken) } refreshStopToken = setTimeout(function () { refresh() }, timeout || 90000) } var reqDate = new Date(date.valueOf()) // 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', }, ), 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 = 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 (responses) { if (!responses) { return } var cacheDate = responses['$cacheDate'] if (cacheDate) { cacheDate = new Date(cacheDate) } var success = !cacheDate 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 }).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() { function changeRskText(newText) { document.querySelectorAll('.rsk').forEach(function (elem) { elem.textContent = newText }) } changeRskText('Refreshing...') refresh().catch(function () { return false}).then(function (success) { if (!success) { changeRskText('Refreshing failed') setTimeout(function (_) { changeRskText('Refresh') }, 3000) } else { changeRskText('Refresh') } }) } window.addEventListener('load', function (e) { if (!new URL(window.location.href).searchParams.has('stationId')) { window.history.back() this.setTimeout(function () { var url = new URL(window.location.href) url.pathname = 'station.html' window.location.href = url.toString() }, 100) } var sp = new URL(window.location.href).searchParams stationId = sp.get('stationId') date = sp.has('date') ? new Date(sp.get('date')) : new Date() // View departures first selectedTab = 1 selectTab(selectedTab) document.querySelectorAll('.rsk').forEach(function (rskElem) { rskElem.addEventListener('click', function (e) { rsk() }) }) if (navigator.canShare && navigator.canShare({ url: '' })) { document.getElementById('title').addEventListener('click', function () { navigator.share({ url: '' }); }) } 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) })