@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,6 +1,7 @@
1
- import { mergeData, setData, htmlify, convertUnixTime } from "../../core/Util";
1
+ import { mergeData, htmlify, convertUnixTime } from "../../core/Util";
2
2
  import { Evented, type EventedInstance } from "../../core/mixins";
3
3
  import Dom from "../../dom/Dom";
4
+ import { sanitizeSlideText } from "../EmbedUtil";
4
5
 
5
6
  interface TextData {
6
7
  uniqueid?: string | null;
@@ -34,11 +35,14 @@ class TextBase {
34
35
  start_btn: {} as HTMLElement,
35
36
  };
36
37
 
37
- // Data
38
+ // Data. The placeholders were the literal strings "headline" and
39
+ // "text", which mergeData only overwrites when the key is present — so
40
+ // a slide with body text and no headline rendered an <h2>headline</h2>.
41
+ // The renderer guards both on `!== ""`, so empty is the real default.
38
42
  this.data = {
39
43
  uniqueid: "",
40
- headline: "headline",
41
- text: "text",
44
+ headline: "",
45
+ text: "",
42
46
  };
43
47
 
44
48
  // Options
@@ -46,13 +50,13 @@ class TextBase {
46
50
  title: false,
47
51
  };
48
52
 
49
- setData(this, data);
53
+ mergeData(this.data, data);
50
54
 
51
55
  // Merge Options
52
56
  mergeData(this.options, options);
53
57
 
54
58
  this._el.container = Dom.create("div", "vco-text");
55
- this._el.container.id = this.data.uniqueid;
59
+ this._el.container.id = this.data.uniqueid ?? "";
56
60
 
57
61
  this._initLayout();
58
62
 
@@ -126,21 +130,23 @@ class TextBase {
126
130
  this.addDateText(convertUnixTime(this.data.date.created_time));
127
131
  }
128
132
 
129
- // Headline
130
- if (this.data.headline !== "") {
133
+ // Headline (sanitized; issue #358 keeps formatting but drops scripts)
134
+ const headline = this.data.headline ?? "";
135
+ if (headline !== "") {
131
136
  let headline_class = "vco-headline";
132
137
  if (this.options.title) {
133
138
  headline_class = "vco-headline vco-headline-title";
134
139
  }
135
140
  this._el.headline = Dom.create("h2", headline_class, this._el.content_container);
136
- this._el.headline.innerHTML = this.data.headline;
141
+ this._el.headline.appendChild(sanitizeSlideText(headline));
137
142
  }
138
143
 
139
- // Text
140
- if (this.data.text !== "") {
144
+ // Text (sanitized; issue #358 allows extra iframe media in the text field)
145
+ const text = this.data.text ?? "";
146
+ if (text !== "") {
141
147
  let text_content = "";
142
148
 
143
- text_content += htmlify(this.data.text);
149
+ text_content += htmlify(text);
144
150
 
145
151
  // Date
146
152
  if (
@@ -157,7 +163,7 @@ class TextBase {
157
163
  }
158
164
 
159
165
  this._el.content = Dom.create("div", "vco-text-content", this._el.content_container);
160
- this._el.content.innerHTML = text_content;
166
+ this._el.content.appendChild(sanitizeSlideText(text_content));
161
167
  }
162
168
 
163
169
  // Fire event that the slide is loaded
@@ -1,6 +1,6 @@
1
1
  import { Media } from "../Media";
2
2
  import Dom from "../../dom/Dom";
3
- import { Language } from "../../language/Language";
3
+ import { sanitizeSlideText } from "../EmbedUtil";
4
4
  import { loadJSONP, uniqueGlobalName } from "../../core/Load";
5
5
 
6
6
  /* Media.Twitter
@@ -15,7 +15,7 @@ export default class Twitter extends Media {
15
15
  ================================================== */
16
16
  _loadMedia() {
17
17
  // Loading Message
18
- this.message.updateMessage(Language.messages.loading + " " + this.options.media_name);
18
+ this.loadingMessage();
19
19
 
20
20
  // Create Dom element
21
21
  this._el.content_item = Dom.create("div", "vco-media-twitter", this._el.content);
@@ -23,10 +23,12 @@ export default class Twitter extends Media {
23
23
  // Get Media ID
24
24
 
25
25
  const r = /(?:twitter\.com|x\.com)\/(.+?)\/status\/(\d+)/;
26
- const match = r.exec(this.data.url);
26
+ const match = r.exec(this._url());
27
27
  if (match) {
28
28
  this.user_id = match[1];
29
29
  this.media_id = match[2];
30
+ } else {
31
+ throw new Error("Invalid Twitter URL");
30
32
  }
31
33
  const callbackName = uniqueGlobalName(`twitterCallback_${this.media_id}`);
32
34
  const api_url = `https://api.twitter.com/1/statuses/oembed.json?id=${this.media_id}&include_entities=true&callback=${callbackName}`;
@@ -43,17 +45,22 @@ export default class Twitter extends Media {
43
45
 
44
46
  createMedia(d: unknown) {
45
47
  const data = d as { html: string; author_url: string; author_name: string };
46
- let tweet = "",
47
- tweet_text;
48
-
48
+ if (!data?.html) {
49
+ this.loadErrorDisplay("Unable to load this post.");
50
+ return;
51
+ }
52
+ // The oEmbed markup is positional: the date link follows the tweet text
53
+ // after a "&mdash;" separator. Every step below is optional so a markup
54
+ // change upstream degrades to a missing date/link rather than throwing.
55
+ const afterText = data.html.split("</p>&mdash;")[1] ?? "";
56
+ const tweetuser = data.author_url.split(/twitter\.com|x\.com\//)[1] ?? "";
57
+ const statusLink = afterText.split('<a href="')[1] ?? "";
58
+ const tweet_status_url = statusLink.split('">')[0] ?? "";
59
+ const tweet_status_date = (statusLink.split('">')[1] ?? "").split("</a>")[0] ?? "";
60
+
61
+ let tweet = "";
49
62
  // TWEET CONTENT
50
- tweet_text = data.html.split("</p>&mdash;")[0] + "</p></blockquote>";
51
- // the oembed endpoint returns x.com URLs since the rebrand — parse both
52
- const tweetuser = data.author_url.split(/twitter\.com|x\.com\//)[1];
53
- const tweet_status_temp = data.html.split("</p>&mdash;")[1].split('<a href="')[1];
54
- const tweet_status_url = tweet_status_temp.split('">')[0];
55
- const tweet_status_date = tweet_status_temp.split('">')[1].split("</a>")[0];
56
-
63
+ let tweet_text = afterText ? data.html.split("</p>&mdash;")[0] + "</p></blockquote>" : "";
57
64
  // Open links in new window
58
65
  tweet_text = tweet_text.replace(/<a href/gi, '<a target="_blank" href');
59
66
 
@@ -84,7 +91,13 @@ export default class Twitter extends Media {
84
91
  tweet += "</div>";
85
92
 
86
93
  // Add to DOM
87
- this._el.content_item.innerHTML = tweet;
94
+ // The whole tweet is assembled from the oEmbed response's own HTML
95
+ // plus five string-concatenated fields, and that API does not escape
96
+ // HTML — so the result is untrusted third-party markup and goes
97
+ // through the sanitizer. It also repairs the four unguarded
98
+ // positional splits above: a markup change upstream now degrades to a
99
+ // missing date instead of throwing.
100
+ this._el.content_item?.appendChild(sanitizeSlideText(tweet));
88
101
 
89
102
  // After Loaded
90
103
  this.onLoaded();
@@ -1,84 +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.Video
4
+ A <video> element with a single <source> child.
6
5
  ================================================== */
7
6
 
8
- export default class Video 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
- "video",
20
- "vco-media-item vco-media-video 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 video with " + source_item.type,
49
- ),
50
- );
51
- this.player_element = media_item;
52
- }
53
-
54
- // Update Media Display
55
- _updateMediaDisplay() {
56
- // video 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 = "video/";
68
- if (!ext) {
69
- return type;
70
- }
71
- switch (ext[1]) {
72
- case "mp4":
73
- type += "mp4";
74
- break;
75
- case "webm":
76
- type += "webm";
77
- break;
78
- default:
79
- type = "";
80
- break;
81
- }
82
- return type;
7
+ export default class Video extends HtmlMediaBase {
8
+ protected spec() {
9
+ return {
10
+ kind: "video" as HtmlMediaKind,
11
+ extensions: { mp4: "mp4", webm: "webm" },
12
+ };
83
13
  }
84
14
  }
@@ -1,7 +1,6 @@
1
+ import { vimeoId } from "../embedId";
1
2
  import { Media } from "../Media";
2
3
  import Dom from "../../dom/Dom";
3
- import { Language } from "../../language/Language";
4
- import { ratio } from "../../core/Util";
5
4
 
6
5
  /* Media.Vimeo
7
6
  ================================================== */
@@ -14,7 +13,7 @@ export default class Vimeo extends Media {
14
13
  ================================================== */
15
14
  _loadMedia() {
16
15
  // Loading Message
17
- this.message.updateMessage(Language.messages.loading + " " + this.options.media_name);
16
+ this.loadingMessage();
18
17
 
19
18
  // Create Dom element
20
19
  this._el.content_item = Dom.create(
@@ -24,17 +23,19 @@ export default class Vimeo extends Media {
24
23
  );
25
24
 
26
25
  // Get Media ID
27
- const parts = this.data.url.split(/video\/|\/\/vimeo\.com\//);
28
- if (!parts[1]) {
26
+ this.media_id = vimeoId(this._url()) ?? "";
27
+ if (!this.media_id) {
29
28
  throw new Error("Invalid Vimeo URL");
30
29
  }
31
- this.media_id = parts[1].split(/[?&]/)[0];
32
30
 
33
- // API URL
31
+ // API URL — plain `&` separators. The HTML entity `&amp;` is correct
32
+ // inside markup but is not decoded in a URL property, so Vimeo was
33
+ // receiving params literally named `amp;byline` / `amp;color` and
34
+ // ignoring them.
34
35
  const api_url =
35
36
  "https://player.vimeo.com/video/" +
36
37
  this.media_id +
37
- "?api=1&title=0&amp;byline=0&amp;portrait=0&amp;color=ffffff";
38
+ "?api=1&title=0&byline=0&portrait=0&color=ffffff";
38
39
 
39
40
  this.player = Dom.create("iframe", "", this._el.content_item) as HTMLIFrameElement;
40
41
  this.player.width = "100%";
@@ -48,8 +49,7 @@ export default class Vimeo extends Media {
48
49
 
49
50
  // Update Media Display
50
51
  _updateMediaDisplay() {
51
- this._el.content_item.style.height =
52
- ratio.r16_9({ w: this._el.content_item.offsetWidth }) + "px";
52
+ this._sizeContentItemTo16x9();
53
53
  }
54
54
 
55
55
  _stopMedia() {
@@ -1,24 +1,30 @@
1
1
  import { Media } from "../Media";
2
2
  import Dom from "../../dom/Dom";
3
- import { Language } from "../../language/Language";
4
3
  import { validateWebURL } from "../EmbedUtil";
5
4
 
6
5
  /* Media.Website
6
+ An <iframe> pointed straight at a URL. Shared with the DocumentCloud
7
+ type, which is the same embed with a different CSS class.
7
8
  ================================================= */
8
9
 
9
- export default class Website extends Media {
10
+ export class WebsiteBase extends Media {
10
11
  declare "media_id": string;
11
12
 
13
+ /** Extra class on the wrapper element, e.g. "vco-media-documentcloud". */
14
+ protected extraClass(): string {
15
+ return "";
16
+ }
17
+
12
18
  _loadMedia() {
13
- this.message.updateMessage(Language.messages.loading + " " + this.options.media_name);
14
- this._el.content_item = Dom.create(
15
- "div",
16
- "vco-media-item vco-media-iframe",
17
- this._el.content,
18
- );
19
- this.media_id = this.data.url;
20
- // rebuild a clean iframe from a validated src: injecting the raw
21
- // URL as markup would allow stored XSS via the storymap JSON
19
+ this.loadingMessage();
20
+
21
+ const classes = `vco-media-item vco-media-iframe ${this.extraClass()}`.trim();
22
+ const content_item = Dom.create("div", classes, this._el.content);
23
+ this._el.content_item = content_item;
24
+
25
+ this.media_id = this._url();
26
+ // rebuilt from a validated src: injecting the raw URL as markup would
27
+ // allow stored XSS via the storymap JSON
22
28
  const src = validateWebURL(this.media_id);
23
29
  if (!src) {
24
30
  this.loadErrorDisplay("Invalid URL.");
@@ -26,11 +32,15 @@ export default class Website extends Media {
26
32
  }
27
33
  const iframe = document.createElement("iframe");
28
34
  iframe.setAttribute("src", src);
29
- this._el.content_item.appendChild(iframe);
35
+ content_item.appendChild(iframe);
30
36
  this.onLoaded();
31
37
  }
32
38
 
33
39
  _updateMediaDisplay() {
34
- this._el.content_item.style.height = this.options.height + "px";
40
+ if (this._el.content_item) {
41
+ this._el.content_item.style.height = Number(this.options.height ?? 0) + "px";
42
+ }
35
43
  }
36
44
  }
45
+
46
+ export default class Website extends WebsiteBase {}
@@ -2,6 +2,7 @@ import { Media } from "../Media";
2
2
  import Dom from "../../dom/Dom";
3
3
  import { Language } from "../../language/Language";
4
4
  import { getObjectAttributeByIndex } from "../../core/Util";
5
+ import { sanitizeSlideText } from "../EmbedUtil";
5
6
  import { loadJSONP, uniqueGlobalName } from "../../core/Load";
6
7
 
7
8
  /* Media.Wikipedia
@@ -45,7 +46,7 @@ export default class Wikipedia extends Media {
45
46
  ================================================== */
46
47
  _loadMedia() {
47
48
  // Loading Message
48
- this.message.updateMessage(Language.messages.loading + " " + this.options.media_name);
49
+ this.loadingMessage();
49
50
 
50
51
  // Create Dom element
51
52
  this._el.content_item = Dom.create(
@@ -55,7 +56,7 @@ export default class Wikipedia extends Media {
55
56
  );
56
57
 
57
58
  // Get Media ID
58
- const { title, language } = parseWikipediaUrl(this.data.url);
59
+ const { title, language } = parseWikipediaUrl(this._url());
59
60
  this.media_id = title;
60
61
  // Claim a unique global slot: same-article concurrent loads must not
61
62
  // share one (the loser's script would call a deleted global).
@@ -86,9 +87,16 @@ export default class Wikipedia extends Media {
86
87
  };
87
88
 
88
89
  const pages = data.query as { pages: Record<string, unknown> };
89
- wiki.entry = getObjectAttributeByIndex(pages.pages, 0) as Record<string, string>;
90
- wiki.extract = wiki.entry.extract;
91
- wiki.title = wiki.entry.title;
90
+ const first = getObjectAttributeByIndex(pages.pages, 0) as
91
+ Record<string, string> | undefined;
92
+ if (!first) {
93
+ this.loadErrorDisplay("Unable to load this article.");
94
+ return;
95
+ }
96
+ wiki.entry = first;
97
+ // a missing extract used to throw on the next line
98
+ wiki.extract = first.extract ?? "";
99
+ wiki.title = first.title ?? "";
92
100
 
93
101
  if (wiki.extract.match("<p>")) {
94
102
  wiki.text_array = wiki.extract.split("<p>");
@@ -103,15 +111,17 @@ export default class Wikipedia extends Media {
103
111
  }
104
112
 
105
113
  content =
106
- "<h4><a href='" + this.data.url + "' target='_blank'>" + wiki.title + "</a></h4>";
114
+ "<h4><a href='" + this._url() + "' target='_blank'>" + wiki.title + "</a></h4>";
107
115
  content += "<span class='wiki-source'>" + Language.messages.wikipedia + "</span>";
108
116
  content += wiki.text;
109
117
 
110
118
  if (wiki.extract.match("REDIRECT")) {
111
119
  // redirect page: leave content empty
112
120
  } else {
113
- // Add to DOM
114
- this._el.content_item.innerHTML = content;
121
+ // Add to DOM. `wiki.text` is MediaWiki API output and
122
+ // the media URL is storymap JSON, so the assembled markup is
123
+ // untrusted — it goes through the sanitizer.
124
+ this._el.content_item?.appendChild(sanitizeSlideText(content));
115
125
  // After Loaded
116
126
  this.onLoaded();
117
127
  }
@@ -1,11 +1,14 @@
1
- import { unique_ID, getUrlVars, ratio } from "../../core/Util";
1
+ import { unique_ID, clearTimer } from "../../core/Util";
2
+ import { youtubeId, queryParam } from "../embedId";
2
3
  import { Media } from "../Media";
3
4
  import Dom from "../../dom/Dom";
4
- import { Language } from "../../language/Language";
5
5
 
6
6
  /* Media.YouTube
7
7
  ================================================== */
8
8
 
9
+ /** How long to wait for the IFrame API script before giving up (one second apart). */
10
+ const YOUTUBE_API_ATTEMPTS = 10;
11
+
9
12
  /* Minimal surface of the YouTube iframe API player used here. */
10
13
  interface YTPlayer {
11
14
  playVideo: () => void;
@@ -34,15 +37,17 @@ interface YouTubeMediaID {
34
37
  export default class YouTube extends Media {
35
38
  declare "youtube_loaded": boolean;
36
39
  declare "media_id": YouTubeMediaID;
37
- declare "player": YTPlayer;
40
+ declare "player": YTPlayer | null;
41
+ declare "_player_attempts": number;
38
42
 
39
43
  /* Load the media
40
44
  ================================================== */
41
45
  async _loadMedia() {
42
46
  // Loading Message
43
- this.message.updateMessage(Language.messages.loading + " " + this.options.media_name);
47
+ this.loadingMessage();
44
48
 
45
49
  this.youtube_loaded = false;
50
+ this._player_attempts = 0;
46
51
 
47
52
  // Create Dom element
48
53
  this._el.content_item = Dom.create(
@@ -50,28 +55,18 @@ export default class YouTube extends Media {
50
55
  "vco-media-item vco-media-youtube vco-media-shadow",
51
56
  this._el.content,
52
57
  );
53
- this._el.content_item.id = unique_ID(7);
54
-
55
- // URL Vars
56
- const url_vars = getUrlVars(this.data.url);
57
-
58
- // Get Media ID
59
- this.media_id = {};
60
-
61
- if (this.data.url.match("v=")) {
62
- this.media_id.id = url_vars["v"];
63
- } else if (this.data.url.match("/embed/")) {
64
- this.media_id.id = this.data.url.split("embed/")[1].split(/[?&]/)[0];
65
- } else if (this.data.url.match(/v\/|v=|youtu\.be\/|shorts\//)) {
66
- this.media_id.id = this.data.url
67
- .split(/v\/|v=|youtu\.be\/|shorts\//)[1]
68
- .split(/[?&]/)[0];
69
- } else {
70
- console.log("YouTube in URL but not a valid video");
58
+ (this._el.content_item as HTMLElement).id = unique_ID(7);
59
+
60
+ // Get Media ID — the previous four-way branch chain missed /live/ and
61
+ // playlist links, and fell through to a console.log while still
62
+ // building a player with `id === undefined`
63
+ const id = youtubeId(this._url());
64
+ if (!id) {
65
+ throw new Error("Invalid YouTube URL");
71
66
  }
72
-
73
- this.media_id.start = url_vars["t"];
74
- this.media_id.hd = url_vars["hd"];
67
+ this.media_id = { id: id };
68
+ this.media_id.start = queryParam(this._url(), "t") ?? undefined;
69
+ this.media_id.hd = queryParam(this._url(), "hd") ?? undefined;
75
70
 
76
71
  // API Call
77
72
  try {
@@ -85,15 +80,14 @@ export default class YouTube extends Media {
85
80
 
86
81
  // Update Media Display
87
82
  _updateMediaDisplay() {
88
- this._el.content_item.style.height =
89
- ratio.r16_9({ w: this._el.content_item.offsetWidth }) + "px";
83
+ this._sizeContentItemTo16x9();
90
84
  }
91
85
 
92
86
  _stopMedia() {
93
87
  // cancel a pending API retry so a poll cannot outlive the slide
94
- clearTimeout(this.timer);
88
+ clearTimer(this.timer);
95
89
  this.timer = null;
96
- if (this.youtube_loaded) {
90
+ if (this.youtube_loaded && this.player) {
97
91
  try {
98
92
  const yt = YT as YTGlobal;
99
93
  if (this.player.getPlayerState() === yt.PlayerState.PLAYING) {
@@ -135,11 +129,15 @@ export default class YouTube extends Media {
135
129
  }
136
130
 
137
131
  createPlayer() {
138
- clearTimeout(this.timer);
132
+ clearTimer(this.timer);
139
133
  if (typeof YT != "undefined" && typeof YT.Player != "undefined") {
140
134
  // Create Player
141
135
  const yt = YT as YTGlobal;
142
- this.player = new yt.Player(this._el.content_item.id, {
136
+ const mount_id = this._el.content_item?.id;
137
+ if (!mount_id) {
138
+ return;
139
+ }
140
+ this.player = new yt.Player(mount_id, {
143
141
  playerVars: {
144
142
  enablejsapi: 1,
145
143
  color: "white",
@@ -154,29 +152,63 @@ export default class YouTube extends Media {
154
152
  events: {
155
153
  onReady: () => {
156
154
  this.onPlayerReady();
157
- // After Loaded
158
- //this.onLoaded();
159
155
  },
160
156
  onStateChange: this.onStateChange,
161
157
  },
162
158
  });
159
+ this.onLoaded();
163
160
  } else {
161
+ // The IFrame API script has not arrived yet. Poll for it, but cap
162
+ // the attempts — an unbounded setTimeout recursion kept a timer
163
+ // alive forever on any network failure, and the loading message
164
+ // used to be dismissed by an unconditional onLoaded() below even
165
+ // though nothing was playing.
166
+ this._player_attempts = (this._player_attempts ?? 0) + 1;
167
+ if (this._player_attempts > YOUTUBE_API_ATTEMPTS) {
168
+ this.loadErrorDisplay("The YouTube player could not be loaded.");
169
+ return;
170
+ }
164
171
  this.timer = setTimeout(() => {
165
172
  this.createPlayer();
166
173
  }, 1000);
167
174
  }
168
- this.onLoaded();
175
+ }
176
+
177
+ /**
178
+ * Destroy the player, not just pause it: `stopMedia()` runs on every
179
+ * slide change, but teardown runs once and the iframe otherwise keeps
180
+ * the embedded video and its event listeners alive after disposal.
181
+ */
182
+ _disposeMedia() {
183
+ clearTimer(this.timer);
184
+ this.timer = null;
185
+ if (this.youtube_loaded) {
186
+ try {
187
+ this.player?.destroy?.();
188
+ } catch (err) {
189
+ console.log(err);
190
+ }
191
+ }
192
+ this.player = null;
193
+ this.youtube_loaded = false;
169
194
  }
170
195
 
171
196
  /* Events
172
197
  ================================================== */
173
198
  onPlayerReady(e?: unknown) {
174
199
  this.youtube_loaded = true;
175
- // the iframe replaces the placeholder div — re-resolve it, but a
176
- // slide removed in the meantime must not crash the display update
177
- const el = document.getElementById(this._el.content_item.id);
178
- if (el) {
179
- this._el.content_item = el;
200
+ // The IFrame API replaces the placeholder div with its own iframe, so
201
+ // re-resolve the mount. Prefer the element this instance already
202
+ // holds: a document-wide getElementById() reads the global id space,
203
+ // which two viewers on a page both write into, and would hand this
204
+ // player the sibling's container. The id lookup stays as a fallback
205
+ // for the case where the node really was replaced under us.
206
+ if (!this._el.content_item?.isConnected) {
207
+ const mount_id = (this._el.content_item as HTMLElement | undefined)?.id;
208
+ const found = mount_id ? document.getElementById(mount_id) : null;
209
+ if (found) {
210
+ this._el.content_item = found;
211
+ }
180
212
  }
181
213
  this.onMediaLoaded();
182
214
  this.onLoaded();
@@ -196,9 +196,7 @@
196
196
  display: block;
197
197
  line-height: $base-font-size + 2;
198
198
  padding: 10px;
199
-
200
- @include border-radius(7px);
201
-
199
+ border-radius: 7px;
202
200
  color: #8a6d3b;
203
201
  background-color: #fcf8e3;
204
202
  border: 1px solid #faebcc;