Add favorites + nearby stations to stations.html
This commit is contained in:
		
							parent
							
								
									e0ad2fa46c
								
							
						
					
					
						commit
						42352a740f
					
				
					 4 changed files with 197 additions and 65 deletions
				
			
		
							
								
								
									
										31
									
								
								base.css
									
										
									
									
									
								
							
							
						
						
									
										31
									
								
								base.css
									
										
									
									
									
								
							| 
						 | 
					@ -351,6 +351,37 @@ pre {
 | 
				
			||||||
	border-bottom-right-radius: 5%;
 | 
						border-bottom-right-radius: 5%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.suggestion {
 | 
				
			||||||
 | 
						display: flex;
 | 
				
			||||||
 | 
						flex-direction: row;
 | 
				
			||||||
 | 
						align-items: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.suggestion :first-child {
 | 
				
			||||||
 | 
						flex: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.suggestion .star {
 | 
				
			||||||
 | 
						flex: 0;
 | 
				
			||||||
 | 
						pointer-events: none;
 | 
				
			||||||
 | 
						width: 24px;
 | 
				
			||||||
 | 
						height: 24px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.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%);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.product-suburban {
 | 
					.product-suburban {
 | 
				
			||||||
	color: green !important;
 | 
						color: green !important;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -115,34 +115,3 @@ div.checkbox p {
 | 
				
			||||||
div.checkbox input {
 | 
					div.checkbox input {
 | 
				
			||||||
    flex: 0;
 | 
					    flex: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
.suggestion {
 | 
					 | 
				
			||||||
    display: flex;
 | 
					 | 
				
			||||||
    flex-direction: row;
 | 
					 | 
				
			||||||
    align-items: center;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.suggestion :first-child {
 | 
					 | 
				
			||||||
    flex: 1;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.suggestion .star {
 | 
					 | 
				
			||||||
    flex: 0;
 | 
					 | 
				
			||||||
    pointer-events: none;
 | 
					 | 
				
			||||||
    width: 24px;
 | 
					 | 
				
			||||||
    height: 24px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.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%);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,6 +15,7 @@
 | 
				
			||||||
	<script src="/common/worker.js"></script>
 | 
						<script src="/common/worker.js"></script>
 | 
				
			||||||
	<script defer src="/common/back.js"></script>
 | 
						<script defer src="/common/back.js"></script>
 | 
				
			||||||
	<script defer src="/common/items.js"></script>
 | 
						<script defer src="/common/items.js"></script>
 | 
				
			||||||
 | 
						<script defer src="/common/components.js"></script>
 | 
				
			||||||
	<script defer src="station.js"></script>
 | 
						<script defer src="station.js"></script>
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										199
									
								
								station.js
									
										
									
									
									
								
							
							
						
						
									
										199
									
								
								station.js
									
										
									
									
									
								
							| 
						 | 
					@ -1,5 +1,12 @@
 | 
				
			||||||
 | 
					var starred = []
 | 
				
			||||||
var knownStations = []
 | 
					var knownStations = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @type {'unavailable' | 'notRequested' | 'waiting' | 'gotData'}
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					var nearbyStatus = 'notRequested'
 | 
				
			||||||
 | 
					var nearbyStations = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function goToStation(stationId) {
 | 
					function goToStation(stationId) {
 | 
				
			||||||
	var url = new URL(window.location.href)
 | 
						var url = new URL(window.location.href)
 | 
				
			||||||
	url.pathname = 'view-station.html'
 | 
						url.pathname = 'view-station.html'
 | 
				
			||||||
| 
						 | 
					@ -22,6 +29,94 @@ var focusedElement = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _rebuildDebounce = null
 | 
					var _rebuildDebounce = null
 | 
				
			||||||
var _rebuildRequested = false
 | 
					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() {
 | 
					function rebuildSuggestions() {
 | 
				
			||||||
	if (_rebuildDebounce !== null) {
 | 
						if (_rebuildDebounce !== null) {
 | 
				
			||||||
		_rebuildRequested = true
 | 
							_rebuildRequested = true
 | 
				
			||||||
| 
						 | 
					@ -37,43 +132,67 @@ function rebuildSuggestions() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var suggestions = knownStations.slice()
 | 
						var suggestions = knownStations.slice()
 | 
				
			||||||
 | 
						if (suggestions.length === 0) {
 | 
				
			||||||
 | 
							suggestions = starred.map(function (s) { return JSON.parse(s) })
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	suggestions.forEach(function (suggestion, index) {
 | 
						suggestions.forEach(function (suggestion, index) {
 | 
				
			||||||
		var suggestionLi = document.createElement('li')
 | 
							suggestionsArea.append(createSuggestion(suggestion, index))
 | 
				
			||||||
		suggestionsArea.appendChild(suggestionLi)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		setTimeout(function () {
 | 
					 | 
				
			||||||
			suggestionLi.classList.add('items')
 | 
					 | 
				
			||||||
			suggestionLi.tabIndex = index + 1
 | 
					 | 
				
			||||||
			suggestionLi.style.padding = '2px 0'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			function onAction(e) {
 | 
					 | 
				
			||||||
				goToStation(suggestion.id)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			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
 | 
					 | 
				
			||||||
			stationNameP.classList.add('pri', 'stationName')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// var trainCompanyP = document.createElement('p')
 | 
					 | 
				
			||||||
			// suggestionLi.appendChild(trainCompanyP)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// trainCompanyP.textContent = suggestion.company
 | 
					 | 
				
			||||||
			// trainCompanyP.classList.add('thi')
 | 
					 | 
				
			||||||
		}, 0)
 | 
					 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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 () {
 | 
						setTimeout(function () {
 | 
				
			||||||
		_rebuildDebounce = null
 | 
							_rebuildDebounce = null
 | 
				
			||||||
		if (_rebuildRequested) {
 | 
							if (_rebuildRequested) {
 | 
				
			||||||
| 
						 | 
					@ -97,7 +216,12 @@ function reloadSuggestions() {
 | 
				
			||||||
	fetchAbortController = new AbortController()
 | 
						fetchAbortController = new AbortController()
 | 
				
			||||||
	fetch(locationsUrl.toString(), { signal: fetchAbortController.signal })
 | 
						fetch(locationsUrl.toString(), { signal: fetchAbortController.signal })
 | 
				
			||||||
		.then(function (response) {
 | 
							.then(function (response) {
 | 
				
			||||||
			return response.json()
 | 
								if (response.ok) {
 | 
				
			||||||
 | 
									return response.json()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								else {
 | 
				
			||||||
 | 
									return {}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		.then(function (data) {
 | 
							.then(function (data) {
 | 
				
			||||||
			if (data) {
 | 
								if (data) {
 | 
				
			||||||
| 
						 | 
					@ -125,6 +249,13 @@ function csk() {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.addEventListener('load', function (e) {
 | 
					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')
 | 
						var stationName = document.getElementById('stationName')
 | 
				
			||||||
	stationName.addEventListener('input', function (e) {
 | 
						stationName.addEventListener('input', function (e) {
 | 
				
			||||||
		reloadSuggestions()
 | 
							reloadSuggestions()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue