gtfs-to-html 2.9.0 → 2.9.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.
Files changed (40) hide show
  1. package/config-sample.json +68 -0
  2. package/dist/app/index.js +2 -1
  3. package/dist/app/index.js.map +1 -1
  4. package/dist/bin/gtfs-to-html.js +16 -9
  5. package/dist/bin/gtfs-to-html.js.map +1 -1
  6. package/dist/index.js +16 -9
  7. package/dist/index.js.map +1 -1
  8. package/docker/Dockerfile +14 -0
  9. package/docker/README.md +5 -0
  10. package/docker/config.json +21 -0
  11. package/docker/docker-compose.yml +10 -0
  12. package/examples/stop_attributes.txt +6 -0
  13. package/examples/timetable_notes.txt +8 -0
  14. package/examples/timetable_notes_references.txt +8 -0
  15. package/examples/timetable_pages.txt +3 -0
  16. package/examples/timetable_stop_order.txt +16 -0
  17. package/examples/timetables.txt +9 -0
  18. package/package.json +8 -3
  19. package/views/default/css/overview_styles.css +198 -0
  20. package/views/default/css/timetable_pdf_styles.css +69 -0
  21. package/views/default/css/timetable_styles.css +522 -0
  22. package/views/default/formatting_functions.pug +104 -0
  23. package/views/default/js/system-map.js +594 -0
  24. package/views/default/js/timetable-map.js +753 -0
  25. package/views/default/js/timetable-menu.js +57 -0
  26. package/views/default/layout.pug +11 -0
  27. package/views/default/overview.pug +27 -0
  28. package/views/default/overview_full.pug +16 -0
  29. package/views/default/timetable_continuation_as.pug +7 -0
  30. package/views/default/timetable_continuation_from.pug +7 -0
  31. package/views/default/timetable_horizontal.pug +42 -0
  32. package/views/default/timetable_hourly.pug +30 -0
  33. package/views/default/timetable_map.pug +15 -0
  34. package/views/default/timetable_menu.pug +48 -0
  35. package/views/default/timetable_note_symbol.pug +5 -0
  36. package/views/default/timetable_stop_name.pug +13 -0
  37. package/views/default/timetable_stoptime.pug +17 -0
  38. package/views/default/timetable_vertical.pug +67 -0
  39. package/views/default/timetablepage.pug +64 -0
  40. package/views/default/timetablepage_full.pug +25 -0
