gtfs-to-html 2.5.9 → 2.6.1

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,33 @@ 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.1] - 2024-03-26
9
+
10
+ ### Fixed
11
+
12
+ - Fix for missing stops
13
+
14
+ ### Updated
15
+ - Dependency updates
16
+ - Day List selector label wording
17
+ - Updates to timetable map
18
+ - Filename format for CSV export files
19
+
20
+ ## [2.6.0] - 2024-02-27
21
+
22
+ ### Updated
23
+
24
+ - Improved timetable_page_label for pages with a single timetable
25
+ - Better default timetable styles
26
+ - Default route_color and route_text_color values
27
+ - Better system map layer ordering
28
+ - Dependency updates
29
+
30
+ ### Added
31
+
32
+ - `showCalendarExceptions` configuration option
33
+ - Add "timepoint" class to stoptimes
34
+
8
35
  ## [2.5.9] - 2024-01-24
9
36
 
10
37
  ### Updated
@@ -11,14 +11,22 @@
11
11
  "coordinatePrecision": 5,
12
12
  "dateFormat": "MMM D, YYYY",
13
13
  "daysShortStrings": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
14
- "daysStrings": ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"],
14
+ "daysStrings": [
15
+ "Monday",
16
+ "Tuesday",
17
+ "Wednesday",
18
+ "Thursday",
19
+ "Friday",
20
+ "Saturday",
21
+ "Sunday"
22
+ ],
15
23
  "defaultOrientation": "vertical",
16
24
  "effectiveDate": "July 8, 2016",
17
25
  "interpolatedStopSymbol": "•",
18
26
  "interpolatedStopText": "Estimated time of arrival",
19
- "linkStopUrls": true,
27
+ "linkStopUrls": false,
20
28
  "mapboxAccessToken": "YOUR MAPBOX ACCESS TOKEN",
21
- "menuType": "simple",
29
+ "menuType": "jump",
22
30
  "noDropoffSymbol": "‡",
23
31
  "noDropoffText": "No drop off available",
24
32
  "noHead": false,
@@ -34,8 +42,9 @@
34
42
  "serviceNotProvidedOnText": "Service not provided on",
35
43
  "serviceProvidedOnText": "Service provided on",
36
44
  "showArrivalOnDifference": 0.2,
45
+ "showCalendarExceptions": true,
37
46
  "showMap": true,
38
- "showOnlyTimepoint": false,
47
+ "showOnlyTimepoint": true,
39
48
  "showRouteTitle": true,
40
49
  "showStopCity": false,
41
50
  "showStopDescription": false,
package/lib/file-utils.js CHANGED
@@ -12,7 +12,12 @@ import puppeteer from 'puppeteer';
12
12
  import sanitize from 'sanitize-filename';
13
13
  import untildify from 'untildify';
14
14
 
15
- import { isNullOrEmpty, formatDays } from './formatters.js';
15
+ import {
16
+ isNullOrEmpty,
17
+ formatDays,
18
+ formatRouteColor,
19
+ formatRouteTextColor,
20
+ } from './formatters.js';
16
21
  import * as templateFunctions from './template-functions.js';
17
22
 
