ctucx.git: trainsearch

web based trip-planner, fork of https://cyberchaos.dev/yuka/trainsearch

commit 1c720049d6c588ae55dc29667b32b015523bc16b
parent 7e07d4d7f2171dee6b6f84adbb51fc85f0ed02f8
Author: Katja (ctucx) <git@ctu.cx>
Date: Thu, 23 Jan 2025 14:15:45 +0100

move formatters to own file
10 files changed, 159 insertions(+), 148 deletions(-)
M
src/app_functions.js
|
23
+++--------------------
M
src/canvas.js
|
5
+++--
M
src/departuresView.js
|
5
+++--
A
src/formatters.js
|
118
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M
src/helpers.js
|
96
-------------------------------------------------------------------------------
M
src/journeyView.js
|
17
+++++++++--------
M
src/journeysView.js
|
9
+++++----
M
src/searchView.js
|
15
++++++++-------
M
src/templates.js
|
2
+-
M
src/tripView.js
|
17
+++++++++--------
diff --git a/src/app_functions.js b/src/app_functions.js
@@ -1,10 +1,11 @@
+import { html } from 'lit-html';
 import { db } from './dataStorage.js';
 import { go } from './router.js';
 import { settings, subscribeSettings } from './settings.js';
 import { showLoader, hideOverlay, showModal, showAlertModal } from './overlays.js';
 import { languages } from './languages.js';
-import { html } from 'lit-html';
-import { formatDateTime, getFrom, getTo } from './helpers.js';
+import { getFrom, getTo } from './helpers.js';
+import { formatDateTime } from './formatters.js';
 import { getHafasClient, client } from './hafas_client';
 import { trainsearchToHafas, hafasToTrainsearch } from './refresh_token';
 

@@ -194,24 +195,6 @@ export const t = (key, ...params) => {
 	return translation;
 };
 
