@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
@@ -37,6 +37,7 @@
37
37
  <li><a class="button" href="./migration.html">Migration guide</a></li>
38
38
  <li><a class="button" href="./iiif.html">IIIF docs</a></li>
39
39
  <li><a class="button" href="./authoring.html">Authoring</a></li>
40
+ <li><a class="button" href="./events.html">Events</a></li>
40
41
  <li><a class="button" href="./api/index.html">API docs</a></li>
41
42
  <li><a class="button" href="../index.html#help">Help</a></li>
42
43
  </ul>
@@ -75,8 +76,10 @@ value of the storymap&#39;s <code>language</code> option must match the file nam
75
76
  <p>Large images served over the IIIF Image API are rendered so when set to be map_as_image the entire image is shown. When set as cartography the zoom will set so that all the markers fit.</p>
76
77
  <p>Points are set to only display on mouseover in image mode, but you can set map_as_image to false in the config options to always show the points. The points are hidden when the intent is an image so that nothing obstructs the image the viewer is looking at. Looking at a painting is hard with a bunch of points on it.</p>
77
78
  <h2>Map Options</h2>
78
- <p>To disable connecting lines on maps set <code>map_as_image: true</code> in the storymap
79
- options (the default <code>false</code> renders cartography).</p>
79
+ <p>To disable connecting lines on maps set <code>show_lines: false</code> in the storymap
80
+ options (the default <code>true</code> renders cartography with <code>show_history_line</code>).
81
+ <code>map_as_image: true</code> is something else: image-map mode, only meaningful with
82
+ <code>map_type: &quot;iiif&quot;</code> (the image itself becomes the map).</p>
80
83
  <p><code>storymap.map</code> is the raw OpenLayers map, typed with the re-exported <code>ol</code>
81
84
  types, and <code>getBaseLayer()</code>, <code>getOverlayLayers()</code>, <code>getMinimap()</code>,
82
85
  <code>getMarkers()</code> and friends hand out the layers the viewer built. Listen for
@@ -102,6 +105,72 @@ show_lines: true,
102
105
  show_history_line: true,
103
106
  </code></pre>
104
107
  <p>To disable zoom calculation/edit zoom level set calculate_zoom to false in the config options.</p>
