@projektemacher/storymapjs 0.10.8 → 0.11.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 (195) hide show
  1. package/CHANGELOG +81 -0
  2. package/README.md +129 -12
  3. package/dist/assets/{demo-DBpeF31K.js → demo-bWnOyfUw.js} +1 -1
  4. package/dist/assets/harness-C3JUv5Ii.js +1 -0
  5. package/dist/assets/{harness-multi-CYHgI0a9.js → harness-multi-XZNex3tb.js} +1 -1
  6. package/dist/assets/{harness-support-Do7sIp2j.js → harness-support-CjfZIVeb.js} +1 -1
  7. package/dist/assets/main-DQ32hWBM.js +17 -0
  8. package/dist/context.json +13 -2
  9. package/dist/css/storymap.css +1 -1
  10. package/dist/demo.html +3 -2
  11. package/dist/docs/api/assets/navigation.js +1 -1
  12. package/dist/docs/api/assets/search.js +1 -1
  13. package/dist/docs/api/classes/StoryMap.html +777 -269
  14. package/dist/docs/api/functions/MediaType.html +1 -1
  15. package/dist/docs/api/functions/isPresentation3Collection.html +1 -1
  16. package/dist/docs/api/functions/isPresentation3Manifest.html +1 -1
  17. package/dist/docs/api/functions/isSlideshowCollection.html +51 -0
  18. package/dist/docs/api/functions/loadCSS.html +1 -1
  19. package/dist/docs/api/functions/manifestToStorymapData.html +1 -1
  20. package/dist/docs/api/functions/setLanguage.html +1 -1
  21. package/dist/docs/api/functions/slideshowToStorymapData.html +51 -0
  22. package/dist/docs/api/functions/slideshowVersion.html +48 -0
  23. package/dist/docs/api/functions/storymapToManifest.html +1 -1
  24. package/dist/docs/api/functions/validateStorymap.html +1 -1
  25. package/dist/docs/api/functions/validateStorymapAndReport.html +1 -1
  26. package/dist/docs/api/index.html +115 -12
  27. package/dist/docs/api/interfaces/AnimationHandle.html +2 -2
  28. package/dist/docs/api/interfaces/ImagereadyPayload.html +4 -4
  29. package/dist/docs/api/interfaces/LanguageEntry.html +6 -6
  30. package/dist/docs/api/interfaces/LatLngLiteral.html +4 -4
  31. package/dist/docs/api/interfaces/LoadOptions.html +2 -2
  32. package/dist/docs/api/interfaces/MapEvents.html +116 -0
  33. package/dist/docs/api/interfaces/MapMarkerEvents.html +66 -0
  34. package/dist/docs/api/interfaces/MarkerEventPayload.html +57 -0
  35. package/dist/docs/api/interfaces/MediaEvents.html +78 -0
  36. package/dist/docs/api/interfaces/MenuBarEvents.html +114 -0
  37. package/dist/docs/api/interfaces/MessageEvents.html +72 -0
  38. package/dist/docs/api/interfaces/OlLayer.html +1 -1
  39. package/dist/docs/api/interfaces/OlMap.html +1 -1
  40. package/dist/docs/api/interfaces/OlSource.html +1 -1
  41. package/dist/docs/api/interfaces/OlTileLayer.html +1 -1
  42. package/dist/docs/api/interfaces/OlVectorLayer.html +1 -1
  43. package/dist/docs/api/interfaces/OlView.html +1 -1
  44. package/dist/docs/api/interfaces/SlideBackgroundState.html +66 -0
  45. package/dist/docs/api/interfaces/SlideEvents.html +78 -0
  46. package/dist/docs/api/interfaces/SlideNavEvents.html +72 -0
  47. package/dist/docs/api/interfaces/SlideshowPlayerSettings.html +182 -0
  48. package/dist/docs/api/interfaces/SlideshowToStorymapResult.html +60 -0
  49. package/dist/docs/api/interfaces/SlideshowTranslatorOptions.html +65 -0
  50. package/dist/docs/api/interfaces/SlideshowWarning.html +72 -0
  51. package/dist/docs/api/interfaces/StoryMapEvents.html +146 -0
  52. package/dist/docs/api/interfaces/StoryMapMarker.html +763 -0
  53. package/dist/docs/api/interfaces/StorySliderEvents.html +102 -0
  54. package/dist/docs/api/interfaces/StorymapBasemap.html +69 -0
  55. package/dist/docs/api/interfaces/StorymapData.html +12 -6
  56. package/dist/docs/api/interfaces/StorymapDataWrapper.html +2 -2
  57. package/dist/docs/api/interfaces/StorymapDocument.html +2 -2
  58. package/dist/docs/api/interfaces/StorymapError.html +3 -3
  59. package/dist/docs/api/interfaces/StorymapGeoreference.html +5 -5
  60. package/dist/docs/api/interfaces/StorymapGeoreferenceBody.html +4 -4
  61. package/dist/docs/api/interfaces/StorymapGroundControlPoint.html +4 -4
  62. package/dist/docs/api/interfaces/StorymapLanguageMap.html +2 -2
  63. package/dist/docs/api/interfaces/StorymapManifest.html +12 -12
  64. package/dist/docs/api/interfaces/StorymapManifestAnnotation.html +43 -11
  65. package/dist/docs/api/interfaces/StorymapManifestBackground.html +6 -6
  66. package/dist/docs/api/interfaces/StorymapManifestCanvas.html +65 -17
  67. package/dist/docs/api/interfaces/StorymapManifestContentResource.html +8 -8
  68. package/dist/docs/api/interfaces/StorymapManifestFeatureCollection.html +4 -4
  69. package/dist/docs/api/interfaces/StorymapManifestGeoreferencing.html +7 -7
  70. package/dist/docs/api/interfaces/StorymapManifestImageService.html +4 -4
  71. package/dist/docs/api/interfaces/StorymapManifestRange.html +5 -5
  72. package/dist/docs/api/interfaces/StorymapManifestService.html +4 -4
  73. package/dist/docs/api/interfaces/StorymapManifestSpecificResource.html +4 -4
  74. package/dist/docs/api/interfaces/StorymapManifestStatement.html +3 -3
  75. package/dist/docs/api/interfaces/StorymapMapOptions.html +5 -5
  76. package/dist/docs/api/interfaces/StorymapOptions.html +277 -75
  77. package/dist/docs/api/interfaces/StorymapOverlayLayer.html +42 -11
  78. package/dist/docs/api/interfaces/StorymapSlide.html +70 -16
  79. package/dist/docs/api/interfaces/StorymapSlideBackground.html +4 -4
  80. package/dist/docs/api/interfaces/StorymapSlideLocation.html +58 -14
  81. package/dist/docs/api/interfaces/StorymapSlideMarker.html +8 -9
  82. package/dist/docs/api/interfaces/StorymapSlideMedia.html +49 -10
  83. package/dist/docs/api/interfaces/StorymapSlideNarration.html +61 -9
  84. package/dist/docs/api/interfaces/StorymapSlideText.html +4 -4
  85. package/dist/docs/api/interfaces/StorymapTilejson.html +8 -8
  86. package/dist/docs/api/interfaces/SwipableEvents.html +102 -0
  87. package/dist/docs/api/interfaces/TextEvents.html +66 -0
  88. package/dist/docs/api/media/events.md +66 -0
  89. package/dist/docs/api/media/migration-from-knightlab.md +89 -49
  90. package/dist/docs/api/media/slideshow.md +109 -0
  91. package/dist/docs/api/media/storymap-as-iiif-manifest.md +1007 -0
  92. package/dist/docs/api/modules.html +6 -6
  93. package/dist/docs/api/types/ContentState.html +3 -3
  94. package/dist/docs/api/types/FiredEvent.html +43 -0
  95. package/dist/docs/api/types/ImagereadyKind.html +1 -1
  96. package/dist/docs/api/types/MediaTypeMatch.html +5 -5
  97. package/dist/docs/api/types/SwipeDirection.html +36 -0
  98. package/dist/docs/api/types/TileSourceFactory.html +1 -1
  99. package/dist/docs/authoring.html +20 -7
  100. package/dist/docs/events.html +263 -0
  101. package/dist/docs/iiif.html +136 -22
  102. package/dist/docs/migration.html +66 -19
  103. package/dist/docs/readme.html +113 -12
  104. package/dist/embed/index.html +7 -0
  105. package/dist/examples/font-css-false.json +33 -0
  106. package/dist/examples/issue-image-region.json +1 -1
  107. package/dist/examples/layers-control.json +60 -0
  108. package/dist/examples/map-template-provider.json +36 -0
  109. package/dist/examples/multi-media.json +71 -0
  110. package/dist/examples-iiif/annotated-image.json +16 -0
  111. package/dist/examples-iiif/font-css-false.json +114 -0
  112. package/dist/examples-iiif/issue-image-region.json +1 -1
  113. package/dist/examples-iiif/layers-control.json +226 -0
  114. package/dist/examples-iiif/map-template-provider.json +133 -0
  115. package/dist/examples-iiif/multi-media.json +237 -0
  116. package/dist/examples-slideshow/single.json +79 -0
  117. package/dist/examples-slideshow/static/pixel.png +0 -0
  118. package/dist/examples-slideshow/static.json +44 -0
  119. package/dist/harness-multi.html +3 -3
  120. package/dist/harness.html +4 -3
  121. package/dist/index.html +3 -2
  122. package/dist/js/storymap.d.ts +1326 -289
  123. package/dist/js/storymap.js +6690 -4784
  124. package/dist/js/storymap.js.map +1 -1
  125. package/dist/navplace-properties.json +14 -0
  126. package/dist/site.css +54 -36
  127. package/docs/migration-from-knightlab.md +89 -49
  128. package/docs/storymap-as-iiif-manifest.md +199 -126
  129. package/package.json +4 -2
  130. package/schema/storymap.schema.json +334 -34
  131. package/src/core/Util.ts +3 -1
  132. package/src/core/mixins.ts +42 -28
  133. package/src/language/Language.ts +7 -0
  134. package/src/language/locale/.expected-gaps.json +287 -27
  135. package/src/language/locale/de.json +11 -1
  136. package/src/language/locale/en.json +11 -1
  137. package/src/main.ts +51 -0
  138. package/src/map/Map.ts +44 -6
  139. package/src/map/MapMarker.ts +16 -3
  140. package/src/map/openlayers/Map.OpenLayers.ts +787 -62
  141. package/src/map/openlayers/MapMarker.OpenLayers.ts +113 -13
  142. package/src/map/slideView.ts +149 -0
  143. package/src/map/types.ts +2 -0
  144. package/src/media/EmbedUtil.ts +27 -2
  145. package/src/media/Media.ts +27 -4
  146. package/src/media/embedId.ts +6 -0
  147. package/src/media/types/Flickr.ts +14 -3
  148. package/src/media/types/HtmlMedia.ts +66 -0
  149. package/src/media/types/Image.ts +15 -3
  150. package/src/media/types/SoundCloud.ts +1 -1
  151. package/src/media/types/Text.ts +27 -8
  152. package/src/media/types/Twitter.ts +9 -7
  153. package/src/media/types/Vimeo.ts +1 -1
  154. package/src/media/types/YouTube.ts +2 -2
  155. package/src/scss/Typography.scss +15 -15
  156. package/src/scss/VCO.StoryMap.scss +173 -8
  157. package/src/scss/core/Reset.scss +5 -18
  158. package/src/scss/map/VCO.Map.scss +1 -1
  159. package/src/scss/map/VCO.MapMarker.scss +20 -9
  160. package/src/scss/map/openlayers/VCO.Map.OpenLayers.scss +7 -4
  161. package/src/scss/media/VCO.Media.scss +7 -7
  162. package/src/scss/media/types/VCO.Media.Blockquote.scss +1 -1
  163. package/src/scss/media/types/VCO.Media.Text.scss +5 -5
  164. package/src/scss/media/types/VCO.Media.Twitter.scss +4 -4
  165. package/src/scss/media/types/VCO.Media.Wikipedia.scss +2 -19
  166. package/src/scss/site/site.scss +55 -36
  167. package/src/scss/slider/VCO.Slide.scss +49 -11
  168. package/src/scss/slider/VCO.SlideNav.scss +23 -7
  169. package/src/scss/slider/VCO.StorySlider.scss +50 -0
  170. package/src/scss/snapmap/VCO.SnapMap.scss +3 -3
  171. package/src/scss/ui/VCO.Consent.scss +20 -14
  172. package/src/scss/ui/VCO.Layers.scss +119 -0
  173. package/src/scss/ui/VCO.MenuBar.Button.scss +140 -19
  174. package/src/scss/ui/VCO.MenuBar.scss +9 -2
  175. package/src/scss/ui/VCO.Message.scss +14 -0
  176. package/src/slider/Slide.ts +232 -74
  177. package/src/slider/SlideNav.ts +15 -4
  178. package/src/slider/StorySlider.ts +203 -22
  179. package/src/storymap/Consent.ts +22 -0
  180. package/src/storymap/StoryMap.ts +944 -131
  181. package/src/storymap/content-state.ts +3 -8
  182. package/src/storymap/font-css.ts +72 -0
  183. package/src/storymap/from-slideshow.ts +785 -0
  184. package/src/storymap/iiif-shared.ts +228 -0
  185. package/src/storymap/iiif.ts +289 -100
  186. package/src/storymap/to-iiif.ts +131 -44
  187. package/src/types.ts +382 -17
  188. package/src/ui/LayersControl.ts +302 -0
  189. package/src/ui/MenuBar.ts +232 -21
  190. package/src/ui/Message.ts +36 -3
  191. package/src/ui/Swipable.ts +25 -7
  192. package/dist/assets/harness-Cr0rYpvt.js +0 -1
  193. package/dist/assets/main-DXalctuR.js +0 -17
  194. package/src/scss/VCO.StoryMap.Dark.scss +0 -150
  195. package/src/scss/Variables.Dark.scss +0 -57
