@projektemacher/storymapjs 0.10.5 → 0.10.7

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 (148) hide show
  1. package/CHANGELOG +11 -0
  2. package/dist/assets/{demo-CVQ7kFek.js → demo-D6cTmzn6.js} +1 -1
  3. package/dist/assets/{harness-C-VZV-ou.js → harness-BPFlzutp.js} +1 -1
  4. package/dist/assets/main-Y-MYUSJ4.js +17 -0
  5. package/dist/css/fonts/font.oldstandard.css +14 -32
  6. package/dist/demo.html +2 -2
  7. package/dist/docs/migration.html +1 -1
  8. package/dist/embed/index.html +129 -4
  9. package/dist/harness.html +2 -2
  10. package/dist/index.html +17 -0
  11. package/dist/js/storymap.d.ts +126 -7
  12. package/dist/js/storymap.js +4518 -3613
  13. package/dist/js/storymap.js.map +1 -1
  14. package/docs/migration-from-knightlab.md +139 -0
  15. package/docs/storymap-as-iiif-manifest.md +510 -0
  16. package/package.json +6 -3
  17. package/src/animation/easings.ts +18 -0
  18. package/src/animation/morpheus.d.ts +41 -0
  19. package/src/core/Browser.ts +19 -0
  20. package/src/core/Load.ts +180 -0
  21. package/src/core/Util.ts +354 -0
  22. package/src/core/mixins.ts +171 -0
  23. package/src/dom/Dom.ts +31 -0
  24. package/src/dom/DomEvent.ts +61 -0
  25. package/src/globals.d.ts +16 -0
  26. package/src/language/Language.ts +107 -0
  27. package/src/language/locale/be.json +18 -0
  28. package/src/language/locale/bg.json +18 -0
  29. package/src/language/locale/cs.json +18 -0
  30. package/src/language/locale/de.json +20 -0
  31. package/src/language/locale/el.json +18 -0
  32. package/src/language/locale/en.json +28 -0
  33. package/src/language/locale/es.json +18 -0
  34. package/src/language/locale/et.json +18 -0
  35. package/src/language/locale/fr.json +18 -0
  36. package/src/language/locale/he.json +18 -0
  37. package/src/language/locale/hu.json +18 -0
  38. package/src/language/locale/is.json +18 -0
  39. package/src/language/locale/it.json +18 -0
  40. package/src/language/locale/jp.json +18 -0
  41. package/src/language/locale/ko.json +18 -0
  42. package/src/language/locale/nl.json +18 -0
  43. package/src/language/locale/nn.json +18 -0
  44. package/src/language/locale/no.json +18 -0
  45. package/src/language/locale/pl.json +18 -0
  46. package/src/language/locale/pt.json +18 -0
  47. package/src/language/locale/ru.json +18 -0
  48. package/src/language/locale/sk.json +18 -0
  49. package/src/language/locale/sr.json +17 -0
  50. package/src/language/locale/sv.json +18 -0
  51. package/src/language/locale/tr.json +18 -0
  52. package/src/language/locale/uk.json +18 -0
  53. package/src/language/locale/ur.json +18 -0
  54. package/src/language/locale/zh-cn.json +18 -0
  55. package/src/language/locale/zh-tw.json +18 -0
  56. package/src/main.ts +51 -0
  57. package/src/map/Map.ts +717 -0
  58. package/src/map/MapMarker.ts +149 -0
  59. package/src/map/openlayers/Map.OpenLayers.ts +1879 -0
  60. package/src/map/openlayers/MapMarker.OpenLayers.ts +176 -0
  61. package/src/map/openlayers/zoomifyTiles.ts +28 -0
  62. package/src/map/types.ts +17 -0
  63. package/src/media/EmbedUtil.ts +168 -0
  64. package/src/media/Media.ts +409 -0
  65. package/src/media/MediaType.ts +163 -0
  66. package/src/media/types/Audio.ts +87 -0
  67. package/src/media/types/Blockquote.ts +40 -0
  68. package/src/media/types/DailyMotion.ts +58 -0
  69. package/src/media/types/DocumentCloud.ts +41 -0
  70. package/src/media/types/Facebook.ts +41 -0
  71. package/src/media/types/Flickr.ts +115 -0
  72. package/src/media/types/GoogleDoc.ts +59 -0
  73. package/src/media/types/IFrame.ts +51 -0
  74. package/src/media/types/Image.ts +49 -0
  75. package/src/media/types/Juxtapose.ts +32 -0
  76. package/src/media/types/SoundCloud.ts +77 -0
  77. package/src/media/types/Text.ts +172 -0
  78. package/src/media/types/Twitter.ts +96 -0
  79. package/src/media/types/Video.ts +84 -0
  80. package/src/media/types/Vimeo.ts +65 -0
  81. package/src/media/types/Website.ts +36 -0
  82. package/src/media/types/Wikipedia.ts +124 -0
  83. package/src/media/types/YouTube.ts +192 -0
  84. package/src/scss/Typography.scss +237 -0
  85. package/src/scss/VCO.StoryMap.Dark.scss +138 -0
  86. package/src/scss/VCO.StoryMap.scss +164 -0
  87. package/src/scss/Variables.Dark.scss +56 -0
  88. package/src/scss/Variables.scss +47 -0
  89. package/src/scss/core/Mixins.scss +112 -0
  90. package/src/scss/core/Reset.scss +206 -0
  91. package/src/scss/fonts/_font.base.scss +113 -0
  92. package/src/scss/fonts/font.abril-droidsans.scss +75 -0
  93. package/src/scss/fonts/font.amatic-andika.scss +73 -0
  94. package/src/scss/fonts/font.bitter-raleway.scss +86 -0
  95. package/src/scss/fonts/font.clicker-garamond.scss +92 -0
  96. package/src/scss/fonts/font.dancing-ledger.scss +74 -0
  97. package/src/scss/fonts/font.default.scss +9 -0
  98. package/src/scss/fonts/font.fjalla-average.scss +80 -0
  99. package/src/scss/fonts/font.georgia-helvetica.scss +58 -0
  100. package/src/scss/fonts/font.knightlab.scss +97 -0
  101. package/src/scss/fonts/font.lustria-lato.scss +80 -0
  102. package/src/scss/fonts/font.medula-lato.scss +73 -0
  103. package/src/scss/fonts/font.oldstandard.scss +88 -0
  104. package/src/scss/fonts/font.opensans-gentiumbook.scss +80 -0
  105. package/src/scss/fonts/font.playfair-faunaone.scss +86 -0
  106. package/src/scss/fonts/font.playfair.scss +85 -0
  107. package/src/scss/fonts/font.pt.scss +90 -0
  108. package/src/scss/fonts/font.roboto-megrim.scss +73 -0
  109. package/src/scss/fonts/font.rufina-sintony.scss +73 -0
  110. package/src/scss/fonts/font.ubuntu.scss +78 -0
  111. package/src/scss/fonts/font.unicaone-vollkorn.scss +85 -0
  112. package/src/scss/icons/Icons.scss +239 -0
  113. package/src/scss/map/VCO.ImageMarker.scss +23 -0
  114. package/src/scss/map/VCO.Map.scss +21 -0
  115. package/src/scss/map/VCO.MapMarker.scss +147 -0
  116. package/src/scss/map/openlayers/VCO.Map.OpenLayers.scss +126 -0
  117. package/src/scss/media/VCO.Media.scss +222 -0
  118. package/src/scss/media/types/VCO.Media.Blockquote.scss +76 -0
  119. package/src/scss/media/types/VCO.Media.IFrame.scss +11 -0
  120. package/src/scss/media/types/VCO.Media.Image.scss +13 -0
  121. package/src/scss/media/types/VCO.Media.Instagram.scss +7 -0
  122. package/src/scss/media/types/VCO.Media.Profile.scss +5 -0
  123. package/src/scss/media/types/VCO.Media.Text.scss +90 -0
  124. package/src/scss/media/types/VCO.Media.Twitter.scss +87 -0
  125. package/src/scss/media/types/VCO.Media.Wikipedia.scss +64 -0
  126. package/src/scss/site/site.scss +748 -0
  127. package/src/scss/slider/VCO.Slide.scss +447 -0
  128. package/src/scss/slider/VCO.SlideNav.scss +338 -0
  129. package/src/scss/slider/VCO.StorySlider.scss +64 -0
  130. package/src/scss/snapmap/VCO.SnapMap.scss +103 -0
  131. package/src/scss/ui/VCO.Consent.scss +77 -0
  132. package/src/scss/ui/VCO.MenuBar.Button.scss +99 -0
  133. package/src/scss/ui/VCO.MenuBar.scss +20 -0
  134. package/src/scss/ui/VCO.Message.scss +104 -0
  135. package/src/site/site.ts +87 -0
  136. package/src/slider/Slide.ts +460 -0
  137. package/src/slider/SlideNav.ts +212 -0
  138. package/src/slider/StorySlider.ts +722 -0
  139. package/src/storymap/Consent.ts +146 -0
  140. package/src/storymap/StoryMap.ts +1108 -0
  141. package/src/storymap/iiif.ts +386 -0
  142. package/src/storymap/validate.ts +184 -0
  143. package/src/types.ts +297 -0
  144. package/src/ui/Draggable.ts +374 -0
  145. package/src/ui/MenuBar.ts +323 -0
  146. package/src/ui/Message.ts +118 -0
  147. package/src/ui/Swipable.ts +388 -0
  148. package/dist/assets/main-DwbXrrYn.js +0 -17