18
23
  /*
@@ -22,12 +27,12 @@ export async function getConfig(argv) {
22
27
  try {
23
28
  const data = await readFile(
24
29
  path.resolve(untildify(argv.configPath)),
25
- 'utf8'
30
+ 'utf8',
26
31
  ).catch((error) => {
27
32
  console.error(
28
33
  new Error(
29
- `Cannot find configuration file at \`${argv.configPath}\`. Use config-sample.json as a starting point, pass --configPath option`
30
- )
34
+ `Cannot find configuration file at \`${argv.configPath}\`. Use config-sample.json as a starting point, pass --configPath option`,
35
+ ),
31
36
  );
32
37
  throw error;
33
38
  });
@@ -45,8 +50,8 @@ export async function getConfig(argv) {
45
50
  } catch (error) {
46
51
  console.error(
47
52
  new Error(
48
- `Cannot parse configuration file at \`${argv.configPath}\`. Check to ensure that it is valid JSON.`
49
- )
53
+ `Cannot parse configuration file at \`${argv.configPath}\`. Check to ensure that it is valid JSON.`,
54
+ ),
50
55
  );
51
56
  throw error;
52
57
  }
@@ -65,14 +70,14 @@ function getTemplatePath(templateFileName, config) {
65
70
  if (config.templatePath !== undefined) {
66
71
  return path.join(
67
72
  untildify(config.templatePath),
68
- `${fullTemplateFileName}.pug`
73
+ `${fullTemplateFileName}.pug`,
69
74
  );
70
75
  }
71
76
 
72
77
  return path.join(
73
78
  fileURLToPath(import.meta.url),
74
79
  '../../views/default',
75
- `${fullTemplateFileName}.pug`
80
+ `${fullTemplateFileName}.pug`,
76
81
  );
77
82
  }
78
83
 
@@ -86,7 +91,7 @@ export async function prepDirectory(exportPath) {
86
91
  } catch (error) {
87
92
  if (error.code === 'ENOENT') {
88
93
  throw new Error(
89
- `Unable to write to ${exportPath}. Try running this command from a writable directory.`
94
+ `Unable to write to ${exportPath}. Try running this command from a writable directory.`,
90
95
  );
91
96
  }
92
97
 
@@ -100,7 +105,7 @@ export async function prepDirectory(exportPath) {
100
105
  export function copyStaticAssets(exportPath) {
101
106
  const staticAssetPath = path.join(
102
107
  fileURLToPath(import.meta.url),
103
- '../../public'
108
+ '../../public',
104
109
  );
105
110
  copydir.sync(path.join(staticAssetPath, 'css'), path.join(exportPath, 'css'));
106
111
  copydir.sync(path.join(staticAssetPath, 'js'), path.join(exportPath, 'js'));
@@ -127,7 +132,7 @@ export function zipFolder(exportPath) {
127
132
  /*
128
133
  * Generate the filename for a given timetable.
129
134
  */
130
- export function generateFileName(timetable, config) {
135
+ export function generateFileName(timetable, config, extension = 'html') {
131
136
  let filename = timetable.timetable_id;
132
137
 
133
138
  for (const route of timetable.routes) {
@@ -140,24 +145,11 @@ export function generateFileName(timetable, config) {
140
145
  filename += `_${timetable.direction_id}`;
141
146
  }
142
147
 
143
- filename += `_${formatDays(timetable, config).replace(/\s/g, '')}.html`;
148
+ filename += `_${formatDays(timetable, config).replace(/\s/g, '')}.${extension}`;
144
149
 
145
150
  return sanitize(filename).toLowerCase();
146
151
  }
147
152
 
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
-
161
153
  /*
162
154
  * Generates the folder name for a timetable page based on the date.
163
155
  */
@@ -181,6 +173,8 @@ export async function renderTemplate(templateFileName, templateVars, config) {
181
173
  const html = await renderFile(templatePath, {
182
174
  _,
183
175
  ...templateFunctions,
176
+ formatRouteColor,
177
+ formatRouteTextColor,
184
178
  ...templateVars,
185
179
  });
186
180
 
package/lib/formatters.js CHANGED
@@ -116,6 +116,10 @@ function formatStopTime(stoptime, timetable, config) {
116
116
  }
117
117
  }
118
118
 
119
+ if (stoptime.timepoint === 1) {
120
+ stoptime.classes.push('timepoint');
121
+ }
122
+
119
123
  return stoptime;
120
124
  }
121
125
  /* eslint-enable complexity */
@@ -374,8 +378,8 @@ export function formatStopName(stop) {
374
378
  stop.type === 'arrival'
375
379
  ? ' (Arrival)'
376
380
  : stop.type === 'departure'
377
- ? ' (Departure)'
378
- : ''
381
+ ? ' (Departure)'
382
+ : ''
379
383
  }`;
380
384
  }
381
385
 
@@ -442,6 +446,22 @@ export function updateStoptimesByOffset(trip, offsetSeconds) {
442
446
  });
443
447
  }
444
448
 
449
+ /*
450
+ * Format a route color as a hex color.
451
+ */
452
+ export function formatRouteColor(route) {
453
+ // Defaults to #000000 (black) if no color is provided.
454
+ return route.route_color ? `#${route.route_color}` : '#000000';
455
+ }
456
+
457
+ /*
458
+ * Format a route text color as a hex color.
459
+ */
460
+ export function formatRouteTextColor(route) {
461
+ // Defaults to #FFFFFF (white) if no color is provided.
462
+ return route.route_text_color ? `#${route.route_text_color}` : '#FFFFFF';
463
+ }
464
+
445
465
  /*
446
466
  * Format a label for a timetable.
447
467
  */
