gtfs-to-html 2.9.3 → 2.9.4
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/dist/app/index.js +5 -2
- package/dist/app/index.js.map +1 -1
- package/dist/bin/gtfs-to-html.js +62 -41
- package/dist/bin/gtfs-to-html.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +62 -41
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/views/default/css/timetable_styles.css +155 -1
- package/views/default/formatting_functions.pug +13 -12
- package/views/default/js/timetable-alerts.js +180 -0
- package/views/default/js/timetable-map.js +10 -5
- package/views/default/timetablepage.pug +9 -2
- package/views/default/timetablepage_full.pug +7 -6
- package/views/default/css/timetable_pdf_styles.css +0 -69
package/package.json
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
@page {
|
|
2
|
+
margin-top: 20px;
|
|
3
|
+
margin-bottom: 20px;
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
/* Base styles */
|
|
2
7
|
body {
|
|
3
8
|
color: #666;
|
|
@@ -463,7 +468,6 @@ a:hover {
|
|
|
463
468
|
max-width: 30%;
|
|
464
469
|
background-color: #fff;
|
|
465
470
|
border-radius: 3px;
|
|
466
|
-
bottom: 30px;
|
|
467
471
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
468
472
|
padding: 10px;
|
|
469
473
|
position: absolute;
|
|
@@ -520,3 +524,153 @@ a:hover {
|
|
|
520
524
|
width: 14px;
|
|
521
525
|
height: 14px;
|
|
522
526
|
}
|
|
527
|
+
|
|
528
|
+
.timetable-page .timetable-alerts {
|
|
529
|
+
margin-bottom: 1.5rem;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
.timetable-page .timetable-alerts .timetable-alerts-list {
|
|
533
|
+
display: grid;
|
|
534
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
535
|
+
gap: 1rem;
|
|
536
|
+
margin-top: 0.25rem;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.timetable-page .timetable-alert {
|
|
540
|
+
background-color: rgb(224, 230, 245);
|
|
541
|
+
color: rgb(37 99 235);
|
|
542
|
+
padding: 0.75rem 1.25rem;
|
|
543
|
+
border-radius: 0.5rem;
|
|
544
|
+
margin-bottom: 0.5rem;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.timetable-page .timetable-alerts .alert-header {
|
|
548
|
+
display: flex;
|
|
549
|
+
flex-direction: row;
|
|
550
|
+
align-items: center;
|
|
551
|
+
gap: 6px;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.timetable-page .timetable-alerts .alert-header .route-list {
|
|
555
|
+
display: flex;
|
|
556
|
+
flex-direction: row;
|
|
557
|
+
align-items: center;
|
|
558
|
+
gap: 4px;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.timetable-page .timetable-alerts .route-color-swatch {
|
|
562
|
+
width: 30px;
|
|
563
|
+
height: 30px;
|
|
564
|
+
border-radius: 50%;
|
|
565
|
+
text-align: center;
|
|
566
|
+
line-height: 30px;
|
|
567
|
+
font-size: 12px;
|
|
568
|
+
flex-shrink: 0;
|
|
569
|
+
color: white;
|
|
570
|
+
font-weight: bold;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.timetable-page .timetable-alerts .alert-header .alert-title {
|
|
574
|
+
font-size: 1.25rem;
|
|
575
|
+
font-weight: bold;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.timetable-page .timetable-alerts .alert-body {
|
|
579
|
+
margin-top: 0.5rem;
|
|
580
|
+
display: flex;
|
|
581
|
+
flex-direction: column;
|
|
582
|
+
gap: 6px;
|
|
583
|
+
align-items: start;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.timetable-page .timetable-alert-empty {
|
|
587
|
+
display: none;
|
|
588
|
+
background-color: rgb(224, 230, 245);
|
|
589
|
+
color: rgb(37 99 235);
|
|
590
|
+
padding: 0.75rem 1.25rem;
|
|
591
|
+
border-radius: 0.5rem;
|
|
592
|
+
margin-bottom: 0.5rem;
|
|
593
|
+
font-size: 16px;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
@media print {
|
|
597
|
+
body {
|
|
598
|
+
-webkit-print-color-adjust: exact;
|
|
599
|
+
width: 100%;
|
|
600
|
+
margin: 0 auto;
|
|
601
|
+
font-size: 0.8rem;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
h1,
|
|
605
|
+
h2,
|
|
606
|
+
h3,
|
|
607
|
+
h4,
|
|
608
|
+
h5,
|
|
609
|
+
p {
|
|
610
|
+
page-break-inside: avoid;
|
|
611
|
+
orphans: 3;
|
|
612
|
+
widows: 3;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
img,
|
|
616
|
+
table {
|
|
617
|
+
max-width: 100%;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
.timetable-page h1 {
|
|
621
|
+
padding-top: 0;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
.timetable-page .timetable:not(:first-of-type) {
|
|
625
|
+
page-break-before: always;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.timetable-page .timetable .table-container {
|
|
629
|
+
overflow: auto;
|
|
630
|
+
margin-bottom: 0;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.timetable-page .timetable table > thead > tr > th,
|
|
634
|
+
.timetable-page .timetable table > tbody > tr > th,
|
|
635
|
+
.timetable-page .timetable table > tfoot > tr > th {
|
|
636
|
+
padding: 3px 5px;
|
|
637
|
+
line-height: 1.3;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.timetable-page .timetable table > thead > tr > td,
|
|
641
|
+
.timetable-page .timetable table > tbody > tr > td,
|
|
642
|
+
.timetable-page .timetable table > tfoot > tr > td {
|
|
643
|
+
padding: 3px 5px;
|
|
644
|
+
line-height: 1;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.timetable .table-container {
|
|
648
|
+
overflow: hidden;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
.timetable table {
|
|
652
|
+
page-break-after: auto;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.timetable tr {
|
|
656
|
+
page-break-inside: avoid;
|
|
657
|
+
page-break-after: auto;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.timetable td {
|
|
661
|
+
page-break-inside: avoid;
|
|
662
|
+
page-break-after: auto;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
.timetable thead {
|
|
666
|
+
display: table-header-group;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
.timetable tfoot {
|
|
670
|
+
display: table-footer-group;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
.timetable-page .map-legend {
|
|
674
|
+
display: none;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
@@ -37,14 +37,11 @@
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
function prepareMapData(timetablePage, config) {
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const tripIds = []
|
|
40
|
+
const routeData = {}
|
|
41
|
+
const stopData = {}
|
|
43
42
|
const geojsons = {}
|
|
44
43
|
|
|
45
44
|
for (const timetable of timetablePage.consolidatedTimetables) {
|
|
46
|
-
tripIds.push(...timetable.orderedTrips.map(trip => trip.trip_id))
|
|
47
|
-
|
|
48
45
|
const minifiedGeojson = {
|
|
49
46
|
type: 'FeatureCollection',
|
|
50
47
|
features: []
|
|
@@ -53,11 +50,11 @@
|
|
|
53
50
|
for (const feature of timetable.geojson.features) {
|
|
54
51
|
if (feature.geometry.type.toLowerCase() === 'point') {
|
|
55
52
|
for (const route of feature.properties.routes) {
|
|
56
|
-
|
|
53
|
+
routeData[route.route_id] = route
|
|
57
54
|
}
|
|
58
55
|
|
|
59
56
|
const stop = _.pick(feature.properties, ['stop_id', 'stop_code', 'stop_name'])
|
|
60
|
-
|
|
57
|
+
stopData[stop.stop_id] = stop
|
|
61
58
|
|
|
62
59
|
feature.properties = {
|
|
63
60
|
route_ids: feature.properties.routes.map(route => route.route_id),
|
|
@@ -79,11 +76,15 @@
|
|
|
79
76
|
}
|
|
80
77
|
|
|
81
78
|
return {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
pageData: {
|
|
80
|
+
routeIds: _.uniq(_.flatMap(timetablePage.consolidatedTimetables, timetable => timetable.routes.map(route => route.route_id))),
|
|
81
|
+
tripIds: _.uniq(_.flatMap(timetablePage.consolidatedTimetables, timetable => timetable.orderedTrips.map(trip => trip.trip_id))),
|
|
82
|
+
stopIds: Object.keys(stopData),
|
|
83
|
+
geojsons,
|
|
84
|
+
gtfsRealtimeUrls: _.pick(config.agencies.find(agency => agency.realtimeVehiclePositions?.url), ['realtimeAlerts', 'realtimeTripUpdates', 'realtimeVehiclePositions']),
|
|
85
|
+
},
|
|
86
|
+
routeData,
|
|
87
|
+
stopData,
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/* global window, document, $, mapboxgl, Pbf, stopData, routeData, routeIds, tripIds, stopIds, gtfsRealtimeUrls */
|
|
2
|
+
/* eslint no-var: "off", prefer-arrow-callback: "off", no-unused-vars: "off" */
|
|
3
|
+
|
|
4
|
+
let gtfsRealtimeAlertsInterval;
|
|
5
|
+
|
|
6
|
+
async function fetchGtfsRealtime(url, headers) {
|
|
7
|
+
const response = await fetch(url, {
|
|
8
|
+
headers: { ...(headers ?? {}) },
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
if (!response.ok) {
|
|
12
|
+
throw new Error(response.status);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const bufferRes = await response.arrayBuffer();
|
|
16
|
+
const pdf = new Pbf(new Uint8Array(bufferRes));
|
|
17
|
+
const obj = FeedMessage.read(pdf);
|
|
18
|
+
return obj.entity;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function formatAlertAsHtml(
|
|
22
|
+
alert,
|
|
23
|
+
affectedRouteIdsInTimetable,
|
|
24
|
+
affectedStopsIdsInTimetable,
|
|
25
|
+
) {
|
|
26
|
+
const $alert = $('<div>').addClass('timetable-alert');
|
|
27
|
+
|
|
28
|
+
const $routeList = $('<div>').addClass('route-list');
|
|
29
|
+
|
|
30
|
+
for (const routeId of affectedRouteIdsInTimetable) {
|
|
31
|
+
const route = routeData[routeId];
|
|
32
|
+
|
|
33
|
+
if (!route) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
$('<div>')
|
|
38
|
+
.addClass('route-color-swatch')
|
|
39
|
+
.css('background-color', route.route_color || '#000000')
|
|
40
|
+
.css('color', route.route_text_color || '#FFFFFF')
|
|
41
|
+
.text(route.route_short_name)
|
|
42
|
+
.appendTo($routeList);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const $alertHeader = $('<div>')
|
|
46
|
+
.addClass('alert-header')
|
|
47
|
+
.append($routeList)
|
|
48
|
+
.append(
|
|
49
|
+
$('<div>')
|
|
50
|
+
.addClass('alert-title')
|
|
51
|
+
.text(alert.alert.header_text.translation[0].text),
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const $alertBody = $('<div>')
|
|
55
|
+
.addClass('alert-body')
|
|
56
|
+
.append($('<div>').text(alert.alert.description_text.translation[0].text));
|
|
57
|
+
|
|
58
|
+
if (alert.alert.url?.translation?.[0].text) {
|
|
59
|
+
$('<a>')
|
|
60
|
+
.attr('href', alert.alert.url.translation[0].text)
|
|
61
|
+
.addClass('btn-blue btn-sm')
|
|
62
|
+
.text('More Info')
|
|
63
|
+
.appendTo($alertBody);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (affectedStopsIdsInTimetable.length > 0) {
|
|
67
|
+
const $stopList = $('<ul>');
|
|
68
|
+
|
|
69
|
+
for (const stopId of affectedStopsIdsInTimetable) {
|
|
70
|
+
const stop = stopData[stopId];
|
|
71
|
+
|
|
72
|
+
if (!stop) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
$('<li>')
|
|
77
|
+
.append($('<div>').addClass('stop-name').text(stop.stop_name))
|
|
78
|
+
.appendTo($stopList);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
$('<div>').text('Stops Affected:').append($stopList).appendTo($alertBody);
|
|
82
|
+
|
|
83
|
+
$stopList.prependTo($alertBody);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
$alertHeader.appendTo($alert);
|
|
87
|
+
$alertBody.appendTo($alert);
|
|
88
|
+
|
|
89
|
+
return $alert;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
jQuery(function ($) {
|
|
93
|
+
async function updateAlerts() {
|
|
94
|
+
const realtimeAlerts = gtfsRealtimeUrls?.realtimeAlerts;
|
|
95
|
+
|
|
96
|
+
if (!realtimeAlerts) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const alerts = await fetchGtfsRealtime(
|
|
102
|
+
realtimeAlerts.url,
|
|
103
|
+
realtimeAlerts.headers,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const formattedAlerts = [];
|
|
107
|
+
|
|
108
|
+
for (const alert of alerts) {
|
|
109
|
+
const affectedRouteIds = alert.alert.informed_entity
|
|
110
|
+
.filter(
|
|
111
|
+
(entity) => entity.route_id !== undefined && entity.route_id !== '',
|
|
112
|
+
)
|
|
113
|
+
.map((entity) => entity.route_id);
|
|
114
|
+
const affectedRouteIdsInTimetable = routeIds.filter((routeId) =>
|
|
115
|
+
affectedRouteIds.includes(routeId),
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const affectedStopIds = [
|
|
119
|
+
...new Set([
|
|
120
|
+
alert.alert.informed_entity
|
|
121
|
+
.filter(
|
|
122
|
+
(entity) =>
|
|
123
|
+
entity.stop_id !== undefined && entity.stop_id !== '',
|
|
124
|
+
)
|
|
125
|
+
.map((entity) => entity.stop_id),
|
|
126
|
+
]),
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
const affectedStopsIdsInTimetable = stopIds.filter((stopId) =>
|
|
130
|
+
affectedStopIds.includes(stopId),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// Hide alerts that don't affect any stops or routes
|
|
134
|
+
if (
|
|
135
|
+
affectedStopsIdsInTimetable.length === 0 &&
|
|
136
|
+
affectedRouteIdsInTimetable.length === 0
|
|
137
|
+
) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
formattedAlerts.push(
|
|
143
|
+
formatAlertAsHtml(
|
|
144
|
+
alert,
|
|
145
|
+
affectedRouteIdsInTimetable,
|
|
146
|
+
affectedStopsIdsInTimetable,
|
|
147
|
+
),
|
|
148
|
+
);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error(error);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Remove previously posted GTFS-RT alerts
|
|
155
|
+
$('.timetable-alerts-list .timetable-alert').remove();
|
|
156
|
+
|
|
157
|
+
if (formattedAlerts.length > 0) {
|
|
158
|
+
// Remove the empty message if present
|
|
159
|
+
$('.timetable-alert-empty').hide();
|
|
160
|
+
|
|
161
|
+
for (const alert of formattedAlerts) {
|
|
162
|
+
$('.timetable-alerts-list').append(alert);
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
// Replace the empty message if present
|
|
166
|
+
$('.timetable-alert-empty').show();
|
|
167
|
+
}
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error(error);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (!gtfsRealtimeAlertsInterval && gtfsRealtimeUrls?.realtimeAlerts?.url) {
|
|
174
|
+
const alertUpdateInterval = 60 * 1000; // Every Minute
|
|
175
|
+
updateAlerts();
|
|
176
|
+
gtfsRealtimeAlertsInterval = setInterval(() => {
|
|
177
|
+
updateAlerts();
|
|
178
|
+
}, alertUpdateInterval);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* global window, document, $, mapboxgl, Pbf,
|
|
1
|
+
/* global window, document, $, mapboxgl, Pbf, stopData, routeData, routeIds, tripIds, geojsons, gtfsRealtimeUrls */
|
|
2
2
|
/* eslint no-var: "off", prefer-arrow-callback: "off", no-unused-vars: "off" */
|
|
3
3
|
|
|
4
4
|
const maps = {};
|
|
@@ -102,7 +102,7 @@ function formatStopPopup(feature, stop) {
|
|
|
102
102
|
$(html).append(
|
|
103
103
|
$('<div>')
|
|
104
104
|
.addClass('route-list')
|
|
105
|
-
.html(routeIds.map((routeId) => formatRoute(
|
|
105
|
+
.html(routeIds.map((routeId) => formatRoute(routeData[routeId]))),
|
|
106
106
|
);
|
|
107
107
|
|
|
108
108
|
$('<a>')
|
|
@@ -201,7 +201,7 @@ function getVehiclePopupHtml(vehiclePosition, vehicleTripUpdate) {
|
|
|
201
201
|
if (stoptimeUpdate.arrival) {
|
|
202
202
|
const secondsToArrival =
|
|
203
203
|
stoptimeUpdate.arrival.time - Date.now() / 1000;
|
|
204
|
-
const stopName =
|
|
204
|
+
const stopName = stopData[stoptimeUpdate.stop_id]?.stop_name;
|
|
205
205
|
|
|
206
206
|
// Don't show arrivals in the past or non-timepoints
|
|
207
207
|
if (secondsToArrival > 0 && stopName) {
|
|
@@ -384,7 +384,6 @@ async function updateArrivals() {
|
|
|
384
384
|
$('.vehicle-legend-item').hide();
|
|
385
385
|
return;
|
|
386
386
|
}
|
|
387
|
-
|
|
388
387
|
$('.vehicle-legend-item').show();
|
|
389
388
|
|
|
390
389
|
const routeVehiclePositions = vehiclePositions.filter((vehiclePosition) => {
|
|
@@ -407,6 +406,12 @@ async function updateArrivals() {
|
|
|
407
406
|
return false;
|
|
408
407
|
}
|
|
409
408
|
|
|
409
|
+
// If vehiclePosition includes route_id, use that to filter
|
|
410
|
+
if (vehiclePosition.vehicle.trip.route_id) {
|
|
411
|
+
return routeIds.includes(vehiclePosition.vehicle.trip.route_id);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Otherwise, fall back to using trip_id to filter
|
|
410
415
|
return tripIds.includes(vehiclePosition.vehicle.trip.trip_id);
|
|
411
416
|
});
|
|
412
417
|
|
|
@@ -692,7 +697,7 @@ function createMap(id) {
|
|
|
692
697
|
|
|
693
698
|
new mapboxgl.Popup()
|
|
694
699
|
.setLngLat(feature.geometry.coordinates)
|
|
695
|
-
.setHTML(formatStopPopup(feature,
|
|
700
|
+
.setHTML(formatStopPopup(feature, stopData[feature.properties.stop_id]))
|
|
696
701
|
.addTo(map);
|
|
697
702
|
});
|
|
698
703
|
|
|
@@ -13,8 +13,15 @@ include formatting_functions.pug
|
|
|
13
13
|
|
|
14
14
|
include timetable_menu.pug
|
|
15
15
|
|
|
16
|
+
if config.hasGtfsRealtimeAlerts
|
|
17
|
+
.timetable-alerts
|
|
18
|
+
h2 Service Alerts
|
|
19
|
+
.timetable-alerts-list
|
|
20
|
+
.timetable-alert-empty
|
|
21
|
+
| There are no service alerts at this time.
|
|
22
|
+
|
|
16
23
|
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
|
|
24
|
+
.timetable(id=`timetable_id_${formatHtmlId(timetable.timetable_id)}` data-day-list=timetable.dayList data-direction-name=timetable.direction_name data-timetable-id=`${formatHtmlId(timetable.timetable_id)}` data-direction-id=timetable.direction_id data-route-id=timetable.route_ids.join('_'))
|
|
18
25
|
if config.showRouteTitle
|
|
19
26
|
.timetable-label
|
|
20
27
|
h2= `${timetable.timetable_label} | ${timetable.dayListLong}`
|
|
@@ -61,5 +68,5 @@ include formatting_functions.pug
|
|
|
61
68
|
|
|
62
69
|
if config.showMap
|
|
63
70
|
script.
|
|
64
|
-
const {
|
|
71
|
+
const { routeData, stopData, pageData: { routeIds, tripIds, stopIds, geojsons, gtfsRealtimeUrls } } = !{JSON.stringify(prepareMapData(timetablePage, config))};
|
|
65
72
|
createMaps();
|
|
@@ -8,18 +8,19 @@ block extraHeader
|
|
|
8
8
|
if config.menuType === 'radio'
|
|
9
9
|
script(src=`${config.assetPath}js/timetable-menu.js`)
|
|
10
10
|
|
|
11
|
+
if (config.hasGtfsRealtime && config.showMap) || config.hasGtfsRealtimeAlerts
|
|
12
|
+
script(src=`${config.assetPath}js/pbf.js`)
|
|
13
|
+
script(src=`${config.assetPath}js/gtfs-realtime.browser.proto.js`)
|
|
14
|
+
|
|
11
15
|
if config.showMap
|
|
12
16
|
link(href="https://api.mapbox.com/mapbox-gl-js/v3.6.0/mapbox-gl.css" rel="stylesheet")
|
|
13
|
-
|
|
14
|
-
if config.hasGtfsRealtime
|
|
15
|
-
script(src=`${config.assetPath}js/pbf.js`)
|
|
16
|
-
script(src=`${config.assetPath}js/gtfs-realtime.browser.proto.js`)
|
|
17
17
|
script(src="https://api.mapbox.com/mapbox-gl-js/v3.6.0/mapbox-gl.js")
|
|
18
18
|
script.
|
|
19
19
|
mapboxgl.accessToken = '#{config.mapboxAccessToken}';
|
|
20
20
|
script(src=`${config.assetPath}js/timetable-map.js`)
|
|
21
21
|
|
|
22
|
+
if config.hasGtfsRealtimeAlerts
|
|
23
|
+
script(src=`${config.assetPath}js/timetable-alerts.js`)
|
|
24
|
+
|
|
22
25
|
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
26
|
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
body {
|
|
2
|
-
-webkit-print-color-adjust: exact;
|
|
3
|
-
width: 100%;
|
|
4
|
-
margin: 0 auto;
|
|
5
|
-
font-size: 0.8rem;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
h1,
|
|
9
|
-
h2,
|
|
10
|
-
h3,
|
|
11
|
-
h4,
|
|
12
|
-
h5,
|
|
13
|
-
p {
|
|
14
|
-
page-break-inside: avoid;
|
|
15
|
-
orphans: 3;
|
|
16
|
-
widows: 3;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
img,
|
|
20
|
-
table {
|
|
21
|
-
max-width: 100%;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
.timetable-page .timetable:not(:first-of-type) {
|
|
25
|
-
page-break-before: always;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.timetable-page .timetable .table-container {
|
|
29
|
-
overflow: auto;
|
|
30
|
-
margin-bottom: 0;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.timetable-page .timetable table > thead > tr > th,
|
|
34
|
-
.timetable-page .timetable table > tbody > tr > th,
|
|
35
|
-
.timetable-page .timetable table > tfoot > tr > th {
|
|
36
|
-
padding: 3px 5px;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.timetable-page .timetable table > thead > tr > td,
|
|
40
|
-
.timetable-page .timetable table > tbody > tr > td,
|
|
41
|
-
.timetable-page .timetable table > tfoot > tr > td {
|
|
42
|
-
padding: 1px 5px;
|
|
43
|
-
line-height: 1;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.timetable .table-container {
|
|
47
|
-
overflow: hidden;
|
|
48
|
-
}
|
|
49
|
-
.timetable table {
|
|
50
|
-
page-break-after: auto;
|
|
51
|
-
}
|
|
52
|
-
.timetable tr {
|
|
53
|
-
page-break-inside: avoid;
|
|
54
|
-
page-break-after: auto;
|
|
55
|
-
}
|
|
56
|
-
.timetable td {
|
|
57
|
-
page-break-inside: avoid;
|
|
58
|
-
page-break-after: auto;
|
|
59
|
-
}
|
|
60
|
-
.timetable thead {
|
|
61
|
-
display: table-header-group;
|
|
62
|
-
}
|
|
63
|
-
.timetable tfoot {
|
|
64
|
-
display: table-footer-group;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.timetable-page .map-legend {
|
|
68
|
-
display: none;
|
|
69
|
-
}
|