@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
@@ -1,16 +1,55 @@
1
- import { mergeData } from "../core/Util";
1
+ import { mergeData, ratio, clearTimer } from "../core/Util";
2
2
  import { Evented, type EventedInstance } from "../core/mixins";
3
3
  import Dom from "../dom/Dom";
4
4
  import Message from "../ui/Message";
5
5
  import { Browser } from "../core/Browser";
6
6
  import { MediaState, StorymapSlideMedia } from "../types";
7
- import { consentManagerOf, consentMessage } from "../storymap/Consent";
7
+ import { Language } from "../language/Language";
8
+ import { consentManagerOf, consentMessage, mediaService } from "../storymap/Consent";
9
+ import { validateWebURL, sanitizeSlideText } from "./EmbedUtil";
8
10
  import { loadJS } from "../core/Load";
9
11
  /* VCO.Media
10
12
  Main media template for media assets.
11
13
  Takes a data object and populates a dom object
12
14
  ================================================== */
13
15
 
16
+ /**
17
+ * The icon glyph each media type shows in its error state.
18
+ *
19
+ * The icon font does not carry a glyph for every registered type, and the
20
+ * class used to be built straight from `media_type`, so dailymotion, audio,
21
+ * googledocs, iframe, documentcloud, juxtapose and website rendered an empty
22
+ * box next to the error text. The mapping is explicit rather than derived, so
23
+ * a type added later falls back to a real glyph instead of a blank one —
24
+ * `tests/embed-util-media.test.ts` checks the whole table against the font.
25
+ */
26
+ const LOAD_ERROR_ICONS: Record<string, string> = {
27
+ youtube: "youtube",
28
+ vimeo: "vimeo",
29
+ dailymotion: "video",
30
+ soundcloud: "soundcloud",
31
+ twitter: "twitter",
32
+ flickr: "flickr",
33
+ image: "image",
34
+ video: "video",
35
+ audio: "music",
36
+ googledocs: "doc",
37
+ wikipedia: "wikipedia",
38
+ iframe: "web",
39
+ facebook: "facebook",
40
+ documentcloud: "doc",
41
+ juxtapose: "image",
42
+ blockquote: "blockquote",
43
+ website: "web",
44
+ };
45
+
46
+ const GENERIC_LOAD_ERROR_ICON = "web";
47
+
48
+ export function loadErrorIcon(media_type: unknown): string {
49
+ const type = typeof media_type === "string" ? media_type : "";
50
+ return LOAD_ERROR_ICONS[type] ?? GENERIC_LOAD_ERROR_ICON;
51
+ }
52
+
14
53
  /* Options for Media and its subclasses: the fields Media itself sets
15
54
  or reads. Everything else merged in via mergeData is absorbed by the
16
55
  index signature. */
