@everyonesoftware/common 2.0.0 → 3.0.0

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 (59) hide show
  1. package/.github/workflows/publish.yml +54 -36
  2. package/package.json +6 -2
  3. package/sources/asyncIterator.ts +436 -436
  4. package/sources/asyncIteratorToJavascriptAsyncIteratorAdapter.ts +47 -47
  5. package/sources/asyncResult.ts +95 -95
  6. package/sources/byteList.ts +201 -201
  7. package/sources/byteListStream.ts +120 -120
  8. package/sources/byteReadStream.ts +23 -23
  9. package/sources/byteWriteStream.ts +15 -15
  10. package/sources/characterList.ts +194 -194
  11. package/sources/characterListStream.ts +150 -150
  12. package/sources/characterReadStream.ts +80 -80
  13. package/sources/characterReadStreamIterator.ts +127 -127
  14. package/sources/concatenateIterable.ts +118 -118
  15. package/sources/concatenateIterator.ts +164 -164
  16. package/sources/currentProcess.ts +157 -157
  17. package/sources/dateTime.ts +129 -129
  18. package/sources/depthFirstSearch.ts +229 -229
  19. package/sources/flatMapIterable.ts +103 -103
  20. package/sources/flatMapIterator.ts +151 -151
  21. package/sources/generator.ts +250 -250
  22. package/sources/index.ts +1 -1
  23. package/sources/iterator.ts +480 -480
  24. package/sources/javascriptAsyncIteratorToAsyncIteratorAdapter.ts +123 -123
  25. package/sources/javascriptSetSet.ts +133 -133
  26. package/sources/listQueue.ts +61 -61
  27. package/sources/listStack.ts +61 -61
  28. package/sources/luxonDateTime.ts +108 -108
  29. package/sources/mapAsyncIterator.ts +140 -140
  30. package/sources/mutableMap.ts +291 -291
  31. package/sources/node.ts +36 -36
  32. package/sources/promiseAsyncResult.ts +173 -173
  33. package/sources/queue.ts +48 -48
  34. package/sources/recreationDotGovClient.ts +258 -258
  35. package/sources/searchControl.ts +41 -41
  36. package/sources/set.ts +243 -243
  37. package/sources/skipAsyncIterator.ts +144 -144
  38. package/sources/stack.ts +47 -47
  39. package/sources/syncResult.ts +299 -299
  40. package/sources/takeAsyncIterator.ts +140 -140
  41. package/sources/whereAsyncIterator.ts +142 -142
  42. package/sources/wonderlandTrailClient.ts +1502 -1502
  43. package/tests/assertTestTests.ts +74 -74
  44. package/tests/byteListStreamTests.ts +389 -389
  45. package/tests/byteListTests.ts +26 -26
  46. package/tests/characterListStreamTests.ts +390 -390
  47. package/tests/characterListTests.ts +249 -249
  48. package/tests/dateTimeTests.ts +29 -29
  49. package/tests/depthFirstSearchTests.ts +105 -105
  50. package/tests/generatorTests.ts +85 -85
  51. package/tests/mutableMapTests.ts +153 -153
  52. package/tests/promiseAsyncResultTests.ts +687 -687
  53. package/tests/queueTests.ts +28 -28
  54. package/tests/recreationDotGovClientTests.ts +190 -190
  55. package/tests/setTests.ts +139 -139
  56. package/tests/stackTests.ts +65 -65
  57. package/tests/syncResultTests.ts +1250 -1250
  58. package/tests/wonderlandTrailClientTests.ts +451 -451
  59. package/tsup.config.ts +12 -12