-export const parseName = (point) => {
-	let nameHTML = '';
-
-	if (point.type === 'stop' || point.type === 'station') {
-		nameHTML += point.name+ds100Names(point.id);
-	} else if (point.type == 'location') {
-		if (point.name) {
-			nameHTML += point.name;
-		} else if (point.address) {
-			nameHTML += point.address;
-		}
-	} else {
-		return '';
-	}
-
-	return nameHTML;
-};
-
 export const ds100Names = (id) => {
 	if (!settings.showDS100) return '';
 	if (!ds100[Number(id)])  return '';
diff --git a/src/canvas.js b/src/canvas.js
@@ -1,6 +1,7 @@
 import { moreJourneys } from './journeysView.js';
 import { go } from './router.js';
-import { padZeros, formatTrainTypes, lineDisplayName } from './helpers.js';
+import { padZeros } from './helpers.js';
+import { formatTrainTypes, formatLineDisplayName } from './formatters'
 import { cachedCoachSequence, coachSequenceCache, coachSequenceCacheKey } from './reihung';
 
 const formatTime = (date) => {

@@ -276,7 +277,7 @@ const renderJourneys = () => {
 			}
 			//ctx.shadowBlur = 0;
 
-			let preRenderedText = getTextCache(lineDisplayName(leg.line), colorFor(leg, 'text'));
+			let preRenderedText = getTextCache(formatLineDisplayName(leg.line), colorFor(leg, 'text'));
 			let offset = duration / 2;
 			if ((offset + preRenderedText.height / dpr) < duration - 5) {
 				ctx.scale(1 / dpr, 1 / dpr);
diff --git a/src/departuresView.js b/src/departuresView.js
@@ -1,7 +1,8 @@
 import { settings } from './settings.js';
 import { platformTemplate, timeTemplate } from './templates.js';
-import { showDiv, hideDiv, ElementById, formatDateTime, formatDuration, formatPrice } from './helpers.js';
-import { parseName, t, processLeg } from './app_functions.js';
+import { showDiv, hideDiv, ElementById } from './helpers.js';
+import { t, processLeg } from './app_functions.js';
+import { formatDateTime, formatDuration, formatPrice } from './formatters.js';
 import { showAlertModal, showLoader, hideOverlay, showModal } from './overlays.js';
 import { go } from './router.js';
 import { html, render } from 'lit-html';
diff --git a/src/formatters.js b/src/formatters.js
@@ -0,0 +1,118 @@
+import { ds100Names } from './app_functions.js';
+import { padZeros } from './helpers.js';
+import { languages } from './languages.js';
+
+export const formatName = (point) => {
+	let nameHTML = '';
+
+	if (point.type === 'stop' || point.type === 'station') {
+		nameHTML += point.name+ds100Names(point.id);
+	} else if (point.type == 'location') {
+		if (point.name) {
+			nameHTML += point.name;
+		} else if (point.address) {
+			nameHTML += point.address;
+		}
+	} else {
+		return '';
+	}
+
+	return nameHTML;
+};
+
+export const formatDateTime = (date, format) => {
+	if (format != null) {
+		switch (format) {
+		case 'full':
+			return padZeros(date.getHours()) + ':' + padZeros(date.getMinutes()) + ', ' + date.getDate() + '.' + (date.getMonth() + 1) + '.' + date.getFullYear();
+			break;
+
+		case 'date':
+			return date.getDate() + '.' + (date.getMonth() + 1) + '.' + date.getFullYear();
+			break;
+
+		default:
+			return false;
+			break;
+		}
+
+	}
+
+	if (date.toLocaleDateString() !== new Date().toLocaleDateString()) {
+		return padZeros(date.getHours()) + ':' + padZeros(date.getMinutes()) + ', ' + date.getDate() + '.' + (date.getMonth() + 1) + '.';
+	} else {
+		return padZeros(date.getHours()) + ':' + padZeros(date.getMinutes());
+	}
+};
+
+export const formatDuration = (duration) => {
+	const mins = duration / 60000;
+	const h = Math.floor(mins / 60);
+	const m = mins % 60;
+
+	if (h > 0) {
+		return h+'h '+m+'min';
+	}
+
+	return m+'min';
+};
+
+export const formatFromTo = obj => {
+	if (obj.type === 'stop' || obj.type === 'station')
+		return obj.id;
+	else if (obj.address)
+		return {
+			latitude: obj.latitude,
+			longitude: obj.longitude,
+			address: obj.address,
+		};
+	else
+		return {
+			id: obj.id,
+			latitude: obj.latitude,
+			longitude: obj.longitude,
+		};
+};
+
+export const formatPrice = price => {
+	if (!price) return '-';
+	const currencies = { USD: '$', EUR: '€', GBP: '£' };
+	let ret = currencies[price.currency] || price.currency;
+	ret += Math.floor(price.amount);
+	ret += '.';
+	ret += padZeros(price.amount * 100 % 100, 2);
+	return ret;
+};
+
+export const formatTrainTypes = info => {
+	const counts = {};
+	for (let group of info.sequence?.groups) {
+		const name = group.baureihe?.name;
+		if (!name) continue;
+		counts[name] = (counts[name] ? counts[name] : 0) + 1;
+	}
+	return Object.entries(counts).map(([name, count]) => {
+		let text = "";
+		if (count > 1) {
+			text += `${count} x `;
+		}
+		text += name;
+		while (text.length < 12) {
+			text = ' ' + text + ' ';
+		}
+		return text;
+	}).join(" + ");
+};
+
+export const formatLineAdditionalName = (line) => {
+	if (!line.name) return null;
+	const splitName = line.name.split(' ');
+	if (splitName.length === 2 && line.fahrtNr && line.fahrtNr != splitName[1])
+		return `${splitName[0]} ${line.fahrtNr}`;
+	else
+		return null;
+};
+
+export const formatLineDisplayName = (line) => {
+	return line?.name || line?.operator?.name || "???";
+};
diff --git a/src/helpers.js b/src/helpers.js
@@ -38,99 +38,3 @@ export const getFrom = journeys => {
 export const getTo = journeys => {
 	return journeys[0].legs[journeys[0].legs.length-1].destination;
 };
-
-export const formatDateTime = (date, format) => {
-	if (format != null) {
-		switch (format) {
-		case 'full':
-			return padZeros(date.getHours()) + ':' + padZeros(date.getMinutes()) + ', ' + date.getDate() + '.' + (date.getMonth() + 1) + '.' + date.getFullYear();
-			break;
-
-		case 'date':
-			return date.getDate() + '.' + (date.getMonth() + 1) + '.' + date.getFullYear();
-			break;
-
-		default:
-			return false;
-			break;
-		}
-
-	}
-
-	if (date.toLocaleDateString() !== new Date().toLocaleDateString()) {
-		return padZeros(date.getHours()) + ':' + padZeros(date.getMinutes()) + ', ' + date.getDate() + '.' + (date.getMonth() + 1) + '.';
-	} else {
-		return padZeros(date.getHours()) + ':' + padZeros(date.getMinutes());
-	}
-};
-
-export const formatDuration = (duration) => {
-	const mins = duration / 60000;
-	const h = Math.floor(mins / 60);
-	const m = mins % 60;
-
-	if (h > 0) {
-		return h+'h '+m+'min';
-	}
-
-	return m+'min';
-};
-
-export const formatFromTo = obj => {
-	if (obj.type === 'stop' || obj.type === 'station')
-		return obj.id;
-	else if (obj.address)
-		return {
-			latitude: obj.latitude,
-			longitude: obj.longitude,
-			address: obj.address,
-		};
-	else
-		return {
-			id: obj.id,
-			latitude: obj.latitude,
-			longitude: obj.longitude,
-		};
-};
-
-export const formatPrice = price => {
-	if (!price) return '-';
-	const currencies = { USD: '$', EUR: '€', GBP: '£' };
-	let ret = currencies[price.currency] || price.currency;
-	ret += Math.floor(price.amount);
-	ret += '.';
-	ret += padZeros(price.amount * 100 % 100, 2);
-	return ret;
-};
-
-export const formatTrainTypes = info => {
-	const counts = {};
-	for (let group of info.sequence?.groups) {
-		const name = group.baureihe?.name;
-		if (!name) continue;
-		counts[name] = (counts[name] ? counts[name] : 0) + 1;
-	}
-	return Object.entries(counts).map(([name, count]) => {
-		let text = "";
-		if (count > 1) {
-			text += `${count} x `;
-		}
-		text += name;
-		while (text.length < 12) {
-			text = ' ' + text + ' ';
-		}
-		return text;
-	}).join(" + ");
-};
-
-export const lineAdditionalName = (line) => {
-	if (!line.name) return null;
-	const splitName = line.name.split(' ');
-	if (splitName.length === 2 && line.fahrtNr && line.fahrtNr != splitName[1])
-		return `${splitName[0]} ${line.fahrtNr}`;
-	else
-		return null;
-};
-export const lineDisplayName = (line) => {
-	return line?.name || line?.operator?.name || "???";
-};
diff --git a/src/journeyView.js b/src/journeyView.js
@@ -1,8 +1,9 @@
 import { cachedCoachSequence } from './reihung';
 import { settings } from './settings.js';
 import { remarksTemplate, platformTemplate, stopTemplate, timeTemplate } from './templates.js';
-import { showDiv, hideDiv, ElementById, formatDateTime, formatDuration, formatPrice, formatTrainTypes, lineAdditionalName, lineDisplayName } from './helpers.js';
-import { parseName, t, getJourney, refreshJourney } from './app_functions.js';
+import { showDiv, hideDiv, ElementById } from './helpers.js';
+import { t, getJourney, refreshJourney } from './app_functions.js';
+import { formatName, formatDateTime, formatDuration, formatPrice, formatTrainTypes, formatLineAdditionalName, formatLineDisplayName } from './formatters.js';
 import { showAlertModal, showLoader, hideOverlay, showModal } from './overlays.js';
 import { go } from './router.js';
 import { html, render } from 'lit-html';

@@ -17,9 +18,9 @@ const legTemplate = (leg, profile) => {
 
 	return html`
 		${leg.walking ? html`
-			<p class="walk">${t(leg.distance === null ? 'walkinfo' : 'walkinfo_meters', parseName(leg.destination), leg.distance)}</p>
+			<p class="walk">${t(leg.distance === null ? 'walkinfo' : 'walkinfo_meters', formatName(leg.destination), leg.distance)}</p>
 		` : leg.transfer ? html`
-			<p class="transfer">${t('transferinfo', parseName(leg.destination))}</p>
+			<p class="transfer">${t('transferinfo', formatName(leg.destination))}</p>
 		` : leg.change ? html`
 			<p class="change">${t('changeinfo', formatDuration(leg.duration))}</p>
 		` : html`

@@ -28,7 +29,7 @@ const legTemplate = (leg, profile) => {
 				<thead>
 					<tr>
 						<td colspan="4">
-							<div class="center"><a href="#/t/${profile}/${leg.tripId}">${lineDisplayName(leg.line)}${leg.direction ? html` → ${leg.direction}` : ''}</a>
+							<div class="center"><a href="#/t/${profile}/${leg.tripId}">${formatLineDisplayName(leg.line)}${leg.direction ? html` → ${leg.direction}` : ''}</a>
 							${leg.cancelled ? html`<b class="cancelled-text">${t('cancelled-ride')}</b>` : ''}
 							${Object.entries(remarks).map(remarksTemplate)}</div>
 						</td>

@@ -36,9 +37,9 @@ const legTemplate = (leg, profile) => {
 					<tr>
 						<td colspan="4">
 							<div class="train-details center">
-								${lineAdditionalName(leg.line) ? html`
+								${formatLineAdditionalName(leg.line) ? html`
 									<div>
-										Trip: ${lineAdditionalName(leg.line)}
+										Trip: ${formatLineAdditionalName(leg.line)}
 									</div>
 								` : ''}
 								${leg.line.trainType ? html`

@@ -125,7 +126,7 @@ const journeyTemplate = (data, profile) => {
 					<a class="icon-back" href="#/${data.slug}/${settings.journeysViewMode}" title="${t('back')}">${t('back')}</a>
 				` : ''}
 				<div class="content">
-					<h3>${parseName(data.legs[0].origin)} → ${parseName(data.legs[data.legs.length - 1].destination)}</h3>
+					<h3>${formatName(data.legs[0].origin)} → ${formatName(data.legs[data.legs.length - 1].destination)}</h3>
 					<p><b>${t('duration')}: ${formatDuration(duration)} | ${t('changes')}: ${changes-1} | ${t('date')}: ${formatDateTime(data.legs[0].plannedDeparture, 'date')}${settings.showPrices && data.price ? html` | ${t('price')}: <td><span>${formatPrice(data.price)}</span></td>` : ''}</b></p>
 				</div>
 				<a class="icon-reload" title="${t("reload")}" @click=${() => refreshJourneyView(data.refreshToken, profile)}>${t("reload")}</a>
diff --git a/src/journeysView.js b/src/journeysView.js
@@ -1,5 +1,6 @@
-import { showDiv, hideDiv, ElementById, formatDuration, formatFromTo, getFrom, getTo, padZeros, formatPrice } from './helpers.js';
-import { parseName, t, getJourneys, getMoreJourneys, refreshJourneys } from './app_functions.js';
+import { showDiv, hideDiv, ElementById, getFrom, getTo, padZeros } from './helpers.js';
+import { t, getJourneys, getMoreJourneys, refreshJourneys } from './app_functions.js';
+import { formatName, formatDuration, formatFromTo, formatPrice } from './formatters.js';
 import { timeTemplate } from './templates.js';
 import { settings, modifySettings } from './settings.js';
 import { setupCanvas } from './canvas.js';

@@ -12,8 +13,8 @@ const journeysTemplate = (data) => html`
 		<header id="header">
 			<a class="icon-back" href="#/" title="${t('back')}">${t('back')}</a>
 			<div class="content">
-				<h3>${t('from')}: ${parseName(getFrom(data.journeys))}</h3>
-				<h3>${t('to')}: ${parseName(getTo(data.journeys))}</h3>
+				<h3>${t('from')}: ${formatName(getFrom(data.journeys))}</h3>
+				<h3>${t('to')}: ${formatName(getTo(data.journeys))}</h3>
 				<div class="mode-changers">
 					<a href="#/${data.slug}/table" class="${settings.journeysViewMode === 'table' ? 'active' : ''}">
 						<div class="icon-table"></div>
diff --git a/src/searchView.js b/src/searchView.js
@@ -1,6 +1,7 @@
-import { showDiv, ElementById, padZeros, isValidDate, formatFromTo } from './helpers.js';
+import { showDiv, ElementById, padZeros, isValidDate } from './helpers.js';
 import { db } from './dataStorage.js';
-import { parseName, t, loadDS100, getJourneys, newJourneys, ds100Reverse } from './app_functions.js';
+import { t, loadDS100, getJourneys, newJourneys, ds100Reverse } from './app_functions.js';
+import { formatName, formatFromTo } from './formatters.js';
 import { modifySettings, settings } from './settings.js';
 import { go } from './router.js';
 import { html, render } from 'lit-html';

@@ -153,17 +154,17 @@ const journeysHistoryTemplate = (journeysHistory) => html`
 		<div class="row" @click="${() => {journeysHistoryAction(journeysHistory, element);}}">
 			<div class="from">
 				<small>${t('from')}:</small><br>
-				${parseName(element.fromPoint)}
+				${formatName(element.fromPoint)}
 				${element.viaPoint ? html`
 					<div class="via">
-						<small>${t('via')} ${parseName(element.viaPoint)}</small>
+						<small>${t('via')} ${formatName(element.viaPoint)}</small>
 					</div>
 				` : ''}
 			</div>
 			<div class="icon-arrow1"></div>
 			<div class="to">
 				<small>${t('to')}:</small><br>
-				${parseName(element.toPoint)}
+				${formatName(element.toPoint)}
 			</div>
 		</div>
 		`)}

@@ -314,7 +315,7 @@ export const search = async (requestId) => {
 const suggestionsTemplate = (data, inputId) => html`
 	<div class="box" @mouseover=${mouseOverSuggestions} @mouseout=${stopMouseOverSuggestions}>
 		${data.map(element => html`
-			<p class="suggestion" @click=${() => setSuggestion(encodeURI(JSON.stringify(element)), inputId)}>${parseName(element)}</p>
+			<p class="suggestion" @click=${() => setSuggestion(encodeURI(JSON.stringify(element)), inputId)}>${formatName(element)}</p>
 		`)}
 	</div>
 `;

@@ -344,7 +345,7 @@ export const setSuggestion = (data, inputId) => {
 	}
 
 	suggestions[inputId] = data;
-	ElementById(inputId).value = parseName(data);
+	ElementById(inputId).value = formatName(data);
 
 	if (inputId === 'from') {
 		ElementById('fromSuggestions').classList.remove('mouseover');
diff --git a/src/templates.js b/src/templates.js
@@ -1,4 +1,4 @@
-import { formatDateTime, lineAdditionalName } from './helpers.js';
+import { formatDateTime, lineAdditionalName } from './formatters.js';
 import { showModal } from './overlays.js';
 import { html } from 'lit-html';
 import { settings } from './settings.js';
diff --git a/src/tripView.js b/src/tripView.js
@@ -1,7 +1,8 @@
 import { settings } from './settings.js';
 import { remarksTemplate, platformTemplate, stopTemplate, timeTemplate } from './templates.js';
-import { showDiv, hideDiv, ElementById, formatDateTime, formatDuration, formatPrice, lineAdditionalName, lineDisplayName } from './helpers.js';
-import { parseName, t, processLeg } from './app_functions.js';
+import { showDiv, hideDiv, ElementById } from './helpers.js';
+import { t, processLeg } from './app_functions.js';
+import { formatDateTime, formatDuration, formatPrice, formatLineAdditionalName, formatLineDisplayName } from './formatters.js';
 import { showAlertModal, showLoader, hideOverlay, showModal } from './overlays.js';
 import { go } from './router.js';
 import { html, render } from 'lit-html';

@@ -20,7 +21,7 @@ const tripTemplate = (data, profile) => {
 
 	let bahnExpertUrl = null;
 	if (data.line && (data.line.product == 'nationalExpress' || data.line.product == 'national' || data.line.product == 'regionalExpress' || data.line.product == 'regional')) {
-		const trainName = lineAdditionalName(data.line) || data.line?.name;
+		const trainName = formatLineAdditionalName(data.line) || data.line?.name;
 		if (trainName) {
 			bahnExpertUrl = 'https://bahn.expert/details/' + encodeURIComponent(trainName) + '/' + Number(data.plannedDeparture);
 		}

@@ -31,7 +32,7 @@ const tripTemplate = (data, profile) => {
 			<header>
 				<a id="back" class="icon-back hidden" title="${t('back')}" @click=${() => history.back()}>${t('back')}</a>
 				<div class="content">
-					<h3>Trip of ${lineDisplayName(data.line)} to ${data.direction}</h3>
+					<h3>Trip of ${formatLineDisplayName(data.line)} to ${data.direction}</h3>
 				</div>
 				<a class="icon-reload invisible">${t("reload")}</a>
 			</header>

@@ -42,9 +43,9 @@ const tripTemplate = (data, profile) => {
 					<tr>
 						<td colspan="4">
 							<div class="center">${bahnExpertUrl ? html`
-								<a href="${bahnExpertUrl}">${lineDisplayName(data.line)}${data.direction ? html` → ${data.direction}` : ''}</a>
+								<a href="${bahnExpertUrl}">${formatLineDisplayName(data.line)}${data.direction ? html` → ${data.direction}` : ''}</a>
 							` : html `
-								${lineDisplayName(data.line)}${data.direction ? html` → ${data.direction}` : ''}
+								${formatLineDisplayName(data.line)}${data.direction ? html` → ${data.direction}` : ''}
 							`}
 							${data.cancelled ? html`<b class="cancelled-text">${t('cancelled-ride')}</b>` : ''}
 							${Object.entries(remarks).map(remarksTemplate)}</div>

@@ -53,9 +54,9 @@ const tripTemplate = (data, profile) => {
 					<tr>
 						<td colspan="4">
 							<div class="train-details center">
-								${lineAdditionalName(data.line) ? html`
+								${formatLineAdditionalName(data.line) ? html`
 									<div>
-										Trip: ${lineAdditionalName(data.line)}
+										Trip: ${formatLineAdditionalName(data.line)}
 									</div>
 								` : ''}
 								${data.line.trainType ? html`