gtfs-to-html 2.2.1 → 2.3.3

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 CHANGED
@@ -5,6 +5,37 @@ 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.3.3] - 2022-01-21
9
+
10
+ ### Updated
11
+
12
+ - Dependency updates
13
+
14
+ ## [2.3.2] - 2021-12-28
15
+
16
+ ### Updated
17
+
18
+ - Dependency updates
19
+ - Updated docs info on multi-route timetables
20
+
21
+ ## [2.3.1] - 2021-11-26
22
+
23
+ ### Updated
24
+
25
+ - Dependency updates
26
+ - Better trip names for CSV export
27
+
28
+ ## [2.3.0] - 2021-11-05
29
+
30
+ ### Added
31
+
32
+ - Support for exporting timetables in CSV format
33
+
34
+ ### Updated
35
+
36
+ - Update route color swatch styles to support longer names
37
+ - Dependency updates
38
+
8
39
  ## [2.2.1] - 2021-10-17
9
40
 
10
41
  ### Added
@@ -102,7 +133,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
102
133
 
103
134
  - Fix for showRouteTitle config
104
135
 
105
- ## [Unreleased]
136
+ ## [2.3.1] - 2021-11-26
106
137
 
107
138
  ### Added
108
139
 
package/README.md CHANGED
@@ -1,35 +1,51 @@
1
-
2
- # GTFS to HTML
3
-
4
- [![NPM version](https://img.shields.io/npm/v/gtfs-to-html.svg?style=flat)](https://www.npmjs.com/package/gtfs-to-html)
5
- [![David](https://img.shields.io/david/blinktaginc/gtfs-to-html.svg)]()
6
- [![npm](https://img.shields.io/npm/dm/gtfs-to-html.svg?style=flat)]()
7
- [![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo)
8
-
9
- [![NPM](https://nodei.co/npm/gtfs-to-html.png?downloads=true)](https://nodei.co/npm/gtfs-to-html/)
10
-
11
- See [gtfstohtml.com](https://gtfstohtml.com) for full documentation.
12
-
13
- `gtfs-to-html` creates human-readable, user-friendly transit timetables in HTML and PDF format directly from [GTFS transit data](https://developers.google.com/transit/gtfs/). Most transit agencies have schedule data in GTFS format but need to show each route's schedule to users on a website. This project automates the process of creating nicely formatted HTML timetables for inclusion on a transit agency website. This makes it easy to keep timetables up to date and accurate when schedule changes happen and reduces the likelihood of errors.
1
+ <p align="center">
2
+ ➡️
3
+ <a href="https://gtfstohtml.com/docs/">Documentation</a> |
4
+ <a href="https://gtfstohtml.com/docs/quick-start">Quick Start</a> |
5
+ <a href="https://gtfstohtml.com/docs/configuration">Configuration</a> |
6
+ <a href="https://gtfstohtml.com/docs/contact">Questions and Support</a>
7
+ ⬅️
8
+ <br /><br />
9
+ <img src="www/static/img/gtfs-to-html-logo.svg" alt="GTFS-to-HTML" />
10
+ <br /><br />
11
+ <a href="https://www.npmjs.com/package/gtfs-to-html" rel="nofollow"><img src="https://img.shields.io/npm/v/gtfs-to-html.svg?style=flat" style="max-width: 100%;"></a>
12
+ <a href="https://www.npmjs.com/package/gtfs-to-html" rel="nofollow"><img src="https://img.shields.io/npm/dm/gtfs-to-html.svg?style=flat" style="max-width: 100%;"></a>
13
+ <img src="https://img.shields.io/badge/License-MIT-yellow.svg">
14
+ <br /><br />
15
+ Create human-readable, user-friendly transit timetables in HTML, PDF or CSV format directly from GTFS.
16
+ <br /><br />
17
+ <a href="https://nodei.co/npm/gtfs-to-html/" rel="nofollow"><img src="https://nodei.co/npm/gtfs-to-html.png?downloads=true" alt="NPM" style="max-width: 100%;"></a>
18
+ </p>
19
+
20
+ <hr>
21
+
22
+ See [gtfstohtml.com](https://gtfstohtml.com) for full documentation.
23
+
24
+ Most transit agencies have schedule data in [GTFS ](https://developers.google.com/transit/gtfs/) format but need to show each route's schedule to users on a website. GTFS-to-HTML automates the process of creating nicely formatted HTML timetables for inclusion on a transit agency website. This makes it easy to keep timetables up to date and accurate when schedule changes happen and reduces the likelihood of errors.
14
25
 
15
26
  <img width="1265" src="https://user-images.githubusercontent.com/96217/28296063-aed45568-6b1a-11e7-9794-94b3d915d668.png">
16
27
 
17
28
  ## Features
18
29
 
19
30
  ### Configurable and customizable
20
- `gtfs-to-html` has many options that configure how timetables are presented. It also allows using a completely custom template which makes it easy to build chunks of HTML that will fit perfectly into any website using any HTML structure and classes that you'd like. Or, create printable PDF versions of timetables using the `outputFormat` config option.
31
+
32
+ `gtfs-to-html` has many options that configure how timetables are presented. It also allows using a completely custom template which makes it easy to build chunks of HTML that will fit perfectly into any website using any HTML structure and classes that you'd like. Or, create printable PDF versions or CSV exports of timetables using the `outputFormat` config option.
21
33
 
22
34
  ### Accessibility for all
35
+
23
36
  `gtfs-to-html` properly formats timetables to ensure they are screen-reader accessible and WCAG 2.0 compliant.
24
37
 
25
38
  ### Mobile responsiveness built in
39
+
26
40
  Built-in styling makes `gtfs-to-html` timetables ready to size and scroll easily on mobile phones and tablets.
27
41
 
28
42
  ### Schedule changes? A cinch.
43
+
29
44
  By generating future timetables and including dates in table metadata, your timetables can appear in advance of a schedule change, and you can validate that your new timetables and GTFS are correct.
30
45
 
31
46
  ### Auto-generated maps
32
- `gtfs-to-html` can also generate a map for each route that can be included with the schedule page. The map shows all stops for the route and lists all routes that serve each stop. See the `showMap` configuration option below.
47
+
48
+ `gtfs-to-html` can also generate a map for each route that can be included with the schedule page. The map shows all stops for the route and lists all routes that serve each stop. See the `showMap` configuration option below.
33
49
 
34
50
  Note: If you only want maps of GTFS data, use the [gtfs-to-geojson](https://github.com/blinktaginc/gtfs-to-geojson) package instead and skip making timetables entirely. If offers many different formats of GeoJSON for routes and stops.
35
51
 
@@ -40,34 +56,36 @@ Note: If you only want maps of GTFS data, use the [gtfs-to-geojson](https://gith
40
56
  You can now use `gtfs-to-html` without actually downloading any code or doing any configuration. [run.gtfstohtml.com](https://run.gtfstohtml.com) provides a web based interface for finding GTFS feeds for agencies, setting configuration and then generates a previewable and downloadable set of timetables.
41
57
 
42
58
  ## Current Usage
59
+
43
60
  Many transit agencies use `gtfs-to-html` to generate the schedule pages used on their websites, including:
44
61
 
45
- * [Advance Transit](https://advancetransit.com)
46
- * [Brockton Area Transit Authority](https://ridebat.com)
47
- * [Capital Transit (Helena, Montana)](http://www.ridethecapitalt.org)
48
- * [Capital Transit (Juneau, Alaska)](https://juneaucapitaltransit.org)
49
- * [Central Transit (Ellensburg, Washington)](https://centraltransit.org)
50
- * [County Connection (Contra Costa County, California)](https://countyconnection.com)
51
- * [El Dorado Transit](http://eldoradotransit.com)
52
- * [Greater Attleboro-Taunton Regional Transit Authority](https://www.gatra.org)
53
- * [Humboldt Transit Authority](http://hta.org)
54
- * [Kings Area Rural Transit (KART)](https://www.kartbus.org)
55
- * [Madera County Connection](http://mcctransit.com)
56
- * [Marin Transit](https://marintransit.org)
57
- * [Morongo Basin Transit Authority](https://mbtabus.com)
58
- * [Mountain Transit](http://mountaintransit.org)
59
- * [MVgo (Mountain View, CA)](https://mvgo.org)
60
- * [NW Connector (Oregon)](http://www.nworegontransit.org)
61
- * [Palo Verde Valley Transit Agency](http://pvvta.com)
62
- * [Petaluma Transit](http://transit.cityofpetaluma.net)
63
- * [RTC Washoe (Reno, NV)](https://www.rtcwashoe.com)
64
- * [Santa Barbara Metropolitan Transit District](https://sbmtd.gov)
65
- * [Sonoma County Transit](http://sctransit.com)
66
- * [Tahoe Truckee Area Regional Transit](https://tahoetruckeetransit.com)
67
- * [Transcollines](https://transcollines.ca)
68
- * [Tulare County Area Transit](https://ridetcat.org)
69
- * [Victor Valley Transit](https://vvta.org)
70
- * [Worcester Regional Transit Authority](https://therta.com)
62
+ - [Advance Transit](https://advancetransit.com)
63
+ - [Brockton Area Transit Authority](https://ridebat.com)
64
+ - [Capital Transit (Helena, Montana)](http://www.ridethecapitalt.org)
65
+ - [Capital Transit (Juneau, Alaska)](https://juneaucapitaltransit.org)
66
+ - [Central Transit (Ellensburg, Washington)](https://centraltransit.org)
67
+ - [County Connection (Contra Costa County, California)](https://countyconnection.com)
68
+ - [El Dorado Transit](http://eldoradotransit.com)
69
+ - [Greater Attleboro-Taunton Regional Transit Authority](https://www.gatra.org)
70
+ - [Humboldt Transit Authority](http://hta.org)
71
+ - [Kings Area Rural Transit (KART)](https://www.kartbus.org)
72
+ - [Madera County Connection](http://mcctransit.com)
73
+ - [Marin Transit](https://marintransit.org)
74
+ - [Morongo Basin Transit Authority](https://mbtabus.com)
75
+ - [Mountain Transit](http://mountaintransit.org)
76
+ - [MVgo (Mountain View, CA)](https://mvgo.org)
77
+ - [NW Connector (Oregon)](http://www.nworegontransit.org)
78
+ - [Palo Verde Valley Transit Agency](http://pvvta.com)
79
+ - [Petaluma Transit](http://transit.cityofpetaluma.net)
80
+ - [RTC Washoe (Reno, NV)](https://www.rtcwashoe.com)
81
+ - [Santa Barbara Metropolitan Transit District](https://sbmtd.gov)
82
+ - [Sonoma County Transit](http://sctransit.com)
83
+ - [Tahoe Transportation District](https://www.tahoetransportation.org)
84
+ - [Tahoe Truckee Area Regional Transit](https://tahoetruckeetransit.com)
85
+ - [Transcollines](https://transcollines.ca)
86
+ - [Tulare County Area Transit](https://ridetcat.org)
87
+ - [Victor Valley Transit](https://vvta.org)
88
+ - [Worcester Regional Transit Authority](https://therta.com)
71
89
 
72
90
  Are you using `gtfs-to-html`? Let us know via email (brendan@blinktag.com) or via opening a github issue or pull request if your agency is using this library.
73
91
 
package/app/index.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  getTimetablePagesForAgency,
15
15
  getFormattedTimetablePage,
16
16
  generateOverviewHTML,
17
- generateHTML,
17
+ generateTimetableHTML,
18
18
  } from '../lib/utils.js';
19
19
 
20
20
  const { argv } = yargs(process.argv).option('c', {
@@ -107,8 +107,8 @@ router.get('/timetables/:timetablePageId', async (request, response, next) => {
107
107
  config
108
108
  );
109
109
 
110
- const results = await generateHTML(timetablePage, config);
111
- response.send(results.html);
110
+ const html = await generateTimetableHTML(timetablePage, config);
111
+ response.send(html);
112
112
  } catch (error) {
113
113
  next(error);
114
114
  }
package/lib/file-utils.js CHANGED
@@ -145,6 +145,19 @@ export function generateFileName(timetable, config) {
145
145
  return sanitize(filename).toLowerCase();
146
146
  }
147
147
 
148
+ /*
149
+ * Generate the filename for a CSV timetable.
150
+ */
151
+ export function generateCSVFileName(timetable, timetablePage) {
152
+ let filename = timetablePage.filename.replace(/.html$/, '');
153
+
154
+ if (timetablePage.timetables.length > 1) {
155
+ filename += `_${timetable.direction_id}`;
156
+ }
157
+
158
+ return sanitize(`${filename}.csv`);
159
+ }
160
+
148
161
  /*
149
162
  * Generates the folder name for a timetable page based on the date.
150
163
  */
package/lib/formatters.js CHANGED
@@ -356,6 +356,37 @@ export function formatStops(timetable, config) {
356
356
  return timetable.stops;
357
357
  }
358
358
 
359
+ /*
360
+ * Formats a stop name.
361
+ */
362
+ export function formatStopName(stop) {
363
+ return `${stop.stop_name}${
364
+ stop.type === 'arrival'
365
+ ? ' (Arrival)'
366
+ : stop.type === 'departure'
367
+ ? ' (Departure)'
368
+ : ''
369
+ }`;
370
+ }
371
+
372
+ /*
373
+ * Formats trip "Contines from".
374
+ */
375
+ export function formatTripContinuesFrom(trip) {
376
+ return trip.continues_from_route
377
+ ? trip.continues_from_route.route.route_short_name
378
+ : '';
379
+ }
380
+
381
+ /*
382
+ * Formats trip "Contines as".
383
+ */
384
+ export function formatTripContinuesAs(trip) {
385
+ return trip.continues_as_route
386
+ ? trip.continues_as_route.route.route_short_name
387
+ : '';
388
+ }
389
+
359
390
  /*
360
391
  * Change all stoptimes of a trip so the first trip starts at midnight. Useful
361
392
  * for hourly schedules.
@@ -1,7 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { mkdir, writeFile } from 'node:fs/promises';
3
3
 
4
- import { each, map } from 'lodash-es';
4
+ import { map } from 'lodash-es';
5
5
  import { openDb, getDb, importGtfs } from 'gtfs';
6
6
  import sanitize from 'sanitize-filename';
7
7
  import Timer from 'timer-machine';
@@ -12,6 +12,7 @@ import {
12
12
  generateFolderName,
13
13
  renderPdf,
14
14
  zipFolder,
15
+ generateCSVFileName,
15
16
  } from './file-utils.js';
16
17
  import {
17
18
  log,
@@ -25,8 +26,10 @@ import {
25
26
  setDefaultConfig,
26
27
  getTimetablePagesForAgency,
27
28
  getFormattedTimetablePage,
28
- generateHTML,
29
+ generateTimetableHTML,
30
+ generateTimetableCSV,
29
31
  generateOverviewHTML,
32
+ generateStats,
30
33
  } from './utils.js';
31
34
 
32
35
  /*
@@ -93,7 +96,7 @@ const gtfsToHtml = async (initialConfig) => {
93
96
  );
94
97
  await prepDirectory(exportPath);
95
98
 
96
- if (config.noHead !== true) {
99
+ if (config.noHead !== true && ['html', 'pdf'].includes(config.outputFormat)) {
97
100
  copyStaticAssets(exportPath);
98
101
  }
99
102
 
@@ -138,26 +141,38 @@ const gtfsToHtml = async (initialConfig) => {
138
141
  sanitize(timetablePage.filename)
139
142
  );
140
143
 
141
- const results = await generateHTML(timetablePage, config);
142
-
143
- each(outputStats, (stat, key) => {
144
- if (results.stats[key]) {
145
- outputStats[key] += results.stats[key];
144
+ if (config.outputFormat === 'csv') {
145
+ for (const timetable of timetablePage.timetables) {
146
+ const csv = await generateTimetableCSV(timetable);
147
+ const csvPath = path.join(
148
+ exportPath,
149
+ datePath,
150
+ generateCSVFileName(timetable, timetablePage)
151
+ );
152
+ await writeFile(csvPath, csv);
146
153
  }
147
- });
148
-
149
- const htmlPath = path.join(
150
- exportPath,
151
- datePath,
152
- sanitize(timetablePage.filename)
153
- );
154
- await writeFile(htmlPath, results.html);
154
+ } else {
155
+ const html = await generateTimetableHTML(timetablePage, config);
156
+ const htmlPath = path.join(
157
+ exportPath,
158
+ datePath,
159
+ sanitize(timetablePage.filename)
160
+ );
161
+ await writeFile(htmlPath, html);
155
162
 
156
- if (config.outputFormat === 'pdf') {
157
- await renderPdf(htmlPath);
163
+ if (config.outputFormat === 'pdf') {
164
+ await renderPdf(htmlPath);
165
+ }
158
166
  }
159
167
 
160
168
  timetablePages.push(timetablePage);
169
+ const timetableStats = generateStats(timetablePage);
170
+
171
+ for (const key of Object.keys(outputStats)) {
172
+ if (timetableStats[key]) {
173
+ outputStats[key] += timetableStats[key];
174
+ }
175
+ }
161
176
  } catch (error) {
162
177
  outputStats.warnings.push(error.message);
163
178
  bar.interrupt(error.message);
@@ -167,10 +182,12 @@ const gtfsToHtml = async (initialConfig) => {
167
182
  }
168
183
  /* eslint-enable no-await-in-loop */
169
184
 
170
- // Generate route summary index.html
171
- config.assetPath = '';
172
- const html = await generateOverviewHTML(timetablePages, config);
173
- await writeFile(path.join(exportPath, 'index.html'), html);
185
+ if (config.outputFormat === 'html') {
186
+ // Generate route summary index.html
187
+ config.assetPath = '';
188
+ const html = await generateOverviewHTML(timetablePages, config);
189
+ await writeFile(path.join(exportPath, 'index.html'), html);
190
+ }
174
191
 
175
192
  // Generate output log.txt
176
193
  const logText = await generateLogText(outputStats, config);
@@ -141,3 +141,45 @@ export function getNotesForStoptime(notes, stoptime) {
141
141
  );
142
142
  });
143
143
  }
144
+
145
+ /*
146
+ * Formats a trip name.
147
+ */
148
+ export function formatTripName(trip, index, timetable) {
149
+ let tripName = '';
150
+ if (timetable.routes.length > 1) {
151
+ tripName = trip.route_short_name;
152
+ } else if (trip.trip_short_name) {
153
+ tripName += trip.trip_short_name;
154
+ } else {
155
+ tripName += `Run #${index + 1}`;
156
+ }
157
+
158
+ if (timetableHasDifferentDays(timetable)) {
159
+ tripName += ` ${trip.dayList}`;
160
+ }
161
+
162
+ return tripName;
163
+ }
164
+
165
+ /*
166
+ * Formats a trip name.
167
+ */
168
+ export function formatTripNameForCSV(trip, timetable) {
169
+ let tripName = '';
170
+ if (timetable.routes.length > 1) {
171
+ tripName += `${trip.route_short_name} - `;
172
+ }
173
+
174
+ if (trip.trip_short_name) {
175
+ tripName += trip.trip_short_name;
176
+ } else {
177
+ tripName += trip.trip_id;
178
+ }
179
+
180
+ if (timetableHasDifferentDays(timetable)) {
181
+ tripName += ` - ${trip.dayList}`;
182
+ }
183
+
184
+ return tripName;
185
+ }
package/lib/utils.js CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  sortBy,
20
20
  uniq,
21
21
  uniqBy,
22
+ zip,
22
23
  } from 'lodash-es';
23
24
  import {
24
25
  getCalendarDates,
@@ -37,6 +38,7 @@ import {
37
38
  getTimetablePages,
38
39
  getAgencies,
39
40
  } from 'gtfs';
41
+ import { stringify } from 'csv-stringify';
40
42
  import moment from 'moment';
41
43
  import sqlString from 'sqlstring';
42
44
  import toposort from 'toposort';
@@ -47,11 +49,14 @@ import {
47
49
  formatDays,
48
50
  formatDaysLong,
49
51
  formatFrequency,
52
+ formatStopName,
50
53
  formatStops,
51
54
  formatTimetableId,
52
55
  formatTimetableLabel,
53
56
  formatTimetablePageLabel,
54
57
  formatTrip,
58
+ formatTripContinuesAs,
59
+ formatTripContinuesFrom,
55
60
  isNullOrEmpty,
56
61
  mergeTimetablesWithSameId,
57
62
  resetStoptimesToMidnight,
@@ -67,6 +72,7 @@ import {
67
72
  fromGTFSTime,
68
73
  calendarCodeToCalendar,
69
74
  } from './time-utils.js';
75
+ import { formatTripNameForCSV } from './template-functions.js';
70
76
 
71
77
  const { version } = JSON.parse(
72
78
  readFileSync(new URL('../package.json', import.meta.url))
@@ -1498,7 +1504,7 @@ export async function getFormattedTimetablePage(timetablePageId, config) {
1498
1504
  /*
1499
1505
  * Generate stats about timetable page.
1500
1506
  */
1501
- const generateStats = (timetablePage) => {
1507
+ export const generateStats = (timetablePage) => {
1502
1508
  const stats = {
1503
1509
  stops: 0,
1504
1510
  trips: 0,
@@ -1527,17 +1533,54 @@ const generateStats = (timetablePage) => {
1527
1533
  /*
1528
1534
  * Generate the HTML timetable for a timetable page.
1529
1535
  */
1530
- export async function generateHTML(timetablePage, config) {
1536
+ export function generateTimetableHTML(timetablePage, config) {
1531
1537
  const templateVars = {
1532
1538
  timetablePage,
1533
1539
  config,
1534
1540
  };
1535
- const html = await renderTemplate('timetablepage', templateVars, config);
1536
- const stats = generateStats(timetablePage);
1537
- return {
1538
- html,
1539
- stats,
1540
- };
1541
+ return renderTemplate('timetablepage', templateVars, config);
1542
+ }
1543
+
1544
+ /*
1545
+ * Generate the CSV timetable for a timetable page.
1546
+ */
1547
+ export async function generateTimetableCSV(timetable) {
1548
+ // Generate horizontal orientation, then transpose if vertical is needed.
1549
+ const lines = [];
1550
+
1551
+ lines.push([
1552
+ '',
1553
+ ...timetable.orderedTrips.map((trip) =>
1554
+ formatTripNameForCSV(trip, timetable)
1555
+ ),
1556
+ ]);
1557
+
1558
+ if (timetable.has_continues_from_route) {
1559
+ lines.push([
1560
+ 'Continues from route',
1561
+ ...timetable.orderedTrips.map((trip) => formatTripContinuesFrom(trip)),
1562
+ ]);
1563
+ }
1564
+
1565
+ for (const stop of timetable.stops) {
1566
+ lines.push([
1567
+ formatStopName(stop),
1568
+ ...stop.trips.map((stoptime) => stoptime.formatted_time),
1569
+ ]);
1570
+ }
1571
+
1572
+ if (timetable.has_continues_as_route) {
1573
+ lines.push([
1574
+ 'Continues as route',
1575
+ ...timetable.orderedTrips.map((trip) => formatTripContinuesAs(trip)),
1576
+ ]);
1577
+ }
1578
+
1579
+ if (timetable.orientation === 'vertical') {
1580
+ return stringify(zip(...lines));
1581
+ }
1582
+
1583
+ return stringify(lines);
1541
1584
  }
1542
1585
 
1543
1586
  /*
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "gtfs-to-html",
3
- "version": "2.2.1",
3
+ "version": "2.3.3",
4
4
  "private": false,
5
- "description": "Build human readable transit timetables as HTML or PDF from GTFS",
5
+ "description": "Build human readable transit timetables as HTML, PDF or CSV from GTFS",
6
6
  "keywords": [
7
7
  "transit",
8
8
  "gtfs",
@@ -38,32 +38,33 @@
38
38
  "@turf/helpers": "^6.5.0",
39
39
  "@turf/simplify": "^6.5.0",
40
40
  "archiver": "^5.3.0",
41
- "chalk": "^4.1.2",
42
- "cli-table": "^0.3.6",
41
+ "chalk": "^5.0.0",
42
+ "cli-table": "^0.3.11",
43
43
  "copy-dir": "^1.3.0",
44
- "express": "^4.17.1",
45
- "gtfs": "^3.1.4",
44
+ "csv-stringify": "^6.0.5",
45
+ "express": "^4.17.2",
46
+ "gtfs": "^3.2.4",
46
47
  "js-beautify": "^1.14.0",
47
48
  "lodash-es": "^4.17.21",
48
49
  "moment": "^2.29.1",
49
50
  "morgan": "^1.10.0",
50
- "pretty-error": "^3.0.4",
51
+ "pretty-error": "^4.0.0",
51
52
  "pug": "^3.0.2",
52
- "puppeteer": "^10.4.0",
53
+ "puppeteer": "^13.1.1",
53
54
  "sanitize-filename": "^1.6.3",
54
55
  "sqlstring": "^2.3.2",
55
56
  "timer-machine": "^1.1.0",
56
57
  "toposort": "^2.0.2",
57
58
  "untildify": "^4.0.0",
58
- "yargs": "^17.2.1"
59
+ "yargs": "^17.3.1"
59
60
  },
60
61
  "devDependencies": {
61
- "eslint": "^8.0.1",
62
+ "eslint": "^8.7.0",
62
63
  "eslint-config-prettier": "^8.3.0",
63
64
  "eslint-config-xo": "^0.39.0",
64
- "husky": "^7.0.2",
65
- "prettier": "^2.4.1",
66
- "pretty-quick": "^3.1.1"
65
+ "husky": "^7.0.4",
66
+ "prettier": "^2.5.1",
67
+ "pretty-quick": "^3.1.3"
67
68
  },
68
69
  "engines": {
69
70
  "node": ">= 12.14.0"
@@ -162,24 +162,26 @@ a:hover {
162
162
  }
163
163
 
164
164
  .route-color-swatch {
165
- width: 26px;
165
+ min-width: 26px;
166
166
  height: 26px;
167
- border-radius: 50%;
167
+ border-radius: 13px;
168
168
  text-align: center;
169
169
  line-height: 26px;
170
170
  font-size: 14px;
171
- letter-spacing: -1px;
171
+ letter-spacing: -0.5px;
172
+ padding: 0 5px;
172
173
  }
173
174
 
174
175
  .route-color-swatch-large {
175
- width: 40px;
176
+ min-width: 40px;
176
177
  height: 40px;
177
- border-radius: 50%;
178
+ border-radius: 20px;
178
179
  text-align: center;
179
180
  line-height: 40px;
180
181
  font-size: 20px;
181
182
  font-weight: bold;
182
183
  letter-spacing: -1px;
184
+ padding: 0 8px;
183
185
  }
184
186
 
185
187
  @media screen and (max-width: 767px) {
@@ -11,23 +11,6 @@
11
11
  return summary;
12
12
  }
13
13
 
14
- function formatTripName(trip, idx, timetable) {
15
- let tripName = '';
16
- if (timetable.routes.length > 1) {
17
- tripName = trip.route_short_name;
18
- } else if (trip.trip_short_name) {
19
- tripName += trip.trip_short_name;
20
- } else {
21
- tripName += `Run #${idx + 1}`;
22
- }
23
-
24
- if (timetableHasDifferentDays(timetable)) {
25
- tripName += ` ${trip.dayList}`;
26
- }
27
-
28
- return tripName;
29
- }
30
-
31
14
  function formatRouteName(route) {
32
15
  const hasLongName = route.route_long_name !== '' && route.route_long_name !== null;
33
16
 
@@ -6,9 +6,9 @@ block extraHeader
6
6
  if config.showMap
7
7
  script(src="https://unpkg.com/jquery@3.6.0/dist/jquery.min.js" crossorigin="anonymous")
8
8
  script(src="https://unpkg.com/lodash@4.17.21/lodash.min.js" crossorigin="anonymous")
9
- script(src="https://api.mapbox.com/mapbox-gl-js/v2.5.0/mapbox-gl.js")
9
+ script(src="https://api.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.js")
10
10
  script.
11
11
  mapboxgl.accessToken = '#{config.mapboxAccessToken}';
12
12
  script(src=`${config.assetPath}js/system-map.js`)
13
13
 
14
- link(href="https://api.mapbox.com/mapbox-gl-js/v2.5.0/mapbox-gl.css" rel="stylesheet")
14
+ link(href="https://api.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.css" rel="stylesheet")
@@ -9,10 +9,10 @@ block extraHeader
9
9
  script(src=`${config.assetPath}js/timetable-menu.js`)
10
10
 
11
11
  if config.showMap
12
- script(src="https://api.mapbox.com/mapbox-gl-js/v2.5.0/mapbox-gl.js")
12
+ script(src="https://api.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.js")
13
13
  script.
14
14
  mapboxgl.accessToken = '#{config.mapboxAccessToken}';
15
15
  script(src=`${config.assetPath}js/timetable-map.js`)
16
16
 
17
- link(href="https://api.mapbox.com/mapbox-gl-js/v2.5.0/mapbox-gl.css" rel="stylesheet")
17
+ link(href="https://api.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.css" rel="stylesheet")
18
18