transit-departures-widget 2.4.0 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -1
- package/dist/app/index.d.ts +2 -0
- package/dist/app/index.js +343 -0
- package/dist/app/index.js.map +1 -0
- package/dist/bin/transit-departures-widget.d.ts +1 -0
- package/dist/bin/transit-departures-widget.js +478 -0
- package/dist/bin/transit-departures-widget.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +420 -0
- package/dist/index.js.map +1 -0
- package/package.json +25 -6
- package/.eslintrc.json +0 -28
- package/.husky/pre-commit +0 -4
- package/CHANGELOG.md +0 -206
- package/app/index.js +0 -95
- package/bin/transit-departures-widget.js +0 -38
- package/config-sample.json +0 -15
- package/docs/images/transit-departures-widget-logo.svg +0 -18
- package/index.js +0 -1
- package/lib/file-utils.js +0 -110
- package/lib/log-utils.js +0 -77
- package/lib/transit-departures-widget.js +0 -97
- package/lib/utils.js +0 -329
- package/locales/en.json +0 -20
- package/locales/pl.json +0 -20
- package/public/css/transit-departures-widget-styles.css +0 -200
- package/public/img/refresh.svg +0 -1
- package/public/js/transit-departures-widget.js +0 -687
- package/views/widget/layout.pug +0 -19
- package/views/widget/widget.pug +0 -60
- package/views/widget/widget_full.pug +0 -3
|
@@ -1,687 +0,0 @@
|
|
|
1
|
-
/* global window, $, jQuery, _, Pbf, FeedMessage, alert, accessibleAutocomplete, */
|
|
2
|
-
/* eslint no-var: "off", no-unused-vars: "off", no-alert: "off" */
|
|
3
|
-
|
|
4
|
-
function setupTransitDeparturesWidget(routes, stops, config) {
|
|
5
|
-
let departuresResponse
|
|
6
|
-
let departuresTimeout
|
|
7
|
-
let initialStop
|
|
8
|
-
let selectedParameters
|
|
9
|
-
let url = new URL(config.gtfsRtTripupdatesUrl)
|
|
10
|
-
|
|
11
|
-
function updateUrlWithStop(stop) {
|
|
12
|
-
const url = new URL(window.location.origin + window.location.pathname)
|
|
13
|
-
url.searchParams.append('stop', stop.stop_code || stop.stop_id)
|
|
14
|
-
|
|
15
|
-
window.history.pushState(null, null, url)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async function fetchTripUpdates() {
|
|
19
|
-
url.searchParams.append('cacheBust', Date.now())
|
|
20
|
-
const response = await fetch(url)
|
|
21
|
-
if (response.ok) {
|
|
22
|
-
const bufferResponse = await response.arrayBuffer()
|
|
23
|
-
const pbf = new Pbf(new Uint8Array(bufferResponse))
|
|
24
|
-
const object = FeedMessage.read(pbf)
|
|
25
|
-
|
|
26
|
-
return object.entity
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
throw new Error(response.status)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function formatMinutes(seconds) {
|
|
33
|
-
if (seconds < 60) {
|
|
34
|
-
return '<1'
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return Math.floor(seconds / 60)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function formatDirectionId(directionId) {
|
|
41
|
-
if (directionId === null || directionId === undefined) {
|
|
42
|
-
return '0'
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return directionId?.toString()
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function timeStamp() {
|
|
49
|
-
const now = new Date()
|
|
50
|
-
let hours = now.getHours()
|
|
51
|
-
let minutes = now.getMinutes()
|
|
52
|
-
|
|
53
|
-
if (minutes < 10) {
|
|
54
|
-
minutes = '0' + minutes
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (config.timeFormat === '24hour') {
|
|
58
|
-
return `${hours}:${minutes}`
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const suffix = hours < 12 ? 'AM' : 'PM'
|
|
62
|
-
hours = hours < 12 ? hours : hours - 12
|
|
63
|
-
hours = hours || 12
|
|
64
|
-
|
|
65
|
-
return `${hours}:${minutes} ${suffix}`
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
jQuery(($) => {
|
|
69
|
-
// Populate dropdown with all routes
|
|
70
|
-
$('#departure_route').append(
|
|
71
|
-
routes.map((route) => {
|
|
72
|
-
return $('<option>')
|
|
73
|
-
.attr('value', route.route_id)
|
|
74
|
-
.text(route.route_full_name)
|
|
75
|
-
}),
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
// Read URL parameters on load
|
|
79
|
-
readUrlWithParameters()
|
|
80
|
-
|
|
81
|
-
function readUrlWithParameters() {
|
|
82
|
-
const url = new URL(window.location.href)
|
|
83
|
-
const stopFromURL = url.searchParams.get('stop')
|
|
84
|
-
|
|
85
|
-
if (!stopFromURL) {
|
|
86
|
-
return
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const stop = stops.find(
|
|
90
|
-
(stop) =>
|
|
91
|
-
stop.stop_id === stopFromURL || stop.stop_code === stopFromURL,
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
if (!stop) {
|
|
95
|
-
return
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
initialStop = stop.stop_code || stop.stop_name
|
|
99
|
-
|
|
100
|
-
// Wait for bootstrap js to initialize before triggering click
|
|
101
|
-
setTimeout(() => {
|
|
102
|
-
$('#stop_form').trigger('submit')
|
|
103
|
-
$(
|
|
104
|
-
'#real_time_departures input[name="departure_type"][value="stop"]',
|
|
105
|
-
).trigger('click')
|
|
106
|
-
}, 100)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function resetResults() {
|
|
110
|
-
if (departuresTimeout) {
|
|
111
|
-
clearTimeout(departuresTimeout)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
$('#departure_results').hide()
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function showLoading() {
|
|
118
|
-
$('#loading').show()
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function hideLoading() {
|
|
122
|
-
$('#loading').hide()
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function renderStopInfo(selectedStops) {
|
|
126
|
-
if (selectedStops && selectedStops.length > 0) {
|
|
127
|
-
$('#departure_results .departure-results-stop-unknown').hide()
|
|
128
|
-
$('#departure_results .departure-results-stop')
|
|
129
|
-
.text(selectedStops[0].stop_name)
|
|
130
|
-
.show()
|
|
131
|
-
} else {
|
|
132
|
-
$('#departure_results .departure-results-stop').hide()
|
|
133
|
-
$('#departure_results .departure-results-stop-unknown').show()
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (
|
|
137
|
-
selectedStops &&
|
|
138
|
-
selectedStops.length === 1 &&
|
|
139
|
-
selectedStops[0].stop_code &&
|
|
140
|
-
!selectedStops[0].is_parent_station
|
|
141
|
-
) {
|
|
142
|
-
$('#departure_results .departure-results-stop-code').text(
|
|
143
|
-
selectedStops[0].stop_code,
|
|
144
|
-
)
|
|
145
|
-
$('#departure_results .departure-results-stop-code-container').show()
|
|
146
|
-
} else {
|
|
147
|
-
$('#departure_results .departure-results-stop-code-container').hide()
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
$('#departure_results .departure-results-fetchtime-time').text(
|
|
151
|
-
timeStamp(),
|
|
152
|
-
)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function formatDepartureGroup(departureGroup) {
|
|
156
|
-
const div = $('<div>').addClass('departure-result')
|
|
157
|
-
const { route, direction } = departureGroup[0]
|
|
158
|
-
|
|
159
|
-
const routeNameDiv = $('<div>')
|
|
160
|
-
.addClass('departure-result-route-name')
|
|
161
|
-
.appendTo(div)
|
|
162
|
-
const departureTimesDiv = $('<div>')
|
|
163
|
-
.addClass('departure-result-times')
|
|
164
|
-
.appendTo(div)
|
|
165
|
-
|
|
166
|
-
if (route.route_short_name) {
|
|
167
|
-
const routeColor = route.route_color ? `#${route.route_color}` : '#ccc'
|
|
168
|
-
const routeTextColor = route.route_text_color
|
|
169
|
-
? `#${route.route_text_color}`
|
|
170
|
-
: '#000'
|
|
171
|
-
$('<div>')
|
|
172
|
-
.text(route.route_short_name)
|
|
173
|
-
.addClass('departure-result-route-circle')
|
|
174
|
-
.css({
|
|
175
|
-
'background-color': routeColor,
|
|
176
|
-
color: routeTextColor,
|
|
177
|
-
})
|
|
178
|
-
.appendTo(routeNameDiv)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
$('<div>')
|
|
182
|
-
.text(direction.direction)
|
|
183
|
-
.addClass('departure-result-route-direction')
|
|
184
|
-
.appendTo(routeNameDiv)
|
|
185
|
-
|
|
186
|
-
const sortedDepartures = _.take(
|
|
187
|
-
_.sortBy(departureGroup, (departure) => departure.time),
|
|
188
|
-
3,
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
for (const departure of sortedDepartures) {
|
|
192
|
-
const minutes = formatMinutes(departure.time - Date.now() / 1000)
|
|
193
|
-
const minutesLabel = $(
|
|
194
|
-
'#departure_results .departure-results-container',
|
|
195
|
-
).data('minutes-label')
|
|
196
|
-
|
|
197
|
-
$('<div>')
|
|
198
|
-
.addClass('departure-result-time-container')
|
|
199
|
-
.append(
|
|
200
|
-
$('<div>')
|
|
201
|
-
.addClass('departure-result-time')
|
|
202
|
-
.html(
|
|
203
|
-
`${minutes}<span class="departure-result-time-label">${minutesLabel}</span>`,
|
|
204
|
-
),
|
|
205
|
-
)
|
|
206
|
-
.appendTo(departureTimesDiv)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return div
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function renderResults(selectedStops, departures) {
|
|
213
|
-
renderStopInfo(selectedStops)
|
|
214
|
-
|
|
215
|
-
if (departures.length === 0) {
|
|
216
|
-
$('#departure_results .departure-results-container').hide()
|
|
217
|
-
$('#departure_results .departure-results-error').hide()
|
|
218
|
-
$('#departure_results .departure-results-none').show()
|
|
219
|
-
} else {
|
|
220
|
-
const departureGroups = _.groupBy(
|
|
221
|
-
departures,
|
|
222
|
-
(departure) =>
|
|
223
|
-
`${departure.route.route_id}||${departure.direction.direction_id}`,
|
|
224
|
-
)
|
|
225
|
-
const sortedDepartureGroups = _.sortBy(
|
|
226
|
-
departureGroups,
|
|
227
|
-
(departureGroup) => {
|
|
228
|
-
const { route } = departureGroup[0]
|
|
229
|
-
return Number.parseInt(route.route_short_name, 10)
|
|
230
|
-
},
|
|
231
|
-
)
|
|
232
|
-
$('#departure_results .departure-results-none').hide()
|
|
233
|
-
$('#departure_results .departure-results-error').hide()
|
|
234
|
-
$('#departure_results .departure-results-container')
|
|
235
|
-
.html(
|
|
236
|
-
sortedDepartureGroups.map((departureGroup) =>
|
|
237
|
-
formatDepartureGroup(departureGroup),
|
|
238
|
-
),
|
|
239
|
-
)
|
|
240
|
-
.show()
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
hideLoading()
|
|
244
|
-
$('#departure_results').show()
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function renderError(selectedStops) {
|
|
248
|
-
renderStopInfo(selectedStops)
|
|
249
|
-
$('#departure_results .departure-results-error').show()
|
|
250
|
-
|
|
251
|
-
hideLoading()
|
|
252
|
-
$('#departure_results').show()
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
function findStops(stopId, stopName) {
|
|
256
|
-
let selectedStops
|
|
257
|
-
if (stopId !== undefined) {
|
|
258
|
-
const selectedStop = stops.find((stop) => stop.stop_id === stopId)
|
|
259
|
-
|
|
260
|
-
if (selectedStop) {
|
|
261
|
-
selectedStops = [selectedStop]
|
|
262
|
-
}
|
|
263
|
-
} else {
|
|
264
|
-
selectedStops = stops.filter(
|
|
265
|
-
(stop) =>
|
|
266
|
-
stop.stop_id === stopName ||
|
|
267
|
-
stop.stop_code === stopName ||
|
|
268
|
-
stop.stop_name === stopName,
|
|
269
|
-
)
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
if (selectedStops) {
|
|
273
|
-
// Use parent stop if it exists
|
|
274
|
-
for (const stop of selectedStops) {
|
|
275
|
-
if (stop.parent_station !== null) {
|
|
276
|
-
const parentStationStop = stops.find(
|
|
277
|
-
(s) => s.stop_id === stop.parent_station,
|
|
278
|
-
)
|
|
279
|
-
if (parentStationStop) {
|
|
280
|
-
return [parentStationStop]
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return selectedStops
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// No stops found
|
|
289
|
-
return undefined
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function selectParameters({ stopId, stopName, directionId, routeId }) {
|
|
293
|
-
$('.stop-code-invalid').hide()
|
|
294
|
-
const selectedStops = findStops(stopId, stopName)
|
|
295
|
-
const route = routes.find((route) => route.route_id === routeId)
|
|
296
|
-
const direction = route
|
|
297
|
-
? route.directions.find(
|
|
298
|
-
(direction) =>
|
|
299
|
-
formatDirectionId(direction.direction_id) === directionId,
|
|
300
|
-
)
|
|
301
|
-
: undefined
|
|
302
|
-
|
|
303
|
-
if (!selectedStops || selectedStops.length === 0) {
|
|
304
|
-
$('.stop-code-invalid').show()
|
|
305
|
-
return
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
selectedParameters = { selectedStops, direction, route }
|
|
309
|
-
|
|
310
|
-
resetResults()
|
|
311
|
-
showLoading()
|
|
312
|
-
updateDepartures()
|
|
313
|
-
|
|
314
|
-
// Every refresh interval seconds, check for tripupdates
|
|
315
|
-
departuresTimeout = setInterval(
|
|
316
|
-
() => updateDepartures(),
|
|
317
|
-
config.refreshIntervalSeconds * 1000,
|
|
318
|
-
)
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function getRouteAndDirectionFromTrip(tripId) {
|
|
322
|
-
let tripRoute
|
|
323
|
-
let tripDirection
|
|
324
|
-
for (const route of routes) {
|
|
325
|
-
for (const direction of route.directions) {
|
|
326
|
-
if (direction.tripIds.includes(tripId)) {
|
|
327
|
-
tripDirection = direction
|
|
328
|
-
tripRoute = route
|
|
329
|
-
break
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
if (tripDirection && tripRoute) {
|
|
334
|
-
break
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return {
|
|
339
|
-
route: tripRoute,
|
|
340
|
-
direction: tripDirection,
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
function filterDepartures(departures, { selectedStops, direction, route }) {
|
|
345
|
-
// Remove departure and arrival information for last stoptime by stop_sequence if it has any
|
|
346
|
-
const cleanedDepartures = departures.map((departure) => {
|
|
347
|
-
if (departure?.trip_update?.stop_time_update?.length > 0) {
|
|
348
|
-
// Find index of largest stop_sequence
|
|
349
|
-
let largestStopSequence = 0
|
|
350
|
-
let largestStopSequenceIndex = 0
|
|
351
|
-
for (
|
|
352
|
-
let index = 0;
|
|
353
|
-
index < departure.trip_update.stop_time_update.length;
|
|
354
|
-
index++
|
|
355
|
-
) {
|
|
356
|
-
if (
|
|
357
|
-
departure.trip_update.stop_time_update[index].stop_sequence >
|
|
358
|
-
largestStopSequence
|
|
359
|
-
) {
|
|
360
|
-
largestStopSequence =
|
|
361
|
-
departure.trip_update.stop_time_update[index].stop_sequence
|
|
362
|
-
largestStopSequenceIndex = index
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
departure.trip_update.stop_time_update.splice(
|
|
366
|
-
largestStopSequenceIndex,
|
|
367
|
-
1,
|
|
368
|
-
)
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
return departure
|
|
372
|
-
})
|
|
373
|
-
|
|
374
|
-
const selectedStopIds = selectedStops.flatMap((stop) => {
|
|
375
|
-
return stop.is_parent_station
|
|
376
|
-
? stops
|
|
377
|
-
.filter((s) => s.parent_station === stop.stop_id)
|
|
378
|
-
.map((stop) => stop.stop_id)
|
|
379
|
-
: [stop.stop_id]
|
|
380
|
-
})
|
|
381
|
-
const filteredDepartures = []
|
|
382
|
-
|
|
383
|
-
for (const departure of cleanedDepartures) {
|
|
384
|
-
let filteredDeparture = {}
|
|
385
|
-
|
|
386
|
-
if (route) {
|
|
387
|
-
if (
|
|
388
|
-
!departure ||
|
|
389
|
-
!departure.trip_update ||
|
|
390
|
-
!departure.trip_update.trip
|
|
391
|
-
) {
|
|
392
|
-
continue
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
if (
|
|
396
|
-
!direction ||
|
|
397
|
-
!direction.tripIds.includes(departure.trip_update.trip.trip_id)
|
|
398
|
-
) {
|
|
399
|
-
continue
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
filteredDeparture.route = route
|
|
403
|
-
filteredDeparture.direction = direction
|
|
404
|
-
} else if (selectedStops && selectedStops.length > 0) {
|
|
405
|
-
if (
|
|
406
|
-
!departure ||
|
|
407
|
-
!departure.trip_update ||
|
|
408
|
-
!departure.trip_update.stop_time_update
|
|
409
|
-
) {
|
|
410
|
-
continue
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// Get route and direction from trip_id
|
|
414
|
-
filteredDeparture = getRouteAndDirectionFromTrip(
|
|
415
|
-
departure.trip_update.trip.trip_id,
|
|
416
|
-
)
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
const stoptime = departure.trip_update.stop_time_update.find(
|
|
420
|
-
(stopTimeUpdate) => selectedStopIds.includes(stopTimeUpdate.stop_id),
|
|
421
|
-
)
|
|
422
|
-
|
|
423
|
-
if (!stoptime || (!stoptime.arrival && !stoptime.departure)) {
|
|
424
|
-
continue
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
filteredDeparture.time =
|
|
428
|
-
stoptime.departure?.time ?? stoptime.arrival?.time
|
|
429
|
-
|
|
430
|
-
// Hide departures more than 90 minutes in the future
|
|
431
|
-
if (filteredDeparture.time - Date.now() / 1000 > 90 * 60) {
|
|
432
|
-
continue
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Hide departures more than 1 minute in the past
|
|
436
|
-
if (filteredDeparture.time - Date.now() / 1000 < -60) {
|
|
437
|
-
continue
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
filteredDepartures.push(filteredDeparture)
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
return filteredDepartures
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
async function updateDepartures(forceRefresh) {
|
|
447
|
-
try {
|
|
448
|
-
const { selectedStops, direction, route } = selectedParameters
|
|
449
|
-
// Use existing data if less than the refresh interval seconds old
|
|
450
|
-
const minimumAge = Date.now() - config.refreshIntervalSeconds * 1000
|
|
451
|
-
if (
|
|
452
|
-
!departuresResponse ||
|
|
453
|
-
departuresResponse.timestamp < minimumAge ||
|
|
454
|
-
forceRefresh === true
|
|
455
|
-
) {
|
|
456
|
-
const departures = await fetchTripUpdates()
|
|
457
|
-
|
|
458
|
-
// Don't use new departure info if nothing is returned
|
|
459
|
-
if (!departures) {
|
|
460
|
-
console.error('No departures returned')
|
|
461
|
-
return
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
departuresResponse = { departures, timestamp: Date.now() }
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
renderResults(
|
|
468
|
-
selectedStops,
|
|
469
|
-
filterDepartures(departuresResponse.departures, {
|
|
470
|
-
selectedStops,
|
|
471
|
-
direction,
|
|
472
|
-
route,
|
|
473
|
-
}),
|
|
474
|
-
)
|
|
475
|
-
|
|
476
|
-
if (selectedStops && selectedStops.length > 0) {
|
|
477
|
-
updateUrlWithStop(selectedStops[0])
|
|
478
|
-
}
|
|
479
|
-
} catch (error) {
|
|
480
|
-
console.error(error)
|
|
481
|
-
renderError(selectedParameters?.selectedStops)
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
$('#real_time_departures input[name="departure_type"]').change((event) => {
|
|
486
|
-
const type = $(event.target).val()
|
|
487
|
-
|
|
488
|
-
$('#real_time_departures #route_form').toggleClass(
|
|
489
|
-
'hidden-form',
|
|
490
|
-
type !== 'route',
|
|
491
|
-
)
|
|
492
|
-
$('#real_time_departures #stop_form').toggleClass(
|
|
493
|
-
'hidden-form',
|
|
494
|
-
type !== 'stop',
|
|
495
|
-
)
|
|
496
|
-
|
|
497
|
-
$('#real_time_departures #departure_stop').val('').prop('disabled', true)
|
|
498
|
-
|
|
499
|
-
$('#real_time_departures #departure_direction option:gt(0)').remove()
|
|
500
|
-
$('#real_time_departures #departure_stop option:gt(0)').remove()
|
|
501
|
-
resetResults()
|
|
502
|
-
})
|
|
503
|
-
|
|
504
|
-
$('#real_time_departures #departure_route').change((event) => {
|
|
505
|
-
const routeId = $(event.target).val()
|
|
506
|
-
|
|
507
|
-
$('#real_time_departures #departure_stop').val('').prop('disabled', true)
|
|
508
|
-
|
|
509
|
-
$('#real_time_departures #departure_direction option:gt(0)').remove()
|
|
510
|
-
$('#real_time_departures #departure_stop option:gt(0)').remove()
|
|
511
|
-
resetResults()
|
|
512
|
-
|
|
513
|
-
if (routeId === '') {
|
|
514
|
-
$('#real_time_departures #departure_direction')
|
|
515
|
-
.val('')
|
|
516
|
-
.prop('disabled', true)
|
|
517
|
-
} else {
|
|
518
|
-
$('#real_time_departures #departure_direction')
|
|
519
|
-
.val('')
|
|
520
|
-
.prop('disabled', false)
|
|
521
|
-
|
|
522
|
-
const route = routes.find((route) => route.route_id === routeId)
|
|
523
|
-
|
|
524
|
-
if (!route) {
|
|
525
|
-
return console.warn(`Unable to find route ${routeId}`)
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
$('#real_time_departures #departure_direction').append(
|
|
529
|
-
route.directions.map((direction) =>
|
|
530
|
-
$('<option>')
|
|
531
|
-
.val(formatDirectionId(direction.direction_id))
|
|
532
|
-
.text(direction.direction),
|
|
533
|
-
),
|
|
534
|
-
)
|
|
535
|
-
|
|
536
|
-
if (route.directions.length === 1) {
|
|
537
|
-
$('#real_time_departures #departure_direction').hide()
|
|
538
|
-
$('#real_time_departures #departure_direction').val(
|
|
539
|
-
formatDirectionId(route.directions[0].direction_id),
|
|
540
|
-
)
|
|
541
|
-
$('#real_time_departures #departure_direction').trigger('change')
|
|
542
|
-
} else {
|
|
543
|
-
$('#real_time_departures #departure_direction').show()
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
})
|
|
547
|
-
|
|
548
|
-
$('#real_time_departures #departure_direction').change((event) => {
|
|
549
|
-
const routeId = $('#real_time_departures #departure_route').val()
|
|
550
|
-
const directionId = $(event.target).val()
|
|
551
|
-
|
|
552
|
-
$('#real_time_departures #departure_stop option:gt(0)').remove()
|
|
553
|
-
resetResults()
|
|
554
|
-
|
|
555
|
-
if (directionId === '') {
|
|
556
|
-
$('#real_time_departures #departure_stop')
|
|
557
|
-
.val('')
|
|
558
|
-
.prop('disabled', true)
|
|
559
|
-
} else {
|
|
560
|
-
$('#real_time_departures #departure_stop')
|
|
561
|
-
.val('')
|
|
562
|
-
.prop('disabled', false)
|
|
563
|
-
|
|
564
|
-
const route = routes.find((route) => route.route_id === routeId)
|
|
565
|
-
|
|
566
|
-
if (!route) {
|
|
567
|
-
return console.warn(`Unable to find route ${routeId}`)
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
const direction = route.directions.find(
|
|
571
|
-
(direction) =>
|
|
572
|
-
formatDirectionId(direction.direction_id) === directionId,
|
|
573
|
-
)
|
|
574
|
-
|
|
575
|
-
$('#real_time_departures #departure_stop').append(
|
|
576
|
-
direction.stopIds.map((stopId) => {
|
|
577
|
-
const stop = stops.find((stop) => stop.stop_id === stopId)
|
|
578
|
-
return $('<option>').val(stop.stop_id).text(stop.stop_name)
|
|
579
|
-
}),
|
|
580
|
-
)
|
|
581
|
-
}
|
|
582
|
-
})
|
|
583
|
-
|
|
584
|
-
$('#real_time_departures #departure_stop').change((event) => {
|
|
585
|
-
const routeId = $('#real_time_departures #departure_route').val()
|
|
586
|
-
const directionId = $('#real_time_departures #departure_direction').val()
|
|
587
|
-
const stopId = $(event.target).val()
|
|
588
|
-
|
|
589
|
-
selectParameters({
|
|
590
|
-
stopId,
|
|
591
|
-
routeId,
|
|
592
|
-
directionId,
|
|
593
|
-
})
|
|
594
|
-
})
|
|
595
|
-
|
|
596
|
-
$('#stop_form').submit((event) => {
|
|
597
|
-
event.preventDefault()
|
|
598
|
-
$('.stop-code-invalid').hide()
|
|
599
|
-
|
|
600
|
-
const stopName = $('#departure_stop_code').val()
|
|
601
|
-
|
|
602
|
-
if (stopName === '') {
|
|
603
|
-
$('.stop-code-invalid').show()
|
|
604
|
-
return
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
selectParameters({
|
|
608
|
-
stopName,
|
|
609
|
-
})
|
|
610
|
-
})
|
|
611
|
-
|
|
612
|
-
$('#departure_results .departure-results-fetchtime').click((event) => {
|
|
613
|
-
event.preventDefault()
|
|
614
|
-
resetResults()
|
|
615
|
-
showLoading()
|
|
616
|
-
updateDepartures(true)
|
|
617
|
-
})
|
|
618
|
-
|
|
619
|
-
accessibleAutocomplete({
|
|
620
|
-
element: $('#departure_stop_code_container').get(0),
|
|
621
|
-
id: 'departure_stop_code',
|
|
622
|
-
source(query, populateResults) {
|
|
623
|
-
const filteredResults = stops.filter((stop) => {
|
|
624
|
-
// Don't list child stations
|
|
625
|
-
if (
|
|
626
|
-
stop.parent_station !== null &&
|
|
627
|
-
stop.parent_station !== undefined
|
|
628
|
-
) {
|
|
629
|
-
return false
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
if (stop.stop_code?.startsWith(query.trim())) {
|
|
633
|
-
return true
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
return stop.stop_name?.toLowerCase().includes(query.toLowerCase())
|
|
637
|
-
})
|
|
638
|
-
const sortedResults = _.sortBy(filteredResults, (stop) =>
|
|
639
|
-
stop.stop_name?.toLowerCase().startsWith(query.toLowerCase().trim())
|
|
640
|
-
? 0
|
|
641
|
-
: 1,
|
|
642
|
-
)
|
|
643
|
-
populateResults(sortedResults)
|
|
644
|
-
},
|
|
645
|
-
minLength: 2,
|
|
646
|
-
autoselect: true,
|
|
647
|
-
placeholder: $('#departure_stop_code_container').data('placeholder'),
|
|
648
|
-
showNoOptionsFound: false,
|
|
649
|
-
templates: {
|
|
650
|
-
inputValue: (result) =>
|
|
651
|
-
result && (result.stop_code || result.stop_name),
|
|
652
|
-
suggestion(result) {
|
|
653
|
-
if (!result) {
|
|
654
|
-
return
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
if (typeof result === 'string') {
|
|
658
|
-
return result
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
const stopCode = result.is_parent_station
|
|
662
|
-
? $('#departure_stop_code_container').data('stop-code-all')
|
|
663
|
-
: result.stop_code
|
|
664
|
-
|
|
665
|
-
let formattedStopName = `<strong>${result.stop_name}</strong>`
|
|
666
|
-
|
|
667
|
-
if (stopCode) {
|
|
668
|
-
formattedStopName += ` (${stopCode})`
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
return formattedStopName
|
|
672
|
-
},
|
|
673
|
-
},
|
|
674
|
-
onConfirm(selectedStop) {
|
|
675
|
-
if (!selectedStop) {
|
|
676
|
-
return
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
$('#departure_stop_code').val(
|
|
680
|
-
selectedStop.stop_code || selectedStop.stop_name,
|
|
681
|
-
)
|
|
682
|
-
$('#stop_form').trigger('submit')
|
|
683
|
-
},
|
|
684
|
-
defaultValue: initialStop,
|
|
685
|
-
})
|
|
686
|
-
})
|
|
687
|
-
}
|
package/views/widget/layout.pug
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
doctype html
|
|
2
|
-
html
|
|
3
|
-
head
|
|
4
|
-
title= title
|
|
5
|
-
meta(charset="utf-8")
|
|
6
|
-
link(rel="stylesheet" href="https://unpkg.com/bootstrap@4.6.0/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous")
|
|
7
|
-
link(rel="stylesheet" href="https://unpkg.com/accessible-autocomplete@2.0.3/dist/accessible-autocomplete.min.css" integrity="sha384-QoUNXPSpjM43RbU2UeGHhlZsoDZ4Ve+k3Una2evEDxo1FLc61Y/NhquJOQOh8TRN" crossorigin="anonymous")
|
|
8
|
-
link(rel="stylesheet" href=`${config.assetPath}css/transit-departures-widget-styles.css`)
|
|
9
|
-
meta(name="viewport" content="initial-scale=1.0, width=device-width")
|
|
10
|
-
|
|
11
|
-
script(src="https://unpkg.com/jquery@3.6.0/dist/jquery.min.js" integrity="sha384-vtXRMe3mGCbOeY7l30aIg8H9p3GdeSe4IFlP6G8JMa7o7lXvnz3GFKzPxzJdPfGK" crossorigin="anonymous")
|
|
12
|
-
script(src="https://unpkg.com/lodash@4.17.21/lodash.min.js" integrity="sha384-H6KKS1H1WwuERMSm+54dYLzjg0fKqRK5ZRyASdbrI/lwrCc6bXEmtGYr5SwvP1pZ" crossorigin="anonymous")
|
|
13
|
-
script(src="https://unpkg.com/pbf@3.2.1/dist/pbf.js" integrity="sha384-v0UA1pHhakpSLzilEI4GK9mx/156zhRyC60GB/GgYwHKHQCfjOYKyNKhDGw5DqaO" crossorigin="anonymous")
|
|
14
|
-
script(src="https://unpkg.com/gtfs-realtime-pbf-js-module@1.0.0/gtfs-realtime.browser.proto.js" integrity="sha256-z/blOP7gdmNvq4KQfxVASnetvUTCcybhhIMIpnCIfa0=" crossorigin="anonymous")
|
|
15
|
-
script(src="https://unpkg.com/accessible-autocomplete@2.0.3/dist/accessible-autocomplete.min.js" integrity="sha384-lA2U/4JpLCszKZ0EJQwuGmVQHQqQbaBxnWcSgfeVNA9wZrMwjwCxVKYlMnT7u/DD" crossorigin="anonymous")
|
|
16
|
-
script(src=`${config.assetPath}js/transit-departures-widget.js`)
|
|
17
|
-
|
|
18
|
-
body
|
|
19
|
-
block content
|