dbf-mirador 4.2.3

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 (384) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +133 -0
  3. package/dist/CommentSharp-B0J_-jEq.js +325 -0
  4. package/dist/CommentSharp-B0J_-jEq.js.map +1 -0
  5. package/dist/FailedImageContext-aQXnWSHN.js +318 -0
  6. package/dist/FailedImageContext-aQXnWSHN.js.map +1 -0
  7. package/dist/PluginHook-CFw_6P5c.js +54 -0
  8. package/dist/PluginHook-CFw_6P5c.js.map +1 -0
  9. package/dist/WindowCanvasNavigationControls-CUX723BQ.js +242 -0
  10. package/dist/WindowCanvasNavigationControls-CUX723BQ.js.map +1 -0
  11. package/dist/action-types-DjIbTE-A.js +74 -0
  12. package/dist/action-types-DjIbTE-A.js.map +1 -0
  13. package/dist/canvas-C8oHvXFN.js +41 -0
  14. package/dist/canvas-C8oHvXFN.js.map +1 -0
  15. package/dist/canvases-gRqhr-IH.js +3154 -0
  16. package/dist/canvases-gRqhr-IH.js.map +1 -0
  17. package/dist/createSvgIcon-Cwt8YPfm.js +2 -0
  18. package/dist/getters-COus7Mvg.js +2066 -0
  19. package/dist/getters-COus7Mvg.js.map +1 -0
  20. package/dist/index.module-CbBRf_R8.js +403 -0
  21. package/dist/index.module-CbBRf_R8.js.map +1 -0
  22. package/dist/mirador.es.js +39600 -0
  23. package/dist/mirador.es.js.map +1 -0
  24. package/dist/mirador.min.js +334 -0
  25. package/dist/mirador.min.js.map +1 -0
  26. package/dist/react-error-boundary.esm-B_pT-oCS.js +67 -0
  27. package/dist/react-error-boundary.esm-B_pT-oCS.js.map +1 -0
  28. package/dist/visuallyHidden-DmYvwNdU.js +98 -0
  29. package/dist/visuallyHidden-DmYvwNdU.js.map +1 -0
  30. package/dist/window-zelwU7wz.js +138 -0
  31. package/dist/window-zelwU7wz.js.map +1 -0
  32. package/package.json +138 -0
  33. package/src/components/AccessTokenSender.jsx +34 -0
  34. package/src/components/AnnotationSettings.jsx +31 -0
  35. package/src/components/AnnotationsOverlay.jsx +328 -0
  36. package/src/components/App.jsx +34 -0
  37. package/src/components/AppProviders.jsx +129 -0
  38. package/src/components/AttributionPanel.jsx +78 -0
  39. package/src/components/AudioViewer.jsx +39 -0
  40. package/src/components/BackgroundPluginArea.jsx +9 -0
  41. package/src/components/Branding.jsx +33 -0
  42. package/src/components/CanvasAnnotations.jsx +125 -0
  43. package/src/components/CanvasInfo.jsx +46 -0
  44. package/src/components/CanvasLayers.jsx +367 -0
  45. package/src/components/ChangeThemeDialog.jsx +65 -0
  46. package/src/components/CollapsibleSection.jsx +53 -0
  47. package/src/components/CollectionDialog.jsx +236 -0
  48. package/src/components/CollectionInfo.jsx +48 -0
  49. package/src/components/CompanionArea.jsx +140 -0
  50. package/src/components/CompanionWindow.jsx +228 -0
  51. package/src/components/CompanionWindowFactory.jsx +38 -0
  52. package/src/components/CompanionWindowSection.jsx +11 -0
  53. package/src/components/CustomPanel.jsx +22 -0
  54. package/src/components/ErrorContent.jsx +65 -0
  55. package/src/components/ErrorDialog.jsx +47 -0
  56. package/src/components/FullScreenButton.jsx +47 -0
  57. package/src/components/GalleryView.jsx +36 -0
  58. package/src/components/GalleryViewThumbnail.jsx +160 -0
  59. package/src/components/IIIFAuthentication.jsx +144 -0
  60. package/src/components/IIIFDropTarget.jsx +168 -0
  61. package/src/components/IIIFIFrameCommunication.jsx +45 -0
  62. package/src/components/IIIFResourceLabel.jsx +26 -0
  63. package/src/components/IIIFThumbnail.jsx +214 -0
  64. package/src/components/ImageFailureMessage.jsx +42 -0
  65. package/src/components/LabelValueMetadata.jsx +46 -0
  66. package/src/components/LanguageSettings.jsx +46 -0
  67. package/src/components/LayersPanel.jsx +24 -0
  68. package/src/components/LocalePicker.jsx +56 -0
  69. package/src/components/ManifestForm.jsx +79 -0
  70. package/src/components/ManifestInfo.jsx +67 -0
  71. package/src/components/ManifestListItem.jsx +209 -0
  72. package/src/components/ManifestListItemError.jsx +53 -0
  73. package/src/components/ManifestRelatedLinks.jsx +154 -0
  74. package/src/components/MinimalWindow.jsx +104 -0
  75. package/src/components/MiradorMenuButton.jsx +71 -0
  76. package/src/components/MosaicRenderPreview.jsx +16 -0
  77. package/src/components/NestedMenu.jsx +42 -0
  78. package/src/components/NewBrowserWindow.jsx +34 -0
  79. package/src/components/OpenSeadragonComponent.jsx +258 -0
  80. package/src/components/OpenSeadragonTileSource.jsx +105 -0
  81. package/src/components/OpenSeadragonViewer.jsx +161 -0
  82. package/src/components/PluginHook.jsx +27 -0
  83. package/src/components/PrimaryWindow.jsx +115 -0
  84. package/src/components/SanitizedHtml.jsx +37 -0
  85. package/src/components/ScrollIndicatedDialogContent.jsx +60 -0
  86. package/src/components/ScrollTo.jsx +61 -0
  87. package/src/components/SearchHit.jsx +220 -0
  88. package/src/components/SearchPanel.jsx +80 -0
  89. package/src/components/SearchPanelControls.jsx +174 -0
  90. package/src/components/SearchPanelNavigation.jsx +84 -0
  91. package/src/components/SearchResults.jsx +138 -0
  92. package/src/components/SelectCollection.jsx +44 -0
  93. package/src/components/SidebarIndexItem.jsx +11 -0
  94. package/src/components/SidebarIndexList.jsx +73 -0
  95. package/src/components/SidebarIndexTableOfContents.jsx +170 -0
  96. package/src/components/SidebarIndexThumbnail.jsx +22 -0
  97. package/src/components/TextViewer.jsx +37 -0
  98. package/src/components/ThumbnailCanvasGrouping.jsx +122 -0
  99. package/src/components/ThumbnailNavigation.jsx +231 -0
  100. package/src/components/VideoViewer.jsx +35 -0
  101. package/src/components/ViewerInfo.jsx +55 -0
  102. package/src/components/ViewerNavigation.jsx +72 -0
  103. package/src/components/Window.jsx +150 -0
  104. package/src/components/WindowAuthenticationBar.jsx +176 -0
  105. package/src/components/WindowCanvasNavigationControls.jsx +91 -0
  106. package/src/components/WindowList.jsx +71 -0
  107. package/src/components/WindowListButton.jsx +57 -0
  108. package/src/components/WindowSideBar.jsx +48 -0
  109. package/src/components/WindowSideBarAnnotationsPanel.jsx +51 -0
  110. package/src/components/WindowSideBarButtons.jsx +151 -0
  111. package/src/components/WindowSideBarCanvasPanel.jsx +165 -0
  112. package/src/components/WindowSideBarCollectionPanel.jsx +179 -0
  113. package/src/components/WindowSideBarInfoPanel.jsx +67 -0
  114. package/src/components/WindowThumbnailSettings.jsx +130 -0
  115. package/src/components/WindowTopBar.jsx +125 -0
  116. package/src/components/WindowTopBarPluginArea.jsx +8 -0
  117. package/src/components/WindowTopBarPluginMenu.jsx +78 -0
  118. package/src/components/WindowTopBarTitle.jsx +73 -0
  119. package/src/components/WindowTopMenu.jsx +81 -0
  120. package/src/components/WindowTopMenuButton.jsx +49 -0
  121. package/src/components/WindowViewSettings.jsx +95 -0
  122. package/src/components/WindowViewer.jsx +26 -0
  123. package/src/components/Workspace.jsx +124 -0
  124. package/src/components/WorkspaceAdd.jsx +221 -0
  125. package/src/components/WorkspaceAddButton.jsx +73 -0
  126. package/src/components/WorkspaceArea.jsx +66 -0
  127. package/src/components/WorkspaceControlPanel.jsx +85 -0
  128. package/src/components/WorkspaceControlPanelButtons.jsx +20 -0
  129. package/src/components/WorkspaceDialog.jsx +11 -0
  130. package/src/components/WorkspaceElastic.jsx +88 -0
  131. package/src/components/WorkspaceElasticWindow.jsx +79 -0
  132. package/src/components/WorkspaceExport.jsx +99 -0
  133. package/src/components/WorkspaceImport.jsx +89 -0
  134. package/src/components/WorkspaceMenu.jsx +126 -0
  135. package/src/components/WorkspaceMenuButton.jsx +42 -0
  136. package/src/components/WorkspaceMosaic.jsx +149 -0
  137. package/src/components/WorkspaceOptionsButton.jsx +35 -0
  138. package/src/components/WorkspaceOptionsMenu.jsx +112 -0
  139. package/src/components/WorkspaceSelectionDialog.jsx +165 -0
  140. package/src/components/ZoomControls.jsx +57 -0
  141. package/src/components/icons/BookViewIcon.jsx +14 -0
  142. package/src/components/icons/CanvasIndexIcon.jsx +14 -0
  143. package/src/components/icons/GalleryViewIcon.jsx +17 -0
  144. package/src/components/icons/MiradorIcon.jsx +14 -0
  145. package/src/components/icons/RestoreZoomIcon.jsx +15 -0
  146. package/src/components/icons/ThumbnailNavigationBottomIcon.jsx +15 -0
  147. package/src/components/icons/ThumbnailNavigationRightIcon.jsx +16 -0
  148. package/src/components/icons/WindowMaxIcon.jsx +15 -0
  149. package/src/components/icons/WindowMinIcon.jsx +15 -0
  150. package/src/components/icons/WindowOptionsIcon.jsx +16 -0
  151. package/src/components/icons/WorkspaceTypeElasticIcon.jsx +157 -0
  152. package/src/components/icons/WorkspaceTypeMosaicIcon.jsx +162 -0
  153. package/src/components/icons/index.js +27 -0
  154. package/src/components/index.js +108 -0
  155. package/src/config/css-ns.js +10 -0
  156. package/src/config/index.js +2 -0
  157. package/src/config/settings.js +607 -0
  158. package/src/containers/AnnotationSettings.js +27 -0
  159. package/src/containers/AnnotationsOverlay.js +51 -0
  160. package/src/containers/AppProviders.js +20 -0
  161. package/src/containers/AttributionPanel.js +20 -0
  162. package/src/containers/AudioViewer.js +16 -0
  163. package/src/containers/BackgroundPluginArea.js +4 -0
  164. package/src/containers/Branding.js +4 -0
  165. package/src/containers/CanvasAnnotations.js +32 -0
  166. package/src/containers/CanvasInfo.js +20 -0
  167. package/src/containers/CanvasLayers.js +25 -0
  168. package/src/containers/ChangeThemeDialog.js +29 -0
  169. package/src/containers/CollapsibleSection.js +3 -0
  170. package/src/containers/CollectionDialog.js +48 -0
  171. package/src/containers/CollectionInfo.js +29 -0
  172. package/src/containers/CompanionArea.js +22 -0
  173. package/src/containers/CompanionWindow.js +40 -0
  174. package/src/containers/CompanionWindowFactory.js +23 -0
  175. package/src/containers/CustomPanel.js +13 -0
  176. package/src/containers/ErrorContent.js +20 -0
  177. package/src/containers/ErrorDialog.js +28 -0
  178. package/src/containers/FullScreenButton.js +22 -0
  179. package/src/containers/GalleryView.js +23 -0
  180. package/src/containers/GalleryViewThumbnail.js +51 -0
  181. package/src/containers/IIIFAuthentication.js +86 -0
  182. package/src/containers/IIIFThumbnail.js +17 -0
  183. package/src/containers/LabelValueMetadata.js +14 -0
  184. package/src/containers/LanguageSettings.js +26 -0
  185. package/src/containers/LayersPanel.js +16 -0
  186. package/src/containers/LocalePicker.js +3 -0
  187. package/src/containers/ManifestForm.js +16 -0
  188. package/src/containers/ManifestInfo.js +29 -0
  189. package/src/containers/ManifestListItem.js +52 -0
  190. package/src/containers/ManifestListItemError.js +15 -0
  191. package/src/containers/ManifestRelatedLinks.js +28 -0
  192. package/src/containers/MinimalWindow.js +25 -0
  193. package/src/containers/MiradorMenuButton.js +7 -0
  194. package/src/containers/MosaicRenderPreview.js +14 -0
  195. package/src/containers/OpenSeadragonViewer.js +59 -0
  196. package/src/containers/PrimaryWindow.js +27 -0
  197. package/src/containers/SanitizedHtml.js +3 -0
  198. package/src/containers/ScrollIndicatedDialogContent.js +3 -0
  199. package/src/containers/SearchHit.js +71 -0
  200. package/src/containers/SearchPanel.js +23 -0
  201. package/src/containers/SearchPanelControls.js +36 -0
  202. package/src/containers/SearchPanelNavigation.js +39 -0
  203. package/src/containers/SearchResults.js +38 -0
  204. package/src/containers/SelectCollection.js +24 -0
  205. package/src/containers/SidebarIndexItem.js +8 -0
  206. package/src/containers/SidebarIndexList.js +28 -0
  207. package/src/containers/SidebarIndexTableOfContents.js +31 -0
  208. package/src/containers/SidebarIndexThumbnail.js +18 -0
  209. package/src/containers/TextViewer.js +19 -0
  210. package/src/containers/ThumbnailCanvasGrouping.js +29 -0
  211. package/src/containers/ThumbnailNavigation.js +48 -0
  212. package/src/containers/VideoViewer.js +16 -0
  213. package/src/containers/ViewerInfo.js +30 -0
  214. package/src/containers/ViewerNavigation.js +31 -0
  215. package/src/containers/Window.js +45 -0
  216. package/src/containers/WindowAuthenticationBar.js +7 -0
  217. package/src/containers/WindowCanvasNavigationControls.js +15 -0
  218. package/src/containers/WindowList.js +30 -0
  219. package/src/containers/WindowListButton.js +15 -0
  220. package/src/containers/WindowSideBar.js +20 -0
  221. package/src/containers/WindowSideBarAnnotationsPanel.js +23 -0
  222. package/src/containers/WindowSideBarButtons.js +75 -0
  223. package/src/containers/WindowSideBarCanvasPanel.js +50 -0
  224. package/src/containers/WindowSideBarCollectionPanel.js +48 -0
  225. package/src/containers/WindowSideBarInfoPanel.js +35 -0
  226. package/src/containers/WindowThumbnailSettings.js +31 -0
  227. package/src/containers/WindowTopBar.js +39 -0
  228. package/src/containers/WindowTopBarPluginArea.js +8 -0
  229. package/src/containers/WindowTopBarPluginMenu.js +7 -0
  230. package/src/containers/WindowTopBarTitle.js +17 -0
  231. package/src/containers/WindowTopMenu.js +28 -0
  232. package/src/containers/WindowTopMenuButton.js +7 -0
  233. package/src/containers/WindowViewSettings.js +30 -0
  234. package/src/containers/WindowViewer.js +10 -0
  235. package/src/containers/Workspace.js +36 -0
  236. package/src/containers/WorkspaceAdd.js +27 -0
  237. package/src/containers/WorkspaceAddButton.js +30 -0
  238. package/src/containers/WorkspaceArea.js +21 -0
  239. package/src/containers/WorkspaceControlPanel.js +10 -0
  240. package/src/containers/WorkspaceControlPanelButtons.js +7 -0
  241. package/src/containers/WorkspaceElastic.js +41 -0
  242. package/src/containers/WorkspaceElasticWindow.js +36 -0
  243. package/src/containers/WorkspaceExport.js +18 -0
  244. package/src/containers/WorkspaceImport.js +19 -0
  245. package/src/containers/WorkspaceMenu.js +30 -0
  246. package/src/containers/WorkspaceMenuButton.js +10 -0
  247. package/src/containers/WorkspaceMosaic.js +32 -0
  248. package/src/containers/WorkspaceOptionsButton.js +7 -0
  249. package/src/containers/WorkspaceOptionsMenu.js +3 -0
  250. package/src/containers/WorkspaceSelectionDialog.js +26 -0
  251. package/src/containers/ZoomControls.js +26 -0
  252. package/src/containers/index.js +94 -0
  253. package/src/contexts/FailedImageContext.js +8 -0
  254. package/src/contexts/FailedImageProvider.jsx +34 -0
  255. package/src/contexts/FullScreenContext.js +5 -0
  256. package/src/contexts/LocaleContext.js +5 -0
  257. package/src/contexts/OpenSeadragonViewerContext.js +5 -0
  258. package/src/contexts/WorkspaceContext.js +5 -0
  259. package/src/contexts/index.js +4 -0
  260. package/src/extend/PluginContext.js +5 -0
  261. package/src/extend/PluginProvider.jsx +22 -0
  262. package/src/extend/index.js +9 -0
  263. package/src/extend/pluginMapping.js +44 -0
  264. package/src/extend/pluginPreprocessing.js +45 -0
  265. package/src/extend/pluginValidation.js +58 -0
  266. package/src/extend/usePlugins.js +13 -0
  267. package/src/extend/withPlugins.jsx +46 -0
  268. package/src/hooks/index.js +4 -0
  269. package/src/hooks/useCanvasWorldService.js +10 -0
  270. package/src/hooks/useThumbnailService.js +8 -0
  271. package/src/i18n.js +71 -0
  272. package/src/index.js +18 -0
  273. package/src/init.js +20 -0
  274. package/src/lib/AnnotationFactory.js +23 -0
  275. package/src/lib/AnnotationItem.js +123 -0
  276. package/src/lib/AnnotationList.js +31 -0
  277. package/src/lib/AnnotationPage.js +41 -0
  278. package/src/lib/AnnotationResource.js +122 -0
  279. package/src/lib/CanvasAnnotationDisplay.js +148 -0
  280. package/src/lib/CanvasGroupings.js +55 -0
  281. package/src/lib/CanvasWorld.js +214 -0
  282. package/src/lib/CompanionWindowRegistry.js +23 -0
  283. package/src/lib/FullScreenHandle.jsx +76 -0
  284. package/src/lib/MiradorCanvas.js +211 -0
  285. package/src/lib/MiradorManifest.js +44 -0
  286. package/src/lib/MiradorViewer.jsx +53 -0
  287. package/src/lib/MosaicLayout.js +77 -0
  288. package/src/lib/OpenSeadragonCanvasOverlay.js +114 -0
  289. package/src/lib/ThumbnailFactory.js +310 -0
  290. package/src/lib/TruncatedHit.js +34 -0
  291. package/src/lib/asArray.js +11 -0
  292. package/src/lib/htmlRules.js +28 -0
  293. package/src/lib/iiif.js +12 -0
  294. package/src/lib/index.js +23 -0
  295. package/src/lib/readImageMetadata.js +21 -0
  296. package/src/lib/svgShapesToPath.js +49 -0
  297. package/src/locales/README.md +60 -0
  298. package/src/locales/ar/translation.json +141 -0
  299. package/src/locales/bg/translation.json +164 -0
  300. package/src/locales/de/translation.json +169 -0
  301. package/src/locales/en/translation.json +170 -0
  302. package/src/locales/et/translation.json +164 -0
  303. package/src/locales/fa/translation.json +164 -0
  304. package/src/locales/fr/translation.json +158 -0
  305. package/src/locales/hr/translation.json +164 -0
  306. package/src/locales/hu/translation.json +166 -0
  307. package/src/locales/it/translation.json +162 -0
  308. package/src/locales/ja/translation.json +156 -0
  309. package/src/locales/kr/translation.json +162 -0
  310. package/src/locales/lt/translation.json +153 -0
  311. package/src/locales/nbNo/translation.json +162 -0
  312. package/src/locales/nl/translation.json +139 -0
  313. package/src/locales/pl/translation.json +162 -0
  314. package/src/locales/ptBr/translation.json +133 -0
  315. package/src/locales/sr/translation.json +155 -0
  316. package/src/locales/sv/translation.json +157 -0
  317. package/src/locales/ta/translation.json +167 -0
  318. package/src/locales/vi/translation.json +156 -0
  319. package/src/locales/zhCn/translation.json +163 -0
  320. package/src/locales/zhTw/translation.json +163 -0
  321. package/src/plugins/OSDReferences.js +15 -0
  322. package/src/plugins/index.js +1 -0
  323. package/src/state/actions/action-types.js +76 -0
  324. package/src/state/actions/annotation.js +123 -0
  325. package/src/state/actions/auth.js +109 -0
  326. package/src/state/actions/canvas.js +65 -0
  327. package/src/state/actions/catalog.js +22 -0
  328. package/src/state/actions/companionWindow.js +107 -0
  329. package/src/state/actions/config.js +49 -0
  330. package/src/state/actions/elasticLayout.js +10 -0
  331. package/src/state/actions/errors.js +22 -0
  332. package/src/state/actions/index.js +19 -0
  333. package/src/state/actions/infoResponse.js +90 -0
  334. package/src/state/actions/layers.js +14 -0
  335. package/src/state/actions/manifest.js +65 -0
  336. package/src/state/actions/search.js +97 -0
  337. package/src/state/actions/window.js +209 -0
  338. package/src/state/actions/workspace.js +84 -0
  339. package/src/state/createPluggableStore.js +24 -0
  340. package/src/state/createStore.js +33 -0
  341. package/src/state/index.js +21 -0
  342. package/src/state/reducers/accessTokens.js +59 -0
  343. package/src/state/reducers/annotations.js +48 -0
  344. package/src/state/reducers/auth.js +43 -0
  345. package/src/state/reducers/catalog.js +30 -0
  346. package/src/state/reducers/companionWindows.js +49 -0
  347. package/src/state/reducers/config.js +39 -0
  348. package/src/state/reducers/elasticLayout.js +29 -0
  349. package/src/state/reducers/errors.js +51 -0
  350. package/src/state/reducers/index.js +15 -0
  351. package/src/state/reducers/infoResponses.js +61 -0
  352. package/src/state/reducers/layers.js +23 -0
  353. package/src/state/reducers/manifests.js +52 -0
  354. package/src/state/reducers/rootReducer.js +44 -0
  355. package/src/state/reducers/search.js +145 -0
  356. package/src/state/reducers/utils.js +45 -0
  357. package/src/state/reducers/viewers.js +42 -0
  358. package/src/state/reducers/windows.js +185 -0
  359. package/src/state/reducers/workspace.js +111 -0
  360. package/src/state/sagas/annotations.js +48 -0
  361. package/src/state/sagas/app.js +56 -0
  362. package/src/state/sagas/auth.js +151 -0
  363. package/src/state/sagas/iiif.js +233 -0
  364. package/src/state/sagas/index.js +30 -0
  365. package/src/state/sagas/windows.js +293 -0
  366. package/src/state/selectors/annotations.js +138 -0
  367. package/src/state/selectors/auth.js +100 -0
  368. package/src/state/selectors/canvases.js +260 -0
  369. package/src/state/selectors/companionWindows.js +182 -0
  370. package/src/state/selectors/config.js +100 -0
  371. package/src/state/selectors/getters.js +90 -0
  372. package/src/state/selectors/index.js +17 -0
  373. package/src/state/selectors/layers.js +79 -0
  374. package/src/state/selectors/manifests.js +433 -0
  375. package/src/state/selectors/ranges.js +147 -0
  376. package/src/state/selectors/searches.js +249 -0
  377. package/src/state/selectors/sequences.js +134 -0
  378. package/src/state/selectors/thumbnails.js +28 -0
  379. package/src/state/selectors/utils.js +18 -0
  380. package/src/state/selectors/viewer.js +19 -0
  381. package/src/state/selectors/windows.js +98 -0
  382. package/src/state/selectors/workspace.js +52 -0
  383. package/src/state/selectors/wrappers.js +15 -0
  384. package/src/styles/react-mosaic-component.js +288 -0