@@ -29,30 +68,50 @@ export interface MediaOptions {
29
68
  /* Data for media assets: the shared exchange format plus the link
30
69
  fields the base layout renders. */
31
70
  export interface MediaData extends StorymapSlideMedia {
32
- uniqueid?: string;
33
- link?: string;
34
- link_target?: string;
71
+ uniqueid?: string | null;
72
+ link?: string | null;
73
+ link_target?: string | null;
35
74
  [key: string]: unknown;
36
75
  }
37
76
 
77
+ /**
78
+ * The media element cache.
79
+ *
80
+ * `content_item`, `content_link`, `caption` and `credit` are only created
81
+ * once `_loadMedia()` / `showMeta()` have run, so they start out null rather
82
+ * than as an empty element — that is what the "did the load happen?" check is
83
+ * for, and pretending otherwise with `{} as HTMLElement` is what let the
84
+ * consent-denied path dereference `undefined.style`.
85
+ */
86
+ type MediaElements = {
87
+ container: HTMLElement;
88
+ content_container: HTMLElement;
89
+ content: HTMLElement;
90
+ content_item: HTMLElement | null;
91
+ content_link: HTMLElement | null;
92
+ source_item: HTMLSourceElement | null;
93
+ caption: HTMLElement | null;
94
+ credit: HTMLElement | null;
95
+ parent: HTMLElement;
96
+ link: HTMLElement | null;
97
+ };
98
+
38
99
  export class MediaBase {
39
- declare "_el": Record<string, HTMLElement>;
100
+ declare "_el": MediaElements;
40
101
  declare "player": unknown;
41
- declare "timer": ReturnType<typeof setTimeout>;
42
- declare "load_timer": ReturnType<typeof setTimeout>;
102
+ declare "timer": ReturnType<typeof setTimeout> | null;
103
+ declare "load_timer": ReturnType<typeof setTimeout> | null;
43
104
  declare "load_controller": AbortController | null;
44
- declare "message": Message;
105
+ declare "message": Message | null;
45
106
  declare "media_id": unknown;
46
107
  declare "_state": MediaState;
108
+ declare "_disposed": boolean;
47
109
  declare "data": MediaData;
48
110
  declare "options": MediaOptions;
49
111
  declare "animator": unknown;
50
112
  declare "_media": unknown;
51
- declare "_": (key: string) => string;
52
113
  declare "fire": EventedInstance["fire"];
53
114
 
54
- //_el: {},
55
-
56
115
  /* Constructor
57
116
  ================================================== */
58
117
  constructor(data: MediaData, options?: MediaOptions, add_to_container?: HTMLElement) {
@@ -61,8 +120,9 @@ export class MediaBase {
61
120
  container: {} as HTMLElement,
62
121
  content_container: {} as HTMLElement,
63
122
  content: {} as HTMLElement,
64
- content_item: {} as HTMLElement,
65
- content_link: {} as HTMLElement,
123
+ content_item: null,
124
+ content_link: null,
125
+ source_item: null,
66
126
  caption: null,
67
127
  credit: null,
68
128
  parent: {} as HTMLElement,
@@ -89,6 +149,7 @@ export class MediaBase {
89
149
  show_meta: false,
90
150
  media_loaded: false,
91
151
  };
152
+ this._disposed = false;
92
153
 
93
154
  // Data
94
155
  this.data = {
@@ -132,8 +193,14 @@ export class MediaBase {
132
193
  const manager = consentManagerOf(this.options);
133
194
  if (this.options.consent_required && manager && this.options.media_type) {
134
195
  // GDPR consent mode: ask before loading anything from this
135
- // external service
136
- const service = this.options.media_type as string;
196
+ // external service. media_type is the stable identity (the
197
+ // MediaType slug) and media_name is the display name — using
198
+ // the slug as the label made this panel ask "Load content from
199
+ // youtube?" where the start-of-story dialog said "YouTube".
200
+ const service = mediaService(
201
+ this.options.media_type,
202
+ (this.options.media_name as string | undefined) ?? "",
203
+ );
137
204
  let host = "";
138
205
  try {
139
206
  host = new URL((this.data as { url?: string })?.url ?? "").host;
@@ -175,49 +242,65 @@ export class MediaBase {
175
242
  const target = (this._el.content_container ?? this._el.container) as HTMLElement;
176
243
  const blocked = document.createElement("div");
177
244
  blocked.className = "vco-consent-blocked";
245
+ // the display name, not the slug — see loadMedia()
246
+ const service_name =
247
+ (this.options.media_name as string | undefined) ||
248
+ (this.options.media_type as string | undefined) ||
249
+ "";
178
250
  blocked.textContent = (
179
251
  consentMessage("consent_blocked", "Content from {service} is blocked.") as string
180
- ).replace("{service}", (this.options.media_type as string) ?? "");
252
+ ).replace("{service}", service_name);
181
253
  target.append(blocked);
182
254
  this.onLoaded(true);
183
255
  }
184
256
 
185
- loadingMessage() {
186
- this.message.updateMessage(this._("loading") + " " + this.options.media_name);
257
+ /**
258
+ * Look up a UI string for the active language, falling back to the
259
+ * English default (which `setLanguage` already merges in) and finally to
260
+ * the key itself, so a missing translation degrades to something visible
261
+ * rather than "undefined".
262
+ */
263
+ _(key: string): string {
264
+ return Language.messages[key] ?? key;
265
+ }
266
+
267
+ /** Show the loading overlay for this media item. */
268
+ loadingMessage(): void {
269
+ this.message?.updateMessage(this._("loading") + " " + this.options.media_name);
187
270
  }
188
271
 
189
272
  updateMediaDisplay(layout?: string) {
190
- if (this._state.loaded) {
191
- this._updateMediaDisplay(layout);
273
+ // `content_item` only exists once a media type's `_loadMedia()` has
274
+ // run. It stays null when a consent decision blocked the load, so the
275
+ // sizing below has to be skipped rather than dereference a
276
+ // placeholder — that was the crash on the denied path.
277
+ const content_item = this._el.content_item;
278
+ if (!this._state.loaded || !content_item) {
279
+ return;
280
+ }
192
281
 
193
- if (!Browser.mobile && layout !== "portrait") {
194
- this._el.content_item.style.maxHeight = this.options.height / 2 + "px";
195
- }
282
+ this._updateMediaDisplay(layout);
196
283
 
197
- if (this._state.media_loaded) {
198
- if (this._el.credit) {
199
- this._el.credit.style.width = "auto";
200
- }
201
- if (this._el.caption) {
202
- this._el.caption.style.width = "auto";
203
- }
204
- }
284
+ if (!Browser.mobile && layout !== "portrait") {
285
+ content_item.style.maxHeight = Number(this.options.height ?? 0) / 2 + "px";
286
+ }
205
287
 
206
- if (layout === "portrait") {
207
- this._el.content_item.style.maxHeight = "none";
288
+ if (layout === "portrait") {
289
+ content_item.style.maxHeight = "none";
290
+ }
291
+ if (this._state.media_loaded) {
292
+ const width = content_item.offsetWidth + "px";
293
+ if (this._el.credit) {
294
+ this._el.credit.style.width = width;
208
295
  }
209
- if (this._state.media_loaded) {
210
- if (this._el.credit) {
211
- this._el.credit.style.width = this._el.content_item.offsetWidth + "px";
212
- }
213
- if (this._el.caption) {
214
- this._el.caption.style.width = this._el.content_item.offsetWidth + "px";
215
- }
296
+ if (this._el.caption) {
297
+ this._el.caption.style.width = width;
216
298
  }
217
299
  }
218
300
  }
219
301
 
220
- /* Media Specific
302
+ /**
303
+ * Media Specific
221
304
  ================================================== */
222
305
  _loadMedia() {}
223
306
 
@@ -225,6 +308,24 @@ export class MediaBase {
225
308
  //this._el.content_item.style.maxHeight = (this.options.height - this.options.credit_height - this.options.caption_height - 16) + "px";
226
309
  }
227
310
 
311
+ /** Size the media box to the `height` option, in pixels. */
312
+ _sizeContentItemToOptionHeight(): void {
313
+ if (this._el.content_item) {
314
+ this._el.content_item.style.height = Number(this.options.height ?? 0) + "px";
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Size the media box to a 16:9 box matching its current width — the
320
+ * aspect ratio every video provider embeds at.
321
+ */
322
+ _sizeContentItemTo16x9(): void {
323
+ const item = this._el.content_item;
324
+ if (item) {
325
+ item.style.height = ratio.r16_9({ w: item.offsetWidth }) + "px";
326
+ }
327
+ }
328
+
228
329
  /* Public
229
330
  ================================================== */
230
331
  show() {}
@@ -241,6 +342,45 @@ export class MediaBase {
241
342
  this.onRemove();
242
343
  }
243
344
 
345
+ /**
346
+ * Terminal teardown: cancel pending work, stop playback, release the
347
+ * media-specific player and drop the DOM. Called by `Slide.dispose()`;
348
+ * the media must not be used afterwards.
349
+ */
350
+ dispose() {
351
+ if (this._disposed) {
352
+ return;
353
+ }
354
+ this._disposed = true;
355
+
356
+ // a load queued by loadMedia() that never started
357
+ if (this.load_timer) {
358
+ clearTimer(this.load_timer);
359
+ this.load_timer = null;
360
+ }
361
+ if (this.timer) {
362
+ clearTimer(this.timer);
363
+ this.timer = null;
364
+ }
365
+ // an external script still in flight (YouTube, Vimeo, Twitter, ...)
366
+ this.load_controller?.abort();
367
+ this.load_controller = null;
368
+
369
+ // let the concrete type tear down its player before the DOM goes away
370
+ this._disposeMedia();
371
+
372
+ this._el.container?.getAnimations?.().forEach((a) => a.cancel());
373
+ this.message?.dispose?.();
374
+ this.message = null;
375
+ this._el.container?.remove();
376
+ }
377
+
378
+ /**
379
+ * Media-specific teardown. Overridden by types that own a player, an
380
+ * embedded document or a listening widget.
381
+ */
382
+ _disposeMedia() {}
383
+
244
384
  // Update Display
245
385
  updateDisplay(w?: number, h?: number, l?: string) {
246
386
  this._updateDisplay(w, h, l);
@@ -251,7 +391,8 @@ export class MediaBase {
251
391
  // load delay): avoids building iframes and injecting scripts for a
252
392
  // slide the visitor already skipped past
253
393
  if (!this._state.loaded && this.load_timer) {
254
- clearTimeout(this.load_timer);
394
+ clearTimer(this.load_timer);
395
+ this.load_timer = null;
255
396
  this.load_timer = null;
256
397
  }
257
398
  // Abort an in-flight external script load, if any
@@ -288,8 +429,18 @@ export class MediaBase {
288
429
  "vco-media-item vco-media-loaderror",
289
430
  this._el.content,
290
431
  );
291
- this._el.content_item.innerHTML =
292
- "<div class='vco-icon-" + this.options.media_type + "'></div><p>" + message + "</p>";
432
+ // The icon font has no glyph for every media type (dailymotion, audio,
433
+ // googledocs, iframe, documentcloud, juxtapose and website had none),
434
+ // and the class was built by string concatenation, so those error
435
+ // states rendered an empty box. Map the missing ones to a glyph that
436
+ // exists, and fall back to a generic "web" icon for anything added
437
+ // later.
438
+ this._el.content_item.appendChild(
439
+ Dom.create("div", `vco-icon-${loadErrorIcon(this.options.media_type)}`),
440
+ );
441
+ const text = Dom.create("p", "");
442
+ text.appendChild(document.createTextNode(message));
443
+ this._el.content_item.appendChild(text);
293
444
 
294
445
  // After Loaded
295
446
  this.onLoaded(true);
@@ -312,27 +463,35 @@ export class MediaBase {
312
463
  onMediaLoaded(e?: unknown) {
313
464
  this._state.media_loaded = true;
314
465
  this.fire("media_loaded", this.data);
466
+ const width = (this._el.content_item?.offsetWidth ?? 0) + "px";
315
467
  if (this._el.credit) {
316
- this._el.credit.style.width = this._el.content_item.offsetWidth + "px";
468
+ this._el.credit.style.width = width;
317
469
  }
318
470
  if (this._el.caption) {
319
- this._el.caption.style.width = this._el.content_item.offsetWidth + "px";
471
+ this._el.caption.style.width = width;
320
472
  }
321
473
  }
322
474
 
323
- showMeta(credit?: unknown, caption?: unknown) {
475
+ /** Render the media credit and caption, if the slide has them. */
476
+ showMeta(): void {
324
477
  this._state.show_meta = true;
325
- // Credit
326
- if (this.data.credit && this.data.credit !== "" && !this._el.credit) {
478
+ // Credit and caption are author-supplied strings that may legitimately
479
+ // contain formatting (they are documented as HTML), so they go through
480
+ // the same sanitizer as the slide text rather than straight to
481
+ // innerHTML — the JSON is untrusted input, and these were the two
482
+ // easiest stored-XSS sinks in the viewer.
483
+ const credit = this._credit();
484
+ if (credit && credit !== "" && !this._el.credit) {
327
485
  this._el.credit = Dom.create("div", "vco-credit", this._el.content_container);
328
- this._el.credit.innerHTML = this.data.credit;
486
+ this._el.credit.appendChild(sanitizeSlideText(credit));
329
487
  this.options.credit_height = this._el.credit.offsetHeight;
330
488
  }
331
489
 
332
490
  // Caption
333
- if (this.data.caption && this.data.caption !== "" && !this._el.caption) {
491
+ const caption = this._caption();
492
+ if (caption && caption !== "" && !this._el.caption) {
334
493
  this._el.caption = Dom.create("div", "vco-caption", this._el.content_container);
335
- this._el.caption.innerHTML = this.data.caption;
494
+ this._el.caption.appendChild(sanitizeSlideText(caption));
336
495
  this.options.caption_height = this._el.caption.offsetHeight;
337
496
  }
338
497
  }
@@ -347,6 +506,21 @@ export class MediaBase {
347
506
 
348
507
  /* Private Methods
349
508
  ================================================== */
509
+ /** The media URL, normalised to a string. */
510
+ _url(): string {
511
+ return typeof this.data.url === "string" ? this.data.url : "";
512
+ }
513
+
514
+ /** The media caption, or null when the slide has none. */
515
+ _caption(): string | null {
516
+ return typeof this.data.caption === "string" ? this.data.caption : null;
517
+ }
518
+
519
+ /** The media credit, or null when the slide has none. */
520
+ _credit(): string | null {
521
+ return typeof this.data.credit === "string" ? this.data.credit : null;
522
+ }
523
+
350
524
  _initLayout() {
351
525
  // Message
352
526
  this.message = new Message({}, this.options);
@@ -360,15 +534,29 @@ export class MediaBase {
360
534
  );
361
535
 
362
536
  // Link
363
- if (this.data.link && this.data.link !== "") {
537
+ // The link target comes from the storymap JSON, so it has to pass the
538
+ // same protocol check as every other author-supplied URL — otherwise a
539
+ // `javascript:` href is a click-through XSS. A rejected link falls
540
+ // through to the unlinked layout below rather than rendering a
541
+ // dead anchor.
542
+ // (`_el.content` starts as a truthy `{}` placeholder, so the branch is
543
+ // tracked with a local flag rather than a truthiness test.)
544
+ const raw_link = this.data.link;
545
+ const href = typeof raw_link === "string" ? validateWebURL(raw_link) : null;
546
+ if (href) {
364
547
  this._el.link = Dom.create("a", "vco-media-link", this._el.content_container);
365
548
  const link = this._el.link as HTMLAnchorElement;
366
- link.href = this.data.link;
549
+ link.href = href;
367
550
  if (this.data.link_target && this.data.link_target !== "") {
368
551
  link.target = this.data.link_target;
369
552
  } else {
370
553
  link.target = "_blank";
371
554
  }
555
+ if (link.target === "_blank") {
556
+ // rel=noopener so the opened page cannot reach back through
557
+ // window.opener
558
+ link.rel = "noopener noreferrer";
559
+ }
372
560
 
373
561
  this._el.content = Dom.create("div", "vco-media-content", this._el.link);
374
562
  } else {
@@ -0,0 +1,104 @@
1
+ /* embedId
2
+ Provider id extraction.
3
+
4
+ Each media type used to roll its own: six different ways of stripping the
5
+ query string, three different failure modes (throw an Error, throw a bare
6
+ string, or log a warning and carry on with `undefined`), and a fourth
7
+ divergence between the URL shapes the code accepted and the ones
8
+ `MediaType`'s routing regex actually matched.
9
+
10
+ These helpers are the single implementation. Each returns null when the
11
+ url does not carry that provider's id, and the caller decides how to
12
+ report it.
13
+ ================================================== */
14
+
15
+ /** Strip the query string and fragment. */
16
+ function withoutQuery(url: string): string {
17
+ return url.split(/[?#]/)[0];
18
+ }
19
+
20
+ /**
21
+ * The first path segment after any of `markers`, with the query string
22
+ * removed. Returns null when none of the markers is present.
23
+ */
24
+ export function segmentAfter(url: string, ...markers: RegExp[]): string | null {
25
+ for (const marker of markers) {
26
+ const match = url.match(marker);
27
+ if (match) {
28
+ const rest = url.slice((match.index ?? 0) + match[0].length);
29
+ const segment = withoutQuery(rest).split("/")[0];
30
+ if (segment) {
31
+ return segment;
32
+ }
33
+ }
34
+ }
35
+ return null;
36
+ }
37
+
38
+ /** A query parameter, via the platform parser rather than a hand-rolled split. */
39
+ export function queryParam(url: string, name: string): string | null {
40
+ try {
41
+ return new URL(url).searchParams.get(name);
42
+ } catch {
43
+ // not an absolute URL; fall back to the raw query string
44
+ const query = url.split("?")[1];
45
+ if (!query) return null;
46
+ for (const pair of query.split("&")) {
47
+ const [key, value] = pair.split("=");
48
+ if (key === name) {
49
+ return value ?? "";
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+ }
55
+
56
+ /** YouTube video id: watch, embed, short, shorts, live and youtu.be links. */
57
+ export function youtubeId(url: string): string | null {
58
+ const fromQuery = queryParam(url, "v");
59
+ if (fromQuery) {
60
+ return fromQuery;
61
+ }
62
+ return segmentAfter(
63
+ url,
64
+ /youtu\.be\//i,
65
+ /\/embed\//i,
66
+ /\/shorts\//i,
67
+ /\/live\//i,
68
+ /\/v\//i,
69
+ /[?&]v=/i,
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Vimeo video id. Watch URLs are the bare `vimeo.com/<id>` form, so the
75
+ * explicit `/video/` path alone is not enough — the previous implementation
76
+ * split on `video\/|\/\/vimeo\.com\//` and relied on the second alternative.
77
+ * Non-numeric pages (channels, albums, showcases) are rejected.
78
+ */
79
+ export function vimeoId(url: string): string | null {
80
+ const explicit = segmentAfter(url, /vimeo\.com\/video\//i, /player\.vimeo\.com\/video\//i);
81
+ if (explicit) {
82
+ return explicit;
83
+ }
84
+ return url.match(/vimeo\.com\/(\d+)(?:$|[/?#])/i)?.[1] ?? null;
85
+ }
86
+
87
+ /** Dailymotion video id from a watch or embed link. */
88
+ export function dailymotionId(url: string): string | null {
89
+ return segmentAfter(url, /dailymotion\.com\/video\//i, /dai\.ly\//i, /\/embed\/video\//i);
90
+ }
91
+
92
+ /** Flickr photo id from a photo page URL. */
93
+ export function flickrId(url: string): string | null {
94
+ const id = segmentAfter(url, /flickr\.com\/photos\/[^/]+\//i);
95
+ if (id && /^\d+$/.test(id)) {
96
+ return id;
97
+ }
98
+ return null;
99
+ }
100
+
101
+ /** Tweet id from a twitter.com or x.com status URL. */
102
+ export function tweetId(url: string): string | null {
103
+ return segmentAfter(url, /\/status(?:es)?\//i);
104
+ }
@@ -1,87 +1,14 @@
1
- import { Media } from "../Media";
2
- import Dom from "../../dom/Dom";
3
- import { Language } from "../../language/Language";
1
+ import { HtmlMediaBase, type HtmlMediaKind } from "./HtmlMedia";
4
2
 
5
- /* Media.Vimeo
3
+ /* Media.Audio
4
+ An <audio> element with a single <source> child.
6
5
  ================================================== */
7
6
 
8
- export default class Audio extends Media {
9
- declare "player_element": HTMLMediaElement;
10
-
11
- /* Load the media
12
- ================================================== */
13
- _loadMedia() {
14
- // Loading Message
15
- this.message.updateMessage(Language.messages.loading + " " + this.options.media_name);
16
-
17
- // Create Dom element
18
- this._el.content_item = Dom.create(
19
- "audio",
20
- "vco-media-item vco-media-audio vco-media-shadow",
21
- this._el.content,
22
- );
23
- const media_item = this._el.content_item as HTMLMediaElement;
24
- media_item.controls = true;
25
- this._el.source_item = Dom.create("source", "", this._el.content_item);
26
- const source_item = this._el.source_item as HTMLSourceElement;
27
-
28
- // Media Loaded Event
29
- media_item.addEventListener("canplay", () => {
30
- this.onLoaded();
31
- });
32
-
33
- // Load Error Event (the source element fires it, not the media element)
34
- source_item.addEventListener("error", () => {
35
- this.loadErrorDisplay(Language.messages.error + " " + this.options.media_name);
36
- });
37
-
38
- source_item.src = this.data.url;
39
- const media_type = this._getType(this.data.url, this.data.mediatype.match_str);
40
- if (media_type) {
41
- source_item.type = media_type;
42
- }
43
- // append as a text node: re-serializing innerHTML would replace the
44
- // source element and drop its error listener, leaving the loading
45
- // message on screen forever
46
- media_item.appendChild(
47
- document.createTextNode(
48
- "Your browser doesn't support HTML5 audio with " + source_item.type,
49
- ),
50
- );
51
- this.player_element = media_item;
52
- }
53
-
54
- // Update Media Display
55
- _updateMediaDisplay() {
56
- // audio height is CSS-driven; nothing to compute here
57
- }
58
-
59
- _stopMedia() {
60
- if (this.player_element) {
61
- this.player_element.pause();
62
- }
63
- }
64
-
65
- _getType(url: string, reg: string | RegExp) {
66
- const ext = url.match(reg);
67
- let type = "audio/";
68
- if (!ext) {
69
- return type;
70
- }
71
- switch (ext[1]) {
72
- case "mp3":
73
- type += "mpeg";
74
- break;
75
- case "wav":
76
- type += "wav";
77
- break;
78
- case "m4a":
79
- type += "mp4";
80
- break;
81
- default:
82
- type = "";
83
- break;
84
- }
85
- return type;
7
+ export default class Audio extends HtmlMediaBase {
8
+ protected spec() {
9
+ return {
10
+ kind: "audio" as HtmlMediaKind,
11
+ extensions: { mp3: "mpeg", wav: "wav", m4a: "mp4" },
12
+ };
86
13
  }
87
14
  }
@@ -1,6 +1,5 @@
1
1
  import { Media } from "../Media";
2
2
  import Dom from "../../dom/Dom";
3
- import { Language } from "../../language/Language";
4
3
  import { sanitizeBlockquote } from "../EmbedUtil";
5
4
 
6
5
  /* Media.Blockquote
@@ -13,7 +12,7 @@ export default class Blockquote extends Media {
13
12
  ================================================== */
14
13
  _loadMedia() {
15
14
  // Loading Message
16
- this.message.updateMessage(Language.messages.loading + " " + this.options.media_name);
15
+ this.loadingMessage();
17
16
 
18
17
  // Create Dom element
19
18
  this._el.content_item = Dom.create(
@@ -23,7 +22,7 @@ export default class Blockquote extends Media {
23
22
  );
24
23
 
25
24
  // Get Media ID
26
- this.media_id = this.data.url;
25
+ this.media_id = this._url();
27
26
 
28
27
  // The url field holds user-pasted blockquote markup. Sanitize it
29
28
  // instead of injecting the raw markup, which would allow stored