108
+ <h3>More data options</h3>
109
+ <p>Beyond the map options above, the storymap data takes <code>overlays[]</code>
110
+ (stacked raster layers over the basemap), <code>tilejson</code> (a TileJSON source for
111
+ the base layer), <code>overview_extent</code> (the overview slide&#39;s bounds),
112
+ <code>keyboard</code> (page-wide arrow-key navigation for multi-viewer pages),
113
+ per-slide <code>marker: { popup, audioBadge }</code> (the active marker&#39;s card, openable
114
+ via <code>storymap.openMarkerPopup(n)</code>, and the narration/audio dot) and
115
+ <code>narration: { url }</code> per slide. <code>autoplay_media</code> is global (wait for slide
116
+ media to end while autoplaying); the per-slide dwell is <code>slidetimeout</code>.
117
+ All are validated
118
+ against <code>schema/storymap.schema.json</code> — read it (or <code>src/types.ts</code>) for the
119
+ full list with defaults.</p>
120
+ <p><code>show_layers_control: true</code> adds a menubar disclosure button listing the
121
+ <code>overlays[]</code> as checkboxes and <code>basemaps[]</code> as basemap radios, so the
122
+ visitor can switch layers without touching code.</p>
123
+ <p><code>theme: &quot;dark&quot;</code> forces the dark palette, <code>&quot;light&quot;</code> pins the light one;
124
+ unset, the widget follows <code>prefers-color-scheme</code>.</p>
125
+ <h3>Slideshow-style tours</h3>
126
+ <p>Slides can carry per-slide presentation for image tours (a stop can restyle
127
+ the view while it is active; everything is optional and validated against
128
+ <code>schema/storymap.schema.json</code>):</p>
129
+ <pre><code>location: {
130
+ region: [x, y, w, h], // image-pixel stop (IIIF xywh); negative origins clamp to the canvas
131
+ rotation: 45, // view rotation in degrees clockwise
132
+ filter: { sepia: 40 }, // brightness/contrast/saturate 0-200 (%), hueRotate -180-180 (°), sepia 0-100 (%), blur 0-20 (px)
133
+ mask: { x: 0.1, y: 0.1, w: 0.8, h: 0.8, opacity: 0.5 }, // spotlight mask (fractions) + color/invert
134
+ basemap: &quot;osm:bright&quot;, // per-slide basemap (any map_type, IIIF info.json URL or image file)
135
+ },
136
+ narration: { url, loop, offset, play: &quot;auto&quot;|&quot;click&quot;, stopOnExit, stopAllPrevious },
137
+ slidetimeout: 9000, // per-slide autoplay dwell in ms (0 holds); default is the global autoplay
138
+ imgoverlay: { url, size, extent, opacity }, // image overlay while the slide is active
139
+ </code></pre>
140
+ <p>Audio/video media take the same <code>offset</code>/<code>loop</code>/<code>play</code>/<code>stopOnExit</code> flags
141
+ on the slide&#39;s <code>media</code> object (<code>stopAllPrevious</code> is narration-only).</p>
142
+ <p>When a slide&#39;s IIIF <code>info.json</code> cannot be loaded, <code>iiif: { width, height, fallbackUrl }</code> paints the static image instead of a blank basemap. Without
143
+ stated dimensions the viewer probes the fallback image&#39;s natural size first,
144
+ but only for slideshow tours (15s timeout); other documents keep the logged
145
+ error path. Per-slide static basemaps (<code>image_srv</code> pointing at a plain
146
+ image file, absolute or same-origin relative) probe the same way, and a
147
+ failed <code>info.json</code> fetch falls back to a probed static image in place.</p>
148
+ <p>The player chrome is opt-in storymap options (absent means the default
149
+ layout): <code>textmode: &quot;left&quot;|&quot;right&quot;|&quot;bottom&quot;</code> with <code>textsize</code> (10–80 %,
150
+ bottom dock only — the player drops side-dock sizes too),
151
+ <code>progressbar: &quot;bar&quot;|&quot;dots&quot;|&quot;squares&quot;|&quot;block&quot;|&quot;thinblock&quot;|&quot;off&quot;</code> (<code>true</code>
152
+ means <code>&quot;bar&quot;</code>, <code>false</code> means hidden),
153
+ <code>fxmode: &quot;slide&quot;|&quot;fade&quot;|&quot;none&quot;</code>, <code>mode: &quot;static&quot;</code> (stacked reading list with
154
+ scroll-spy map sync), <code>hudcolor</code>/<code>hudbgcolor</code>/<code>hudopacity</code>,
155
+ <code>shownav</code>/<code>show_headings</code>/<code>show_scrollbars</code>/<code>show_info</code> toggles and
156
+ <code>viewerheight: &quot;400px&quot;</code>.</p>
157
+ <p><strong>IIIF round-trip:</strong> the per-slide fields do survive — <code>rotation</code>,
158
+ <code>filter</code>, <code>mask</code>, <code>basemap</code>, <code>slidetimeout</code>, <code>imgoverlay</code>, <code>provenance</code>
159
+ and the playback bags travel as <code>storymap:</code> terms (see
160
+ <a href="./iiif.html">docs/storymap-as-iiif-manifest.md</a>).
161
+ The player chrome does <strong>not</strong>: it has no IIIF vocabulary and is
162
+ translator-only (a storymap→manifest→storymap cycle drops it).</p>
163
+ <p>These presentation fields render <strong>only for slideshow tours</strong> (documents
164
+ loaded from that format, which set the internal <code>slideshow_source</code> marker).
165
+ Hand-written storymap documents and IIIF manifests keep the long-standing
166
+ rendering even when they name the new keys — captions always show, the
167
+ autoplay interval stays global, the panel keeps its default dock. Hosts may
168
+ pass <code>slideshow_source: true</code> explicitly as an escape hatch (e.g. in tests).</p>
169
+ <p>Slideshow tours (W3C <code>AnnotationCollection</code> documents) load directly — by
170
+ object, file or URL, following <code>next</code> pages — or convert offline with
171
+ <code>npm run convert:slideshow -- [--settings settings.json] [--out out.json] &lt;tour-url-or-file&gt;</code>
172
+ (see <a href="docs/slideshow.md">docs/slideshow.md</a> for the field mapping,
173
+ player-settings handling and loss table).</p>
105
174
  <p>Images can now be used in place of map pins.
106
175
  Use <code>image</code> inside the location object and include a url to use, together with
107
176
  <code>use_custom_marker: true</code> in the location object (or set <code>use_custom_markers: true</code> globally in the storymap options). Same goes for custom icons except you
@@ -126,18 +195,27 @@ map_type: &quot;https://tiles.openfreemap.org/styles/bright&quot;, // vector st
126
195
  <p>Any value containing <code>{z}</code> renders as a raster XYZ layer; a path without <code>{z}</code>
127
196
  that looks like a URL/path renders as a vector style layer; anything else falls
128
197
  back to classic OSM raster — except <code>&quot;none&quot;</code>, which builds no map at all and
129
- runs the story as text and media only.</p>
198
+ runs the story as text and media only. A full template naming a known provider
199
+ (<code>https://tile.openstreetmap.org/{z}/{x}/{y}.png</code>) is credited to that
200
+ provider; anything else gets the generic &quot;Map data&quot; line (add your own via
201
+ <code>attribution</code>).</p>
130
202
  <h3>Switching the basemap at runtime</h3>
131
203
  <p><code>storymap.setMapOption(&quot;map_type&quot;, next)</code> / <code>setMapOptions({...})</code> rebuilds the
132
204
  main tile layer <strong>and</strong> the minimap (<code>ol/control/OverviewMap</code>) layer, keeping
133
205
  the overview fitted to the marker bounds (zoomify/IIIF extents preserved). The
134
206
  current slide is re-fitted after the swap.</p>
135
207
  <h3>Icons</h3>
136
- <p>Default pins use the bundled <code>vco-icons</code> font (<code>dist/css/icons/</code>, referenced via
137
- relative <code>./icons/...</code> URLs from <code>dist/css/storymap.css</code>), so pins render on
138
- subpath deploys, bundler consumers (Vite leaves absolute <code>/css/...</code> untouched)
139
- and <code>file://</code>/Electron hosts without extra configuration. Import the stylesheet
140
- once (<code>import &quot;@projektemacher/storymapjs/css/storymap.css&quot;</code>).</p>
208
+ <p>Default pins use the bundled <code>vco-icons</code> font (<code>dist/css/icons/</code>). The SCSS
209
+ source uses the absolute public path (<code>/css/icons/...</code>) so <code>vite dev</code> serves
210
+ the fonts directly; <code>vite build</code> rewrites them to relative <code>./icons/...</code> in
211
+ <code>dist/css/storymap.css</code>, so pins render on subpath deploys, for bundler
212
+ consumers and on <code>file://</code>/Electron hosts without extra configuration.
213
+ Import the stylesheet once
214
+ (<code>import &quot;@projektemacher/storymapjs/css/storymap.css&quot;</code>).</p>
215
+ <p>Font themes (<code>font_css: &quot;stock:&lt;name&gt;&quot;</code>) resolve via <code>import.meta.url</code>, so
216
+ they only work script-tagged, unbundled. A bundler consumer imports the theme
217
+ instead (<code>import &quot;@projektemacher/storymapjs/css/fonts/font.default.css&quot;</code>)
218
+ and passes <code>font_css: false</code> for no injected <code>&lt;link&gt;</code>.</p>
141
219
  <h2>Custom HTML in slide content</h2>
142
220
  <p>Slide text is rendered as HTML: the <code>text</code> (and <code>headline</code>) fields accept
143
221
  arbitrary markup. If the text contains no <code>&lt;p&gt;</code> tag, it is wrapped in one
@@ -181,15 +259,38 @@ appear for services discovered later (e.g. a preloaded slide).</p>
181
259
  <p>Decisions are stored in <code>localStorage</code> under <code>storymapjs-consent</code> and have no
182
260
  expiry — clearing site data asks again.</p>
183
261
  <p>The consent labels are <strong>not</strong> fully translated. <code>src/language/locale/en.json</code>
184
- defines every string; 28 bundled locales fall back to English for the keys
185
- they lack — most are missing one, a handful are missing around ten (the
186
- per-service consent prompts and the fullscreen button labels). Run
262
+ defines every string (34 keys across 29 locale files: English plus 28
263
+ translations). German is missing 1 key, most locales are missing 9 (the audio
264
+ badge, autoplay, layer-switcher, iframe-title and narration-consent strings),
265
+ and five locales (<code>et</code>, <code>is</code>, <code>nn</code>, <code>ur</code>, <code>sr</code>) are missing 18–19 (additionally
266
+ the fullscreen, consent-dialog, error and overview strings). Run
187
267
  <code>npm run check:locales</code> to see the current state — it reports the gap per
188
268
  locale and is expected to be runnable without failing.</p>
189
269
  <h2>Troubleshooting</h2>
190
270
  <p>If a storymap fails to render, open the browser console: the viewer logs
191
271
  fetch and validation errors (e.g. &quot;could not load storymap data from ...&quot;).
192
- The <code>error</code> event also fires for programmatic consumers.</p>
272
+ The <code>error</code> event also fires for programmatic consumers. The full event
273
+ reference (payloads included) is <a href="./events.html">docs/events.md</a>.</p>
274
+ <h2>Accessibility</h2>
275
+ <p>Supported, and covered by tests: text alternatives on slide images
276
+ (decorative <code>alt=&quot;&quot;</code> included); real <code>&lt;button&gt;</code> menubar actions and slide
277
+ navigation; polite live-region slide announcements; reduced-motion handling
278
+ of the slide glide and autoplay; an embed page that does not disable zoom;
279
+ sanitized author tables that keep their semantics; slides hidden from
280
+ assistive tech while inactive (<code>aria-hidden</code> + <code>inert</code>), with a layer
281
+ switcher built from native checkboxes, radios and fieldsets; keyboard
282
+ operable map pins and message dismissal; a skip link to the slide content;
283
+ an autoplay pause/resume toggle; slide text pronounced in its own language
284
+ (<code>lang</code>); consent dialogs exposed with labels and focus; media errors
285
+ announced as alerts; and a motion kill-switch that also honors an
286
+ OS reduced-motion toggle flipped mid-story.</p>
287
+ <p>Known limitations (not fixed here): map markers are pointer-only; audio and
288
+ video have no viewer-side captions or transcripts; narration ships without a
289
+ transcript; focus moves only when the layer panel closes with Escape; labels
290
+ and announcements are hardcoded English; several muted text colors fall
291
+ below 4.5:1; third-party embeds are the vendor&#39;s conformance. No VPAT or
292
+ ACR is claimed — §508 conformance is decided per deliverable by an
293
+ external audit.</p>
193
294
  <h2>Bundled credentials</h2>
194
295
  <p>The viewer ships no credentials. Mapbox/Stadia tiles need a token passed via
195
296
  <code>map_access_token</code>, and <code>flickr.com/photos</code> API URLs need <code>api_key_flickr</code>
@@ -63,6 +63,7 @@
63
63
  }
64
64
  .storymap-url-card button {
65
65
  padding: 8px 16px;
66
+ min-height: 44px;
66
67
  font-size: 1rem;
67
68
  border: 1px solid #333;
68
69
  border-radius: 4px;
@@ -128,6 +129,8 @@
128
129
  const showEmbedError = (message) => {
129
130
  const el = document.createElement("div");
130
131
  el.className = "vco-consent-blocked";
132
+ // announce to AT (issue #385 wave)
133
+ el.setAttribute("role", "alert");
131
134
  // inline fallback styles in case the widget CSS did not load
132
135
  el.style.cssText =
133
136
  "display:flex;align-items:center;justify-content:center;width:100%;height:100%;padding:20px;background-color:#fff;color:#333;font-family:sans-serif;text-align:center;";
@@ -173,6 +176,8 @@
173
176
  error.id = "storymap-url-error";
174
177
  error.className = "storymap-url-error";
175
178
  error.hidden = true;
179
+ // the input describes its error slot (issue #385 wave)
180
+ input.setAttribute("aria-describedby", "storymap-url-error");
176
181
  row.append(input, button);
177
182
  form.append(label, row, error);
178
183
  card.append(heading, hint, form);
@@ -191,6 +196,8 @@
191
196
  } catch {
192
197
  error.textContent = "Please enter a valid URL to a storymap.json file.";
193
198
  error.hidden = false;
199
+ error.setAttribute("role", "alert");
200
+ input.setAttribute("aria-invalid", "true");
194
201
  return;
195
202
  }
196
203
  urlParams.set("url", value);
@@ -0,0 +1,33 @@
1
+ {
2
+ "storymap": {
3
+ "map_type": "osm",
4
+ "font_css": false,
5
+ "slides": [
6
+ {
7
+ "type": "overview",
8
+ "date": "",
9
+ "text": {
10
+ "headline": "A story with font loading disabled",
11
+ "text": "The host page bundles its own font theme."
12
+ },
13
+ "media": {
14
+ "url": "",
15
+ "caption": "",
16
+ "credit": ""
17
+ }
18
+ },
19
+ {
20
+ "date": "",
21
+ "text": {
22
+ "headline": "Second slide",
23
+ "text": "<p>No font stylesheet is appended by the viewer.</p>"
24
+ },
25
+ "media": {
26
+ "url": "",
27
+ "caption": "",
28
+ "credit": ""
29
+ }
30
+ }
31
+ ]
32
+ }
33
+ }
@@ -31,7 +31,7 @@
31
31
  "date": "",
32
32
  "text": {
33
33
  "headline": "Torso region",
34
- "text": "StrollView-style image stops via the IIIF xywh convention."
34
+ "text": "Slideshow-style image stops via the IIIF xywh convention."
35
35
  },
36
36
  "location": {
37
37
  "region": [700, 800, 900, 1100]
@@ -0,0 +1,60 @@
1
+ {
2
+ "storymap": {
3
+ "map_type": "osm",
4
+ "keyboard": true,
5
+ "overlays": [
6
+ {
7
+ "map_type": "./tiles/historic/{z}/{x}/{y}.png",
8
+ "opacity": 0.6,
9
+ "attribution": "Historic sheet (demo overlay)"
10
+ },
11
+ {
12
+ "map_type": "./tiles/railway/{z}/{x}/{y}.png",
13
+ "visible": false,
14
+ "attribution": "Railway lines (demo overlay)"
15
+ }
16
+ ],
17
+ "slides": [
18
+ {
19
+ "type": "overview",
20
+ "date": "1890",
21
+ "text": {
22
+ "headline": "Layers control",
23
+ "text": "A menubar disclosure button lists the overlays above so the visitor can toggle them. Arrow keys navigate the story."
24
+ },
25
+ "media": {
26
+ "url": "",
27
+ "caption": "",
28
+ "credit": ""
29
+ },
30
+ "location": { "lat": 51.5074, "lon": -0.1278, "zoom": 10 }
31
+ },
32
+ {
33
+ "date": "1900",
34
+ "text": {
35
+ "headline": "The historic sheet",
36
+ "text": "The first overlay starts visible; unchecking its row hides the layer and drops its credit."
37
+ },
38
+ "media": {
39
+ "url": "",
40
+ "caption": "",
41
+ "credit": ""
42
+ },
43
+ "location": { "lat": 51.5155, "lon": -0.0922, "zoom": 12, "line": true }
44
+ },
45
+ {
46
+ "date": "1910",
47
+ "text": {
48
+ "headline": "The railway lines",
49
+ "text": "The second overlay starts hidden; checking its row paints it above the basemap."
50
+ },
51
+ "media": {
52
+ "url": "",
53
+ "caption": "",
54
+ "credit": ""
55
+ },
56
+ "location": { "lat": 51.4818, "lon": -0.0046, "zoom": 11, "line": true }
57
+ }
58
+ ]
59
+ }
60
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "storymap": {
3
+ "map_type": "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
4
+ "slides": [
5
+ {
6
+ "type": "overview",
7
+ "date": "",
8
+ "text": {
9
+ "headline": "A longhand OSM template",
10
+ "text": "The base layer names OpenStreetMap by its full tile template, and the credit line still credits it."
11
+ },
12
+ "media": {
13
+ "url": "",
14
+ "caption": "",
15
+ "credit": ""
16
+ }
17
+ },
18
+ {
19
+ "date": "",
20
+ "text": {
21
+ "headline": "Paris",
22
+ "text": "<p>A stop on a template-credited map.</p>"
23
+ },
24
+ "location": {
25
+ "lat": 48.85,
26
+ "lon": 2.35
27
+ },
28
+ "media": {
29
+ "url": "",
30
+ "caption": "",
31
+ "credit": ""
32
+ }
33
+ }
34
+ ]
35
+ }
36
+ }
@@ -0,0 +1,71 @@
1
+ {
2
+ "storymap": {
3
+ "call_to_action": false,
4
+ "map_as_image": false,
5
+ "map_type": "osm",
6
+ "slides": [
7
+ {
8
+ "date": "",
9
+ "type": "overview",
10
+ "text": {
11
+ "headline": "Multiple media per slide",
12
+ "text": "A slide can carry extra visible media items after its primary one."
13
+ },
14
+ "media": {
15
+ "url": "/examples/assets/placeholder-landscape.jpg",
16
+ "caption": "Primary media",
17
+ "credit": ""
18
+ }
19
+ },
20
+ {
21
+ "date": "",
22
+ "text": {
23
+ "headline": "Side by side",
24
+ "text": "Two items with media_layout row sit next to each other."
25
+ },
26
+ "location": {
27
+ "lat": 48.85,
28
+ "lon": 2.35,
29
+ "zoom": 6
30
+ },
31
+ "media": {
32
+ "url": "/examples/assets/placeholder-city.jpg",
33
+ "caption": "First item",
34
+ "credit": ""
35
+ },
36
+ "media_extra": [
37
+ {
38
+ "url": "https://example.com/iframe/embed/123",
39
+ "caption": "Second item",
40
+ "credit": ""
41
+ }
42
+ ],
43
+ "media_layout": "row"
44
+ },
45
+ {
46
+ "date": "",
47
+ "text": {
48
+ "headline": "Stacked with audio",
49
+ "text": "Items pile vertically by default, and an audible extra badges the marker."
50
+ },
51
+ "location": {
52
+ "lat": 52.52,
53
+ "lon": 13.4,
54
+ "zoom": 6
55
+ },
56
+ "media": {
57
+ "url": "/examples/assets/placeholder-portrait.jpg",
58
+ "caption": "First item",
59
+ "credit": ""
60
+ },
61
+ "media_extra": [
62
+ {
63
+ "url": "/examples/assets/tone.mp3",
64
+ "caption": "Second item",
65
+ "credit": ""
66
+ }
67
+ ]
68
+ }
69
+ ]
70
+ }
71
+ }
@@ -179,6 +179,22 @@
179
179
  "y": 1500
180
180
  }
181
181
  }
182
+ },
183
+ {
184
+ "id": "https://example.org/storymap/annotated-image/canvas/1/annotation/105",
185
+ "type": "Annotation",
186
+ "motivation": "supplementing",
187
+ "body": {
188
+ "id": "https://example.org/audio/tour-narration.mp3",
189
+ "type": "Sound",
190
+ "format": "audio/mpeg",
191
+ "label": {
192
+ "none": [
193
+ "Spoken tour of the panel"
194
+ ]
195
+ }
196
+ },
197
+ "target": "https://example.org/storymap/annotated-image/canvas/1"
182
198
  }
