var starred = [] var knownStations = [] /** * @type {'unavailable' | 'notRequested' | 'waiting' | 'gotData'} */ var nearbyStatus = 'notRequested' var nearbyStations = [] function goToStation(stationId) { var url = new URL(window.location.href) url.pathname = 'view-station.html' url.searchParams.set('stationId', stationId) url.searchParams.set('date', new Date().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 createSuggestion(suggestion, index) { delete suggestion['products'] var suggestionDiv = document.createElement('div') suggestionDiv.classList.add('suggestion') var suggestionLi = document.createElement('li') suggestionDiv.appendChild(suggestionLi) suggestionLi.classList.add('items') if (index) { 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) var stationNameSpan = document.createElement('span') stationNameP.append(stationNameSpan) stationNameSpan.textContent = suggestion.name || suggestion.address stationNameSpan.classList.add('pri', 'stationName') if (suggestion.distance) { stationNameP.append(' ') var stationDistanceSpan = document.createElement('span') stationNameP.append(stationDistanceSpan) stationDistanceSpan.append('(', suggestion.distance.toString(), ' m)') stationDistanceSpan.classList.add('stationDistance') } if (window.localStorage) { var suggestionDistance = suggestion['distance'] delete suggestion['distance'] var suggestionLink = document.createElement('a') 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() suggestionDiv.appendChild(suggestionLink) suggestion['distance'] = suggestionDistance } // var trainCompanyP = document.createElement('p') // suggestionLi.appendChild(trainCompanyP) // trainCompanyP.textContent = suggestion.company // trainCompanyP.classList.add('thi') return suggestionDiv } 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) { suggestionsArea.append(createSuggestion(suggestion, index)) }) if (nearbyStatus !== 'unavailable') { suggestionsArea.appendChild(h4('Nearby stations')) if (nearbyStatus === 'notRequested') { suggestionsArea.appendChild(a('', 'Load nearby stations').event$('click', function (event) { event.preventDefault() var watchId = navigator.geolocation.watchPosition( function (data) { var geoUrl = new URL('https://v6.db.transport.rest/locations/nearby') geoUrl.searchParams.append('latitude', data.coords.latitude.toString()) geoUrl.searchParams.append('longitude', data.coords.longitude.toString()) geoUrl.searchParams.append('results', '10') fetch(geoUrl) .then(function (response) { return response.json() }) .then(function (data) { nearbyStatus = 'gotData' nearbyStations = data rebuildSuggestions() }) .catch(function () { nearbyStatus = 'unavailable' rebuildSuggestions() }) }, function (error) { if (nearbyStations.length === 0) { nearbyStatus = 'unavailable' rebuildSuggestions() } navigator.geolocation.clearWatch(watchId) }, { enableHighAccuracy: true, }, ) nearbyStatus = 'waiting' rebuildSuggestions() })) } else if (nearbyStatus === 'waiting') { var waitingP = document.createElement('p') suggestionsArea.append(waitingP) waitingP.append('Loading...') waitingP.classList.add('pri') } else if (nearbyStatus === 'gotData') { nearbyStations.forEach(function (suggestion, index) { suggestionsArea.appendChild(createSuggestion(suggestion, suggestions.length + index)) }) } } 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') 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() } }) } 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) { if (window.localStorage) { var maybeStarred = JSON.parse(localStorage.getItem('stations/starred')) if (maybeStarred) { starred = maybeStarred } } 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()) } }) 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() } }) reloadSuggestions() })