Init project
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
node_modules
|
45
app.js
Normal file
45
app.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import express from 'express';
|
||||||
|
import {createClient} from 'db-vendo-client/index.js';
|
||||||
|
import {profile as dbnavProfile} from 'db-vendo-client/p/dbnav/index.js';
|
||||||
|
import {mapRouteParsers} from 'db-vendo-client/lib/api-parsers.js';
|
||||||
|
import {createHafasRestApi as createApi} from 'hafas-rest-api';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
hostname: process.env.HOSTNAME || 'localhost',
|
||||||
|
port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
|
||||||
|
name: 'db-vendo-client',
|
||||||
|
description: 'db-vendo-client',
|
||||||
|
homepage: 'https://github.com/public-transport/db-vendo-client',
|
||||||
|
version: '6',
|
||||||
|
docsLink: 'https://github.com/public-transport/db-vendo-client',
|
||||||
|
openapiSpec: true,
|
||||||
|
logging: true,
|
||||||
|
aboutPage: true,
|
||||||
|
enrichStations: true,
|
||||||
|
etags: 'strong',
|
||||||
|
csp: 'default-src \'none\'; style-src \'self\' \'unsafe-inline\'; img-src https:',
|
||||||
|
mapRouteParsers,
|
||||||
|
};
|
||||||
|
|
||||||
|
const start = async () => {
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
const vendo = createClient(
|
||||||
|
dbnavProfile,
|
||||||
|
'traveldrafter',
|
||||||
|
config,
|
||||||
|
);
|
||||||
|
const api = await createApi(vendo, config);
|
||||||
|
|
||||||
|
app.use("/api", api);
|
||||||
|
app.use('/web', express.static('web'));
|
||||||
|
|
||||||
|
app.listen(config.port, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
start();
|
2164
package-lock.json
generated
Normal file
2164
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
package.json
Normal file
18
package.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "traveldrafter",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node app.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "clerie",
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"description": "",
|
||||||
|
"type": "module",
|
||||||
|
"dependencies": {
|
||||||
|
"db-vendo-client": "^6.8.2",
|
||||||
|
"express": "^5.1.0",
|
||||||
|
"hafas-rest-api": "^5.1.3"
|
||||||
|
}
|
||||||
|
}
|
28
web/api.js
Normal file
28
web/api.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
export function fetchApi(pathcomponents, query) {
|
||||||
|
query.pretty = true;
|
||||||
|
let url = '/api/' + pathcomponents.join("/") + "?" + new URLSearchParams(query).toString();
|
||||||
|
return fetch(url).then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Fetching api failed");
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}).then(response => response.json());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchLocations(query) {
|
||||||
|
return fetchApi(["locations"], {
|
||||||
|
query: query,
|
||||||
|
addresses: false,
|
||||||
|
poi: false,
|
||||||
|
subStop: false,
|
||||||
|
entrances: false,
|
||||||
|
linesOfStops: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchJourneys(from_, to) {
|
||||||
|
return fetchApi(["journeys"], {
|
||||||
|
from: from_,
|
||||||
|
to: to,
|
||||||
|
});
|
||||||
|
}
|
26
web/index.html
Normal file
26
web/index.html
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="style.css" />
|
||||||
|
<script type="module" src="traveldrafter.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="journey-search-from">Select…</div>
|
||||||
|
<div id="journey-search-to">Select…</div>
|
||||||
|
<div id="journey-search-submit">Search</div>
|
||||||
|
<div id="journey-search-result"></div>
|
||||||
|
|
||||||
|
<div id="locations-search" class="popup">
|
||||||
|
<div class="popup-close">×</div>
|
||||||
|
<div id="locations-search-content" class="popup-content">
|
||||||
|
<div class="container">
|
||||||
|
<input id="locations-search-query" class="form-control" type="text" />
|
||||||
|
<div id="locations-search-response"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
39
web/journeys-search.js
Normal file
39
web/journeys-search.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { fetchJourneys } from './api.js';
|
||||||
|
import { attachLocationsSearch } from './locations-search.js';
|
||||||
|
|
||||||
|
let element_from = document.querySelector("#journey-search-from");
|
||||||
|
let element_to = document.querySelector("#journey-search-to");
|
||||||
|
let element_submit = document.querySelector("#journey-search-submit");
|
||||||
|
let element_result = document.querySelector("#journey-search-result");
|
||||||
|
|
||||||
|
export function setupJourneysSearch() {
|
||||||
|
attachLocationsSearch(element_from);
|
||||||
|
attachLocationsSearch(element_to);
|
||||||
|
|
||||||
|
element_submit.addEventListener("click", event => {
|
||||||
|
element_result.innerText = "Loading…";
|
||||||
|
fetchJourneys(element_from.dataset.locationId, element_to.dataset.locationId).then(result => {
|
||||||
|
for (let journey of result.journeys) {
|
||||||
|
element_result.appendChild(createJourneyElement(journey));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createJourneyElement(journey) {
|
||||||
|
let el = document.createElement("div");
|
||||||
|
|
||||||
|
for (let leg of journey.legs) {
|
||||||
|
el.appendChild(createJourneyLegElement(leg));
|
||||||
|
}
|
||||||
|
|
||||||
|
return el;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function createJourneyLegElement(leg) {
|
||||||
|
let el = document.createElement("div");
|
||||||
|
el.innerText = JSON.stringify(leg);
|
||||||
|
el.innerText = leg?.line?.name + ": " + leg.origin.name + " > " + leg.destination.name;
|
||||||
|
return el;
|
||||||
|
}
|
36
web/locations-search.js
Normal file
36
web/locations-search.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { fetchLocations } from './api.js';
|
||||||
|
|
||||||
|
let element_locations_search = document.querySelector("#locations-search");
|
||||||
|
let element_query = document.querySelector("#locations-search-query");
|
||||||
|
let element_response = document.querySelector("#locations-search-response");
|
||||||
|
|
||||||
|
export function setupLocationsSearch() {
|
||||||
|
element_query.addEventListener("change", (event) => {
|
||||||
|
element_response.innerText = "Loading…";
|
||||||
|
fetchLocations(event.target.value).then(result => {
|
||||||
|
element_response.innerText = "";
|
||||||
|
result.forEach(lr => {
|
||||||
|
let location_element = document.createElement("div");
|
||||||
|
location_element.innerText = lr.name;
|
||||||
|
location_element.dataset.locationId = lr.id;
|
||||||
|
|
||||||
|
location_element.addEventListener("click", event => {
|
||||||
|
console.log(event.target.dataset.locationId);
|
||||||
|
element_locations_search.locationSelectedCallback(event.target.innerText, event.target.dataset.locationId);
|
||||||
|
element_locations_search.style.display = "none";
|
||||||
|
});
|
||||||
|
element_response.appendChild(location_element);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function attachLocationsSearch(search_element) {
|
||||||
|
search_element.addEventListener("click", event => {
|
||||||
|
element_locations_search.locationSelectedCallback = (location_name, location_id) => {
|
||||||
|
search_element.innerText = location_name;
|
||||||
|
search_element.dataset.locationId = location_id;
|
||||||
|
};
|
||||||
|
element_locations_search.style.display = "block";
|
||||||
|
});
|
||||||
|
}
|
7
web/popup.js
Normal file
7
web/popup.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export function setupPopups() {
|
||||||
|
document.querySelectorAll(".popup-close").forEach(element => {
|
||||||
|
element.addEventListener("click", event => {
|
||||||
|
event.target.parentElement.style.display="none";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
54
web/style.css
Normal file
54
web/style.css
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
* {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
|
max-width: 768px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup {
|
||||||
|
position: fixed;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
z-index: 10000;
|
||||||
|
|
||||||
|
background-color: rgba(0, 0, 0, 0.9);
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup .popup-close {
|
||||||
|
margin: 60px;
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: 60px;
|
||||||
|
width: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup .popup-content {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup input {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.form-control {
|
||||||
|
width: 100%;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
10
web/traveldrafter.js
Normal file
10
web/traveldrafter.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import * as Api from './api.js';
|
||||||
|
import { setupPopups } from "./popup.js";
|
||||||
|
import { setupLocationsSearch } from './locations-search.js';
|
||||||
|
import { setupJourneysSearch } from './journeys-search.js';
|
||||||
|
|
||||||
|
window.Api = Api;
|
||||||
|
|
||||||
|
setupPopups();
|
||||||
|
setupLocationsSearch();
|
||||||
|
setupJourneysSearch();
|
Reference in New Issue
Block a user