@@ -488,21 +508,26 @@ export function formatTimetablePageLabel(timetablePage) {
488
508
  return timetablePage.timetable_page_label;
489
509
  }
490
510
 
491
- // Get label from first timetable.
492
511
  if (
493
512
  timetablePage.consolidatedTimetables &&
494
513
  timetablePage.consolidatedTimetables.length > 0
495
514
  ) {
496
- const routes = uniqBy(
497
- flatMap(
498
- timetablePage.consolidatedTimetables,
499
- (timetable) => timetable.routes,
500
- ),
501
- 'route_id',
502
- );
503
- const timetablePageLabel = routes.map((route) => formatRouteName(route));
515
+ if (timetablePage.consolidatedTimetables.length === 1) {
516
+ // Get label from first timetable if there is only one
517
+ return timetablePage.consolidatedTimetables[0].timetable_label;
518
+ } else {
519
+ // Otherwise, use route names from all timetables
520
+ const routes = uniqBy(
521
+ flatMap(
522
+ timetablePage.consolidatedTimetables,
523
+ (timetable) => timetable.routes,
524
+ ),
525
+ 'route_id',
526
+ );
527
+ const timetablePageLabel = routes.map((route) => formatRouteName(route));
504
528
 
505
- return timetablePageLabel.join(' and ');
529
+ return timetablePageLabel.join(' and ');
530
+ }
506
531
  }
507
532
 
508
533
  return 'Unknown';
@@ -12,7 +12,7 @@ import {
12
12
  generateFolderName,
13
13
  renderPdf,
14
14
  zipFolder,
15
- generateCSVFileName,
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
- generateCSVFileName(timetable, timetablePage),
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 parentStationStop = stops.find(
1157
- (stop) => stop.stop_id === stoptime.stop_id,
1158
- );
1159
- stoptime.stop_id =
1160
- parentStationStop.parent_station || parentStationStop.stop_id;
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
  }