package/src/map/Map.ts ADDED
@@ -0,0 +1,717 @@
1
+ import { mergeData, slideTransitionDuration } from "../core/Util";
2
+ import { DomMixed, Evented, type EventedInstance } from "../core/mixins";
3
+ import Dom from "../dom/Dom";
4
+ import { Browser } from "../core/Browser";
5
+ import type { Map as OlMap } from "ol";
6
+ import type { Tile as TileLayer, Vector as VectorLayer } from "ol/layer";
7
+ import type OverviewMap from "ol/control/OverviewMap";
8
+ import type MapMarker from "./MapMarker";
9
+ import type { LinePoint, ViewToOptions } from "./types";
10
+ import type {
11
+ AnimationHandle,
12
+ LatLngLiteral,
13
+ StorymapData,
14
+ StorymapOptions,
15
+ StorymapSlide,
16
+ StorymapSlideLocation,
17
+ } from "../types";
18
+ /* Map
19
+ Makes a Map
20
+
21
+ Events:
22
+ markerAdded
23
+ markerRemoved
24
+
25
+
26
+ ================================================ */
27
+
28
+ /** Wheel/scroll zoom bookkeeping (handles cleared via clearTimeout). */
29
+ interface ScrollState {
30
+ start_time: number | null;
31
+ timer?: ReturnType<typeof setTimeout>;
32
+ timer_done?: ReturnType<typeof setTimeout>;
33
+ }
34
+
35
+ class MapBase {
36
+ declare "_el": { container: HTMLElement; map: HTMLElement; map_mask: HTMLElement };
37
+ declare "_loaded": { data: boolean; map: boolean };
38
+ declare "_map": OlMap | null;
39
+ declare "_mini_map": OverviewMap | null;
40
+ declare "_markers": MapMarker[];
41
+ declare "zoom_min_max": { min: number | null; max: number | null };
42
+ declare "_line": VectorLayer | null;
43
+ declare "_line_active": VectorLayer | null;
44
+ declare "current_marker": number;
45
+ declare "bounds_array": number[][] | null;
46
+ declare "_tile_layer": TileLayer | null;
47
+ declare "_tile_layer_mini": TileLayer | null;
48
+ declare "_image_layer": TileLayer | null;
49
+ declare "data": StorymapData;
50
+ declare "options": StorymapOptions;
51
+ declare "animator": AnimationHandle | null;
52
+ declare "_transition_duration": number;
53
+ declare "timer": ReturnType<typeof setTimeout> | null;
54
+ declare "touch_scale": number;
55
+ declare "scroll": ScrollState;
56
+ declare "fire": EventedInstance["fire"];
57
+ constructor(
58
+ elem: string | HTMLElement,
59
+ data?: Partial<StorymapData>,
60
+ options?: Partial<StorymapOptions>,
61
+ ) {
62
+ // DOM ELEMENTS
63
+ this._el = {
64
+ container: {} as HTMLElement,
65
+ map: {} as HTMLElement,
66
+ map_mask: {} as HTMLElement,
67
+ };
68
+
69
+ if (typeof elem === "object") {
70
+ this._el.container = elem;
71
+ } else {
72
+ this._el.container = Dom.get(elem);
73
+ }
74
+
75
+ // LOADED
76
+ this._loaded = {
77
+ data: false,
78
+ map: false,
79
+ };
80
+
81
+ // MAP
82
+ this._map = null;
83
+
84
+ // MINI MAP
85
+ this._mini_map = null;
86
+
87
+ // Markers
88
+ this._markers = [];
89
+
90
+ // Marker Zoom Miniumum and Maximum
91
+ this.zoom_min_max = {
92
+ min: null,
93
+ max: null,
94
+ };
95
+
96
+ // Line
97
+ this._line = null;
98
+ this._line_active = null;
99
+
100
+ // Current Marker
101
+ this.current_marker = 0;
102
+
103
+ // Markers Bounds Array
104
+ this.bounds_array = null;
105
+
106
+ // Map Tiles Layer
107
+ this._tile_layer = null;
108
+
109
+ // Map Tiles Layer for Mini Map
110
+ this._tile_layer_mini = null;
111
+
112
+ // Image Layer (for iiif)
113
+ this._image_layer = null;
114
+
115
+ // Data
116
+ this.data = {
117
+ uniqueid: "",
118
+ slides: [{ test: "yes" }, { test: "yes" }, { test: "yes" }],
119
+ };
120
+
121
+ //Options
122
+ this.options = {
123
+ map_type: "osm:standard",
124
+ map_as_image: false,
125
+ map_mini: false,
126
+ map_background_color: "#d9d9d9",
127
+ map_subdomains: "",
128
+ map_access_token: "",
129
+ overlays: [],
130
+ overview_extent: null,
131
+ iiif: {
132
+ url: "",
133
+ attribution: "",
134
+ },
135
+ skinny_size: 650,
136
+ less_bounce: true,
137
+ path_gfx: "gfx",
138
+ start_at_slide: 0,
139
+ map_popup: false,
140
+ zoom_distance: 100,
141
+ calculate_zoom: true, // Allow map to determine best zoom level between markers (recommended)
142
+ line_follows_path: true, // Map history path follows default line, if false it will connect previous and current only
143
+ line_color: "#333",
144
+ line_color_inactive: "#000",
145
+ line_weight: 5,
146
+ line_opacity: 0.2,
147
+ line_dash: "5,5",
148
+ line_join: "miter",
149
+ show_lines: true,
150
+ show_history_line: true,
151
+ map_center_offset: null, // takes object {top:0,left:0}
152
+ } as StorymapOptions;
153
+
154
+ // Animation
155
+ this.animator = null;
156
+ this._transition_duration = this.options.duration;
157
+
158
+ // Timer
159
+ this.timer = null;
160
+
161
+ // Touchpad Events
162
+ this.touch_scale = 1;
163
+ this.scroll = {
164
+ start_time: null,
165
+ };
166
+
167
+ // Merge Data and Options
168
+ mergeData(this.options, options);
169
+ mergeData(this.data, data);
170
+
171
+ this._initLayout();
172
+ this._initEvents();
173
+ this._createMap();
174
+ this._initData();
175
+ }
176
+
177
+ /* Public
178
+ ================================================== */
179
+ updateDisplay(w: number, h: number, animate?: boolean, d?: number, offset?: unknown): void {
180
+ this._updateDisplay(w, h, animate, d, offset);
181
+ }
182
+
183
+ goTo(n: number, change?: boolean): void {
184
+ if (n < this._markers.length && n >= 0) {
185
+ let zoom;
186
+ const previous_marker = this.current_marker;
187
+
188
+ this.current_marker = n;
189
+
190
+ // Scale the transition duration with the jump distance so that
191
+ // out-of-order navigation glides instead of flicking
192
+ this._transition_duration = slideTransitionDuration(previous_marker, n);
193
+
194
+ const marker = this._markers[this.current_marker];
195
+
196
+ // Stop animation
197
+ if (this.animator) {
198
+ this.animator.stop();
199
+ }
200
+
201
+ // Reset Active Markers
202
+ this._resetMarkersActive();
203
+
204
+ // Check to see if it's an overview
205
+ if (marker.data.type && marker.data.type === "overview") {
206
+ this._markerOverview();
207
+ if (!change) {
208
+ this._onMarkerChange();
209
+ }
210
+ } else {
211
+ // Make marker active
212
+ marker.active(true);
213
+
214
+ if (change) {
215
+ // Set Map View
216
+ if (marker.data.location) {
217
+ this._viewTo(marker.data.location, {
218
+ duration: this._transition_duration,
219
+ });
220
+ } else {
221
+ // nothing to show
222
+ }
223
+ } else {
224
+ if (this._hasLocation(marker.data)) {
225
+ // Calculate Zoom
226
+ zoom = this._calculateZoomChange(
227
+ this._getMapCenter(true),
228
+ marker.location(),
229
+ );
230
+
231
+ // Set Map View
232
+ this._viewTo(marker.data.location, {
233
+ calculate_zoom: this.options.calculate_zoom,
234
+ zoom: zoom,
235
+ duration: this._transition_duration,
236
+ });
237
+
238
+ // Show Line
239
+ if (this.options.line_follows_path) {
240
+ if (
241
+ this.options.show_history_line &&
242
+ marker.data.real_marker &&
243
+ this._markers[previous_marker].data.real_marker
244
+ ) {
245
+ let lines_array: LinePoint[] = [];
246
+ // Accumulate from the first marker so the
247
+ // connections between previous slides stay
248
+ // marked (mirrors the backward branch, which
249
+ // settles at the traveled path 0..current).
250
+ // The end state is the full traveled path, but
251
+ // only the latest hop animates: grow_from is
252
+ // the already-traveled prefix 0..previous,
253
+ // which stays drawn while [prev..current]
254
+ // traces progressively (see _replaceLines).
255
+ let line_num = 0,
256
+ point;
257
+ let retract_path_source: LinePoint[] | null = null;
258
+ let grow_path_source: LinePoint[] | null = null;
259
+ if (previous_marker < this.current_marker) {
260
+ while (line_num < this.current_marker) {
261
+ if (this._hasLocation(this._markers[line_num].data)) {
262
+ point = {
263
+ lat: this._markers[line_num].data.location.lat,
264
+ lon: this._markers[line_num].data.location.lon,
265
+ };
266
+ lines_array.push(point);
267
+ }
268
+
269
+ line_num++;
270
+ }
271
+ const grow_path: LinePoint[] = [];
272
+ for (let idx = 0; idx <= previous_marker; idx++) {
273
+ if (this._hasLocation(this._markers[idx].data)) {
274
+ grow_path.push({
275
+ lat: this._markers[idx].data.location.lat,
276
+ lon: this._markers[idx].data.location.lon,
277
+ });
278
+ }
279
+ }
280
+ grow_path_source = grow_path;
281
+ } else if (previous_marker > this.current_marker) {
282
+ // Backward navigation: the line retracts —
283
+ // the end state is the traveled path from
284
+ // the first marker to the current one, and
285
+ // the far end pulls back from the old
286
+ // marker to the new one (handled by the
287
+ // map's retract animation).
288
+ const traveled = [];
289
+ for (let idx = 0; idx <= this.current_marker; idx++) {
290
+ if (this._hasLocation(this._markers[idx].data)) {
291
+ traveled.push({
292
+ lat: this._markers[idx].data.location.lat,
293
+ lon: this._markers[idx].data.location.lon,
294
+ });
295
+ }
296
+ }
297
+ const retract_path = [];
298
+ for (let idx = 0; idx <= previous_marker; idx++) {
299
+ if (this._hasLocation(this._markers[idx].data)) {
300
+ retract_path.push({
301
+ lat: this._markers[idx].data.location.lat,
302
+ lon: this._markers[idx].data.location.lon,
303
+ });
304
+ }
305
+ }
306
+ lines_array = traveled;
307
+ retract_path_source = retract_path;
308
+ }
309
+
310
+ if (!retract_path_source) {
311
+ lines_array.push({
312
+ lat: marker.data.location.lat,
313
+ lon: marker.data.location.lon,
314
+ });
315
+ }
316
+
317
+ this._replaceLines(this._line_active, lines_array, {
318
+ duration: this._transition_duration,
319
+ retractFrom: retract_path_source,
320
+ growFrom: grow_path_source,
321
+ });
322
+ }
323
+ } else {
324
+ // Show Line
325
+ if (
326
+ this.options.show_history_line &&
327
+ marker.data.real_marker &&
328
+ this._markers[previous_marker].data.real_marker
329
+ ) {
330
+ this._replaceLines(
331
+ this._line_active,
332
+ [
333
+ {
334
+ lat: marker.data.location.lat,
335
+ lon: marker.data.location.lon,
336
+ },
337
+ {
338
+ lat: this._markers[previous_marker].data.location.lat,
339
+ lon: this._markers[previous_marker].data.location.lon,
340
+ },
341
+ ],
342
+ { duration: this._transition_duration },
343
+ );
344
+ }
345
+ }
346
+ } else {
347
+ this._markerOverview();
348
+ if (!change) {
349
+ this._onMarkerChange();
350
+ }
351
+ }
352
+
353
+ // Fire Event
354
+ this._onMarkerChange();
355
+ }
356
+ }
357
+ }
358
+ }
359
+
360
+ panTo(loc: LatLngLiteral, animate?: boolean): void {
361
+ this._panTo(loc, animate);
362
+ }
363
+
364
+ zoomTo(z: number, animate?: boolean): void {
365
+ this._zoomTo(z, animate);
366
+ }
367
+
368
+ viewTo(loc: StorymapSlideLocation, opts?: ViewToOptions): void {
369
+ this._viewTo(loc, opts);
370
+ }
371
+
372
+ getBoundsZoom(m1: LatLngLiteral, m2: LatLngLiteral, inside?: boolean, padding?: unknown): void {
373
+ this._getBoundsZoom(m1, m2, inside, padding); // (LatLngBounds[, Boolean, Point]) -> Number
374
+ }
375
+
376
+ markerOverview(): void {
377
+ this._markerOverview();
378
+ }
379
+
380
+ calculateMarkerZooms(): void {
381
+ this._calculateMarkerZooms();
382
+ }
383
+
384
+ createMiniMap(): void {
385
+ this._createMiniMap();
386
+ }
387
+
388
+ setMapOffset(left: number, top: number): void {
389
+ // Update Component Displays
390
+ if (!this.options.map_center_offset) {
391
+ this.options.map_center_offset = { left: left, top: top };
392
+ } else {
393
+ this.options.map_center_offset.left = left;
394
+ this.options.map_center_offset.top = top;
395
+ }
396
+ }
397
+
398
+ calculateMinMaxZoom(): void {
399
+ for (let i = 0; i < this._markers.length; i++) {
400
+ if (this._markers[i].data.location && this._markers[i].data.location.zoom) {
401
+ this.updateMinMaxZoom(this._markers[i].data.location.zoom);
402
+ }
403
+ }
404
+ }
405
+
406
+ updateMinMaxZoom(zoom: number): void {
407
+ if (!this.zoom_min_max.max) {
408
+ this.zoom_min_max.max = zoom;
409
+ }
410
+
411
+ if (!this.zoom_min_max.min) {
412
+ this.zoom_min_max.min = zoom;
413
+ }
414
+
415
+ if (this.zoom_min_max.max < zoom) {
416
+ this.zoom_min_max.max = zoom;
417
+ }
418
+ if (this.zoom_min_max.min > zoom) {
419
+ this.zoom_min_max.min = zoom;
420
+ }
421
+ }
422
+
423
+ initialMapLocation(): void {
424
+ if (this._loaded.data && this._loaded.map) {
425
+ // don't clobber navigation that already happened while the map was
426
+ // still loading (the first loadend can arrive late)
427
+ if (this.current_marker === 0) {
428
+ this.goTo(this.options.start_at_slide, true);
429
+ }
430
+ }
431
+ }
432
+
433
+ /* Adding, Hiding, Showing etc
434
+ ================================================== */
435
+ show(): void {}
436
+
437
+ hide(): void {}
438
+
439
+ /* Adding and Removing Markers
440
+ ================================================== */
441
+ createMarkers(array: StorymapSlide[]): void {
442
+ this._createMarkers(array);
443
+ }
444
+
445
+ createMarker(d: StorymapSlide): void {
446
+ this._createMarker(d);
447
+ }
448
+
449
+ _destroyMarker(marker: MapMarker): void {
450
+ this._removeMarker(marker);
451
+ for (let i = 0; i < this._markers.length; i++) {
452
+ if (this._markers[i] === marker) {
453
+ this._markers.splice(i, 1);
454
+ }
455
+ }
456
+ this.fire("markerRemoved", marker);
457
+ }
458
+
459
+ _createMarkers(array: StorymapSlide[]): void {
460
+ for (let i = 0; i < array.length; i++) {
461
+ this._createMarker(array[i]); // this must be called even for overview which has no marker or other logic must be fixed.
462
+ if (this._hasLocation(array[i]) && this.options.show_lines) {
463
+ this._addToLine(this._line, array[i]);
464
+ }
465
+ }
466
+ }
467
+
468
+ /**
469
+ * A slide has a real location: lat and lon are both numbers — lat 0 and
470
+ * lon 0 are valid coordinates and must not be treated as missing.
471
+ */
472
+ _hasLocation(d: StorymapSlide): boolean {
473
+ return (
474
+ !!d.location && typeof d.location.lat == "number" && typeof d.location.lon == "number"
475
+ );
476
+ }
477
+
478
+ /* Map Specific
479
+ ================================================== */
480
+
481
+ /* Map Specific Create
482
+ ================================================== */
483
+ // Extend this map class and use this to create the map using preferred API
484
+ _createMap(): void {}
485
+
486
+ /* Mini Map Specific Create
487
+ ================================================== */
488
+ // Extend this map class and use this to create the map using preferred API
489
+ _createMiniMap(): void {}
490
+
491
+ /* Map Specific Marker
492
+ ================================================== */
493
+
494
+ // Specific Marker Methods based on preferred Map API
495
+ _createMarker(d?: StorymapSlide): void {
496
+ const marker = {} as MapMarker;
497
+ marker.on("markerclick", this._onMarkerClick);
498
+ this._addMarker(marker);
499
+ this._markers.push(marker);
500
+ marker.marker_number = this._markers.length - 1;
501
+ this.fire("markerAdded", marker);
502
+ }
503
+
504
+ _addMarker(marker: MapMarker): void {}
505
+
506
+ _removeMarker(marker: MapMarker): void {}
507
+
508
+ _resetMarkersActive(): void {
509
+ for (let i = 0; i < this._markers.length; i++) {
510
+ this._markers[i].active(false);
511
+ }
512
+ }
513
+
514
+ _calculateMarkerZooms(): void {}
515
+
516
+ /* Map Specific Line
517
+ ================================================== */
518
+
519
+ _createLine(d?: StorymapSlide): unknown {
520
+ return { data: d };
521
+ }
522
+
523
+ _addToLine(line: VectorLayer | null, d: LinePoint): void {}
524
+
525
+ _replaceLines(
526
+ line: VectorLayer | null,
527
+ d: LinePoint[],
528
+ animate?: {
529
+ duration: number;
530
+ retractFrom?: LinePoint[] | null;
531
+ growFrom?: LinePoint[] | null;
532
+ },
533
+ ): void {}
534
+
535
+ _addLineToMap(line: VectorLayer): void {}
536
+
537
+ /* Map Specific Methods
538
+ ================================================== */
539
+
540
+ _panTo(loc: LatLngLiteral, animate?: boolean): void {}
541
+
542
+ _zoomTo(z: number, animate?: boolean): void {}
543
+
544
+ _viewTo(loc: StorymapSlideLocation, opts?: ViewToOptions): void {}
545
+
546
+ _updateMapDisplay(animate?: boolean, d?: number): void {}
547
+
548
+ _refreshMap(): void {}
549
+
550
+ _getMapZoom(): number {
551
+ return 1;
552
+ }
553
+
554
+ _getMapCenter(correct_for_center?: boolean): LatLngLiteral {
555
+ return { lat: 0, lng: 0 };
556
+ }
557
+
558
+ _getBoundsZoom(
559
+ m1: LatLngLiteral,
560
+ m2: LatLngLiteral,
561
+ inside?: boolean,
562
+ padding?: unknown,
563
+ ): number {
564
+ return undefined;
565
+ }
566
+
567
+ _markerOverview(duration?: number): void {}
568
+
569
+ /**
570
+ * Great-circle length of the route through all markers in kilometers;
571
+ * `undefined` when the map cannot compute it or there are fewer than two
572
+ * markers (issue #341).
573
+ */
574
+ getRouteDistance(): number | undefined {
575
+ return undefined;
576
+ }
577
+
578
+ /* Events
579
+ ================================================== */
580
+ _onMarkerChange(e?: unknown): void {
581
+ this.fire("change", { current_marker: this.current_marker });
582
+ }
583
+
584
+ _onMarkerClick(e: { marker_number: number }): void {
585
+ if (this.current_marker !== e.marker_number) {
586
+ this.goTo(e.marker_number, false);
587
+ }
588
+ }
589
+
590
+ _onMapLoaded(e?: unknown): void {
591
+ // OpenLayers fires `loadend` after every finished tile-load cycle, not
592
+ // just the first render — without this guard every navigation would
593
+ // snap the map back to the start slide once its tiles arrive
594
+ if (this._loaded.map) {
595
+ return;
596
+ }
597
+ this._loaded.map = true;
598
+
599
+ if (this.options.calculate_zoom) {
600
+ this.calculateMarkerZooms();
601
+ }
602
+
603
+ this.calculateMinMaxZoom();
604
+
605
+ if (this.options.map_mini && !Browser.touch) {
606
+ this.createMiniMap();
607
+ }
608
+
609
+ this.initialMapLocation();
610
+ this.fire("loaded", this.data);
611
+ }
612
+
613
+ _onWheel(e: WheelEvent): void {
614
+ // borrowed from http://jsbin.com/qiyaseza/5/edit
615
+ if (e.ctrlKey) {
616
+ const s = Math.exp(-e.deltaY / 100);
617
+ this.touch_scale *= s;
618
+ e.preventDefault();
619
+ e.stopPropagation();
620
+ }
621
+
622
+ if (!this.scroll.start_time) {
623
+ this.scroll.start_time = +new Date();
624
+ }
625
+
626
+ const time_left = Math.max(40 - (+new Date() - this.scroll.start_time), 0);
627
+
628
+ clearTimeout(this.scroll.timer);
629
+
630
+ this.scroll.timer = setTimeout(() => {
631
+ this._scollZoom();
632
+ }, time_left);
633
+ }
634
+
635
+ _scollZoom(e?: unknown): void {
636
+ const current_zoom = this._getMapZoom();
637
+
638
+ this.scroll.start_time = null;
639
+ clearTimeout(this.scroll.timer);
640
+ clearTimeout(this.scroll.timer_done);
641
+
642
+ this.scroll.timer_done = setTimeout(() => {
643
+ this._scollZoomDone();
644
+ }, 1000);
645
+
646
+ this.zoomTo(Math.round(current_zoom * this.touch_scale));
647
+ }
648
+
649
+ _scollZoomDone(e?: unknown): void {
650
+ this.touch_scale = 1;
651
+ }
652
+
653
+ /* Private Methods
654
+ ================================================== */
655
+
656
+ _calculateZoomChange(
657
+ origin: LatLngLiteral,
658
+ destination: LatLngLiteral,
659
+ correct_for_center?: boolean,
660
+ ): number {
661
+ return this._getBoundsZoom(origin, destination, correct_for_center);
662
+ }
663
+
664
+ _updateDisplay(w?: number, h?: number, animate?: boolean, d?: number, offset?: unknown): void {
665
+ // Update Map Display
666
+ this._updateMapDisplay(animate, d);
667
+ }
668
+
669
+ /** Re-apply runtime-changed options (no-op in the base class) */
670
+ applyOptions(_keys: string[]): void {}
671
+
672
+ _initLayout(): void {
673
+ // Create Layout
674
+ this._el.map_mask = Dom.create("div", "vco-map-mask", this._el.container);
675
+
676
+ if (this.options.map_as_image) {
677
+ this._el.map = Dom.create(
678
+ "div",
679
+ "vco-map-display vco-mapimage-display",
680
+ this._el.map_mask,
681
+ );
682
+ } else {
683
+ this._el.map = Dom.create("div", "vco-map-display", this._el.map_mask);
684
+ }
685
+ }
686
+
687
+ _initData(): void {
688
+ if (this.data.slides) {
689
+ this._createMarkers(this.data.slides);
690
+ this._afterCreateMarkers();
691
+ this._resetMarkersActive();
692
+ if (this._markers.length > 0) {
693
+ this._markers[this.current_marker].active(true);
694
+ }
695
+ this._loaded.data = true;
696
+ }
697
+ }
698
+
699
+ /** Hook after marker creation (overridden per engine as needed) */
700
+ _afterCreateMarkers(): void {}
701
+
702
+ _initEvents(): void {
703
+ this._el.map.addEventListener("wheel", (e) => {
704
+ this._onWheel(e);
705
+ });
706
+
707
+ //this.on("wheel", this._onWheel, this);
708
+ }
709
+ }
710
+
711
+ export default class Map extends DomMixed(Evented(MapBase)) {
712
+ constructor(...args: ConstructorParameters<typeof MapBase>) {
713
+ super(...args);
714
+ }
715
+ }
716
+
717
+ export { Map };