@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.
- package/CHANGELOG +81 -0
- package/README.md +129 -12
- package/dist/assets/{demo-DBpeF31K.js → demo-bWnOyfUw.js} +1 -1
- package/dist/assets/harness-C3JUv5Ii.js +1 -0
- package/dist/assets/{harness-multi-CYHgI0a9.js → harness-multi-XZNex3tb.js} +1 -1
- package/dist/assets/{harness-support-Do7sIp2j.js → harness-support-CjfZIVeb.js} +1 -1
- package/dist/assets/main-DQ32hWBM.js +17 -0
- package/dist/context.json +13 -2
- package/dist/css/storymap.css +1 -1
- package/dist/demo.html +3 -2
- package/dist/docs/api/assets/navigation.js +1 -1
- package/dist/docs/api/assets/search.js +1 -1
- package/dist/docs/api/classes/StoryMap.html +777 -269
- package/dist/docs/api/functions/MediaType.html +1 -1
- package/dist/docs/api/functions/isPresentation3Collection.html +1 -1
- package/dist/docs/api/functions/isPresentation3Manifest.html +1 -1
- package/dist/docs/api/functions/isSlideshowCollection.html +51 -0
- package/dist/docs/api/functions/loadCSS.html +1 -1
- package/dist/docs/api/functions/manifestToStorymapData.html +1 -1
- package/dist/docs/api/functions/setLanguage.html +1 -1
- package/dist/docs/api/functions/slideshowToStorymapData.html +51 -0
- package/dist/docs/api/functions/slideshowVersion.html +48 -0
- package/dist/docs/api/functions/storymapToManifest.html +1 -1
- package/dist/docs/api/functions/validateStorymap.html +1 -1
- package/dist/docs/api/functions/validateStorymapAndReport.html +1 -1
- package/dist/docs/api/index.html +115 -12
- package/dist/docs/api/interfaces/AnimationHandle.html +2 -2
- package/dist/docs/api/interfaces/ImagereadyPayload.html +4 -4
- package/dist/docs/api/interfaces/LanguageEntry.html +6 -6
- package/dist/docs/api/interfaces/LatLngLiteral.html +4 -4
- package/dist/docs/api/interfaces/LoadOptions.html +2 -2
- package/dist/docs/api/interfaces/MapEvents.html +116 -0
- package/dist/docs/api/interfaces/MapMarkerEvents.html +66 -0
- package/dist/docs/api/interfaces/MarkerEventPayload.html +57 -0
- package/dist/docs/api/interfaces/MediaEvents.html +78 -0
- package/dist/docs/api/interfaces/MenuBarEvents.html +114 -0
- package/dist/docs/api/interfaces/MessageEvents.html +72 -0
- package/dist/docs/api/interfaces/OlLayer.html +1 -1
- package/dist/docs/api/interfaces/OlMap.html +1 -1
- package/dist/docs/api/interfaces/OlSource.html +1 -1
- package/dist/docs/api/interfaces/OlTileLayer.html +1 -1
- package/dist/docs/api/interfaces/OlVectorLayer.html +1 -1
- package/dist/docs/api/interfaces/OlView.html +1 -1
- package/dist/docs/api/interfaces/SlideBackgroundState.html +66 -0
- package/dist/docs/api/interfaces/SlideEvents.html +78 -0
- package/dist/docs/api/interfaces/SlideNavEvents.html +72 -0
- package/dist/docs/api/interfaces/SlideshowPlayerSettings.html +182 -0
- package/dist/docs/api/interfaces/SlideshowToStorymapResult.html +60 -0
- package/dist/docs/api/interfaces/SlideshowTranslatorOptions.html +65 -0
- package/dist/docs/api/interfaces/SlideshowWarning.html +72 -0
- package/dist/docs/api/interfaces/StoryMapEvents.html +146 -0
- package/dist/docs/api/interfaces/StoryMapMarker.html +763 -0
- package/dist/docs/api/interfaces/StorySliderEvents.html +102 -0
- package/dist/docs/api/interfaces/StorymapBasemap.html +69 -0
- package/dist/docs/api/interfaces/StorymapData.html +12 -6
- package/dist/docs/api/interfaces/StorymapDataWrapper.html +2 -2
- package/dist/docs/api/interfaces/StorymapDocument.html +2 -2
- package/dist/docs/api/interfaces/StorymapError.html +3 -3
- package/dist/docs/api/interfaces/StorymapGeoreference.html +5 -5
- package/dist/docs/api/interfaces/StorymapGeoreferenceBody.html +4 -4
- package/dist/docs/api/interfaces/StorymapGroundControlPoint.html +4 -4
- package/dist/docs/api/interfaces/StorymapLanguageMap.html +2 -2
- package/dist/docs/api/interfaces/StorymapManifest.html +12 -12
- package/dist/docs/api/interfaces/StorymapManifestAnnotation.html +43 -11
- package/dist/docs/api/interfaces/StorymapManifestBackground.html +6 -6
- package/dist/docs/api/interfaces/StorymapManifestCanvas.html +65 -17
- package/dist/docs/api/interfaces/StorymapManifestContentResource.html +8 -8
- package/dist/docs/api/interfaces/StorymapManifestFeatureCollection.html +4 -4
- package/dist/docs/api/interfaces/StorymapManifestGeoreferencing.html +7 -7
- package/dist/docs/api/interfaces/StorymapManifestImageService.html +4 -4
- package/dist/docs/api/interfaces/StorymapManifestRange.html +5 -5
- package/dist/docs/api/interfaces/StorymapManifestService.html +4 -4
- package/dist/docs/api/interfaces/StorymapManifestSpecificResource.html +4 -4
- package/dist/docs/api/interfaces/StorymapManifestStatement.html +3 -3
- package/dist/docs/api/interfaces/StorymapMapOptions.html +5 -5
- package/dist/docs/api/interfaces/StorymapOptions.html +277 -75
- package/dist/docs/api/interfaces/StorymapOverlayLayer.html +42 -11
- package/dist/docs/api/interfaces/StorymapSlide.html +70 -16
- package/dist/docs/api/interfaces/StorymapSlideBackground.html +4 -4
- package/dist/docs/api/interfaces/StorymapSlideLocation.html +58 -14
- package/dist/docs/api/interfaces/StorymapSlideMarker.html +8 -9
- package/dist/docs/api/interfaces/StorymapSlideMedia.html +49 -10
- package/dist/docs/api/interfaces/StorymapSlideNarration.html +61 -9
- package/dist/docs/api/interfaces/StorymapSlideText.html +4 -4
- package/dist/docs/api/interfaces/StorymapTilejson.html +8 -8
- package/dist/docs/api/interfaces/SwipableEvents.html +102 -0
- package/dist/docs/api/interfaces/TextEvents.html +66 -0
- package/dist/docs/api/media/events.md +66 -0
- package/dist/docs/api/media/migration-from-knightlab.md +89 -49
- package/dist/docs/api/media/slideshow.md +109 -0
- package/dist/docs/api/media/storymap-as-iiif-manifest.md +1007 -0
- package/dist/docs/api/modules.html +6 -6
- package/dist/docs/api/types/ContentState.html +3 -3
- package/dist/docs/api/types/FiredEvent.html +43 -0
- package/dist/docs/api/types/ImagereadyKind.html +1 -1
- package/dist/docs/api/types/MediaTypeMatch.html +5 -5
- package/dist/docs/api/types/SwipeDirection.html +36 -0
- package/dist/docs/api/types/TileSourceFactory.html +1 -1
- package/dist/docs/authoring.html +20 -7
- package/dist/docs/events.html +263 -0
- package/dist/docs/iiif.html +136 -22
- package/dist/docs/migration.html +66 -19
- package/dist/docs/readme.html +113 -12
- package/dist/embed/index.html +7 -0
- package/dist/examples/font-css-false.json +33 -0
- package/dist/examples/issue-image-region.json +1 -1
- package/dist/examples/layers-control.json +60 -0
- package/dist/examples/map-template-provider.json +36 -0
- package/dist/examples/multi-media.json +71 -0
- package/dist/examples-iiif/annotated-image.json +16 -0
- package/dist/examples-iiif/font-css-false.json +114 -0
- package/dist/examples-iiif/issue-image-region.json +1 -1
- package/dist/examples-iiif/layers-control.json +226 -0
- package/dist/examples-iiif/map-template-provider.json +133 -0
- package/dist/examples-iiif/multi-media.json +237 -0
- package/dist/examples-slideshow/single.json +79 -0
- package/dist/examples-slideshow/static/pixel.png +0 -0
- package/dist/examples-slideshow/static.json +44 -0
- package/dist/harness-multi.html +3 -3
- package/dist/harness.html +4 -3
- package/dist/index.html +3 -2
- package/dist/js/storymap.d.ts +1326 -289
- package/dist/js/storymap.js +6690 -4784
- package/dist/js/storymap.js.map +1 -1
- package/dist/navplace-properties.json +14 -0
- package/dist/site.css +54 -36
- package/docs/migration-from-knightlab.md +89 -49
- package/docs/storymap-as-iiif-manifest.md +199 -126
- package/package.json +4 -2
- package/schema/storymap.schema.json +334 -34
- package/src/core/Util.ts +3 -1
- package/src/core/mixins.ts +42 -28
- package/src/language/Language.ts +7 -0
- package/src/language/locale/.expected-gaps.json +287 -27
- package/src/language/locale/de.json +11 -1
- package/src/language/locale/en.json +11 -1
- package/src/main.ts +51 -0
- package/src/map/Map.ts +44 -6
- package/src/map/MapMarker.ts +16 -3
- package/src/map/openlayers/Map.OpenLayers.ts +787 -62
- package/src/map/openlayers/MapMarker.OpenLayers.ts +113 -13
- package/src/map/slideView.ts +149 -0
- package/src/map/types.ts +2 -0
- package/src/media/EmbedUtil.ts +27 -2
- package/src/media/Media.ts +27 -4
- package/src/media/embedId.ts +6 -0
- package/src/media/types/Flickr.ts +14 -3
- package/src/media/types/HtmlMedia.ts +66 -0
- package/src/media/types/Image.ts +15 -3
- package/src/media/types/SoundCloud.ts +1 -1
- package/src/media/types/Text.ts +27 -8
- package/src/media/types/Twitter.ts +9 -7
- package/src/media/types/Vimeo.ts +1 -1
- package/src/media/types/YouTube.ts +2 -2
- package/src/scss/Typography.scss +15 -15
- package/src/scss/VCO.StoryMap.scss +173 -8
- package/src/scss/core/Reset.scss +5 -18
- package/src/scss/map/VCO.Map.scss +1 -1
- package/src/scss/map/VCO.MapMarker.scss +20 -9
- package/src/scss/map/openlayers/VCO.Map.OpenLayers.scss +7 -4
- package/src/scss/media/VCO.Media.scss +7 -7
- package/src/scss/media/types/VCO.Media.Blockquote.scss +1 -1
- package/src/scss/media/types/VCO.Media.Text.scss +5 -5
- package/src/scss/media/types/VCO.Media.Twitter.scss +4 -4
- package/src/scss/media/types/VCO.Media.Wikipedia.scss +2 -19
- package/src/scss/site/site.scss +55 -36
- package/src/scss/slider/VCO.Slide.scss +49 -11
- package/src/scss/slider/VCO.SlideNav.scss +23 -7
- package/src/scss/slider/VCO.StorySlider.scss +50 -0
- package/src/scss/snapmap/VCO.SnapMap.scss +3 -3
- package/src/scss/ui/VCO.Consent.scss +20 -14
- package/src/scss/ui/VCO.Layers.scss +119 -0
- package/src/scss/ui/VCO.MenuBar.Button.scss +140 -19
- package/src/scss/ui/VCO.MenuBar.scss +9 -2
- package/src/scss/ui/VCO.Message.scss +14 -0
- package/src/slider/Slide.ts +232 -74
- package/src/slider/SlideNav.ts +15 -4
- package/src/slider/StorySlider.ts +203 -22
- package/src/storymap/Consent.ts +22 -0
- package/src/storymap/StoryMap.ts +944 -131
- package/src/storymap/content-state.ts +3 -8
- package/src/storymap/font-css.ts +72 -0
- package/src/storymap/from-slideshow.ts +785 -0
- package/src/storymap/iiif-shared.ts +228 -0
- package/src/storymap/iiif.ts +289 -100
- package/src/storymap/to-iiif.ts +131 -44
- package/src/types.ts +382 -17
- package/src/ui/LayersControl.ts +302 -0
- package/src/ui/MenuBar.ts +232 -21
- package/src/ui/Message.ts +36 -3
- package/src/ui/Swipable.ts +25 -7
- package/dist/assets/harness-Cr0rYpvt.js +0 -1
- package/dist/assets/main-DXalctuR.js +0 -17
- package/src/scss/VCO.StoryMap.Dark.scss +0 -150
- package/src/scss/Variables.Dark.scss +0 -57
package/src/storymap/to-iiif.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|