gtfs-to-html 2.6.0 → 2.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/examples/timetable_notes.txt +2 -1
- package/examples/timetable_notes_references.txt +1 -0
- package/lib/file-utils.js +6 -16
- package/lib/gtfs-to-html.js +2 -2
- package/lib/utils.js +20 -9
- package/package.json +8 -6
- package/public/js/timetable-map.js +6 -1
- package/views/default/timetable_menu.pug +1 -1
- package/views/default/timetablepage.pug +4 -1
- package/www/docs/additional-files.md +1 -1
- package/www/docs/timetable-notes.md +3 -2
- package/www/package.json +2 -2
- package/www/yarn.lock +1228 -1217
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.6.2] - 2024-04-19
|
|
9
|
+
|
|
10
|
+
### Updated
|
|
11
|
+
- Dependency updates
|
|
12
|
+
- Update to timetable page sorting
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Markdown support in timetable notes
|
|
17
|
+
|
|
18
|
+
## [2.6.1] - 2024-03-26
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Fix for missing stops
|
|
23
|
+
|
|
24
|
+
### Updated
|
|
25
|
+
- Dependency updates
|
|
26
|
+
- Day List selector label wording
|
|
27
|
+
- Updates to timetable map
|
|
28
|
+
- Filename format for CSV export files
|
|
29
|
+
|
|
8
30
|
## [2.6.0] - 2024-02-27
|
|
9
31
|
|
|
10
32
|
### Updated
|
|
@@ -4,4 +4,5 @@ note_id,symbol,note
|
|
|
4
4
|
3,,"Trip is cancelled if drawbridge is up"
|
|
5
5
|
4,,"This stop is sometimes underwater"
|
|
6
6
|
5,,"Driver will only stop if prearranged by fax"
|
|
7
|
-
6,§,"Vehicle can arrive early if leap second is added during trip"
|
|
7
|
+
6,§,"Vehicle can arrive early if leap second is added during trip and *will not wait*"
|
|
8
|
+
7,,"[See list of holidays](http://transitagency.org/holidays)"
|
package/lib/file-utils.js
CHANGED
|
@@ -11,6 +11,8 @@ import { renderFile } from 'pug';
|
|
|
11
11
|
import puppeteer from 'puppeteer';
|
|
12
12
|
import sanitize from 'sanitize-filename';
|
|
13
13
|
import untildify from 'untildify';
|
|
14
|
+
import insane from 'insane';
|
|
15
|
+
import { marked } from 'marked';
|
|
14
16
|
|
|
15
17
|
import {
|
|
16
18
|
isNullOrEmpty,
|
|
@@ -132,7 +134,7 @@ export function zipFolder(exportPath) {
|
|
|
132
134
|
/*
|
|
133
135
|
* Generate the filename for a given timetable.
|
|
134
136
|
*/
|
|
135
|
-
export function generateFileName(timetable, config) {
|
|
137
|
+
export function generateFileName(timetable, config, extension = 'html') {
|
|
136
138
|
let filename = timetable.timetable_id;
|
|
137
139
|
|
|
138
140
|
for (const route of timetable.routes) {
|
|
@@ -145,24 +147,11 @@ export function generateFileName(timetable, config) {
|
|
|
145
147
|
filename += `_${timetable.direction_id}`;
|
|
146
148
|
}
|
|
147
149
|
|
|
148
|
-
filename += `_${formatDays(timetable, config).replace(/\s/g, '')}
|
|
150
|
+
filename += `_${formatDays(timetable, config).replace(/\s/g, '')}.${extension}`;
|
|
149
151
|
|
|
150
152
|
return sanitize(filename).toLowerCase();
|
|
151
153
|
}
|
|
152
154
|
|
|
153
|
-
/*
|
|
154
|
-
* Generate the filename for a CSV timetable.
|
|
155
|
-
*/
|
|
156
|
-
export function generateCSVFileName(timetable, timetablePage) {
|
|
157
|
-
let filename = timetablePage.filename.replace(/.html$/, '');
|
|
158
|
-
|
|
159
|
-
if (timetablePage.timetables.length > 1) {
|
|
160
|
-
filename += `_${timetable.direction_id}`;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return sanitize(`${filename}.csv`);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
155
|
/*
|
|
167
156
|
* Generates the folder name for a timetable page based on the date.
|
|
168
157
|
*/
|
|
@@ -182,9 +171,10 @@ export function generateFolderName(timetablePage) {
|
|
|
182
171
|
export async function renderTemplate(templateFileName, templateVars, config) {
|
|
183
172
|
const templatePath = getTemplatePath(templateFileName, config);
|
|
184
173
|
|
|
185
|
-
// Make template functions and
|
|
174
|
+
// Make template functions, lodash and marked available inside pug templates.
|
|
186
175
|
const html = await renderFile(templatePath, {
|
|
187
176
|
_,
|
|
177
|
+
md: (text) => insane(marked.parseInline(text)),
|
|
188
178
|
...templateFunctions,
|
|
189
179
|
formatRouteColor,
|
|
190
180
|
formatRouteTextColor,
|
package/lib/gtfs-to-html.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
generateFolderName,
|
|
13
13
|
renderPdf,
|
|
14
14
|
zipFolder,
|
|
15
|
-
|
|
15
|
+
generateFileName,
|
|
16
16
|
} from './file-utils.js';
|
|
17
17
|
import {
|
|
18
18
|
log,
|
|
@@ -139,7 +139,7 @@ const gtfsToHtml = async (initialConfig) => {
|
|
|
139
139
|
const csvPath = path.join(
|
|
140
140
|
exportPath,
|
|
141
141
|
datePath,
|
|
142
|
-
|
|
142
|
+
generateFileName(timetable, config, 'csv'),
|
|
143
143
|
);
|
|
144
144
|
await writeFile(csvPath, csv);
|
|
145
145
|
}
|
package/lib/utils.js
CHANGED
|
@@ -472,7 +472,7 @@ const convertTimetableToTimetablePage = (timetable, config) => {
|
|
|
472
472
|
});
|
|
473
473
|
}
|
|
474
474
|
|
|
475
|
-
const filename = generateFileName(timetable, config);
|
|
475
|
+
const filename = generateFileName(timetable, config, 'html');
|
|
476
476
|
|
|
477
477
|
return {
|
|
478
478
|
timetable_page_id: timetable.timetable_id,
|
|
@@ -855,6 +855,10 @@ const getAllStationStopIds = (stopId) => {
|
|
|
855
855
|
stop_id: stopId,
|
|
856
856
|
});
|
|
857
857
|
|
|
858
|
+
if (stops.length === 0) {
|
|
859
|
+
throw new Error(`No stop found for stop_id=${stopId}`);
|
|
860
|
+
}
|
|
861
|
+
|
|
858
862
|
const stop = stops[0];
|
|
859
863
|
|
|
860
864
|
if (isNullOrEmpty(stop.parent_station)) {
|
|
@@ -1153,11 +1157,11 @@ const getTripsForTimetable = (timetable, calendars, config) => {
|
|
|
1153
1157
|
|
|
1154
1158
|
for (const trip of formattedTrips) {
|
|
1155
1159
|
for (const stoptime of trip.stoptimes) {
|
|
1156
|
-
const
|
|
1157
|
-
|
|
1158
|
-
)
|
|
1159
|
-
|
|
1160
|
-
|
|
1160
|
+
const stop = stops.find((stop) => stop.stop_id === stoptime.stop_id);
|
|
1161
|
+
|
|
1162
|
+
if (stop?.parent_station) {
|
|
1163
|
+
stoptime.stop_id = stop.parent_station;
|
|
1164
|
+
}
|
|
1161
1165
|
}
|
|
1162
1166
|
}
|
|
1163
1167
|
}
|
|
@@ -1608,17 +1612,24 @@ export function generateOverviewHTML(timetablePages, config) {
|
|
|
1608
1612
|
// Sort timetables for display, first numerically then alphabetically.
|
|
1609
1613
|
const sortedTimetablePages = sortBy(timetablePages, [
|
|
1610
1614
|
(timetablePage) => {
|
|
1615
|
+
// First sort numerically by route_short_name, removing leading non-digits
|
|
1611
1616
|
if (
|
|
1612
1617
|
timetablePage.consolidatedTimetables.length > 0 &&
|
|
1613
1618
|
timetablePage.consolidatedTimetables[0].routes.length > 0
|
|
1614
1619
|
) {
|
|
1615
|
-
return
|
|
1616
|
-
|
|
1617
|
-
|
|
1620
|
+
return (
|
|
1621
|
+
Number.parseInt(
|
|
1622
|
+
timetablePage.consolidatedTimetables[0].routes[0].route_short_name?.replace(
|
|
1623
|
+
/^\D+/g,
|
|
1624
|
+
'',
|
|
1625
|
+
),
|
|
1626
|
+
10,
|
|
1627
|
+
) || 0
|
|
1618
1628
|
);
|
|
1619
1629
|
}
|
|
1620
1630
|
},
|
|
1621
1631
|
(timetablePage) => {
|
|
1632
|
+
// Then sort by route_short_name alphabetically
|
|
1622
1633
|
if (
|
|
1623
1634
|
timetablePage.consolidatedTimetables.length > 0 &&
|
|
1624
1635
|
timetablePage.consolidatedTimetables[0].routes.length > 0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gtfs-to-html",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Build human readable transit timetables as HTML, PDF or CSV from GTFS",
|
|
6
6
|
"keywords": [
|
|
@@ -37,26 +37,28 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@turf/helpers": "^6.5.0",
|
|
39
39
|
"@turf/simplify": "^6.5.0",
|
|
40
|
-
"archiver": "^
|
|
40
|
+
"archiver": "^7.0.1",
|
|
41
41
|
"cli-table": "^0.3.11",
|
|
42
42
|
"copy-dir": "^1.3.0",
|
|
43
43
|
"csv-stringify": "^6.4.6",
|
|
44
|
-
"express": "^4.
|
|
45
|
-
"gtfs": "^4.
|
|
44
|
+
"express": "^4.19.2",
|
|
45
|
+
"gtfs": "^4.10.2",
|
|
46
|
+
"insane": "^2.6.2",
|
|
46
47
|
"js-beautify": "^1.15.1",
|
|
47
48
|
"lodash-es": "^4.17.21",
|
|
49
|
+
"marked": "^12.0.2",
|
|
48
50
|
"moment": "^2.30.1",
|
|
49
51
|
"morgan": "^1.10.0",
|
|
50
52
|
"pretty-error": "^4.0.0",
|
|
51
53
|
"pug": "^3.0.2",
|
|
52
|
-
"puppeteer": "^22.
|
|
54
|
+
"puppeteer": "^22.6.5",
|
|
53
55
|
"sanitize-filename": "^1.6.3",
|
|
54
56
|
"sqlstring": "^2.3.3",
|
|
55
57
|
"timer-machine": "^1.1.0",
|
|
56
58
|
"toposort": "^2.0.2",
|
|
57
59
|
"untildify": "^5.0.0",
|
|
58
60
|
"yargs": "^17.7.2",
|
|
59
|
-
"yoctocolors": "^
|
|
61
|
+
"yoctocolors": "^2.0.0"
|
|
60
62
|
},
|
|
61
63
|
"devDependencies": {
|
|
62
64
|
"husky": "^9.0.11",
|
|
@@ -28,6 +28,11 @@ function formatRoute(route) {
|
|
|
28
28
|
.text(route.route_long_name || ''),
|
|
29
29
|
])
|
|
30
30
|
.appendTo(html);
|
|
31
|
+
} else {
|
|
32
|
+
$('<div>')
|
|
33
|
+
.addClass('hover:underline')
|
|
34
|
+
.text(route.route_long_name || '')
|
|
35
|
+
.appendTo(html);
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
return html.prop('outerHTML');
|
|
@@ -48,7 +53,7 @@ function formatStopPopup(feature, routes) {
|
|
|
48
53
|
$('<strong>').text(feature.properties.stop_code).appendTo(html);
|
|
49
54
|
}
|
|
50
55
|
|
|
51
|
-
$('<div>').addClass('text-sm').text('Routes Served:').appendTo(html);
|
|
56
|
+
$('<div>').addClass('text-sm mb-2').text('Routes Served:').appendTo(html);
|
|
52
57
|
|
|
53
58
|
$(html).append(routeIds.map((routeId) => formatRoute(routes[routeId])));
|
|
54
59
|
|
|
@@ -42,7 +42,7 @@ if timetablePage.consolidatedTimetables.length > 1
|
|
|
42
42
|
span= directionName
|
|
43
43
|
div(hidden=timetablePage.dayLists.length <= 1)
|
|
44
44
|
#day_list_selector
|
|
45
|
-
h3.font-bold
|
|
45
|
+
h3.font-bold Day of Week
|
|
46
46
|
each dayList, idx in timetablePage.dayLists
|
|
47
47
|
label.cursor-pointer.mb-2.w-full.flex.items-center.justify-center.px-8.py-3.border.border-transparent.text-base.rounded-md(class=idx === 0 ? 'text-white bg-blue-600': 'text-gray-600 bg-gray-300')
|
|
48
48
|
input.hidden(type="radio" name="dayList" autocomplete="off" value=dayList checked=(idx === 0))
|
|
@@ -54,7 +54,10 @@ include formatting_functions.pug
|
|
|
54
54
|
if timetable.interpolatedStopSymbolUsed
|
|
55
55
|
.note(id=`note-${timetable.timetable_id}-interpolated-stop`)= `${config.interpolatedStopSymbol} = ${config.interpolatedStopText}`
|
|
56
56
|
each note in _.uniqBy(timetable.notes, 'note_id')
|
|
57
|
-
.note(id=`note-${timetable.timetable_id}-${note.note_id}`)
|
|
57
|
+
.note(id=`note-${timetable.timetable_id}-${note.note_id}`)
|
|
58
|
+
span= note.symbol
|
|
59
|
+
span =
|
|
60
|
+
span!= md(note.note)
|
|
58
61
|
|
|
59
62
|
if config.showCalendarExceptions && timetable.calendarDates.includedDates.length
|
|
60
63
|
.included-dates= `${config.serviceProvidedOnText}: ${timetable.calendarDates.includedDates.join(', ')}`
|
|
@@ -13,7 +13,7 @@ By default, GTFS-to-HTML attempts to generate a timetable for each route and dir
|
|
|
13
13
|
* [timetable_pages.txt](/docs/timetable-pages) - Specifies which HTML timetables should be grouped together into a single HTML page.
|
|
14
14
|
|
|
15
15
|
## Adding notes to timetables
|
|
16
|
-
Notes about a specific trip, stop, stoptime, route or timetable can be added to timetables by using `timetable_notes.txt` and `timetable_notes_references.txt` in your GTFS.
|
|
16
|
+
Notes about a specific trip, stop, stoptime, route or timetable can be added to timetables by using `timetable_notes.txt` and `timetable_notes_references.txt` in your GTFS. These notes can include links and other formatting using Markdown syntax.
|
|
17
17
|
|
|
18
18
|
* [timetable_notes.txt](/docs/timetable-notes) - Specifies notes to be used in timetables.
|
|
19
19
|
* [timetable_notes_references.txt](/docs/timetable-notes-references) - Specifies where notes should be placed in timetables.
|
|
@@ -15,7 +15,7 @@ Notes can have a `symbol` specified or can be left blank and GTFS-to-HTML will a
|
|
|
15
15
|
| ----------- | ----------- |
|
|
16
16
|
| `note_id` | A unique ID for the timetable note |
|
|
17
17
|
| `symbol` | The symbol used to indicate the note, such as `§`. Optional, if omitted a letter of the alphabet starting with `a` will be used. |
|
|
18
|
-
| `note` | The text of the note, such as "This stop is sometimes underwater". |
|
|
18
|
+
| `note` | The text of the note, such as "This stop is sometimes underwater". [Markdown syntax](https://daringfireball.net/projects/markdown/syntax) is supported which allows including links and formatting. |
|
|
19
19
|
|
|
20
20
|
### Example
|
|
21
21
|
|
|
@@ -26,7 +26,8 @@ note_id,symbol,note
|
|
|
26
26
|
3,,"Trip is cancelled if drawbridge is up"
|
|
27
27
|
4,,"This stop is sometimes underwater"
|
|
28
28
|
5,,"Driver will only stop if prearranged by fax"
|
|
29
|
-
6,§,"Vehicle can arrive early if leap second is added during trip"
|
|
29
|
+
6,§,"Vehicle can arrive early if leap second is added during trip and *will not wait*"
|
|
30
|
+
7,,"[See list of holidays](http://transitagency.org/holidays)"
|
|
30
31
|
```
|
|
31
32
|
|
|
32
33
|
An example of this file is located in [examples/timetable_notes.txt](https://github.com/BlinkTagInc/gtfs-to-html/blob/master/examples/timetable_notes.txt).
|
package/www/package.json
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"deploy": "docusaurus deploy"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@docusaurus/core": "^3.1
|
|
13
|
-
"@docusaurus/preset-classic": "^3.1
|
|
12
|
+
"@docusaurus/core": "^3.2.1",
|
|
13
|
+
"@docusaurus/preset-classic": "^3.2.1",
|
|
14
14
|
"clsx": "^2.1.0",
|
|
15
15
|
"react": "^18.2.0",
|
|
16
16
|
"react-dom": "^18.2.0"
|