183
199
  ]
184
200
  }
@@ -0,0 +1,114 @@
1
+ {
2
+ "@context": [
3
+ "http://iiif.io/api/extension/navplace/context.json",
4
+ "http://iiif.io/api/presentation/3/context.json",
5
+ "https://cmahnke.github.io/StoryMapJS/navplace-properties.json",
6
+ "https://cmahnke.github.io/StoryMapJS/context.json"
7
+ ],
8
+ "id": "https://example.org/storymap/font-css-false",
9
+ "type": "Manifest",
10
+ "label": {
11
+ "none": [
12
+ "font-css-false"
13
+ ]
14
+ },
15
+ "behavior": [
16
+ "paged"
17
+ ],
18
+ "metadata": [
19
+ {
20
+ "label": {
21
+ "none": [
22
+ "Generated by"
23
+ ]
24
+ },
25
+ "value": {
26
+ "none": [
27
+ "StoryMapJS"
28
+ ]
29
+ }
30
+ }
31
+ ],
32
+ "items": [
33
+ {
34
+ "id": "https://example.org/storymap/font-css-false/canvas/1",
35
+ "type": "Canvas",
36
+ "height": 1080,
37
+ "width": 1080,
38
+ "items": [
39
+ {
40
+ "id": "https://example.org/storymap/font-css-false/canvas/1/annotationpage/1",
41
+ "type": "AnnotationPage",
42
+ "items": [
43
+ {
44
+ "id": "https://example.org/storymap/font-css-false/canvas/1/annotation/1",
45
+ "type": "Annotation",
46
+ "motivation": "painting",
47
+ "body": {
48
+ "type": "TextualBody",
49
+ "format": "text/html",
50
+ "value": "The host page bundles its own font theme."
51
+ },
52
+ "target": "https://example.org/storymap/font-css-false/canvas/1"
53
+ }
54
+ ]
55
+ }
56
+ ],
57
+ "label": {
58
+ "none": [
59
+ "A story with font loading disabled"
60
+ ]
61
+ },
62
+ "summary": {
63
+ "none": [
64
+ "The host page bundles its own font theme."
65
+ ]
66
+ },
67
+ "storymap:type": "overview"
68
+ },
69
+ {
70
+ "id": "https://example.org/storymap/font-css-false/canvas/2",
71
+ "type": "Canvas",
72
+ "height": 1080,
73
+ "width": 1080,
74
+ "items": [
75
+ {
76
+ "id": "https://example.org/storymap/font-css-false/canvas/2/annotationpage/1",
77
+ "type": "AnnotationPage",
78
+ "items": [
79
+ {
80
+ "id": "https://example.org/storymap/font-css-false/canvas/2/annotation/1",
81
+ "type": "Annotation",
82
+ "motivation": "painting",
83
+ "body": {
84
+ "type": "TextualBody",
85
+ "format": "text/html",
86
+ "value": "<p>No font stylesheet is appended by the viewer.</p>"
87
+ },
88
+ "target": "https://example.org/storymap/font-css-false/canvas/2"
89
+ }
90
+ ]
91
+ }
92
+ ],
93
+ "label": {
94
+ "none": [
95
+ "Second slide"
96
+ ]
97
+ },
98
+ "summary": {
99
+ "none": [
100
+ "<p>No font stylesheet is appended by the viewer.</p>"
101
+ ]
102
+ }
103
+ }
104
+ ],
105
+ "service": [
106
+ {
107
+ "id": "https://example.org/storymap/font-css-false/map-config",
108
+ "type": "Service",
109
+ "profile": "https://cmahnke.github.io/StoryMapJS/context.json/mapconfig",
110
+ "storymap:basemap": "osm",
111
+ "storymap:fontCss": false
112
+ }
113
+ ]
114
+ }
@@ -173,7 +173,7 @@
173
173
  },
174
174
  "summary": {
175
175
  "none": [
176
- "StrollView-style image stops via the IIIF xywh convention."
176
+ "Slideshow-style image stops via the IIIF xywh convention."
177
177
  ]
178
178
  }
179
179
  }