@@ -1,1503 +1,1503 @@
1
- import { DateTime } from "./dateTime";
2
- import { HttpClient } from "./httpClient";
3
- import { HttpIncomingResponse } from "./httpIncomingResponse";
4
- import { HttpOutgoingRequest } from "./httpOutgoingRequest";
5
- import { Iterable } from "./iterable";
6
- import { PreCondition } from "./preCondition";
7
- import { RecreationDotGovClient, RecreationDotGovDivisionAvailability, RecreationDotGovDivisionDayAvailability } from "./recreationDotGovClient";
8
- import { AsyncResult } from "./asyncResult";
9
- import { Map } from "./map";
10
- import { List } from "./list";
11
- import { JavascriptIterable } from "./javascript";
12
- import { hasProperty, isNumber, isObject, isUndefinedOrNull } from "./types";
13
- import { MutableMap } from "./mutableMap";
14
- import { NotFoundError } from "./notFoundError";
15
- import { Iterator } from "./iterator";
16
- import { Stack } from "./stack";
17
- import { ListStack } from "./listStack";
18
- import { PromiseAsyncResult } from "./promiseAsyncResult";
19
- import { SyncResult } from "./syncResult";
20
-
21
- export interface WonderlandTrailLocation
22
- {
23
- readonly name: string;
24
- readonly trailhead: boolean;
25
- readonly foodCacheStorage: boolean;
26
- readonly divisionId: string;
27
- readonly groupSiteDivisionId: string;
28
- readonly latitude: number;
29
- readonly longitude: number;
30
- readonly elevationFeet: number;
31
- }
32
-
33
- export function isWonderlandTrailLocation(value: unknown): value is WonderlandTrailLocation
34
- {
35
- return isObject(value) &&
36
- hasProperty(value, "name") &&
37
- hasProperty(value, "trailhead") &&
38
- hasProperty(value, "foodCacheStorage") &&
39
- hasProperty(value, "divisionId") &&
40
- hasProperty(value, "groupSiteDivisionId");
41
- }
42
-
43
- export abstract class WonderlandTrailLocations
44
- {
45
- public static readonly graniteCreek: WonderlandTrailLocation = {
46
- name: "Granite Creek",
47
- trailhead: false,
48
- foodCacheStorage: false,
49
- divisionId: "46753170009",
50
- groupSiteDivisionId: "46753170010",
51
- latitude: 46.92058,
52
- longitude: -121.70800,
53
- elevationFeet: 5851,
54
- };
55
-
56
- public static readonly sunriseCamp: WonderlandTrailLocation = {
57
- name: "Sunrise Camp",
58
- trailhead: false,
59
- foodCacheStorage: false,
60
- divisionId: "46753170058",
61
- groupSiteDivisionId: "46753170059",
62
- latitude: 46.91129,
63
- longitude: -121.66002,
64
- elevationFeet: 6258,
65
- };
66
-
67
- public static readonly sunriseVisitorCenter: WonderlandTrailLocation = {
68
- name: "Sunrise Visitor Center",
69
- trailhead: true,
70
- foodCacheStorage: true,
71
- divisionId: "",
72
- groupSiteDivisionId: "",
73
- latitude: 46.91448,
74
- longitude: -121.64337,
75
- elevationFeet: 6410,
76
- };
77
-
78
- public static readonly whiteRiver: WonderlandTrailLocation = {
79
- name: "White River",
80
- trailhead: true,
81
- foodCacheStorage: true,
82
- divisionId: "46753170066",
83
- groupSiteDivisionId: "46753170067",
84
- latitude: 46.90255,
85
- longitude: -121.63869,
86
- elevationFeet: 4229,
87
- };
88
-
89
- public static readonly fryingPanCreek: WonderlandTrailLocation = {
90
- name: "Fryingpan Creek",
91
- trailhead: true,
92
- foodCacheStorage: false,
93
- divisionId: "",
94
- groupSiteDivisionId: "",
95
- latitude: 46.88809,
96
- longitude: -121.61019,
97
- elevationFeet: 3835,
98
- };
99
-
100
- public static readonly summerland: WonderlandTrailLocation = {
101
- name: "Summerland",
102
- trailhead: false,
103
- foodCacheStorage: false,
104
- divisionId: "46753170056",
105
- groupSiteDivisionId: "46753170057",
106
- latitude: 46.86616,
107
- longitude: -121.65843,
108
- elevationFeet: 5988,
109
- };
110
-
111
- public static readonly indianBar: WonderlandTrailLocation = {
112
- name: "Indian Bar",
113
- trailhead: false,
114
- foodCacheStorage: false,
115
- divisionId: "46753170046",
116
- groupSiteDivisionId: "46753170047",
117
- latitude: 46.82593,
118
- longitude: -121.63942,
119
- elevationFeet: 5101,
120
- };
121
-
122
- public static readonly nickelCreek: WonderlandTrailLocation = {
123
- name: "Nickel Creek",
124
- trailhead: false,
125
- foodCacheStorage: false,
126
- divisionId: "46753170051",
127
- groupSiteDivisionId: "46753170052",
128
- latitude: 46.77204,
129
- longitude: -121.62402,
130
- elevationFeet: 3383,
131
- };
132
-
133
- public static readonly boxCanyon: WonderlandTrailLocation = {
134
- name: "Box Canyon",
135
- trailhead: true,
136
- foodCacheStorage: false,
137
- divisionId: "",
138
- groupSiteDivisionId: "",
139
- latitude: 46.76570,
140
- longitude: -121.63517,
141
- elevationFeet: 3025,
142
- };
143
-
144
- public static readonly mapleCreek: WonderlandTrailLocation = {
145
- name: "Maple Creek",
146
- trailhead: false,
147
- foodCacheStorage: false,
148
- divisionId: "46753170027",
149
- groupSiteDivisionId: "46753170028",
150
- latitude: 46.75745,
151
- longitude: -121.65764,
152
- elevationFeet: 2806,
153
- };
154
-
155
- public static readonly reflectionLakes: WonderlandTrailLocation = {
156
- name: "Reflection Lakes",
157
- trailhead: true,
158
- foodCacheStorage: false,
159
- divisionId: "",
160
- groupSiteDivisionId: "",
161
- latitude: 46.76824,
162
- longitude: -121.72890,
163
- elevationFeet: 4862,
164
- };
165
-
166
- public static readonly paradiseRiver: WonderlandTrailLocation = {
167
- name: "Paradise River",
168
- trailhead: false,
169
- foodCacheStorage: false,
170
- divisionId: "46753170031",
171
- groupSiteDivisionId: "46753170032",
172
- latitude: 46.77076,
173
- longitude: -121.75909,
174
- elevationFeet: 3960,
175
- };
176
-
177
- public static readonly longmire: WonderlandTrailLocation = {
178
- name: "Longmire",
179
- trailhead: true,
180
- foodCacheStorage: true,
181
- divisionId: "",
182
- groupSiteDivisionId: "",
183
- latitude: 46.75011,
184
- longitude: -121.81253,
185
- elevationFeet: 2750,
186
- };
187
-
188
- public static readonly pyramidCreek: WonderlandTrailLocation = {
189
- name: "Pyramid Creek",
190
- trailhead: false,
191
- foodCacheStorage: false,
192
- divisionId: "46753170033",
193
- groupSiteDivisionId: "",
194
- latitude: 46.77832,
195
- longitude: -121.80963,
196
- elevationFeet: 3721,
197
- };
198
-
199
- public static readonly devilsDream: WonderlandTrailLocation = {
200
- name: "Devil's Dream",
201
- trailhead: false,
202
- foodCacheStorage: false,
203
- divisionId: "46753170040",
204
- groupSiteDivisionId: "46753170041",
205
- latitude: 46.78196,
206
- longitude: -121.83297,
207
- elevationFeet: 4929,
208
- };
209
-
210
- public static readonly southPuyallupRiver: WonderlandTrailLocation = {
211
- name: "South Puyallup River",
212
- trailhead: false,
213
- foodCacheStorage: false,
214
- divisionId: "46753170035",
215
- groupSiteDivisionId: "46753170036",
216
- latitude: 46.81331,
217
- longitude: -121.86445,
218
- elevationFeet: 4183,
219
- };
220
-
221
- public static readonly klapatchePark: WonderlandTrailLocation = {
222
- name: "Klapatche Park",
223
- trailhead: false,
224
- foodCacheStorage: false,
225
- divisionId: "46753170024",
226
- groupSiteDivisionId: "",
227
- latitude: 46.83571,
228
- longitude: -121.87752,
229
- elevationFeet: 5496,
230
- };
231
-
232
- public static readonly northPuyallupRiver: WonderlandTrailLocation = {
233
- name: "North Puyallup River",
234
- trailhead: false,
235
- foodCacheStorage: false,
236
- divisionId: "46753170029",
237
- groupSiteDivisionId: "46753170030",
238
- latitude: 46.84751,
239
- longitude: -121.87005,
240
- elevationFeet: 3733,
241
- };
242
-
243
- public static readonly goldenLakes: WonderlandTrailLocation = {
244
- name: "Golden Lakes",
245
- trailhead: false,
246
- foodCacheStorage: false,
247
- divisionId: "46753170022",
248
- groupSiteDivisionId: "46753170023",
249
- latitude: 46.88327,
250
- longitude: -121.89919,
251
- elevationFeet: 4927,
252
- };
253
-
254
- public static readonly southMowichRiver: WonderlandTrailLocation = {
255
- name: "South Mowich River",
256
- trailhead: false,
257
- foodCacheStorage: false,
258
- divisionId: "46753170019",
259
- groupSiteDivisionId: "46753170020",
260
- latitude: 46.91146,
261
- longitude: -121.89314,
262
- elevationFeet: 2686,
263
- };
264
-
265
- public static readonly mowichLake: WonderlandTrailLocation = {
266
- name: "Mowich Lake",
267
- trailhead: false,
268
- foodCacheStorage: false,
269
- divisionId: "46753170015",
270
- groupSiteDivisionId: "46753170016",
271
- latitude: 46.93204,
272
- longitude: -121.86351,
273
- elevationFeet: 4868,
274
- };
275
-
276
- public static readonly eaglesRoost: WonderlandTrailLocation = {
277
- name: "Eagle's Roost",
278
- trailhead: false,
279
- foodCacheStorage: false,
280
- divisionId: "46753170006",
281
- groupSiteDivisionId: "",
282
- latitude: 46.91529,
283
- longitude: -121.84816,
284
- elevationFeet: 4834,
285
- };
286
-
287
- public static readonly cataractValley: WonderlandTrailLocation = {
288
- name: "Cataract Valley",
289
- trailhead: false,
290
- foodCacheStorage: false,
291
- divisionId: "46753170003",
292
- groupSiteDivisionId: "46753170004",
293
- latitude: 46.94049,
294
- longitude: -121.80486,
295
- elevationFeet: 4488,
296
- };
297
-
298
- public static readonly ipsutCreek: WonderlandTrailLocation = {
299
- name: "Ipsut Creek",
300
- trailhead: false,
301
- foodCacheStorage: false,
302
- divisionId: "46753170011",
303
- groupSiteDivisionId: "46753170012",
304
- latitude: 46.97615,
305
- longitude: -121.83022,
306
- elevationFeet: 2359,
307
- };
308
-
309
- public static readonly carbonRiver: WonderlandTrailLocation = {
310
- name: "Carbon River",
311
- trailhead: false,
312
- foodCacheStorage: false,
313
- divisionId: "46753170001",
314
- groupSiteDivisionId: "46753170002",
315
- latitude: 46.95063,
316
- longitude: -121.79991,
317
- elevationFeet: 3255,
318
- };
319
-
320
- public static readonly dickCreek: WonderlandTrailLocation = {
321
- name: "Dick Creek",
322
- trailhead: false,
323
- foodCacheStorage: false,
324
- divisionId: "46753170005",
325
- groupSiteDivisionId: "",
326
- latitude: 46.94071,
327
- longitude: -121.78431,
328
- elevationFeet: 4114,
329
- };
330
-
331
- public static readonly mysticLake: WonderlandTrailLocation = {
332
- name: "Mystic Lake",
333
- trailhead: false,
334
- foodCacheStorage: false,
335
- divisionId: "46753170017",
336
- groupSiteDivisionId: "46753170018",
337
- latitude: 46.91570,
338
- longitude: -121.75045,
339
- elevationFeet: 5538,
340
- };
341
-
342
- public static getLocations(): Iterable<WonderlandTrailLocation>
343
- {
344
- return Iterable.create([
345
- this.graniteCreek,
346
- this.sunriseCamp,
347
- this.sunriseVisitorCenter,
348
- this.whiteRiver,
349
- this.fryingPanCreek,
350
- this.summerland,
351
- this.indianBar,
352
- this.nickelCreek,
353
- this.boxCanyon,
354
- this.mapleCreek,
355
- this.reflectionLakes,
356
- this.paradiseRiver,
357
- this.longmire,
358
- this.pyramidCreek,
359
- this.devilsDream,
360
- this.southPuyallupRiver,
361
- this.klapatchePark,
362
- this.northPuyallupRiver,
363
- this.goldenLakes,
364
- this.southMowichRiver,
365
- this.mowichLake,
366
- this.eaglesRoost,
367
- this.cataractValley,
368
- this.ipsutCreek,
369
- this.carbonRiver,
370
- this.dickCreek,
371
- this.mysticLake,
372
- ]);
373
- }
374
-
375
- public static getTrailheads(): Iterable<WonderlandTrailLocation>
376
- {
377
- return this.getLocations().where(location => location.trailhead);
378
- }
379
- }
380
-
381
- export enum WonderlandTrailReservationType
382
- {
383
- Reserved,
384
- Walkup,
385
- }
386
-
387
- export interface WonderlandTrailAvailabilityType
388
- {
389
- readonly individualSite?: WonderlandTrailReservationType;
390
- readonly groupSite?: WonderlandTrailReservationType;
391
- }
392
-
393
- export class WonderlandTrailAvailability
394
- {
395
- private readonly availabilityMap: MutableMap<WonderlandTrailLocation, MutableMap<string, WonderlandTrailAvailabilityType>>;
396
-
397
- private constructor()
398
- {
399
- this.availabilityMap = Map.create();
400
- }
401
-
402
- public static create(): WonderlandTrailAvailability
403
- {
404
- return new WonderlandTrailAvailability();
405
- }
406
-
407
- public any(): boolean
408
- {
409
- return this.availabilityMap.any().await();
410
- }
411
-
412
- public addAvailability(location: WonderlandTrailLocation, date: DateTime, individualSite?: WonderlandTrailReservationType, groupSite?: WonderlandTrailReservationType): void
413
- {
414
- PreCondition.assertNotUndefinedAndNotNull(location, "location");
415
- PreCondition.assertNotUndefinedAndNotNull(date, "date");
416
-
417
- const locationAvailability: MutableMap<string, WonderlandTrailAvailabilityType> | undefined = this.getAvailability(location);
418
-
419
- const dateString: string = date.toDateString();
420
- const locationDayAvailability: WonderlandTrailAvailabilityType = locationAvailability.getOrSet(dateString, () => { return {}; }).await();
421
- locationAvailability.set(dateString, {
422
- individualSite: individualSite ?? locationDayAvailability.individualSite,
423
- groupSite: groupSite ?? locationDayAvailability.groupSite,
424
- });
425
- }
426
-
427
- public getAvailability(location: WonderlandTrailLocation): MutableMap<string, WonderlandTrailAvailabilityType>
428
- {
429
- PreCondition.assertNotUndefinedAndNotNull(location, "location");
430
-
431
- return this.availabilityMap.getOrSet(location, () => MutableMap.create()).await();
432
- }
433
-
434
- public getDayAvailability(location: WonderlandTrailLocation, date: DateTime): SyncResult<WonderlandTrailAvailabilityType>
435
- {
436
- PreCondition.assertNotUndefinedAndNotNull(location, "location");
437
- PreCondition.assertNotUndefinedAndNotNull(date, "date");
438
-
439
- return this.getAvailability(location).get(date.toDateString())
440
- .convertError(NotFoundError, () => new NotFoundError(`No availability was found for ${location.name} on ${date.toDateString()}.`));
441
- }
442
- }
443
-
444
- export class WonderlandTrailDirection
445
- {
446
- private readonly value: string;
447
-
448
- private constructor(value: string)
449
- {
450
- PreCondition.assertNotEmpty(value, "value");
451
-
452
- this.value = value;
453
- }
454
-
455
- public static readonly clockwise = new WonderlandTrailDirection("Clockwise");
456
- public static readonly counterClockwise = new WonderlandTrailDirection("CounterClockwise");
457
-
458
- public toString(): string
459
- {
460
- return this.value;
461
- }
462
-
463
- public reverse(): WonderlandTrailDirection
464
- {
465
- return this === WonderlandTrailDirection.clockwise ? WonderlandTrailDirection.counterClockwise : WonderlandTrailDirection.clockwise;
466
- }
467
- }
468
-
469
- export class WonderlandTrailConnection
470
- {
471
- public readonly startLocation: WonderlandTrailLocation;
472
- public readonly endLocation: WonderlandTrailLocation;
473
- public readonly distanceMiles: number;
474
- public readonly ascentFeet: number;
475
- public readonly descentFeet: number;
476
- public readonly direction: WonderlandTrailDirection;
477
- public readonly intermediateLocations: Iterable<WonderlandTrailLocation>;
478
-
479
- private constructor(startLocation: WonderlandTrailLocation, endLocation: WonderlandTrailLocation, distanceMiles: number, ascentFeet: number, descentFeet: number, direction: WonderlandTrailDirection, intermediateLocations: Iterable<WonderlandTrailLocation>)
480
- {
481
- PreCondition.assertNotUndefinedAndNotNull(startLocation, "startLocation");
482
- PreCondition.assertNotUndefinedAndNotNull(endLocation, "endLocation");
483
- PreCondition.assertGreaterThanOrEqualTo(distanceMiles, 0, "distanceMiles");
484
- PreCondition.assertGreaterThanOrEqualTo(ascentFeet, 0, "ascentFeet");
485
- PreCondition.assertGreaterThanOrEqualTo(descentFeet, 0, "descentFeet");
486
- PreCondition.assertNotUndefinedAndNotNull(intermediateLocations, "intermediateLocations");
487
-
488
- this.startLocation = startLocation;
489
- this.endLocation = endLocation;
490
- this.distanceMiles = distanceMiles;
491
- this.ascentFeet = ascentFeet;
492
- this.descentFeet = descentFeet;
493
- this.direction = direction;
494
- this.intermediateLocations = intermediateLocations;
495
- }
496
-
497
- public static create(startLocation: WonderlandTrailLocation, endLocation: WonderlandTrailLocation, distanceMiles: number, ascentFeet: number, descentFeet: number, direction: WonderlandTrailDirection, intermediateLocations: Iterable<WonderlandTrailLocation>): WonderlandTrailConnection
498
- {
499
- return new WonderlandTrailConnection(startLocation, endLocation, distanceMiles, ascentFeet, descentFeet, direction, intermediateLocations);
500
- }
501
-
502
- public getLocations(): Iterable<WonderlandTrailLocation>
503
- {
504
- const result: List<WonderlandTrailLocation> = List.create();
505
- WonderlandTrailConnection.ensureExists(result, this.startLocation);
506
- WonderlandTrailConnection.ensureAllExist(result, this.intermediateLocations);
507
- WonderlandTrailConnection.ensureExists(result, this.endLocation);
508
- return result;
509
- }
510
-
511
- private static ensureExists(list: List<WonderlandTrailLocation>, location: WonderlandTrailLocation): void
512
- {
513
- if (!list.contains(location).await())
514
- {
515
- list.add(location);
516
- }
517
- }
518
-
519
- private static ensureAllExist(list: List<WonderlandTrailLocation>, locations: JavascriptIterable<WonderlandTrailLocation>): void
520
- {
521
- for (const location of locations)
522
- {
523
- WonderlandTrailConnection.ensureExists(list, location);
524
- }
525
- }
526
-
527
- public reverseDirection(): WonderlandTrailConnection
528
- {
529
- return WonderlandTrailConnection.create(
530
- this.endLocation,
531
- this.startLocation,
532
- this.distanceMiles,
533
- this.descentFeet,
534
- this.ascentFeet,
535
- this.direction.reverse(),
536
- this.intermediateLocations,
537
- )
538
- }
539
-
540
- public join(connection: WonderlandTrailConnection): WonderlandTrailConnection
541
- {
542
- const intermediateLocations: List<WonderlandTrailLocation> = List.create();
543
- WonderlandTrailConnection.ensureAllExist(intermediateLocations, this.intermediateLocations);
544
- WonderlandTrailConnection.ensureExists(intermediateLocations, this.endLocation);
545
- WonderlandTrailConnection.ensureExists(intermediateLocations, connection.startLocation);
546
- WonderlandTrailConnection.ensureAllExist(intermediateLocations, connection.intermediateLocations);
547
-
548
- return WonderlandTrailConnection.create(
549
- this.startLocation,
550
- connection.endLocation,
551
- this.distanceMiles + connection.distanceMiles,
552
- this.ascentFeet + connection.ascentFeet,
553
- this.descentFeet + connection.descentFeet,
554
- this.direction,
555
- intermediateLocations,
556
- );
557
- }
558
-
559
- public containsLocation(location: WonderlandTrailLocation): boolean
560
- {
561
- return this.getLocations().contains(location).await();
562
- }
563
-
564
- public isLoop(): boolean
565
- {
566
- return this.startLocation === this.endLocation;
567
- }
568
- }
569
-
570
- export class WonderlandTrailConnections
571
- {
572
- private connections: MutableMap<WonderlandTrailLocation, List<WonderlandTrailConnection>>;
573
- public readonly addReverseConnectionDefault: boolean;
574
-
575
- private constructor(addReverseConnectionDefault: boolean)
576
- {
577
- this.connections = Map.create();
578
- this.addReverseConnectionDefault = addReverseConnectionDefault;
579
- }
580
-
581
- public static create(addReverseConnectionDefault?: boolean): WonderlandTrailConnections
582
- {
583
- return new WonderlandTrailConnections(!!addReverseConnectionDefault);
584
- }
585
-
586
- public static createDefault(): WonderlandTrailConnections
587
- {
588
- return WonderlandTrailConnections.create(true)
589
- .addConnection({
590
- startLocation: WonderlandTrailLocations.whiteRiver,
591
- endLocation: WonderlandTrailLocations.fryingPanCreek,
592
- distanceMiles: 2.7,
593
- ascentFeet: 100,
594
- descentFeet: 600,
595
- })
596
- .addConnection({
597
- startLocation: WonderlandTrailLocations.fryingPanCreek,
598
- endLocation: WonderlandTrailLocations.summerland,
599
- distanceMiles: 4.3,
600
- ascentFeet: 2200,
601
- descentFeet: 100,
602
- })
603
- .addConnection({
604
- startLocation: WonderlandTrailLocations.summerland,
605
- endLocation: WonderlandTrailLocations.indianBar,
606
- distanceMiles: 4.7,
607
- ascentFeet: 1200,
608
- descentFeet: 2100,
609
- })
610
- .addConnection({
611
- startLocation: WonderlandTrailLocations.indianBar,
612
- endLocation: WonderlandTrailLocations.nickelCreek,
613
- distanceMiles: 6.8,
614
- ascentFeet: 1400,
615
- descentFeet: 3200,
616
- })
617
- .addConnection({
618
- startLocation: WonderlandTrailLocations.nickelCreek,
619
- endLocation: WonderlandTrailLocations.boxCanyon,
620
- distanceMiles: 0.9,
621
- ascentFeet: 100,
622
- descentFeet: 400,
623
- })
624
- .addConnection({
625
- startLocation: WonderlandTrailLocations.boxCanyon,
626
- endLocation: WonderlandTrailLocations.mapleCreek,
627
- distanceMiles: 2.7,
628
- ascentFeet: 500,
629
- descentFeet: 700,
630
- })
631
- .addConnection({
632
- startLocation: WonderlandTrailLocations.mapleCreek,
633
- endLocation: WonderlandTrailLocations.reflectionLakes,
634
- distanceMiles: 4.7,
635
- ascentFeet: 2300,
636
- descentFeet: 200,
637
- })
638
- .addConnection({
639
- startLocation: WonderlandTrailLocations.reflectionLakes,
640
- endLocation: WonderlandTrailLocations.paradiseRiver,
641
- distanceMiles: 2.6,
642
- ascentFeet: 200,
643
- descentFeet: 1200,
644
- })
645
- .addConnection({
646
- startLocation: WonderlandTrailLocations.paradiseRiver,
647
- endLocation: WonderlandTrailLocations.longmire,
648
- distanceMiles: 3.6,
649
- ascentFeet: 100,
650
- descentFeet: 1200,
651
- })
652
- .addConnection({
653
- startLocation: WonderlandTrailLocations.longmire,
654
- endLocation: WonderlandTrailLocations.pyramidCreek,
655
- distanceMiles: 3.3,
656
- ascentFeet: 1400,
657
- descentFeet: 400,
658
- })
659
- .addConnection({
660
- startLocation: WonderlandTrailLocations.pyramidCreek,
661
- endLocation: WonderlandTrailLocations.devilsDream,
662
- distanceMiles: 2.5,
663
- ascentFeet: 1400,
664
- descentFeet: 100,
665
- })
666
- .addConnection({
667
- startLocation: WonderlandTrailLocations.devilsDream,
668
- endLocation: WonderlandTrailLocations.southPuyallupRiver,
669
- distanceMiles: 6.5,
670
- ascentFeet: 1900,
671
- descentFeet: 2700,
672
- })
673
- .addConnection({
674
- startLocation: WonderlandTrailLocations.southPuyallupRiver,
675
- endLocation: WonderlandTrailLocations.klapatchePark,
676
- distanceMiles: 4.1,
677
- ascentFeet: 2100,
678
- descentFeet: 800,
679
- })
680
- .addConnection({
681
- startLocation: WonderlandTrailLocations.klapatchePark,
682
- endLocation: WonderlandTrailLocations.northPuyallupRiver,
683
- distanceMiles: 2.6,
684
- ascentFeet: 100,
685
- descentFeet: 1900,
686
- })
687
- .addConnection({
688
- startLocation: WonderlandTrailLocations.northPuyallupRiver,
689
- endLocation: WonderlandTrailLocations.goldenLakes,
690
- distanceMiles: 5.1,
691
- ascentFeet: 1900,
692
- descentFeet: 600,
693
- })
694
- .addConnection({
695
- startLocation: WonderlandTrailLocations.goldenLakes,
696
- endLocation: WonderlandTrailLocations.southMowichRiver,
697
- distanceMiles: 5.8,
698
- ascentFeet: 200,
699
- descentFeet: 2400,
700
- })
701
- .addConnection({
702
- startLocation: WonderlandTrailLocations.southMowichRiver,
703
- endLocation: WonderlandTrailLocations.mowichLake,
704
- distanceMiles: 4.4,
705
- ascentFeet: 2400,
706
- descentFeet: 300,
707
- })
708
- .addConnection({
709
- startLocation: WonderlandTrailLocations.mowichLake,
710
- endLocation: WonderlandTrailLocations.ipsutCreek,
711
- distanceMiles: 5.6,
712
- ascentFeet: 400,
713
- descentFeet: 3000,
714
- })
715
- .addConnection({
716
- startLocation: WonderlandTrailLocations.ipsutCreek,
717
- endLocation: WonderlandTrailLocations.carbonRiver,
718
- distanceMiles: 4,
719
- ascentFeet: 1300,
720
- descentFeet: 400,
721
- })
722
- .addConnection({
723
- startLocation: WonderlandTrailLocations.mowichLake,
724
- endLocation: WonderlandTrailLocations.eaglesRoost,
725
- distanceMiles: 2,
726
- ascentFeet: 500,
727
- descentFeet: 600,
728
- })
729
- .addConnection({
730
- startLocation: WonderlandTrailLocations.eaglesRoost,
731
- endLocation: WonderlandTrailLocations.cataractValley,
732
- distanceMiles: 12.4,
733
- ascentFeet: 3300,
734
- descentFeet: 3700,
735
- })
736
- .addConnection({
737
- startLocation: WonderlandTrailLocations.cataractValley,
738
- endLocation: WonderlandTrailLocations.carbonRiver,
739
- distanceMiles: 1.6,
740
- ascentFeet: 100,
741
- descentFeet: 1300,
742
- })
743
- .addConnection({
744
- startLocation: WonderlandTrailLocations.carbonRiver,
745
- endLocation: WonderlandTrailLocations.dickCreek,
746
- distanceMiles: 1.3,
747
- ascentFeet: 1000,
748
- descentFeet: 100,
749
- })
750
- .addConnection({
751
- startLocation: WonderlandTrailLocations.dickCreek,
752
- endLocation: WonderlandTrailLocations.mysticLake,
753
- distanceMiles: 3.7,
754
- ascentFeet: 2100,
755
- descentFeet: 600,
756
- })
757
- .addConnection({
758
- startLocation: WonderlandTrailLocations.mysticLake,
759
- endLocation: WonderlandTrailLocations.graniteCreek,
760
- distanceMiles: 4.1,
761
- ascentFeet: 1500,
762
- descentFeet: 1200,
763
- })
764
- .addConnection({
765
- startLocation: WonderlandTrailLocations.graniteCreek,
766
- endLocation: WonderlandTrailLocations.sunriseCamp,
767
- distanceMiles: 4.6,
768
- ascentFeet: 1400,
769
- descentFeet: 1000,
770
- })
771
- .addConnection({
772
- startLocation: WonderlandTrailLocations.sunriseCamp,
773
- endLocation: WonderlandTrailLocations.whiteRiver,
774
- distanceMiles: 3.5,
775
- ascentFeet: 100,
776
- descentFeet: 2100,
777
- })
778
- .addConnection({
779
- startLocation: WonderlandTrailLocations.sunriseCamp,
780
- endLocation: WonderlandTrailLocations.sunriseVisitorCenter,
781
- distanceMiles: 1.4,
782
- ascentFeet: 400,
783
- descentFeet: 200,
784
- })
785
- .addConnection({
786
- startLocation: WonderlandTrailLocations.sunriseVisitorCenter,
787
- endLocation: WonderlandTrailLocations.whiteRiver,
788
- distanceMiles: 3.1,
789
- ascentFeet: 0,
790
- descentFeet: 2200,
791
- });
792
- }
793
-
794
- public iterateConnections(startLocation?: WonderlandTrailLocation, endLocation?: WonderlandTrailLocation, direction?: WonderlandTrailDirection): Iterator<WonderlandTrailConnection>;
795
- public iterateConnections(properties: { startLocation?: WonderlandTrailLocation, endLocation?: WonderlandTrailLocation, direction?: WonderlandTrailDirection }): Iterator<WonderlandTrailConnection>;
796
- iterateConnections(startLocationOrProperties?: WonderlandTrailLocation | { startLocation?: WonderlandTrailLocation, endLocation?: WonderlandTrailLocation, direction?: WonderlandTrailDirection }, endLocation?: WonderlandTrailLocation, direction?: WonderlandTrailDirection): Iterator<WonderlandTrailConnection>
797
- {
798
- let startLocation: WonderlandTrailLocation | undefined;
799
- if (isWonderlandTrailLocation(startLocationOrProperties))
800
- {
801
- startLocation = startLocationOrProperties;
802
- }
803
- else if (startLocationOrProperties)
804
- {
805
- startLocation = startLocationOrProperties.startLocation;
806
- endLocation = startLocationOrProperties.endLocation;
807
- direction = startLocationOrProperties.direction;
808
- }
809
-
810
- let result: Iterator<WonderlandTrailConnection>;
811
- if (!startLocation)
812
- {
813
- result = this.connections.iterateValues().flatMap(x => x);
814
- }
815
- else
816
- {
817
- result = this.connections.get(startLocation)
818
- .catch(NotFoundError, () => Iterable.create<WonderlandTrailConnection>())
819
- .await()
820
- .iterate();
821
- }
822
-
823
- if (endLocation)
824
- {
825
- result = result.where(connection => connection.endLocation == endLocation);
826
- }
827
-
828
- if (direction != null)
829
- {
830
- result = result.where(connection => connection.direction == direction);
831
- }
832
-
833
- return result;
834
- }
835
-
836
- private addConnectionInner(connection: WonderlandTrailConnection): void
837
- {
838
- const startLocationConnections: List<WonderlandTrailConnection> = this.connections.getOrSet(connection.startLocation, () => List.create()).await();
839
- startLocationConnections.add(connection);
840
- }
841
-
842
- public addConnection(connection: WonderlandTrailConnection, addReverseConnection?: boolean): this;
843
- public addConnection(startLocation: WonderlandTrailLocation, endLocation: WonderlandTrailLocation, distanceMiles: number, ascentFeet: number, descentFeet: number, direction?: WonderlandTrailDirection, intermediateLocations?: Iterable<WonderlandTrailLocation>, addReverseConnection?: boolean): this
844
- public addConnection(properties: { startLocation: WonderlandTrailLocation, endLocation: WonderlandTrailLocation, distanceMiles: number, ascentFeet: number, descentFeet: number, direction?: WonderlandTrailDirection, intermediateLocations?: Iterable<WonderlandTrailLocation>, addReverseConnection?: boolean }): this
845
- addConnection(connectionStartLocationOrProperties: WonderlandTrailLocation | { startLocation: WonderlandTrailLocation, endLocation: WonderlandTrailLocation, distanceMiles: number, ascentFeet: number, descentFeet: number, direction?: WonderlandTrailDirection, intermediateLocations?: Iterable<WonderlandTrailLocation>, addReverseConnection?: boolean }, addReverseConnectionOrEndLocation?: boolean | WonderlandTrailLocation, distanceMiles?: number, ascentFeet?: number, descentFeet?: number, direction?: WonderlandTrailDirection, intermediateLocations?: Iterable<WonderlandTrailLocation>, addReverseConnection?: boolean): this
846
- {
847
- let startLocation: WonderlandTrailLocation;
848
- let endLocation: WonderlandTrailLocation;
849
- if (isWonderlandTrailLocation(connectionStartLocationOrProperties))
850
- {
851
- startLocation = connectionStartLocationOrProperties;
852
- }
853
- else if (connectionStartLocationOrProperties instanceof WonderlandTrailConnection)
854
- {
855
- startLocation = connectionStartLocationOrProperties.startLocation;
856
- endLocation = connectionStartLocationOrProperties.endLocation;
857
- distanceMiles = connectionStartLocationOrProperties.distanceMiles;
858
- ascentFeet = connectionStartLocationOrProperties.ascentFeet;
859
- descentFeet = connectionStartLocationOrProperties.descentFeet;
860
- direction = connectionStartLocationOrProperties.direction;
861
- intermediateLocations = connectionStartLocationOrProperties.intermediateLocations;
862
- addReverseConnection = !!addReverseConnectionOrEndLocation;
863
- }
864
- else
865
- {
866
- startLocation = connectionStartLocationOrProperties.startLocation;
867
- endLocation = connectionStartLocationOrProperties.endLocation;
868
- distanceMiles = connectionStartLocationOrProperties.distanceMiles;
869
- ascentFeet = connectionStartLocationOrProperties.ascentFeet;
870
- descentFeet = connectionStartLocationOrProperties.descentFeet;
871
- direction = connectionStartLocationOrProperties.direction;
872
- intermediateLocations = connectionStartLocationOrProperties.intermediateLocations;
873
- addReverseConnection = connectionStartLocationOrProperties.addReverseConnection;
874
- }
875
-
876
- const connection: WonderlandTrailConnection = WonderlandTrailConnection.create(
877
- startLocation,
878
- endLocation!,
879
- distanceMiles!,
880
- ascentFeet!,
881
- descentFeet!,
882
- direction ?? WonderlandTrailDirection.clockwise,
883
- intermediateLocations ?? Iterable.create(),
884
- );
885
-
886
- this.addConnectionInner(connection);
887
- if (addReverseConnection ?? this.addReverseConnectionDefault)
888
- {
889
- this.addConnectionInner(connection.reverseDirection());
890
- }
891
-
892
- return this;
893
- }
894
-
895
- public containsConnection(connection: WonderlandTrailConnection): boolean
896
- {
897
- PreCondition.assertNotUndefinedAndNotNull(connection, "connection");
898
-
899
- const startLocationConnections: Iterable<WonderlandTrailConnection> | undefined = this.connections.get(connection.startLocation).catch(() => undefined).await();
900
- return startLocationConnections?.contains(connection)?.await() === true;
901
- }
902
-
903
- public reverseDirection(): WonderlandTrailConnections
904
- {
905
- const result: WonderlandTrailConnections = WonderlandTrailConnections.create(this.addReverseConnectionDefault);
906
- for (const connection of this.iterateConnections())
907
- {
908
- result.addConnection(connection.reverseDirection());
909
- }
910
- return result;
911
- }
912
-
913
- public expandConnections(startLocation?: WonderlandTrailLocation, endLocation?: WonderlandTrailLocation, direction?: WonderlandTrailDirection): WonderlandTrailConnections
914
- {
915
- if (isUndefinedOrNull(direction))
916
- {
917
- direction = WonderlandTrailDirection.clockwise;
918
- }
919
-
920
- const result: WonderlandTrailConnections = WonderlandTrailConnections.create(this.addReverseConnectionDefault);
921
-
922
- const toVisit: ListStack<WonderlandTrailConnection> = Stack.create();
923
- toVisit.addAll(this.iterateConnections(startLocation, undefined, direction));
924
-
925
- while (toVisit.any().await())
926
- {
927
- const currentConnection: WonderlandTrailConnection = toVisit.remove().await();
928
- result.addConnection(currentConnection, false);
929
-
930
- if (!currentConnection.isLoop() && currentConnection.endLocation !== endLocation)
931
- {
932
- for (const endLocationConnection of this.iterateConnections(currentConnection.endLocation, undefined, direction))
933
- {
934
- if (!result.containsConnection(endLocationConnection) &&
935
- !toVisit.contains(endLocationConnection))
936
- {
937
- toVisit.add(endLocationConnection);
938
- }
939
-
940
- if (!currentConnection.intermediateLocations.contains(endLocationConnection.endLocation))
941
- {
942
- toVisit.add(currentConnection.join(endLocationConnection));
943
- }
944
- }
945
- }
946
- }
947
-
948
- return result;
949
- }
950
- }
951
-
952
- export class WonderlandTrailItinerary
953
- {
954
- public readonly startDay: DateTime;
955
- private readonly connections: List<WonderlandTrailConnection>;
956
- private readonly availabilityTypes: List<WonderlandTrailAvailabilityType>;
957
-
958
- private constructor(startDay: DateTime)
959
- {
960
- PreCondition.assertNotUndefinedAndNotNull(startDay, "startDay");
961
-
962
- this.startDay = startDay;
963
- this.connections = List.create();
964
- this.availabilityTypes = List.create();
965
- }
966
-
967
- public static create(startDay: DateTime): WonderlandTrailItinerary
968
- {
969
- return new WonderlandTrailItinerary(startDay);
970
- }
971
-
972
- public clone(): WonderlandTrailItinerary
973
- {
974
- return WonderlandTrailItinerary.create(this.startDay)
975
- .addConnections(this.connections)
976
- .addAvailabilityTypes(this.availabilityTypes);
977
- }
978
-
979
- public getConnections(): Iterable<WonderlandTrailConnection>
980
- {
981
- return this.connections;
982
- }
983
-
984
- public getEndDay(): DateTime
985
- {
986
- return this.startDay.addDays(this.getDayCount() - 1);
987
- }
988
-
989
- public getDayCount(): number
990
- {
991
- return this.connections.getCount().await();
992
- }
993
-
994
- public getStartLocation(): SyncResult<WonderlandTrailLocation>
995
- {
996
- return this.connections.first().then(firstConnection => firstConnection.startLocation);
997
- }
998
-
999
- public getIntermediateLocations(): Iterable<WonderlandTrailLocation>
1000
- {
1001
- const result: List<WonderlandTrailLocation> = List.create();
1002
- for (const connection of this.connections)
1003
- {
1004
- if (result.any().await())
1005
- {
1006
- result.add(connection.startLocation);
1007
- }
1008
- result.addAll(connection.intermediateLocations);
1009
- }
1010
- return result;
1011
- }
1012
-
1013
- public getEndLocation(): SyncResult<WonderlandTrailLocation>
1014
- {
1015
- return this.connections.last().then(lastConnection => lastConnection.endLocation);
1016
- }
1017
-
1018
- public getPath(): Iterable<WonderlandTrailLocation>
1019
- {
1020
- const result: List<WonderlandTrailLocation> = List.create();
1021
- for (const connection of this.connections)
1022
- {
1023
- if (!result.any().await())
1024
- {
1025
- result.add(connection.startLocation);
1026
- }
1027
- result.add(connection.endLocation);
1028
- }
1029
- return result;
1030
- }
1031
-
1032
- public getPathStrings(includeAvailabilityTypes?: boolean): Iterable<string>
1033
- {
1034
- includeAvailabilityTypes = includeAvailabilityTypes ?? true;
1035
-
1036
- const result: List<string> = List.create();
1037
- let availabilityTypeIndex: number = 0;
1038
- const availabilityTypeCount: number = this.availabilityTypes.getCount().await();
1039
-
1040
- for (const connection of this.connections)
1041
- {
1042
- if (!result.any().await())
1043
- {
1044
- result.add(connection.startLocation.name);
1045
- }
1046
-
1047
- let pathString: string = connection.endLocation.name;
1048
- if (includeAvailabilityTypes && availabilityTypeIndex < availabilityTypeCount)
1049
- {
1050
- pathString += " ";
1051
-
1052
- const availabilityType: WonderlandTrailAvailabilityType = this.availabilityTypes.get(availabilityTypeIndex).await();
1053
- availabilityTypeIndex++;
1054
-
1055
- if (availabilityType.individualSite !== undefined)
1056
- {
1057
- if (availabilityType.groupSite !== undefined)
1058
- {
1059
- pathString += `(Individual ${availabilityType.individualSite}/Group ${availabilityType.groupSite})`;
1060
- }
1061
- else
1062
- {
1063
- pathString += `(Individual ${availabilityType.individualSite})`;
1064
- }
1065
- }
1066
- else
1067
- {
1068
- pathString += `(Group ${availabilityType.groupSite})`;
1069
- }
1070
- }
1071
-
1072
- result.add(pathString);
1073
- }
1074
-
1075
- return result;
1076
- }
1077
-
1078
- public contains(parameters: {
1079
- location: WonderlandTrailLocation,
1080
- checkItineraryStartLocation?: boolean,
1081
- checkItineraryIntermediateLocations?: boolean,
1082
- checkItineraryEndLocation?: boolean,
1083
- }): boolean
1084
- {
1085
- PreCondition.assertNotUndefinedAndNotNull(parameters, "parameters");
1086
-
1087
- const location: WonderlandTrailLocation = parameters.location;
1088
- const checkItineraryStartLocation: boolean = parameters.checkItineraryStartLocation ?? true;
1089
- const checkItineraryIntermediateLocations: boolean = parameters.checkItineraryIntermediateLocations ?? true;
1090
- const checkItineraryEndLocation: boolean = parameters.checkItineraryEndLocation ?? true;
1091
-
1092
- PreCondition.assertNotUndefinedAndNotNull(location, "location");
1093
-
1094
- let result: boolean = false;
1095
-
1096
- const connectionCount: number = this.connections.getCount().await();
1097
- for (let i = 0; i < connectionCount; i++)
1098
- {
1099
- if (i !== 0 || checkItineraryStartLocation)
1100
- {
1101
- result = (this.connections.get(i).await().startLocation === location);
1102
- if (result)
1103
- {
1104
- break;
1105
- }
1106
- }
1107
-
1108
- if (checkItineraryIntermediateLocations)
1109
- {
1110
- result = this.connections.get(i).await().intermediateLocations.contains(location).await();
1111
- if (result)
1112
- {
1113
- break;
1114
- }
1115
- }
1116
-
1117
- if (i === connectionCount - 1 && checkItineraryEndLocation)
1118
- {
1119
- result = (this.connections.get(i).await().endLocation === location);
1120
- if (result)
1121
- {
1122
- break;
1123
- }
1124
- }
1125
- }
1126
-
1127
- return result;
1128
- }
1129
-
1130
- public containsAny(parameters: {
1131
- connection: WonderlandTrailConnection,
1132
- checkConnectionStartLocation?: boolean,
1133
- checkConnectionIntermediateLocations?: boolean,
1134
- checkConnectionEndLocation?: boolean,
1135
- checkItineraryStartLocation?: boolean,
1136
- checkItineraryIntermediateLocations?: boolean,
1137
- checkItineraryEndLocation?: boolean,
1138
- }): boolean
1139
- {
1140
- PreCondition.assertNotUndefinedAndNotNull(parameters, "parameters");
1141
-
1142
- const connection: WonderlandTrailConnection = parameters.connection;
1143
- const checkConnectionStartLocation: boolean = parameters.checkConnectionStartLocation ?? true;
1144
- const checkConnectionIntermediateLocations: boolean = parameters.checkConnectionIntermediateLocations ?? true;
1145
- const checkConnectionEndLocation: boolean = parameters.checkConnectionEndLocation ?? true;
1146
- const checkItineraryStartLocation: boolean = parameters.checkItineraryStartLocation ?? true;
1147
- const checkItineraryIntermediateLocations: boolean = parameters.checkItineraryIntermediateLocations ?? true;
1148
- const checkItineraryEndLocation: boolean = parameters.checkItineraryEndLocation ?? true;
1149
-
1150
- PreCondition.assertNotUndefinedAndNotNull(connection, "connection");
1151
-
1152
- let result: boolean = false;
1153
-
1154
- if (!result && checkConnectionStartLocation)
1155
- {
1156
- result = this.contains({
1157
- location: connection.startLocation,
1158
- checkItineraryStartLocation,
1159
- checkItineraryIntermediateLocations,
1160
- checkItineraryEndLocation,
1161
- });
1162
- }
1163
-
1164
- if (!result && checkConnectionIntermediateLocations)
1165
- {
1166
- for (const connectionIntermediateLocation of connection.intermediateLocations)
1167
- {
1168
- result = this.contains({
1169
- location: connectionIntermediateLocation,
1170
- checkItineraryStartLocation,
1171
- checkItineraryIntermediateLocations,
1172
- checkItineraryEndLocation,
1173
- });
1174
- if (result)
1175
- {
1176
- break;
1177
- }
1178
- }
1179
- }
1180
-
1181
- if (!result && checkConnectionEndLocation)
1182
- {
1183
- result = this.contains({
1184
- location: connection.endLocation,
1185
- checkItineraryStartLocation,
1186
- checkItineraryIntermediateLocations,
1187
- checkItineraryEndLocation,
1188
- });
1189
- }
1190
-
1191
- return result;
1192
- }
1193
-
1194
- public addConnection(connection: WonderlandTrailConnection): this
1195
- {
1196
- PreCondition.assertNotUndefinedAndNotNull(connection, "connection");
1197
-
1198
- this.connections.add(connection);
1199
-
1200
- return this;
1201
- }
1202
-
1203
- public addConnections(connections: JavascriptIterable<WonderlandTrailConnection>): this
1204
- {
1205
- PreCondition.assertNotUndefinedAndNotNull(connections, "connections");
1206
-
1207
- this.connections.addAll(connections);
1208
-
1209
- return this;
1210
- }
1211
-
1212
- public addAvailabilityType(availabilityType: WonderlandTrailAvailabilityType): this
1213
- {
1214
- PreCondition.assertNotUndefinedAndNotNull(availabilityType, "availabilityType");
1215
-
1216
- this.availabilityTypes.add(availabilityType);
1217
-
1218
- return this;
1219
- }
1220
-
1221
- public addAvailabilityTypes(availabilityTypes: JavascriptIterable<WonderlandTrailAvailabilityType>): this
1222
- {
1223
- PreCondition.assertNotUndefinedAndNotNull(availabilityTypes, "availabilityTypes");
1224
-
1225
- this.availabilityTypes.addAll(availabilityTypes);
1226
-
1227
- return this;
1228
- }
1229
-
1230
- public toString(includeAvailabilityTypes?: boolean): string
1231
- {
1232
- includeAvailabilityTypes = includeAvailabilityTypes ?? true;
1233
- return `startDay:${this.startDay.toDateString()},path:${this.getPathStrings(includeAvailabilityTypes)}`;
1234
- }
1235
- }
1236
-
1237
- export class WonderlandTrailClient implements HttpClient
1238
- {
1239
- public static readonly permitItineraryId: string = "4675317";
1240
-
1241
- private readonly httpClient: HttpClient;
1242
- private readonly recreationDotGovClient: RecreationDotGovClient;
1243
-
1244
- private constructor(httpClient: HttpClient)
1245
- {
1246
- PreCondition.assertNotUndefinedAndNotNull(httpClient, "httpClient");
1247
-
1248
- this.httpClient = httpClient;
1249
- this.recreationDotGovClient = RecreationDotGovClient.create(httpClient);
1250
- }
1251
-
1252
- public static create(httpClient: HttpClient): WonderlandTrailClient
1253
- {
1254
- return new WonderlandTrailClient(httpClient);
1255
- }
1256
-
1257
- public sendRequest(request: HttpOutgoingRequest): AsyncResult<HttpIncomingResponse>
1258
- {
1259
- return this.httpClient.sendRequest(request);
1260
- }
1261
-
1262
- public sendGetRequest(url: string): AsyncResult<HttpIncomingResponse>
1263
- {
1264
- return HttpClient.sendGetRequest(this, url);
1265
- }
1266
-
1267
- public getAvailability(month: number, year: number, allowWalkupPermits: boolean, allowIndividualSites: boolean, allowGroupSites: boolean, earlyAccessPermitLotteryId?: string): PromiseAsyncResult<WonderlandTrailAvailability>;
1268
- public getAvailability(options: { month: number, year: number, allowWalkupPermits: boolean, allowIndividualSites: boolean, allowGroupSites: boolean, earlyAccessPermitLotteryId?: string }): PromiseAsyncResult<WonderlandTrailAvailability>;
1269
- getAvailability(monthOrOptions: number | { month: number, year: number, allowWalkupPermits: boolean, allowIndividualSites: boolean, allowGroupSites: boolean, earlyAccessPermitLotteryId?: string }, year?: number, allowWalkupPermits?: boolean, allowIndividualSites?: boolean, allowGroupSites?: boolean, earlyAccessPermitLotteryId?: string): PromiseAsyncResult<WonderlandTrailAvailability>
1270
- {
1271
- let month: number;
1272
- if (isNumber(monthOrOptions))
1273
- {
1274
- month = monthOrOptions;
1275
- year = year!;
1276
- allowWalkupPermits = allowWalkupPermits!;
1277
- allowIndividualSites = allowIndividualSites!;
1278
- allowGroupSites = allowGroupSites!;
1279
- }
1280
- else
1281
- {
1282
- month = monthOrOptions.month;
1283
- year = monthOrOptions.year;
1284
- allowWalkupPermits = monthOrOptions.allowWalkupPermits;
1285
- allowIndividualSites = monthOrOptions.allowIndividualSites;
1286
- allowGroupSites = monthOrOptions.allowGroupSites;
1287
- earlyAccessPermitLotteryId = monthOrOptions.earlyAccessPermitLotteryId;
1288
- }
1289
- return PromiseAsyncResult.create(async () =>
1290
- {
1291
- const result = WonderlandTrailAvailability.create();
1292
-
1293
- for (const location of WonderlandTrailLocations.getLocations())
1294
- {
1295
- if (allowIndividualSites && location.divisionId)
1296
- {
1297
- const divisionAvailability: RecreationDotGovDivisionAvailability = await this.recreationDotGovClient.getDivisionAvailability(
1298
- WonderlandTrailClient.permitItineraryId,
1299
- location.divisionId,
1300
- month,
1301
- year,
1302
- earlyAccessPermitLotteryId,
1303
- );
1304
- const divisionDayAvailabilities: Iterable<RecreationDotGovDivisionDayAvailability> = divisionAvailability.dayAvailabilities;
1305
- if (divisionDayAvailabilities)
1306
- {
1307
- for (const divisionDayAvailability of divisionDayAvailabilities)
1308
- {
1309
- const hasWalkupPermits: boolean = (allowWalkupPermits && divisionDayAvailability.walkup);
1310
- const hasReservationPermits: boolean = (divisionDayAvailability.reservationsRemaining > 0);
1311
- if (hasWalkupPermits || hasReservationPermits)
1312
- {
1313
- result.addAvailability(
1314
- location,
1315
- divisionDayAvailability.date,
1316
- hasWalkupPermits ? WonderlandTrailReservationType.Walkup : WonderlandTrailReservationType.Reserved,
1317
- undefined,
1318
- );
1319
- }
1320
- }
1321
- }
1322
- }
1323
-
1324
- if (allowGroupSites && location.groupSiteDivisionId)
1325
- {
1326
- const groupSiteDivisionAvailability: RecreationDotGovDivisionAvailability = await this.recreationDotGovClient.getDivisionAvailability(
1327
- WonderlandTrailClient.permitItineraryId,
1328
- location.groupSiteDivisionId,
1329
- month,
1330
- year,
1331
- earlyAccessPermitLotteryId,
1332
- );
1333
- const groupSiteDivisionDayAvailabilities: Iterable<RecreationDotGovDivisionDayAvailability> = groupSiteDivisionAvailability.dayAvailabilities;
1334
- if (groupSiteDivisionDayAvailabilities)
1335
- {
1336
- for (const groupSiteDayAvailability of groupSiteDivisionDayAvailabilities)
1337
- {
1338
- const hasWalkupPermits: boolean = (allowWalkupPermits && groupSiteDayAvailability.walkup);
1339
- const hasReservationPermits: boolean = (groupSiteDayAvailability.reservationsRemaining > 0);
1340
- if (hasWalkupPermits || hasReservationPermits)
1341
- {
1342
- result.addAvailability(
1343
- location,
1344
- groupSiteDayAvailability.date,
1345
- undefined,
1346
- hasWalkupPermits ? WonderlandTrailReservationType.Walkup : WonderlandTrailReservationType.Reserved,
1347
- );
1348
- }
1349
- }
1350
- }
1351
- }
1352
- }
1353
-
1354
- return result;
1355
- });
1356
- }
1357
-
1358
- public findItineraries(parameters: {
1359
- availability: WonderlandTrailAvailability,
1360
- startDate: DateTime,
1361
- startLocation: WonderlandTrailLocation,
1362
- endLocation: WonderlandTrailLocation,
1363
- direction: WonderlandTrailDirection,
1364
- maximumDayDistanceMiles?: number,
1365
- maximumItineraryDays?: number,
1366
- campsitesToAvoid?: JavascriptIterable<WonderlandTrailLocation>,
1367
- }): Iterable<WonderlandTrailItinerary>
1368
- {
1369
- PreCondition.assertNotUndefinedAndNotNull(parameters, "parameters");
1370
-
1371
- const availability: WonderlandTrailAvailability = parameters.availability;
1372
- const startDate: DateTime = parameters.startDate;
1373
- const startLocation: WonderlandTrailLocation = parameters.startLocation;
1374
- const endLocation: WonderlandTrailLocation = parameters.endLocation;
1375
- const direction: WonderlandTrailDirection = parameters.direction;
1376
- const maximumDayDistanceMiles: number | undefined = parameters.maximumDayDistanceMiles;
1377
- const maximumItineraryDays: number | undefined = parameters.maximumItineraryDays;
1378
- const campsitesToAvoid: Iterable<WonderlandTrailLocation> = Iterable.create(parameters.campsitesToAvoid ?? []);
1379
-
1380
- const result: List<WonderlandTrailItinerary> = List.create();
1381
-
1382
- const connections: WonderlandTrailConnections = WonderlandTrailConnections.createDefault()
1383
- .expandConnections(startLocation, endLocation, direction);
1384
-
1385
- let startLocationConnections: Iterator<WonderlandTrailConnection> = connections.iterateConnections(startLocation, undefined, direction);
1386
- if (!isUndefinedOrNull(maximumDayDistanceMiles))
1387
- {
1388
- startLocationConnections = startLocationConnections.where(connection => connection.distanceMiles <= maximumDayDistanceMiles);
1389
- }
1390
-
1391
- const possibleItineraries: ListStack<WonderlandTrailItinerary> = Stack.create();
1392
- possibleItineraries.addAll(startLocationConnections.map(c => WonderlandTrailItinerary.create(startDate).addConnection(c)));
1393
-
1394
- while (possibleItineraries.any().await())
1395
- {
1396
- const currentItinerary: WonderlandTrailItinerary = possibleItineraries.remove().await();
1397
- const currentItineraryEndLocation: WonderlandTrailLocation = currentItinerary.getEndLocation().await();
1398
-
1399
- if ((maximumItineraryDays === undefined || currentItinerary.getDayCount() <= maximumItineraryDays) &&
1400
- (startLocation === currentItineraryEndLocation || endLocation === currentItineraryEndLocation || !campsitesToAvoid.contains(currentItineraryEndLocation).await()))
1401
- {
1402
- if (currentItineraryEndLocation === endLocation)
1403
- {
1404
- result.add(currentItinerary);
1405
- }
1406
- else
1407
- {
1408
- const dayAvailability: WonderlandTrailAvailabilityType | undefined = availability.getDayAvailability(currentItineraryEndLocation, currentItinerary.getEndDay())
1409
- .catch(() => undefined)
1410
- .await();
1411
- if (dayAvailability)
1412
- {
1413
- currentItinerary.addAvailabilityType(dayAvailability);
1414
-
1415
- for (const nextDayConnection of connections.iterateConnections(currentItineraryEndLocation, undefined, direction))
1416
- {
1417
- if (!currentItinerary.containsAny({
1418
- connection: nextDayConnection,
1419
- checkConnectionStartLocation: false,
1420
- checkItineraryStartLocation: false,
1421
- checkItineraryEndLocation: false,
1422
- }))
1423
- {
1424
- if (maximumDayDistanceMiles === undefined || nextDayConnection.distanceMiles <= maximumDayDistanceMiles)
1425
- {
1426
- possibleItineraries.add(currentItinerary.clone().addConnection(nextDayConnection));
1427
- }
1428
- }
1429
- }
1430
- }
1431
- }
1432
- }
1433
- }
1434
-
1435
- return result;
1436
- }
1437
-
1438
- public findItinerariesAsync(parameters: {
1439
- startDay: DateTime,
1440
- startLocation?: WonderlandTrailLocation,
1441
- endLocation?: WonderlandTrailLocation,
1442
- direction?: WonderlandTrailDirection,
1443
- maximumDayDistanceMiles?: number,
1444
- maximumItineraryDays?: number,
1445
- allowWalkupPermits?: boolean,
1446
- allowIndividualSites?: boolean,
1447
- allowGroupSites?: boolean,
1448
- campsitesToAvoid?: JavascriptIterable<WonderlandTrailLocation>,
1449
- }): PromiseAsyncResult<Iterable<WonderlandTrailItinerary>>
1450
- {
1451
- PreCondition.assertNotUndefinedAndNotNull(parameters, "parameters");
1452
-
1453
- const startDay: DateTime = parameters.startDay;
1454
- const startLocation: WonderlandTrailLocation | undefined = parameters.startLocation;
1455
- const endLocation: WonderlandTrailLocation | undefined = parameters.endLocation;
1456
- const direction: WonderlandTrailDirection | undefined = parameters.direction;
1457
- const maximumDayDistanceMiles: number | undefined = parameters.maximumDayDistanceMiles;
1458
- const maximumItineraryDays: number | undefined = parameters.maximumItineraryDays;
1459
- const allowWalkupPermits: boolean = parameters.allowWalkupPermits ?? true;
1460
- const allowIndividualSites: boolean = parameters.allowIndividualSites ?? true;
1461
- const allowGroupSites: boolean = parameters.allowGroupSites ?? false;
1462
- const campsitesToAvoid: JavascriptIterable<WonderlandTrailLocation> = parameters.campsitesToAvoid ?? [];
1463
-
1464
- PreCondition.assertNotUndefinedAndNotNull(startDay, "startDay");
1465
-
1466
- return PromiseAsyncResult.create(async () =>
1467
- {
1468
- const result: List<WonderlandTrailItinerary> = List.create();
1469
-
1470
- const startLocationsToCheck: Iterable<WonderlandTrailLocation> =
1471
- (!isUndefinedOrNull(startLocation) ? Iterable.create([startLocation]) : WonderlandTrailLocations.getTrailheads());
1472
- const directionsToCheck: Iterable<WonderlandTrailDirection> =
1473
- (!isUndefinedOrNull(direction) ? Iterable.create([direction]) : Iterable.create([WonderlandTrailDirection.clockwise, WonderlandTrailDirection.counterClockwise]));
1474
-
1475
- const availability: WonderlandTrailAvailability = await this.getAvailability(
1476
- startDay.getMonth(),
1477
- startDay.getYear(),
1478
- allowWalkupPermits,
1479
- allowIndividualSites,
1480
- allowGroupSites,
1481
- );
1482
-
1483
- for (const startLocationToCheck of startLocationsToCheck)
1484
- {
1485
- for (const directionToCheck of directionsToCheck)
1486
- {
1487
- result.addAll(this.findItineraries({
1488
- availability,
1489
- startDate: startDay,
1490
- startLocation: startLocationToCheck,
1491
- endLocation: endLocation ?? startLocationToCheck,
1492
- direction: directionToCheck,
1493
- maximumDayDistanceMiles,
1494
- maximumItineraryDays,
1495
- campsitesToAvoid,
1496
- }))
1497
- }
1498
- }
1499
-
1500
- return result;
1501
- });
1502
- }
1
+ import { DateTime } from "./dateTime";
2
+ import { HttpClient } from "./httpClient";
3
+ import { HttpIncomingResponse } from "./httpIncomingResponse";
4
+ import { HttpOutgoingRequest } from "./httpOutgoingRequest";
5
+ import { Iterable } from "./iterable";
6
+ import { PreCondition } from "./preCondition";
7
+ import { RecreationDotGovClient, RecreationDotGovDivisionAvailability, RecreationDotGovDivisionDayAvailability } from "./recreationDotGovClient";
8
+ import { AsyncResult } from "./asyncResult";
9
+ import { Map } from "./map";
10
+ import { List } from "./list";
11
+ import { JavascriptIterable } from "./javascript";
12
+ import { hasProperty, isNumber, isObject, isUndefinedOrNull } from "./types";
13
+ import { MutableMap } from "./mutableMap";
14
+ import { NotFoundError } from "./notFoundError";
15
+ import { Iterator } from "./iterator";
16
+ import { Stack } from "./stack";
17
+ import { ListStack } from "./listStack";
18
+ import { PromiseAsyncResult } from "./promiseAsyncResult";
19
+ import { SyncResult } from "./syncResult";
20
+
21
+ export interface WonderlandTrailLocation
22
+ {
23
+ readonly name: string;
24
+ readonly trailhead: boolean;
25
+ readonly foodCacheStorage: boolean;
26
+ readonly divisionId: string;
27
+ readonly groupSiteDivisionId: string;
28
+ readonly latitude: number;
29
+ readonly longitude: number;
30
+ readonly elevationFeet: number;
31
+ }
32
+
33
+ export function isWonderlandTrailLocation(value: unknown): value is WonderlandTrailLocation
34
+ {
35
+ return isObject(value) &&
36
+ hasProperty(value, "name") &&
37
+ hasProperty(value, "trailhead") &&
38
+ hasProperty(value, "foodCacheStorage") &&
39
+ hasProperty(value, "divisionId") &&
40
+ hasProperty(value, "groupSiteDivisionId");
41
+ }
42
+
43
+ export abstract class WonderlandTrailLocations
44
+ {
45
+ public static readonly graniteCreek: WonderlandTrailLocation = {
46
+ name: "Granite Creek",
47
+ trailhead: false,
48
+ foodCacheStorage: false,
49
+ divisionId: "46753170009",
50
+ groupSiteDivisionId: "46753170010",
51
+ latitude: 46.92058,
52
+ longitude: -121.70800,
53
+ elevationFeet: 5851,
54
+ };
55
+
56
+ public static readonly sunriseCamp: WonderlandTrailLocation = {
57
+ name: "Sunrise Camp",
58
+ trailhead: false,
59
+ foodCacheStorage: false,
60
+ divisionId: "46753170058",
61
+ groupSiteDivisionId: "46753170059",
62
+ latitude: 46.91129,
63
+ longitude: -121.66002,
64
+ elevationFeet: 6258,
65
+ };
66
+
67
+ public static readonly sunriseVisitorCenter: WonderlandTrailLocation = {
68
+ name: "Sunrise Visitor Center",
69
+ trailhead: true,
70
+ foodCacheStorage: true,
71
+ divisionId: "",
72
+ groupSiteDivisionId: "",
73
+ latitude: 46.91448,
74
+ longitude: -121.64337,
75
+ elevationFeet: 6410,
76
+ };
77
+
78
+ public static readonly whiteRiver: WonderlandTrailLocation = {
79
+ name: "White River",
80
+ trailhead: true,
81
+ foodCacheStorage: true,
82
+ divisionId: "46753170066",
83
+ groupSiteDivisionId: "46753170067",
84
+ latitude: 46.90255,
85
+ longitude: -121.63869,
86
+ elevationFeet: 4229,
87
+ };
88
+
89
+ public static readonly fryingPanCreek: WonderlandTrailLocation = {
90
+ name: "Fryingpan Creek",
91
+ trailhead: true,
92
+ foodCacheStorage: false,
93
+ divisionId: "",
94
+ groupSiteDivisionId: "",
95
+ latitude: 46.88809,
96
+ longitude: -121.61019,
97
+ elevationFeet: 3835,
98
+ };
99
+
100
+ public static readonly summerland: WonderlandTrailLocation = {
101
+ name: "Summerland",
102
+ trailhead: false,
103
+ foodCacheStorage: false,
104
+ divisionId: "46753170056",
105
+ groupSiteDivisionId: "46753170057",
106
+ latitude: 46.86616,
107
+ longitude: -121.65843,
108
+ elevationFeet: 5988,
109
+ };
110
+
111
+ public static readonly indianBar: WonderlandTrailLocation = {
112
+ name: "Indian Bar",
113
+ trailhead: false,
114
+ foodCacheStorage: false,
115
+ divisionId: "46753170046",
116
+ groupSiteDivisionId: "46753170047",
117
+ latitude: 46.82593,
118
+ longitude: -121.63942,
119
+ elevationFeet: 5101,
120
+ };
121
+
122
+ public static readonly nickelCreek: WonderlandTrailLocation = {
123
+ name: "Nickel Creek",
124
+ trailhead: false,
125
+ foodCacheStorage: false,
126
+ divisionId: "46753170051",
127
+ groupSiteDivisionId: "46753170052",
128
+ latitude: 46.77204,
129
+ longitude: -121.62402,
130
+ elevationFeet: 3383,
131
+ };
132
+
133
+ public static readonly boxCanyon: WonderlandTrailLocation = {
134
+ name: "Box Canyon",
135
+ trailhead: true,
136
+ foodCacheStorage: false,
137
+ divisionId: "",
138
+ groupSiteDivisionId: "",
139
+ latitude: 46.76570,
140
+ longitude: -121.63517,
141
+ elevationFeet: 3025,
142
+ };
143
+
144
+ public static readonly mapleCreek: WonderlandTrailLocation = {
145
+ name: "Maple Creek",
146
+ trailhead: false,
147
+ foodCacheStorage: false,
148
+ divisionId: "46753170027",
149
+ groupSiteDivisionId: "46753170028",
150
+ latitude: 46.75745,
151
+ longitude: -121.65764,
152
+ elevationFeet: 2806,
153
+ };
154
+
155
+ public static readonly reflectionLakes: WonderlandTrailLocation = {
156
+ name: "Reflection Lakes",
157
+ trailhead: true,
158
+ foodCacheStorage: false,
159
+ divisionId: "",
160
+ groupSiteDivisionId: "",
161
+ latitude: 46.76824,
162
+ longitude: -121.72890,
163
+ elevationFeet: 4862,
164
+ };
165
+
166
+ public static readonly paradiseRiver: WonderlandTrailLocation = {
167
+ name: "Paradise River",
168
+ trailhead: false,
169
+ foodCacheStorage: false,
170
+ divisionId: "46753170031",
171
+ groupSiteDivisionId: "46753170032",
172
+ latitude: 46.77076,
173
+ longitude: -121.75909,
174
+ elevationFeet: 3960,
175
+ };
176
+
177
+ public static readonly longmire: WonderlandTrailLocation = {
178
+ name: "Longmire",
179
+ trailhead: true,
180
+ foodCacheStorage: true,
181
+ divisionId: "",
182
+ groupSiteDivisionId: "",
183
+ latitude: 46.75011,
184
+ longitude: -121.81253,
185
+ elevationFeet: 2750,
186
+ };
187
+
188
+ public static readonly pyramidCreek: WonderlandTrailLocation = {
189
+ name: "Pyramid Creek",
190
+ trailhead: false,
191
+ foodCacheStorage: false,
192
+ divisionId: "46753170033",
193
+ groupSiteDivisionId: "",
194
+ latitude: 46.77832,
195
+ longitude: -121.80963,
196
+ elevationFeet: 3721,
197
+ };
198
+
199
+ public static readonly devilsDream: WonderlandTrailLocation = {
200
+ name: "Devil's Dream",
201
+ trailhead: false,
202
+ foodCacheStorage: false,
203
+ divisionId: "46753170040",
204
+ groupSiteDivisionId: "46753170041",
205
+ latitude: 46.78196,
206
+ longitude: -121.83297,
207
+ elevationFeet: 4929,
208
+ };
209
+
210
+ public static readonly southPuyallupRiver: WonderlandTrailLocation = {
211
+ name: "South Puyallup River",
212
+ trailhead: false,
213
+ foodCacheStorage: false,
214
+ divisionId: "46753170035",
215
+ groupSiteDivisionId: "46753170036",
216
+ latitude: 46.81331,
217
+ longitude: -121.86445,
218
+ elevationFeet: 4183,
219
+ };
220
+
221
+ public static readonly klapatchePark: WonderlandTrailLocation = {
222
+ name: "Klapatche Park",
223
+ trailhead: false,
224
+ foodCacheStorage: false,
225
+ divisionId: "46753170024",
226
+ groupSiteDivisionId: "",
227
+ latitude: 46.83571,
228
+ longitude: -121.87752,
229
+ elevationFeet: 5496,
230
+ };
231
+
232
+ public static readonly northPuyallupRiver: WonderlandTrailLocation = {
233
+ name: "North Puyallup River",
234
+ trailhead: false,
235
+ foodCacheStorage: false,
236
+ divisionId: "46753170029",
237
+ groupSiteDivisionId: "46753170030",
238
+ latitude: 46.84751,
239
+ longitude: -121.87005,
240
+ elevationFeet: 3733,
241
+ };
242
+
243
+ public static readonly goldenLakes: WonderlandTrailLocation = {
244
+ name: "Golden Lakes",
245
+ trailhead: false,
246
+ foodCacheStorage: false,
247
+ divisionId: "46753170022",
248
+ groupSiteDivisionId: "46753170023",
249
+ latitude: 46.88327,
250
+ longitude: -121.89919,
251
+ elevationFeet: 4927,
252
+ };
253
+
254
+ public static readonly southMowichRiver: WonderlandTrailLocation = {
255
+ name: "South Mowich River",
256
+ trailhead: false,
257
+ foodCacheStorage: false,
258
+ divisionId: "46753170019",
259
+ groupSiteDivisionId: "46753170020",
260
+ latitude: 46.91146,
261
+ longitude: -121.89314,
262
+ elevationFeet: 2686,
263
+ };
264
+
265
+ public static readonly mowichLake: WonderlandTrailLocation = {
266
+ name: "Mowich Lake",
267
+ trailhead: false,
268
+ foodCacheStorage: false,
269
+ divisionId: "46753170015",
270
+ groupSiteDivisionId: "46753170016",
271
+ latitude: 46.93204,
272
+ longitude: -121.86351,
273
+ elevationFeet: 4868,
274
+ };
275
+
276
+ public static readonly eaglesRoost: WonderlandTrailLocation = {
277
+ name: "Eagle's Roost",
278
+ trailhead: false,
279
+ foodCacheStorage: false,
280
+ divisionId: "46753170006",
281
+ groupSiteDivisionId: "",
282
+ latitude: 46.91529,
283
+ longitude: -121.84816,
284
+ elevationFeet: 4834,
285
+ };
286
+
287
+ public static readonly cataractValley: WonderlandTrailLocation = {
288
+ name: "Cataract Valley",
289
+ trailhead: false,
290
+ foodCacheStorage: false,
291
+ divisionId: "46753170003",
292
+ groupSiteDivisionId: "46753170004",
293
+ latitude: 46.94049,
294
+ longitude: -121.80486,
295
+ elevationFeet: 4488,
296
+ };
297
+
298
+ public static readonly ipsutCreek: WonderlandTrailLocation = {
299
+ name: "Ipsut Creek",
300
+ trailhead: false,
301
+ foodCacheStorage: false,
302
+ divisionId: "46753170011",
303
+ groupSiteDivisionId: "46753170012",
304
+ latitude: 46.97615,
305
+ longitude: -121.83022,
306
+ elevationFeet: 2359,
307
+ };
308
+
309
+ public static readonly carbonRiver: WonderlandTrailLocation = {
310
+ name: "Carbon River",
311
+ trailhead: false,
312
+ foodCacheStorage: false,
313
+ divisionId: "46753170001",
314
+ groupSiteDivisionId: "46753170002",
315
+ latitude: 46.95063,
316
+ longitude: -121.79991,
317
+ elevationFeet: 3255,
318
+ };
319
+
320
+ public static readonly dickCreek: WonderlandTrailLocation = {
321
+ name: "Dick Creek",
322
+ trailhead: false,
323
+ foodCacheStorage: false,
324
+ divisionId: "46753170005",
325
+ groupSiteDivisionId: "",
326
+ latitude: 46.94071,
327
+ longitude: -121.78431,
328
+ elevationFeet: 4114,
329
+ };
330
+
331
+ public static readonly mysticLake: WonderlandTrailLocation = {
332
+ name: "Mystic Lake",
333
+ trailhead: false,
334
+ foodCacheStorage: false,
335
+ divisionId: "46753170017",
336
+ groupSiteDivisionId: "46753170018",
337
+ latitude: 46.91570,
338
+ longitude: -121.75045,
339
+ elevationFeet: 5538,
340
+ };
341
+
342
+ public static getLocations(): Iterable<WonderlandTrailLocation>
343
+ {
344
+ return Iterable.create([
345
+ this.graniteCreek,
346
+ this.sunriseCamp,
347
+ this.sunriseVisitorCenter,
348
+ this.whiteRiver,
349
+ this.fryingPanCreek,
350
+ this.summerland,
351
+ this.indianBar,
352
+ this.nickelCreek,
353
+ this.boxCanyon,
354
+ this.mapleCreek,
355
+ this.reflectionLakes,
356
+ this.paradiseRiver,
357
+ this.longmire,
358
+ this.pyramidCreek,
359
+ this.devilsDream,
360
+ this.southPuyallupRiver,
361
+ this.klapatchePark,
362
+ this.northPuyallupRiver,
363
+ this.goldenLakes,
364
+ this.southMowichRiver,
365
+ this.mowichLake,
366
+ this.eaglesRoost,
367
+ this.cataractValley,
368
+ this.ipsutCreek,
369
+ this.carbonRiver,
370
+ this.dickCreek,
371
+ this.mysticLake,
372
+ ]);
373
+ }
374
+
375
+ public static getTrailheads(): Iterable<WonderlandTrailLocation>
376
+ {
377
+ return this.getLocations().where(location => location.trailhead);
378
+ }
379
+ }
380
+
381
+ export enum WonderlandTrailReservationType
382
+ {
383
+ Reserved,
384
+ Walkup,
385
+ }
386
+
387
+ export interface WonderlandTrailAvailabilityType
388
+ {
389
+ readonly individualSite?: WonderlandTrailReservationType;
390
+ readonly groupSite?: WonderlandTrailReservationType;
391
+ }
392
+
393
+ export class WonderlandTrailAvailability
394
+ {
395
+ private readonly availabilityMap: MutableMap<WonderlandTrailLocation, MutableMap<string, WonderlandTrailAvailabilityType>>;
396
+
397
+ private constructor()
398
+ {
399
+ this.availabilityMap = Map.create();
400
+ }
401
+
402
+ public static create(): WonderlandTrailAvailability
403
+ {
404
+ return new WonderlandTrailAvailability();
405
+ }
406
+
407
+ public any(): boolean
408
+ {
409
+ return this.availabilityMap.any().await();
410
+ }
411
+
412
+ public addAvailability(location: WonderlandTrailLocation, date: DateTime, individualSite?: WonderlandTrailReservationType, groupSite?: WonderlandTrailReservationType): void
413
+ {
414
+ PreCondition.assertNotUndefinedAndNotNull(location, "location");
415
+ PreCondition.assertNotUndefinedAndNotNull(date, "date");
416
+
417
+ const locationAvailability: MutableMap<string, WonderlandTrailAvailabilityType> | undefined = this.getAvailability(location);
418
+
419
+ const dateString: string = date.toDateString();
420
+ const locationDayAvailability: WonderlandTrailAvailabilityType = locationAvailability.getOrSet(dateString, () => { return {}; }).await();
421
+ locationAvailability.set(dateString, {
422
+ individualSite: individualSite ?? locationDayAvailability.individualSite,
423
+ groupSite: groupSite ?? locationDayAvailability.groupSite,
424
+ });
425
+ }
426
+
427
+ public getAvailability(location: WonderlandTrailLocation): MutableMap<string, WonderlandTrailAvailabilityType>
428
+ {
429
+ PreCondition.assertNotUndefinedAndNotNull(location, "location");
430
+
431
+ return this.availabilityMap.getOrSet(location, () => MutableMap.create()).await();
432
+ }
433
+
434
+ public getDayAvailability(location: WonderlandTrailLocation, date: DateTime): SyncResult<WonderlandTrailAvailabilityType>
435
+ {
436
+ PreCondition.assertNotUndefinedAndNotNull(location, "location");
437
+ PreCondition.assertNotUndefinedAndNotNull(date, "date");
438
+
439
+ return this.getAvailability(location).get(date.toDateString())
440
+ .convertError(NotFoundError, () => new NotFoundError(`No availability was found for ${location.name} on ${date.toDateString()}.`));
441
+ }
442
+ }
443
+
444
+ export class WonderlandTrailDirection
445
+ {
446
+ private readonly value: string;
447
+
448
+ private constructor(value: string)
449
+ {
450
+ PreCondition.assertNotEmpty(value, "value");
451
+
452
+ this.value = value;
453
+ }
454
+
455
+ public static readonly clockwise = new WonderlandTrailDirection("Clockwise");
456
+ public static readonly counterClockwise = new WonderlandTrailDirection("CounterClockwise");
457
+
458
+ public toString(): string
459
+ {
460
+ return this.value;
461
+ }
462
+
463
+ public reverse(): WonderlandTrailDirection
464
+ {
465
+ return this === WonderlandTrailDirection.clockwise ? WonderlandTrailDirection.counterClockwise : WonderlandTrailDirection.clockwise;
466
+ }
467
+ }
468
+
469
+ export class WonderlandTrailConnection
470
+ {
471
+ public readonly startLocation: WonderlandTrailLocation;
472
+ public readonly endLocation: WonderlandTrailLocation;
473
+ public readonly distanceMiles: number;
474
+ public readonly ascentFeet: number;
475
+ public readonly descentFeet: number;
476
+ public readonly direction: WonderlandTrailDirection;
477
+ public readonly intermediateLocations: Iterable<WonderlandTrailLocation>;
478
+
479
+ private constructor(startLocation: WonderlandTrailLocation, endLocation: WonderlandTrailLocation, distanceMiles: number, ascentFeet: number, descentFeet: number, direction: WonderlandTrailDirection, intermediateLocations: Iterable<WonderlandTrailLocation>)
480
+ {
481
+ PreCondition.assertNotUndefinedAndNotNull(startLocation, "startLocation");
482
+ PreCondition.assertNotUndefinedAndNotNull(endLocation, "endLocation");
483
+ PreCondition.assertGreaterThanOrEqualTo(distanceMiles, 0, "distanceMiles");
484
+ PreCondition.assertGreaterThanOrEqualTo(ascentFeet, 0, "ascentFeet");
485
+ PreCondition.assertGreaterThanOrEqualTo(descentFeet, 0, "descentFeet");
486
+ PreCondition.assertNotUndefinedAndNotNull(intermediateLocations, "intermediateLocations");
487
+
488
+ this.startLocation = startLocation;
489
+ this.endLocation = endLocation;
490
+ this.distanceMiles = distanceMiles;
491
+ this.ascentFeet = ascentFeet;
492
+ this.descentFeet = descentFeet;
493
+ this.direction = direction;
494
+ this.intermediateLocations = intermediateLocations;
495
+ }
496
+
497
+ public static create(startLocation: WonderlandTrailLocation, endLocation: WonderlandTrailLocation, distanceMiles: number, ascentFeet: number, descentFeet: number, direction: WonderlandTrailDirection, intermediateLocations: Iterable<WonderlandTrailLocation>): WonderlandTrailConnection
498
+ {
499
+ return new WonderlandTrailConnection(startLocation, endLocation, distanceMiles, ascentFeet, descentFeet, direction, intermediateLocations);
500
+ }
501
+
502
+ public getLocations(): Iterable<WonderlandTrailLocation>
503
+ {
504
+ const result: List<WonderlandTrailLocation> = List.create();
505
+ WonderlandTrailConnection.ensureExists(result, this.startLocation);
506
+ WonderlandTrailConnection.ensureAllExist(result, this.intermediateLocations);
507
+ WonderlandTrailConnection.ensureExists(result, this.endLocation);
508
+ return result;
509
+ }
510
+
511
+ private static ensureExists(list: List<WonderlandTrailLocation>, location: WonderlandTrailLocation): void
512
+ {
513
+ if (!list.contains(location).await())
514
+ {
515
+ list.add(location);
516
+ }
517
+ }
518
+
519
+ private static ensureAllExist(list: List<WonderlandTrailLocation>, locations: JavascriptIterable<WonderlandTrailLocation>): void
520
+ {
521
+ for (const location of locations)
522
+ {
523
+ WonderlandTrailConnection.ensureExists(list, location);
524
+ }
525
+ }
526
+
527
+ public reverseDirection(): WonderlandTrailConnection
528
+ {
529
+ return WonderlandTrailConnection.create(
530
+ this.endLocation,
531
+ this.startLocation,
532
+ this.distanceMiles,
533
+ this.descentFeet,
534
+ this.ascentFeet,
535
+ this.direction.reverse(),
536
+ this.intermediateLocations,
537
+ )
538
+ }
539
+
540
+ public join(connection: WonderlandTrailConnection): WonderlandTrailConnection
541
+ {
542
+ const intermediateLocations: List<WonderlandTrailLocation> = List.create();
543
+ WonderlandTrailConnection.ensureAllExist(intermediateLocations, this.intermediateLocations);
544
+ WonderlandTrailConnection.ensureExists(intermediateLocations, this.endLocation);
545
+ WonderlandTrailConnection.ensureExists(intermediateLocations, connection.startLocation);
546
+ WonderlandTrailConnection.ensureAllExist(intermediateLocations, connection.intermediateLocations);
547
+
548
+ return WonderlandTrailConnection.create(
549
+ this.startLocation,
550
+ connection.endLocation,
551
+ this.distanceMiles + connection.distanceMiles,
552
+ this.ascentFeet + connection.ascentFeet,
553
+ this.descentFeet + connection.descentFeet,
554
+ this.direction,
555
+ intermediateLocations,
556
+ );
557
+ }
558
+
559
+ public containsLocation(location: WonderlandTrailLocation): boolean
560
+ {
561
+ return this.getLocations().contains(location).await();
562
+ }
563
+
564
+ public isLoop(): boolean
565
+ {
566
+ return this.startLocation === this.endLocation;
567
+ }
568
+ }
569
+
570
+ export class WonderlandTrailConnections
571
+ {
572
+ private connections: MutableMap<WonderlandTrailLocation, List<WonderlandTrailConnection>>;
573
+ public readonly addReverseConnectionDefault: boolean;
574
+
575
+ private constructor(addReverseConnectionDefault: boolean)
576
+ {
577
+ this.connections = Map.create();
578
+ this.addReverseConnectionDefault = addReverseConnectionDefault;
579
+ }
580
+
581
+ public static create(addReverseConnectionDefault?: boolean): WonderlandTrailConnections
582
+ {
583
+ return new WonderlandTrailConnections(!!addReverseConnectionDefault);
584
+ }
585
+
586
+ public static createDefault(): WonderlandTrailConnections
587
+ {
588
+ return WonderlandTrailConnections.create(true)
589
+ .addConnection({
590
+ startLocation: WonderlandTrailLocations.whiteRiver,
591
+ endLocation: WonderlandTrailLocations.fryingPanCreek,
592
+ distanceMiles: 2.7,
593
+ ascentFeet: 100,
594
+ descentFeet: 600,
595
+ })
596
+ .addConnection({
597
+ startLocation: WonderlandTrailLocations.fryingPanCreek,
598
+ endLocation: WonderlandTrailLocations.summerland,
599
+ distanceMiles: 4.3,
600
+ ascentFeet: 2200,
601
+ descentFeet: 100,
602
+ })
603
+ .addConnection({
604
+ startLocation: WonderlandTrailLocations.summerland,
605
+ endLocation: WonderlandTrailLocations.indianBar,
606
+ distanceMiles: 4.7,
607
+ ascentFeet: 1200,
608
+ descentFeet: 2100,
609
+ })
610
+ .addConnection({
611
+ startLocation: WonderlandTrailLocations.indianBar,
612
+ endLocation: WonderlandTrailLocations.nickelCreek,
613
+ distanceMiles: 6.8,
614
+ ascentFeet: 1400,
615
+ descentFeet: 3200,
616
+ })
617
+ .addConnection({
618
+ startLocation: WonderlandTrailLocations.nickelCreek,
619
+ endLocation: WonderlandTrailLocations.boxCanyon,
620
+ distanceMiles: 0.9,
621
+ ascentFeet: 100,
622
+ descentFeet: 400,
623
+ })
624
+ .addConnection({
625
+ startLocation: WonderlandTrailLocations.boxCanyon,
626
+ endLocation: WonderlandTrailLocations.mapleCreek,
627
+ distanceMiles: 2.7,
628
+ ascentFeet: 500,
629
+ descentFeet: 700,
630
+ })
631
+ .addConnection({
632
+ startLocation: WonderlandTrailLocations.mapleCreek,
633
+ endLocation: WonderlandTrailLocations.reflectionLakes,
634
+ distanceMiles: 4.7,
635
+ ascentFeet: 2300,
636
+ descentFeet: 200,
637
+ })
638
+ .addConnection({
639
+ startLocation: WonderlandTrailLocations.reflectionLakes,
640
+ endLocation: WonderlandTrailLocations.paradiseRiver,
641
+ distanceMiles: 2.6,
642
+ ascentFeet: 200,
643
+ descentFeet: 1200,
644
+ })
645
+ .addConnection({
646
+ startLocation: WonderlandTrailLocations.paradiseRiver,
647
+ endLocation: WonderlandTrailLocations.longmire,
648
+ distanceMiles: 3.6,
649
+ ascentFeet: 100,
650
+ descentFeet: 1200,
651
+ })
652
+ .addConnection({
653
+ startLocation: WonderlandTrailLocations.longmire,
654
+ endLocation: WonderlandTrailLocations.pyramidCreek,
655
+ distanceMiles: 3.3,
656
+ ascentFeet: 1400,
657
+ descentFeet: 400,
658
+ })
659
+ .addConnection({
660
+ startLocation: WonderlandTrailLocations.pyramidCreek,
661
+ endLocation: WonderlandTrailLocations.devilsDream,
662
+ distanceMiles: 2.5,
663
+ ascentFeet: 1400,
664
+ descentFeet: 100,
665
+ })
666
+ .addConnection({
667
+ startLocation: WonderlandTrailLocations.devilsDream,
668
+ endLocation: WonderlandTrailLocations.southPuyallupRiver,
669
+ distanceMiles: 6.5,
670
+ ascentFeet: 1900,
671
+ descentFeet: 2700,
672
+ })
673
+ .addConnection({
674
+ startLocation: WonderlandTrailLocations.southPuyallupRiver,
675
+ endLocation: WonderlandTrailLocations.klapatchePark,
676
+ distanceMiles: 4.1,
677
+ ascentFeet: 2100,
678
+ descentFeet: 800,
679
+ })
680
+ .addConnection({
681
+ startLocation: WonderlandTrailLocations.klapatchePark,
682
+ endLocation: WonderlandTrailLocations.northPuyallupRiver,
683
+ distanceMiles: 2.6,
684
+ ascentFeet: 100,
685
+ descentFeet: 1900,
686
+ })
687
+ .addConnection({
688
+ startLocation: WonderlandTrailLocations.northPuyallupRiver,
689
+ endLocation: WonderlandTrailLocations.goldenLakes,
690
+ distanceMiles: 5.1,
691
+ ascentFeet: 1900,
692
+ descentFeet: 600,
693
+ })
694
+ .addConnection({
695
+ startLocation: WonderlandTrailLocations.goldenLakes,
696
+ endLocation: WonderlandTrailLocations.southMowichRiver,
697
+ distanceMiles: 5.8,
698
+ ascentFeet: 200,
699
+ descentFeet: 2400,
700
+ })
701
+ .addConnection({
702
+ startLocation: WonderlandTrailLocations.southMowichRiver,
703
+ endLocation: WonderlandTrailLocations.mowichLake,
704
+ distanceMiles: 4.4,
705
+ ascentFeet: 2400,
706
+ descentFeet: 300,
707
+ })
708
+ .addConnection({
709
+ startLocation: WonderlandTrailLocations.mowichLake,
710
+ endLocation: WonderlandTrailLocations.ipsutCreek,
711
+ distanceMiles: 5.6,
712
+ ascentFeet: 400,
713
+ descentFeet: 3000,
714
+ })
715
+ .addConnection({
716
+ startLocation: WonderlandTrailLocations.ipsutCreek,
717
+ endLocation: WonderlandTrailLocations.carbonRiver,
718
+ distanceMiles: 4,
719
+ ascentFeet: 1300,
720
+ descentFeet: 400,
721
+ })
722
+ .addConnection({
723
+ startLocation: WonderlandTrailLocations.mowichLake,
724
+ endLocation: WonderlandTrailLocations.eaglesRoost,
725
+ distanceMiles: 2,
726
+ ascentFeet: 500,
727
+ descentFeet: 600,
728
+ })
729
+ .addConnection({
730
+ startLocation: WonderlandTrailLocations.eaglesRoost,
731
+ endLocation: WonderlandTrailLocations.cataractValley,
732
+ distanceMiles: 12.4,
733
+ ascentFeet: 3300,
734
+ descentFeet: 3700,
735
+ })
736
+ .addConnection({
737
+ startLocation: WonderlandTrailLocations.cataractValley,
738
+ endLocation: WonderlandTrailLocations.carbonRiver,
739
+ distanceMiles: 1.6,
740
+ ascentFeet: 100,
741
+ descentFeet: 1300,
742
+ })
743
+ .addConnection({
744
+ startLocation: WonderlandTrailLocations.carbonRiver,
745
+ endLocation: WonderlandTrailLocations.dickCreek,
746
+ distanceMiles: 1.3,
747
+ ascentFeet: 1000,
748
+ descentFeet: 100,
749
+ })
750
+ .addConnection({
751
+ startLocation: WonderlandTrailLocations.dickCreek,
752
+ endLocation: WonderlandTrailLocations.mysticLake,
753
+ distanceMiles: 3.7,
754
+ ascentFeet: 2100,
755
+ descentFeet: 600,
756
+ })
757
+ .addConnection({
758
+ startLocation: WonderlandTrailLocations.mysticLake,
759
+ endLocation: WonderlandTrailLocations.graniteCreek,
760
+ distanceMiles: 4.1,
761
+ ascentFeet: 1500,
762
+ descentFeet: 1200,
763
+ })
764
+ .addConnection({
765
+ startLocation: WonderlandTrailLocations.graniteCreek,
766
+ endLocation: WonderlandTrailLocations.sunriseCamp,
767
+ distanceMiles: 4.6,
768
+ ascentFeet: 1400,
769
+ descentFeet: 1000,
770
+ })
771
+ .addConnection({
772
+ startLocation: WonderlandTrailLocations.sunriseCamp,
773
+ endLocation: WonderlandTrailLocations.whiteRiver,
774
+ distanceMiles: 3.5,
775
+ ascentFeet: 100,
776
+ descentFeet: 2100,
777
+ })
778
+ .addConnection({
779
+ startLocation: WonderlandTrailLocations.sunriseCamp,
780
+ endLocation: WonderlandTrailLocations.sunriseVisitorCenter,
781
+ distanceMiles: 1.4,
782
+ ascentFeet: 400,
783
+ descentFeet: 200,
784
+ })
785
+ .addConnection({
786
+ startLocation: WonderlandTrailLocations.sunriseVisitorCenter,
787
+ endLocation: WonderlandTrailLocations.whiteRiver,
788
+ distanceMiles: 3.1,
789
+ ascentFeet: 0,
790
+ descentFeet: 2200,
791
+ });
792
+ }
793
+
794
+ public iterateConnections(startLocation?: WonderlandTrailLocation, endLocation?: WonderlandTrailLocation, direction?: WonderlandTrailDirection): Iterator<WonderlandTrailConnection>;
795
+ public iterateConnections(properties: { startLocation?: WonderlandTrailLocation, endLocation?: WonderlandTrailLocation, direction?: WonderlandTrailDirection }): Iterator<WonderlandTrailConnection>;
796
+ iterateConnections(startLocationOrProperties?: WonderlandTrailLocation | { startLocation?: WonderlandTrailLocation, endLocation?: WonderlandTrailLocation, direction?: WonderlandTrailDirection }, endLocation?: WonderlandTrailLocation, direction?: WonderlandTrailDirection): Iterator<WonderlandTrailConnection>
797
+ {
798
+ let startLocation: WonderlandTrailLocation | undefined;
799
+ if (isWonderlandTrailLocation(startLocationOrProperties))
800
+ {
801
+ startLocation = startLocationOrProperties;
802
+ }
803
+ else if (startLocationOrProperties)
804
+ {
805
+ startLocation = startLocationOrProperties.startLocation;
806
+ endLocation = startLocationOrProperties.endLocation;
807
+ direction = startLocationOrProperties.direction;
808
+ }
809
+
810
+ let result: Iterator<WonderlandTrailConnection>;
811
+ if (!startLocation)
812
+ {
813
+ result = this.connections.iterateValues().flatMap(x => x);
814
+ }
815
+ else
816
+ {
817
+ result = this.connections.get(startLocation)
818
+ .catch(NotFoundError, () => Iterable.create<WonderlandTrailConnection>())
819
+ .await()
820
+ .iterate();
821
+ }
822
+
823
+ if (endLocation)
824
+ {
825
+ result = result.where(connection => connection.endLocation == endLocation);
826
+ }
827
+
828
+ if (direction != null)
829
+ {
830
+ result = result.where(connection => connection.direction == direction);
831
+ }
832
+
833
+ return result;
834
+ }
835
+
836
+ private addConnectionInner(connection: WonderlandTrailConnection): void
837
+ {
838
+ const startLocationConnections: List<WonderlandTrailConnection> = this.connections.getOrSet(connection.startLocation, () => List.create()).await();
839
+ startLocationConnections.add(connection);
840
+ }
841
+
842
+ public addConnection(connection: WonderlandTrailConnection, addReverseConnection?: boolean): this;
843
+ public addConnection(startLocation: WonderlandTrailLocation, endLocation: WonderlandTrailLocation, distanceMiles: number, ascentFeet: number, descentFeet: number, direction?: WonderlandTrailDirection, intermediateLocations?: Iterable<WonderlandTrailLocation>, addReverseConnection?: boolean): this
844
+ public addConnection(properties: { startLocation: WonderlandTrailLocation, endLocation: WonderlandTrailLocation, distanceMiles: number, ascentFeet: number, descentFeet: number, direction?: WonderlandTrailDirection, intermediateLocations?: Iterable<WonderlandTrailLocation>, addReverseConnection?: boolean }): this
845
+ addConnection(connectionStartLocationOrProperties: WonderlandTrailLocation | { startLocation: WonderlandTrailLocation, endLocation: WonderlandTrailLocation, distanceMiles: number, ascentFeet: number, descentFeet: number, direction?: WonderlandTrailDirection, intermediateLocations?: Iterable<WonderlandTrailLocation>, addReverseConnection?: boolean }, addReverseConnectionOrEndLocation?: boolean | WonderlandTrailLocation, distanceMiles?: number, ascentFeet?: number, descentFeet?: number, direction?: WonderlandTrailDirection, intermediateLocations?: Iterable<WonderlandTrailLocation>, addReverseConnection?: boolean): this
846
+ {
847
+ let startLocation: WonderlandTrailLocation;
848
+ let endLocation: WonderlandTrailLocation;
849
+ if (isWonderlandTrailLocation(connectionStartLocationOrProperties))
850
+ {
851
+ startLocation = connectionStartLocationOrProperties;
852
+ }
853
+ else if (connectionStartLocationOrProperties instanceof WonderlandTrailConnection)
854
+ {
855
+ startLocation = connectionStartLocationOrProperties.startLocation;
856
+ endLocation = connectionStartLocationOrProperties.endLocation;
857
+ distanceMiles = connectionStartLocationOrProperties.distanceMiles;
858
+ ascentFeet = connectionStartLocationOrProperties.ascentFeet;
859
+ descentFeet = connectionStartLocationOrProperties.descentFeet;
860
+ direction = connectionStartLocationOrProperties.direction;
861
+ intermediateLocations = connectionStartLocationOrProperties.intermediateLocations;
862
+ addReverseConnection = !!addReverseConnectionOrEndLocation;
863
+ }
864
+ else
865
+ {
866
+ startLocation = connectionStartLocationOrProperties.startLocation;
867
+ endLocation = connectionStartLocationOrProperties.endLocation;
868
+ distanceMiles = connectionStartLocationOrProperties.distanceMiles;
869
+ ascentFeet = connectionStartLocationOrProperties.ascentFeet;
870
+ descentFeet = connectionStartLocationOrProperties.descentFeet;
871
+ direction = connectionStartLocationOrProperties.direction;
872
+ intermediateLocations = connectionStartLocationOrProperties.intermediateLocations;
873
+ addReverseConnection = connectionStartLocationOrProperties.addReverseConnection;
874
+ }
875
+
876
+ const connection: WonderlandTrailConnection = WonderlandTrailConnection.create(
877
+ startLocation,
878
+ endLocation!,
879
+ distanceMiles!,
880
+ ascentFeet!,
881
+ descentFeet!,
882
+ direction ?? WonderlandTrailDirection.clockwise,
883
+ intermediateLocations ?? Iterable.create(),
884
+ );
885
+
886
+ this.addConnectionInner(connection);
887
+ if (addReverseConnection ?? this.addReverseConnectionDefault)
888
+ {
889
+ this.addConnectionInner(connection.reverseDirection());
890
+ }
891
+
892
+ return this;
893
+ }
894
+
895
+ public containsConnection(connection: WonderlandTrailConnection): boolean
896
+ {
897
+ PreCondition.assertNotUndefinedAndNotNull(connection, "connection");
898
+
899
+ const startLocationConnections: Iterable<WonderlandTrailConnection> | undefined = this.connections.get(connection.startLocation).catch(() => undefined).await();
900
+ return startLocationConnections?.contains(connection)?.await() === true;
901
+ }
902
+
903
+ public reverseDirection(): WonderlandTrailConnections
904
+ {
905
+ const result: WonderlandTrailConnections = WonderlandTrailConnections.create(this.addReverseConnectionDefault);
906
+ for (const connection of this.iterateConnections())
907
+ {
908
+ result.addConnection(connection.reverseDirection());
909
+ }
910
+ return result;
911
+ }
912
+
913
+ public expandConnections(startLocation?: WonderlandTrailLocation, endLocation?: WonderlandTrailLocation, direction?: WonderlandTrailDirection): WonderlandTrailConnections
914
+ {
915
+ if (isUndefinedOrNull(direction))
916
+ {
917
+ direction = WonderlandTrailDirection.clockwise;
918
+ }
919
+
920
+ const result: WonderlandTrailConnections = WonderlandTrailConnections.create(this.addReverseConnectionDefault);
921
+
922
+ const toVisit: ListStack<WonderlandTrailConnection> = Stack.create();
923
+ toVisit.addAll(this.iterateConnections(startLocation, undefined, direction));
924
+
925
+ while (toVisit.any().await())
926
+ {
927
+ const currentConnection: WonderlandTrailConnection = toVisit.remove().await();
928
+ result.addConnection(currentConnection, false);
929
+
930
+ if (!currentConnection.isLoop() && currentConnection.endLocation !== endLocation)
931
+ {
932
+ for (const endLocationConnection of this.iterateConnections(currentConnection.endLocation, undefined, direction))
933
+ {
934
+ if (!result.containsConnection(endLocationConnection) &&
935
+ !toVisit.contains(endLocationConnection))
936
+ {
937
+ toVisit.add(endLocationConnection);
938
+ }
939
+
940
+ if (!currentConnection.intermediateLocations.contains(endLocationConnection.endLocation))
941
+ {
942
+ toVisit.add(currentConnection.join(endLocationConnection));
943
+ }
944
+ }
945
+ }
946
+ }
947
+
948
+ return result;
949
+ }
950
+ }
951
+
952
+ export class WonderlandTrailItinerary
953
+ {
954
+ public readonly startDay: DateTime;
955
+ private readonly connections: List<WonderlandTrailConnection>;
956
+ private readonly availabilityTypes: List<WonderlandTrailAvailabilityType>;
957
+
958
+ private constructor(startDay: DateTime)
959
+ {
960
+ PreCondition.assertNotUndefinedAndNotNull(startDay, "startDay");
961
+
962
+ this.startDay = startDay;
963
+ this.connections = List.create();
964
+ this.availabilityTypes = List.create();
965
+ }
966
+
967
+ public static create(startDay: DateTime): WonderlandTrailItinerary
968
+ {
969
+ return new WonderlandTrailItinerary(startDay);
970
+ }
971
+
972
+ public clone(): WonderlandTrailItinerary
973
+ {
974
+ return WonderlandTrailItinerary.create(this.startDay)
975
+ .addConnections(this.connections)
976
+ .addAvailabilityTypes(this.availabilityTypes);
977
+ }
978
+
979
+ public getConnections(): Iterable<WonderlandTrailConnection>
980
+ {
981
+ return this.connections;
982
+ }
983
+
984
+ public getEndDay(): DateTime
985
+ {
986
+ return this.startDay.addDays(this.getDayCount() - 1);
987
+ }
988
+
989
+ public getDayCount(): number
990
+ {
991
+ return this.connections.getCount().await();
992
+ }
993
+
994
+ public getStartLocation(): SyncResult<WonderlandTrailLocation>
995
+ {
996
+ return this.connections.first().then(firstConnection => firstConnection.startLocation);
997
+ }
998
+
999
+ public getIntermediateLocations(): Iterable<WonderlandTrailLocation>
1000
+ {
1001
+ const result: List<WonderlandTrailLocation> = List.create();
1002
+ for (const connection of this.connections)
1003
+ {
1004
+ if (result.any().await())
1005
+ {
1006
+ result.add(connection.startLocation);
1007
+ }
1008
+ result.addAll(connection.intermediateLocations);
1009
+ }
1010
+ return result;
1011
+ }
1012
+
1013
+ public getEndLocation(): SyncResult<WonderlandTrailLocation>
1014
+ {
1015
+ return this.connections.last().then(lastConnection => lastConnection.endLocation);
1016
+ }
1017
+
1018
+ public getPath(): Iterable<WonderlandTrailLocation>
1019
+ {
1020
+ const result: List<WonderlandTrailLocation> = List.create();
1021
+ for (const connection of this.connections)
1022
+ {
1023
+ if (!result.any().await())
1024
+ {
1025
+ result.add(connection.startLocation);
1026
+ }
1027
+ result.add(connection.endLocation);
1028
+ }
1029
+ return result;
1030
+ }
1031
+
1032
+ public getPathStrings(includeAvailabilityTypes?: boolean): Iterable<string>
1033
+ {
1034
+ includeAvailabilityTypes = includeAvailabilityTypes ?? true;
1035
+
1036
+ const result: List<string> = List.create();
1037
+ let availabilityTypeIndex: number = 0;
1038
+ const availabilityTypeCount: number = this.availabilityTypes.getCount().await();
1039
+
1040
+ for (const connection of this.connections)
1041
+ {
1042
+ if (!result.any().await())
1043
+ {
1044
+ result.add(connection.startLocation.name);
1045
+ }
1046
+
1047
+ let pathString: string = connection.endLocation.name;
1048
+ if (includeAvailabilityTypes && availabilityTypeIndex < availabilityTypeCount)
1049
+ {
1050
+ pathString += " ";
1051
+
1052
+ const availabilityType: WonderlandTrailAvailabilityType = this.availabilityTypes.get(availabilityTypeIndex).await();
1053
+ availabilityTypeIndex++;
1054
+
1055
+ if (availabilityType.individualSite !== undefined)
1056
+ {
1057
+ if (availabilityType.groupSite !== undefined)
1058
+ {
1059
+ pathString += `(Individual ${availabilityType.individualSite}/Group ${availabilityType.groupSite})`;
1060
+ }
1061
+ else
1062
+ {
1063
+ pathString += `(Individual ${availabilityType.individualSite})`;
1064
+ }
1065
+ }
1066
+ else
1067
+ {
1068
+ pathString += `(Group ${availabilityType.groupSite})`;
1069
+ }
1070
+ }
1071
+
1072
+ result.add(pathString);
1073
+ }
1074
+
1075
+ return result;
1076
+ }
1077
+
1078
+ public contains(parameters: {
1079
+ location: WonderlandTrailLocation,
1080
+ checkItineraryStartLocation?: boolean,
1081
+ checkItineraryIntermediateLocations?: boolean,
1082
+ checkItineraryEndLocation?: boolean,
1083
+ }): boolean
1084
+ {
1085
+ PreCondition.assertNotUndefinedAndNotNull(parameters, "parameters");
1086
+
1087
+ const location: WonderlandTrailLocation = parameters.location;
1088
+ const checkItineraryStartLocation: boolean = parameters.checkItineraryStartLocation ?? true;
1089
+ const checkItineraryIntermediateLocations: boolean = parameters.checkItineraryIntermediateLocations ?? true;
1090
+ const checkItineraryEndLocation: boolean = parameters.checkItineraryEndLocation ?? true;
1091
+
1092
+ PreCondition.assertNotUndefinedAndNotNull(location, "location");
1093
+
1094
+ let result: boolean = false;
1095
+
1096
+ const connectionCount: number = this.connections.getCount().await();
1097
+ for (let i = 0; i < connectionCount; i++)
1098
+ {
1099
+ if (i !== 0 || checkItineraryStartLocation)
1100
+ {
1101
+ result = (this.connections.get(i).await().startLocation === location);
1102
+ if (result)
1103
+ {
1104
+ break;
1105
+ }
1106
+ }
1107
+
1108
+ if (checkItineraryIntermediateLocations)
1109
+ {
1110
+ result = this.connections.get(i).await().intermediateLocations.contains(location).await();
1111
+ if (result)
1112
+ {
1113
+ break;
1114
+ }
1115
+ }
1116
+
1117
+ if (i === connectionCount - 1 && checkItineraryEndLocation)
1118
+ {
1119
+ result = (this.connections.get(i).await().endLocation === location);
1120
+ if (result)
1121
+ {
1122
+ break;
1123
+ }
1124
+ }
1125
+ }
1126
+
1127
+ return result;
1128
+ }
1129
+
1130
+ public containsAny(parameters: {
1131
+ connection: WonderlandTrailConnection,
1132
+ checkConnectionStartLocation?: boolean,
1133
+ checkConnectionIntermediateLocations?: boolean,
1134
+ checkConnectionEndLocation?: boolean,
1135
+ checkItineraryStartLocation?: boolean,
1136
+ checkItineraryIntermediateLocations?: boolean,
1137
+ checkItineraryEndLocation?: boolean,
1138
+ }): boolean
1139
+ {
1140
+ PreCondition.assertNotUndefinedAndNotNull(parameters, "parameters");
1141
+
1142
+ const connection: WonderlandTrailConnection = parameters.connection;
1143
+ const checkConnectionStartLocation: boolean = parameters.checkConnectionStartLocation ?? true;
1144
+ const checkConnectionIntermediateLocations: boolean = parameters.checkConnectionIntermediateLocations ?? true;
1145
+ const checkConnectionEndLocation: boolean = parameters.checkConnectionEndLocation ?? true;
1146
+ const checkItineraryStartLocation: boolean = parameters.checkItineraryStartLocation ?? true;
1147
+ const checkItineraryIntermediateLocations: boolean = parameters.checkItineraryIntermediateLocations ?? true;
1148
+ const checkItineraryEndLocation: boolean = parameters.checkItineraryEndLocation ?? true;
1149
+
1150
+ PreCondition.assertNotUndefinedAndNotNull(connection, "connection");
1151
+
1152
+ let result: boolean = false;
1153
+
1154
+ if (!result && checkConnectionStartLocation)
1155
+ {
1156
+ result = this.contains({
1157
+ location: connection.startLocation,
1158
+ checkItineraryStartLocation,
1159
+ checkItineraryIntermediateLocations,
1160
+ checkItineraryEndLocation,
1161
+ });
1162
+ }
1163
+
1164
+ if (!result && checkConnectionIntermediateLocations)
1165
+ {
1166
+ for (const connectionIntermediateLocation of connection.intermediateLocations)
1167
+ {
1168
+ result = this.contains({
1169
+ location: connectionIntermediateLocation,
1170
+ checkItineraryStartLocation,
1171
+ checkItineraryIntermediateLocations,
1172
+ checkItineraryEndLocation,
1173
+ });
1174
+ if (result)
1175
+ {
1176
+ break;
1177
+ }
1178
+ }
1179
+ }
1180
+
1181
+ if (!result && checkConnectionEndLocation)
1182
+ {
1183
+ result = this.contains({
1184
+ location: connection.endLocation,
1185
+ checkItineraryStartLocation,
1186
+ checkItineraryIntermediateLocations,
1187
+ checkItineraryEndLocation,
1188
+ });
1189
+ }
1190
+
1191
+ return result;
1192
+ }
1193
+
1194
+ public addConnection(connection: WonderlandTrailConnection): this
1195
+ {
1196
+ PreCondition.assertNotUndefinedAndNotNull(connection, "connection");
1197
+
1198
+ this.connections.add(connection);
1199
+
1200
+ return this;
1201
+ }
1202
+
1203
+ public addConnections(connections: JavascriptIterable<WonderlandTrailConnection>): this
1204
+ {
1205
+ PreCondition.assertNotUndefinedAndNotNull(connections, "connections");
1206
+
1207
+ this.connections.addAll(connections);
1208
+
1209
+ return this;
1210
+ }
1211
+
1212
+ public addAvailabilityType(availabilityType: WonderlandTrailAvailabilityType): this
1213
+ {
1214
+ PreCondition.assertNotUndefinedAndNotNull(availabilityType, "availabilityType");
1215
+
1216
+ this.availabilityTypes.add(availabilityType);
1217
+
1218
+ return this;
1219
+ }
1220
+
1221
+ public addAvailabilityTypes(availabilityTypes: JavascriptIterable<WonderlandTrailAvailabilityType>): this
1222
+ {
1223
+ PreCondition.assertNotUndefinedAndNotNull(availabilityTypes, "availabilityTypes");
1224
+
1225
+ this.availabilityTypes.addAll(availabilityTypes);
1226
+
1227
+ return this;
1228
+ }
1229
+
1230
+ public toString(includeAvailabilityTypes?: boolean): string
1231
+ {
1232
+ includeAvailabilityTypes = includeAvailabilityTypes ?? true;
1233
+ return `startDay:${this.startDay.toDateString()},path:${this.getPathStrings(includeAvailabilityTypes)}`;
1234
+ }
1235
+ }
1236
+
1237
+ export class WonderlandTrailClient implements HttpClient
1238
+ {
1239
+ public static readonly permitItineraryId: string = "4675317";
1240
+
1241
+ private readonly httpClient: HttpClient;
1242
+ private readonly recreationDotGovClient: RecreationDotGovClient;
1243
+
1244
+ private constructor(httpClient: HttpClient)
1245
+ {
1246
+ PreCondition.assertNotUndefinedAndNotNull(httpClient, "httpClient");
1247
+
1248
+ this.httpClient = httpClient;
1249
+ this.recreationDotGovClient = RecreationDotGovClient.create(httpClient);
1250
+ }
1251
+
1252
+ public static create(httpClient: HttpClient): WonderlandTrailClient
1253
+ {
1254
+ return new WonderlandTrailClient(httpClient);
1255
+ }
1256
+
1257
+ public sendRequest(request: HttpOutgoingRequest): AsyncResult<HttpIncomingResponse>
1258
+ {
1259
+ return this.httpClient.sendRequest(request);
1260
+ }
1261
+
1262
+ public sendGetRequest(url: string): AsyncResult<HttpIncomingResponse>
1263
+ {
1264
+ return HttpClient.sendGetRequest(this, url);
1265
+ }
1266
+
1267
+ public getAvailability(month: number, year: number, allowWalkupPermits: boolean, allowIndividualSites: boolean, allowGroupSites: boolean, earlyAccessPermitLotteryId?: string): PromiseAsyncResult<WonderlandTrailAvailability>;
1268
+ public getAvailability(options: { month: number, year: number, allowWalkupPermits: boolean, allowIndividualSites: boolean, allowGroupSites: boolean, earlyAccessPermitLotteryId?: string }): PromiseAsyncResult<WonderlandTrailAvailability>;
1269
+ getAvailability(monthOrOptions: number | { month: number, year: number, allowWalkupPermits: boolean, allowIndividualSites: boolean, allowGroupSites: boolean, earlyAccessPermitLotteryId?: string }, year?: number, allowWalkupPermits?: boolean, allowIndividualSites?: boolean, allowGroupSites?: boolean, earlyAccessPermitLotteryId?: string): PromiseAsyncResult<WonderlandTrailAvailability>
1270
+ {
1271
+ let month: number;
1272
+ if (isNumber(monthOrOptions))
1273
+ {
1274
+ month = monthOrOptions;
1275
+ year = year!;
1276
+ allowWalkupPermits = allowWalkupPermits!;
1277
+ allowIndividualSites = allowIndividualSites!;
1278
+ allowGroupSites = allowGroupSites!;
1279
+ }
1280
+ else
1281
+ {
1282
+ month = monthOrOptions.month;
1283
+ year = monthOrOptions.year;
1284
+ allowWalkupPermits = monthOrOptions.allowWalkupPermits;
1285
+ allowIndividualSites = monthOrOptions.allowIndividualSites;
1286
+ allowGroupSites = monthOrOptions.allowGroupSites;
1287
+ earlyAccessPermitLotteryId = monthOrOptions.earlyAccessPermitLotteryId;
1288
+ }
1289
+ return PromiseAsyncResult.create(async () =>
1290
+ {
1291
+ const result = WonderlandTrailAvailability.create();
1292
+
1293
+ for (const location of WonderlandTrailLocations.getLocations())
1294
+ {
1295
+ if (allowIndividualSites && location.divisionId)
1296
+ {
1297
+ const divisionAvailability: RecreationDotGovDivisionAvailability = await this.recreationDotGovClient.getDivisionAvailability(
1298
+ WonderlandTrailClient.permitItineraryId,
1299
+ location.divisionId,
1300
+ month,
1301
+ year,
1302
+ earlyAccessPermitLotteryId,
1303
+ );
1304
+ const divisionDayAvailabilities: Iterable<RecreationDotGovDivisionDayAvailability> = divisionAvailability.dayAvailabilities;
1305
+ if (divisionDayAvailabilities)
1306
+ {
1307
+ for (const divisionDayAvailability of divisionDayAvailabilities)
1308
+ {
1309
+ const hasWalkupPermits: boolean = (allowWalkupPermits && divisionDayAvailability.walkup);
1310
+ const hasReservationPermits: boolean = (divisionDayAvailability.reservationsRemaining > 0);
1311
+ if (hasWalkupPermits || hasReservationPermits)
1312
+ {
1313
+ result.addAvailability(
1314
+ location,
1315
+ divisionDayAvailability.date,
1316
+ hasWalkupPermits ? WonderlandTrailReservationType.Walkup : WonderlandTrailReservationType.Reserved,
1317
+ undefined,
1318
+ );
1319
+ }
1320
+ }
1321
+ }
1322
+ }
1323
+
1324
+ if (allowGroupSites && location.groupSiteDivisionId)
1325
+ {
1326
+ const groupSiteDivisionAvailability: RecreationDotGovDivisionAvailability = await this.recreationDotGovClient.getDivisionAvailability(
1327
+ WonderlandTrailClient.permitItineraryId,
1328
+ location.groupSiteDivisionId,
1329
+ month,
1330
+ year,
1331
+ earlyAccessPermitLotteryId,
1332
+ );
1333
+ const groupSiteDivisionDayAvailabilities: Iterable<RecreationDotGovDivisionDayAvailability> = groupSiteDivisionAvailability.dayAvailabilities;
1334
+ if (groupSiteDivisionDayAvailabilities)
1335
+ {
1336
+ for (const groupSiteDayAvailability of groupSiteDivisionDayAvailabilities)
1337
+ {
1338
+ const hasWalkupPermits: boolean = (allowWalkupPermits && groupSiteDayAvailability.walkup);
1339
+ const hasReservationPermits: boolean = (groupSiteDayAvailability.reservationsRemaining > 0);
1340
+ if (hasWalkupPermits || hasReservationPermits)
1341
+ {
1342
+ result.addAvailability(
1343
+ location,
1344
+ groupSiteDayAvailability.date,
1345
+ undefined,
1346
+ hasWalkupPermits ? WonderlandTrailReservationType.Walkup : WonderlandTrailReservationType.Reserved,
1347
+ );
1348
+ }
1349
+ }
1350
+ }
1351
+ }
1352
+ }
1353
+
1354
+ return result;
1355
+ });
1356
+ }
1357
+
1358
+ public findItineraries(parameters: {
1359
+ availability: WonderlandTrailAvailability,
1360
+ startDate: DateTime,
1361
+ startLocation: WonderlandTrailLocation,
1362
+ endLocation: WonderlandTrailLocation,
1363
+ direction: WonderlandTrailDirection,
1364
+ maximumDayDistanceMiles?: number,
1365
+ maximumItineraryDays?: number,
1366
+ campsitesToAvoid?: JavascriptIterable<WonderlandTrailLocation>,
1367
+ }): Iterable<WonderlandTrailItinerary>
1368
+ {
1369
+ PreCondition.assertNotUndefinedAndNotNull(parameters, "parameters");
1370
+
1371
+ const availability: WonderlandTrailAvailability = parameters.availability;
1372
+ const startDate: DateTime = parameters.startDate;
1373
+ const startLocation: WonderlandTrailLocation = parameters.startLocation;
1374
+ const endLocation: WonderlandTrailLocation = parameters.endLocation;
1375
+ const direction: WonderlandTrailDirection = parameters.direction;
1376
+ const maximumDayDistanceMiles: number | undefined = parameters.maximumDayDistanceMiles;
1377
+ const maximumItineraryDays: number | undefined = parameters.maximumItineraryDays;
1378
+ const campsitesToAvoid: Iterable<WonderlandTrailLocation> = Iterable.create(parameters.campsitesToAvoid ?? []);
1379
+
1380
+ const result: List<WonderlandTrailItinerary> = List.create();
1381
+
1382
+ const connections: WonderlandTrailConnections = WonderlandTrailConnections.createDefault()
1383
+ .expandConnections(startLocation, endLocation, direction);
1384
+
1385
+ let startLocationConnections: Iterator<WonderlandTrailConnection> = connections.iterateConnections(startLocation, undefined, direction);
1386
+ if (!isUndefinedOrNull(maximumDayDistanceMiles))
1387
+ {
1388
+ startLocationConnections = startLocationConnections.where(connection => connection.distanceMiles <= maximumDayDistanceMiles);
1389
+ }
1390
+
1391
+ const possibleItineraries: ListStack<WonderlandTrailItinerary> = Stack.create();
1392
+ possibleItineraries.addAll(startLocationConnections.map(c => WonderlandTrailItinerary.create(startDate).addConnection(c)));
1393
+
1394
+ while (possibleItineraries.any().await())
1395
+ {
1396
+ const currentItinerary: WonderlandTrailItinerary = possibleItineraries.remove().await();
1397
+ const currentItineraryEndLocation: WonderlandTrailLocation = currentItinerary.getEndLocation().await();
1398
+
1399
+ if ((maximumItineraryDays === undefined || currentItinerary.getDayCount() <= maximumItineraryDays) &&
1400
+ (startLocation === currentItineraryEndLocation || endLocation === currentItineraryEndLocation || !campsitesToAvoid.contains(currentItineraryEndLocation).await()))
1401
+ {
1402
+ if (currentItineraryEndLocation === endLocation)
1403
+ {
1404
+ result.add(currentItinerary);
1405
+ }
1406
+ else
1407
+ {
1408
+ const dayAvailability: WonderlandTrailAvailabilityType | undefined = availability.getDayAvailability(currentItineraryEndLocation, currentItinerary.getEndDay())
1409
+ .catch(() => undefined)
1410
+ .await();
1411
+ if (dayAvailability)
1412
+ {
1413
+ currentItinerary.addAvailabilityType(dayAvailability);
1414
+
1415
+ for (const nextDayConnection of connections.iterateConnections(currentItineraryEndLocation, undefined, direction))
1416
+ {
1417
+ if (!currentItinerary.containsAny({
1418
+ connection: nextDayConnection,
1419
+ checkConnectionStartLocation: false,
1420
+ checkItineraryStartLocation: false,
1421
+ checkItineraryEndLocation: false,
1422
+ }))
1423
+ {
1424
+ if (maximumDayDistanceMiles === undefined || nextDayConnection.distanceMiles <= maximumDayDistanceMiles)
1425
+ {
1426
+ possibleItineraries.add(currentItinerary.clone().addConnection(nextDayConnection));
1427
+ }
1428
+ }
1429
+ }
1430
+ }
1431
+ }
1432
+ }
1433
+ }
1434
+
1435
+ return result;
1436
+ }
1437
+
1438
+ public findItinerariesAsync(parameters: {
1439
+ startDay: DateTime,
1440
+ startLocation?: WonderlandTrailLocation,
1441
+ endLocation?: WonderlandTrailLocation,
1442
+ direction?: WonderlandTrailDirection,
1443
+ maximumDayDistanceMiles?: number,
1444
+ maximumItineraryDays?: number,
1445
+ allowWalkupPermits?: boolean,
1446
+ allowIndividualSites?: boolean,
1447
+ allowGroupSites?: boolean,
1448
+ campsitesToAvoid?: JavascriptIterable<WonderlandTrailLocation>,
1449
+ }): PromiseAsyncResult<Iterable<WonderlandTrailItinerary>>
1450
+ {
1451
+ PreCondition.assertNotUndefinedAndNotNull(parameters, "parameters");
1452
+
1453
+ const startDay: DateTime = parameters.startDay;
1454
+ const startLocation: WonderlandTrailLocation | undefined = parameters.startLocation;
1455
+ const endLocation: WonderlandTrailLocation | undefined = parameters.endLocation;
1456
+ const direction: WonderlandTrailDirection | undefined = parameters.direction;
1457
+ const maximumDayDistanceMiles: number | undefined = parameters.maximumDayDistanceMiles;
1458
+ const maximumItineraryDays: number | undefined = parameters.maximumItineraryDays;
1459
+ const allowWalkupPermits: boolean = parameters.allowWalkupPermits ?? true;
1460
+ const allowIndividualSites: boolean = parameters.allowIndividualSites ?? true;
1461
+ const allowGroupSites: boolean = parameters.allowGroupSites ?? false;
1462
+ const campsitesToAvoid: JavascriptIterable<WonderlandTrailLocation> = parameters.campsitesToAvoid ?? [];
1463
+
1464
+ PreCondition.assertNotUndefinedAndNotNull(startDay, "startDay");
1465
+
1466
+ return PromiseAsyncResult.create(async () =>
1467
+ {
1468
+ const result: List<WonderlandTrailItinerary> = List.create();
1469
+
1470
+ const startLocationsToCheck: Iterable<WonderlandTrailLocation> =
1471
+ (!isUndefinedOrNull(startLocation) ? Iterable.create([startLocation]) : WonderlandTrailLocations.getTrailheads());
1472
+ const directionsToCheck: Iterable<WonderlandTrailDirection> =
1473
+ (!isUndefinedOrNull(direction) ? Iterable.create([direction]) : Iterable.create([WonderlandTrailDirection.clockwise, WonderlandTrailDirection.counterClockwise]));
1474
+
1475
+ const availability: WonderlandTrailAvailability = await this.getAvailability(
1476
+ startDay.getMonth(),
1477
+ startDay.getYear(),
1478
+ allowWalkupPermits,
1479
+ allowIndividualSites,
1480
+ allowGroupSites,
1481
+ );
1482
+
1483
+ for (const startLocationToCheck of startLocationsToCheck)
1484
+ {
1485
+ for (const directionToCheck of directionsToCheck)
1486
+ {
1487
+ result.addAll(this.findItineraries({
1488
+ availability,
1489
+ startDate: startDay,
1490
+ startLocation: startLocationToCheck,
1491
+ endLocation: endLocation ?? startLocationToCheck,
1492
+ direction: directionToCheck,
1493
+ maximumDayDistanceMiles,
1494
+ maximumItineraryDays,
1495
+ campsitesToAvoid,
1496
+ }))
1497
+ }
1498
+ }
1499
+
1500
+ return result;
1501
+ });
1502
+ }
1503
1503
  }