@opentripplanner/core-utils 15.0.0 → 16.0.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/esm/index.js +3 -0
- package/esm/index.js.map +1 -1
- package/esm/itinerary.js +104 -78
- package/esm/itinerary.js.map +1 -1
- package/esm/map.js +2 -2
- package/esm/map.js.map +1 -1
- package/esm/query-gen.js +9 -5
- package/esm/query-gen.js.map +1 -1
- package/esm/route.js +26 -20
- package/esm/route.js.map +1 -1
- package/esm/storage.js +4 -1
- package/esm/storage.js.map +1 -1
- package/esm/time.js +6 -5
- package/esm/time.js.map +1 -1
- package/esm/ui.js +4 -2
- package/esm/ui.js.map +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +6 -0
- package/lib/index.js.map +1 -1
- package/lib/itinerary.d.ts +20 -11
- package/lib/itinerary.d.ts.map +1 -1
- package/lib/itinerary.js +96 -85
- package/lib/itinerary.js.map +1 -1
- package/lib/map.d.ts +2 -2
- package/lib/map.d.ts.map +1 -1
- package/lib/map.js +1 -1
- package/lib/map.js.map +1 -1
- package/lib/query-gen.d.ts +1 -19
- package/lib/query-gen.d.ts.map +1 -1
- package/lib/query-gen.js +9 -5
- package/lib/query-gen.js.map +1 -1
- package/lib/route.d.ts +10 -8
- package/lib/route.d.ts.map +1 -1
- package/lib/route.js +22 -16
- package/lib/route.js.map +1 -1
- package/lib/storage.d.ts +1 -1
- package/lib/storage.d.ts.map +1 -1
- package/lib/storage.js +4 -1
- package/lib/storage.js.map +1 -1
- package/lib/time.d.ts +3 -1
- package/lib/time.d.ts.map +1 -1
- package/lib/time.js +5 -4
- package/lib/time.js.map +1 -1
- package/lib/ui.d.ts.map +1 -1
- package/lib/ui.js +4 -2
- package/lib/ui.js.map +1 -1
- package/package.json +9 -7
- package/src/__tests__/itinerary.ts +64 -6
- package/src/index.ts +3 -0
- package/src/itinerary.ts +145 -97
- package/src/map.ts +5 -3
- package/src/query-gen.ts +15 -9
- package/src/route.ts +65 -38
- package/src/storage.ts +8 -2
- package/src/time.ts +7 -6
- package/src/ui.ts +8 -6
- package/tsconfig.json +1 -0
- package/tsconfig.tsbuildinfo +1 -1
package/src/route.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Leg,
|
|
3
|
+
Route,
|
|
4
|
+
TransitMode,
|
|
5
|
+
TransitOperator
|
|
6
|
+
} from "@opentripplanner/types";
|
|
2
7
|
import chroma from "chroma-js";
|
|
3
8
|
/**
|
|
4
9
|
* Returns the transit operator (if an exact match is found) from the transit
|
|
@@ -16,7 +21,7 @@ export function getTransitOperatorFromFeedIdAndAgencyId(
|
|
|
16
21
|
feedId: string,
|
|
17
22
|
agencyId: string | number,
|
|
18
23
|
transitOperators: TransitOperator[]
|
|
19
|
-
): TransitOperator {
|
|
24
|
+
): TransitOperator | null {
|
|
20
25
|
return (
|
|
21
26
|
transitOperators.find(
|
|
22
27
|
transitOperator =>
|
|
@@ -37,7 +42,7 @@ export function getTransitOperatorFromFeedIdAndAgencyId(
|
|
|
37
42
|
export function getTransitOperatorFromLeg(
|
|
38
43
|
leg: Leg,
|
|
39
44
|
transitOperators: TransitOperator[]
|
|
40
|
-
): TransitOperator {
|
|
45
|
+
): TransitOperator | null {
|
|
41
46
|
if (!leg.routeId || !leg.agencyId) return null;
|
|
42
47
|
const feedId = leg.routeId.split(":")[0];
|
|
43
48
|
return getTransitOperatorFromFeedIdAndAgencyId(
|
|
@@ -60,7 +65,7 @@ export function getTransitOperatorFromLeg(
|
|
|
60
65
|
export function getTransitOperatorFromOtpRoute(
|
|
61
66
|
route: Route,
|
|
62
67
|
transitOperators: TransitOperator[]
|
|
63
|
-
): TransitOperator {
|
|
68
|
+
): TransitOperator | null {
|
|
64
69
|
if (!route.id) return null;
|
|
65
70
|
const feedId = route.id.split(":")[0];
|
|
66
71
|
let agencyId: string | number;
|
|
@@ -108,7 +113,7 @@ const END_OF_LIST_COMPARATOR_VALUE = 999999999999;
|
|
|
108
113
|
function getTransitOperatorComparatorValue(
|
|
109
114
|
route: Route,
|
|
110
115
|
transitOperators: TransitOperator[]
|
|
111
|
-
): number | string {
|
|
116
|
+
): number | string | undefined {
|
|
112
117
|
// if the transitOperators is undefined or has zero length, use the route's
|
|
113
118
|
// agency name as the comparator value
|
|
114
119
|
if (!transitOperators || transitOperators.length === 0) {
|
|
@@ -146,7 +151,7 @@ export function makeTransitOperatorComparator(
|
|
|
146
151
|
return (a: Route, b: Route) => {
|
|
147
152
|
const aVal = getTransitOperatorComparatorValue(a, transitOperators);
|
|
148
153
|
const bVal = getTransitOperatorComparatorValue(b, transitOperators);
|
|
149
|
-
if (typeof aVal === "string") {
|
|
154
|
+
if (typeof aVal === "string" && typeof bVal === "string") {
|
|
150
155
|
// happens when transitOperators is undefined. Both aVal are guaranteed to
|
|
151
156
|
// be strings. Make a string comparison.
|
|
152
157
|
if (aVal < bVal) return -1;
|
|
@@ -163,21 +168,19 @@ export function makeTransitOperatorComparator(
|
|
|
163
168
|
* Gets the desired sort values according to an optional getter function. If the
|
|
164
169
|
* getter function is not defined, the original sort values are returned.
|
|
165
170
|
*/
|
|
166
|
-
function getSortValues(
|
|
167
|
-
|
|
168
|
-
a:
|
|
169
|
-
b:
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
function getSortValues<T>(a: T, b: T): { aVal: T; bVal: T };
|
|
172
|
+
function getSortValues<T, TValue>(
|
|
173
|
+
a: T,
|
|
174
|
+
b: T,
|
|
175
|
+
getterFn: (item: T) => TValue
|
|
176
|
+
): { aVal: TValue; bVal: TValue };
|
|
177
|
+
function getSortValues<T, TValue>(a: T, b: T, getterFn?: (item: T) => TValue) {
|
|
173
178
|
if (typeof getterFn === "function") {
|
|
174
|
-
aVal
|
|
175
|
-
bVal = getterFn(b);
|
|
176
|
-
} else {
|
|
177
|
-
aVal = a;
|
|
178
|
-
bVal = b;
|
|
179
|
+
return { aVal: getterFn(a), bVal: getterFn(b) };
|
|
179
180
|
}
|
|
180
|
-
|
|
181
|
+
|
|
182
|
+
// When no getter is provided, TValue is inferred as T via overload.
|
|
183
|
+
return { aVal: (a as unknown) as TValue, bVal: (b as unknown) as TValue };
|
|
181
184
|
}
|
|
182
185
|
|
|
183
186
|
// Lookup for the sort values associated with various OTP modes.
|
|
@@ -193,12 +196,19 @@ const modeComparatorValue = {
|
|
|
193
196
|
CABLE_CAR: 6,
|
|
194
197
|
FUNICULAR: 7,
|
|
195
198
|
BUS: 8
|
|
196
|
-
|
|
199
|
+
// eslint-disable-next-line prettier/prettier
|
|
200
|
+
} as const satisfies Partial<Record<TransitMode, number>>;
|
|
201
|
+
|
|
202
|
+
type SupportedTransitMode = keyof typeof modeComparatorValue;
|
|
203
|
+
|
|
204
|
+
function isSupportedTransitMode(mode: TransitMode): mode is SupportedTransitMode {
|
|
205
|
+
return mode in modeComparatorValue;
|
|
206
|
+
}
|
|
197
207
|
|
|
198
208
|
// Lookup that maps route types to the OTP mode sort values.
|
|
199
209
|
// Note: JSDoc format not used to avoid bug in documentationjs.
|
|
200
210
|
// https://github.com/documentationjs/documentation/issues/372
|
|
201
|
-
const routeTypeComparatorValue = {
|
|
211
|
+
const routeTypeComparatorValue: Record<number, number> = {
|
|
202
212
|
0: modeComparatorValue.TRAM, // - Tram, Streetcar, Light rail.
|
|
203
213
|
1: modeComparatorValue.SUBWAY, // - Subway, Metro.
|
|
204
214
|
2: modeComparatorValue.RAIL, // - Rail. Used for intercity or long-distance travel.
|
|
@@ -222,10 +232,13 @@ function getRouteTypeComparatorValue(route: Route): number {
|
|
|
222
232
|
// string-based modes, but the long route response returns the
|
|
223
233
|
// integer route type. This attempts to account for both of those cases.
|
|
224
234
|
if (!route) throw new Error(`Route is undefined. ${route}`);
|
|
225
|
-
if (
|
|
235
|
+
if (route.mode && isSupportedTransitMode(route.mode)) {
|
|
226
236
|
return modeComparatorValue[route.mode];
|
|
227
237
|
}
|
|
228
|
-
if (
|
|
238
|
+
if (
|
|
239
|
+
route.type &&
|
|
240
|
+
typeof routeTypeComparatorValue[route.type] !== "undefined"
|
|
241
|
+
) {
|
|
229
242
|
return routeTypeComparatorValue[route.type];
|
|
230
243
|
}
|
|
231
244
|
// Default the comparator value to a large number (placing the route at the
|
|
@@ -311,11 +324,18 @@ const isNullOrNaN = (val: any): boolean => {
|
|
|
311
324
|
* @param {function} [objGetterFn] An optional function to obtain the
|
|
312
325
|
* comparison value from the comparator function arguments
|
|
313
326
|
*/
|
|
314
|
-
export function makeNumericValueComparator(
|
|
315
|
-
|
|
327
|
+
export function makeNumericValueComparator(): (a: number, b: number) => number;
|
|
328
|
+
export function makeNumericValueComparator<T>(
|
|
329
|
+
objGetterFn: (item: T) => number | null | undefined
|
|
330
|
+
): (a: T, b: T) => number;
|
|
331
|
+
export function makeNumericValueComparator<T>(
|
|
332
|
+
objGetterFn?: (item: T) => number | null | undefined
|
|
316
333
|
) {
|
|
317
|
-
return (a:
|
|
318
|
-
const { aVal, bVal } =
|
|
334
|
+
return (a: T, b: T): number => {
|
|
335
|
+
const { aVal, bVal } =
|
|
336
|
+
typeof objGetterFn === "function"
|
|
337
|
+
? getSortValues(a, b, objGetterFn)
|
|
338
|
+
: getSortValues(a, b);
|
|
319
339
|
|
|
320
340
|
// if both values aren't valid numbers, use the next sort criteria
|
|
321
341
|
if (isNullOrNaN(aVal) && isNullOrNaN(bVal)) {
|
|
@@ -328,8 +348,7 @@ export function makeNumericValueComparator(
|
|
|
328
348
|
if (isNullOrNaN(bVal)) return -1;
|
|
329
349
|
|
|
330
350
|
// a and b are valid numbers, return the sort value
|
|
331
|
-
|
|
332
|
-
return aVal - bVal;
|
|
351
|
+
return (aVal as number) - (bVal as number);
|
|
333
352
|
};
|
|
334
353
|
}
|
|
335
354
|
|
|
@@ -343,11 +362,19 @@ export function makeNumericValueComparator(
|
|
|
343
362
|
* @param {function} [objGetterFn] An optional function to obtain the
|
|
344
363
|
* comparison value from the comparator function arguments
|
|
345
364
|
*/
|
|
346
|
-
export function makeStringValueComparator(
|
|
347
|
-
|
|
365
|
+
export function makeStringValueComparator(): (a: string, b: string) => number;
|
|
366
|
+
export function makeStringValueComparator<T>(
|
|
367
|
+
objGetterFn: (item: T) => string | null | undefined
|
|
368
|
+
): (a: T, b: T) => number;
|
|
369
|
+
export function makeStringValueComparator<T>(
|
|
370
|
+
objGetterFn?: (item: T) => string | null | undefined
|
|
348
371
|
) {
|
|
349
|
-
return (a:
|
|
350
|
-
const { aVal, bVal } =
|
|
372
|
+
return (a: T, b: T): number => {
|
|
373
|
+
const { aVal, bVal } =
|
|
374
|
+
typeof objGetterFn === "function"
|
|
375
|
+
? getSortValues(a, b, objGetterFn)
|
|
376
|
+
: getSortValues(a, b);
|
|
377
|
+
|
|
351
378
|
// both a and b are uncomparable strings, return equivalent value
|
|
352
379
|
if (!aVal && !bVal) return 0;
|
|
353
380
|
// a is not a comparable string, b gets priority
|
|
@@ -370,7 +397,7 @@ export function makeStringValueComparator(
|
|
|
370
397
|
* Also see https://github.com/opentripplanner/otp-react-redux/issues/122
|
|
371
398
|
* This was updated in OTP2 TO be empty by default. https://docs.opentripplanner.org/en/v2.3.0/OTP2-MigrationGuide/#:~:text=the%20Alerts-,Changes%20to%20the%20Index%20API,-Error%20handling%20is
|
|
372
399
|
*/
|
|
373
|
-
export function getRouteSortOrderValue(route: Route): number {
|
|
400
|
+
export function getRouteSortOrderValue(route: Route): number | null {
|
|
374
401
|
const isOTP1 = !!route.agencyId;
|
|
375
402
|
const { sortOrder } = route;
|
|
376
403
|
|
|
@@ -388,10 +415,10 @@ export function getRouteSortOrderValue(route: Route): number {
|
|
|
388
415
|
* returned. If all comparison functions return equivalence, then the values
|
|
389
416
|
* are assumed to be equivalent.
|
|
390
417
|
*/
|
|
391
|
-
export function makeMultiCriteriaSort(
|
|
392
|
-
...criteria: ((a:
|
|
418
|
+
export function makeMultiCriteriaSort<T = unknown>(
|
|
419
|
+
...criteria: ((a: T, b: T) => number)[]
|
|
393
420
|
) {
|
|
394
|
-
return (a:
|
|
421
|
+
return (a: T, b: T): number => {
|
|
395
422
|
for (let i = 0; i < criteria.length; i++) {
|
|
396
423
|
const curCriteriaComparatorValue = criteria[i](a, b);
|
|
397
424
|
// if the comparison objects are not equivalent, return the value obtained
|
|
@@ -437,7 +464,7 @@ export function makeMultiCriteriaSort(
|
|
|
437
464
|
*/
|
|
438
465
|
export function makeRouteComparator(
|
|
439
466
|
transitOperators: TransitOperator[]
|
|
440
|
-
): (a:
|
|
467
|
+
): (a: Route, b: Route) => number {
|
|
441
468
|
return makeMultiCriteriaSort(
|
|
442
469
|
makeTransitOperatorComparator(transitOperators),
|
|
443
470
|
makeNumericValueComparator(obj => getRouteSortOrderValue(obj)),
|
package/src/storage.ts
CHANGED
|
@@ -16,10 +16,16 @@ export function storeItem(key: string, object: unknown): void {
|
|
|
16
16
|
* Retrieve a javascript object at the specified key. If not found, defaults to
|
|
17
17
|
* null or, the optionally provided notFoundValue.
|
|
18
18
|
*/
|
|
19
|
-
export function getItem(
|
|
20
|
-
|
|
19
|
+
export function getItem<T>(
|
|
20
|
+
key: string,
|
|
21
|
+
notFoundValue: T | null = null
|
|
22
|
+
): T | null {
|
|
23
|
+
let itemAsString: string | null = null;
|
|
21
24
|
try {
|
|
22
25
|
itemAsString = window.localStorage.getItem(`${STORAGE_PREFIX}.${key}`);
|
|
26
|
+
if (!itemAsString) {
|
|
27
|
+
return notFoundValue;
|
|
28
|
+
}
|
|
23
29
|
const json = JSON.parse(itemAsString);
|
|
24
30
|
if (json) return json;
|
|
25
31
|
return notFoundValue;
|
package/src/time.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Config } from "@opentripplanner/types";
|
|
2
2
|
import { startOfDay, add, format } from "date-fns";
|
|
3
|
-
import {
|
|
3
|
+
import { toZonedTime } from "date-fns-tz";
|
|
4
4
|
|
|
5
5
|
// Date/time formats (per date-fns) when sending/receiving date from OTP
|
|
6
6
|
// regardless of whatever the user has configured as the display format.
|
|
@@ -55,7 +55,7 @@ export function getLongDateFormat(config: Config): string {
|
|
|
55
55
|
* Offsets a time according to the provided time options
|
|
56
56
|
* and returns the result.
|
|
57
57
|
*/
|
|
58
|
-
export function offsetTime(ms, options) {
|
|
58
|
+
export function offsetTime(ms: number, options?: { offset: number }): number {
|
|
59
59
|
return ms + (options?.offset || 0);
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -79,8 +79,9 @@ export function formatSecondsAfterMidnight(
|
|
|
79
79
|
* GMT+0 if the Intl API is unavailable.
|
|
80
80
|
*/
|
|
81
81
|
export function getUserTimezone(fallbackTimezone = "Etc/Greenwich"): string {
|
|
82
|
-
if (process.env.NODE_ENV === "test")
|
|
83
|
-
|
|
82
|
+
if (process.env.NODE_ENV === "test")
|
|
83
|
+
return process.env.TZ ?? fallbackTimezone;
|
|
84
|
+
return Intl?.DateTimeFormat().resolvedOptions().timeZone ?? fallbackTimezone;
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
/**
|
|
@@ -88,7 +89,7 @@ export function getUserTimezone(fallbackTimezone = "Etc/Greenwich"): string {
|
|
|
88
89
|
* The conversion to the user's timezone is needed for testing purposes.
|
|
89
90
|
*/
|
|
90
91
|
export function getCurrentTime(timezone = getUserTimezone()): string {
|
|
91
|
-
return format(
|
|
92
|
+
return format(toZonedTime(Date.now(), timezone), OTP_API_TIME_FORMAT);
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
/**
|
|
@@ -96,5 +97,5 @@ export function getCurrentTime(timezone = getUserTimezone()): string {
|
|
|
96
97
|
* The conversion to the user's timezone is needed for testing purposes.
|
|
97
98
|
*/
|
|
98
99
|
export function getCurrentDate(timezone = getUserTimezone()): string {
|
|
99
|
-
return format(
|
|
100
|
+
return format(toZonedTime(Date.now(), timezone), OTP_API_DATE_FORMAT);
|
|
100
101
|
}
|
package/src/ui.ts
CHANGED
|
@@ -11,15 +11,17 @@ export function isMobile(): boolean {
|
|
|
11
11
|
* https://github.com/conveyal/trimet-mod-otp/issues/92.
|
|
12
12
|
*/
|
|
13
13
|
export function enableScrollForSelector(selector: string): void {
|
|
14
|
-
const overlay = document.querySelector(selector);
|
|
15
|
-
let clientY = null; // remember Y position on touch start
|
|
14
|
+
const overlay = document.querySelector<HTMLElement>(selector);
|
|
15
|
+
let clientY: number | null = null; // remember Y position on touch start
|
|
16
16
|
|
|
17
17
|
function isOverlayTotallyScrolled(): boolean {
|
|
18
18
|
// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions
|
|
19
|
+
if (!overlay) return false;
|
|
19
20
|
return overlay.scrollHeight - overlay.scrollTop <= overlay.clientHeight;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
function disableRubberBand(event: TouchEvent) {
|
|
24
|
+
if (!overlay || clientY === null) return;
|
|
23
25
|
const clientYDelta = event.targetTouches[0].clientY - clientY;
|
|
24
26
|
|
|
25
27
|
if (overlay.scrollTop === 0 && clientYDelta > 0) {
|
|
@@ -33,9 +35,9 @@ export function enableScrollForSelector(selector: string): void {
|
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
overlay
|
|
38
|
+
overlay?.addEventListener(
|
|
37
39
|
"touchstart",
|
|
38
|
-
|
|
40
|
+
(event: TouchEvent) => {
|
|
39
41
|
if (event.targetTouches.length === 1) {
|
|
40
42
|
// detect single touch
|
|
41
43
|
clientY = event.targetTouches[0].clientY;
|
|
@@ -44,9 +46,9 @@ export function enableScrollForSelector(selector: string): void {
|
|
|
44
46
|
false
|
|
45
47
|
);
|
|
46
48
|
|
|
47
|
-
overlay
|
|
49
|
+
overlay?.addEventListener(
|
|
48
50
|
"touchmove",
|
|
49
|
-
|
|
51
|
+
(event: TouchEvent) => {
|
|
50
52
|
if (event.targetTouches.length === 1) {
|
|
51
53
|
// detect single touch
|
|
52
54
|
disableRubberBand(event);
|