@@ -1413,11 +1417,12 @@ export function setDefaultConfig(initialConfig) {
1413
1417
  interpolatedStopSymbol: '•',
1414
1418
  interpolatedStopText: 'Estimated time of arrival',
1415
1419
  gtfsToHtmlVersion: version,
1420
+ linkStopUrls: false,
1416
1421
  menuType: 'jump',
1417
1422
  noDropoffSymbol: '‡',
1418
1423
  noDropoffText: 'No drop off available',
1419
1424
  noHead: false,
1420
- noPickupSymbol: '***',
1425
+ noPickupSymbol: '**',
1421
1426
  noPickupText: 'No pickup available',
1422
1427
  noServiceSymbol: '-',
1423
1428
  noServiceText: 'No service at this stop',
@@ -1429,6 +1434,7 @@ export function setDefaultConfig(initialConfig) {
1429
1434
  serviceNotProvidedOnText: 'Service not provided on',
1430
1435
  serviceProvidedOnText: 'Service provided on',
1431
1436
  showArrivalOnDifference: 0.2,
1437
+ showCalendarExceptions: true,
1432
1438
  showMap: false,
1433
1439
  showOnlyTimepoint: false,
1434
1440
  showRouteTitle: true,
@@ -1482,10 +1488,6 @@ export function getFormattedTimetablePage(timetablePageId, config) {
1482
1488
  ['route_color', 'route_text_color', 'agency_id'],
1483
1489
  );
1484
1490
 
1485
- timetablePage.routeColors = timetableRoutes.map((route) => route.route_color);
1486
- timetablePage.routeTextColors = timetableRoutes.map(
1487
- (route) => route.route_text_color,
1488
- );
1489
1491
  timetablePage.agency_ids = compact(
1490
1492
  timetableRoutes.map((route) => route.agency_id),
1491
1493
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtfs-to-html",
3
- "version": "2.5.9",
3
+ "version": "2.6.1",
4
4
  "private": false,
5
5
  "description": "Build human readable transit timetables as HTML, PDF or CSV from GTFS",
6
6
  "keywords": [
@@ -37,31 +37,31 @@
37
37
  "dependencies": {
38
38
  "@turf/helpers": "^6.5.0",
39
39
  "@turf/simplify": "^6.5.0",
40
- "archiver": "^6.0.1",
40
+ "archiver": "^7.0.1",
41
41
  "cli-table": "^0.3.11",
42
42
  "copy-dir": "^1.3.0",
43
- "csv-stringify": "^6.4.5",
44
- "express": "^4.18.2",
45
- "gtfs": "^4.6.0",
46
- "js-beautify": "^1.14.11",
43
+ "csv-stringify": "^6.4.6",
44
+ "express": "^4.19.2",
45
+ "gtfs": "^4.9.0",
46
+ "js-beautify": "^1.15.1",
47
47
  "lodash-es": "^4.17.21",
48
48
  "moment": "^2.30.1",
49
49
  "morgan": "^1.10.0",
50
50
  "pretty-error": "^4.0.0",
51
51
  "pug": "^3.0.2",
52
- "puppeteer": "^21.9.0",
52
+ "puppeteer": "^22.6.1",
53
53
  "sanitize-filename": "^1.6.3",
54
54
  "sqlstring": "^2.3.3",
55
55
  "timer-machine": "^1.1.0",
56
56
  "toposort": "^2.0.2",
57
57
  "untildify": "^5.0.0",
58
58
  "yargs": "^17.7.2",
59
- "yoctocolors": "^1.0.0"
59
+ "yoctocolors": "^2.0.0"
60
60
  },
61
61
  "devDependencies": {
62
- "husky": "^8.0.3",
63
- "lint-staged": "^15.2.0",
64
- "prettier": "^3.2.4"
62
+ "husky": "^9.0.11",
63
+ "lint-staged": "^15.2.2",
64
+ "prettier": "^3.2.5"
65
65
  },
66
66
  "engines": {
67
67
  "node": ">= 18.0.0"
@@ -163,22 +163,22 @@ a:hover {
163
163
  }
164
164
 
165
165
  .route-color-swatch {
166
- min-width: 26px;
167
- height: 26px;
168
- border-radius: 13px;
166
+ min-width: 34px;
167
+ height: 34px;
168
+ border-radius: 17px;
169
169
  text-align: center;
170
- line-height: 26px;
170
+ line-height: 34px;
171
171
  font-size: 14px;
172
172
  letter-spacing: -0.5px;
173
173
  padding: 0 5px;
174
174
  }
175
175
 
176
176
  .route-color-swatch-large {
177
- min-width: 40px;
178
- height: 40px;
179
- border-radius: 20px;
177
+ min-width: 46px;
178
+ height: 46px;
179
+ border-radius: 23px;
180
180
  text-align: center;
181
- line-height: 40px;
181
+ line-height: 46px;
182
182
  font-size: 20px;
183
183
  font-weight: bold;
184
184
  letter-spacing: -1px;
@@ -3,33 +3,33 @@
3
3
 
4
4
  const maps = {};
5
5
 
6
- function formatRouteName(route) {
7
- let routeName = '';
8
- if (route.route_short_name !== undefined) {
9
- routeName += route.route_short_name;
10
- } else if (route.route_long_name !== undefined) {
11
- routeName += route.route_long_name;
12
- }
13
-
14
- return routeName;
6
+ function formatRouteColor(route) {
7
+ return route.route_color || '#000000';
15
8
  }
16
9
 
17
10
  function formatRoute(route) {
18
11
  const html = route.route_url
19
- ? $('<a>').attr('href', route.route_url)
12
+ ? $('<a>').attr('href', route.route_url).addClass('hover:no-underline')
20
13
  : $('<div>');
21
14
 
22
- html.addClass('route-item text-sm mb-2');
15
+ html.addClass('route-item text-xs mb-2');
23
16
 
24
17
  if (route.route_color) {
18
+ // Only add color swatch if route has a color
25
19
  $('<div>')
26
- .addClass('route-color-swatch mr-2 flex-shrink-0')
27
- .css('backgroundColor', route.route_color)
20
+ .addClass('flex items-center gap-2')
21
+ .html([
22
+ $('<div>')
23
+ .addClass('route-color-swatch flex-shrink-0 text-white')
24
+ .css('backgroundColor', formatRouteColor(route))
25
+ .text(route.route_short_name || ''),
26
+ $('<div>')
27
+ .addClass('hover:underline')
28
+ .text(route.route_long_name || ''),
29
+ ])
28
30
  .appendTo(html);
29
31
  }
30
32
 
31
- $('<span>').text(formatRouteName(route)).appendTo(html);
32
-
33
33
  return html.prop('outerHTML');
34
34
  }
35
35
 
@@ -60,7 +60,7 @@ function formatStopPopup(feature) {
60
60
  $('<strong>').text(feature.properties.stop_code).appendTo(html);
61
61
  }
62
62
 
63
- $('<div>').text('Routes Served:').appendTo(html);
63
+ $('<div>').addClass('text-sm').text('Routes Served:').appendTo(html);
64
64
 
65
65
  $(html).append(routes.map((route) => formatRoute(route)));
66
66
 
@@ -83,7 +83,7 @@ function getBounds(geojson) {
83
83
  }
84
84
 
85
85
  function createSystemMap(id, geojson) {
86
- const defaultRouteColor = '#FF4728';
86
+ const defaultRouteColor = '#000000';
87
87
  const lineLayout = {
88
88
  'line-join': 'round',
89
89
  'line-cap': 'round',
@@ -220,54 +220,54 @@ function createSystemMap(id, geojson) {
220
220
  firstSymbolId,
221
221
  );
222
222
 
223
- // Add highlighted route white outlines next
223
+ // Add route lines next
224
224
  map.addLayer(
225
225
  {
226
- id: `highlighted-route-outlines`,
226
+ id: 'routes',
227
227
  type: 'line',
228
228
  source: {
229
229
  type: 'geojson',
230
230
  data: geojson,
231
231
  },
232
232
  paint: {
233
- 'line-color': '#FFFFFF',
233
+ 'line-color': ['coalesce', ['get', 'route_color'], defaultRouteColor],
234
234
  'line-opacity': 1,
235
235
  'line-width': {
236
- base: 10,
236
+ base: 4,
237
237
  stops: [
238
- [14, 16],
239
- [18, 40],
238
+ [14, 6],
239
+ [18, 16],
240
240
  ],
241
241
  },
242
242
  },
243
243
  layout: lineLayout,
244
- filter: ['==', ['get', 'route_id'], 'none'],
244
+ filter: ['has', 'route_id'],
245
245
  },
246
246
  firstSymbolId,
247
247
  );
248
248
 
249
- // Add route lines next
249
+ // Add highlighted route white outlines next
250
250
  map.addLayer(
251
251
  {
252
- id: 'routes',
252
+ id: `highlighted-route-outlines`,
253
253
  type: 'line',
254
254
  source: {
255
255
  type: 'geojson',
256
256
  data: geojson,
257
257
  },
258
258
  paint: {
259
- 'line-color': ['coalesce', ['get', 'route_color'], defaultRouteColor],
259
+ 'line-color': '#FFFFFF',
260
260
  'line-opacity': 1,
261
261
  'line-width': {
262
- base: 4,
262
+ base: 10,
263
263
  stops: [
264
- [14, 6],
265
- [18, 16],
264
+ [14, 16],
265
+ [18, 40],
266
266
  ],
267
267
  },
268
268
  },
269
269
  layout: lineLayout,
270
- filter: ['has', 'route_id'],
270
+ filter: ['==', ['get', 'route_id'], 'none'],
271
271
  },
272
272
  firstSymbolId,
273
273
  );
@@ -3,33 +3,38 @@
3
3
 
4
4
  const maps = {};
5
5
 
6
- function formatRouteName(route) {
7
- let routeName = '';
8
- if (route.route_short_name !== undefined) {
9
- routeName += route.route_short_name;
10
- } else if (route.route_long_name !== undefined) {
11
- routeName += route.route_long_name;
12
- }
13
-
14
- return routeName;
6
+ function formatRouteColor(route) {
7
+ return route.route_color || '#000000';
15
8
  }
16
9
 
17
10
  function formatRoute(route) {
18
11
  const html = route.route_url
19
- ? $('<a>').attr('href', route.route_url)
12
+ ? $('<a>').attr('href', route.route_url).addClass('hover:no-underline')
20
13
  : $('<div>');
21
14
 
22
- html.addClass('route-item text-sm mb-2');
15
+ html.addClass('route-item text-xs mb-2');
23
16
 
24
17
  if (route.route_color) {
18
+ // Only add color swatch if route has a color
25
19
  $('<div>')
26
- .addClass('route-color-swatch mr-2 flex-shrink-0')
27
- .css('backgroundColor', route.route_color)
20
+ .addClass('flex items-center gap-2')
21
+ .html([
22
+ $('<div>')
23
+ .addClass('route-color-swatch flex-shrink-0 text-white')
24
+ .css('backgroundColor', formatRouteColor(route))
25
+ .text(route.route_short_name || ''),
26
+ $('<div>')
27
+ .addClass('hover:underline')
28
+ .text(route.route_long_name || ''),
29
+ ])
30
+ .appendTo(html);
31
+ } else {
32
+ $('<div>')
33
+ .addClass('hover:underline')
34
+ .text(route.route_long_name || '')
28
35
  .appendTo(html);
29
36
  }
30
37
 
31
- $('<span>').text(formatRouteName(route)).appendTo(html);
32
-
33
38
  return html.prop('outerHTML');
34
39
  }
35
40
 
@@ -48,7 +53,7 @@ function formatStopPopup(feature, routes) {
48
53
  $('<strong>').text(feature.properties.stop_code).appendTo(html);
49
54
  }
50
55
 
51
- $('<div>').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
 
@@ -71,7 +76,7 @@ function getBounds(geojson) {
71
76
  }
72
77
 
73
78
  function createMap(id, geojson, routes) {
74
- const defaultRouteColor = '#FF4728';
79
+ const defaultRouteColor = '#000000';
75
80
  const lineLayout = {
76
81
  'line-join': 'round',
77
82
  'line-cap': 'round',
@@ -10,12 +10,12 @@ include formatting_functions.pug
10
10
  h1.text-2xl.pt-4.pb-2= `${formatAgencyName(timetablePageGroup.agency)} Routes`
11
11
  each timetablePage in timetablePageGroup.timetablePages
12
12
  if config.allowEmptyTimetables || timetablePage.consolidatedTimetables.length > 0
13
- a.block.p-2.border-b.border-slate-200(class="hover:bg-slate-200 hover:no-underline" href=`${timetablePage.relativePath}` data-route-ids=`${timetablePage.route_ids ? timetablePage.route_ids.join(',') : ''}`)
13
+ a.block.p-2.border-b.border-slate-200(class="hover:bg-slate-100 hover:no-underline" href=`${timetablePage.relativePath}` data-route-ids=`${timetablePage.route_ids ? timetablePage.route_ids.join(',') : ''}`)
14
14
  .text-lg.text-gray-800.leading-none= timetablePage.timetable_page_label
15
15
  each route in _.uniqBy(_.flatMap(timetablePage.consolidatedTimetables, timetable => timetable.routes), 'route_id')
16
- .flex.my-1
17
- .route-color-swatch.flex-none.mr-2(style=`background-color: #${route.route_color}; color: #${route.route_text_color};`)= route.route_short_name || ''
18
- .mt-1.text-gray-600.leading-none= formatRouteName(route)
16
+ .flex.my-1.items-center.gap-1
17
+ .route-color-swatch.flex-none(style=`background-color: ${formatRouteColor(route)}; color: ${formatRouteTextColor(route)};`)= route.route_short_name || ''
18
+ .text-gray-600.leading-none= formatRouteName(route)
19
19
  .inline-flex.items-center.justify-center.px-2.py-1.text-xs.font-bold.leading-none.text-slate-800.bg-slate-200.rounded-full= timetablePage.dayList
20
20
  if config.showMap
21
21
  .map.ml-4.h-full.w-full(id="system_map")
@@ -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 Days of Week
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))