Compare commits
34 Commits
b426fb304e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ec0d9164e | |||
| 678d00a57b | |||
| 7b3e750513 | |||
| a216984bb4 | |||
| 7df84bca5c | |||
| 60ae28c9d3 | |||
| c0f50dfe3e | |||
| 5c89d26691 | |||
| efec9271c3 | |||
| 68301c44d4 | |||
| 6e3e3eb8e8 | |||
| b5b6431662 | |||
| fbaec27ed7 | |||
| 76febca006 | |||
| 48056a0380 | |||
| 33cd61e116 | |||
| d9fa338813 | |||
| 650bed8348 | |||
| b6f6f65c63 | |||
| 60c24d8a5e | |||
| 4efc000794 | |||
| 357fff97f2 | |||
| 0d16660d97 | |||
| 6789e044ed | |||
| 8cd52728c8 | |||
| 6c9bd60235 | |||
| bf884ac39c | |||
| d3d1aeecd8 | |||
| 83feb9af10 | |||
| d4d97d899d | |||
| 0c17dfc94f | |||
| a4c68ec801 | |||
| 2ecf1b59af | |||
| c2d96c6a98 |
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 marudor
|
||||
Copyright (c) 2020 clerie
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -4,14 +4,19 @@ Extends the booking portal of bahn.de with linking to useful information.
|
||||
## Installation
|
||||
### Aus Firefox Add-ons (Empfohlen)
|
||||
Einfach zu Firefox hinzufügen:
|
||||
TBD
|
||||
|
||||
https://addons.mozilla.org/firefox/addon/bahn-insight/
|
||||
|
||||
### Self-signed binarys
|
||||
Because Mozilla isn't as fast anymore at reviewing addons you can download the self-signed Versions here:
|
||||
|
||||
https://clerie.de/bahn-insight/
|
||||
|
||||
### Als Entwicklerversion
|
||||
1. Herunterladen
|
||||
2. Entpacken
|
||||
3. In Firefox nach `about:debugging` navigieren
|
||||
4. Add-on temporär laden
|
||||
5. YouTube aufrufen
|
||||
|
||||
Die Installation besteht nur während der aktuellen Session.
|
||||
|
||||
|
||||
@@ -6,52 +6,314 @@ function getMediaURL(path) {
|
||||
path = "media/" + path;
|
||||
return chrome.runtime.getURL(path) || browser.runtime.getURL(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return train name in format "PRODUCT TRAIN_NUMER"
|
||||
* i.e. "ICE 112", "RE 12734"
|
||||
* Sometimes train name is in format "PRODUCT LINE_NUMER (TRAIN_NUMER)"
|
||||
* i.e. "STB 12 (62371)"
|
||||
* this will be cleaned up
|
||||
* Sometimes 'trains' are not trains or we can't find an unique id for them
|
||||
* i.e. busses, ferrys, trams
|
||||
* they will also be cleaned up and returned as undefined, because we can't link to them correctly
|
||||
*/
|
||||
function bahnParseTrainName(dirty_train_name) {
|
||||
var name_list = dirty_train_name.split(" ");
|
||||
var name_list = dirty_train_name.trim().replace(/ +/g, ' ').split(" ");
|
||||
|
||||
// Train name in format "STB 12 (23561)"
|
||||
if(name_list.length == 3 && name_list[2].charAt(0) == '(' && name_list[2].charAt(name_list[2].length-1) == ')') {
|
||||
return name_list[0] + " " + name_list[2].substring(1, name_list[2].length-1);
|
||||
}
|
||||
|
||||
// Exclude linking to specific products
|
||||
if(["bus", "fäh", "str"].indexOf(name_list[0].toLowerCase()) !== -1) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return dirty_train_name;
|
||||
}
|
||||
|
||||
var observer = new MutationObserver((mutations, observer) => {
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.type === 'childList') {
|
||||
var target = mutation.target;
|
||||
if (target.tagName === 'TD') {
|
||||
var timetable = target.querySelector("td div.detailContainer table.result tbody");
|
||||
var products = timetable.querySelectorAll("tr.first td.products");
|
||||
products.forEach((product) => {
|
||||
var train_name = bahnParseTrainName(product.querySelector("span a").innerText);
|
||||
product.innerHTML = product.innerHTML + ' <a href="https://marudor.de/details/' + train_name + '" target="_blank"><img src="' + getMediaURL("marudor.svg") + '" style="height: 2em; vertical-align:middle;"></a>';
|
||||
});
|
||||
var stations = timetable.querySelectorAll("tr td.station");
|
||||
stations.forEach((station) => {
|
||||
var station_name = station.innerText;
|
||||
station.innerHTML = station.innerHTML + ' <a href="https://marudor.de/' + station_name + '" target="_blank"><img src="' + getMediaURL("marudor.svg") + '" style="height: 2em; vertical-align:middle;"></a>';
|
||||
/**
|
||||
* Returns date string in format YYYYMMDDHHMM
|
||||
*
|
||||
* @param datetime Date object
|
||||
*/
|
||||
function datetimeToYYYYMMDDHHMM(datetime) {
|
||||
return datetime.getFullYear().toString().padStart(4, '0') + "" + (datetime.getMonth()+1).toString().padStart(2, '0') + "" + datetime.getDate().toString().padStart(2, '0') + "" + datetime.getHours().toString().padStart(2, '0') + "" + datetime.getMinutes().toString().padStart(2, '0')
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML a as DOM object
|
||||
*
|
||||
* @param href URI as string
|
||||
*/
|
||||
function domCreateLink(href) {
|
||||
var link = document.createElement("a");
|
||||
link.setAttribute("href", href);
|
||||
return link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML img as DOM object
|
||||
*
|
||||
* @param src Image URI as string
|
||||
*/
|
||||
function domCreateImage(src) {
|
||||
var image = document.createElement("img");
|
||||
image.setAttribute("src", src);
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML br as DOM object
|
||||
*/
|
||||
function domCreateLinebreak() {
|
||||
var linebreak = document.createElement("br");
|
||||
return linebreak;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML span as DOM object
|
||||
*/
|
||||
function domCreateSpan() {
|
||||
var span = document.createElement("span");
|
||||
return span;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an 'image button' like construct as DOM object
|
||||
*
|
||||
* @param href URI as string
|
||||
* @param image_src Image URI as string
|
||||
*/
|
||||
function domCreateButton(href, image_src) {
|
||||
var link = domCreateLink(href);
|
||||
link.setAttribute("target", "_blank");
|
||||
var image = domCreateImage(image_src);
|
||||
image.setAttribute("style", "height: 16px; vertical-align:middle;");
|
||||
link.append(image);
|
||||
return link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an 'image buttom' to specifically link to marudor.de
|
||||
*
|
||||
* @param path URL path part after https://marudor.de
|
||||
*/
|
||||
function domCreateButtonMarudor(path) {
|
||||
var button = domCreateButton("https://marudor.de" + path, getMediaURL("marudor.svg"));
|
||||
button.setAttribute("title", "marudor.de");
|
||||
return button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an 'image buttom' to specifically link to dbf.finalrewind.org
|
||||
*
|
||||
* @param path URL path part after https://dbf.finalrewind.org
|
||||
*/
|
||||
function domCreateButtonDbf(path) {
|
||||
var button = domCreateButton("https://dbf.finalrewind.org" + path, getMediaURL("dbf.png"));
|
||||
button.setAttribute("title", "dbf.finalrewind.org");
|
||||
return button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a 'bahn-insight' element as DOM object
|
||||
*
|
||||
* Used to determine if this is an object set by this extension itself
|
||||
*/
|
||||
function domCreateBahnInsightField() {
|
||||
var span = domCreateSpan();
|
||||
span.setAttribute("class", "bahn-insight");
|
||||
return span;
|
||||
}
|
||||
|
||||
var connection_result_observer = new MutationObserver((mutations) => {
|
||||
log("change detected")
|
||||
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.type === 'childList') {
|
||||
var target = mutation.target;
|
||||
if (target.tagName === 'TD') {
|
||||
log("change is interesting");
|
||||
|
||||
var timetable = target.querySelector("td div.detailContainer table.result tbody");
|
||||
|
||||
// Just break if there are elements injected by us
|
||||
if(timetable.querySelectorAll(".bahn-insight").length != 0) {
|
||||
log("links already injected")
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* COLLECT DATA
|
||||
*/
|
||||
var data = [];
|
||||
var relations = timetable.querySelectorAll("tr.first");
|
||||
relations.forEach((relation, i) => {
|
||||
var relationend = relation.nextElementSibling;
|
||||
while(relationend.querySelector("td.station") == null) {
|
||||
relationend = relationend.nextElementSibling;
|
||||
}
|
||||
|
||||
data[i] = {};
|
||||
data[i]["from"] = {}
|
||||
data[i]["to"] = {}
|
||||
|
||||
// Depature station name
|
||||
var relation_from = relation.querySelector("td.station");
|
||||
data[i]["from"]["station"] = relation_from.innerText;
|
||||
|
||||
// Arrival station name
|
||||
var relation_to = relationend.querySelector("td.station");
|
||||
data[i]["to"]["station"] = relation_to.innerText;
|
||||
|
||||
// Departure time & current estimation
|
||||
var relation_departure = relation.querySelector("td.time");
|
||||
var relation_departure_list = relation_departure.firstChild.textContent.trim().split(" ");
|
||||
data[i]["from"]["time"] = relation_departure_list[1];
|
||||
data[i]["from"]["time_current"] = null;
|
||||
var relation_departure_current = relation_departure.querySelector("span.delay, span.delayOnTime");
|
||||
if(relation_departure_current != null) {
|
||||
data[i]["from"]["time_current"] = relation_departure_current.innerText.trim();
|
||||
}
|
||||
|
||||
// Arrival time & current estimation
|
||||
var relation_arrival = relationend.querySelector("td.time");
|
||||
var relation_arrival_list = relation_arrival.firstChild.textContent.trim().split(" ");
|
||||
data[i]["to"]["time"] = relation_arrival_list[1];
|
||||
data[i]["to"]["time_current"] = null;
|
||||
var relation_arrival_current = relation_arrival.querySelector("span.delay, span.delayOnTime");
|
||||
if(relation_arrival_current != null) {
|
||||
data[i]["to"]["time_current"] = relation_arrival_current.innerText.trim();
|
||||
}
|
||||
|
||||
// Departure platform
|
||||
var relation_platform = relation.querySelector("td.platform");
|
||||
data[i]["from"]["platform"] = relation_platform.innerText;
|
||||
|
||||
// Arrival platform
|
||||
var relation_to_platform = relationend.querySelector("td.platform");
|
||||
data[i]["to"]["platform"] = relation_to_platform.innerText;
|
||||
|
||||
// Travel products
|
||||
var relation_products = relation.querySelector("td.products");
|
||||
data[i]["products"] = [];
|
||||
|
||||
// Fetch all products for this travel
|
||||
var relation_trains = relation_products.querySelectorAll("span a");
|
||||
relation_trains.forEach((train, j) => {
|
||||
data[i]["products"][j] = {};
|
||||
|
||||
// Prodcut details
|
||||
data[i]["products"][j]["type"] = null;
|
||||
data[i]["products"][j]["train_number"] = null;
|
||||
data[i]["products"][j]["train_name"] = null;
|
||||
data[i]["products"][j]["line_name"] = null;
|
||||
|
||||
var name_list = train.innerText.trim().replace(/ +/g, ' ').split(" ");
|
||||
|
||||
// Product name in format "STB 12 (23561)"
|
||||
if(name_list.length == 3 && name_list[2].charAt(0) == '(' && name_list[2].charAt(name_list[2].length-1) == ')') {
|
||||
data[i]["products"][j]["type"] = name_list[0];
|
||||
data[i]["products"][j]["train_number"] = name_list[2].substring(1, name_list[2].length-1)
|
||||
data[i]["products"][j]["line_name"] = name_list[0] + " " + name_list[1];
|
||||
}
|
||||
// Product name in format "ICE 234"
|
||||
else {
|
||||
data[i]["products"][j]["type"] = name_list[0];
|
||||
data[i]["products"][j]["train_number"] = name_list[1];
|
||||
}
|
||||
data[i]["products"][j]["train_name"] = data[i]["products"][j]["type"] + " " + data[i]["products"][j]["train_number"];
|
||||
});
|
||||
|
||||
}
|
||||
// Travel information
|
||||
var relation_items = relation.querySelectorAll("td");
|
||||
var relation_info = relation_items[relation_items.length-1];
|
||||
data[i]["info"] = relation_info.innerText;
|
||||
|
||||
});
|
||||
|
||||
log("data collected");
|
||||
|
||||
console.log(data);
|
||||
|
||||
/*
|
||||
* INJECT CUSTOM UI
|
||||
*/
|
||||
|
||||
var relations = timetable.querySelectorAll("tr.first");
|
||||
relations.forEach((relation, i) => {
|
||||
var relationend = relation.nextElementSibling;
|
||||
while(relationend.querySelector("td.station") == null) {
|
||||
relationend = relationend.nextElementSibling;
|
||||
}
|
||||
|
||||
var products = relation.querySelectorAll("td.products span a");
|
||||
products.forEach((product, j) => {
|
||||
// Field for 'bahn-insight' stuff
|
||||
var bahn_insight_field = domCreateBahnInsightField();
|
||||
product.after(bahn_insight_field);
|
||||
|
||||
var departure_time = new Date(Date.parse(connection_result_date + " " + data[i]["from"]["time"]));
|
||||
|
||||
// Button linking to marudor.de
|
||||
var marudor_button = domCreateButtonMarudor("/details/" + data[i]["products"][j]["train_name"] + "/" + departure_time.getTime());
|
||||
bahn_insight_field.appendChild(marudor_button);
|
||||
|
||||
var dbf_button = domCreateButtonDbf("/_wr/" + data[i]["products"][j]["train_number"] + "/" + datetimeToYYYYMMDDHHMM(departure_time));
|
||||
bahn_insight_field.appendChild(dbf_button);
|
||||
|
||||
// Move linebreaks from link inner, after our 'bahn-insight' field
|
||||
var linebreaks = product.querySelectorAll("br");
|
||||
if(linebreaks.length != 0) {
|
||||
linebreaks.forEach((linebreak) => {
|
||||
product.removeChild(linebreak);
|
||||
});
|
||||
bahn_insight_field.after(domCreateLinebreak());
|
||||
}
|
||||
});
|
||||
|
||||
// Linking to station
|
||||
var from = relation.querySelector("td.station");
|
||||
var bahn_insight_field = domCreateBahnInsightField();
|
||||
from.appendChild(bahn_insight_field);
|
||||
|
||||
var marudor_button = domCreateButtonMarudor("/" + data[i]["from"]["station"]);
|
||||
bahn_insight_field.appendChild(marudor_button);
|
||||
|
||||
var dbf_button = domCreateButtonDbf("/" + data[i]["from"]["station"]);
|
||||
bahn_insight_field.appendChild(dbf_button);
|
||||
|
||||
// Linking to station
|
||||
var to = relationend.querySelector("td.station");
|
||||
var bahn_insight_field = domCreateBahnInsightField();
|
||||
to.appendChild(bahn_insight_field);
|
||||
|
||||
var marudor_button = domCreateButtonMarudor("/" + data[i]["to"]["station"]);
|
||||
bahn_insight_field.appendChild(marudor_button);
|
||||
|
||||
var dbf_button = domCreateButtonDbf("/" + data[i]["to"]["station"]);
|
||||
bahn_insight_field.appendChild(dbf_button);
|
||||
});
|
||||
|
||||
log("ui injected");
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
var target = document.getElementById('resultsOverview');
|
||||
|
||||
if(typeof target !== 'undefined') {
|
||||
observer.observe(target, {
|
||||
|
||||
var connection_result_date = document.querySelector("html body div div div.resultContentHolder form div h2 span").innerText.replace(/(\d{2})\.(\d{2})\.(\d{2})/,'20$3-$2-$1');
|
||||
|
||||
connection_result_observer.observe(target, {
|
||||
subtree: true,
|
||||
childList: true
|
||||
});
|
||||
|
||||
log("observation started")
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
"manifest_version": 2,
|
||||
"name": "Bahn Insight",
|
||||
"version": "0.2",
|
||||
"version": "0.7",
|
||||
|
||||
"description": "Extends the booking portal of bahn.de with linking to useful information.",
|
||||
"homepage_url": "https://git.clerie.de/clerie/bahn-insight/",
|
||||
|
||||
BIN
media/dbf.png
Normal file
BIN
media/dbf.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 652 B |
Reference in New Issue
Block a user