@@ -0,0 +1,57 @@
1
+ /* global window, document, $, maps */
2
+ /* eslint no-unused-vars: "off" */
3
+
4
+ $(() => {
5
+ showSelectedTimetable();
6
+
7
+ $('#day_list_selector input[name="dayList"]').change(() => {
8
+ showSelectedTimetable();
9
+ });
10
+
11
+ $('#direction_name_selector input[name="directionName"]').change(() => {
12
+ showSelectedTimetable();
13
+ });
14
+
15
+ function showSelectedTimetable() {
16
+ if ($('.timetable').length === 1) {
17
+ showTimetable($('.timetable').data('timetable-id'));
18
+ return false;
19
+ }
20
+
21
+ $('#day_list_selector input[name="dayList"]').each((index, element) => {
22
+ $(element)
23
+ .parents('label')
24
+ .toggleClass('btn-blue', $(element).is(':checked'));
25
+ $(element)
26
+ .parents('label')
27
+ .toggleClass('btn-gray', $(element).is(':not(:checked)'));
28
+ });
29
+
30
+ $('#direction_name_selector input[name="directionName"]').each(
31
+ (index, element) => {
32
+ $(element)
33
+ .parents('label')
34
+ .toggleClass('btn-blue', $(element).is(':checked'));
35
+ $(element)
36
+ .parents('label')
37
+ .toggleClass('btn-gray', $(element).is(':not(:checked)'));
38
+ },
39
+ );
40
+
41
+ const dayList = $('#day_list_selector input[name="dayList"]:checked').val();
42
+ const directionName = $(
43
+ '#direction_name_selector input[name="directionName"]:checked',
44
+ ).val();
45
+
46
+ $('.timetable').hide();
47
+ const id = $(
48
+ `.timetable[data-day-list="${dayList}"][data-direction-name="${directionName}"]`,
49
+ ).data('timetable-id');
50
+ showTimetable(id);
51
+ }
52
+
53
+ function showTimetable(id) {
54
+ $(`#timetable_id_${id}`).show();
55
+ toggleMap(id);
56
+ }
57
+ });
@@ -0,0 +1,11 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title= title
5
+ meta(charset="utf-8")
6
+ meta(name="viewport" content="initial-scale=1.0, width=device-width")
7
+
8
+ block extraHeader
9
+
10
+ body
11
+ block content
@@ -0,0 +1,27 @@
1
+ include formatting_functions.pug
2
+
3
+ .timetable-overview
4
+ if !timetablePages || !timetablePages.length
5
+ h1 No timetables found
6
+ else
7
+ .overview-list
8
+ each timetablePageGroup in getAgencyTimetableGroups(timetablePages, agencies)
9
+ h1= `${!agency || !agency.agency_name ? '' : agency.agency_name} Routes`
10
+ each timetablePage in timetablePageGroup.timetablePages
11
+ if config.allowEmptyTimetables || timetablePage.consolidatedTimetables.length > 0
12
+ a.timetable-page-link(href=`${timetablePage.relativePath}` data-route-ids=`${timetablePage.route_ids ? timetablePage.route_ids.join(',') : ''}`)
13
+ - const timetableRouteList = _.uniqBy(_.flatMap(timetablePage.consolidatedTimetables, timetable => timetable.routes), 'route_id')
14
+ each route in timetableRouteList
15
+ .route-color-swatch-large(style=`background-color: ${formatRouteColor(route)}; color: ${formatRouteTextColor(route)};`)= route.route_short_name || ''
16
+ div
17
+ .timetable-page-label= timetablePage.timetable_page_label || timetableRouteList.map(route => formatRouteName(route)).join(' and ')
18
+ .badge-gray= timetablePage.dayList
19
+ if config.showMap
20
+ #system_map.overview-map
21
+
22
+ //- Use !{variable} format to inject values from pug to client side js
23
+ script.
24
+ (function() {
25
+ const geojson = !{JSON.stringify(geojson) || '\'\''};
26
+ createSystemMap('system_map', geojson);
27
+ })();
@@ -0,0 +1,16 @@
1
+ extends layout
2
+ block content
3
+ include overview.pug
4
+
5
+ block extraHeader
6
+ if config.showMap
7
+ script(src="https://unpkg.com/jquery@3.7.1/dist/jquery.min.js" crossorigin="anonymous")
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/v3.6.0/mapbox-gl.js")
10
+ script.
11
+ mapboxgl.accessToken = '#{config.mapboxAccessToken}';
12
+ script(src=`${config.assetPath}js/system-map.js`)
13
+
14
+ link(href="https://api.mapbox.com/mapbox-gl-js/v3.6.0/mapbox-gl.css" rel="stylesheet")
15
+
16
+ link(rel="stylesheet" href=`${config.assetPath}css/overview_styles.css`)
@@ -0,0 +1,7 @@
1
+ - const routeColorCss = trip.continues_as_route ? getRouteColorsAsCss(trip.continues_as_route.route) : ''
2
+ td.run-footer(style=routeColorCss)
3
+ if trip.continues_as_route
4
+ if trip.continues_as_route.route.route_url
5
+ a.continues-as-route(href=`${trip.continues_as_route.route.route_url}` style=routeColorCss data-trip-id=trip.continues_as_route.trip_id)= trip.continues_as_route.route.route_short_name
6
+ else
7
+ .continues-as-route(data-trip-id=trip.continues_as_route.trip_id)= trip.continues_as_route.route.route_short_name
@@ -0,0 +1,7 @@
1
+ - const routeColorCss = trip.continues_from_route ? getRouteColorsAsCss(trip.continues_from_route.route) : ''
2
+ td.run-footer(style=routeColorCss)
3
+ if trip.continues_from_route
4
+ if trip.continues_from_route.route.route_url
5
+ a.continues-from-route(href=`${trip.continues_from_route.route.route_url}` style=routeColorCss data-trip-id=trip.continues_from_route.trip_id)= trip.continues_from_route.route.route_short_name
6
+ else
7
+ .continues-from-route(data-trip-id=trip.continues_from_route.trip_id)= trip.continues_from_route.route.route_short_name
@@ -0,0 +1,42 @@
1
+ .table-container
2
+ table.table-horizontal(summary=`This table shows schedules for a selection of key stops on the route for ${timetable.timetable_label} ${timetable.dayList}. Schedule times are listed in rows, starting with the stop name in the first cell of the row.` data-orientation="horizontal")
3
+ caption.sr-only= `${timetable.timetable_label} | ${timetable.dayList}`
4
+ colgroup
5
+ col
6
+ each trip, idx in timetable.orderedTrips
7
+ col(id=`trip_id_${formatHtmlId(trip.trip_id)}` class=`run_${idx} ${(trip.trip_short_name) ? 'trip_short_name_' + trip.trip_short_name : ''}`)
8
+ thead
9
+ tr
10
+ th.stop-header(scope="col") Stop
11
+ each trip, idx in timetable.orderedTrips
12
+ th.run-header(scope="col")= formatTripName(trip, idx, timetable)
13
+ each note in getNotesForTrip(timetable.notes, trip)
14
+ include timetable_note_symbol.pug
15
+ if timetable.frequencies && !timetable.frequencyExactTimes
16
+ tr.trip-row
17
+ td.trip-frequency(colspan=`${timetable.orderedTrips.length + 1}`)!= formatFrequencyWarning(timetable.frequencies)
18
+ tbody
19
+ if timetable.has_continues_from_route
20
+ tr.continues-from-row
21
+ th Continues from route
22
+ each trip in timetable.orderedTrips
23
+ include timetable_continuation_from.pug
24
+ - let previousCity = null;
25
+ each stop in timetable.stops
26
+ if stop.stop_city !== '' && previousCity !== stop.stop_city && config.showStopCity
27
+ tr.city-row
28
+ th(scope="row" colspan=`${stop.trips.length + 1}`)= stop.stop_city
29
+ - previousCity = stop.stop_city
30
+
31
+ tr.stop-row(id=`stop_id_${formatHtmlId(stop.stop_id)}` data-stop-id=`${stop.stop_id}` data-is-timepoint=`${stop.is_timepoint}`)
32
+ th.stop-name-container(scope="row")
33
+ include timetable_stop_name.pug
34
+
35
+ each stoptime in stop.trips
36
+ include timetable_stoptime.pug
37
+
38
+ if timetable.has_continues_as_route
39
+ tr.continues-as-row
40
+ th Continues as route
41
+ each trip in timetable.orderedTrips
42
+ include timetable_continuation_as.pug
@@ -0,0 +1,30 @@
1
+ .table-container
2
+ table.table-hourly(summary=`This table shows schedules for a selection of key stops on the route for ${timetable.timetable_label} ${timetable.dayList}. Schedule times are listed in rows, starting with the stop name in the first cell of the row and the minutes after the hour in the second row.` data-orientation="hourly")
3
+ caption.sr-only= `${timetable.timetable_label} | ${timetable.dayList}`
4
+ thead
5
+ tr
6
+ th.stop-header(scope="col") Stop
7
+ th(scope="col") Minutes after the hour
8
+
9
+ tbody
10
+ if timetable.frequencies && !timetable.frequencyExactTimes
11
+ tr.trip-row
12
+ td.trip-frequency(colspan="2")!= formatFrequencyWarning(timetable.frequencies)
13
+ - let previousCity
14
+ each stop in timetable.stops
15
+ if previousCity !== stop.stop_city && config.showStopCity
16
+ tr.city-row
17
+ th(scope="row" colspan="2")= stop.stop_city
18
+ - previousCity = stop.stop_city
19
+
20
+ tr.stop-row(id=`stop_id_${formatHtmlId(stop.stop_id)}` data-stop-id=`${stop.stop_id}`)
21
+ th.stop-name-container(scope="row")
22
+ include timetable_stop_name.pug
23
+
24
+ td.stop-time= stop.hourlyTimes.join(', ')
25
+
26
+ - const firstStop = timetable.stops[0]
27
+ - const firstTripStartTime = firstStop.trips[0]
28
+ - const lastStop = timetable.stops[timetable.stops.length - 1]
29
+ - const lastTripEndTime = lastStop.trips[lastStop.trips.length - 1]
30
+ .table-hourly-notes!= `Service begins at ${firstStop.stop_name} at ${firstTripStartTime.formatted_time} and ends at ${lastStop.stop_name} at ${lastTripEndTime.formatted_time}.`
@@ -0,0 +1,15 @@
1
+ .map-container
2
+ .map(id=`map_timetable_id_${formatHtmlId(timetable.timetable_id)}`)
3
+ .map-legend
4
+ .legend-item
5
+ .legend-icon
6
+ .stop-marker
7
+ .legend-text
8
+ <b>Stops</b>
9
+ .legend-item.vehicle-legend-item(style="display: none;")
10
+ .legend-icon
11
+ .vehicle-marker
12
+ .vehicle-marker-arrow
13
+ .legend-text
14
+ <b>Current Vehicle Locations</b><br />
15
+ <small>Hover or tap to see arrival times.</small>
@@ -0,0 +1,48 @@
1
+ if timetablePage.consolidatedTimetables.length > 1
2
+ if config.menuType === 'simple'
3
+ ul.timetable-simple-menu
4
+ - const showTimetableLabel = timetablePageHasDifferentLabels(timetablePage);
5
+ - const showDayList = timetablePageHasDifferentDays(timetablePage);
6
+ each timetable in timetablePage.consolidatedTimetables
7
+ li
8
+ a(href=`#timetable_id_${timetable.timetable_id}`)
9
+ if showTimetableLabel
10
+ span= timetable.timetable_label
11
+ if showTimetableLabel && showDayList
12
+ span &nbsp;|&nbsp;
13
+ if showDayList
14
+ span= timetable.dayListLong
15
+
16
+ if config.menuType === 'jump'
17
+ -
18
+ const groupedTimetables = timetablePage.consolidatedTimetables.reduce((memo, timetable) => {
19
+ if (!memo.hasOwnProperty(timetable.dayList)) {
20
+ memo[timetable.dayList] = []
21
+ }
22
+ memo[timetable.dayList].push(timetable);
23
+ return memo;
24
+ }, {});
25
+ .timetable-jump-menu
26
+ each group, dayList in groupedTimetables
27
+ div
28
+ h3= dayList
29
+ each timetable in group
30
+ a.btn-blue(href=`#timetable_id_${timetable.timetable_id}`)= timetable.timetable_label
31
+
32
+ if config.menuType === 'radio'
33
+ .timetable-radio-menu
34
+ -const directionNames = _.uniq(timetablePage.consolidatedTimetables.map(timetable => timetable.direction_name));
35
+ div(hidden=directionNames.length <= 1)
36
+ #direction_name_selector
37
+ h3 Service Direction
38
+ each directionName, idx in directionNames
39
+ label(class=idx === 0 ? 'btn-blue' : 'btn-gray')
40
+ input.hidden(type="radio" name="directionName" autocomplete="off" value=directionName checked=(idx === 0))
41
+ span= directionName
42
+ div(hidden=timetablePage.dayLists.length <= 1)
43
+ #day_list_selector
44
+ h3 Day of Week
45
+ each dayList, idx in timetablePage.dayLists
46
+ label(class=idx === 0 ? 'btn-blue' : 'btn-gray')
47
+ input.hidden(type="radio" name="dayList" autocomplete="off" value=dayList checked=(idx === 0))
48
+ span= dayList
@@ -0,0 +1,5 @@
1
+ a(href=`#note-${timetable.timetable_id}-${note.note_id}` class=`timetable-note-${note.note_id}`).symbol
2
+ if timetable.routes.length <= 1 && timetable.orientation === 'vertical'
3
+ span= note.symbol
4
+ else
5
+ sup= note.symbol
@@ -0,0 +1,13 @@
1
+ - const stopName = `${stop.stop_name}${stop.type === 'arrival' ? ' (Arrival)' : stop.type === 'departure' ? ' (Departure)' : ''}`;
2
+
3
+ if config.linkStopUrls && stop.stop_url
4
+ a.stop-name(href=`${stop.stop_url}`)= stopName
5
+ each note in getNotesForStop(timetable.notes, stop)
6
+ include timetable_note_symbol.pug
7
+ else
8
+ .stop-name= stopName
9
+ each note in getNotesForStop(timetable.notes, stop)
10
+ include timetable_note_symbol.pug
11
+ .stop-code= stop.stop_code
12
+ if config.showStopDescription
13
+ .stop-description= stop.stop_desc
@@ -0,0 +1,17 @@
1
+ td.stop-time(class=`${stoptime.classes.join(' ')}`)
2
+ if config.showStoptimesForRequestStops || (!stoptime.requestDropoff && !stoptime.requestPickup)
3
+ span.stop-time-text!= stoptime.formatted_time
4
+ if stoptime.skipped && timetable.noServiceSymbol !== null
5
+ a(href=`#note-${timetable.timetable_id}-no-service`).symbol= timetable.noServiceSymbol
6
+ if stoptime.interpolated && timetable.interpolatedStopSymbol !== null
7
+ a(href=`#note-${timetable.timetable_id}-interpolated-stop`).symbol= timetable.interpolatedStopSymbol
8
+ if stoptime.requestPickup && timetable.requestPickupSymbol !== null
9
+ a(href=`#note-${timetable.timetable_id}-request-pickup`).symbol= timetable.requestPickupSymbol
10
+ if stoptime.noPickup && timetable.noPickupSymbol !== null
11
+ a(href=`#note-${timetable.timetable_id}-no-pickup`).symbol= timetable.noPickupSymbol
12
+ if stoptime.requestDropoff && timetable.requestDropoffSymbol !== null
13
+ a(href=`#note-${timetable.timetable_id}-request-dropoff`).symbol= timetable.requestDropoffSymbol
14
+ if stoptime.noDropoff && timetable.noDropoffSymbol !== null
15
+ a(href=`#note-${timetable.timetable_id}-no-dropoff`).symbol= timetable.noDropoffSymbol
16
+ each note in getNotesForStoptime(timetable.notes, stoptime)
17
+ include timetable_note_symbol.pug
@@ -0,0 +1,67 @@
1
+ - let columnCount = timetable.stops.length;
2
+ - if (config.showStopCity) {
3
+ - columnCount += timetable.stops.reduce((memo, stop, index) => {
4
+ - if (index === 0 || stop.stop_city !== timetable.stops[index - 1].stop_city) {
5
+ - memo += 1;
6
+ - }
7
+ - return memo;
8
+ - }, 0);
9
+ - }
10
+ - const timetableHasTripNotes = timetable.orderedTrips.flatMap(trip => getNotesForTrip(timetable.notes, trip)).length > 0;
11
+ .table-container
12
+ table.table-vertical(summary=`This table shows schedules for a selection of key stops on the route for ${timetable.timetable_label} ${timetable.dayList}. Stops and their schedule times are listed in the columns.` data-orientation="vertical")
13
+ caption.sr-only= `${timetable.timetable_label} | ${timetable.dayList}`
14
+ colgroup
15
+ each stop, idx in timetable.stops
16
+ col(id=`stop_id_${formatHtmlId(stop.stop_id)}` class=`stop-${idx}` data-stop-id=`${stop.stop_id}` data-is-timepoint=`${stop.is_timepoint}`)
17
+ thead
18
+ tr
19
+ if timetableHasTripNotes || timetable.routes.length > 1
20
+ th
21
+ if timetable.has_continues_from_route
22
+ th.stop-header.continues-from Continues from route
23
+
24
+ - let previousCity = null;
25
+ each stop, idx in timetable.stops
26
+ if stop.stop_city !== '' && previousCity !== stop.stop_city && config.showStopCity
27
+ th.city-column= stop.stop_city
28
+ - previousCity = stop.stop_city
29
+
30
+ th.stop-header(scope="col" width=`${(100 / columnCount).toFixed(2)}%`)
31
+ include timetable_stop_name.pug
32
+
33
+ if timetable.has_continues_as_route
34
+ th.stop-header.continues-as Continues as route
35
+
36
+ tbody
37
+ if timetable.frequencies && !timetable.frequencyExactTimes
38
+ tr.trip-row
39
+ if timetableHasTripNotes || timetable.routes.length > 1
40
+ td
41
+ if timetable.has_continues_from_route
42
+ td
43
+
44
+ td.trip-frequency(colspan=`${columnCount}`)!= formatFrequencyWarning(timetable.frequencies)
45
+
46
+ if timetable.has_continues_as_route
47
+ td
48
+ each trip, idx in timetable.orderedTrips
49
+ tr.trip-row(id=`trip_id_${formatHtmlId(trip.trip_id)}`)
50
+ if timetableHasTripNotes || timetable.routes.length > 1
51
+ td.trip-notes= formatTripName(trip, idx, timetable)
52
+ each note in getNotesForTrip(timetable.notes, trip)
53
+ include timetable_note_symbol.pug
54
+ if timetable.has_continues_from_route
55
+ include timetable_continuation_from.pug
56
+
57
+ - let previousCity = null;
58
+ each stop in timetable.stops
59
+ if stop.stop_city !== '' && previousCity !== stop.stop_city && config.showStopCity
60
+ td
61
+ - previousCity = stop.stop_city
62
+
63
+ - stoptime = stop.trips[idx]
64
+ include timetable_stoptime.pug
65
+
66
+ if timetable.has_continues_as_route
67
+ include timetable_continuation_as.pug
@@ -0,0 +1,64 @@
1
+ include formatting_functions.pug
2
+
3
+ <!-- Timetable generated on #{new Date().toISOString()} using GTFS-to-HTML version #{config.gtfsToHtmlVersion} -->
4
+ .timetable-page(class=`menu-type-${config.menuType}`)
5
+ if config.showRouteTitle
6
+ - const timetableRouteList = _.uniqBy(_.flatMap(timetablePage.consolidatedTimetables, timetable => timetable.routes), 'route_id')
7
+ h1
8
+ each route in timetableRouteList
9
+ .route-color-swatch-large(style=`background-color: ${formatRouteColor(route)}; color: ${formatRouteTextColor(route)};`)= route.route_short_name || ''
10
+ div= timetablePage.timetable_page_label || timetableRouteList.map(route => formatRouteName(route)).join(' and ')
11
+ if config.effectiveDate
12
+ .effective-date= `Effective ${config.effectiveDate}`
13
+
14
+ include timetable_menu.pug
15
+
16
+ each timetable in timetablePage.consolidatedTimetables
17
+ .timetable(id=`timetable_id_${formatHtmlId(timetable.timetable_id)}` data-day-list=timetable.dayList data-direction-name=timetable.direction_name data-timetable-id=timetable.timetable_id data-direction-id=timetable.direction_id data-route-id=timetable.route_ids.join('_'))
18
+ if config.showRouteTitle
19
+ .timetable-label
20
+ h2= `${timetable.timetable_label} | ${timetable.dayListLong}`
21
+ each note in getNotesForTimetableLabel(timetable.notes)
22
+ include timetable_note_symbol.pug
23
+ if timetable.service_notes !== null
24
+ .timetable-service-notes= timetable.service_notes
25
+
26
+ if config.showMap
27
+ include timetable_map.pug
28
+ if timetable.orientation === 'horizontal'
29
+ include timetable_horizontal.pug
30
+ else if timetable.orientation === 'hourly'
31
+ include timetable_hourly.pug
32
+ else if timetable.orientation === 'vertical'
33
+ include timetable_vertical.pug
34
+
35
+ .timetable-footer
36
+ if hasNotesOrNotices(timetable)
37
+ .notes
38
+ if timetable.requestPickupSymbolUsed
39
+ .note(id=`note-${timetable.timetable_id}-request-pickup`)= `${config.requestPickupSymbol} = ${config.requestPickupText}`
40
+ if timetable.noPickupSymbolUsed
41
+ .note(id=`note-${timetable.timetable_id}-no-pickup`)= `${config.noPickupSymbol} = ${config.noPickupText}`
42
+ if timetable.requestDropoffSymbolUsed
43
+ .note(id=`note-${timetable.timetable_id}-request-dropoff`)= `${config.requestDropoffSymbol} = ${config.requestDropoffText}`
44
+ if timetable.noDropoffSymbolUsed
45
+ .note(id=`note-${timetable.timetable_id}-no-dropoff`)= `${config.noDropoffSymbol} = ${config.noDropoffText}`
46
+ if timetable.noServiceSymbolUsed
47
+ .note(id=`note-${timetable.timetable_id}-no-service`)= `${config.noServiceSymbol} = ${config.noServiceText}`
48
+ if timetable.interpolatedStopSymbolUsed
49
+ .note(id=`note-${timetable.timetable_id}-interpolated-stop`)= `${config.interpolatedStopSymbol} = ${config.interpolatedStopText}`
50
+ each note in _.uniqBy(timetable.notes, 'note_id')
51
+ .note(id=`note-${timetable.timetable_id}-${note.note_id}`)
52
+ span= note.symbol
53
+ span &nbsp;=&nbsp;
54
+ span!= md(note.note)
55
+
56
+ if config.showCalendarExceptions && timetable.calendarDates.includedDates.length
57
+ .included-dates= `${config.serviceProvidedOnText}: ${timetable.calendarDates.includedDates.join(', ')}`
58
+
59
+ if config.showCalendarExceptions && timetable.calendarDates.excludedDates.length
60
+ .excluded-dates= `${config.serviceNotProvidedOnText}: ${timetable.calendarDates.excludedDates.join(', ')}`
61
+
62
+ script.
63
+ const { geojsons, tripIds, routes, stops, gtfsRealtimeUrls } = !{JSON.stringify(prepareMapData(timetablePage, config))};
64
+ createMaps();
@@ -0,0 +1,25 @@
1
+ extends layout
2
+ block content
3
+ include timetablepage.pug
4
+
5
+ block extraHeader
6
+ script(src="https://unpkg.com/jquery@3.7.1/dist/jquery.min.js" crossorigin="anonymous")
7
+
8
+ if config.menuType === 'radio'
9
+ script(src=`${config.assetPath}js/timetable-menu.js`)
10
+
11
+ if config.showMap
12
+ if config.hasGtfsRealtime
13
+ script(src=`${config.assetPath}js/pbf.js`)
14
+ script(src=`${config.assetPath}js/gtfs-realtime.browser.proto.js`)
15
+ script(src="https://api.mapbox.com/mapbox-gl-js/v3.6.0/mapbox-gl.js")
16
+ script.
17
+ mapboxgl.accessToken = '#{config.mapboxAccessToken}';
18
+ script(src=`${config.assetPath}js/timetable-map.js`)
19
+
20
+ link(href="https://api.mapbox.com/mapbox-gl-js/v3.6.0/mapbox-gl.css" rel="stylesheet")
21
+
22
+ link(rel="stylesheet" href=`${config.assetPath}css/timetable_styles.css`)
23
+ if config.outputFormat === 'pdf'
24
+ link(rel="stylesheet" href=`${config.assetPath}css/timetable_pdf_styles.css`)
25
+