@@ -0,0 +1,233 @@
1
+ import { all, call, put, select, takeEvery } from 'redux-saga/effects';
2
+ import { Utils } from 'manifesto.js';
3
+ import normalizeUrl from 'normalize-url';
4
+ import ActionTypes from '../actions/action-types';
5
+ import {
6
+ receiveManifest,
7
+ receiveManifestFailure,
8
+ receiveInfoResponse,
9
+ receiveInfoResponseFailure,
10
+ receiveDegradedInfoResponse,
11
+ receiveSearch,
12
+ receiveSearchFailure,
13
+ receiveAnnotation,
14
+ receiveAnnotationFailure,
15
+ } from '../actions';
16
+ import { getManifests, getRequestsConfig, getAccessTokens, selectInfoResponse } from '../selectors';
17
+
18
+ /** */
19
+ function fetchWrapper(url, options, { success, degraded, failure }) {
20
+ return fetch(url, options)
21
+ .then((response) =>
22
+ response
23
+ .json()
24
+ .then((json) => {
25
+ if (response.status === 401) return (degraded || success)({ json, response });
26
+ if (response.ok) return success({ json, response });
27
+ return failure({ error: response.statusText, json, response });
28
+ })
29
+ .catch((error) => {
30
+ // Error responses (4xx/5xx) often have a body that isn't valid JSON
31
+ // only surface the raw parse error when the response was otherwise ok,
32
+ // where it points to an actual unexpected JSON body bug.
33
+ if (!response.ok) {
34
+ return failure({ error: `${response.status} ${response.statusText}: ${url}`, response });
35
+ }
36
+ return failure({ error, response });
37
+ }),
38
+ )
39
+ .catch((error) => failure({ error }));
40
+ }
41
+
42
+ /** */
43
+ function* fetchIiifResource(url, options, { success, degraded, failure }) {
44
+ const { preprocessors = [], postprocessors = [] } = yield select(getRequestsConfig);
45
+
46
+ try {
47
+ const reqOptions = preprocessors.reduce((acc, f) => f(url, acc) || acc, options);
48
+
49
+ let action = yield call(fetchWrapper, url, reqOptions, { degraded, failure, success });
50
+ action = postprocessors.reduce((acc, f) => f(url, acc) || acc, action);
51
+ return action;
52
+ } catch (error) {
53
+ return failure({ error });
54
+ }
55
+ }
56
+
57
+ /** */
58
+ function* fetchIiifResourceWithAuth(url, iiifResource, options, { degraded, failure, success }) {
59
+ const urlOptions = { ...options };
60
+ let tokenServiceId;
61
+
62
+ // If we have a requested IIIF resource (say, the image description from the manifest)
63
+ // we can optimistically try an appropriate access token.
64
+ //
65
+ // TODO: there might be multiple applicable access token services
66
+ if (iiifResource) {
67
+ const tokenService = yield call(getAccessTokenService, iiifResource);
68
+ tokenServiceId = tokenService && tokenService.id;
69
+
70
+ if (tokenService && tokenService.json) {
71
+ urlOptions.headers = {
72
+ Authorization: `Bearer ${tokenService.json.accessToken}`,
73
+ ...options.headers,
74
+ };
75
+ }
76
+ }
77
+
78
+ const { error, json, response } = yield call(fetchIiifResource, url, urlOptions, {
79
+ failure: (arg) => arg,
80
+ success: (arg) => arg,
81
+ });
82
+
83
+ // Hard error either requesting the resource or deserializing the JSON.
84
+ if (error) {
85
+ yield put(
86
+ failure({
87
+ error,
88
+ json,
89
+ response,
90
+ tokenServiceId,
91
+ }),
92
+ );
93
+ return;
94
+ }
95
+
96
+ const id = json['@id'] || json.id;
97
+ if (response.ok) {
98
+ if (
99
+ normalizeUrl(id, { stripAuthentication: false }) ===
100
+ normalizeUrl(url.replace(/info\.json$/, ''), { stripAuthentication: false })
101
+ ) {
102
+ yield put(success({ json, response, tokenServiceId }));
103
+ return;
104
+ }
105
+ } else if (response.status !== 401) {
106
+ yield put(
107
+ failure({
108
+ error,
109
+ json,
110
+ response,
111
+ tokenServiceId,
112
+ }),
113
+ );
114
+
115
+ return;
116
+ }
117
+
118
+ // Start attempting some IIIF Auth;
119
+ // First, the IIIF resource we were given may not be authoritative; check if
120
+ // it suggests a different access token service and re-enter the auth workflow
121
+ const authoritativeTokenService = yield call(getAccessTokenService, json);
122
+ if (authoritativeTokenService && authoritativeTokenService.id !== tokenServiceId) {
123
+ yield call(fetchIiifResourceWithAuth, url, json, options, { degraded, failure, success });
124
+ return;
125
+ }
126
+
127
+ // Record the response (potentially kicking off other auth flows)
128
+ yield put((degraded || success)({ json, response, tokenServiceId }));
129
+ }
130
+
131
+ /** */
132
+ export function* fetchManifest({ manifestId }) {
133
+ const callbacks = {
134
+ failure: ({ error, json, response }) => receiveManifestFailure(manifestId, typeof error === 'object' ? String(error) : error),
135
+ success: ({ json, response }) => receiveManifest(manifestId, json),
136
+ };
137
+ const dispatch = yield call(fetchIiifResource, manifestId, {}, callbacks);
138
+ yield put(dispatch);
139
+ }
140
+
141
+ /** @private */
142
+ function* getAccessTokenService(resource) {
143
+ const manifestoCompatibleResource = resource && resource.__jsonld ? resource : { ...resource, options: {} };
144
+ const services = Utils.getServices(manifestoCompatibleResource).filter((s) =>
145
+ s.getProfile().match(/http:\/\/iiif.io\/api\/auth\//),
146
+ );
147
+ if (services.length === 0) return undefined;
148
+
149
+ const accessTokens = yield select(getAccessTokens);
150
+ if (!accessTokens) return undefined;
151
+
152
+ for (let i = 0; i < services.length; i += 1) {
153
+ const authService = services[i];
154
+ const accessTokenService =
155
+ Utils.getService(authService, 'http://iiif.io/api/auth/1/token') ||
156
+ Utils.getService(authService, 'http://iiif.io/api/auth/0/token');
157
+ const token = accessTokenService && accessTokens[accessTokenService.id];
158
+ if (token && token.json) return token;
159
+ }
160
+
161
+ return undefined;
162
+ }
163
+
164
+ /** @private */
165
+ export function* fetchInfoResponse({ imageResource, infoId, windowId }) {
166
+ let iiifResource = imageResource;
167
+ if (!iiifResource) {
168
+ iiifResource = yield select(selectInfoResponse, { infoId });
169
+ }
170
+
171
+ const callbacks = {
172
+ degraded: ({ json, response, tokenServiceId }) =>
173
+ receiveDegradedInfoResponse(infoId, json, response.ok, tokenServiceId, windowId),
174
+ failure: ({ error, json, response, tokenServiceId }) => receiveInfoResponseFailure(infoId, error, tokenServiceId),
175
+ success: ({ json, response, tokenServiceId }) => receiveInfoResponse(infoId, json, response.ok, tokenServiceId),
176
+ };
177
+
178
+ yield call(fetchIiifResourceWithAuth, `${infoId.replace(/\/$/, '')}/info.json`, iiifResource, {}, callbacks);
179
+ }
180
+
181
+ /** @private */
182
+ export function* fetchSearchResponse({ windowId, companionWindowId, query, searchId }) {
183
+ const callbacks = {
184
+ failure: ({ error, json, response }) => receiveSearchFailure(windowId, companionWindowId, searchId, error),
185
+ success: ({ json, response }) => receiveSearch(windowId, companionWindowId, searchId, json),
186
+ };
187
+ const dispatch = yield call(fetchIiifResource, searchId, {}, callbacks);
188
+ yield put(dispatch);
189
+ }
190
+
191
+ /** @private */
192
+ export function* fetchAnnotation({ targetId, annotationId }) {
193
+ const callbacks = {
194
+ failure: ({ error, json, response }) => receiveAnnotationFailure(targetId, annotationId, error),
195
+ success: ({ json, response }) => receiveAnnotation(targetId, annotationId, json),
196
+ };
197
+ const dispatch = yield call(fetchIiifResource, annotationId, {}, callbacks);
198
+ yield put(dispatch);
199
+ }
200
+
201
+ /** */
202
+ export function* fetchResourceManifest({ manifestId, manifestJson }) {
203
+ if (manifestJson) {
204
+ yield put(receiveManifest(manifestId, manifestJson));
205
+ return;
206
+ }
207
+
208
+ if (!manifestId) return;
209
+
210
+ const manifests = yield select(getManifests) || {};
211
+ if (!manifests[manifestId]) yield* fetchManifest({ manifestId });
212
+ }
213
+
214
+ /** */
215
+ export function* fetchManifests(...manifestIds) {
216
+ const manifests = yield select(getManifests);
217
+
218
+ for (let i = 0; i < manifestIds.length; i += 1) {
219
+ const manifestId = manifestIds[i];
220
+ if (!manifests[manifestId]) yield call(fetchManifest, { manifestId });
221
+ }
222
+ }
223
+
224
+ /** */
225
+ export default function* iiifSaga() {
226
+ yield all([
227
+ takeEvery(ActionTypes.REQUEST_MANIFEST, fetchManifest),
228
+ takeEvery(ActionTypes.REQUEST_INFO_RESPONSE, fetchInfoResponse),
229
+ takeEvery(ActionTypes.REQUEST_SEARCH, fetchSearchResponse),
230
+ takeEvery(ActionTypes.REQUEST_ANNOTATION, fetchAnnotation),
231
+ takeEvery(ActionTypes.ADD_RESOURCE, fetchResourceManifest),
232
+ ]);
233
+ }
@@ -0,0 +1,30 @@
1
+ import { all, call, spawn } from 'redux-saga/effects';
2
+
3
+ import appSaga from './app';
4
+ import iiifSaga from './iiif';
5
+ import windowSaga from './windows';
6
+ import annotationsSaga from './annotations';
7
+ import authSaga from './auth';
8
+
9
+ /** */
10
+ function* launchSaga(saga) {
11
+ while (true) {
12
+ try {
13
+ yield call(saga);
14
+ break;
15
+ } catch (e) {
16
+ console.log(e);
17
+ }
18
+ }
19
+ }
20
+
21
+ /** */
22
+ function getRootSaga(pluginSagas = []) {
23
+ return function* rootSaga() {
24
+ const sagas = [annotationsSaga, appSaga, iiifSaga, windowSaga, authSaga, ...pluginSagas];
25
+
26
+ yield all(sagas.map((saga) => spawn(launchSaga, saga)));
27
+ };
28
+ }
29
+
30
+ export default getRootSaga;
@@ -0,0 +1,293 @@
1
+ import { all, call, put, select, takeEvery } from 'redux-saga/effects';
2
+ import ActionTypes from '../actions/action-types';
3
+ import {
4
+ setContentSearchCurrentAnnotation,
5
+ selectAnnotation,
6
+ setWorkspaceViewportPosition,
7
+ updateWindow,
8
+ setCanvas,
9
+ fetchSearch,
10
+ receiveManifest,
11
+ fetchInfoResponse,
12
+ showCollectionDialog,
13
+ } from '../actions';
14
+ import {
15
+ getSearchForWindow,
16
+ getSearchAnnotationsForCompanionWindow,
17
+ getCanvasGrouping,
18
+ getWindow,
19
+ getManifestoInstance,
20
+ getCompanionWindowIdsForPosition,
21
+ getManifestSearchService,
22
+ getCanvasForAnnotation,
23
+ getSelectedContentSearchAnnotationIds,
24
+ getSortedSearchAnnotationsForCompanionWindow,
25
+ getVisibleCanvasIds,
26
+ getWorkspace,
27
+ getElasticLayout,
28
+ getCanvases,
29
+ selectInfoResponses,
30
+ getWindowConfig,
31
+ getMiradorCanvasWrapper,
32
+ getMiradorManifestWrapper,
33
+ } from '../selectors';
34
+ import { fetchManifests } from './iiif';
35
+ import { getIiifResourceImageService } from '../../lib/iiif';
36
+
37
+ /** */
38
+ export function* fetchWindowManifest(action) {
39
+ const { collectionPath, id, manifestId } = action.payload || action.window;
40
+
41
+ if (!manifestId) return;
42
+
43
+ if (action.manifest) {
44
+ yield put(receiveManifest(manifestId, action.manifest));
45
+ } else {
46
+ yield call(fetchManifests, manifestId, ...(collectionPath || []));
47
+ }
48
+
49
+ yield call(setWindowStartingCanvas, action);
50
+ yield call(setWindowDefaultSearchQuery, action);
51
+ if (!collectionPath) {
52
+ yield call(setCollectionPath, { manifestId, windowId: action.id || action.window.id });
53
+ }
54
+ yield call(determineAndShowCollectionDialog, manifestId, id);
55
+ }
56
+
57
+ /** */
58
+ export function* setCanvasOnNewSequence(action) {
59
+ const windowId = action.id;
60
+ if (!action || !action.payload || !action.payload.sequenceId) return;
61
+
62
+ const canvases = yield select(getCanvases, { windowId });
63
+ if (!canvases || !canvases[0] || !canvases[0].id) return;
64
+
65
+ const thunk = yield call(setCanvas, windowId, canvases[0].id);
66
+ yield put(thunk);
67
+ }
68
+
69
+ /** */
70
+ export function* setCollectionPath({ manifestId, windowId }) {
71
+ const manifestoInstance = yield select(getManifestoInstance, { manifestId });
72
+
73
+ if (manifestoInstance) {
74
+ const partOfs = manifestoInstance.getProperty('partOf');
75
+ const partOf = Array.isArray(partOfs) ? partOfs[0] : partOfs;
76
+
77
+ if (partOf && partOf.id) {
78
+ yield put(updateWindow(windowId, { collectionPath: [partOf.id] }));
79
+ }
80
+ }
81
+ }
82
+
83
+ /** */
84
+ export function* fetchCollectionManifests(action) {
85
+ const { collectionPath } = action.payload;
86
+ if (!collectionPath) return;
87
+
88
+ yield call(fetchManifests, ...collectionPath);
89
+ }
90
+
91
+ /** @private */
92
+ export function* setWindowStartingCanvas(action) {
93
+ const { canvasId, canvasIndex, manifestId } = action.payload || action.window;
94
+
95
+ const windowId = action.id || action.window.id;
96
+
97
+ if (canvasId) {
98
+ // Preserve viewport when initialViewerConfig exists, event if the preserveViewport OSD setting is set to false
99
+ const preserveViewport = !!action.payload || !!action.window?.initialViewerConfig;
100
+ // When canvasId is explicitly provided, always pass preserveViewport flag
101
+ const thunk = yield call(setCanvas, windowId, canvasId, null, { preserveViewport });
102
+ yield put(thunk);
103
+ } else {
104
+ const getMiradorManifest = yield select(getMiradorManifestWrapper);
105
+ const manifestoInstance = yield select(getManifestoInstance, { manifestId });
106
+ if (manifestoInstance) {
107
+ // set the startCanvas
108
+ const miradorManifest = getMiradorManifest(manifestoInstance);
109
+ const startCanvas =
110
+ miradorManifest.startCanvas || miradorManifest.canvasAt(canvasIndex || 0) || miradorManifest.canvasAt(0);
111
+ if (startCanvas) {
112
+ const preserveViewport = !!action.payload || !!action.window?.initialViewerConfig;
113
+ // When canvas is calculated, only pass preserveViewport when true
114
+ const thunk = preserveViewport
115
+ ? yield call(setCanvas, windowId, startCanvas.id, null, { preserveViewport })
116
+ : yield call(setCanvas, windowId, startCanvas.id);
117
+ yield put(thunk);
118
+ }
119
+ }
120
+ }
121
+ }
122
+
123
+ /** @private */
124
+ export function* setWindowDefaultSearchQuery(action) {
125
+ // only for a brand new window
126
+ if (!action.window || !action.window.defaultSearchQuery) return;
127
+
128
+ const { id: windowId, defaultSearchQuery } = action.window;
129
+ const searchService = yield select(getManifestSearchService, { windowId });
130
+ const companionWindowIds = yield select(getCompanionWindowIdsForPosition, {
131
+ position: 'left',
132
+ windowId,
133
+ });
134
+ const companionWindowId = companionWindowIds[0];
135
+
136
+ if (searchService && companionWindowId) {
137
+ const searchId = searchService && `${searchService.id}?${new URLSearchParams({ q: defaultSearchQuery })}`;
138
+ yield put(fetchSearch(windowId, companionWindowId, searchId, defaultSearchQuery));
139
+ }
140
+ }
141
+
142
+ /** @private */
143
+ export function getAnnotationsBySearch(state, { canvasIds, companionWindowIds, windowId }) {
144
+ const annotationBySearch = companionWindowIds.reduce((accumulator, companionWindowId) => {
145
+ const annotations = getSearchAnnotationsForCompanionWindow(state, {
146
+ companionWindowId,
147
+ windowId,
148
+ });
149
+
150
+ const resourceAnnotations = annotations.resources;
151
+ const hitAnnotation = resourceAnnotations.find((r) => canvasIds.includes(r.targetId));
152
+
153
+ // eslint-disable-next-line no-param-reassign
154
+ if (hitAnnotation) accumulator[companionWindowId] = [hitAnnotation.id];
155
+
156
+ return accumulator;
157
+ }, {});
158
+
159
+ return annotationBySearch;
160
+ }
161
+
162
+ /** @private */
163
+ export function* setCurrentAnnotationsOnCurrentCanvas({ annotationId, windowId, visibleCanvases }) {
164
+ const searches = yield select(getSearchForWindow, { windowId });
165
+ const companionWindowIds = Object.keys(searches || {});
166
+ if (companionWindowIds.length === 0) return;
167
+
168
+ const annotationBySearch = yield select(getAnnotationsBySearch, {
169
+ canvasIds: visibleCanvases,
170
+ companionWindowIds,
171
+ windowId,
172
+ });
173
+
174
+ yield all(
175
+ Object.keys(annotationBySearch).map((companionWindowId) =>
176
+ put(setContentSearchCurrentAnnotation(windowId, companionWindowId, annotationBySearch[companionWindowId])),
177
+ ),
178
+ );
179
+
180
+ if (Object.values(annotationBySearch).length > 0) {
181
+ // if the currently selected annotation isn't on this canvas, do a thing.
182
+ yield put(selectAnnotation(windowId, Object.values(annotationBySearch)[0][0]));
183
+ }
184
+ }
185
+
186
+ /** @private */
187
+ export function* panToFocusedWindow({ pan, windowId }) {
188
+ if (!pan) return;
189
+ const elasticLayout = yield select(getElasticLayout);
190
+ const { x, y, width, height } = elasticLayout[windowId] || {};
191
+
192
+ const {
193
+ viewportPosition: { width: viewWidth, height: viewHeight },
194
+ } = yield select(getWorkspace);
195
+
196
+ yield put(
197
+ setWorkspaceViewportPosition({
198
+ x: x + width / 2 - viewWidth / 2,
199
+ y: y + height / 2 - viewHeight / 2,
200
+ }),
201
+ );
202
+ }
203
+
204
+ /** @private */
205
+ export function* updateVisibleCanvases({ windowId }) {
206
+ const { canvasId } = yield select(getWindow, { windowId });
207
+ const visibleCanvases = yield select(getCanvasGrouping, { canvasId, windowId });
208
+ yield put(updateWindow(windowId, { visibleCanvases: (visibleCanvases || []).map((c) => c.id) }));
209
+ }
210
+
211
+ /** @private */
212
+ export function* setCanvasOfFirstSearchResult({ companionWindowId, windowId }) {
213
+ const { switchCanvasOnSearch } = yield select(getWindowConfig, { windowId });
214
+ if (!switchCanvasOnSearch) {
215
+ return;
216
+ }
217
+ const selectedIds = yield select(getSelectedContentSearchAnnotationIds, {
218
+ companionWindowId,
219
+ windowId,
220
+ });
221
+
222
+ if (selectedIds.length !== 0) return;
223
+
224
+ const annotations = yield select(getSortedSearchAnnotationsForCompanionWindow, {
225
+ companionWindowId,
226
+ windowId,
227
+ });
228
+ if (!annotations || annotations.length === 0) return;
229
+
230
+ yield put(selectAnnotation(windowId, annotations[0].id));
231
+ }
232
+
233
+ /** @private */
234
+ export function* setCanvasforSelectedAnnotation({ annotationId, windowId }) {
235
+ const canvasIds = yield select(getVisibleCanvasIds, { windowId });
236
+ const canvas = yield select(getCanvasForAnnotation, {
237
+ annotationId,
238
+ windowId,
239
+ });
240
+
241
+ if (!canvas || canvasIds.includes(canvas.id)) return;
242
+
243
+ const thunk = yield call(setCanvas, windowId, canvas.id);
244
+ yield put(thunk);
245
+ }
246
+
247
+ /** Fetch info responses for the visible canvases */
248
+ export function* fetchInfoResponses({ visibleCanvases: visibleCanvasIds, windowId }) {
249
+ const canvases = yield select(getCanvases, { windowId });
250
+ const infoResponses = yield select(selectInfoResponses);
251
+ const visibleCanvases = (canvases || []).filter((c) => visibleCanvasIds.includes(c.id));
252
+ const getMiradorCanvas = yield select(getMiradorCanvasWrapper);
253
+
254
+ yield all(
255
+ visibleCanvases.map((canvas) => {
256
+ const miradorCanvas = getMiradorCanvas(canvas);
257
+ return all(
258
+ miradorCanvas.iiifImageResources
259
+ .map(
260
+ (imageResource) =>
261
+ !infoResponses[getIiifResourceImageService(imageResource)?.id] &&
262
+ put(fetchInfoResponse({ imageResource, windowId })),
263
+ )
264
+ .filter(Boolean),
265
+ );
266
+ }),
267
+ );
268
+ }
269
+
270
+ /** */
271
+ export function* determineAndShowCollectionDialog(manifestId, windowId) {
272
+ const manifestoInstance = yield select(getManifestoInstance, { manifestId });
273
+ if (manifestoInstance && manifestoInstance.isCollection()) {
274
+ yield put(showCollectionDialog(manifestId, [], windowId));
275
+ }
276
+ }
277
+
278
+ /** */
279
+ export default function* windowsSaga() {
280
+ yield all([
281
+ takeEvery(ActionTypes.ADD_WINDOW, fetchWindowManifest),
282
+ takeEvery(ActionTypes.UPDATE_WINDOW, fetchWindowManifest),
283
+ takeEvery(ActionTypes.UPDATE_WINDOW, setCanvasOnNewSequence),
284
+ takeEvery(ActionTypes.UPDATE_WINDOW, fetchCollectionManifests),
285
+ takeEvery(ActionTypes.SET_CANVAS, setCurrentAnnotationsOnCurrentCanvas),
286
+ takeEvery(ActionTypes.SET_CANVAS, fetchInfoResponses),
287
+ takeEvery(ActionTypes.UPDATE_COMPANION_WINDOW, fetchCollectionManifests),
288
+ takeEvery(ActionTypes.SET_WINDOW_VIEW_TYPE, updateVisibleCanvases),
289
+ takeEvery(ActionTypes.RECEIVE_SEARCH, setCanvasOfFirstSearchResult),
290
+ takeEvery(ActionTypes.SELECT_ANNOTATION, setCanvasforSelectedAnnotation),
291
+ takeEvery(ActionTypes.FOCUS_WINDOW, panToFocusedWindow),
292
+ ]);
293
+ }
@@ -0,0 +1,138 @@
1
+ import { createSelector } from 'reselect';
2
+ import filter from 'lodash/filter';
3
+ import AnnotationFactory from '../../lib/AnnotationFactory';
4
+ import { miradorSlice } from './utils';
5
+ import { getCanvas, getVisibleCanvasIds } from './canvases';
6
+ import { getConfig } from './config';
7
+ import { getWindow } from './getters';
8
+
9
+ /**
10
+ * Returns the annotation object from the mirador slice.
11
+ * @param {object} state redux state
12
+ * @returns {object} Annotations from the state
13
+ */
14
+ export const getAnnotations = (state) => miradorSlice(state).annotations;
15
+
16
+ const getMotivations = createSelector(
17
+ [getConfig, (state, { motivations }) => motivations],
18
+ (config, motivations) => motivations || config.annotations.filteredMotivations,
19
+ );
20
+
21
+ const getAnnotationsOnCanvas = createSelector([getCanvas, getAnnotations], (canvas, annotations) => {
22
+ if (!annotations || !canvas) return [];
23
+ if (!annotations[canvas.id]) return [];
24
+
25
+ return Object.values(annotations[canvas.id]).flat();
26
+ });
27
+
28
+ const getPresentAnnotationsCanvas = createSelector([getAnnotationsOnCanvas], (annotations) =>
29
+ filter(
30
+ Object.values(annotations).map((annotation) => annotation && AnnotationFactory.determineAnnotation(annotation.json)),
31
+ (annotation) => annotation && annotation.present(),
32
+ ),
33
+ );
34
+
35
+ const getAnnotationsOnSelectedCanvases = createSelector(
36
+ [(state, { canvasId, ...otherProps }) => (canvasId ? [canvasId] : getVisibleCanvasIds(state, otherProps)), getAnnotations],
37
+ (canvasIds, annotations) => {
38
+ if (!annotations || canvasIds.length === 0) return [];
39
+ return canvasIds.flatMap((targetId) => annotations[targetId] && Object.values(annotations[targetId]));
40
+ },
41
+ );
42
+
43
+ /**
44
+ * Returns an array of present annotations given a canvasId.
45
+ * @param {object} state redux state
46
+ * @param {string} canvasId canvasId
47
+ * @returns {Array} An array of present annotations
48
+ */
49
+ export const getPresentAnnotationsOnSelectedCanvases = createSelector([getAnnotationsOnSelectedCanvases], (annotations) =>
50
+ filter(
51
+ Object.values(annotations).map((annotation) => annotation && AnnotationFactory.determineAnnotation(annotation.json)),
52
+ (annotation) => annotation && annotation.present(),
53
+ ),
54
+ );
55
+
56
+ /**
57
+ * Returns an array of annotation resources filtered by the given motivation for a particular canvas.
58
+ * @param {Array} annotations
59
+ * @param {Array} motivations
60
+ * @returns {Array}
61
+ */
62
+ export const getAnnotationResourcesByMotivationForCanvas = createSelector(
63
+ [getPresentAnnotationsCanvas, getMotivations],
64
+ (annotations, motivations) => {
65
+ const resources = annotations.flatMap((annotation) => annotation.resources);
66
+
67
+ // If motivations is empty, null, or undefined, return everything
68
+ if (!motivations || motivations.length === 0) {
69
+ return resources;
70
+ }
71
+
72
+ // Otherwise filter by motivations
73
+ return resources.filter((resource) => resource.motivations.some((motivation) => motivations.includes(motivation)));
74
+ },
75
+ );
76
+
77
+ /**
78
+ * Returns annotation resources for a canvas destructured into their id/content/tags/targetId.
79
+ * @param {object} state
80
+ * @param {object} props
81
+ * @param {string} props.canvasId
82
+ * @param {string} props.windowId
83
+ * @returns {Array} [{ id: 'abc123', content: 'Annotation Content' }, ...]
84
+ */
85
+ export const getAnnotationResourcesDataForCanvas = createSelector([getAnnotationResourcesByMotivationForCanvas], (resources) =>
86
+ resources.map((resource) => ({
87
+ content: resource.chars,
88
+ id: resource.id,
89
+ tags: resource.tags,
90
+ targetId: resource.targetId,
91
+ })),
92
+ );
93
+
94
+ /**
95
+ * Returns an array of annotation resources filtered by the given motivation.
96
+ * @param {Array} annotations
97
+ * @param {Array} motivations
98
+ * @returns {Array}
99
+ */
100
+ export const getAnnotationResourcesByMotivation = createSelector(
101
+ [getPresentAnnotationsOnSelectedCanvases, getMotivations],
102
+ (annotations, motivations) => {
103
+ const resources = annotations.flatMap((annotation) => annotation.resources);
104
+
105
+ // If motivations is empty, null, or undefined, return everything
106
+ if (!motivations || motivations.length === 0) {
107
+ return resources;
108
+ }
109
+
110
+ // Otherwise filter by motivations
111
+ return resources.filter((resource) => resource.motivations.some((motivation) => motivations.includes(motivation)));
112
+ },
113
+ );
114
+
115
+ /**
116
+ * Returns the selected annotations IDs.
117
+ * @param {object} state
118
+ * @param {string} windowId
119
+ * @param {Array} targetIds
120
+ * @returns {Array}
121
+ */
122
+ export const getSelectedAnnotationId = createSelector([getWindow], ({ selectedAnnotationId }) => selectedAnnotationId);
123
+
124
+ /**
125
+ * Returns annotations on selected canvases.
126
+ * @param {object} state
127
+ * @returns {Array}
128
+ */
129
+ export const getSelectedAnnotationsOnCanvases = createSelector(
130
+ [getPresentAnnotationsOnSelectedCanvases, getSelectedAnnotationId],
131
+ (canvasAnnotations, selectedAnnotationId) =>
132
+ canvasAnnotations
133
+ .map((annotation) => ({
134
+ id: annotation['@id'] || annotation.id,
135
+ resources: annotation.resources.filter((r) => selectedAnnotationId === r.id),
136
+ }))
137
+ .filter((val) => val.resources.length > 0),
138
+ );