Skip to content

Instantly share code, notes, and snippets.

@gloriousDan
Last active February 8, 2022 16:22
Show Gist options
  • Save gloriousDan/15cdefbed9efa5c4a80f6dabf67162d4 to your computer and use it in GitHub Desktop.
Save gloriousDan/15cdefbed9efa5c4a80f6dabf67162d4 to your computer and use it in GitHub Desktop.
AVV departure list

AVV departure list

This is a simple static html site. It uses javascript to request departures of the AVV public transport service in Augsburg. It uses the same calls as the official frontend hosted at https://efa.avv-augsburg.de/avv/XSLT_TRIP_REQUEST2.

The main request (to get departure data) is a POST request to https://efa.avv-augsburg.de/avv/XSLT_DM_REQUEST. Since this endpoint doesn't have the access-control-allow-origin: * set, it is proxied through one of my servers. In addition to proxying I set the following headers:

  • access-control-allow-origin: * This could also be set to only allow the origin where this html file is hosted
  • content-type: text/html; charset=UTF-8 (Originally the charset is not set in the returned html which results in garbled text)

Set up such a CORS proxy yourself or use a public proxy and then replace the <proxy url> parts with the correct URL.

<html>
<header>
<meta charset="UTF-8"/>
</header>
<script>
(async function() {
await main();
}());
function processHtml(htmlText) {
const departureInformationStyle = "style=\"display: flex;/*! justify-content: space-evenly; */gap: 5px;padding-left: 10px;\""
return htmlText
.replace(/(<div class="brf"[\w\W\n]*)(?=<table)/, "") // remove unnecessary header
.replaceAll(/(?!<span class="mdv_dateChange">[0-9]*:[0-9]*)(\*)(?=<\/span>)/g, "") // remove star from time
.replaceAll(/<td class="dmTimeCol">[\w\W\n]*?<\/td>/g, "") // remove Schließen
.replaceAll(/src="images/g,"src=\"https://<proxy url/avv2/images")
.replaceAll(/<td class="laterTd">[\w\W\n]*?<\/td>/g, "") // remove "Später" Button
.replaceAll(/<div class="mdv_departureInformations">/g, `<div class="mdv_departureInformations" ${departureInformationStyle}>`)
.replaceAll(/<td class="partialHeaderText">/g, `<td class="partialHeaderText" style="font-size: x-large; padding-top: 10px;">`)
}
async function fireAvvRequest(pos) {
try {
const date = new Date()
/*
const getGeoLocation = () => new Promise((resolve, reject) => {
function positionSuccess(position) {
resolve(position);
}
function error() {
reject();
}
navigator.geolocation.getCurrentPosition(positionSuccess, error, { enableHighAccuracy: true });
});
try {
const geoLocation = await getGeoLocation()
console.log(geoLocation)
//pos = `coord:${geoLocation.coords.latitude}:${geoLocation.coords.longitude}:NAV4:Augsburg`
} catch (e) {
pos = place
}
*/
const body = [
["language","de"],
["deleteAssignedStops_dm","1"],
["trITMOTvalue100","5"],
["useProxFootSearch","0"],
["itdLPxx_today","12"],
["mode","direct"],
["lsShowTrainsExplicit","0"],
["type_dm","any"],
["name_dm",pos],
["includedMeans","checkbox"],
["inclMOT_0","1"],
["inclMOT_1","1"],
["inclMOT_2","1"],
["inclMOT_3","1"],
["inclMOT_4","1"],
["inclMOT_5","1"],
["inclMOT_6","1"],
["inclMOT_7","1"],
["inclMOT_8","1"],
["inclMOT_9","1"],
["inclMOT_10","1"],
["inclMOT_11","1"],
["commonMacro","true"],
["itdLPxx_depOnly","1"],
["itdDateDayMonthYear",[date.getDate() <= 9 ? "0" + date.getDate() : date.getDate(), date.getMonth() <= 9 ? "0" + (date.getMonth() + 1).toString(): date.getMonth() + 1 , date.getFullYear()].join(".")],
["maxAssignedStops","1"],
["hideBannerInfo","1"],
["execInst","normal"],
["limit","30"],
["itdTime",[date.getHours().toString(), date.getMinutes.toString()].join("")],
["useAllStops","1"],
].map(tuple => encodeURI(tuple.join("="))).join("&")
console.log(body)
const response = await fetch("https://<proxy url>/avv2/XSLT_DM_REQUEST", {
"credentials": "omit",
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Content-Type": "application/x-www-form-urlencoded",
},
"body": body,
"method": "POST",
"mode": "cors"
})
const htmlText = await response.text()
return processHtml(htmlText)
} catch (e) {
console.log(e.message)
return "<div> Error when fetching Data </div>"
}
}
async function main() {
const date = new Date()
const placeHome = "<insert location string (get it by inspecting the request on the official avv site>"
const placeSecond = "<insert second location string (get it by inspecting the request on the official avv site>"
const homeHtml = await fireAvvRequest(placeHome)
document.getElementById("contentHome").innerHTML = homeHtml
const secondHtml = await fireAvvRequest(placeSecond)
document.getElementById("contentSecond").innerHTML = secondHtml
document.getElementById("time").innerHTML = `<h1>Uhrzeit: ${date.getHours()}:${date.getMinutes() < 10 ?
["0", date.getMinutes()].join(""): date.getMinutes()}</h1>`
}
</script>
<body>
<div id="time"></div>
<div style="display: flex; gap: 20px; flex-wrap: wrap;">
<div id="contentHome"></div>
<div id="contentSecond"></div>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment