@projektemacher/storymapjs 0.10.7 → 0.10.8

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 (532) hide show
  1. package/CHANGELOG +350 -19
  2. package/README.md +43 -9
  3. package/dist/assets/demo-DBpeF31K.js +1 -0
  4. package/dist/assets/harness-Cr0rYpvt.js +1 -0
  5. package/dist/assets/harness-multi-CYHgI0a9.js +1 -0
  6. package/dist/assets/harness-support-Do7sIp2j.js +1 -0
  7. package/dist/assets/index-wHPqGrBS.js +1 -0
  8. package/dist/assets/main-DXalctuR.js +17 -0
  9. package/dist/context.json +12 -9
  10. package/dist/css/fonts/files/amatic-sc-cyrillic-400-normal.woff2 +0 -0
  11. package/dist/css/fonts/files/amatic-sc-cyrillic-700-normal.woff2 +0 -0
  12. package/dist/css/fonts/files/amatic-sc-hebrew-400-normal.woff2 +0 -0
  13. package/dist/css/fonts/files/amatic-sc-hebrew-700-normal.woff2 +0 -0
  14. package/dist/css/fonts/files/amatic-sc-vietnamese-400-normal.woff2 +0 -0
  15. package/dist/css/fonts/files/amatic-sc-vietnamese-700-normal.woff2 +0 -0
  16. package/dist/css/fonts/files/bitter-cyrillic-100-italic.woff2 +0 -0
  17. package/dist/css/fonts/files/bitter-cyrillic-100-normal.woff2 +0 -0
  18. package/dist/css/fonts/files/bitter-cyrillic-200-italic.woff2 +0 -0
  19. package/dist/css/fonts/files/bitter-cyrillic-200-normal.woff2 +0 -0
  20. package/dist/css/fonts/files/bitter-cyrillic-300-italic.woff2 +0 -0
  21. package/dist/css/fonts/files/bitter-cyrillic-300-normal.woff2 +0 -0
  22. package/dist/css/fonts/files/bitter-cyrillic-400-italic.woff2 +0 -0
  23. package/dist/css/fonts/files/bitter-cyrillic-400-normal.woff2 +0 -0
  24. package/dist/css/fonts/files/bitter-cyrillic-500-italic.woff2 +0 -0
  25. package/dist/css/fonts/files/bitter-cyrillic-500-normal.woff2 +0 -0
  26. package/dist/css/fonts/files/bitter-cyrillic-600-italic.woff2 +0 -0
  27. package/dist/css/fonts/files/bitter-cyrillic-600-normal.woff2 +0 -0
  28. package/dist/css/fonts/files/bitter-cyrillic-700-italic.woff2 +0 -0
  29. package/dist/css/fonts/files/bitter-cyrillic-700-normal.woff2 +0 -0
  30. package/dist/css/fonts/files/bitter-cyrillic-800-italic.woff2 +0 -0
  31. package/dist/css/fonts/files/bitter-cyrillic-800-normal.woff2 +0 -0
  32. package/dist/css/fonts/files/bitter-cyrillic-900-italic.woff2 +0 -0
  33. package/dist/css/fonts/files/bitter-cyrillic-900-normal.woff2 +0 -0
  34. package/dist/css/fonts/files/bitter-cyrillic-ext-100-italic.woff2 +0 -0
  35. package/dist/css/fonts/files/bitter-cyrillic-ext-100-normal.woff2 +0 -0
  36. package/dist/css/fonts/files/bitter-cyrillic-ext-200-italic.woff2 +0 -0
  37. package/dist/css/fonts/files/bitter-cyrillic-ext-200-normal.woff2 +0 -0
  38. package/dist/css/fonts/files/bitter-cyrillic-ext-300-italic.woff2 +0 -0
  39. package/dist/css/fonts/files/bitter-cyrillic-ext-300-normal.woff2 +0 -0
  40. package/dist/css/fonts/files/bitter-cyrillic-ext-400-italic.woff2 +0 -0
  41. package/dist/css/fonts/files/bitter-cyrillic-ext-400-normal.woff2 +0 -0
  42. package/dist/css/fonts/files/bitter-cyrillic-ext-500-italic.woff2 +0 -0
  43. package/dist/css/fonts/files/bitter-cyrillic-ext-500-normal.woff2 +0 -0
  44. package/dist/css/fonts/files/bitter-cyrillic-ext-600-italic.woff2 +0 -0
  45. package/dist/css/fonts/files/bitter-cyrillic-ext-600-normal.woff2 +0 -0
  46. package/dist/css/fonts/files/bitter-cyrillic-ext-700-italic.woff2 +0 -0
  47. package/dist/css/fonts/files/bitter-cyrillic-ext-700-normal.woff2 +0 -0
  48. package/dist/css/fonts/files/bitter-cyrillic-ext-800-italic.woff2 +0 -0
  49. package/dist/css/fonts/files/bitter-cyrillic-ext-800-normal.woff2 +0 -0
  50. package/dist/css/fonts/files/bitter-cyrillic-ext-900-italic.woff2 +0 -0
  51. package/dist/css/fonts/files/bitter-cyrillic-ext-900-normal.woff2 +0 -0
  52. package/dist/css/fonts/files/bitter-cyrillic-ext-wght-italic.woff2 +0 -0
  53. package/dist/css/fonts/files/bitter-cyrillic-ext-wght-normal.woff2 +0 -0
  54. package/dist/css/fonts/files/bitter-cyrillic-wght-italic.woff2 +0 -0
  55. package/dist/css/fonts/files/bitter-cyrillic-wght-normal.woff2 +0 -0
  56. package/dist/css/fonts/files/bitter-vietnamese-100-italic.woff2 +0 -0
  57. package/dist/css/fonts/files/bitter-vietnamese-100-normal.woff2 +0 -0
  58. package/dist/css/fonts/files/bitter-vietnamese-200-italic.woff2 +0 -0
  59. package/dist/css/fonts/files/bitter-vietnamese-200-normal.woff2 +0 -0
  60. package/dist/css/fonts/files/bitter-vietnamese-300-italic.woff2 +0 -0
  61. package/dist/css/fonts/files/bitter-vietnamese-300-normal.woff2 +0 -0
  62. package/dist/css/fonts/files/bitter-vietnamese-400-italic.woff2 +0 -0
  63. package/dist/css/fonts/files/bitter-vietnamese-400-normal.woff2 +0 -0
  64. package/dist/css/fonts/files/bitter-vietnamese-500-italic.woff2 +0 -0
  65. package/dist/css/fonts/files/bitter-vietnamese-500-normal.woff2 +0 -0
  66. package/dist/css/fonts/files/bitter-vietnamese-600-italic.woff2 +0 -0
  67. package/dist/css/fonts/files/bitter-vietnamese-600-normal.woff2 +0 -0
  68. package/dist/css/fonts/files/bitter-vietnamese-700-italic.woff2 +0 -0
  69. package/dist/css/fonts/files/bitter-vietnamese-700-normal.woff2 +0 -0
  70. package/dist/css/fonts/files/bitter-vietnamese-800-italic.woff2 +0 -0
  71. package/dist/css/fonts/files/bitter-vietnamese-800-normal.woff2 +0 -0
  72. package/dist/css/fonts/files/bitter-vietnamese-900-italic.woff2 +0 -0
  73. package/dist/css/fonts/files/bitter-vietnamese-900-normal.woff2 +0 -0
  74. package/dist/css/fonts/files/bitter-vietnamese-wght-italic.woff2 +0 -0
  75. package/dist/css/fonts/files/bitter-vietnamese-wght-normal.woff2 +0 -0
  76. package/dist/css/fonts/files/cairo-arabic-200-normal.woff2 +0 -0
  77. package/dist/css/fonts/files/cairo-arabic-300-normal.woff2 +0 -0
  78. package/dist/css/fonts/files/cairo-arabic-400-normal.woff2 +0 -0
  79. package/dist/css/fonts/files/cairo-arabic-500-normal.woff2 +0 -0
  80. package/dist/css/fonts/files/cairo-arabic-600-normal.woff2 +0 -0
  81. package/dist/css/fonts/files/cairo-arabic-700-normal.woff2 +0 -0
  82. package/dist/css/fonts/files/cairo-arabic-800-normal.woff2 +0 -0
  83. package/dist/css/fonts/files/cairo-arabic-900-normal.woff2 +0 -0
  84. package/dist/css/fonts/files/cairo-latin-200-normal.woff2 +0 -0
  85. package/dist/css/fonts/files/cairo-latin-300-normal.woff2 +0 -0
  86. package/dist/css/fonts/files/cairo-latin-400-normal.woff2 +0 -0
  87. package/dist/css/fonts/files/cairo-latin-500-normal.woff2 +0 -0
  88. package/dist/css/fonts/files/cairo-latin-600-normal.woff2 +0 -0
  89. package/dist/css/fonts/files/cairo-latin-700-normal.woff2 +0 -0
  90. package/dist/css/fonts/files/cairo-latin-800-normal.woff2 +0 -0
  91. package/dist/css/fonts/files/cairo-latin-900-normal.woff2 +0 -0
  92. package/dist/css/fonts/files/cairo-latin-ext-200-normal.woff2 +0 -0
  93. package/dist/css/fonts/files/cairo-latin-ext-300-normal.woff2 +0 -0
  94. package/dist/css/fonts/files/cairo-latin-ext-400-normal.woff2 +0 -0
  95. package/dist/css/fonts/files/cairo-latin-ext-500-normal.woff2 +0 -0
  96. package/dist/css/fonts/files/cairo-latin-ext-600-normal.woff2 +0 -0
  97. package/dist/css/fonts/files/cairo-latin-ext-700-normal.woff2 +0 -0
  98. package/dist/css/fonts/files/cairo-latin-ext-800-normal.woff2 +0 -0
  99. package/dist/css/fonts/files/cairo-latin-ext-900-normal.woff2 +0 -0
  100. package/dist/css/fonts/files/dancing-script-vietnamese-wght-normal.woff2 +0 -0
  101. package/dist/css/fonts/files/eb-garamond-cyrillic-ext-wght-italic.woff2 +0 -0
  102. package/dist/css/fonts/files/eb-garamond-cyrillic-ext-wght-normal.woff2 +0 -0
  103. package/dist/css/fonts/files/eb-garamond-cyrillic-wght-italic.woff2 +0 -0
  104. package/dist/css/fonts/files/eb-garamond-cyrillic-wght-normal.woff2 +0 -0
  105. package/dist/css/fonts/files/eb-garamond-greek-ext-wght-italic.woff2 +0 -0
  106. package/dist/css/fonts/files/eb-garamond-greek-ext-wght-normal.woff2 +0 -0
  107. package/dist/css/fonts/files/eb-garamond-greek-wght-italic.woff2 +0 -0
  108. package/dist/css/fonts/files/eb-garamond-greek-wght-normal.woff2 +0 -0
  109. package/dist/css/fonts/files/eb-garamond-vietnamese-wght-italic.woff2 +0 -0
  110. package/dist/css/fonts/files/eb-garamond-vietnamese-wght-normal.woff2 +0 -0
  111. package/dist/css/fonts/files/gentium-book-plus-cyrillic-400-italic.woff2 +0 -0
  112. package/dist/css/fonts/files/gentium-book-plus-cyrillic-400-normal.woff2 +0 -0
  113. package/dist/css/fonts/files/gentium-book-plus-cyrillic-700-italic.woff2 +0 -0
  114. package/dist/css/fonts/files/gentium-book-plus-cyrillic-700-normal.woff2 +0 -0
  115. package/dist/css/fonts/files/gentium-book-plus-cyrillic-ext-400-italic.woff2 +0 -0
  116. package/dist/css/fonts/files/gentium-book-plus-cyrillic-ext-400-normal.woff2 +0 -0
  117. package/dist/css/fonts/files/gentium-book-plus-cyrillic-ext-700-italic.woff2 +0 -0
  118. package/dist/css/fonts/files/gentium-book-plus-cyrillic-ext-700-normal.woff2 +0 -0
  119. package/dist/css/fonts/files/gentium-book-plus-greek-400-italic.woff2 +0 -0
  120. package/dist/css/fonts/files/gentium-book-plus-greek-400-normal.woff2 +0 -0
  121. package/dist/css/fonts/files/gentium-book-plus-greek-700-italic.woff2 +0 -0
  122. package/dist/css/fonts/files/gentium-book-plus-greek-700-normal.woff2 +0 -0
  123. package/dist/css/fonts/files/gentium-book-plus-greek-ext-400-italic.woff2 +0 -0
  124. package/dist/css/fonts/files/gentium-book-plus-greek-ext-400-normal.woff2 +0 -0
  125. package/dist/css/fonts/files/gentium-book-plus-greek-ext-700-italic.woff2 +0 -0
  126. package/dist/css/fonts/files/gentium-book-plus-greek-ext-700-normal.woff2 +0 -0
  127. package/dist/css/fonts/files/gentium-book-plus-vietnamese-400-italic.woff2 +0 -0
  128. package/dist/css/fonts/files/gentium-book-plus-vietnamese-400-normal.woff2 +0 -0
  129. package/dist/css/fonts/files/gentium-book-plus-vietnamese-700-italic.woff2 +0 -0
  130. package/dist/css/fonts/files/gentium-book-plus-vietnamese-700-normal.woff2 +0 -0
  131. package/dist/css/fonts/files/old-standard-tt-cyrillic-400-italic.woff2 +0 -0
  132. package/dist/css/fonts/files/old-standard-tt-cyrillic-400-normal.woff2 +0 -0
  133. package/dist/css/fonts/files/old-standard-tt-cyrillic-700-normal.woff2 +0 -0
  134. package/dist/css/fonts/files/old-standard-tt-cyrillic-ext-400-italic.woff2 +0 -0
  135. package/dist/css/fonts/files/old-standard-tt-cyrillic-ext-400-normal.woff2 +0 -0
  136. package/dist/css/fonts/files/old-standard-tt-cyrillic-ext-700-normal.woff2 +0 -0
  137. package/dist/css/fonts/files/old-standard-tt-vietnamese-400-italic.woff2 +0 -0
  138. package/dist/css/fonts/files/old-standard-tt-vietnamese-400-normal.woff2 +0 -0
  139. package/dist/css/fonts/files/old-standard-tt-vietnamese-700-normal.woff2 +0 -0
  140. package/dist/css/fonts/files/open-sans-cyrillic-ext-wght-italic.woff2 +0 -0
  141. package/dist/css/fonts/files/open-sans-cyrillic-ext-wght-normal.woff2 +0 -0
  142. package/dist/css/fonts/files/open-sans-cyrillic-wght-italic.woff2 +0 -0
  143. package/dist/css/fonts/files/open-sans-cyrillic-wght-normal.woff2 +0 -0
  144. package/dist/css/fonts/files/open-sans-greek-ext-wght-italic.woff2 +0 -0
  145. package/dist/css/fonts/files/open-sans-greek-ext-wght-normal.woff2 +0 -0
  146. package/dist/css/fonts/files/open-sans-greek-wght-italic.woff2 +0 -0
  147. package/dist/css/fonts/files/open-sans-greek-wght-normal.woff2 +0 -0
  148. package/dist/css/fonts/files/open-sans-hebrew-wght-italic.woff2 +0 -0
  149. package/dist/css/fonts/files/open-sans-hebrew-wght-normal.woff2 +0 -0
  150. package/dist/css/fonts/files/open-sans-vietnamese-wght-italic.woff2 +0 -0
  151. package/dist/css/fonts/files/open-sans-vietnamese-wght-normal.woff2 +0 -0
  152. package/dist/css/fonts/files/playfair-display-cyrillic-wght-italic.woff2 +0 -0
  153. package/dist/css/fonts/files/playfair-display-cyrillic-wght-normal.woff2 +0 -0
  154. package/dist/css/fonts/files/playfair-display-sc-cyrillic-400-italic.woff2 +0 -0
  155. package/dist/css/fonts/files/playfair-display-sc-cyrillic-400-normal.woff2 +0 -0
  156. package/dist/css/fonts/files/playfair-display-sc-cyrillic-700-italic.woff2 +0 -0
  157. package/dist/css/fonts/files/playfair-display-sc-cyrillic-700-normal.woff2 +0 -0
  158. package/dist/css/fonts/files/playfair-display-sc-cyrillic-900-italic.woff2 +0 -0
  159. package/dist/css/fonts/files/playfair-display-sc-cyrillic-900-normal.woff2 +0 -0
  160. package/dist/css/fonts/files/playfair-display-sc-vietnamese-400-italic.woff2 +0 -0
  161. package/dist/css/fonts/files/playfair-display-sc-vietnamese-400-normal.woff2 +0 -0
  162. package/dist/css/fonts/files/playfair-display-sc-vietnamese-700-italic.woff2 +0 -0
  163. package/dist/css/fonts/files/playfair-display-sc-vietnamese-700-normal.woff2 +0 -0
  164. package/dist/css/fonts/files/playfair-display-sc-vietnamese-900-italic.woff2 +0 -0
  165. package/dist/css/fonts/files/playfair-display-sc-vietnamese-900-normal.woff2 +0 -0
  166. package/dist/css/fonts/files/playfair-display-vietnamese-wght-italic.woff2 +0 -0
  167. package/dist/css/fonts/files/playfair-display-vietnamese-wght-normal.woff2 +0 -0
  168. package/dist/css/fonts/files/pt-sans-cyrillic-400-italic.woff2 +0 -0
  169. package/dist/css/fonts/files/pt-sans-cyrillic-400-normal.woff2 +0 -0
  170. package/dist/css/fonts/files/pt-sans-cyrillic-700-italic.woff2 +0 -0
  171. package/dist/css/fonts/files/pt-sans-cyrillic-700-normal.woff2 +0 -0
  172. package/dist/css/fonts/files/pt-sans-cyrillic-ext-400-italic.woff2 +0 -0
  173. package/dist/css/fonts/files/pt-sans-cyrillic-ext-400-normal.woff2 +0 -0
  174. package/dist/css/fonts/files/pt-sans-cyrillic-ext-700-italic.woff2 +0 -0
  175. package/dist/css/fonts/files/pt-sans-cyrillic-ext-700-normal.woff2 +0 -0
  176. package/dist/css/fonts/files/pt-sans-narrow-cyrillic-400-normal.woff2 +0 -0
  177. package/dist/css/fonts/files/pt-sans-narrow-cyrillic-700-normal.woff2 +0 -0
  178. package/dist/css/fonts/files/pt-sans-narrow-cyrillic-ext-400-normal.woff2 +0 -0
  179. package/dist/css/fonts/files/pt-sans-narrow-cyrillic-ext-700-normal.woff2 +0 -0
  180. package/dist/css/fonts/files/pt-serif-cyrillic-400-italic.woff2 +0 -0
  181. package/dist/css/fonts/files/pt-serif-cyrillic-400-normal.woff2 +0 -0
  182. package/dist/css/fonts/files/pt-serif-cyrillic-700-italic.woff2 +0 -0
  183. package/dist/css/fonts/files/pt-serif-cyrillic-700-normal.woff2 +0 -0
  184. package/dist/css/fonts/files/pt-serif-cyrillic-ext-400-italic.woff2 +0 -0
  185. package/dist/css/fonts/files/pt-serif-cyrillic-ext-400-normal.woff2 +0 -0
  186. package/dist/css/fonts/files/pt-serif-cyrillic-ext-700-italic.woff2 +0 -0
  187. package/dist/css/fonts/files/pt-serif-cyrillic-ext-700-normal.woff2 +0 -0
  188. package/dist/css/fonts/files/raleway-cyrillic-ext-wght-italic.woff2 +0 -0
  189. package/dist/css/fonts/files/raleway-cyrillic-ext-wght-normal.woff2 +0 -0
  190. package/dist/css/fonts/files/raleway-cyrillic-wght-italic.woff2 +0 -0
  191. package/dist/css/fonts/files/raleway-cyrillic-wght-normal.woff2 +0 -0
  192. package/dist/css/fonts/files/raleway-vietnamese-wght-italic.woff2 +0 -0
  193. package/dist/css/fonts/files/raleway-vietnamese-wght-normal.woff2 +0 -0
  194. package/dist/css/fonts/files/roboto-slab-cyrillic-ext-wght-normal.woff2 +0 -0
  195. package/dist/css/fonts/files/roboto-slab-cyrillic-wght-normal.woff2 +0 -0
  196. package/dist/css/fonts/files/roboto-slab-greek-ext-wght-normal.woff2 +0 -0
  197. package/dist/css/fonts/files/roboto-slab-greek-wght-normal.woff2 +0 -0
  198. package/dist/css/fonts/files/roboto-slab-vietnamese-wght-normal.woff2 +0 -0
  199. package/dist/css/fonts/files/ubuntu-cyrillic-300-italic.woff2 +0 -0
  200. package/dist/css/fonts/files/ubuntu-cyrillic-300-normal.woff2 +0 -0
  201. package/dist/css/fonts/files/ubuntu-cyrillic-400-italic.woff2 +0 -0
  202. package/dist/css/fonts/files/ubuntu-cyrillic-400-normal.woff2 +0 -0
  203. package/dist/css/fonts/files/ubuntu-cyrillic-500-italic.woff2 +0 -0
  204. package/dist/css/fonts/files/ubuntu-cyrillic-500-normal.woff2 +0 -0
  205. package/dist/css/fonts/files/ubuntu-cyrillic-700-italic.woff2 +0 -0
  206. package/dist/css/fonts/files/ubuntu-cyrillic-700-normal.woff2 +0 -0
  207. package/dist/css/fonts/files/ubuntu-cyrillic-ext-300-italic.woff2 +0 -0
  208. package/dist/css/fonts/files/ubuntu-cyrillic-ext-300-normal.woff2 +0 -0
  209. package/dist/css/fonts/files/ubuntu-cyrillic-ext-400-italic.woff2 +0 -0
  210. package/dist/css/fonts/files/ubuntu-cyrillic-ext-400-normal.woff2 +0 -0
  211. package/dist/css/fonts/files/ubuntu-cyrillic-ext-500-italic.woff2 +0 -0
  212. package/dist/css/fonts/files/ubuntu-cyrillic-ext-500-normal.woff2 +0 -0
  213. package/dist/css/fonts/files/ubuntu-cyrillic-ext-700-italic.woff2 +0 -0
  214. package/dist/css/fonts/files/ubuntu-cyrillic-ext-700-normal.woff2 +0 -0
  215. package/dist/css/fonts/files/ubuntu-greek-300-italic.woff2 +0 -0
  216. package/dist/css/fonts/files/ubuntu-greek-300-normal.woff2 +0 -0
  217. package/dist/css/fonts/files/ubuntu-greek-400-italic.woff2 +0 -0
  218. package/dist/css/fonts/files/ubuntu-greek-400-normal.woff2 +0 -0
  219. package/dist/css/fonts/files/ubuntu-greek-500-italic.woff2 +0 -0
  220. package/dist/css/fonts/files/ubuntu-greek-500-normal.woff2 +0 -0
  221. package/dist/css/fonts/files/ubuntu-greek-700-italic.woff2 +0 -0
  222. package/dist/css/fonts/files/ubuntu-greek-700-normal.woff2 +0 -0
  223. package/dist/css/fonts/files/ubuntu-greek-ext-300-italic.woff2 +0 -0
  224. package/dist/css/fonts/files/ubuntu-greek-ext-300-normal.woff2 +0 -0
  225. package/dist/css/fonts/files/ubuntu-greek-ext-400-italic.woff2 +0 -0
  226. package/dist/css/fonts/files/ubuntu-greek-ext-400-normal.woff2 +0 -0
  227. package/dist/css/fonts/files/ubuntu-greek-ext-500-italic.woff2 +0 -0
  228. package/dist/css/fonts/files/ubuntu-greek-ext-500-normal.woff2 +0 -0
  229. package/dist/css/fonts/files/ubuntu-greek-ext-700-italic.woff2 +0 -0
  230. package/dist/css/fonts/files/ubuntu-greek-ext-700-normal.woff2 +0 -0
  231. package/dist/css/fonts/files/unica-one-vietnamese-400-normal.woff2 +0 -0
  232. package/dist/css/fonts/files/vollkorn-cyrillic-ext-wght-italic.woff2 +0 -0
  233. package/dist/css/fonts/files/vollkorn-cyrillic-ext-wght-normal.woff2 +0 -0
  234. package/dist/css/fonts/files/vollkorn-cyrillic-wght-italic.woff2 +0 -0
  235. package/dist/css/fonts/files/vollkorn-cyrillic-wght-normal.woff2 +0 -0
  236. package/dist/css/fonts/files/vollkorn-greek-wght-italic.woff2 +0 -0
  237. package/dist/css/fonts/files/vollkorn-greek-wght-normal.woff2 +0 -0
  238. package/dist/css/fonts/files/vollkorn-vietnamese-wght-italic.woff2 +0 -0
  239. package/dist/css/fonts/files/vollkorn-vietnamese-wght-normal.woff2 +0 -0
  240. package/dist/css/fonts/font.abril-droidsans.css +3 -0
  241. package/dist/css/fonts/font.amatic-andika.css +57 -0
  242. package/dist/css/fonts/font.bitter-raleway.css +111 -0
  243. package/dist/css/fonts/font.cairo.css +312 -0
  244. package/dist/css/fonts/font.clicker-garamond.css +93 -0
  245. package/dist/css/fonts/font.dancing-ledger.css +12 -0
  246. package/dist/css/fonts/font.default.css +183 -0
  247. package/dist/css/fonts/font.fjalla-average.css +3 -0
  248. package/dist/css/fonts/font.georgia-helvetica.css +3 -0
  249. package/dist/css/fonts/font.knightlab.css +642 -0
  250. package/dist/css/fonts/font.lustria-lato.css +3 -0
  251. package/dist/css/fonts/font.medula-lato.css +3 -0
  252. package/dist/css/fonts/font.oldstandard.css +84 -0
  253. package/dist/css/fonts/font.opensans-gentiumbook.css +183 -0
  254. package/dist/css/fonts/font.playfair-faunaone.css +39 -0
  255. package/dist/css/fonts/font.playfair.css +147 -0
  256. package/dist/css/fonts/font.pt.css +183 -0
  257. package/dist/css/fonts/font.roboto-megrim.css +3 -0
  258. package/dist/css/fonts/font.rufina-sintony.css +3 -0
  259. package/dist/css/fonts/font.ubuntu.css +291 -0
  260. package/dist/css/fonts/font.unicaone-vollkorn.css +84 -0
  261. package/dist/css/storymap.css +1 -1
  262. package/dist/demo.html +2 -2
  263. package/dist/docs/api/.nojekyll +1 -0
  264. package/dist/docs/api/assets/custom.css +47 -0
  265. package/dist/docs/api/assets/hierarchy.js +1 -0
  266. package/dist/docs/api/assets/highlight.css +57 -0
  267. package/dist/docs/api/assets/icons.js +18 -0
  268. package/dist/docs/api/assets/icons.svg +1 -0
  269. package/dist/docs/api/assets/main.js +60 -0
  270. package/dist/docs/api/assets/navigation.js +1 -0
  271. package/dist/docs/api/assets/search.js +1 -0
  272. package/dist/docs/api/assets/style.css +1648 -0
  273. package/dist/docs/api/classes/StoryMap.html +1604 -0
  274. package/dist/docs/api/functions/MediaType.html +51 -0
  275. package/dist/docs/api/functions/isPresentation3Collection.html +47 -0
  276. package/dist/docs/api/functions/isPresentation3Manifest.html +53 -0
  277. package/dist/docs/api/functions/loadCSS.html +63 -0
  278. package/dist/docs/api/functions/manifestToStorymapData.html +49 -0
  279. package/dist/docs/api/functions/setLanguage.html +54 -0
  280. package/dist/docs/api/functions/storymapToManifest.html +57 -0
  281. package/dist/docs/api/functions/validateStorymap.html +53 -0
  282. package/dist/docs/api/functions/validateStorymapAndReport.html +54 -0
  283. package/dist/docs/api/hierarchy.html +28 -0
  284. package/dist/docs/api/index.html +197 -0
  285. package/dist/docs/api/interfaces/AnimationHandle.html +60 -0
  286. package/dist/docs/api/interfaces/ImagereadyPayload.html +71 -0
  287. package/dist/docs/api/interfaces/LanguageEntry.html +102 -0
  288. package/dist/docs/api/interfaces/LatLngLiteral.html +73 -0
  289. package/dist/docs/api/interfaces/LoadOptions.html +59 -0
  290. package/dist/docs/api/interfaces/OlLayer.html +1400 -0
  291. package/dist/docs/api/interfaces/OlMap.html +1656 -0
  292. package/dist/docs/api/interfaces/OlProjection.html +332 -0
  293. package/dist/docs/api/interfaces/OlSource.html +784 -0
  294. package/dist/docs/api/interfaces/OlTileLayer.html +1497 -0
  295. package/dist/docs/api/interfaces/OlVectorLayer.html +1559 -0
  296. package/dist/docs/api/interfaces/OlView.html +1958 -0
  297. package/dist/docs/api/interfaces/StorymapData.html +71 -0
  298. package/dist/docs/api/interfaces/StorymapDataWrapper.html +60 -0
  299. package/dist/docs/api/interfaces/StorymapDocument.html +68 -0
  300. package/dist/docs/api/interfaces/StorymapError.html +63 -0
  301. package/dist/docs/api/interfaces/StorymapGeoreference.html +93 -0
  302. package/dist/docs/api/interfaces/StorymapGeoreferenceBody.html +74 -0
  303. package/dist/docs/api/interfaces/StorymapGroundControlPoint.html +69 -0
  304. package/dist/docs/api/interfaces/StorymapLanguageMap.html +57 -0
  305. package/dist/docs/api/interfaces/StorymapManifest.html +118 -0
  306. package/dist/docs/api/interfaces/StorymapManifestAnnotation.html +105 -0
  307. package/dist/docs/api/interfaces/StorymapManifestBackground.html +81 -0
  308. package/dist/docs/api/interfaces/StorymapManifestCanvas.html +140 -0
  309. package/dist/docs/api/interfaces/StorymapManifestContentResource.html +97 -0
  310. package/dist/docs/api/interfaces/StorymapManifestFeatureCollection.html +82 -0
  311. package/dist/docs/api/interfaces/StorymapManifestGeoreferencing.html +87 -0
  312. package/dist/docs/api/interfaces/StorymapManifestImageService.html +69 -0
  313. package/dist/docs/api/interfaces/StorymapManifestRange.html +75 -0
  314. package/dist/docs/api/interfaces/StorymapManifestService.html +77 -0
  315. package/dist/docs/api/interfaces/StorymapManifestSpecificResource.html +69 -0
  316. package/dist/docs/api/interfaces/StorymapManifestStatement.html +63 -0
  317. package/dist/docs/api/interfaces/StorymapMapOptions.html +92 -0
  318. package/dist/docs/api/interfaces/StorymapOptions.html +532 -0
  319. package/dist/docs/api/interfaces/StorymapOverlayLayer.html +126 -0
  320. package/dist/docs/api/interfaces/StorymapSlide.html +132 -0
  321. package/dist/docs/api/interfaces/StorymapSlideBackground.html +72 -0
  322. package/dist/docs/api/interfaces/StorymapSlideLocation.html +117 -0
  323. package/dist/docs/api/interfaces/StorymapSlideMarker.html +104 -0
  324. package/dist/docs/api/interfaces/StorymapSlideMedia.html +110 -0
  325. package/dist/docs/api/interfaces/StorymapSlideNarration.html +65 -0
  326. package/dist/docs/api/interfaces/StorymapSlideText.html +72 -0
  327. package/dist/docs/api/interfaces/StorymapTilejson.html +105 -0
  328. package/dist/docs/api/media/es.json +31 -0
  329. package/dist/docs/api/media/migration-from-knightlab.md +359 -0
  330. package/dist/docs/api/modules.html +50 -0
  331. package/dist/docs/api/types/ContentState.html +70 -0
  332. package/dist/docs/api/types/ImagereadyKind.html +34 -0
  333. package/dist/docs/api/types/MediaTypeMatch.html +78 -0
  334. package/dist/docs/api/types/TileSourceFactory.html +53 -0
  335. package/dist/docs/authoring.html +293 -0
  336. package/dist/docs/iiif.html +617 -62
  337. package/dist/docs/migration.html +313 -11
  338. package/dist/docs/readme.html +40 -9
  339. package/dist/embed/index.html +1 -1
  340. package/dist/examples/issue-242-nonlatin.json +55 -0
  341. package/dist/examples/issue-305-start-at-slide-data.json +96 -0
  342. package/dist/examples/issue-358-iframe.json +57 -0
  343. package/dist/examples/issue-iiif-geo.json +32 -0
  344. package/dist/examples/issue-image-alt.json +23 -0
  345. package/dist/examples/issue-image-region.json +42 -0
  346. package/dist/examples/issue-image-srcset.json +24 -0
  347. package/dist/examples/map-area-overlays.json +68 -0
  348. package/dist/examples/no-map.json +44 -0
  349. package/dist/examples-iiif/annotated-image.json +188 -0
  350. package/dist/examples-iiif/bob-boilen.json +11 -5
  351. package/dist/examples-iiif/courbet.json +9 -6
  352. package/dist/examples-iiif/empty.json +8 -4
  353. package/dist/examples-iiif/flickr-drewvigal.json +248 -24
  354. package/dist/examples-iiif/flickr-zach.wise.json +8 -4
  355. package/dist/examples-iiif/georeferenced-layer-unsupported.json +153 -0
  356. package/dist/examples-iiif/georeferenced-layer.json +318 -0
  357. package/dist/examples-iiif/iiif-wellcome.json +9 -6
  358. package/dist/examples-iiif/instagram-chenpamela.json +381 -36
  359. package/dist/examples-iiif/instagram_brentfoster.json +429 -40
  360. package/dist/examples-iiif/instagram_couch.json +261 -26
  361. package/dist/examples-iiif/instagram_joegermuska.json +441 -41
  362. package/dist/examples-iiif/instagram_matteich.json +261 -26
  363. package/dist/examples-iiif/instagram_mirandamulligan.json +261 -26
  364. package/dist/examples-iiif/instagram_raymondmccrea.json +261 -26
  365. package/dist/examples-iiif/instagram_test.json +261 -26
  366. package/dist/examples-iiif/issue-134-no-lines.json +9 -5
  367. package/dist/examples-iiif/issue-177-color-vars.json +9 -5
  368. package/dist/examples-iiif/issue-211-rtl.json +9 -5
  369. package/dist/examples-iiif/issue-242-nonlatin.json +239 -0
  370. package/dist/examples-iiif/issue-272-facebook.json +9 -5
  371. package/dist/examples-iiif/issue-305-start-at-slide-data.json +305 -0
  372. package/dist/examples-iiif/issue-305-start-at-slide.json +9 -5
  373. package/dist/examples-iiif/issue-341-distance.json +9 -5
  374. package/dist/examples-iiif/issue-358-iframe.json +190 -0
  375. package/dist/examples-iiif/issue-360-juxtapose.json +22 -7
  376. package/dist/examples-iiif/issue-368-null-fields.json +9 -5
  377. package/dist/examples-iiif/issue-375-code-lists.json +9 -5
  378. package/dist/examples-iiif/issue-381-dateline.json +9 -5
  379. package/dist/examples-iiif/issue-405-custom-icon.json +203 -0
  380. package/dist/examples-iiif/issue-425-many-slides.json +9 -5
  381. package/dist/examples-iiif/issue-428-background.json +20 -8
  382. package/dist/examples-iiif/issue-435-googlemaps.json +9 -5
  383. package/dist/examples-iiif/issue-437-documentcloud.json +22 -7
  384. package/dist/examples-iiif/issue-451-webp.json +9 -5
  385. package/dist/examples-iiif/issue-455-audio.json +9 -5
  386. package/dist/examples-iiif/issue-480-shorts.json +9 -5
  387. package/dist/examples-iiif/issue-506-marker-sync.json +9 -5
  388. package/dist/examples-iiif/issue-iiif-geo.json +200 -0
  389. package/dist/examples-iiif/issue-image-alt.json +90 -0
  390. package/dist/examples-iiif/issue-image-region.json +203 -0
  391. package/dist/examples-iiif/issue-image-srcset.json +87 -0
  392. package/dist/examples-iiif/jansteen.json +9 -6
  393. package/dist/examples-iiif/katrina.json +197 -41
  394. package/dist/examples-iiif/map-area-overlays.json +289 -0
  395. package/dist/examples-iiif/marktwain.json +255 -58
  396. package/dist/examples-iiif/no-map.json +148 -0
  397. package/dist/examples-iiif/population.json +440 -76
  398. package/dist/examples-iiif/president.json +456 -83
  399. package/dist/examples-iiif/scroll-hint.json +190 -0
  400. package/dist/examples-iiif/seurat.json +113 -22
  401. package/dist/examples-iiif/zoomify-clamp.json +153 -0
  402. package/dist/harness-multi.html +17 -0
  403. package/dist/harness.html +3 -2
  404. package/dist/index.html +16 -10
  405. package/dist/js/storymap.d.ts +1439 -114
  406. package/dist/js/storymap.js +24654 -21936
  407. package/dist/js/storymap.js.map +1 -1
  408. package/dist/navplace-properties.json +40 -0
  409. package/docs/migration-from-knightlab.md +229 -9
  410. package/docs/storymap-as-iiif-manifest.md +559 -135
  411. package/package.json +11 -8
  412. package/schema/storymap.schema.json +431 -78
  413. package/src/animation/easings.ts +8 -1
  414. package/src/animation/tween.ts +186 -0
  415. package/src/core/Browser.ts +6 -5
  416. package/src/core/Load.ts +54 -2
  417. package/src/core/Util.ts +255 -193
  418. package/src/core/mixins.ts +24 -7
  419. package/src/core/viewers.ts +94 -0
  420. package/src/dom/DomEvent.ts +27 -8
  421. package/src/globals.d.ts +8 -0
  422. package/src/language/Language.ts +199 -25
  423. package/src/language/locale/.expected-gaps.json +86 -0
  424. package/src/language/locale/be.json +15 -2
  425. package/src/language/locale/bg.json +15 -2
  426. package/src/language/locale/cs.json +15 -2
  427. package/src/language/locale/de.json +12 -1
  428. package/src/language/locale/el.json +15 -2
  429. package/src/language/locale/en.json +6 -2
  430. package/src/language/locale/es.json +15 -2
  431. package/src/language/locale/et.json +5 -1
  432. package/src/language/locale/fr.json +15 -2
  433. package/src/language/locale/he.json +16 -3
  434. package/src/language/locale/hu.json +15 -2
  435. package/src/language/locale/is.json +5 -1
  436. package/src/language/locale/it.json +15 -2
  437. package/src/language/locale/jp.json +15 -2
  438. package/src/language/locale/ko.json +15 -2
  439. package/src/language/locale/nl.json +15 -2
  440. package/src/language/locale/nn.json +5 -1
  441. package/src/language/locale/no.json +15 -2
  442. package/src/language/locale/pl.json +15 -2
  443. package/src/language/locale/pt.json +15 -2
  444. package/src/language/locale/ru.json +15 -2
  445. package/src/language/locale/sk.json +15 -2
  446. package/src/language/locale/sr.json +5 -1
  447. package/src/language/locale/sv.json +15 -2
  448. package/src/language/locale/tr.json +15 -2
  449. package/src/language/locale/uk.json +15 -2
  450. package/src/language/locale/ur.json +5 -1
  451. package/src/language/locale/zh-cn.json +15 -2
  452. package/src/language/locale/zh-tw.json +15 -2
  453. package/src/main.ts +92 -0
  454. package/src/map/Map.ts +183 -106
  455. package/src/map/MapMarker.ts +28 -5
  456. package/src/map/georeference.ts +325 -0
  457. package/src/map/openlayers/Map.OpenLayers.ts +850 -237
  458. package/src/map/openlayers/MapMarker.OpenLayers.ts +248 -24
  459. package/src/map/openlayers/zoomifyTiles.ts +1 -4
  460. package/src/media/EmbedUtil.ts +308 -24
  461. package/src/media/Media.ts +243 -55
  462. package/src/media/embedId.ts +104 -0
  463. package/src/media/types/Audio.ts +9 -82
  464. package/src/media/types/Blockquote.ts +2 -3
  465. package/src/media/types/DailyMotion.ts +5 -10
  466. package/src/media/types/DocumentCloud.ts +6 -35
  467. package/src/media/types/Facebook.ts +3 -4
  468. package/src/media/types/Flickr.ts +9 -9
  469. package/src/media/types/GoogleDoc.ts +3 -4
  470. package/src/media/types/HtmlMedia.ts +167 -0
  471. package/src/media/types/IFrame.ts +3 -4
  472. package/src/media/types/Image.ts +72 -6
  473. package/src/media/types/Juxtapose.ts +14 -5
  474. package/src/media/types/SoundCloud.ts +28 -6
  475. package/src/media/types/Text.ts +19 -13
  476. package/src/media/types/Twitter.ts +27 -14
  477. package/src/media/types/Video.ts +9 -79
  478. package/src/media/types/Vimeo.ts +10 -10
  479. package/src/media/types/Website.ts +23 -13
  480. package/src/media/types/Wikipedia.ts +18 -8
  481. package/src/media/types/YouTube.ts +71 -39
  482. package/src/scss/Typography.scss +1 -3
  483. package/src/scss/VCO.StoryMap.Dark.scss +18 -6
  484. package/src/scss/VCO.StoryMap.scss +37 -6
  485. package/src/scss/Variables.Dark.scss +3 -2
  486. package/src/scss/Variables.scss +3 -2
  487. package/src/scss/core/Mixins.scss +5 -63
  488. package/src/scss/fonts/font.amatic-andika.scss +3 -0
  489. package/src/scss/fonts/font.bitter-raleway.scss +6 -0
  490. package/src/scss/fonts/font.cairo.scss +79 -0
  491. package/src/scss/fonts/font.clicker-garamond.scss +5 -0
  492. package/src/scss/fonts/font.dancing-ledger.scss +1 -0
  493. package/src/scss/fonts/font.knightlab.scss +14 -0
  494. package/src/scss/fonts/font.oldstandard.scss +6 -0
  495. package/src/scss/fonts/font.opensans-gentiumbook.scss +5 -0
  496. package/src/scss/fonts/font.playfair-faunaone.scss +2 -0
  497. package/src/scss/fonts/font.playfair.scss +4 -0
  498. package/src/scss/fonts/font.pt.scss +6 -0
  499. package/src/scss/fonts/font.ubuntu.scss +4 -0
  500. package/src/scss/fonts/font.unicaone-vollkorn.scss +5 -0
  501. package/src/scss/map/VCO.MapMarker.scss +98 -3
  502. package/src/scss/media/VCO.Media.scss +7 -10
  503. package/src/scss/media/types/VCO.Media.Text.scss +8 -0
  504. package/src/scss/media/types/VCO.Media.Wikipedia.scss +2 -1
  505. package/src/scss/slider/VCO.Slide.scss +13 -3
  506. package/src/scss/slider/VCO.SlideNav.scss +11 -17
  507. package/src/scss/slider/VCO.StorySlider.scss +4 -2
  508. package/src/scss/ui/VCO.Consent.scss +44 -0
  509. package/src/scss/ui/VCO.MenuBar.Button.scss +8 -0
  510. package/src/scss/ui/VCO.Message.scss +2 -5
  511. package/src/site/site.ts +2 -1
  512. package/src/slider/Slide.ts +121 -27
  513. package/src/slider/SlideNav.ts +39 -5
  514. package/src/slider/StorySlider.ts +153 -42
  515. package/src/storymap/Consent.ts +407 -82
  516. package/src/storymap/StoryMap.ts +1192 -182
  517. package/src/storymap/content-state.ts +185 -0
  518. package/src/storymap/iiif.ts +1526 -68
  519. package/src/storymap/to-iiif.ts +921 -0
  520. package/src/storymap/validate.ts +80 -7
  521. package/src/types.ts +223 -26
  522. package/src/ui/MenuBar.ts +98 -45
  523. package/src/ui/Message.ts +9 -0
  524. package/src/ui/Swipable.ts +145 -78
  525. package/dist/assets/demo-D6cTmzn6.js +0 -1
  526. package/dist/assets/harness-BPFlzutp.js +0 -1
  527. package/dist/assets/index-DDsPBPZA.js +0 -1
  528. package/dist/assets/main-Y-MYUSJ4.js +0 -17
  529. package/src/animation/morpheus.d.ts +0 -41
  530. package/src/scss/media/types/VCO.Media.Instagram.scss +0 -7
  531. package/src/scss/media/types/VCO.Media.Profile.scss +0 -5
  532. package/src/ui/Draggable.ts +0 -374
package/src/core/Util.ts CHANGED
@@ -1,104 +1,79 @@
1
1
  // const debug = true;
2
2
 
3
- export function extend<T extends Record<string, unknown>>(
4
- dest: T,
5
- ...sources: (Record<string, unknown> | null | undefined)[]
6
- ): T {
7
- // merge src properties into dest
8
- sources = sources.filter(Boolean);
9
- for (let j = 0, len = sources.length, src; j < len; j++) {
10
- src = sources[j] || {};
11
- for (const i in src) {
12
- if (Object.hasOwn(src, i)) {
13
- (dest as Record<string, unknown>)[i] = src[i];
14
- }
15
- }
16
- }
17
- return dest;
18
- }
19
-
20
- export function convertUnixTime(str: string): string {
21
- // created for Instagram. It's ISO8601-ish
22
- // 2013-12-09 01:56:28
23
- const pattern = /^(\d{4})-(\d{2})-(\d{2})[T\s](\d{2}):(\d{2}):(\d{2})/;
24
- const date_parts = str.match(pattern)?.slice(1);
25
- if (!date_parts) {
3
+ /**
4
+ * Format an ISO-8601-ish timestamp (as found in `date.created_time`, originally
5
+ * from Instagram) as a short, locale-aware date, e.g. "Mar 3, 2013".
6
+ *
7
+ * Falls back to the raw input when it does not parse, so an unusual value
8
+ * shows as-is rather than "Invalid Date".
9
+ */
10
+ export function convertUnixTime(str: string, locale?: string): string {
11
+ if (!str) {
26
12
  return str;
27
13
  }
28
- const date_array = [];
29
- for (let i = 0; i < date_parts.length; i++) {
30
- let val = parseInt(date_parts[i]);
31
- if (i === 1) {
32
- val = val - 1;
33
- } // stupid javascript months
34
- date_array.push(val);
14
+ // "2013-12-09 01:56:28" is not valid ISO-8601 (space instead of "T"), so
15
+ // normalise it before parsing.
16
+ const parsed = new Date(/^\d{4}-\d{2}-\d{2} /.test(str) ? str.replace(" ", "T") : str);
17
+ if (Number.isNaN(parsed.getTime())) {
18
+ return str;
35
19
  }
36
- const date = new Date(
37
- date_array[0],
38
- date_array[1],
39
- date_array[2],
40
- date_array[3],
41
- date_array[4],
42
- date_array[5],
43
- );
44
- const months = [
45
- "Jan",
46
- "Feb",
47
- "Mar",
48
- "Apr",
49
- "May",
50
- "Jun",
51
- "Jul",
52
- "Aug",
53
- "Sep",
54
- "Oct",
55
- "Nov",
56
- "Dec",
57
- ];
58
- const year = date.getFullYear();
59
- const month = months[date.getMonth()];
60
- const day = date.getDate();
61
- const time = month + ", " + day + " " + year;
62
- return time;
20
+ return parsed.toLocaleDateString(locale ?? "en-US", {
21
+ year: "numeric",
22
+ month: "short",
23
+ day: "numeric",
24
+ });
63
25
  }
64
26
 
65
- export function setData(obj: { data?: unknown }, data: unknown): void {
66
- obj.data = extend(
67
- {} as Record<string, unknown>,
68
- obj.data as Record<string, unknown> | undefined,
69
- data as Record<string, unknown> | undefined,
70
- );
71
- const stored = obj.data as Record<string, unknown>;
72
- if (stored.uniqueid === "") {
73
- stored.uniqueid = unique_ID(6);
27
+ /**
28
+ * Copy the own enumerable properties of `source` onto `target`, limited to
29
+ * `keys` (or all of the source's keys when omitted). Shared body for the two
30
+ * public merge helpers below.
31
+ */
32
+ function copyOwn(
33
+ target: Record<string, unknown>,
34
+ source: Record<string, unknown> | null | undefined,
35
+ keys?: Iterable<string>,
36
+ ): void {
37
+ // `for...in` over a null/undefined source was a silent no-op, and several
38
+ // call sites pass optional values straight through — keep that behaviour.
39
+ if (source == null) {
40
+ return;
74
41
  }
75
- }
76
-
77
- export function mergeData<T extends object>(data_main: T, data_to_merge: object): T {
78
- const target = data_main as Record<string, unknown>;
79
- const source = data_to_merge as Record<string, unknown>;
80
- let x;
81
- for (x in source) {
82
- if (Object.prototype.hasOwnProperty.call(source, x)) {
83
- target[x] = source[x];
42
+ const names = keys ?? Object.keys(source);
43
+ for (const name of names) {
44
+ if (Object.prototype.hasOwnProperty.call(source, name)) {
45
+ target[name] = source[name];
84
46
  }
85
47
  }
48
+ }
49
+
50
+ /**
51
+ * Shallow-assign every own property of `data_to_merge` onto `data_main`.
52
+ *
53
+ * Despite the name this is *not* a deep merge, and it must not become one:
54
+ * every call site merges a partial options object over a defaults object, and
55
+ * a recursive merge would silently change how nested `map_options` and
56
+ * `overlays` values are combined.
57
+ */
58
+ export function mergeData<T extends object>(data_main: T, data_to_merge?: object | null): T {
59
+ copyOwn(
60
+ data_main as Record<string, unknown>,
61
+ data_to_merge as Record<string, unknown> | null | undefined,
62
+ );
86
63
  return data_main;
87
64
  }
88
65
 
89
66
  /**
90
- * Like mergeData, except will only try to copy data that already exists
91
- * in data_main
67
+ * Like `mergeData`, but only copies keys that already exist in `data_main`.
68
+ * This is what lets storymap *data* override a known option without being able
69
+ * to introduce new ones.
92
70
  */
93
- export function updateData<T extends object>(data_main: T, data_to_merge: object): T {
94
- const target = data_main as Record<string, unknown>;
95
- const source = data_to_merge as Record<string, unknown>;
96
- let x;
97
- for (x in data_main) {
98
- if (Object.prototype.hasOwnProperty.call(source, x)) {
99
- target[x] = source[x];
100
- }
101
- }
71
+ export function updateData<T extends object>(data_main: T, data_to_merge?: object | null): T {
72
+ copyOwn(
73
+ data_main as Record<string, unknown>,
74
+ data_to_merge as Record<string, unknown> | null | undefined,
75
+ Object.keys(data_main),
76
+ );
102
77
  return data_main;
103
78
  }
104
79
 
@@ -112,18 +87,36 @@ export function stamp(obj: object): number {
112
87
  return target[_stampKey] as number;
113
88
  }
114
89
 
90
+ /**
91
+ * Index of the entry whose `data[prop]` equals `id`, or -1 when there is no
92
+ * match. Returning -1 matters: a "not found" result of 0 is indistinguishable
93
+ * from "the first slide", so a bad id silently jumped the story to the start
94
+ * (contrast StoryMap.goTo, which range-checks).
95
+ */
115
96
  export function findArrayNumberByUniqueID(
116
97
  id: unknown,
117
98
  array: { data: Record<string, unknown> }[],
118
99
  prop: string,
119
100
  ): number {
120
- let _n = 0;
121
101
  for (let i = 0; i < array.length; i++) {
122
102
  if (array[i].data[prop] === id) {
123
- _n = i;
103
+ return i;
124
104
  }
125
105
  }
126
- return _n;
106
+ return -1;
107
+ }
108
+
109
+ /**
110
+ * Clear a timer that may be null/undefined and reset the variable.
111
+ *
112
+ * `clearTimeout(null)` is a harmless no-op at runtime but not assignable to
113
+ * the overloads, and forgetting to null the handle afterwards is what lets a
114
+ * cancelled timer be cleared twice (or, worse, not cleared at all).
115
+ */
116
+ export function clearTimer(timer: ReturnType<typeof setTimeout> | null | undefined): void {
117
+ if (timer !== null && timer !== undefined) {
118
+ clearTimeout(timer);
119
+ }
127
120
  }
128
121
 
129
122
  export function unique_ID(size: number, prefix?: string): string {
@@ -167,55 +160,153 @@ export function hexToRgb(hex: string): { r: number; g: number; b: number } | nul
167
160
 
168
161
  // CSS named colors (Colors Level 4) for parseCssColor below.
169
162
  const NAMED_COLORS: Record<string, [number, number, number]> = {
170
- aliceblue: [240, 248, 255], antiquewhite: [250, 235, 215], aqua: [0, 255, 255],
171
- aquamarine: [127, 255, 212], azure: [240, 255, 255], beige: [245, 245, 220],
172
- bisque: [255, 228, 196], black: [0, 0, 0], blanchedalmond: [255, 235, 205],
173
- blue: [0, 0, 255], blueviolet: [138, 43, 226], brown: [165, 42, 42],
174
- burlywood: [222, 184, 135], cadetblue: [95, 158, 160], chartreuse: [127, 255, 0],
175
- chocolate: [210, 105, 30], coral: [255, 127, 80], cornflowerblue: [100, 149, 237],
176
- cornsilk: [255, 248, 220], crimson: [220, 20, 60], cyan: [0, 255, 255],
177
- darkblue: [0, 0, 139], darkcyan: [0, 139, 139], darkgoldenrod: [184, 134, 11],
178
- darkgray: [169, 169, 169], darkgreen: [0, 100, 0], darkgrey: [169, 169, 169],
179
- darkkhaki: [189, 183, 107], darkmagenta: [139, 0, 139], darkolivegreen: [85, 107, 47],
180
- darkorange: [255, 140, 0], darkorchid: [153, 50, 204], darkred: [139, 0, 0],
181
- darksalmon: [233, 150, 122], darkseagreen: [143, 188, 143], darkslateblue: [72, 61, 139],
182
- darkslategray: [47, 79, 79], darkslategrey: [47, 79, 79], darkturquoise: [0, 206, 209],
183
- darkviolet: [148, 0, 211], deeppink: [255, 20, 147], deepskyblue: [0, 191, 255],
184
- dimgray: [105, 105, 105], dimgrey: [105, 105, 105], dodgerblue: [30, 144, 255],
185
- firebrick: [178, 34, 34], floralwhite: [255, 250, 240], forestgreen: [34, 139, 34],
186
- fuchsia: [255, 0, 255], gainsboro: [220, 220, 220], ghostwhite: [248, 248, 255],
187
- gold: [255, 215, 0], goldenrod: [218, 165, 32], gray: [128, 128, 128],
188
- green: [0, 128, 0], greenyellow: [173, 255, 47], grey: [128, 128, 128],
189
- honeydew: [240, 255, 240], hotpink: [255, 105, 180], indianred: [205, 92, 92],
190
- indigo: [75, 0, 130], ivory: [255, 255, 240], khaki: [240, 230, 140],
191
- lavender: [230, 230, 250], lavenderblush: [255, 240, 245], lawngreen: [124, 252, 0],
192
- lemonchiffon: [255, 250, 205], lightblue: [173, 216, 230], lightcoral: [240, 128, 128],
193
- lightcyan: [224, 255, 255], lightgoldenrodyellow: [250, 250, 210], lightgray: [211, 211, 211],
194
- lightgreen: [144, 238, 144], lightgrey: [211, 211, 211], lightpink: [255, 182, 193],
195
- lightsalmon: [255, 160, 122], lightseagreen: [32, 178, 170], lightskyblue: [135, 206, 250],
196
- lightslategray: [119, 136, 153], lightslategrey: [119, 136, 153], lightsteelblue: [176, 196, 222],
197
- lightyellow: [255, 255, 224], lime: [0, 255, 0], limegreen: [50, 205, 50],
198
- linen: [250, 240, 230], magenta: [255, 0, 255], maroon: [128, 0, 0],
199
- mediumaquamarine: [102, 205, 170], mediumblue: [0, 0, 205], mediumorchid: [186, 85, 211],
200
- mediumpurple: [147, 112, 219], mediumseagreen: [60, 179, 113], mediumslateblue: [123, 104, 238],
201
- mediumspringgreen: [0, 250, 154], mediumturquoise: [72, 209, 204], mediumvioletred: [199, 21, 133],
202
- midnightblue: [25, 25, 112], mintcream: [245, 255, 250], mistyrose: [255, 228, 225],
203
- moccasin: [255, 228, 181], navajowhite: [255, 222, 173], navy: [0, 0, 128],
204
- oldlace: [253, 245, 230], olive: [128, 128, 0], olivedrab: [107, 142, 35],
205
- orange: [255, 165, 0], orangered: [255, 69, 0], orchid: [218, 112, 214],
206
- palegoldenrod: [238, 232, 170], palegreen: [152, 251, 152], paleturquoise: [175, 238, 238],
207
- palevioletred: [219, 112, 147], papayawhip: [255, 239, 213], peachpuff: [255, 218, 185],
208
- peru: [205, 133, 63], pink: [255, 192, 203], plum: [221, 160, 221],
209
- powderblue: [176, 224, 230], purple: [128, 0, 128], rebeccapurple: [102, 51, 153],
210
- red: [255, 0, 0], rosybrown: [188, 143, 143], royalblue: [65, 105, 225],
211
- saddlebrown: [139, 69, 19], salmon: [250, 128, 114], sandybrown: [244, 164, 96],
212
- seagreen: [46, 139, 87], seashell: [255, 245, 238], sienna: [160, 82, 45],
213
- silver: [192, 192, 192], skyblue: [135, 206, 235], slateblue: [106, 90, 205],
214
- slategray: [112, 128, 144], slategrey: [112, 128, 144], snow: [255, 250, 250],
215
- springgreen: [0, 255, 127], steelblue: [70, 130, 180], tan: [210, 180, 140],
216
- teal: [0, 128, 128], thistle: [216, 191, 216], tomato: [255, 99, 71],
217
- turquoise: [64, 224, 208], violet: [238, 130, 238], wheat: [245, 222, 179],
218
- white: [255, 255, 255], whitesmoke: [245, 245, 245], yellow: [255, 255, 0],
163
+ aliceblue: [240, 248, 255],
164
+ antiquewhite: [250, 235, 215],
165
+ aqua: [0, 255, 255],
166
+ aquamarine: [127, 255, 212],
167
+ azure: [240, 255, 255],
168
+ beige: [245, 245, 220],
169
+ bisque: [255, 228, 196],
170
+ black: [0, 0, 0],
171
+ blanchedalmond: [255, 235, 205],
172
+ blue: [0, 0, 255],
173
+ blueviolet: [138, 43, 226],
174
+ brown: [165, 42, 42],
175
+ burlywood: [222, 184, 135],
176
+ cadetblue: [95, 158, 160],
177
+ chartreuse: [127, 255, 0],
178
+ chocolate: [210, 105, 30],
179
+ coral: [255, 127, 80],
180
+ cornflowerblue: [100, 149, 237],
181
+ cornsilk: [255, 248, 220],
182
+ crimson: [220, 20, 60],
183
+ cyan: [0, 255, 255],
184
+ darkblue: [0, 0, 139],
185
+ darkcyan: [0, 139, 139],
186
+ darkgoldenrod: [184, 134, 11],
187
+ darkgray: [169, 169, 169],
188
+ darkgreen: [0, 100, 0],
189
+ darkgrey: [169, 169, 169],
190
+ darkkhaki: [189, 183, 107],
191
+ darkmagenta: [139, 0, 139],
192
+ darkolivegreen: [85, 107, 47],
193
+ darkorange: [255, 140, 0],
194
+ darkorchid: [153, 50, 204],
195
+ darkred: [139, 0, 0],
196
+ darksalmon: [233, 150, 122],
197
+ darkseagreen: [143, 188, 143],
198
+ darkslateblue: [72, 61, 139],
199
+ darkslategray: [47, 79, 79],
200
+ darkslategrey: [47, 79, 79],
201
+ darkturquoise: [0, 206, 209],
202
+ darkviolet: [148, 0, 211],
203
+ deeppink: [255, 20, 147],
204
+ deepskyblue: [0, 191, 255],
205
+ dimgray: [105, 105, 105],
206
+ dimgrey: [105, 105, 105],
207
+ dodgerblue: [30, 144, 255],
208
+ firebrick: [178, 34, 34],
209
+ floralwhite: [255, 250, 240],
210
+ forestgreen: [34, 139, 34],
211
+ fuchsia: [255, 0, 255],
212
+ gainsboro: [220, 220, 220],
213
+ ghostwhite: [248, 248, 255],
214
+ gold: [255, 215, 0],
215
+ goldenrod: [218, 165, 32],
216
+ gray: [128, 128, 128],
217
+ green: [0, 128, 0],
218
+ greenyellow: [173, 255, 47],
219
+ grey: [128, 128, 128],
220
+ honeydew: [240, 255, 240],
221
+ hotpink: [255, 105, 180],
222
+ indianred: [205, 92, 92],
223
+ indigo: [75, 0, 130],
224
+ ivory: [255, 255, 240],
225
+ khaki: [240, 230, 140],
226
+ lavender: [230, 230, 250],
227
+ lavenderblush: [255, 240, 245],
228
+ lawngreen: [124, 252, 0],
229
+ lemonchiffon: [255, 250, 205],
230
+ lightblue: [173, 216, 230],
231
+ lightcoral: [240, 128, 128],
232
+ lightcyan: [224, 255, 255],
233
+ lightgoldenrodyellow: [250, 250, 210],
234
+ lightgray: [211, 211, 211],
235
+ lightgreen: [144, 238, 144],
236
+ lightgrey: [211, 211, 211],
237
+ lightpink: [255, 182, 193],
238
+ lightsalmon: [255, 160, 122],
239
+ lightseagreen: [32, 178, 170],
240
+ lightskyblue: [135, 206, 250],
241
+ lightslategray: [119, 136, 153],
242
+ lightslategrey: [119, 136, 153],
243
+ lightsteelblue: [176, 196, 222],
244
+ lightyellow: [255, 255, 224],
245
+ lime: [0, 255, 0],
246
+ limegreen: [50, 205, 50],
247
+ linen: [250, 240, 230],
248
+ magenta: [255, 0, 255],
249
+ maroon: [128, 0, 0],
250
+ mediumaquamarine: [102, 205, 170],
251
+ mediumblue: [0, 0, 205],
252
+ mediumorchid: [186, 85, 211],
253
+ mediumpurple: [147, 112, 219],
254
+ mediumseagreen: [60, 179, 113],
255
+ mediumslateblue: [123, 104, 238],
256
+ mediumspringgreen: [0, 250, 154],
257
+ mediumturquoise: [72, 209, 204],
258
+ mediumvioletred: [199, 21, 133],
259
+ midnightblue: [25, 25, 112],
260
+ mintcream: [245, 255, 250],
261
+ mistyrose: [255, 228, 225],
262
+ moccasin: [255, 228, 181],
263
+ navajowhite: [255, 222, 173],
264
+ navy: [0, 0, 128],
265
+ oldlace: [253, 245, 230],
266
+ olive: [128, 128, 0],
267
+ olivedrab: [107, 142, 35],
268
+ orange: [255, 165, 0],
269
+ orangered: [255, 69, 0],
270
+ orchid: [218, 112, 214],
271
+ palegoldenrod: [238, 232, 170],
272
+ palegreen: [152, 251, 152],
273
+ paleturquoise: [175, 238, 238],
274
+ palevioletred: [219, 112, 147],
275
+ papayawhip: [255, 239, 213],
276
+ peachpuff: [255, 218, 185],
277
+ peru: [205, 133, 63],
278
+ pink: [255, 192, 203],
279
+ plum: [221, 160, 221],
280
+ powderblue: [176, 224, 230],
281
+ purple: [128, 0, 128],
282
+ rebeccapurple: [102, 51, 153],
283
+ red: [255, 0, 0],
284
+ rosybrown: [188, 143, 143],
285
+ royalblue: [65, 105, 225],
286
+ saddlebrown: [139, 69, 19],
287
+ salmon: [250, 128, 114],
288
+ sandybrown: [244, 164, 96],
289
+ seagreen: [46, 139, 87],
290
+ seashell: [255, 245, 238],
291
+ sienna: [160, 82, 45],
292
+ silver: [192, 192, 192],
293
+ skyblue: [135, 206, 235],
294
+ slateblue: [106, 90, 205],
295
+ slategray: [112, 128, 144],
296
+ slategrey: [112, 128, 144],
297
+ snow: [255, 250, 250],
298
+ springgreen: [0, 255, 127],
299
+ steelblue: [70, 130, 180],
300
+ tan: [210, 180, 140],
301
+ teal: [0, 128, 128],
302
+ thistle: [216, 191, 216],
303
+ tomato: [255, 99, 71],
304
+ turquoise: [64, 224, 208],
305
+ violet: [238, 130, 238],
306
+ wheat: [245, 222, 179],
307
+ white: [255, 255, 255],
308
+ whitesmoke: [245, 245, 245],
309
+ yellow: [255, 255, 0],
219
310
  yellowgreen: [154, 205, 50],
220
311
  };
221
312
 
@@ -263,6 +354,19 @@ export function slideTransitionDuration(from: number, to: number): number {
263
354
  return Math.max(600, Math.min(1000 + steps * 120, 2000));
264
355
  }
265
356
 
357
+ /**
358
+ * True when the user prefers reduced motion
359
+ * (`prefers-reduced-motion: reduce`) — slide/map glides collapse to
360
+ * instant changes and autoplay stays off.
361
+ */
362
+ export function prefersReducedMotion(): boolean {
363
+ return (
364
+ typeof window !== "undefined" &&
365
+ typeof window.matchMedia === "function" &&
366
+ window.matchMedia("(prefers-reduced-motion: reduce)").matches
367
+ );
368
+ }
369
+
266
370
  export function htmlify(str: string): string {
267
371
  //if (str.match(/<\s*p[^>]*>([^<]*)<\s*\/\s*p\s*>/)) {
268
372
  if (str.match(/<p>[\s\S]*?<\/p>/)) {
@@ -272,69 +376,27 @@ export function htmlify(str: string): string {
272
376
  }
273
377
  }
274
378
 
275
- export function getUrlVars(string: string): string[] & Record<string, string> {
276
- let str: string, hash: string[];
277
- const vars: string[] = [];
278
- str = string.toString();
279
- if (str.match("&#038;")) {
280
- str = str.replace("&#038;", "&");
281
- } else if (str.match("&#38;")) {
282
- str = str.replace("&#38;", "&");
283
- } else if (str.match("&amp;")) {
284
- str = str.replace("&amp;", "&");
285
- }
286
- const hashes = str.slice(str.indexOf("?") + 1).split("&");
287
- for (let i = 0; i < hashes.length; i++) {
288
- hash = hashes[i].split("=");
289
- vars.push(hash[0]);
290
- (vars as unknown as Record<string, string>)[hash[0]] = hash[1];
291
- }
292
- return vars as string[] & Record<string, string>;
293
- }
294
-
295
379
  export const ratio = {
380
+ /**
381
+ * The 16:9 height for a given width, or the 16:9 width for a given
382
+ * height. Returns 0 when neither is a usable number.
383
+ *
384
+ * The old guards were `!== null && !== ""`, which an `undefined` passes
385
+ * through — so a caller that omitted `w` computed `Math.round(NaN)` and
386
+ * wrote `height: "NaNpx"`. A `typeof === "number"` check is what the
387
+ * arithmetic actually needs.
388
+ */
296
389
  r16_9: function (size: { w?: number; h?: number }): number {
297
- if (size.w !== null && (size.w as unknown as string) !== "") {
390
+ if (typeof size.w === "number" && isFinite(size.w)) {
298
391
  return Math.round((size.w / 16) * 9);
299
- } else if (size.h !== null && (size.h as unknown as string) !== "") {
392
+ }
393
+ if (typeof size.h === "number" && isFinite(size.h)) {
300
394
  return Math.round((size.h / 9) * 16);
301
- } else {
302
- return 0;
303
395
  }
396
+ return 0;
304
397
  },
305
398
  };
306
399
 
307
- export function urljoin(base_url: string, path: string): string {
308
- if (base_url.length && base_url.at(-1) === "/") {
309
- base_url = base_url.substring(0, base_url.length - 1);
310
- }
311
- if (path.length && path[0] === "/") {
312
- path = path.substring(1);
313
- }
314
- const url1 = base_url.split("/");
315
- const url2 = path.split("/");
316
- const url3 = [];
317
- for (let i = 0, l = url1.length; i < l; i++) {
318
- if (url1[i] === "..") {
319
- url3.pop();
320
- } else if (url1[i] === ".") {
321
- continue;
322
- } else {
323
- url3.push(url1[i]);
324
- }
325
- }
326
- for (let i = 0, l = url2.length; i < l; i++) {
327
- if (url2[i] === "..") {
328
- url3.pop();
329
- } else if (url2[i] === ".") {
330
- continue;
331
- } else {
332
- url3.push(url2[i]);
333
- }
334
- }
335
- return url3.join("/");
336
- }
337
-
338
400
  export function getObjectAttributeByIndex(
339
401
  obj: Record<string, unknown> | undefined,
340
402
  index: number,
@@ -16,9 +16,17 @@ export interface EventedInstance {
16
16
  hasEventListeners: (type: string) => boolean;
17
17
  }
18
18
 
19
- /** Instance contract for DomMixed consumers. */
19
+ /**
20
+ * Instance contract for DomMixed consumers.
21
+ *
22
+ * `_el` is deliberately `Record<string, unknown>`: each class declares its
23
+ * own element shape, and several of them hold members that are legitimately
24
+ * null before the component builds them (a slide's call-to-action, a
25
+ * slider's live region). Requiring `Record<string, HTMLElement>` here would
26
+ * force every one of those back into an `{} as HTMLElement` placeholder.
27
+ */
20
28
  export interface DomMixedInstance extends EventedInstance {
21
- _el: Record<string, HTMLElement>;
29
+ _el: Record<string, unknown>;
22
30
  data?: unknown;
23
31
  }
24
32
 
@@ -115,27 +123,36 @@ export function Evented<T extends Constructor>(Base: T) {
115
123
  * and the onAdd/onRemove/onLoaded lifecycle events) to a class. */
116
124
  export function DomMixed<T extends Constructor<DomMixedInstance>>(Base: T) {
117
125
  return class extends Base {
126
+ /**
127
+ * The host element. Every DomMixed consumer keeps its root element
128
+ * here, so this narrows the `Record<string, unknown>` element map to
129
+ * something with a style and children.
130
+ */
131
+ container(): HTMLElement {
132
+ return this._el.container as HTMLElement;
133
+ }
134
+
118
135
  /* Adding, Hiding, Showing etc
119
136
  ================================================== */
120
137
  show(animate?: unknown): void {
121
138
  if (!animate) {
122
- this._el.container.style.display = "block";
139
+ this.container().style.display = "block";
123
140
  }
124
141
  // animated show is not implemented
125
142
  }
126
143
 
127
144
  hide(): void {
128
- this._el.container.style.display = "none";
145
+ this.container().style.display = "none";
129
146
  }
130
147
 
131
148
  addTo(container: HTMLElement): this {
132
- container.appendChild(this._el.container);
149
+ container.appendChild(this.container());
133
150
  this.onAdd();
134
151
  return this;
135
152
  }
136
153
 
137
154
  removeFrom(container: HTMLElement): this {
138
- container.removeChild(this._el.container);
155
+ container.removeChild(this.container());
139
156
  this.onRemove();
140
157
  return this;
141
158
  }
@@ -143,7 +160,7 @@ export function DomMixed<T extends Constructor<DomMixedInstance>>(Base: T) {
143
160
  /* Set the Position
144
161
  ================================================== */
145
162
  setPosition(pos: Record<string, number>, el?: HTMLElement): this {
146
- const target = el || this._el.container;
163
+ const target = el || this.container();
147
164
  for (const name of Object.keys(pos)) {
148
165
  (target.style as unknown as Record<string, string>)[name] = pos[name] + "px";
149
166
  }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * A minimal registry of the viewers alive on this page.
3
+ *
4
+ * This is deliberately *not* a viewer singleton: nothing here owns or replaces
5
+ * a viewer, and the viewer itself holds all of its own state. It exists only
6
+ * so a viewer can answer the one question it cannot answer alone — "given a
7
+ * keypress or a focus change on the document, is this viewer the one that
8
+ * should react?" Two page-wide inputs (the arrow keys and `document.activeElement`)
9
+ * can only be attributed to a single viewer by comparing against the others.
10
+ *
11
+ * Entries are removed in `dispose()`, which is what keeps the registry from
12
+ * outliving the page content it describes.
13
+ */
14
+
15
+ /** What a viewer has to expose to take part in page-wide arbitration. */
16
+ export interface Participant {
17
+ /** The viewer's root element, or null before layout. */
18
+ readonly element: HTMLElement | null;
19
+ /** Higher means more recently interacted. */
20
+ readonly interaction: number;
21
+ }
22
+
23
+ /** Monotonic interaction clock. Shared, but it carries no state of its own. */
24
+ let interactionClock = 0;
25
+
26
+ const participants = new Set<Participant>();
27
+
28
+ /** Stamp an interaction. Returns the new value, for the caller to store. */
29
+ export function markInteraction(): number {
30
+ interactionClock += 1;
31
+ return interactionClock;
32
+ }
33
+
34
+ /** The interaction value currently held by the most recent participant. */
35
+ function latestInteraction(): number {
36
+ let latest = -1;
37
+ for (const participant of participants) {
38
+ if (participant.interaction > latest) {
39
+ latest = participant.interaction;
40
+ }
41
+ }
42
+ return latest;
43
+ }
44
+
45
+ /** Add a viewer. Call from the same place that registers its listeners. */
46
+ export function registerParticipant(participant: Participant): void {
47
+ participants.add(participant);
48
+ }
49
+
50
+ /** Remove a viewer. Call from `dispose()`. */
51
+ export function unregisterParticipant(participant: Participant): void {
52
+ participants.delete(participant);
53
+ }
54
+
55
+ /** The registered viewer whose element contains `node`, if any. */
56
+ export function participantFor(node: Node | null): Participant | null {
57
+ if (!node) {
58
+ return null;
59
+ }
60
+ for (const participant of participants) {
61
+ if (participant.element && participant.element.contains(node)) {
62
+ return participant;
63
+ }
64
+ }
65
+ return null;
66
+ }
67
+
68
+ /**
69
+ * The viewer that should react to a page-wide event.
70
+ *
71
+ * Focus wins over recency: if the visitor has focused one viewer, arrow keys
72
+ * belong to it even if they last clicked a different one. With nothing focused
73
+ * — the common case, since clicking a `<div>` does not move focus — the most
74
+ * recently interacted viewer takes it, so the viewer the visitor just used is
75
+ * the one that responds.
76
+ *
77
+ * @param viewer - The viewer asking whether it should react.
78
+ * @returns True if this viewer owns the event.
79
+ */
80
+ export function ownsPageEvent(viewer: Participant): boolean {
81
+ const active = typeof document === "undefined" ? null : document.activeElement;
82
+ // body (or null) means the visitor has focused nothing viewer-specific
83
+ const focused = participantFor(active);
84
+ if (focused) {
85
+ return focused === viewer;
86
+ }
87
+ // no viewer holds focus, so the most recently interacted one responds
88
+ return viewer.interaction === latestInteraction();
89
+ }
90
+
91
+ /** The viewers currently registered. Test helper. */
92
+ export function registeredParticipants(): readonly Participant[] {
93
+ return [...participants];
94
+ }