@@ -18,6 +18,19 @@ import type {
18
18
  StorymapSlide,
19
19
  StorymapSlideLocation,
20
20
  } from "../types";
21
+ // Explicit `.ts` extension: scripts/convert-to-iiif.mjs imports this module
22
+ // straight into node --experimental-strip-types, whose ESM resolution does
23
+ // not try extensions. Extensionless (repo style) breaks `npm run convert:iiif`
24
+ // and tests/convert-cli.test.ts catches it.
25
+ import {
26
+ LOCATION_PROPERTIES,
27
+ asRecord,
28
+ isDuplicateNarration,
29
+ isHttpUrl,
30
+ isLonLatBox,
31
+ languageMap,
32
+ writePlaybackTerms,
33
+ } from "./iiif-shared.ts";
21
34
 
22
35
  /**
23
36
  * The document {@link storymapToManifest} reads: the `{ "storymap": {...} }`
@@ -99,6 +112,12 @@ export interface StorymapManifestAnnotation {
99
112
  accessibilitySummary?: StorymapLanguageMap;
100
113
  body: StorymapManifestContentResource;
101
114
  target: string | StorymapManifestSpecificResource;
115
+ /** Playback flags (slideshow audio terms); foreign members, absent when unset. */
116
+ "storymap:loop"?: boolean;
117
+ "storymap:offset"?: number;
118
+ "storymap:play"?: string;
119
+ "storymap:stopOnExit"?: boolean;
120
+ "storymap:stopAllPrevious"?: boolean;
102
121
  }
103
122
 
104
123
  /** A painting annotation whose target is a region of a larger image (§2.8). */
@@ -161,6 +180,16 @@ export interface StorymapManifestCanvas {
161
180
  "storymap:type"?: string;
162
181
  "storymap:mediaSrcset"?: unknown;
163
182
  "storymap:mediaSizes"?: unknown;
183
+ /** View directives (slideshow rotation/basemap/filter/mask); navPlace carries them when lat/lon exist. */
184
+ "storymap:rotation"?: number;
185
+ "storymap:basemap"?: string;
186
+ "storymap:filter"?: unknown;
187
+ "storymap:mask"?: unknown;
188
+ /** Per-slide autoplay dwell (slideshow slidetimeout) and image overlay. */
189
+ "storymap:slidetimeout"?: number;
190
+ "storymap:imgoverlay"?: unknown;
191
+ /** Provenance identifiers (slideshow manifest/canvas/image ids). */
192
+ "storymap:provenance"?: unknown;
164
193
  navPlace?: StorymapManifestFeatureCollection;
165
194
  }
166
195
 
@@ -232,28 +261,6 @@ const IMAGE_FORMATS: Record<string, string> = {
232
261
  const VIDEO_HOSTS = ["youtube.com", "youtu.be", "vimeo.com", "dailymotion.com", "vine.co"];
233
262
  const SOUND_HOSTS = ["soundcloud.com"];
234
263
 
235
- /**
236
- * Keys copied out of navPlace Feature properties, in the order they are
237
- * written. This list mirrors the reader's (`src/storymap/iiif.ts`): `popup`
238
- * and `audioBadge` are marker presentation with no IIIF vocabulary of their
239
- * own, and omitting them here silently drops them on the round trip.
240
- */
241
- const LOCATION_PROPERTIES = [
242
- "name",
243
- "zoom",
244
- "line",
245
- "icon",
246
- "iconSize",
247
- "image",
248
- "use_custom_marker",
249
- "popup",
250
- "audioBadge",
251
- ] as const;
252
-
253
- function isHttpUrl(value: unknown): value is string {
254
- return typeof value === "string" && /^https?:\/\//i.test(value);
255
- }
256
-
257
264
  function classifyMediaUrl(url: string): { type: string; format?: string } {
258
265
  const ext = url.split(/[?#]/)[0].split(".").pop()?.toLowerCase();
259
266
  // Own-property check: a bare lookup hits Object.prototype, so a URL
@@ -278,10 +285,6 @@ function classifyMediaUrl(url: string): { type: string; format?: string } {
278
285
  return { type: "Text", format: "text/html" };
279
286
  }
280
287
 
281
- function languageMap(value: string): StorymapLanguageMap {
282
- return { none: [value] };
283
- }
284
-
285
288
  /**
286
289
  * A value worth writing: neither absent nor the empty string. A type guard, so
287
290
  * a declared `string | null` member narrows to the string it is.
@@ -290,28 +293,11 @@ function present<T>(value: T | null | undefined): value is T {
290
293
  return value !== undefined && value !== null && value !== "";
291
294
  }
292
295
 
293
- /** A plain object, or null for anything else (arrays included). */
294
- function asRecord(value: unknown): Record<string, unknown> | null {
295
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
296
- return null;
297
- }
298
- return value as Record<string, unknown>;
299
- }
300
-
301
296
  /** An array of `T`, or an empty one. */
302
297
  function asArray<T>(value: unknown): T[] {
303
298
  return Array.isArray(value) ? (value as T[]) : [];
304
299
  }
305
300
 
306
- /** A `[west, south, east, north]` box of four finite numbers. */
307
- function isLonLatBox(value: unknown): value is [number, number, number, number] {
308
- return (
309
- Array.isArray(value) &&
310
- value.length === 4 &&
311
- value.every((n) => typeof n === "number" && Number.isFinite(n))
312
- );
313
- }
314
-
315
301
  /**
316
302
  * Convert a legacy storymap document to a IIIF Presentation 3.0 manifest.
317
303
  *
@@ -367,6 +353,23 @@ export function storymapToManifest(name: string, legacy: StorymapDocument): Stor
367
353
  ],
368
354
  items: canvases,
369
355
  };
356
+ // Story credit (slideshow `creator`/`rights`): structured metadata
357
+ // entries, which the reader keeps in `data.metadata`. The dedicated
358
+ // `provider`/`rights` slots stay untouched — provider names the
359
+ // publisher, which the converter does not know (§3.2).
360
+ const credit = storymap.credit;
361
+ if (credit && typeof credit.creator === "string" && credit.creator !== "") {
362
+ manifest.metadata.push({
363
+ label: languageMap("Creator"),
364
+ value: languageMap(credit.creator),
365
+ });
366
+ }
367
+ if (credit && typeof credit.rights === "string" && credit.rights !== "") {
368
+ manifest.metadata.push({
369
+ label: languageMap("Rights"),
370
+ value: languageMap(credit.rights),
371
+ });
372
+ }
370
373
 
371
374
  // `structures` carries the groups (§3.5). A storymap states a slide's
372
375
  // group as a plain string; P3 states it as a Range over the canvases it
@@ -468,7 +471,12 @@ function buildMapConfig(
468
471
  if (storymap.map_center_offset !== undefined) {
469
472
  config["storymap:mapCenterOffset"] = storymap.map_center_offset;
470
473
  }
471
- const fontCss: unknown = legacy.font_css || storymap.font_css;
474
+ // false is meaningful (no injected theme) and must survive the legacy
475
+ // fallback; everything else keeps the previous `||` semantics.
476
+ const fontCss: unknown =
477
+ legacy.font_css === false || storymap.font_css === false
478
+ ? false
479
+ : legacy.font_css || storymap.font_css;
472
480
  if (present(fontCss)) {
473
481
  config["storymap:fontCss"] = fontCss;
474
482
  }
@@ -590,7 +598,20 @@ function buildCanvas(
590
598
  ...buildAnnotationPresentation(slide),
591
599
  body: buildBody(slide, isImageMap),
592
600
  target: buildRegionTarget(canvasId, slide.location?.region) ?? canvasId,
601
+ // Playback flags for audio/video media (slideshow
602
+ // audio terms); absent stays absent.
603
+ ...writePlaybackTerms(slide.media ?? {}),
593
604
  },
605
+ // Narration is a supplementing Sound body (V1 of #358):
606
+ // "additional to the painting", with no rendering rules
607
+ // attached — exactly what the dedicated audio element
608
+ // plays. Skipped when it duplicates the painting URL.
609
+ ...buildNarration(canvasId, slide),
610
+ // Extra visible media as further painting annotations (V2
611
+ // of #358): IIIF expresses multiple media per canvas as
612
+ // multiple paintings. The layout (media_layout) has no
613
+ // term and stays viewer-side.
614
+ ...buildExtraPaintings(canvasId, slide),
594
615
  // Georeference Extension annotations for the placed rasters
595
616
  // (§2.10). The layers are map-wide but an annotation is
596
617
  // canvas-scoped, so they are written on the first canvas and
@@ -617,6 +638,48 @@ function buildCanvas(
617
638
  return canvas;
618
639
  }
619
640
 
641
+ /**
642
+ * A slide's extra visible media as further `painting` annotations (V2 of
643
+ * #358). Each extra reuses the primary's body and presentation builders, so
644
+ * captions, credits and alt text travel the same way; extras without a usable
645
+ * url are skipped, matching the viewer.
646
+ */
647
+ function buildExtraPaintings(canvasId: string, slide: StorymapSlide): StorymapManifestAnnotation[] {
648
+ const extras = Array.isArray(slide.media_extra) ? slide.media_extra : [];
649
+ return extras
650
+ .filter((item) => typeof item?.url === "string" && item.url !== "")
651
+ .map((item, index) => ({
652
+ id: `${canvasId}/annotation/extra-${index + 1}`,
653
+ type: "Annotation",
654
+ motivation: "painting",
655
+ ...buildAnnotationPresentation({ media: item }),
656
+ body: buildBody({ media: item }, false),
657
+ target: canvasId,
658
+ }));
659
+ }
660
+
661
+ /**
662
+ * A slide narration as a `motivation: "supplementing"` annotation (V1 of
663
+ * #358). The body is always a `Sound`: narration plays through the
664
+ * dedicated audio element, which never renders video.
665
+ */
666
+ function buildNarration(canvasId: string, slide: StorymapSlide): StorymapManifestAnnotation[] {
667
+ const url = slide.narration?.url;
668
+ if (typeof url !== "string" || url === "" || isDuplicateNarration([slide.media?.url], url)) {
669
+ return [];
670
+ }
671
+ return [
672
+ {
673
+ id: `${canvasId}/annotation/narration`,
674
+ type: "Annotation",
675
+ motivation: "supplementing",
676
+ body: { id: url, type: "Sound" },
677
+ target: canvasId,
678
+ ...writePlaybackTerms(slide.narration ?? {}),
679
+ },
680
+ ];
681
+ }
682
+
620
683
  /**
621
684
  * A map's georeferenced rasters as Georeference Extension annotations
622
685
  * (§2.10).
@@ -855,6 +918,30 @@ function buildCanvasTerms(slide: StorymapSlide): Partial<StorymapManifestCanvas>
855
918
  if (present(slide.media?.sizes)) {
856
919
  terms["storymap:mediaSizes"] = slide.media?.sizes;
857
920
  }
921
+ // View directives that also ride the navPlace properties (which only
922
+ // exist when the slide has lat/lon): the canvas terms are the fallback
923
+ // for image-region-only slides, and the reader prefers navPlace.
924
+ if (typeof slide.location?.rotation === "number") {
925
+ terms["storymap:rotation"] = slide.location.rotation;
926
+ }
927
+ if (present(slide.location?.basemap) && typeof slide.location?.basemap === "string") {
928
+ terms["storymap:basemap"] = slide.location.basemap;
929
+ }
930
+ if (present(slide.location?.filter)) {
931
+ terms["storymap:filter"] = slide.location?.filter;
932
+ }
933
+ if (present(slide.location?.mask)) {
934
+ terms["storymap:mask"] = slide.location?.mask;
935
+ }
936
+ if (typeof slide.slidetimeout === "number") {
937
+ terms["storymap:slidetimeout"] = slide.slidetimeout;
938
+ }
939
+ if (present(slide.imgoverlay)) {
940
+ terms["storymap:imgoverlay"] = slide.imgoverlay;
941
+ }
942
+ if (present(slide.provenance)) {
943
+ terms["storymap:provenance"] = slide.provenance;
944
+ }
858
945
  return terms;
859
946
  }
860
947