@internetarchive/bookreader 5.0.0-95 → 5.0.0-96
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.
- package/BookReader/BookReader.css +2251 -0
- package/BookReader/BookReader.js +3 -0
- package/BookReader/BookReader.js.LICENSE.txt +72 -0
- package/BookReader/BookReader.js.map +1 -0
- package/BookReader/hypothesis/LICENSE +50 -0
- package/BookReader/hypothesis/README.md +55 -0
- package/BookReader/hypothesis/build/boot.js +1 -0
- package/BookReader/hypothesis/build/manifest.json +20 -0
- package/BookReader/hypothesis/build/scripts/annotator.bundle.js +184 -0
- package/BookReader/hypothesis/build/scripts/annotator.bundle.js.map +1 -0
- package/BookReader/hypothesis/build/scripts/sidebar.bundle.js +798 -0
- package/BookReader/hypothesis/build/scripts/sidebar.bundle.js.map +1 -0
- package/BookReader/hypothesis/build/scripts/ui-playground.bundle.js +711 -0
- package/BookReader/hypothesis/build/scripts/ui-playground.bundle.js.map +1 -0
- package/BookReader/hypothesis/build/styles/annotator.css +2235 -0
- package/BookReader/hypothesis/build/styles/annotator.css.map +1 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/highlights.css +2 -0
- package/BookReader/hypothesis/build/styles/highlights.css.map +1 -0
- package/BookReader/hypothesis/build/styles/katex.min.css +2 -0
- package/BookReader/hypothesis/build/styles/katex.min.css.map +1 -0
- package/BookReader/hypothesis/build/styles/pdfjs-overrides.css +2 -0
- package/BookReader/hypothesis/build/styles/pdfjs-overrides.css.map +1 -0
- package/BookReader/hypothesis/build/styles/sidebar.css +2731 -0
- package/BookReader/hypothesis/build/styles/sidebar.css.map +1 -0
- package/BookReader/hypothesis/build/styles/ui-playground.css +2659 -0
- package/BookReader/hypothesis/build/styles/ui-playground.css.map +1 -0
- package/BookReader/hypothesis/package.json +126 -0
- package/BookReader/ia-bookreader-bundle.js +1782 -0
- package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +7 -0
- package/BookReader/ia-bookreader-bundle.js.map +1 -0
- package/BookReader/icons/1up.svg +1 -0
- package/BookReader/icons/2up.svg +1 -0
- package/BookReader/icons/advance.svg +3 -0
- package/BookReader/icons/chevron-right.svg +1 -0
- package/BookReader/icons/close-circle-dark.svg +1 -0
- package/BookReader/icons/close-circle.svg +1 -0
- package/BookReader/icons/fullscreen.svg +1 -0
- package/BookReader/icons/fullscreen_exit.svg +1 -0
- package/BookReader/icons/hamburger.svg +1 -0
- package/BookReader/icons/left-arrow.svg +1 -0
- package/BookReader/icons/magnify-minus.svg +1 -0
- package/BookReader/icons/magnify-plus.svg +1 -0
- package/BookReader/icons/magnify.svg +1 -0
- package/BookReader/icons/pause.svg +1 -0
- package/BookReader/icons/play.svg +1 -0
- package/BookReader/icons/playback-speed.svg +1 -0
- package/BookReader/icons/read-aloud.svg +1 -0
- package/BookReader/icons/review.svg +3 -0
- package/BookReader/icons/thumbnails.svg +1 -0
- package/BookReader/icons/voice.svg +1 -0
- package/BookReader/icons/volume-full.svg +1 -0
- package/BookReader/images/BRicons.png +0 -0
- package/BookReader/images/BRicons.svg +5 -0
- package/BookReader/images/BRicons_ia.png +0 -0
- package/BookReader/images/back_pages.png +0 -0
- package/BookReader/images/book_bottom_icon.png +0 -0
- package/BookReader/images/book_down_icon.png +0 -0
- package/BookReader/images/book_left_icon.png +0 -0
- package/BookReader/images/book_leftmost_icon.png +0 -0
- package/BookReader/images/book_right_icon.png +0 -0
- package/BookReader/images/book_rightmost_icon.png +0 -0
- package/BookReader/images/book_top_icon.png +0 -0
- package/BookReader/images/book_up_icon.png +0 -0
- package/BookReader/images/books_graphic.svg +1 -0
- package/BookReader/images/booksplit.png +0 -0
- package/BookReader/images/control_pause_icon.png +0 -0
- package/BookReader/images/control_play_icon.png +0 -0
- package/BookReader/images/embed_icon.png +0 -0
- package/BookReader/images/hypothesis.ico +0 -0
- package/BookReader/images/icon-home-ia.png +0 -0
- package/BookReader/images/icon_OL-logo-xs.png +0 -0
- package/BookReader/images/icon_alert-xs.png +0 -0
- package/BookReader/images/icon_book.svg +1 -0
- package/BookReader/images/icon_bookmark.svg +1 -0
- package/BookReader/images/icon_close-pop.png +0 -0
- package/BookReader/images/icon_download.png +0 -0
- package/BookReader/images/icon_gear.svg +1 -0
- package/BookReader/images/icon_hamburger.svg +1 -0
- package/BookReader/images/icon_home.png +0 -0
- package/BookReader/images/icon_home.svg +1 -0
- package/BookReader/images/icon_home_ia.png +0 -0
- package/BookReader/images/icon_indicator.png +0 -0
- package/BookReader/images/icon_info.svg +1 -0
- package/BookReader/images/icon_one_page.svg +1 -0
- package/BookReader/images/icon_pause.svg +1 -0
- package/BookReader/images/icon_play.svg +1 -0
- package/BookReader/images/icon_playback-rate.svg +1 -0
- package/BookReader/images/icon_return.png +0 -0
- package/BookReader/images/icon_search_button.svg +1 -0
- package/BookReader/images/icon_share.svg +1 -0
- package/BookReader/images/icon_skip-ahead.svg +1 -0
- package/BookReader/images/icon_skip-back.svg +2 -0
- package/BookReader/images/icon_speaker.svg +1 -0
- package/BookReader/images/icon_speaker_open.svg +1 -0
- package/BookReader/images/icon_thumbnails.svg +1 -0
- package/BookReader/images/icon_toc.svg +1 -0
- package/BookReader/images/icon_two_pages.svg +1 -0
- package/BookReader/images/icon_zoomer.png +0 -0
- package/BookReader/images/loading.gif +0 -0
- package/BookReader/images/logo_icon.png +0 -0
- package/BookReader/images/marker_chap-off.png +0 -0
- package/BookReader/images/marker_chap-off.svg +1 -0
- package/BookReader/images/marker_chap-off_ia.png +0 -0
- package/BookReader/images/marker_chap-on.png +0 -0
- package/BookReader/images/marker_chap-on.svg +1 -0
- package/BookReader/images/marker_srch-on.svg +1 -0
- package/BookReader/images/marker_srchchap-off.png +0 -0
- package/BookReader/images/marker_srchchap-on.png +0 -0
- package/BookReader/images/nav_control-dn.png +0 -0
- package/BookReader/images/nav_control-dn_ia.png +0 -0
- package/BookReader/images/nav_control-up.png +0 -0
- package/BookReader/images/nav_control-up_ia.png +0 -0
- package/BookReader/images/nav_control.png +0 -0
- package/BookReader/images/one_page_mode_icon.png +0 -0
- package/BookReader/images/paper-badge.png +0 -0
- package/BookReader/images/print_icon.png +0 -0
- package/BookReader/images/progressbar.gif +0 -0
- package/BookReader/images/right_edges.png +0 -0
- package/BookReader/images/slider.png +0 -0
- package/BookReader/images/slider_ia.png +0 -0
- package/BookReader/images/thumbnail_mode_icon.png +0 -0
- package/BookReader/images/transparent.png +0 -0
- package/BookReader/images/two_page_mode_icon.png +0 -0
- package/BookReader/images/unviewable_page.png +0 -0
- package/BookReader/images/zoom_in_icon.png +0 -0
- package/BookReader/images/zoom_out_icon.png +0 -0
- package/BookReader/jquery-3.js +2 -0
- package/BookReader/jquery-3.js.LICENSE.txt +24 -0
- package/BookReader/plugins/plugin.archive_analytics.js +2 -0
- package/BookReader/plugins/plugin.archive_analytics.js.map +1 -0
- package/BookReader/plugins/plugin.autoplay.js +2 -0
- package/BookReader/plugins/plugin.autoplay.js.map +1 -0
- package/BookReader/plugins/plugin.chapters.js +26 -0
- package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -0
- package/BookReader/plugins/plugin.chapters.js.map +1 -0
- package/BookReader/plugins/plugin.experiments.js +3 -0
- package/BookReader/plugins/plugin.experiments.js.LICENSE.txt +1 -0
- package/BookReader/plugins/plugin.experiments.js.map +1 -0
- package/BookReader/plugins/plugin.iframe.js +2 -0
- package/BookReader/plugins/plugin.iframe.js.map +1 -0
- package/BookReader/plugins/plugin.iiif.js +2 -0
- package/BookReader/plugins/plugin.iiif.js.map +1 -0
- package/BookReader/plugins/plugin.resume.js +2 -0
- package/BookReader/plugins/plugin.resume.js.map +1 -0
- package/BookReader/plugins/plugin.search.js +3 -0
- package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
- package/BookReader/plugins/plugin.search.js.map +1 -0
- package/BookReader/plugins/plugin.text_selection.js +3 -0
- package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
- package/BookReader/plugins/plugin.text_selection.js.map +1 -0
- package/BookReader/plugins/plugin.tts.js +3 -0
- package/BookReader/plugins/plugin.tts.js.LICENSE.txt +29 -0
- package/BookReader/plugins/plugin.tts.js.map +1 -0
- package/BookReader/plugins/plugin.url.js +2 -0
- package/BookReader/plugins/plugin.url.js.map +1 -0
- package/BookReader/plugins/plugin.vendor-fullscreen.js +2 -0
- package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -0
- package/BookReader/webcomponents-bundle.js +3 -0
- package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
- package/BookReader/webcomponents-bundle.js.map +1 -0
- package/package.json +6 -1
- package/src/BookReader/BookModel.js +564 -0
- package/src/BookReader/DragScrollable.js +233 -0
- package/src/BookReader/ImageCache.js +149 -0
- package/src/BookReader/Mode1Up.js +110 -0
- package/src/BookReader/Mode1UpLit.js +388 -0
- package/src/BookReader/Mode2Up.js +107 -0
- package/src/BookReader/Mode2UpLit.js +777 -0
- package/src/BookReader/ModeCoordinateSpace.js +29 -0
- package/src/BookReader/ModeSmoothZoom.js +312 -0
- package/src/BookReader/ModeThumb.js +344 -0
- package/src/BookReader/Navbar/Navbar.js +355 -0
- package/src/BookReader/PageContainer.js +172 -0
- package/src/BookReader/ReduceSet.js +26 -0
- package/src/BookReader/Toolbar/Toolbar.js +362 -0
- package/src/BookReader/events.js +19 -0
- package/src/BookReader/options.js +387 -0
- package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
- package/src/BookReader/utils/ScrollClassAdder.js +31 -0
- package/src/BookReader/utils/SelectionObserver.js +45 -0
- package/src/BookReader/utils/classes.js +36 -0
- package/src/BookReader/utils.js +313 -0
- package/.eslintrc.cjs +0 -51
- package/.gitattributes +0 -2
- package/.github/ISSUE_TEMPLATE/bug.md +0 -32
- package/.github/ISSUE_TEMPLATE/feature-request.md +0 -30
- package/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -15
- package/.github/workflows/node.js.yml +0 -102
- package/.github/workflows/npm-publish.yml +0 -23
- package/.testcaferc.cjs +0 -10
- package/BookReaderDemo/BookReaderDemo.css +0 -40
- package/BookReaderDemo/BookReaderJSAdvanced.js +0 -112
- package/BookReaderDemo/BookReaderJSSimple.js +0 -56
- package/BookReaderDemo/IADemoBr.js +0 -149
- package/BookReaderDemo/assets/v5/Bookreader-logo-cool-grad.svg +0 -1
- package/BookReaderDemo/assets/v5/Bookreader-logo-flat.svg +0 -1
- package/BookReaderDemo/assets/v5/Bookreader-logo-hex-cool-grad.png +0 -0
- package/BookReaderDemo/assets/v5/Bookreader-logo-hex-flat.png +0 -0
- package/BookReaderDemo/assets/v5/Bookreader-logo-lines.png +0 -0
- package/BookReaderDemo/assets/v5/Bookreader-logo-lines.svg +0 -1
- package/BookReaderDemo/assets/v5/Bookreader-logo-warm.svg +0 -1
- package/BookReaderDemo/assets/v5/bookreader-logo-renders@1x.png +0 -0
- package/BookReaderDemo/assets/v5/bookreader-logo-renders@2x.png +0 -0
- package/BookReaderDemo/assets/v5/bookreader-v5-screenshot.png +0 -0
- package/BookReaderDemo/demo-advanced.html +0 -33
- package/BookReaderDemo/demo-embed-iframe-src.html +0 -85
- package/BookReaderDemo/demo-embed.html +0 -26
- package/BookReaderDemo/demo-fullscreen-mobile.html +0 -34
- package/BookReaderDemo/demo-fullscreen.html +0 -31
- package/BookReaderDemo/demo-iiif.html +0 -121
- package/BookReaderDemo/demo-internetarchive.html +0 -271
- package/BookReaderDemo/demo-multiple.html +0 -44
- package/BookReaderDemo/demo-preview-pages.html +0 -1093
- package/BookReaderDemo/demo-simple.html +0 -35
- package/BookReaderDemo/demo-vendor-fullscreen.html +0 -34
- package/BookReaderDemo/ia-multiple-volumes-manifest.js +0 -169
- package/BookReaderDemo/immersion-1up.html +0 -64
- package/BookReaderDemo/immersion-mode.html +0 -33
- package/BookReaderDemo/toggle_controls.html +0 -54
- package/BookReaderDemo/view_mode.html +0 -40
- package/BookReaderDemo/viewmode-cycle.html +0 -40
- package/CHANGELOG.md +0 -1087
- package/CONTRIBUTING.md +0 -7
- package/babel.config.cjs +0 -20
- package/codecov.yml +0 -23
- package/index.html +0 -34
- package/netlify.toml +0 -9
- package/renovate.json +0 -52
- package/screenshot.png +0 -0
- package/scripts/postversion.js +0 -11
- package/scripts/preversion.js +0 -15
- package/scripts/version.js +0 -24
- package/tests/e2e/README.md +0 -112
- package/tests/e2e/autoplay.test.js +0 -16
- package/tests/e2e/base.test.js +0 -27
- package/tests/e2e/helpers/base.js +0 -263
- package/tests/e2e/helpers/debug.js +0 -13
- package/tests/e2e/helpers/mockSearch.js +0 -90
- package/tests/e2e/helpers/params.js +0 -17
- package/tests/e2e/helpers/rightToLeft.js +0 -23
- package/tests/e2e/helpers/search.js +0 -73
- package/tests/e2e/models/BookReader.js +0 -11
- package/tests/e2e/models/Navigation.js +0 -39
- package/tests/e2e/rightToLeft.test.js +0 -19
- package/tests/e2e/viewmode.test.js +0 -44
- package/tests/jest/BookNavigator/book-navigator.test.js +0 -653
- package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +0 -43
- package/tests/jest/BookNavigator/bookmarks/bookmark-edit.test.js +0 -132
- package/tests/jest/BookNavigator/bookmarks/bookmarks-list.test.js +0 -221
- package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +0 -45
- package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +0 -67
- package/tests/jest/BookNavigator/downloads/downloads.test.js +0 -53
- package/tests/jest/BookNavigator/search/search-provider.test.js +0 -179
- package/tests/jest/BookNavigator/search/search-results.test.js +0 -289
- package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +0 -49
- package/tests/jest/BookNavigator/viewable-files/viewable-files-provider.test.js +0 -80
- package/tests/jest/BookNavigator/visual-adjustments.test.js +0 -200
- package/tests/jest/BookReader.keyboard.test.js +0 -190
- package/tests/jest/BookReader.options.test.js +0 -47
- package/tests/jest/BookReader.test.js +0 -316
- package/tests/jest/plugins/plugin.archive_analytics.test.js +0 -20
- package/tests/jest/plugins/plugin.autoplay.test.js +0 -35
- package/tests/jest/plugins/plugin.chapters.test.js +0 -193
- package/tests/jest/plugins/plugin.iframe.test.js +0 -42
- package/tests/jest/plugins/plugin.resume.test.js +0 -85
- package/tests/jest/plugins/plugin.text_selection.test.js +0 -447
- package/tests/jest/plugins/plugin.vendor-fullscreen.test.js +0 -65
- package/tests/jest/plugins/search/plugin.search.test.js +0 -120
- package/tests/jest/plugins/search/plugin.search.view.test.js +0 -131
- package/tests/jest/plugins/search/utils.js +0 -25
- package/tests/jest/plugins/search/utils.test.js +0 -29
- package/tests/jest/plugins/tts/AbstractTTSEngine.test.js +0 -173
- package/tests/jest/plugins/tts/FestivalTTSEngine.test.js +0 -59
- package/tests/jest/plugins/tts/PageChunk.test.js +0 -57
- package/tests/jest/plugins/tts/PageChunkIterator.test.js +0 -179
- package/tests/jest/plugins/tts/WebTTSEngine.test.js +0 -178
- package/tests/jest/plugins/tts/utils.test.js +0 -74
- package/tests/jest/plugins/url/UrlPlugin.test.js +0 -198
- package/tests/jest/plugins/url/plugin.url.test.js +0 -168
- package/tests/jest/setup.js +0 -3
- package/tests/jest/util/browserSniffing.test.js +0 -62
- package/tests/jest/util/docCookies.test.js +0 -24
- package/tests/jest/util/strings.test.js +0 -63
- package/tests/jest/utils.js +0 -83
- package/webpack.config.js +0 -97
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"annotator.bundle.js","sources":["app:///node_modules/focus-visible/dist/focus-visible.js","app:///node_modules/preact/dist/preact.mjs","app:///node_modules/preact/hooks/dist/hooks.mjs","app:///node_modules/@hypothesis/frontend-shared/lib/util/listener-collection.js","app:///node_modules/@hypothesis/frontend-shared/lib/hooks/use-arrow-key-navigation.js","app:///node_modules/@hypothesis/frontend-shared/lib/hooks/use-stable-callback.js","app:///node_modules/@hypothesis/frontend-shared/lib/hooks/use-synced-ref.js","app:///node_modules/classnames/index.js","app:///node_modules/preact/jsx-runtime/dist/jsxRuntime.mjs","app:///node_modules/@hypothesis/frontend-shared/lib/components/CloseableContext.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/data/ScrollContext.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/layout/Card.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/layout/CardContent.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/Annotate.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/Cancel.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/CaretDown.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/CaretLeft.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/CaretRight.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/Caution.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/Check.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/Checkbox.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/CheckboxCheckedFilled.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/Hide.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/Highlight.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/MenuCollapse.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/MenuExpand.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/Note.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/Pin.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/PointerDown.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/PointerUp.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/Radio.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/RadioChecked.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/Selection.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/icons/Show.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/input/Button.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/input/InputGroup.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/input/IconButton.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/input/ToggleInput.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/input/Checkbox.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/input/RadioGroupContext.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/input/RadioGroup.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/feedback/Popover.js","app:///node_modules/@hypothesis/frontend-shared/lib/hooks/use-click-away.js","app:///node_modules/@hypothesis/frontend-shared/lib/hooks/use-key-press.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/input/SelectContext.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/input/Select.js","app:///node_modules/@hypothesis/frontend-shared/lib/hooks/use-focus-away.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/data/TableContext.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/data/TableSectionContext.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/feedback/Callout.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/feedback/ToastMessages.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/navigation/Link.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/navigation/PointerButton.js","app:///src/shared/random.ts","app:///src/shared/messaging/port-util.ts","app:///src/shared/messaging/port-finder.ts","app:///src/shared/event-emitter.ts","app:///src/shared/frame-error-capture.ts","app:///src/shared/messaging/port-provider.ts","app:///src/shared/messaging/port-rpc.ts","app:///src/shared/type-coercions.ts","app:///src/boot/parse-json-config.ts","app:///src/shared/has-own.ts","app:///src/annotator/util/geometry.ts","app:///src/annotator/util/node.ts","app:///src/annotator/integrations/html-side-by-side.ts","app:///src/annotator/config/url-from-link-tag.ts","app:///src/annotator/config/settings.ts","app:///src/annotator/config/config-func-settings-from.ts","app:///src/shared/is-object.ts","app:///src/annotator/config/index.ts","app:///src/annotator/config/is-browser-extension.ts","app:///src/shared/shortcut.ts","app:///src/shared/user-agent.ts","app:///src/annotator/components/AdderToolbar.tsx","app:///src/annotator/util/shadow-root.ts","app:///src/annotator/util/preact-container.ts","app:///src/annotator/adder.tsx","app:///src/annotator/anchoring/trim-range.ts","app:///src/annotator/anchoring/text-range.ts","app:///src/annotator/anchoring/placeholder.ts","app:///src/annotator/range-util.ts","app:///src/annotator/highlighter.ts","app:///src/annotator/util/buckets.ts","app:///src/annotator/bucket-bar-client.ts","app:///src/annotator/draw-tool.tsx","app:///src/shared/promise-with-resolvers.ts","app:///src/annotator/events.ts","app:///src/shared/warn-once.ts","app:///src/annotator/features.ts","app:///src/annotator/components/ClusterToolbar.tsx","app:///src/annotator/highlight-clusters.tsx","app:///node_modules/approx-string-match/build/src/index.js","app:///src/annotator/anchoring/match-quote.ts","app:///src/annotator/anchoring/xpath.ts","app:///src/annotator/anchoring/types.ts","app:///src/annotator/anchoring/html.ts","app:///src/annotator/util/navigation-observer.ts","app:///node_modules/scroll-into-view/scrollIntoView.js","app:///src/annotator/util/scroll.ts","app:///src/annotator/util/url.ts","app:///src/annotator/integrations/html-metadata.ts","app:///src/annotator/integrations/html.ts","app:///node_modules/lodash.debounce/index.js","app:///src/annotator/util/normalize.ts","app:///src/annotator/anchoring/pdf.ts","app:///src/annotator/components/Banners.tsx","app:///src/annotator/components/ContentInfoBanner.tsx","app:///src/annotator/components/WarningBanner.tsx","app:///src/annotator/integrations/pdf-metadata.ts","app:///src/annotator/integrations/pdf.tsx","app:///src/shared/cfi.ts","app:///src/annotator/frame-observer.ts","app:///src/annotator/hypothesis-injector.ts","app:///src/annotator/integrations/image-text-layer.ts","app:///src/annotator/integrations/vitalsource.ts","app:///src/annotator/integrations/index.ts","app:///src/annotator/components/OutsideAssignmentNotice.tsx","app:///src/annotator/outside-assignment-notice.tsx","app:///src/annotator/selection-observer.ts","app:///src/annotator/util/frame.ts","app:///src/annotator/guest.ts","app:///src/shared/config-fragment.ts","app:///src/annotator/config/app.ts","app:///src/annotator/components/ModalDialog.tsx","app:///src/annotator/components/NotebookModal.tsx","app:///src/annotator/notebook.tsx","app:///src/annotator/components/ProfileModal.tsx","app:///src/annotator/profile.tsx","app:///src/annotator/components/Buckets.tsx","app:///src/annotator/bucket-bar.tsx","app:///src/annotator/components/ToastMessages.tsx","app:///src/annotator/components/Toolbar.tsx","app:///src/annotator/toolbar.tsx","app:///src/annotator/util/drag-handler.tsx","app:///src/annotator/sidebar.tsx","app:///src/annotator/annotation-counts.ts","app:///src/annotator/sidebar-trigger.ts","app:///src/annotator/util/emitter.ts","app:///src/annotator/index.ts"],"sourcesContent":["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on <html> whenever the\n // window blurs, even if you're tabbing out of the page. ¯\\_(ツ)_/¯\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n","var n,l,u,t,i,r,o,e,f,c,s,a,h,p={},y=[],v=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,w=Array.isArray;function d(n,l){for(var u in l)n[u]=l[u];return n}function g(n){n&&n.parentNode&&n.parentNode.removeChild(n)}function _(l,u,t){var i,r,o,e={};for(o in u)\"key\"==o?i=u[o]:\"ref\"==o?r=u[o]:e[o]=u[o];if(arguments.length>2&&(e.children=arguments.length>3?n.call(arguments,2):t),\"function\"==typeof l&&null!=l.defaultProps)for(o in l.defaultProps)null==e[o]&&(e[o]=l.defaultProps[o]);return m(l,e,i,r,null)}function m(n,t,i,r,o){var e={type:n,props:t,key:i,ref:r,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:null==o?++u:o,__i:-1,__u:0};return null==o&&null!=l.vnode&&l.vnode(e),e}function b(){return{current:null}}function k(n){return n.children}function x(n,l){this.props=n,this.context=l}function S(n,l){if(null==l)return n.__?S(n.__,n.__i+1):null;for(var u;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e)return u.__e;return\"function\"==typeof n.type?S(n):null}function C(n){var l,u;if(null!=(n=n.__)&&null!=n.__c){for(n.__e=n.__c.base=null,l=0;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e){n.__e=n.__c.base=u.__e;break}return C(n)}}function M(n){(!n.__d&&(n.__d=!0)&&i.push(n)&&!$.__r++||r!=l.debounceRendering)&&((r=l.debounceRendering)||o)($)}function $(){for(var n,u,t,r,o,f,c,s=1;i.length;)i.length>s&&i.sort(e),n=i.shift(),s=i.length,n.__d&&(t=void 0,o=(r=(u=n).__v).__e,f=[],c=[],u.__P&&((t=d({},r)).__v=r.__v+1,l.vnode&&l.vnode(t),O(u.__P,t,r,u.__n,u.__P.namespaceURI,32&r.__u?[o]:null,f,null==o?S(r):o,!!(32&r.__u),c),t.__v=r.__v,t.__.__k[t.__i]=t,z(f,t,c),t.__e!=o&&C(t)));$.__r=0}function I(n,l,u,t,i,r,o,e,f,c,s){var a,h,v,w,d,g,_=t&&t.__k||y,m=l.length;for(f=P(u,l,_,f,m),a=0;a<m;a++)null!=(v=u.__k[a])&&(h=-1==v.__i?p:_[v.__i]||p,v.__i=a,g=O(n,v,h,i,r,o,e,f,c,s),w=v.__e,v.ref&&h.ref!=v.ref&&(h.ref&&q(h.ref,null,v),s.push(v.ref,v.__c||w,v)),null==d&&null!=w&&(d=w),4&v.__u||h.__k===v.__k?f=A(v,f,n):\"function\"==typeof v.type&&void 0!==g?f=g:w&&(f=w.nextSibling),v.__u&=-7);return u.__e=d,f}function P(n,l,u,t,i){var r,o,e,f,c,s=u.length,a=s,h=0;for(n.__k=new Array(i),r=0;r<i;r++)null!=(o=l[r])&&\"boolean\"!=typeof o&&\"function\"!=typeof o?(f=r+h,(o=n.__k[r]=\"string\"==typeof o||\"number\"==typeof o||\"bigint\"==typeof o||o.constructor==String?m(null,o,null,null,null):w(o)?m(k,{children:o},null,null,null):null==o.constructor&&o.__b>0?m(o.type,o.props,o.key,o.ref?o.ref:null,o.__v):o).__=n,o.__b=n.__b+1,e=null,-1!=(c=o.__i=L(o,u,f,a))&&(a--,(e=u[c])&&(e.__u|=2)),null==e||null==e.__v?(-1==c&&(i>s?h--:i<s&&h++),\"function\"!=typeof o.type&&(o.__u|=4)):c!=f&&(c==f-1?h--:c==f+1?h++:(c>f?h--:h++,o.__u|=4))):n.__k[r]=null;if(a)for(r=0;r<s;r++)null!=(e=u[r])&&0==(2&e.__u)&&(e.__e==t&&(t=S(e)),B(e,e));return t}function A(n,l,u){var t,i;if(\"function\"==typeof n.type){for(t=n.__k,i=0;t&&i<t.length;i++)t[i]&&(t[i].__=n,l=A(t[i],l,u));return l}n.__e!=l&&(l&&n.type&&!u.contains(l)&&(l=S(n)),u.insertBefore(n.__e,l||null),l=n.__e);do{l=l&&l.nextSibling}while(null!=l&&8==l.nodeType);return l}function H(n,l){return l=l||[],null==n||\"boolean\"==typeof n||(w(n)?n.some(function(n){H(n,l)}):l.push(n)),l}function L(n,l,u,t){var i,r,o=n.key,e=n.type,f=l[u];if(null===f&&null==n.key||f&&o==f.key&&e==f.type&&0==(2&f.__u))return u;if(t>(null!=f&&0==(2&f.__u)?1:0))for(i=u-1,r=u+1;i>=0||r<l.length;){if(i>=0){if((f=l[i])&&0==(2&f.__u)&&o==f.key&&e==f.type)return i;i--}if(r<l.length){if((f=l[r])&&0==(2&f.__u)&&o==f.key&&e==f.type)return r;r++}}return-1}function T(n,l,u){\"-\"==l[0]?n.setProperty(l,null==u?\"\":u):n[l]=null==u?\"\":\"number\"!=typeof u||v.test(l)?u:u+\"px\"}function j(n,l,u,t,i){var r;n:if(\"style\"==l)if(\"string\"==typeof u)n.style.cssText=u;else{if(\"string\"==typeof t&&(n.style.cssText=t=\"\"),t)for(l in t)u&&l in u||T(n.style,l,\"\");if(u)for(l in u)t&&u[l]==t[l]||T(n.style,l,u[l])}else if(\"o\"==l[0]&&\"n\"==l[1])r=l!=(l=l.replace(f,\"$1\")),l=l.toLowerCase()in n||\"onFocusOut\"==l||\"onFocusIn\"==l?l.toLowerCase().slice(2):l.slice(2),n.l||(n.l={}),n.l[l+r]=u,u?t?u.u=t.u:(u.u=c,n.addEventListener(l,r?a:s,r)):n.removeEventListener(l,r?a:s,r);else{if(\"http://www.w3.org/2000/svg\"==i)l=l.replace(/xlink(H|:h)/,\"h\").replace(/sName$/,\"s\");else if(\"width\"!=l&&\"height\"!=l&&\"href\"!=l&&\"list\"!=l&&\"form\"!=l&&\"tabIndex\"!=l&&\"download\"!=l&&\"rowSpan\"!=l&&\"colSpan\"!=l&&\"role\"!=l&&\"popover\"!=l&&l in n)try{n[l]=null==u?\"\":u;break n}catch(n){}\"function\"==typeof u||(null==u||!1===u&&\"-\"!=l[4]?n.removeAttribute(l):n.setAttribute(l,\"popover\"==l&&1==u?\"\":u))}}function F(n){return function(u){if(this.l){var t=this.l[u.type+n];if(null==u.t)u.t=c++;else if(u.t<t.u)return;return t(l.event?l.event(u):u)}}}function O(n,u,t,i,r,o,e,f,c,s){var a,h,p,y,v,_,m,b,S,C,M,$,P,A,H,L,T,j=u.type;if(null!=u.constructor)return null;128&t.__u&&(c=!!(32&t.__u),o=[f=u.__e=t.__e]),(a=l.__b)&&a(u);n:if(\"function\"==typeof j)try{if(b=u.props,S=\"prototype\"in j&&j.prototype.render,C=(a=j.contextType)&&i[a.__c],M=a?C?C.props.value:a.__:i,t.__c?m=(h=u.__c=t.__c).__=h.__E:(S?u.__c=h=new j(b,M):(u.__c=h=new x(b,M),h.constructor=j,h.render=D),C&&C.sub(h),h.props=b,h.state||(h.state={}),h.context=M,h.__n=i,p=h.__d=!0,h.__h=[],h._sb=[]),S&&null==h.__s&&(h.__s=h.state),S&&null!=j.getDerivedStateFromProps&&(h.__s==h.state&&(h.__s=d({},h.__s)),d(h.__s,j.getDerivedStateFromProps(b,h.__s))),y=h.props,v=h.state,h.__v=u,p)S&&null==j.getDerivedStateFromProps&&null!=h.componentWillMount&&h.componentWillMount(),S&&null!=h.componentDidMount&&h.__h.push(h.componentDidMount);else{if(S&&null==j.getDerivedStateFromProps&&b!==y&&null!=h.componentWillReceiveProps&&h.componentWillReceiveProps(b,M),!h.__e&&null!=h.shouldComponentUpdate&&!1===h.shouldComponentUpdate(b,h.__s,M)||u.__v==t.__v){for(u.__v!=t.__v&&(h.props=b,h.state=h.__s,h.__d=!1),u.__e=t.__e,u.__k=t.__k,u.__k.some(function(n){n&&(n.__=u)}),$=0;$<h._sb.length;$++)h.__h.push(h._sb[$]);h._sb=[],h.__h.length&&e.push(h);break n}null!=h.componentWillUpdate&&h.componentWillUpdate(b,h.__s,M),S&&null!=h.componentDidUpdate&&h.__h.push(function(){h.componentDidUpdate(y,v,_)})}if(h.context=M,h.props=b,h.__P=n,h.__e=!1,P=l.__r,A=0,S){for(h.state=h.__s,h.__d=!1,P&&P(u),a=h.render(h.props,h.state,h.context),H=0;H<h._sb.length;H++)h.__h.push(h._sb[H]);h._sb=[]}else do{h.__d=!1,P&&P(u),a=h.render(h.props,h.state,h.context),h.state=h.__s}while(h.__d&&++A<25);h.state=h.__s,null!=h.getChildContext&&(i=d(d({},i),h.getChildContext())),S&&!p&&null!=h.getSnapshotBeforeUpdate&&(_=h.getSnapshotBeforeUpdate(y,v)),L=a,null!=a&&a.type===k&&null==a.key&&(L=N(a.props.children)),f=I(n,w(L)?L:[L],u,t,i,r,o,e,f,c,s),h.base=u.__e,u.__u&=-161,h.__h.length&&e.push(h),m&&(h.__E=h.__=null)}catch(n){if(u.__v=null,c||null!=o)if(n.then){for(u.__u|=c?160:128;f&&8==f.nodeType&&f.nextSibling;)f=f.nextSibling;o[o.indexOf(f)]=null,u.__e=f}else for(T=o.length;T--;)g(o[T]);else u.__e=t.__e,u.__k=t.__k;l.__e(n,u,t)}else null==o&&u.__v==t.__v?(u.__k=t.__k,u.__e=t.__e):f=u.__e=V(t.__e,u,t,i,r,o,e,c,s);return(a=l.diffed)&&a(u),128&u.__u?void 0:f}function z(n,u,t){for(var i=0;i<t.length;i++)q(t[i],t[++i],t[++i]);l.__c&&l.__c(u,n),n.some(function(u){try{n=u.__h,u.__h=[],n.some(function(n){n.call(u)})}catch(n){l.__e(n,u.__v)}})}function N(n){return\"object\"!=typeof n||null==n||n.__b&&n.__b>0?n:w(n)?n.map(N):d({},n)}function V(u,t,i,r,o,e,f,c,s){var a,h,y,v,d,_,m,b=i.props,k=t.props,x=t.type;if(\"svg\"==x?o=\"http://www.w3.org/2000/svg\":\"math\"==x?o=\"http://www.w3.org/1998/Math/MathML\":o||(o=\"http://www.w3.org/1999/xhtml\"),null!=e)for(a=0;a<e.length;a++)if((d=e[a])&&\"setAttribute\"in d==!!x&&(x?d.localName==x:3==d.nodeType)){u=d,e[a]=null;break}if(null==u){if(null==x)return document.createTextNode(k);u=document.createElementNS(o,x,k.is&&k),c&&(l.__m&&l.__m(t,e),c=!1),e=null}if(null==x)b===k||c&&u.data==k||(u.data=k);else{if(e=e&&n.call(u.childNodes),b=i.props||p,!c&&null!=e)for(b={},a=0;a<u.attributes.length;a++)b[(d=u.attributes[a]).name]=d.value;for(a in b)if(d=b[a],\"children\"==a);else if(\"dangerouslySetInnerHTML\"==a)y=d;else if(!(a in k)){if(\"value\"==a&&\"defaultValue\"in k||\"checked\"==a&&\"defaultChecked\"in k)continue;j(u,a,null,d,o)}for(a in k)d=k[a],\"children\"==a?v=d:\"dangerouslySetInnerHTML\"==a?h=d:\"value\"==a?_=d:\"checked\"==a?m=d:c&&\"function\"!=typeof d||b[a]===d||j(u,a,d,b[a],o);if(h)c||y&&(h.__html==y.__html||h.__html==u.innerHTML)||(u.innerHTML=h.__html),t.__k=[];else if(y&&(u.innerHTML=\"\"),I(\"template\"==t.type?u.content:u,w(v)?v:[v],t,i,r,\"foreignObject\"==x?\"http://www.w3.org/1999/xhtml\":o,e,f,e?e[0]:i.__k&&S(i,0),c,s),null!=e)for(a=e.length;a--;)g(e[a]);c||(a=\"value\",\"progress\"==x&&null==_?u.removeAttribute(\"value\"):null!=_&&(_!==u[a]||\"progress\"==x&&!_||\"option\"==x&&_!=b[a])&&j(u,a,_,b[a],o),a=\"checked\",null!=m&&m!=u[a]&&j(u,a,m,b[a],o))}return u}function q(n,u,t){try{if(\"function\"==typeof n){var i=\"function\"==typeof n.__u;i&&n.__u(),i&&null==u||(n.__u=n(u))}else n.current=u}catch(n){l.__e(n,t)}}function B(n,u,t){var i,r;if(l.unmount&&l.unmount(n),(i=n.ref)&&(i.current&&i.current!=n.__e||q(i,null,u)),null!=(i=n.__c)){if(i.componentWillUnmount)try{i.componentWillUnmount()}catch(n){l.__e(n,u)}i.base=i.__P=null}if(i=n.__k)for(r=0;r<i.length;r++)i[r]&&B(i[r],u,t||\"function\"!=typeof n.type);t||g(n.__e),n.__c=n.__=n.__e=void 0}function D(n,l,u){return this.constructor(n,u)}function E(u,t,i){var r,o,e,f;t==document&&(t=document.documentElement),l.__&&l.__(u,t),o=(r=\"function\"==typeof i)?null:i&&i.__k||t.__k,e=[],f=[],O(t,u=(!r&&i||t).__k=_(k,null,[u]),o||p,p,t.namespaceURI,!r&&i?[i]:o?null:t.firstChild?n.call(t.childNodes):null,e,!r&&i?i:o?o.__e:t.firstChild,r,f),z(e,u,f)}function G(n,l){E(n,l,G)}function J(l,u,t){var i,r,o,e,f=d({},l.props);for(o in l.type&&l.type.defaultProps&&(e=l.type.defaultProps),u)\"key\"==o?i=u[o]:\"ref\"==o?r=u[o]:f[o]=null==u[o]&&null!=e?e[o]:u[o];return arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):t),m(l.type,f,i||l.key,r||l.ref,null)}function K(n){function l(n){var u,t;return this.getChildContext||(u=new Set,(t={})[l.__c]=this,this.getChildContext=function(){return t},this.componentWillUnmount=function(){u=null},this.shouldComponentUpdate=function(n){this.props.value!=n.value&&u.forEach(function(n){n.__e=!0,M(n)})},this.sub=function(n){u.add(n);var l=n.componentWillUnmount;n.componentWillUnmount=function(){u&&u.delete(n),l&&l.call(n)}}),n.children}return l.__c=\"__cC\"+h++,l.__=n,l.Provider=l.__l=(l.Consumer=function(n,l){return n.children(l)}).contextType=l,l}n=y.slice,l={__e:function(n,l,u,t){for(var i,r,o;l=l.__;)if((i=l.__c)&&!i.__)try{if((r=i.constructor)&&null!=r.getDerivedStateFromError&&(i.setState(r.getDerivedStateFromError(n)),o=i.__d),null!=i.componentDidCatch&&(i.componentDidCatch(n,t||{}),o=i.__d),o)return i.__E=i}catch(l){n=l}throw n}},u=0,t=function(n){return null!=n&&null==n.constructor},x.prototype.setState=function(n,l){var u;u=null!=this.__s&&this.__s!=this.state?this.__s:this.__s=d({},this.state),\"function\"==typeof n&&(n=n(d({},u),this.props)),n&&d(u,n),null!=n&&this.__v&&(l&&this._sb.push(l),M(this))},x.prototype.forceUpdate=function(n){this.__v&&(this.__e=!0,n&&this.__h.push(n),M(this))},x.prototype.render=k,i=[],o=\"function\"==typeof Promise?Promise.prototype.then.bind(Promise.resolve()):setTimeout,e=function(n,l){return n.__v.__b-l.__v.__b},$.__r=0,f=/(PointerCapture)$|Capture$/i,c=0,s=F(!1),a=F(!0),h=0;export{x as Component,k as Fragment,J as cloneElement,K as createContext,_ as createElement,b as createRef,_ as h,G as hydrate,t as isValidElement,l as options,E as render,H as toChildArray};\n//# sourceMappingURL=preact.module.js.map\n","import{options as n}from\"preact\";var t,r,u,i,o=0,f=[],c=n,e=c.__b,a=c.__r,v=c.diffed,l=c.__c,m=c.unmount,s=c.__;function p(n,t){c.__h&&c.__h(r,n,o||t),o=0;var u=r.__H||(r.__H={__:[],__h:[]});return n>=u.__.length&&u.__.push({}),u.__[n]}function d(n){return o=1,h(D,n)}function h(n,u,i){var o=p(t++,2);if(o.t=n,!o.__c&&(o.__=[i?i(u):D(void 0,u),function(n){var t=o.__N?o.__N[0]:o.__[0],r=o.t(t,n);t!==r&&(o.__N=[r,o.__[1]],o.__c.setState({}))}],o.__c=r,!r.__f)){var f=function(n,t,r){if(!o.__c.__H)return!0;var u=o.__c.__H.__.filter(function(n){return!!n.__c});if(u.every(function(n){return!n.__N}))return!c||c.call(this,n,t,r);var i=o.__c.props!==n;return u.forEach(function(n){if(n.__N){var t=n.__[0];n.__=n.__N,n.__N=void 0,t!==n.__[0]&&(i=!0)}}),c&&c.call(this,n,t,r)||i};r.__f=!0;var c=r.shouldComponentUpdate,e=r.componentWillUpdate;r.componentWillUpdate=function(n,t,r){if(this.__e){var u=c;c=void 0,f(n,t,r),c=u}e&&e.call(this,n,t,r)},r.shouldComponentUpdate=f}return o.__N||o.__}function y(n,u){var i=p(t++,3);!c.__s&&C(i.__H,u)&&(i.__=n,i.u=u,r.__H.__h.push(i))}function _(n,u){var i=p(t++,4);!c.__s&&C(i.__H,u)&&(i.__=n,i.u=u,r.__h.push(i))}function A(n){return o=5,T(function(){return{current:n}},[])}function F(n,t,r){o=6,_(function(){if(\"function\"==typeof n){var r=n(t());return function(){n(null),r&&\"function\"==typeof r&&r()}}if(n)return n.current=t(),function(){return n.current=null}},null==r?r:r.concat(n))}function T(n,r){var u=p(t++,7);return C(u.__H,r)&&(u.__=n(),u.__H=r,u.__h=n),u.__}function q(n,t){return o=8,T(function(){return n},t)}function x(n){var u=r.context[n.__c],i=p(t++,9);return i.c=n,u?(null==i.__&&(i.__=!0,u.sub(r)),u.props.value):n.__}function P(n,t){c.useDebugValue&&c.useDebugValue(t?t(n):n)}function b(n){var u=p(t++,10),i=d();return u.__=n,r.componentDidCatch||(r.componentDidCatch=function(n,t){u.__&&u.__(n,t),i[1](n)}),[i[0],function(){i[1](void 0)}]}function g(){var n=p(t++,11);if(!n.__){for(var u=r.__v;null!==u&&!u.__m&&null!==u.__;)u=u.__;var i=u.__m||(u.__m=[0,0]);n.__=\"P\"+i[0]+\"-\"+i[1]++}return n.__}function j(){for(var n;n=f.shift();)if(n.__P&&n.__H)try{n.__H.__h.forEach(z),n.__H.__h.forEach(B),n.__H.__h=[]}catch(t){n.__H.__h=[],c.__e(t,n.__v)}}c.__b=function(n){r=null,e&&e(n)},c.__=function(n,t){n&&t.__k&&t.__k.__m&&(n.__m=t.__k.__m),s&&s(n,t)},c.__r=function(n){a&&a(n),t=0;var i=(r=n.__c).__H;i&&(u===r?(i.__h=[],r.__h=[],i.__.forEach(function(n){n.__N&&(n.__=n.__N),n.u=n.__N=void 0})):(i.__h.forEach(z),i.__h.forEach(B),i.__h=[],t=0)),u=r},c.diffed=function(n){v&&v(n);var t=n.__c;t&&t.__H&&(t.__H.__h.length&&(1!==f.push(t)&&i===c.requestAnimationFrame||((i=c.requestAnimationFrame)||w)(j)),t.__H.__.forEach(function(n){n.u&&(n.__H=n.u),n.u=void 0})),u=r=null},c.__c=function(n,t){t.some(function(n){try{n.__h.forEach(z),n.__h=n.__h.filter(function(n){return!n.__||B(n)})}catch(r){t.some(function(n){n.__h&&(n.__h=[])}),t=[],c.__e(r,n.__v)}}),l&&l(n,t)},c.unmount=function(n){m&&m(n);var t,r=n.__c;r&&r.__H&&(r.__H.__.forEach(function(n){try{z(n)}catch(n){t=n}}),r.__H=void 0,t&&c.__e(t,r.__v))};var k=\"function\"==typeof requestAnimationFrame;function w(n){var t,r=function(){clearTimeout(u),k&&cancelAnimationFrame(t),setTimeout(n)},u=setTimeout(r,100);k&&(t=requestAnimationFrame(r))}function z(n){var t=r,u=n.__c;\"function\"==typeof u&&(n.__c=void 0,u()),r=t}function B(n){var t=r;n.__c=n.__(),r=t}function C(n,t){return!n||n.length!==t.length||t.some(function(t,r){return t!==n[r]})}function D(n,t){return\"function\"==typeof t?t(n):t}export{q as useCallback,x as useContext,P as useDebugValue,y as useEffect,b as useErrorBoundary,g as useId,F as useImperativeHandle,_ as useLayoutEffect,T as useMemo,h as useReducer,A as useRef,d as useState};\n//# sourceMappingURL=hooks.module.js.map\n","function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }\nfunction _toPropertyKey(t) { var i = _toPrimitive(t, \"string\"); return \"symbol\" == typeof i ? i : i + \"\"; }\nfunction _toPrimitive(t, r) { if (\"object\" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || \"default\"); if (\"object\" != typeof i) return i; throw new TypeError(\"@@toPrimitive must return a primitive value.\"); } return (\"string\" === r ? String : Number)(t); }\n/**\n * Return the event type that a listener will receive.\n *\n * For example `EventType<HTMLElement, 'keydown'>` evaluates to `KeyboardEvent`.\n *\n * The event type is extracted from the target's `on${Type}` property (eg.\n * `HTMLElement.onkeydown` here) If there is no such property, the type defaults\n * to `Event`.\n */\n\n/**\n * Utility that provides a way to conveniently remove a set of DOM event\n * listeners when they are no longer needed.\n */\nexport class ListenerCollection {\n constructor() {\n _defineProperty(this, \"_listeners\", void 0);\n this._listeners = new Map();\n }\n\n /**\n * Add a listener and return an ID that can be used to remove it later\n */\n add(eventTarget, eventType, listener, options) {\n eventTarget.addEventListener(eventType, listener, options);\n const symbol = Symbol();\n this._listeners.set(symbol, {\n eventTarget,\n eventType,\n listener,\n options\n });\n return symbol;\n }\n\n /**\n * Remove a specific listener.\n */\n remove(listenerId) {\n const event = this._listeners.get(listenerId);\n if (event) {\n const {\n eventTarget,\n eventType,\n listener,\n options\n } = event;\n eventTarget.removeEventListener(eventType, listener, options);\n this._listeners.delete(listenerId);\n }\n }\n removeAll() {\n this._listeners.forEach(({\n eventTarget,\n eventType,\n listener,\n options\n }) => {\n eventTarget.removeEventListener(eventType, listener, options);\n });\n this._listeners.clear();\n }\n}\n//# sourceMappingURL=listener-collection.js.map","import { useEffect, useRef } from 'preact/hooks';\nimport { ListenerCollection } from '../util/listener-collection';\nimport { useStableCallback } from './use-stable-callback';\nfunction isElementDisabled(element) {\n return typeof element.disabled === 'boolean' && element.disabled;\n}\nfunction isElementVisible(element) {\n return element.offsetParent !== null;\n}\nfunction defaultSetFocus(element) {\n element.focus();\n}\n/**\n * Enable arrow key navigation between interactive descendants of a\n * container element.\n *\n * In addition to moving focus between elements when arrow keys are pressed,\n * this also implements the \"roving tabindex\" pattern [1] which sets the\n * `tabindex` attribute of elements to control which element gets focus when the\n * user tabs into the container.\n *\n * See [2] for a reference of how keyboard navigation should work in web\n * applications and how it applies to various common widgets.\n *\n * @example\n * function MyToolbar() {\n * const container = useRef();\n *\n * // Enable arrow key navigation between interactive elements in the\n * // toolbar container.\n * useArrowKeyNavigation(container);\n *\n * return (\n * <div ref={container} role=\"toolbar\">\n * <button>Bold</bold>\n * <button>Italic</bold>\n * <a href=\"https://example.com/help\">Help</a>\n * </div>\n * )\n * }\n *\n * [1] https://www.w3.org/TR/wai-aria-practices/#kbd_roving_tabindex\n * [2] https://www.w3.org/TR/wai-aria-practices/#keyboard\n *\n */\nexport function useArrowKeyNavigation(containerRef, {\n autofocus = false,\n loop = true,\n horizontal = true,\n vertical = true,\n selector = 'a,button',\n containerVisible = true,\n focusElement: focusElement_ = defaultSetFocus\n} = {}) {\n // Keep track of the element that was last focused by this hook such that\n // navigation can be restored if focus moves outside the container and then\n // back to/into it.\n const lastFocusedItem = useRef(null);\n const focusElement = useStableCallback(focusElement_);\n useEffect(() => {\n if (!containerVisible) {\n return () => {};\n }\n if (!containerRef.current) {\n throw new Error('Container ref not set');\n }\n const container = containerRef.current;\n const getNavigableElements = () => {\n const elements = Array.from(container.querySelectorAll(selector));\n const filtered = elements.filter(el => isElementVisible(el) && !isElementDisabled(el));\n // Include the container itself in the set of navigable elements if it\n // is currently focused. It will not be part of the tab sequence once it\n // loses focus. This allows, e.g., a widget container to be focused when\n // opened but not be part of the subsequent keyboard-navigation sequence.\n if (document.activeElement === container) {\n filtered.unshift(container);\n }\n return filtered;\n };\n\n /**\n * Update the `tabindex` attribute of navigable elements.\n *\n * Exactly one element will have `tabindex=0` and all others will have\n * `tabindex=1`.\n * @param currentIndex - Index of element in `elements` to make current.\n * Defaults to the current element if there is one, or the first element\n * otherwise.\n * @param setFocus - Whether to focus the current element\n */\n const updateTabIndexes = (elements = getNavigableElements(), currentIndex = -1, setFocus = false, keyEvent) => {\n if (currentIndex < 0) {\n currentIndex = elements.findIndex(el => el.tabIndex === 0);\n if (currentIndex < 0) {\n currentIndex = 0;\n }\n }\n for (const [index, element] of elements.entries()) {\n element.tabIndex = index === currentIndex ? 0 : -1;\n if (index === currentIndex && setFocus) {\n lastFocusedItem.current = element;\n focusElement(element, keyEvent);\n }\n }\n };\n const onKeyDown = event => {\n const elements = getNavigableElements();\n let currentIndex = elements.findIndex(item => item.tabIndex === 0);\n let handled = false;\n if (horizontal && event.key === 'ArrowLeft' || vertical && event.key === 'ArrowUp') {\n if (currentIndex === 0) {\n currentIndex = loop ? elements.length - 1 : currentIndex;\n } else {\n --currentIndex;\n }\n handled = true;\n } else if (horizontal && event.key === 'ArrowRight' || vertical && event.key === 'ArrowDown') {\n if (currentIndex === elements.length - 1) {\n currentIndex = loop ? 0 : currentIndex;\n } else {\n ++currentIndex;\n }\n handled = true;\n } else if (event.key === 'Home') {\n currentIndex = 0;\n handled = true;\n } else if (event.key === 'End') {\n currentIndex = elements.length - 1;\n handled = true;\n }\n if (!handled) {\n return;\n }\n updateTabIndexes(elements, currentIndex, true /* setFocus */, event);\n event.preventDefault();\n event.stopPropagation();\n };\n const navigableElements = getNavigableElements();\n // Start focus sequence with previously focused element, if any\n const initialIndex = lastFocusedItem.current ? navigableElements.indexOf(lastFocusedItem.current) : 0;\n updateTabIndexes(navigableElements, initialIndex, autofocus);\n const listeners = new ListenerCollection();\n\n // Set an element as current when it gains focus. In Safari this event\n // may not be received if the element immediately loses focus after it\n // is triggered.\n listeners.add(container, 'focusin', event => {\n if (event.target === container && lastFocusedItem.current) {\n // Focus is moving back to the container after having left. Restore the\n // last tabindex. This allows users to exit and re-enter the widget\n // without resetting the navigation sequence.\n focusElement(lastFocusedItem.current);\n return;\n }\n const elements = getNavigableElements();\n const targetIndex = elements.indexOf(event.target);\n if (targetIndex >= 0) {\n updateTabIndexes(elements, targetIndex, autofocus);\n }\n });\n listeners.add(container, 'keydown', onKeyDown);\n\n // Update the tab indexes of elements as they are added, removed, enabled\n // or disabled.\n const mo = new MutationObserver(() => {\n updateTabIndexes();\n });\n mo.observe(container, {\n subtree: true,\n attributes: true,\n attributeFilter: ['disabled'],\n childList: true\n });\n return () => {\n listeners.removeAll();\n mo.disconnect();\n };\n }, [autofocus, containerRef, focusElement, horizontal, loop, selector, vertical, containerVisible]);\n}\n//# sourceMappingURL=use-arrow-key-navigation.js.map","import { useRef } from 'preact/hooks';\n\n/**\n * Return a function which wraps a callback to give it a stable value.\n *\n * The wrapper has a stable value across renders, but always forwards to the\n * callback from the most recent render. This is useful if you want to use a\n * callback inside a `useEffect` or `useMemo` hook without re-running the effect\n * or re-computing the memoed value when the callback changes.\n */\nexport function useStableCallback(callback) {\n const wrapper = useRef({\n callback,\n call: (...args) => wrapper.current.callback(...args)\n });\n\n // On each render, save the last callback value.\n wrapper.current.callback = callback;\n return wrapper.current.call;\n}\n//# sourceMappingURL=use-stable-callback.js.map","function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }\nfunction _toPropertyKey(t) { var i = _toPrimitive(t, \"string\"); return \"symbol\" == typeof i ? i : i + \"\"; }\nfunction _toPrimitive(t, r) { if (\"object\" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || \"default\"); if (\"object\" != typeof i) return i; throw new TypeError(\"@@toPrimitive must return a primitive value.\"); } return (\"string\" === r ? String : Number)(t); }\nimport { useRef } from 'preact/hooks';\n\n/**\n * Object ref which synchronizes its value to another ref.\n */\nclass SyncedRef {\n /**\n * @param [target] - Initial target for this ref to synchronize to.\n * This is not called/set until the {@link current} property of the\n * SyncedRef is set. This makes the target behave close to how it would\n * if used in place of the SyncedRef.\n */\n constructor(target) {\n _defineProperty(this, \"_target\", void 0);\n _defineProperty(this, \"_value\", void 0);\n this._target = target;\n this._value = null;\n }\n get current() {\n return this._value;\n }\n set current(value) {\n this._value = value;\n this._updateTarget();\n }\n get target() {\n return this._target;\n }\n set target(target) {\n if (target === this._target) {\n return;\n }\n this._target = target;\n\n // If the target changes after the initial render, we currently synchronize\n // the value immediately. This is different than what happens if the target\n // were passed to an element directly, as it would be updated only after the\n // render.\n this._updateTarget();\n }\n _updateTarget() {\n const value = this._value;\n if (typeof this._target === 'function') {\n this._target(value);\n } else if (this._target) {\n this._target.current = value;\n }\n }\n}\n\n/**\n * Return an object ref which synchronizes its value to another \"target\" ref.\n *\n * This is useful when a component needs an object ref for an element for\n * internal use, but also wants to allow the caller to get a ref for the same\n * element.\n *\n * The target ref can be either a callback or an object.\n *\n * @example\n * function Widget({ elementRef }) {\n * const ref = useSyncedRef(elementRef);\n *\n * useEffect(() => {\n * ref.current.focus();\n * }, []);\n *\n * return <input ref={ref}>...</input>;\n * }\n */\nexport function useSyncedRef(targetRef) {\n const container = useRef(new SyncedRef(targetRef));\n container.current.target = targetRef;\n return container.current;\n}\n//# sourceMappingURL=use-synced-ref.js.map","/*!\n\tCopyright (c) 2018 Jed Watson.\n\tLicensed under the MIT License (MIT), see\n\thttp://jedwatson.github.io/classnames\n*/\n/* global define */\n\n(function () {\n\t'use strict';\n\n\tvar hasOwn = {}.hasOwnProperty;\n\n\tfunction classNames () {\n\t\tvar classes = '';\n\n\t\tfor (var i = 0; i < arguments.length; i++) {\n\t\t\tvar arg = arguments[i];\n\t\t\tif (arg) {\n\t\t\t\tclasses = appendClass(classes, parseValue(arg));\n\t\t\t}\n\t\t}\n\n\t\treturn classes;\n\t}\n\n\tfunction parseValue (arg) {\n\t\tif (typeof arg === 'string' || typeof arg === 'number') {\n\t\t\treturn arg;\n\t\t}\n\n\t\tif (typeof arg !== 'object') {\n\t\t\treturn '';\n\t\t}\n\n\t\tif (Array.isArray(arg)) {\n\t\t\treturn classNames.apply(null, arg);\n\t\t}\n\n\t\tif (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {\n\t\t\treturn arg.toString();\n\t\t}\n\n\t\tvar classes = '';\n\n\t\tfor (var key in arg) {\n\t\t\tif (hasOwn.call(arg, key) && arg[key]) {\n\t\t\t\tclasses = appendClass(classes, key);\n\t\t\t}\n\t\t}\n\n\t\treturn classes;\n\t}\n\n\tfunction appendClass (value, newClass) {\n\t\tif (!newClass) {\n\t\t\treturn value;\n\t\t}\n\t\n\t\tif (value) {\n\t\t\treturn value + ' ' + newClass;\n\t\t}\n\t\n\t\treturn value + newClass;\n\t}\n\n\tif (typeof module !== 'undefined' && module.exports) {\n\t\tclassNames.default = classNames;\n\t\tmodule.exports = classNames;\n\t} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {\n\t\t// register as 'classnames', consistent with npm package name\n\t\tdefine('classnames', [], function () {\n\t\t\treturn classNames;\n\t\t});\n\t} else {\n\t\twindow.classNames = classNames;\n\t}\n}());\n","import{options as r,Fragment as e}from\"preact\";export{Fragment}from\"preact\";var t=/[\"&<]/;function n(r){if(0===r.length||!1===t.test(r))return r;for(var e=0,n=0,o=\"\",f=\"\";n<r.length;n++){switch(r.charCodeAt(n)){case 34:f=\""\";break;case 38:f=\"&\";break;case 60:f=\"<\";break;default:continue}n!==e&&(o+=r.slice(e,n)),o+=f,e=n+1}return n!==e&&(o+=r.slice(e,n)),o}var o=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,f=0,i=Array.isArray;function u(e,t,n,o,i,u){t||(t={});var a,c,p=t;if(\"ref\"in p)for(c in p={},t)\"ref\"==c?a=t[c]:p[c]=t[c];var l={type:e,props:p,key:n,ref:a,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:--f,__i:-1,__u:0,__source:i,__self:u};if(\"function\"==typeof e&&(a=e.defaultProps))for(c in a)void 0===p[c]&&(p[c]=a[c]);return r.vnode&&r.vnode(l),l}function a(r){var t=u(e,{tpl:r,exprs:[].slice.call(arguments,1)});return t.key=t.__v,t}var c={},p=/[A-Z]/g;function l(e,t){if(r.attr){var f=r.attr(e,t);if(\"string\"==typeof f)return f}if(\"ref\"===e||\"key\"===e)return\"\";if(\"style\"===e&&\"object\"==typeof t){var i=\"\";for(var u in t){var a=t[u];if(null!=a&&\"\"!==a){var l=\"-\"==u[0]?u:c[u]||(c[u]=u.replace(p,\"-$&\").toLowerCase()),s=\";\";\"number\"!=typeof a||l.startsWith(\"--\")||o.test(l)||(s=\"px;\"),i=i+l+\":\"+a+s}}return e+'=\"'+i+'\"'}return null==t||!1===t||\"function\"==typeof t||\"object\"==typeof t?\"\":!0===t?e:e+'=\"'+n(t)+'\"'}function s(r){if(null==r||\"boolean\"==typeof r||\"function\"==typeof r)return null;if(\"object\"==typeof r){if(void 0===r.constructor)return r;if(i(r)){for(var e=0;e<r.length;e++)r[e]=s(r[e]);return r}}return n(\"\"+r)}export{u as jsx,l as jsxAttr,u as jsxDEV,s as jsxEscape,a as jsxTemplate,u as jsxs};\n//# sourceMappingURL=jsxRuntime.module.js.map\n","import { createContext } from 'preact';\nconst CloseableContext = createContext({});\nexport default CloseableContext;\n//# sourceMappingURL=CloseableContext.js.map","import { createContext } from 'preact';\nconst ScrollContext = createContext({});\nexport default ScrollContext;\n//# sourceMappingURL=ScrollContext.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/layout/Card.tsx\";\nimport classnames from 'classnames';\nimport { downcastRef } from '../../util/typing';\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Render content in a card-like frame\n */\nexport default function Card({\n children,\n classes,\n elementRef,\n active = false,\n variant = 'raised',\n width = 'full',\n ...htmlAttributes\n}) {\n return _jsxDEV(\"div\", {\n \"data-component\": \"Card\",\n ...htmlAttributes,\n ref: downcastRef(elementRef),\n className: classnames('rounded-lg border bg-white', {\n 'shadow hover:shadow-md': variant === 'raised',\n // default\n 'shadow-md': active && variant === 'raised'\n }, {\n 'w-full': width === 'full',\n // default\n 'w-auto': width === 'auto'\n // No width is set if `width === 'custom'`\n }, classes),\n children: children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 40,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Card.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/layout/CardContent.tsx\";\nimport classnames from 'classnames';\nimport { downcastRef } from '../../util/typing';\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Apply consistent spacing and padding for actions content inside a Card\n */\nexport default function CardContent({\n children,\n classes,\n elementRef,\n size = 'md',\n ...htmlAttributes\n}) {\n return _jsxDEV(\"div\", {\n \"data-component\": \"CardContent\",\n ...htmlAttributes,\n ref: downcastRef(elementRef),\n className: classnames({\n 'p-3 space-y-4': size === 'md',\n // Default\n 'p-2 space-y-3': size === 'sm',\n 'p-4 space-y-6': size === 'lg'\n }, classes),\n children: children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 29,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=CardContent.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/Annotate.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from annotate.svg\n */\nexport default function AnnotateIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"AnnotateIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M14 0a2 2 0 0 1 2 2v13a1 1 0 0 1-1.555.832l-4.262-1.757A1 1 0 0 0 9.802 14H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm-2.109 3.5h-.484l-.14.006-.122.018a.7.7 0 0 0-.2.071l-.076.054-.108.1-.097.1-1.632 1.999-.091.12-.084.129a2.6 2.6 0 0 0-.291.722l-.03.142-.027.218-.009.223v2.646l.01.086.027.08a.54.54 0 0 0 .236.236l.067.028.07.017.074.005h2.907l.074-.005.094-.025a.5.5 0 0 0 .169-.108.5.5 0 0 0 .082-.096l.029-.051.027-.081.01-.086V7.336l-.006-.073-.018-.068a.44.44 0 0 0-.124-.178.6.6 0 0 0-.103-.074l-.055-.026-.087-.024-.092-.009h-.579l-.057-.006-.054-.017a.3.3 0 0 1-.096-.07.2.2 0 0 1-.045-.078l-.004-.04.01-.043.022-.043 1.311-2.227.047-.09.037-.106a.5.5 0 0 0-.06-.394.53.53 0 0 0-.255-.22l-.084-.028-.092-.016zm-5.924 0h-.424l-.121.006-.108.018a.6.6 0 0 0-.174.071l-.067.054-.095.1-.084.1-1.429 1.999-.08.12-.096.174a2.8 2.8 0 0 0-.232.677l-.025.142-.024.218L3 7.402v2.646l.008.086.024.08a.5.5 0 0 0 .097.148q.052.055.11.088l.058.028.062.017.065.005h2.543l.065-.005.082-.025a.515.515 0 0 0 .22-.204l.025-.051.024-.081.008-.086V7.336l-.005-.073-.023-.09a.487.487 0 0 0-.191-.23l-.048-.026-.076-.024-.08-.009H5.46l-.05-.006-.047-.017a.3.3 0 0 1-.084-.07.2.2 0 0 1-.04-.078l-.003-.04.008-.043.02-.043L6.411 4.36l.04-.09.033-.106a.55.55 0 0 0-.053-.394.5.5 0 0 0-.222-.22l-.074-.028-.08-.016z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Annotate.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/Cancel.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from cancel.svg\n */\nexport default function CancelIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"CancelIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"m12.322 5.085-.08.087-2.828 2.827 2.829 2.83a1 1 0 0 1-1.32 1.497l-.095-.083L8 9.414l-2.828 2.829c-.915.914-2.272-.388-1.494-1.328l.08-.087L6.584 8 3.757 5.172a1 1 0 0 1 1.32-1.498l.095.083L8 6.585l2.828-2.828c.886-.885 2.188.309 1.56 1.239z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Cancel.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/CaretDown.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from caret-down.svg\n */\nexport default function CaretDownIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"CaretDownIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M3.293 5.293a1 1 0 0 1 1.32-.083l.094.083L8 8.585l3.293-3.292a1 1 0 0 1 1.32-.083l.094.083a1 1 0 0 1 .083 1.32l-.083.094-4 4a1 1 0 0 1-1.32.083l-.094-.083-4-4a1 1 0 0 1 0-1.414\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=CaretDown.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/CaretLeft.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from caret-left.svg\n */\nexport default function CaretLeftIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"CaretLeftIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M9.293 3.293a1 1 0 0 1 1.497 1.32l-.083.094L7.415 8l3.292 3.293a1 1 0 0 1 .083 1.32l-.083.094a1 1 0 0 1-1.32.083l-.094-.083-4-4a1 1 0 0 1-.083-1.32l.083-.094z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=CaretLeft.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/CaretRight.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from caret-right.svg\n */\nexport default function CaretRightIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"CaretRightIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M5.293 3.293a1 1 0 0 1 1.32-.083l.094.083 4 4a1 1 0 0 1 .083 1.32l-.083.094-4 4a1 1 0 0 1-1.497-1.32l.083-.094L8.585 8 5.293 4.707a1 1 0 0 1-.083-1.32z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=CaretRight.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/Caution.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from caution.svg\n */\nexport default function CautionIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"CautionIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M8 0c.767 0 1.432.455 1.794 1.148l5.917 11.326c.386.738.36 1.573.071 2.23-.286.651-.926 1.296-1.864 1.296H2.082c-.938 0-1.578-.645-1.864-1.295a2.6 2.6 0 0 1 .07-2.23L6.207 1.147C6.568.455 7.233 0 8 0m0 2.038-.021.036L2.06 13.4a.6.6 0 0 0-.012.5.3.3 0 0 0 .062.1H13.89a.3.3 0 0 0 .061-.1.6.6 0 0 0-.012-.5L8.02 2.074zm5.874 11.972q0 0 .01-.006-.01.007-.01.006m-11.748 0q0 .002-.01-.006z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M8 5a1 1 0 0 1 1 1v3a1 1 0 0 1-2 0V6a1 1 0 0 1 1-1M9 12a1 1 0 1 1-2 0 1 1 0 0 1 2 0\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 26,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Caution.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/Check.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from check.svg\n */\nexport default function CheckIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"CheckIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M12.18 2.427a1 1 0 0 1 1.705 1.04l-.066.106-7 10a1 1 0 0 1-1.606.045l-.07-.104-3-5A1 1 0 0 1 3.792 7.39l.065.097 2.207 3.677z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Check.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/Checkbox.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from checkbox.svg\n */\nexport default function CheckboxIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"CheckboxIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M0 2.5A2.5 2.5 0 0 1 2.5 0h11A2.5 2.5 0 0 1 16 2.5v11a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 13.5zM2.5 1A1.5 1.5 0 0 0 1 2.5v11A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-11A1.5 1.5 0 0 0 13.5 1z\",\n \"clip-rule\": \"evenodd\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Checkbox.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/CheckboxCheckedFilled.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from checkbox-checked-filled.svg\n */\nexport default function CheckboxCheckedFilledIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"CheckboxCheckedFilledIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 23,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm10.18 3.636a.75.75 0 0 0-1.044.184l-4.588 6.552-1.655-2.758-.05-.072a.75.75 0 0 0-1.236.844l2.25 3.75.053.078a.75.75 0 0 0 1.204-.034l5.25-7.5.05-.08a.75.75 0 0 0-.234-.964\",\n \"clip-rule\": \"evenodd\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 24,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 13,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=CheckboxCheckedFilled.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/Hide.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from hide.svg\n */\nexport default function HideIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"HideIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"m1.613.21.094.083 14 14a1 1 0 0 1-1.32 1.497l-.094-.083-14-14A1 1 0 0 1 1.613.21m.583 4.845a1 1 0 0 1 .292 1.384C2.165 6.935 2 7.46 2 8c0 2.123 2.628 4 6 4q.33 0 .657-.024a1 1 0 0 1 .147 1.994Q8.405 14 8 14c-4.36 0-8-2.6-8-6 0-.937.283-1.84.812-2.653a1 1 0 0 1 1.384-.292M16 8c0-3.4-3.64-6-8-6q-.405 0-.804.03a1 1 0 1 0 .147 1.994Q7.668 4 8 4c3.372 0 6 1.877 6 4 0 .539-.165 1.065-.488 1.561a1 1 0 1 0 1.676 1.092C15.718 9.84 16 8.937 16 8\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Hide.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/Highlight.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from highlight.svg\n */\nexport default function HighlightIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"HighlightIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M13 1a1 1 0 0 0-1-1h-2l-.117.007a1 1 0 0 0-.75 1.493A1 1 0 0 0 9 2v6l.009.134a1 1 0 0 0 .125.365.996.996 0 0 0 .374 1.37A1 1 0 0 0 9.5 10v2l.006.114c.087.814 1.098 1.196 1.701.593l1-1 .087-.099A1 1 0 0 0 12.5 11l.001-1.134a1 1 0 0 0 .365-1.366A1 1 0 0 0 13 8V2l-.009-.134a1 1 0 0 0-.125-.365A1 1 0 0 0 13 1m0 14a1 1 0 0 0-1-1H1l-.117.007A1 1 0 0 0 1 16h11l.117-.007A1 1 0 0 0 13 15\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Highlight.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/MenuCollapse.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from menu-collapse.svg\n */\nexport default function MenuCollapseIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"data-component\": \"MenuCollapseIcon\",\n ...props,\n children: _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"m8.54 4.205 6.23 5.87a.69.69 0 0 1 0 1.03l-.72.68a.8.8 0 0 1-1.09 0L8 7.135l-4.96 4.65a.8.8 0 0 1-1.09 0l-.73-.69a.69.69 0 0 1 0-1.02l6.24-5.87a.8.8 0 0 1 1.08 0\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 18,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=MenuCollapse.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/MenuExpand.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from menu-expand.svg\n */\nexport default function MenuExpandIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"MenuExpandIcon\",\n ...props,\n children: _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M7.456 11.788 1.226 5.92a.695.695 0 0 1 0-1.025l.726-.684a.804.804 0 0 1 1.087-.001L8 8.861l4.961-4.65a.804.804 0 0 1 1.087.001l.727.684c.3.283.3.742 0 1.025l-6.23 5.867a.804.804 0 0 1-1.09 0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 19,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=MenuExpand.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/Note.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from note.svg\n */\nexport default function NoteIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"NoteIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M14 0a2 2 0 0 1 1.995 1.85L16 2v7a1 1 0 0 1-.31.724l-.09.076-8 6a1 1 0 0 1-.471.192L7 16H2a2 2 0 0 1-1.995-1.85L0 14V2A2 2 0 0 1 1.85.005L2 0zm0 2H2v12h4V9a1 1 0 0 1 .883-.993L7 8h7zm-6 8h4l-4 3z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Note.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/Pin.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from pin.svg\n */\nexport default function PinIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"PinIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M3 1a1 1 0 0 1 1-1h8a1 1 0 1 1 0 2 .5.5 0 0 0-.5.5v.146a.5.5 0 0 0 .053.224l1.341 2.683c.07.139.106.292.106.447v3a1 1 0 0 1-1 1H9.024l.093 4.977a1 1 0 0 1-2 .046L7.024 10H4a1 1 0 0 1-1-1V6a1 1 0 0 1 .106-.447L4.447 2.87a.5.5 0 0 0 .053-.224V2.5A.5.5 0 0 0 4 2a1 1 0 0 1-1-1m4 1a.5.5 0 0 0-.5.5V3a1 1 0 0 1-.106.447L5.053 6.13A.5.5 0 0 0 5 6.355V7.5a.5.5 0 0 0 .5.5h5a.5.5 0 0 0 .5-.5V6.354a.5.5 0 0 0-.053-.224L9.606 3.448A1 1 0 0 1 9.5 3v-.5A.5.5 0 0 0 9 2z\",\n \"clip-rule\": \"evenodd\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Pin.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/PointerDown.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from pointer-down.svg\n */\nexport default function PointerDownIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"9\",\n \"data-component\": \"PointerDownIcon\",\n ...props,\n children: _jsxDEV(\"path\", {\n stroke: \"currentColor\",\n d: \"m15.5 0-7 8-8-8\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 18,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=PointerDown.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/PointerUp.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from pointer-up.svg\n */\nexport default function PointerUpIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"9\",\n \"data-component\": \"PointerUpIcon\",\n ...props,\n children: _jsxDEV(\"path\", {\n stroke: \"currentColor\",\n d: \"m.5 9 7-8 8 8\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 18,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=PointerUp.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/Radio.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from radio.svg\n */\nexport default function RadioIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"RadioIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8\",\n \"clip-rule\": \"evenodd\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Radio.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/RadioChecked.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from radio-checked.svg\n */\nexport default function RadioCheckedIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"RadioCheckedIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8\",\n \"clip-rule\": \"evenodd\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M14 8A6 6 0 1 1 2 8a6 6 0 0 1 12 0\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 27,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=RadioChecked.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/Selection.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from selection.svg\n */\nexport default function SelectionIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"SelectionIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M2.5 0h-1A1.5 1.5 0 0 0 0 1.5v1a1.5 1.5 0 0 0 1 1.415v8.17A1.5 1.5 0 0 0 0 13.5v1A1.5 1.5 0 0 0 1.5 16h1a1.5 1.5 0 0 0 1.415-1h8.17a1.5 1.5 0 0 0 1.415 1h1a1.5 1.5 0 0 0 1.5-1.5v-1a1.5 1.5 0 0 0-1-1.415v-8.17A1.5 1.5 0 0 0 16 2.5v-1A1.5 1.5 0 0 0 14.5 0h-1a1.5 1.5 0 0 0-1.415 1h-8.17A1.5 1.5 0 0 0 2.5 0m9.585 13c.151-.426.489-.764.915-.915v-8.17A1.5 1.5 0 0 1 12.085 3h-8.17c-.151.426-.489.764-.915.915v8.17c.426.151.764.489.915.915zM1 1.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zM1.5 13a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zM13 1.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm.5 11.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5z\",\n \"clip-rule\": \"evenodd\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Selection.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/icons/Show.tsx\"; // This file was auto-generated using scripts/generate-icons.js\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Icon generated from show.svg\n */\nexport default function ShowIcon(props) {\n return _jsxDEV(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n \"aria-hidden\": \"true\",\n viewBox: \"0 0 16 16\",\n \"data-component\": \"ShowIcon\",\n ...props,\n children: _jsxDEV(\"g\", {\n \"fill-rule\": \"evenodd\",\n children: [_jsxDEV(\"path\", {\n fill: \"none\",\n d: \"M0 0h16v16H0z\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 21,\n columnNumber: 9\n }, this), _jsxDEV(\"path\", {\n fill: \"currentColor\",\n d: \"M8 2c4.36 0 8 2.6 8 6s-3.64 6-8 6-8-2.6-8-6 3.64-6 8-6m0 2C4.628 4 2 5.877 2 8s2.628 4 6 4 6-1.877 6-4-2.628-4-6-4m2 4a2 2 0 1 0-4 0 2 2 0 0 0 4 0\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 22,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 20,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 11,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Show.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/input/Button.tsx\";\nimport classnames from 'classnames';\nimport { downcastRef } from '../../util/typing';\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Render a button with optional icon\n */\nexport default function Button({\n children,\n classes,\n elementRef,\n expanded,\n pressed,\n title,\n icon: Icon,\n size = 'md',\n variant = 'secondary',\n unstyled = false,\n role,\n ...htmlAttributes\n}) {\n const styled = !unstyled;\n const themed = styled && variant !== 'custom';\n const sized = styled && size !== 'custom';\n const ariaProps = {\n 'aria-label': title\n };\n\n // aria-pressed and aria-expanded are not allowed for buttons with\n // an aria role of `tab`. Instead, the aria-selected attribute is expected.\n if (role === 'tab') {\n ariaProps['aria-selected'] = pressed;\n } else {\n ariaProps['aria-pressed'] = pressed;\n ariaProps['aria-expanded'] = expanded;\n }\n return _jsxDEV(\"button\", {\n role: role !== null && role !== void 0 ? role : 'button',\n \"data-component\": \"Button\"\n // Setting a default `type` can prevent undesired form submissions in\n // certain cases\n ,\n type: \"button\",\n ...ariaProps,\n ...htmlAttributes,\n className: classnames({\n 'focus-visible-ring transition-colors whitespace-nowrap flex items-center': styled\n }, themed && {\n 'font-semibold rounded': true,\n 'text-grey-7 bg-grey-1 enabled:hover:text-grey-9 enabled:hover:bg-grey-2 aria-pressed:text-grey-9 aria-expanded:text-grey-9': variant === 'secondary',\n // default\n 'text-grey-1 bg-grey-7 enabled:hover:bg-grey-8 disabled:text-grey-4': variant === 'primary'\n }, sized && {\n 'p-2 gap-x-2': size === 'md',\n // default\n 'p-1 gap-x-1': size === 'xs',\n 'p-1.5 gap-x-1.5': size === 'sm',\n 'p-2.5 gap-x-1.5': size === 'lg'\n }, classes),\n ref: downcastRef(elementRef),\n title: title,\n children: [Icon && _jsxDEV(Icon, {\n className: \"w-em h-em\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 120,\n columnNumber: 16\n }, this), children]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 89,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Button.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/input/InputGroup.tsx\";\nimport classnames from 'classnames';\nimport { downcastRef } from '../../util/typing';\n\n// These styles may be applied to input components to adapt them when in\n// an InputGroup\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nexport const inputGroupStyles = classnames(\n// All inputs within an InputGroup should have a border. Turn off border-radius\n'input-group:border input-group:rounded-none',\n// Restore border-radius on the leftmost and rightmost components in the group\n'input-group:first:rounded-l input-group:last:rounded-r',\n// \"Collapse\" borders between input components\n'input-group:ml-[-1px] input-group:first:ml-0',\n// Make sure focused element appears on top, preventing a cropped focus ring\n'focus:z-1');\n/**\n * Render a container that lays out a group of input components\n */\nexport default function InputGroup({\n children,\n classes,\n elementRef,\n ...htmlAttributes\n}) {\n return _jsxDEV(\"div\", {\n \"data-component\": \"InputGroup\",\n ...htmlAttributes,\n ref: downcastRef(elementRef),\n className: classnames(\n // Set the `.input-group` class so that children may\n // use the `input-group:` variant in their styles\n 'input-group', 'flex items-stretch w-full justify-center', classes),\n children: children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 34,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=InputGroup.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/input/IconButton.tsx\";\nimport classnames from 'classnames';\nimport { downcastRef } from '../../util/typing';\nimport Button from './Button';\nimport { inputGroupStyles } from './InputGroup';\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Render a button that only contains an icon.\n */\nexport default function IconButton({\n children,\n classes,\n elementRef,\n pressed,\n expanded,\n icon: Icon,\n size = 'md',\n title,\n variant = 'secondary',\n unstyled = false,\n ...htmlAttributes\n}) {\n const styled = !unstyled;\n const themed = styled && variant !== 'custom';\n const sized = styled && size !== 'custom';\n return _jsxDEV(Button, {\n \"data-component\": \"IconButton\",\n ...htmlAttributes,\n classes: classnames({\n 'focus-visible-ring transition-colors rounded whitespace-nowrap': styled,\n 'flex items-center justify-center': styled\n },\n // Adapt styles when in an InputGroup\n styled && inputGroupStyles, themed && {\n // variant\n 'text-grey-7 bg-transparent enabled:hover:text-grey-9 aria-pressed:text-brand aria-expanded:text-brand': variant === 'secondary',\n //default\n 'text-brand bg-transparent enabled:hover:text-grey-9 disabled:text-grey-7': variant === 'primary',\n 'text-grey-7 bg-grey-2 enabled:hover:text-grey-9 enabled:hover:bg-grey-3 disabled:text-grey-5 aria-pressed:bg-grey-3 aria-expanded:bg-grey-3': variant === 'dark'\n }, sized && {\n 'gap-x-2 touch:min-w-touch-minimum touch:min-h-touch-minimum': true,\n 'p-2': size === 'md',\n // Default\n 'p-1': size === 'xs',\n 'p-1.5': size === 'sm',\n 'p-2.5': size === 'lg'\n }, classes),\n elementRef: downcastRef(elementRef),\n title: title,\n pressed: pressed,\n expanded: expanded,\n unstyled: true,\n children: [Icon && _jsxDEV(Icon, {\n className: \"w-em h-em\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 84,\n columnNumber: 16\n }, this), children]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 49,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=IconButton.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/input/ToggleInput.tsx\";\nimport classnames from 'classnames';\nimport { downcastRef } from '../../util/typing';\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Render a labeled checkbox or radio input. The input is styled with two icons:\n * one for the unchecked state and one for the checked state. The input itself\n * is positioned exactly on top of the icon, but is non-visible.\n */\nexport default function ToggleInput({\n children,\n elementRef,\n containerRef,\n checked,\n icon: UncheckedIcon,\n checkedIcon: CheckedIcon,\n disabled,\n onChange,\n id,\n type,\n containerClasses,\n ...htmlAttributes\n}) {\n const Icon = checked ? CheckedIcon : UncheckedIcon;\n return _jsxDEV(\"label\", {\n className: classnames('relative flex items-center gap-x-1.5', {\n 'cursor-pointer': !disabled,\n 'opacity-70': disabled\n }, containerClasses),\n htmlFor: id,\n \"data-composite-component\": type === 'checkbox' ? 'Checkbox' : 'RadioButton',\n ref: downcastRef(containerRef),\n children: [_jsxDEV(\"input\", {\n ...htmlAttributes,\n type: type,\n ref: downcastRef(elementRef),\n className: classnames(\n // Set the special Tailwind peer class to allow sibling elements\n // to style themselves based on the state of this element\n 'peer',\n // Position this atop the icon and size it to the same dimensions\n 'absolute w-em h-em',\n // Make input visually hidden, but some screen readers won't read out\n // elements with 0 opacity\n 'opacity-[.00001]', {\n 'cursor-pointer': !disabled\n }),\n checked: checked,\n disabled: disabled,\n id: id,\n onChange: onChange\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 66,\n columnNumber: 7\n }, this), _jsxDEV(Icon, {\n className: classnames(\n // Add an outline ring to the icon when the input is focus-visible.\n // The ring needs to be applied here because the `input` has an\n // effectively-0 opacity.\n 'peer-focus-visible:ring', 'w-em h-em')\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 88,\n columnNumber: 7\n }, this), children]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 51,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=ToggleInput.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/input/Checkbox.tsx\";\nimport { useState } from 'preact/hooks';\nimport { CheckboxCheckedFilledIcon, CheckboxIcon } from '../icons';\nimport ToggleInput from './ToggleInput';\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Render a labeled checkbox input. The checkbox is styled with two icons:\n * one for the unchecked state and one for the checked state. The input itself\n * is positioned exactly on top of the icon, but is non-visible.\n */\nexport default function Checkbox({\n checked,\n defaultChecked = false,\n icon = CheckboxIcon,\n checkedIcon = CheckboxCheckedFilledIcon,\n onChange,\n ...rest\n}) {\n // If `checked` is present, treat this as a controlled component\n const isControlled = typeof checked === 'boolean';\n // Only use this local state if checkbox is uncontrolled\n const [uncontrolledChecked, setUncontrolledChecked] = useState(defaultChecked);\n const isChecked = isControlled ? checked : uncontrolledChecked;\n function handleChange(event) {\n onChange === null || onChange === void 0 || onChange.call(this, event);\n if (!isControlled) {\n setUncontrolledChecked(event.target.checked);\n }\n }\n return _jsxDEV(ToggleInput, {\n icon: icon,\n checkedIcon: checkedIcon,\n type: \"checkbox\",\n checked: isChecked,\n onChange: handleChange,\n ...rest\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 65,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Checkbox.js.map","import { createContext } from 'preact';\nconst RadioGroupContext = createContext(null);\nexport default RadioGroupContext;\n//# sourceMappingURL=RadioGroupContext.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/input/RadioGroup.tsx\";\nimport classnames from 'classnames';\nimport { useContext, useRef } from 'preact/hooks';\nimport { useArrowKeyNavigation } from '../../hooks/use-arrow-key-navigation';\nimport { RadioCheckedIcon, RadioIcon } from '../icons';\nimport RadioGroupContext from './RadioGroupContext';\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nfunction Radio({\n value,\n children,\n subtitle,\n disabled: radioDisabled\n}) {\n const radioGroupContext = useContext(RadioGroupContext);\n if (!radioGroupContext) {\n throw new Error('RadioGroup.Radio can only be used as RadioGroup child');\n }\n const {\n selected,\n disabled = radioDisabled,\n onChange\n } = radioGroupContext;\n const isSelected = !disabled && selected === value;\n return _jsxDEV(\"div\", {\n role: \"radio\",\n \"aria-checked\": isSelected,\n \"aria-disabled\": disabled,\n className: classnames('focus-visible-ring rounded-lg px-3 py-2 flex-1 group', {\n 'bg-grey-3/50': isSelected,\n 'hover:bg-grey-3/25': !isSelected && !disabled,\n 'opacity-70': disabled,\n 'cursor-pointer': !disabled\n }),\n \"data-value\": value,\n onClick: !disabled ? () => onChange(value) : undefined,\n onKeyDown: disabled ? undefined : e => {\n if (['Enter', ' '].includes(e.key)) {\n e.preventDefault();\n onChange(value);\n }\n },\n tabIndex: -1,\n children: [_jsxDEV(\"div\", {\n className: \"flex items-center gap-x-1.5\",\n children: [isSelected ? _jsxDEV(RadioCheckedIcon, {}, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 68,\n columnNumber: 23\n }, this) : _jsxDEV(RadioIcon, {}, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 68,\n columnNumber: 46\n }, this), children]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 67,\n columnNumber: 7\n }, this), subtitle && _jsxDEV(\"div\", {\n className: classnames('pl-4 ml-1.5 mt-1 text-sm', {\n 'text-grey-7': isSelected,\n 'text-grey-6 group-hover:text-grey-7': !isSelected && !disabled\n }),\n children: subtitle\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 72,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 40,\n columnNumber: 5\n }, this);\n}\nRadio.displayName = 'RadioGroup.Radio';\nfunction RadioGroupMain({\n direction = 'horizontal',\n children,\n selected,\n onChange,\n disabled,\n 'aria-label': label,\n 'aria-labelledby': labelledBy,\n name\n}) {\n const containerRef = useRef(null);\n useArrowKeyNavigation(containerRef, {\n loop: false,\n selector: '[role=\"radio\"]:not([aria-disabled=\"true\"])',\n focusElement: el => {\n onChange(el.dataset.value);\n el.focus();\n }\n });\n return _jsxDEV(RadioGroupContext.Provider, {\n value: {\n selected,\n disabled,\n onChange: onChange\n },\n children: [_jsxDEV(\"div\", {\n \"aria-label\": label,\n \"aria-labelledby\": labelledBy,\n ref: containerRef,\n role: \"radiogroup\",\n className: classnames('w-full flex gap-1.5', {\n 'flex-col': direction === 'vertical'\n }),\n children: children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 134,\n columnNumber: 7\n }, this), name && _jsxDEV(\"input\", {\n type: \"hidden\",\n \"data-testid\": \"hidden-input\",\n name: name,\n value: selected,\n disabled: disabled\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 146,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 131,\n columnNumber: 5\n }, this);\n}\nconst RadioGroup = Object.assign(RadioGroupMain, {\n Radio,\n displayName: 'RadioGroup'\n});\nexport default RadioGroup;\n//# sourceMappingURL=RadioGroup.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/feedback/Popover.tsx\";\nimport classnames from 'classnames';\nimport { useCallback, useEffect, useLayoutEffect } from 'preact/hooks';\nimport { useClickAway } from '../../hooks/use-click-away';\nimport { useKeyPress } from '../../hooks/use-key-press';\nimport { useSyncedRef } from '../../hooks/use-synced-ref';\nimport { ListenerCollection } from '../../util/listener-collection';\nimport { downcastRef } from '../../util/typing';\n\n/** Small space to apply between the anchor element and the popover */\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nconst POPOVER_ANCHOR_EL_GAP = '.15rem';\n\n/**\n * Space in pixels to apply between the popover and the viewport sides to\n * prevent it from growing to the very edges.\n */\nexport const POPOVER_VIEWPORT_HORIZONTAL_GAP = 8;\n/**\n * Manages the popover position manually to make sure it renders \"next\" to the\n * anchor element (above or below). This is mainly needed when using the\n * popover API, as that makes it render in the top layer, making it impossible\n * to position it relative to the anchor element via regular CSS.\n *\n * @param asNativePopover - Native popover API is used to toggle the popover\n * @param alignToRight - Whether the popover should be aligned to the right side\n * of the anchor element or not\n */\nfunction usePopoverPositioning(anchorElementRef, popoverRef, popoverOpen, asNativePopover, alignToRight) {\n const adjustPopoverPositioning = useCallback(() => {\n const popoverEl = popoverRef.current;\n const anchorEl = anchorElementRef.current;\n\n /**\n * Set the positioning styles synchronously (not via <div style={computedStyles} />),\n * to make sure positioning happens before other side effects.\n * @return - A callback that undoes the property assignments\n */\n const setPopoverCSSProps = props => {\n Object.assign(popoverEl.style, props);\n const keys = Object.keys(props);\n return () => keys.map(prop => popoverEl.style[prop] = '');\n };\n const viewportHeight = window.innerHeight;\n const {\n top: anchorElDistanceToTop,\n bottom: anchorElBottom,\n left: anchorElLeft,\n height: anchorElHeight,\n width: anchorElWidth\n } = anchorEl.getBoundingClientRect();\n const anchorElDistanceToBottom = viewportHeight - anchorElBottom;\n const {\n height: popoverHeight,\n width: popoverWidth\n } = popoverEl.getBoundingClientRect();\n\n // The popover should render above only if there's not enough space below to\n // fit it and there's more absolute space above than below\n const shouldBeAbove = anchorElDistanceToBottom < popoverHeight && anchorElDistanceToTop > anchorElDistanceToBottom;\n if (!asNativePopover) {\n // Set styles for non-popover mode\n if (shouldBeAbove) {\n return setPopoverCSSProps({\n bottom: '100%',\n marginBottom: POPOVER_ANCHOR_EL_GAP\n });\n }\n return setPopoverCSSProps({\n top: '100%',\n marginTop: POPOVER_ANCHOR_EL_GAP\n });\n }\n const {\n top: bodyTop,\n width: bodyWidth\n } = document.body.getBoundingClientRect();\n const absBodyTop = Math.abs(bodyTop);\n\n // The available space is:\n // - left-aligned popovers: distance from left side of anchor element to\n // right side of viewport\n // - right-aligned popovers: distance from right side of anchor element to\n // left side of viewport\n const availableSpace = (alignToRight ? anchorElLeft + anchorElWidth : bodyWidth - anchorElLeft) - POPOVER_VIEWPORT_HORIZONTAL_GAP;\n let left = anchorElLeft;\n if (popoverWidth > availableSpace) {\n // If the popover is not going to fit the available space, let it \"grow\"\n // in the opposite direction\n left = alignToRight ? POPOVER_VIEWPORT_HORIZONTAL_GAP : left - (popoverWidth - availableSpace);\n } else if (alignToRight && popoverWidth > anchorElWidth) {\n // If a right-aligned popover fits the available space, but it's bigger\n // than the anchor element, move it to the left so that it is aligned with\n // the right side of the element\n left -= popoverWidth - anchorElWidth;\n }\n return setPopoverCSSProps({\n minWidth: `${anchorElWidth}px`,\n top: shouldBeAbove ? `calc(${absBodyTop + anchorElDistanceToTop - popoverHeight}px - ${POPOVER_ANCHOR_EL_GAP})` : `calc(${absBodyTop + anchorElDistanceToTop + anchorElHeight}px + ${POPOVER_ANCHOR_EL_GAP})`,\n left: `${Math.max(POPOVER_VIEWPORT_HORIZONTAL_GAP, left)}px`\n });\n }, [asNativePopover, anchorElementRef, popoverRef, alignToRight]);\n useLayoutEffect(() => {\n if (!popoverOpen) {\n return () => {};\n }\n\n // First of all, open popover if it's using the native API, otherwise its\n // size is 0x0 and positioning calculations won't work.\n const popover = popoverRef.current;\n if (asNativePopover) {\n popover.togglePopover(true);\n }\n const cleanup = adjustPopoverPositioning();\n if (!asNativePopover) {\n return cleanup;\n }\n\n // Readjust popover position when any element scrolls, just in case that\n // affected the anchor element position.\n const listeners = new ListenerCollection();\n listeners.add(document.body, 'scroll', adjustPopoverPositioning, {\n capture: true\n });\n\n // Readjust popover positioning if its resized, in case it dropped-up, and\n // it needs to be moved down\n const observer = new ResizeObserver(adjustPopoverPositioning);\n observer.observe(popover);\n return () => {\n if (asNativePopover) {\n popover === null || popover === void 0 || popover.togglePopover(false);\n }\n cleanup();\n listeners.removeAll();\n observer.disconnect();\n };\n }, [adjustPopoverPositioning, asNativePopover, popoverOpen, popoverRef]);\n}\n\n/**\n * Add the right listeners to the popover so that `onClose` is called when\n * clicking away or pressing `Escape`.\n */\nfunction useOnClose(popoverRef, anchorElementRef, onClose, popoverOpen, asNativePopover) {\n // When the popover API is used, listen for the `toggle` event and call\n // onClose() when transitioning from `open` to `closed`.\n // This happens when clicking away or pressing `Escape` key.\n useEffect(() => {\n if (!asNativePopover) {\n return () => {};\n }\n const popover = popoverRef.current;\n const toggleListener = e => {\n if (e.oldState === 'open' && e.newState === 'closed') {\n onClose();\n }\n };\n popover.addEventListener('toggle', toggleListener);\n return () => popover.removeEventListener('toggle', toggleListener);\n }, [asNativePopover, onClose, popoverRef]);\n\n // When the popover API is not used, manually add listeners for Escape key\n // press and click away, to mimic the native popover behavior.\n // Disable these while the popover is closed, otherwise trying to open it\n // by interacting with some other element will trigger a click-away and\n // immediately close the popover after it opens..\n const enabled = popoverOpen && !asNativePopover;\n useClickAway(popoverRef, e => {\n // Ignore clicking \"away\" when the target is the anchor element.\n // In most cases, popovers will be anchored to a \"toggle\" which is\n // supposed to open/close the popover on click, so closing-on-click-away\n // when they are the target will cause the popover to close and\n // immediately open again.\n if (!e.composedPath().includes(anchorElementRef.current)) {\n onClose();\n }\n }, {\n enabled\n });\n useKeyPress(['Escape'], onClose, {\n enabled\n });\n}\n/**\n * Restore focus to the previously active element when a popover is closed.\n */\nfunction useRestoreFocusOnClose({\n popoverRef,\n open\n}) {\n useLayoutEffect(() => {\n const container = popoverRef.current;\n const restoreFocusTo = open ? document.activeElement : null;\n if (!container || !restoreFocusTo) {\n return () => {};\n }\n return () => {\n // When a popover is opened and then closed, there are several\n // possibilities for what happens to the focus:\n //\n // 1. The focus may be unchanged from before the popover was opened.\n //\n // 2. The focus may have moved into the popover when it was opened, and\n // then back to either the previously focused element or the body when\n // it was closed.\n //\n // When a native popover is closed via `togglePopover` or `hidePopover`,\n // focus will revert to the element that was focused at the time the\n // popover was shown. See https://html.spec.whatwg.org/multipage/popover.html#dom-hidepopover.\n //\n // 3. The user may have clicked an element outside the popover, focusing\n // that element and causing the popover to close.\n //\n // From the above cases, we only need to restore focus if it is still\n // inside the popover, or focus reverted to the document body.\n const currentFocus = document.activeElement;\n if (currentFocus && !container.contains(currentFocus) && currentFocus !== document.body) {\n return;\n }\n restoreFocusTo.focus();\n };\n }, [popoverRef, open]);\n}\nexport default function Popover({\n anchorElementRef,\n children,\n open,\n onClose,\n align = 'left',\n classes,\n variant = 'panel',\n onScroll,\n elementRef,\n /* eslint-disable-next-line no-prototype-builtins */\n asNativePopover = HTMLElement.prototype.hasOwnProperty('popover')\n}) {\n const popoverRef = useSyncedRef(elementRef);\n usePopoverPositioning(anchorElementRef, popoverRef, open, asNativePopover, align === 'right');\n useOnClose(popoverRef, anchorElementRef, onClose, open, asNativePopover);\n useRestoreFocusOnClose({\n open,\n popoverRef: popoverRef\n });\n return _jsxDEV(\"div\", {\n className: classnames('absolute z-5', variant === 'panel' && ['max-h-80 overflow-y-auto overflow-x-hidden', 'rounded border bg-white shadow hover:shadow-md focus-within:shadow-md'], asNativePopover && [\n // We don't want the popover to ever render outside the viewport,\n // and we give it a 16px gap\n 'max-w-[calc(100%-16px)]',\n // Overwrite [popover] default styles\n 'p-0 m-0'], !asNativePopover && {\n // Hiding instead of unmounting so that popover size can be computed\n // to position it above or below\n hidden: !open,\n 'right-0': align === 'right',\n 'min-w-full': true\n }, classes),\n ref: downcastRef(popoverRef),\n popover: asNativePopover && 'auto',\n onScroll: onScroll,\n \"data-testid\": \"popover\",\n \"data-component\": \"Popover\",\n children: open && children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 356,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Popover.js.map","import { useEffect } from 'preact/hooks';\nimport { ListenerCollection } from '../util/listener-collection';\n/**\n * Listen on document.body for click events. If a click event's target is\n * outside the `container` element, invoke the `callback`. Do not listen if\n * not `enabled`.\n */\nexport function useClickAway(container, callback, {\n enabled = true\n} = {}) {\n useEffect(() => {\n if (!enabled) {\n return () => {};\n }\n const target = document.body;\n const listeners = new ListenerCollection();\n const handleAwayClick = event => {\n if (container.current &&\n // We test the composed path here to handle the case where the clicked\n // element was in fact in the container, but is removed from the DOM\n // (eg. by a re-render in a child component) before this callback is run.\n // The composed path reflects the DOM hierarchy at the time the event was\n // dispatched.\n !event.composedPath().includes(container.current)) {\n callback(event);\n }\n };\n listeners.add(target, 'mousedown', handleAwayClick);\n listeners.add(target, 'click', handleAwayClick);\n return () => {\n listeners.removeAll();\n };\n }, [container, enabled, callback]);\n}\n//# sourceMappingURL=use-click-away.js.map","import { useEffect } from 'preact/hooks';\nimport { ListenerCollection } from '../util/listener-collection';\n/**\n * Listen on HTMLElement `target` for key press events for the designated `keys`\n * and invoke a callback. Do not listen if not `enabled`.\n *\n * @param keys - Array of keys (e.g. 'Escape', 'd') to listen for\n */\nexport function useKeyPress(keys, callback, {\n enabled = true\n} = {}) {\n useEffect(() => {\n if (!enabled) {\n return () => {};\n }\n const target = document.body;\n const listeners = new ListenerCollection();\n listeners.add(target, 'keydown', event => {\n if (keys.includes(event.key)) {\n callback(event);\n }\n });\n return () => {\n listeners.removeAll();\n };\n }, [enabled, callback, keys]);\n}\n//# sourceMappingURL=use-key-press.js.map","import { createContext } from 'preact';\nconst SelectContext = createContext(null);\nexport default SelectContext;\n//# sourceMappingURL=SelectContext.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/input/Select.tsx\";\nimport classnames from 'classnames';\nimport { useCallback, useContext, useId, useMemo, useRef, useState } from 'preact/hooks';\nimport { useArrowKeyNavigation } from '../../hooks/use-arrow-key-navigation';\nimport { useFocusAway } from '../../hooks/use-focus-away';\nimport { useSyncedRef } from '../../hooks/use-synced-ref';\nimport { downcastRef } from '../../util/typing';\nimport Popover from '../feedback/Popover';\nimport { CheckboxCheckedFilledIcon, CheckIcon, MenuCollapseIcon, MenuExpandIcon } from '../icons';\nimport Checkbox from './Checkbox';\nimport { inputGroupStyles } from './InputGroup';\nimport SelectContext from './SelectContext';\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nfunction optionChildren(children, status) {\n if (typeof children === 'function') {\n return children(status);\n }\n return children;\n}\nfunction SelectOption({\n value,\n children,\n disabled = false,\n classes,\n elementRef,\n title\n}) {\n const checkboxRef = useRef(null);\n const checkboxContainerRef = useRef(null);\n const optionRef = useSyncedRef(elementRef);\n const eventTriggeredInCheckbox = e => e.target === checkboxRef.current || e.target === checkboxContainerRef.current;\n const selectContext = useContext(SelectContext);\n if (!selectContext) {\n throw new Error('Select.Option can only be used as Select or MultiSelect child');\n }\n const {\n selectValue,\n value: currentValue,\n multiple\n } = selectContext;\n const selected = useMemo(() => {\n if (disabled) {\n return false;\n }\n if (!multiple) {\n return currentValue === value;\n }\n\n // In multi-select, the option should be marked as selected for values\n // which are explicitly part of the array, or for `undefined` values if the\n // array is empty\n return currentValue.includes(value) || currentValue.length === 0 && value === undefined;\n }, [currentValue, disabled, multiple, value]);\n const selectOneValue = useCallback(() => {\n const options = {\n closeListbox: true\n };\n if (!multiple) {\n selectValue(value, options);\n } else {\n selectValue(value !== undefined ? [value] : [], options);\n }\n }, [multiple, selectValue, value]);\n const toggleValue = useCallback(() => {\n /* istanbul ignore next - This will never be invoked in single-select, but TS doesn't know it */\n if (!multiple) {\n return;\n }\n const options = {\n // Close listbox only if selected value is a \"clear\" option. Clear options\n // are those with `undefined` value\n closeListbox: value === undefined\n };\n\n // In multi-select, clear selection for `undefined` values\n if (value === undefined) {\n selectValue([], options);\n return;\n }\n\n // In multi-select, toggle clicked items\n const index = currentValue.indexOf(value);\n if (index === -1) {\n selectValue([...currentValue, value], options);\n } else {\n const copy = [...currentValue];\n copy.splice(index, 1);\n selectValue(copy, options);\n }\n }, [currentValue, multiple, selectValue, value]);\n return _jsxDEV(\"li\", {\n className: classnames('w-full ring-inset outline-none rounded-none select-none', 'px-1 mb-1 first:mt-1 whitespace-nowrap group', {\n 'text-grey-4': disabled,\n 'cursor-pointer': !disabled\n }, classes),\n onClick: e => {\n if (!disabled &&\n // Do not invoke callback if clicked element is the checkbox or its\n // container, as it has its own event handler.\n !eventTriggeredInCheckbox(e)) {\n selectOneValue();\n }\n },\n onKeyDown: e => {\n if (disabled) {\n return;\n }\n if (['Enter', ' '].includes(e.key) &&\n // Do not invoke callback if event triggered in the checkbox or its\n // container, as it has its own event handler.\n !eventTriggeredInCheckbox(e)) {\n e.preventDefault();\n selectOneValue();\n } else if (checkboxRef.current && e.key === 'ArrowRight') {\n e.preventDefault();\n checkboxRef.current.focus();\n }\n },\n role: \"option\",\n \"aria-disabled\": disabled,\n \"aria-selected\": selected\n // Set tabIndex to 0 for selected option, so that useArrowKeyNavigation\n // initially focuses it\n ,\n tabIndex: selected ? 0 : -1,\n ref: downcastRef(optionRef),\n title: title,\n children: _jsxDEV(\"div\", {\n className: classnames('flex justify-between items-center', 'w-full rounded', {\n 'hover:bg-grey-1 group-focus-visible:ring': !disabled,\n 'bg-grey-1 hover:bg-grey-2': selected\n }),\n children: [_jsxDEV(\"div\", {\n className: classnames('py-2 pl-3', {\n truncate: selectContext.listboxOverflow === 'truncate',\n 'whitespace-normal': selectContext.listboxOverflow === 'wrap'\n }),\n children: optionChildren(children, {\n selected,\n disabled\n })\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 203,\n columnNumber: 9\n }, this), !multiple && _jsxDEV(\"div\", {\n className: \"px-3\",\n children: _jsxDEV(CheckIcon, {\n className: classnames('text-grey-6 scale-125', {\n // Make the icon visible/invisible, instead of conditionally\n // rendering it, to ensure consistent spacing among selected and\n // non-selected options\n 'opacity-0': !selected\n })\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 213,\n columnNumber: 13\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 212,\n columnNumber: 11\n }, this), multiple && _jsxDEV(Checkbox, {\n containerClasses: classnames(\n // Make the checkbox stretch, so that its actionable surface spans\n // to the very edges of the option containing it.\n 'self-stretch px-3',\n // The checkbox is sized based on the container's font size. Make\n // it a bit larger.\n 'text-lg', {\n 'text-grey-6': selected,\n 'text-grey-3 hover:text-grey-6': !selected\n }),\n checked: selected,\n checkedIcon: CheckboxCheckedFilledIcon,\n elementRef: checkboxRef,\n containerRef: checkboxContainerRef,\n onChange: toggleValue,\n onKeyDown: e => {\n if (e.key === 'ArrowLeft') {\n var _optionRef$current;\n e.preventDefault();\n (_optionRef$current = optionRef.current) === null || _optionRef$current === void 0 || _optionRef$current.focus();\n }\n }\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 224,\n columnNumber: 11\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 193,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 146,\n columnNumber: 5\n }, this);\n}\nSelectOption.displayName = 'Select.Option';\nfunction SelectMain({\n buttonContent,\n value,\n onChange,\n children,\n disabled,\n elementRef,\n buttonId,\n buttonClasses,\n popoverClasses,\n containerClasses,\n onPopoverScroll,\n alignListbox = 'left',\n multiple,\n listboxOverflow = 'truncate',\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n listboxAsPopover\n}) {\n const wrapperRef = useRef(null);\n const listboxRef = useRef(null);\n const [listboxOpen, setListboxOpen] = useState(false);\n const closeListbox = useCallback(() => setListboxOpen(false), [setListboxOpen]);\n const listboxId = useId();\n const buttonRef = useSyncedRef(elementRef);\n const defaultButtonId = useId();\n const selectValue = useCallback((value, options) => {\n onChange(value);\n if (options.closeListbox) {\n closeListbox();\n }\n }, [onChange, closeListbox]);\n\n // Close the listbox when focusing away\n useFocusAway(wrapperRef, closeListbox);\n\n // Vertical arrow key for options in the listbox\n useArrowKeyNavigation(listboxRef, {\n horizontal: false,\n loop: false,\n autofocus: true,\n containerVisible: listboxOpen,\n selector: '[role=\"option\"]:not([aria-disabled=\"true\"])'\n });\n return _jsxDEV(\"div\", {\n className: classnames('relative w-full border rounded', {\n 'border-grey-5': listboxOpen\n }, inputGroupStyles, containerClasses),\n ref: wrapperRef,\n children: [_jsxDEV(\"button\", {\n id: buttonId !== null && buttonId !== void 0 ? buttonId : defaultButtonId,\n className: classnames('focus-visible-ring transition-colors whitespace-nowrap', 'w-full flex items-center justify-between gap-x-2', 'bg-grey-0 disabled:bg-grey-1 disabled:text-grey-6',\n // Buttons are center-aligned by default. Overwrite it.\n 'text-left',\n // Add inherited rounded corners so that the toggle is consistent with\n // the wrapper, which is the element rendering borders.\n // Using overflow-hidden in the parent is not an option here, because\n // that would hide the listbox\n 'rounded-[inherit]', buttonClasses),\n type: \"button\",\n role: \"combobox\",\n disabled: disabled,\n \"aria-expanded\": listboxOpen,\n \"aria-haspopup\": \"listbox\",\n \"aria-controls\": listboxId,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n ref: downcastRef(buttonRef),\n onClick: () => setListboxOpen(prev => !prev),\n onKeyDown: e => {\n if (e.key === 'ArrowDown' && !listboxOpen) {\n e.preventDefault();\n setListboxOpen(true);\n }\n },\n \"data-testid\": \"select-toggle-button\",\n children: [_jsxDEV(\"div\", {\n className: \"pl-2 py-2 truncate grow\",\n children: buttonContent\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 429,\n columnNumber: 9\n }, this), _jsxDEV(\"div\", {\n className: \"pr-2 py-2 text-grey-6\",\n children: listboxOpen ? _jsxDEV(MenuCollapseIcon, {}, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 431,\n columnNumber: 26\n }, this) : _jsxDEV(MenuExpandIcon, {}, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 431,\n columnNumber: 49\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 430,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 396,\n columnNumber: 7\n }, this), _jsxDEV(SelectContext.Provider, {\n value: {\n // Explicit type casting needed here\n value: value,\n selectValue,\n multiple,\n listboxOverflow\n },\n children: _jsxDEV(Popover, {\n anchorElementRef: wrapperRef,\n open: listboxOpen,\n onClose: closeListbox,\n asNativePopover: listboxAsPopover,\n align: alignListbox,\n classes: popoverClasses,\n onScroll: onPopoverScroll,\n children: _jsxDEV(\"ul\", {\n role: \"listbox\",\n id: listboxId,\n ref: listboxRef,\n \"aria-multiselectable\": multiple,\n \"aria-labelledby\": buttonId !== null && buttonId !== void 0 ? buttonId : defaultButtonId,\n \"aria-orientation\": \"vertical\",\n children: children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 453,\n columnNumber: 11\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 444,\n columnNumber: 9\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 435,\n columnNumber: 7\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 387,\n columnNumber: 5\n }, this);\n}\nexport const Select = Object.assign(\n// eslint-disable-next-line prefer-arrow-callback\nfunction (props) {\n // Calling the function directly instead of returning a JSX element, to\n // avoid an unnecessary extra layer in the component tree\n // eslint-disable-next-line new-cap\n return SelectMain({\n ...props,\n multiple: false\n });\n}, {\n Option: SelectOption,\n displayName: 'Select'\n});\nexport const MultiSelect = Object.assign(\n// eslint-disable-next-line prefer-arrow-callback\nfunction (props) {\n // Calling the function directly instead of returning a JSX element, to\n // avoid an unnecessary extra layer in the component tree\n // eslint-disable-next-line new-cap\n return SelectMain({\n ...props,\n multiple: true\n });\n}, {\n Option: SelectOption,\n displayName: 'MultiSelect'\n});\n//# sourceMappingURL=Select.js.map","import { useEffect } from 'preact/hooks';\nimport { ListenerCollection } from '../util/listener-collection';\n/**\n * Listen on container for focusout events. If a focusout event's relatedTarget\n * is outside of the `container` element, invoke the `callback`.\n * Do not listen if not `enabled`.\n */\nexport function useFocusAway(container, callback, {\n enabled = true\n} = {}) {\n useEffect(() => {\n if (!enabled || !container.current) {\n return () => {};\n }\n const listeners = new ListenerCollection();\n listeners.add(container.current, 'focusout', e => {\n // Event type is not being properly inferred as FocusEvent\n const event = e;\n if (container.current && !container.current.contains(event.relatedTarget)) {\n callback(event);\n }\n });\n return () => {\n listeners.removeAll();\n };\n }, [container, enabled, callback]);\n}\n//# sourceMappingURL=use-focus-away.js.map","import { createContext } from 'preact';\nconst TableContext = createContext({});\nexport default TableContext;\n//# sourceMappingURL=TableContext.js.map","import { createContext } from 'preact';\nconst TableSectionContext = createContext(null);\nexport default TableSectionContext;\n//# sourceMappingURL=TableSectionContext.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/feedback/Callout.tsx\";\nimport classnames from 'classnames';\nimport { downcastRef } from '../../util/typing';\nimport { CautionIcon, CheckIcon, CancelIcon } from '../icons';\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Render a banner-like alert message with corresponding icon and coloring\n */\nexport default function Callout({\n children,\n classes,\n elementRef,\n icon: Icon,\n status = 'notice',\n size = 'md',\n variant = 'outlined',\n unstyled = false,\n ...htmlAttributes\n}) {\n const styled = !unstyled;\n const themed = styled && variant !== 'custom';\n const sized = styled && size !== 'custom';\n let StatusIcon = Icon;\n if (!StatusIcon) {\n switch (status) {\n case 'success':\n StatusIcon = CheckIcon;\n break;\n case 'error':\n StatusIcon = CancelIcon;\n break;\n default:\n StatusIcon = CautionIcon;\n break;\n }\n }\n\n // Only render an icon if no custom styling API props have been set.\n const withIcon = themed && sized;\n return _jsxDEV(\"div\", {\n \"data-component\": \"Callout\",\n ...htmlAttributes,\n ref: downcastRef(elementRef),\n className: classnames(styled && 'flex items-center border', themed && {\n 'rounded border': true,\n 'shadow hover:shadow-md cursor-pointer': variant === 'raised',\n 'border-yellow-notice': status === 'notice',\n 'border-green-success': status === 'success',\n 'border-red-error': status === 'error'\n },\n // Set background color, but only if rendering an icon\n themed && {\n 'bg-yellow-notice': status === 'notice' && withIcon,\n 'bg-green-success': status === 'success' && withIcon,\n 'bg-red-error': status === 'error' && withIcon,\n 'bg-white': !withIcon\n }, classes),\n children: [withIcon && _jsxDEV(\"div\", {\n className: classnames({\n 'p-2': size === 'md',\n 'p-1.5': size === 'sm',\n 'p-3': size === 'lg'\n }),\n children: _jsxDEV(StatusIcon, {\n \"data-testid\": \"callout-icon\",\n className: classnames('text-white', {\n 'w-[1.25em] h-[1.25em]': size === 'md',\n // default\n 'w-[0.85em] h-[0.85em]': size === 'sm',\n 'w-[1.5em] h-[1.5em]': size === 'lg'\n })\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 95,\n columnNumber: 11\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 88,\n columnNumber: 9\n }, this), _jsxDEV(\"div\", {\n className: classnames(sized && {\n 'p-2': size === 'md',\n // default\n 'py-1.5 px-2': size === 'sm',\n 'p-3': size === 'lg'\n }, styled && 'grow', themed && 'bg-white rounded-r'),\n children: children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 105,\n columnNumber: 7\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 64,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Callout.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/feedback/ToastMessages.tsx\";\nimport classnames from 'classnames';\nimport { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'preact/hooks';\nimport Callout from './Callout';\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * An individual toast message: a brief and transient success or error message.\n * The message may be dismissed by clicking on it. `visuallyHidden` toast\n * messages will not be visible but are still available to screen readers.\n */\nfunction ToastMessageItem({\n message,\n onDismiss\n}) {\n return _jsxDEV(Callout, {\n classes: classnames({\n 'sr-only': message.visuallyHidden\n }),\n status: message.type,\n onClick: () => onDismiss(message.id),\n variant: \"raised\",\n children: message.message\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 55,\n columnNumber: 5\n }, this);\n}\nconst ToastMessageTransition = ({\n direction,\n onTransitionEnd,\n children,\n transitionClasses = {}\n}) => {\n const isDismissed = direction === 'out';\n const containerRef = useRef(null);\n const handleAnimation = e => {\n // Ignore animations happening on child elements\n if (e.target !== containerRef.current) {\n return;\n }\n onTransitionEnd === null || onTransitionEnd === void 0 || onTransitionEnd(direction !== null && direction !== void 0 ? direction : 'in');\n };\n const classes = useMemo(() => {\n const {\n transitionIn = 'animate-fade-in',\n transitionOut = 'animate-fade-out'\n } = transitionClasses;\n return {\n [transitionIn]: !isDismissed,\n [transitionOut]: isDismissed\n };\n }, [isDismissed, transitionClasses]);\n return _jsxDEV(\"div\", {\n \"data-testid\": \"animation-container\",\n onAnimationEnd: handleAnimation,\n ref: containerRef,\n className: classnames('relative w-full container', classes),\n children: children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 103,\n columnNumber: 5\n }, this);\n};\n/**\n * A collection of toast messages. These are rendered within an `aria-live`\n * region for accessibility with screen readers.\n */\nexport default function ToastMessages({\n messages,\n onMessageDismiss,\n transitionClasses,\n /* istanbul ignore next - test seam */\n setTimeout_ = setTimeout\n}) {\n // List of IDs of toast messages that have been dismissed and have an\n // in-progress 'out' transition\n const [dismissedMessages, setDismissedMessages] = useState([]);\n // Tracks not finished timeouts for auto-dismiss toast messages\n const messageSchedules = useRef(new Map());\n const dismissMessage = useCallback(id => setDismissedMessages(ids => [...ids, id]), []);\n const scheduleMessageDismiss = useCallback(id => {\n const timeout = setTimeout_(() => {\n dismissMessage(id);\n messageSchedules.current.delete(id);\n }, 5000);\n messageSchedules.current.set(id, timeout);\n }, [dismissMessage, setTimeout_]);\n const onTransitionEnd = useCallback((direction, message) => {\n var _message$autoDismiss;\n const autoDismiss = (_message$autoDismiss = message.autoDismiss) !== null && _message$autoDismiss !== void 0 ? _message$autoDismiss : true;\n if (direction === 'in' && autoDismiss) {\n scheduleMessageDismiss(message.id);\n }\n if (direction === 'out') {\n onMessageDismiss(message.id);\n setDismissedMessages(ids => ids.filter(id => id !== message.id));\n }\n }, [scheduleMessageDismiss, onMessageDismiss]);\n useLayoutEffect(() => {\n // Clear all pending timeouts for not yet dismissed toast messages when the\n // component is unmounted\n const pendingTimeouts = messageSchedules.current;\n return () => {\n pendingTimeouts.forEach(timeout => clearTimeout(timeout));\n };\n }, []);\n return _jsxDEV(\"ul\", {\n \"aria-live\": \"polite\",\n \"aria-relevant\": \"additions\",\n className: \"w-full space-y-2\",\n \"data-component\": \"ToastMessages\",\n children: messages.map(message => {\n const isDismissed = dismissedMessages.includes(message.id);\n return _jsxDEV(\"li\", {\n className: classnames({\n // Add a bottom margin to visible messages only. Typically, we'd\n // use a `space-y-2` class on the parent to space children.\n // Doing that here could cause an undesired top margin on\n // the first visible message in a list that contains (only)\n // visually-hidden messages before it.\n // See https://tailwindcss.com/docs/space#limitations\n 'mb-2': !message.visuallyHidden\n }),\n children: _jsxDEV(ToastMessageTransition, {\n direction: isDismissed ? 'out' : 'in',\n onTransitionEnd: direction => onTransitionEnd(direction, message),\n transitionClasses: transitionClasses,\n children: _jsxDEV(ToastMessageItem, {\n message: message,\n onDismiss: dismissMessage\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 206,\n columnNumber: 15\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 201,\n columnNumber: 13\n }, this)\n }, message.id, false, {\n fileName: _jsxFileName,\n lineNumber: 189,\n columnNumber: 11\n }, this);\n })\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 180,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=ToastMessages.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/navigation/Link.tsx\";\nimport classnames from 'classnames';\nimport { downcastRef } from '../../util/typing';\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * Styled component for a link (`<a>` element).\n */\nexport default function Link({\n children,\n classes,\n elementRef,\n underline = 'none',\n unstyled = false,\n variant = 'brand',\n ...htmlAttributes\n}) {\n const styled = !unstyled;\n const themed = styled && variant !== 'custom';\n return _jsxDEV(\"a\", {\n \"data-component\": \"Link\",\n rel: \"noopener noreferrer\",\n ...htmlAttributes,\n className: classnames(styled && {\n 'focus-visible-ring rounded': true,\n // underline\n // TODO: Underline should be controlled by `variant` and should default\n // to `always`\n 'no-underline hover:no-underline': underline === 'none',\n // default\n 'underline hover:underline': underline === 'always',\n 'no-underline hover:underline': underline === 'hover'\n }, themed && {\n // color\n 'text-brand hover:text-brand-dark': variant === 'brand',\n // default\n 'text-color-text-light hover:text-brand': variant === 'text-light',\n 'text-color-text hover:text-brand-dark': variant === 'text'\n }, classes),\n ref: downcastRef(elementRef),\n children: children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 37,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Link.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/navigation/PointerButton.tsx\";\nimport classnames from 'classnames';\nimport { downcastRef } from '../../util/typing';\nimport Button from '../input/Button';\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n/**\n * A button for pointing toward a quantified set of items somewhere else in the\n * UI or off-screen. When clicked, the application should navigate to the\n * indicated or implied position.\n *\n * Used by the bucket bar in the client application to point at\n * highlights/annotations in the guest document. Expected button content is\n * numeric text, e.g.:\n *\n * <PointerButton direction=\"left\">5</PointerButton>\n *\n * The arrow-points are created by the combination of borders and positioning.\n * See https://css-tricks.com/snippets/css/css-triangle/\n */\nexport default function PointerButton({\n children,\n classes,\n elementRef,\n expanded,\n pressed,\n title,\n direction,\n ...htmlAndButtonProps\n}) {\n return _jsxDEV(Button, {\n \"data-component\": \"PointerButton\",\n ...htmlAndButtonProps,\n elementRef: downcastRef(elementRef),\n classes: classnames(\n // Establish relative positioning to allow absolute positioning of\n // ::before and ::after pseudo-elements (the arrow pointers)\n 'relative w-[26px] h-[16px]', 'flex items-center justify-center', 'bg-white rounded-[4px] border',\n // The borders of ::before and ::after will be used to style the arrow\n // pointer border (grey) and fill (white) respectively\n 'before:absolute before:border-transparent', 'after:absolute after:border-transparent', 'text-[10px] text-color-text-light leading-none font-semibold', {\n // This adds a 1-pixel x-offset to the default `shadow` (see tailwind\n // config)\n 'shadow-[1px_1px_1px_rgba(0,0,0,0.1)]': direction !== 'down',\n // The down arrow has no y-offset on its shadow to avoid odd edges\n // around its down-pointing wedge\n 'shadow-[1px_0px_1px_rgba(0,0,0,0.1)]': direction === 'down'\n },\n // Styling for left-pointing arrows\n {\n 'rounded-r-[4px] rounded-l-[2px]': direction === 'left',\n // Position the right edges of ::before and ::after to align with the\n // left edge of the button's body, centered vertically\n 'before:right-full before:top-1/2 after:right-full after:top-1/2': direction === 'left',\n // ::before is the grey border of the left-pointing wedge, 1px wider\n // than the fill\n 'before:mt-[-8px] before:border-8 before:border-r-[5px] before:border-r-grey-3': direction === 'left',\n // ::after is the white fill of the left-pointing wedge. NB: ordering\n // of these rules after the ::before rules is important for\n // compositing order\n 'after:mt-[-7px] after:border-[7px] after:border-r-[4px] after:border-r-white': direction === 'left'\n },\n // Styling for up-pointing arrows\n {\n 'z-1 rounded-t-px-sm rounded-b-px': direction === 'up',\n // Position the bottom edges of ::before and ::after to align with the\n // top edges of the button body. Center horizontally.\n 'before:top-auto before:left-1/2 before:bottom-full after:top-auto after:left-1/2 after:bottom-full': direction === 'up',\n // Grey border of up-pointing wedge\n 'before:ml-[-13px] before:border-[13px] before:border-b-[6px] before:border-b-grey-3': direction === 'up',\n // White fill of up-pointing wedge\n 'after:ml-[-12px] after:border-[12px] after:border-b-[5px] after:border-b-white': direction === 'up'\n },\n // Styling for down-pointing arrows\n {\n 'z-1 rounded-t-px rounded-b-px-sm': direction === 'down',\n // Position the top edges of ::before and ::after at the bottom of\n // the button body. Center horizontally.\n 'before:top-full before:left-1/2 after:top-full after:left-1/2': direction === 'down',\n // Grey border of down-pointing wedge\n 'before:ml-[-13px] before:border-[13px] before:border-t-[6px] before:border-t-grey-3': direction === 'down',\n // White fill of down-pointing wedge\n 'after:ml-[-12px] after:border-[12px] after:border-t-[5px] after:border-t-white': direction === 'down'\n }, classes),\n expanded: expanded,\n pressed: pressed,\n title: title,\n variant: \"custom\",\n size: \"custom\",\n children: children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 48,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=PointerButton.js.map","function byteToHex(val: number): string {\n const str = val.toString(16);\n return str.length === 1 ? '0' + str : str;\n}\n\n/**\n * Generate a random hex string of `len` chars.\n *\n * @param len - An even-numbered length string to generate.\n */\nexport function generateHexString(len: number): string {\n const bytes = new Uint8Array(len / 2);\n window.crypto.getRandomValues(bytes);\n return Array.from(bytes).map(byteToHex).join('');\n}\n","export type Frame = 'guest' | 'host' | 'notebook' | 'profile' | 'sidebar';\n\n/**\n * Message sent by `PortProvider` and `PortFinder` to establish a\n * MessageChannel-based connection between two frames.\n */\nexport type Message = {\n /** Role of the source frame. */\n frame1: Frame;\n\n /** Role of the target frame. */\n frame2: Frame;\n\n /**\n * Message type. \"request\" messages are sent by the source frame to the host\n * frame to request a connection. \"offer\" messages are sent from the host\n * frame back to the source frame and also to the target frame, accompanied by\n * a MessagePort.\n */\n type: 'offer' | 'request';\n\n /** If set on an `offer` message, indicates that the request was rejected. */\n error?: string;\n\n /**\n * ID of the request. Used to associate \"offer\" messages with their\n * corresponding \"request\" messages.\n */\n requestId: string;\n\n /**\n * Identifier for the source frame. This is useful in cases where multiple\n * source frames with a given role may connect to the same destination frame.\n */\n sourceId?: string;\n};\n\n/**\n * Return true if an object, eg. from the `data` field of a `MessageEvent`, is a\n * valid `Message`.\n */\nexport function isMessage(data: any): data is Message {\n if (data === null || typeof data !== 'object') {\n return false;\n }\n\n for (const property of ['frame1', 'frame2', 'type', 'requestId']) {\n if (typeof data[property] !== 'string') {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Return true if the data payload from a MessageEvent matches `message`.\n */\nexport function isMessageEqual(data: any, message: Partial<Message>) {\n if (!isMessage(data)) {\n return false;\n }\n\n // We assume `JSON.stringify` cannot throw because `isMessage` verifies that\n // all the fields we serialize here are serializable values.\n return (\n JSON.stringify(data, Object.keys(message).sort()) ===\n JSON.stringify(message, Object.keys(message).sort())\n );\n}\n\n/**\n * Check that source is of type Window.\n */\nexport function isSourceWindow(\n source: MessageEventSource | null,\n): source is Window {\n if (\n // `source` can be of type Window, MessagePort, ServiceWorker, or null.\n // `source instanceof Window` doesn't work in Chrome if `source` is a\n // cross-origin window.\n source === null ||\n source instanceof MessagePort ||\n (window.ServiceWorker && source instanceof ServiceWorker)\n ) {\n return false;\n }\n\n return true;\n}\n","import { ListenerCollection } from '@hypothesis/frontend-shared';\n\nimport type { Destroyable } from '../../types/annotator';\nimport { generateHexString } from '../random';\nimport { isMessage } from './port-util';\nimport type { Frame } from './port-util';\n\n/** Timeout for waiting for the host frame to respond to a port request. */\nexport const MAX_WAIT_FOR_PORT = 1000 * 20;\n\n/** Polling interval for requests to the host frame for a port. */\nexport const POLLING_INTERVAL_FOR_PORT = 250;\n\nexport type Options = {\n /** The role of this frame. */\n source: Exclude<Frame, 'host'>;\n\n /** Identifier for this frame. */\n sourceId?: string;\n\n /** The frame where the `PortProvider` is listening for messages. */\n hostFrame: Window;\n};\n\n/** Error thrown when a {@link PortFinder.discover} request fails. */\nexport class PortRequestError extends Error {\n constructor(message: string) {\n super(message);\n }\n}\n\n/**\n * PortFinder is used by non-host frames in the client to establish a\n * MessagePort-based connection to other frames. It is used together with\n * PortProvider which runs in the host frame. See PortProvider for an overview.\n */\nexport class PortFinder implements Destroyable {\n private _hostFrame: Window;\n private _listeners: ListenerCollection;\n private _source: Exclude<Frame, 'host'>;\n private _sourceId: string | undefined;\n\n constructor({ hostFrame, source, sourceId }: Options) {\n this._hostFrame = hostFrame;\n this._source = source;\n this._sourceId = sourceId;\n this._listeners = new ListenerCollection();\n }\n\n destroy() {\n this._listeners.removeAll();\n }\n\n /**\n * Request a specific port from the host frame\n *\n * @param target - the frame aiming to be discovered\n */\n async discover(target: Frame): Promise<MessagePort> {\n const requestId = generateHexString(6);\n\n return new Promise((resolve, reject) => {\n const postRequest = () => {\n this._hostFrame.postMessage(\n {\n frame1: this._source,\n frame2: target,\n type: 'request',\n requestId,\n sourceId: this._sourceId,\n },\n '*',\n );\n };\n\n // Because `guest` iframes load in parallel to the `host` frame, we can\n // not assume that the code in the `host` frame is executed before the\n // code in a `guest` frame. Hence, we can't assume that `PortProvider` (in\n // the `host` frame) is initialized before `PortFinder` (in the non-host\n // frames). Therefore, for the `PortFinder`, we implement a polling\n // strategy (sending a message every N milliseconds) until a response is\n // received.\n const intervalId = setInterval(postRequest, POLLING_INTERVAL_FOR_PORT);\n\n // The `host` frame maybe busy, that's why we should wait.\n const timeoutId = setTimeout(() => {\n clearInterval(intervalId);\n reject(\n new PortRequestError(\n `Unable to establish ${this._source}-${target} communication channel`,\n ),\n );\n }, MAX_WAIT_FOR_PORT);\n\n const listenerId = this._listeners.add(window, 'message', event => {\n const { data, ports } = event;\n\n // Ignore messages that are:\n //\n // - Not related to port discovery\n // - Not a response to the request we sent above. Note that the host\n // frame may be the same as the current window, since eg. the host\n // frame can also be a guest frame. Therefore we check `data.type` as\n // well to make sure this is a response.\n if (\n !isMessage(data) ||\n data.requestId !== requestId ||\n data.type === 'request'\n ) {\n return;\n }\n\n clearInterval(intervalId);\n clearTimeout(timeoutId);\n this._listeners.remove(listenerId);\n\n if (typeof data.error === 'string') {\n reject(new PortRequestError(data.error));\n } else if (ports.length > 0) {\n resolve(ports[0]);\n } else {\n reject(\n new PortRequestError(\n `${this._source}-${target} port request failed`,\n ),\n );\n }\n });\n\n postRequest();\n });\n }\n}\n","export type EventMap = Record<string, (...args: any) => void>;\n\n/**\n * A simple event emitter with an API similar to Node's `EventEmitter`.\n *\n * `Event` is an object type that maps event names to the function signatures of\n * subscribers for those events. For example, this defines an emitter which\n * emits one event, `uriChanged`, with a single string argument:\n *\n * ```\n * type Events = {\n * uriChanged(uri: string): void;\n * }\n * ```\n */\nexport class EventEmitter<out Event extends EventMap> {\n // Use a private field here to avoid conflicts with subclasses.\n #listeners: Array<{ name: string; callback: (...args: any) => void }> = [];\n\n /** Remove all event listeners. */\n destroy() {\n this.#listeners = [];\n }\n\n /** Add an event handler. */\n on<K extends keyof Event & string>(name: K, callback: Event[K]) {\n this.#listeners.push({ name, callback });\n }\n\n /** Remove an event handler. */\n off<K extends keyof Event>(name: K, callback: Event[K]) {\n this.#listeners = this.#listeners.filter(\n ln => !(ln.name === name && ln.callback === callback),\n );\n }\n\n /** Emit an event. */\n emit<K extends keyof Event>(name: K, ...args: Parameters<Event[K]>) {\n for (const listener of this.#listeners) {\n if (listener.name !== name) {\n continue;\n }\n // Ensure callback is invoked without `this`.\n const callback = listener.callback;\n callback(...args);\n }\n }\n}\n","let errorDestination: Window | null = null;\n\n/**\n * Wrap a callback with an error handler which forwards errors to another frame\n * using {@link sendError}.\n *\n * @param context - A short message indicating where the error happened.\n */\nexport function captureErrors<Result, Args = unknown>(\n callback: (...args: Args[]) => Result,\n context: string,\n): (...args: Args[]) => Result {\n return (...args) => {\n try {\n return callback(...args);\n } catch (err) {\n sendError(err, context);\n throw err;\n }\n };\n}\n\ntype ErrorData = {\n message: string;\n stack?: string;\n};\n\n/**\n * Return a cloneable representation of an Error.\n *\n * This is needed in browsers that don't support structured-cloning of Error\n * objects, or if the error is not cloneable for some reason.\n */\nfunction serializeError(err: Error | unknown): ErrorData {\n if (!(err instanceof Error)) {\n return { message: String(err), stack: undefined };\n }\n\n return {\n message: err.message,\n stack: err.stack,\n };\n}\n\n/**\n * Convert error data serialized by {@link serializeError} back into an Error.\n */\nfunction deserializeError(data: ErrorData): Error {\n const err = new Error(data.message);\n err.stack = data.stack;\n return err;\n}\n\n/**\n * Forward an error to the frame registered with {@link sendErrorsTo}.\n *\n * Errors are delivered on a best-effort basis. If no error handling frame has\n * been registered or the frame is still loading, the error will not be received.\n *\n * Ideally we would use a more robust delivery system which can queue messages\n * until they can be processed (eg. using MessagePort). We use `window.postMessage`\n * for the moment because we are trying to rule out problems with\n * MessageChannel/MessagePort when setting up sidebar <-> host communication.\n *\n * @param context - A short message indicating where the error happened.\n */\nexport function sendError(error: unknown, context: string) {\n if (!errorDestination) {\n return;\n }\n\n const data = {\n type: 'hypothesis-error',\n error: error instanceof Error ? error : serializeError(error),\n context,\n };\n\n try {\n // Try to send the error. If this fails because the browser doesn't support\n // structured cloning of errors, use a fallback.\n try {\n errorDestination.postMessage(data, '*');\n } catch (postErr) {\n if (\n postErr instanceof DOMException &&\n postErr.name === 'DataCloneError'\n ) {\n data.error = serializeError(data.error);\n errorDestination.postMessage(data, '*');\n } else {\n throw postErr;\n }\n }\n } catch (sendErr) {\n console.warn('Unable to report Hypothesis error', sendErr);\n }\n}\n\n/**\n * Register a handler for errors sent to the current frame using {@link sendError}\n *\n * @return A function that unregisters the handler\n */\nexport function handleErrorsInFrames(\n callback: (error: unknown, context: string) => void,\n): () => void {\n const handleMessage = (event: MessageEvent) => {\n const { data } = event;\n if (data && data?.type === 'hypothesis-error') {\n const { context, error } = data;\n callback(\n error instanceof Error ? error : deserializeError(error),\n context,\n );\n }\n };\n window.addEventListener('message', handleMessage);\n return () => window.removeEventListener('message', handleMessage);\n}\n\n/**\n * Register a destination frame that {@link sendError} should submit errors to.\n */\nexport function sendErrorsTo(destination: Window | null) {\n errorDestination = destination;\n}\n","import { ListenerCollection } from '@hypothesis/frontend-shared';\n\nimport { EventEmitter } from '../../shared/event-emitter';\nimport type { Destroyable } from '../../types/annotator';\nimport { captureErrors, sendError } from '../frame-error-capture';\nimport { isMessage, isMessageEqual, isSourceWindow } from './port-util';\nimport type { Message } from './port-util';\n\ntype Channel =\n | 'guest-host'\n | 'guest-sidebar'\n | 'notebook-sidebar'\n | 'sidebar-host';\n\n/**\n * PortProvider creates a `MessageChannel` for communication between two\n * frames.\n *\n * There are 4 types of frames:\n * - `host`: frame where the Hypothesis client is initially loaded.\n * - `guest`: frames with annotatable content. In some instances a `guest`\n * frame can be the same as the `host` frame, in other cases, it is an iframe\n * where either (1) the hypothesis client has been injected or (2) the\n * hypothesis client has been configured to act exclusively as a `guest` (not\n * showing the sidebar).\n * - `notebook` and `profile`: front-end app views that each have their own\n * iframe.\n * - `sidebar`: the main hypothesis front-end app. It runs in an iframe on a\n * different origin than the host and is responsible for the communication\n * with the backend (fetching and saving annotations).\n *\n * This layout represents the current arrangement of frames:\n *\n * `host` frame\n * |-> `sidebar` iframe\n * |-> `notebook` iframe\n * |-> `profile` iframe\n * |-> [`guest` iframes]\n *\n * Currently, we support communication between the following pairs of frames:\n * - `guest-host`\n * - `guest-sidebar`\n * - `notebook-sidebar`\n * - `sidebar-host`\n *\n * `PortProvider` is used only in the `host` frame. The other frames use the\n * companion class, `PortFinder`. `PortProvider` creates a `MessageChannel`\n * for two frames to communicate with each other. It also listens to requests for\n * particular `MessagePort` and dispatches the corresponding `MessagePort`.\n *\n *\n * PortFinder (non-host frame) | PortProvider (host frame)\n * -----------------------------------------------------------------------------------------------\n * 1. Request `MessagePort` via `window.postMessage` ---> 2. Receive request and create port pair\n * |\n * V\n * 4. Receive requested port <---------------------- 3. Send first port to requesting frame\n * 5. Send second port to other frame\n * (eg. via MessageChannel connection\n * between host and other frame)\n */\nexport class PortProvider implements Destroyable {\n private _allowedMessages: Array<Partial<Message> & { allowedOrigin: string }>;\n private _emitter: EventEmitter<{\n [event: string]: (...args: any) => void;\n }>;\n private _hypothesisAppsOrigin: string;\n\n /**\n * IDs of port requests that have been handled.\n *\n * This is used to avoid responding to the same request multiple times.\n * Guest frames in particular may send duplicate requests (see comments in\n * PortFinder).\n */\n private _handledRequests: Set<string>;\n\n private _listeners: ListenerCollection;\n\n // Message channels for connections from Hypothesis apps to the host frame.\n private _sidebarHostChannel: MessageChannel;\n private _sidebarConnected: boolean;\n\n /**\n * Begin listening to port requests from other frames.\n *\n * @param hypothesisAppsOrigin - Origin of the Hypothesis apps (sidebar,\n * notebook). Used to verify requests for ports.\n */\n constructor(hypothesisAppsOrigin: string) {\n this._hypothesisAppsOrigin = hypothesisAppsOrigin;\n this._emitter = new EventEmitter();\n\n this._handledRequests = new Set();\n\n // Create the `sidebar-host` channel immediately, while other channels are\n // created on demand\n this._sidebarHostChannel = new MessageChannel();\n this._sidebarConnected = false;\n\n this._listeners = new ListenerCollection();\n\n this._allowedMessages = [\n {\n allowedOrigin: '*',\n frame1: 'guest',\n frame2: 'host',\n type: 'request',\n },\n {\n allowedOrigin: '*',\n frame1: 'guest',\n frame2: 'sidebar',\n type: 'request',\n },\n {\n allowedOrigin: this._hypothesisAppsOrigin,\n frame1: 'sidebar',\n frame2: 'host',\n type: 'request',\n },\n {\n allowedOrigin: this._hypothesisAppsOrigin,\n frame1: 'notebook',\n frame2: 'sidebar',\n type: 'request',\n },\n ];\n\n this._listen();\n }\n\n private _listen() {\n const errorContext = 'Handling port request';\n const sentErrors = new Set<string>();\n\n const reportError = (message: string) => {\n if (sentErrors.has(message)) {\n // PortFinder will send requests repeatedly until it gets a response or\n // a timeout is reached.\n //\n // Only send errors once to avoid spamming Sentry.\n return;\n }\n sentErrors.add(message);\n sendError(new Error(message), errorContext);\n };\n\n const handleRequest = (event: MessageEvent) => {\n const { data, origin, source } = event;\n\n // Ignore messages where the sender went away before we can send a response.\n if (!source) {\n return;\n }\n\n // Ignore messages that don't look like port requests.\n if (!isMessage(data) || data.type !== 'request') {\n return;\n }\n\n const { frame1, frame2, requestId, sourceId } = data;\n const channel = `${frame1}-${frame2}` as Channel;\n\n if (!isSourceWindow(source)) {\n reportError(\n `Ignored port request for channel ${channel} from non-Window source`,\n );\n return;\n }\n\n const match = this._allowedMessages.find(\n ({ allowedOrigin, ...allowedMessage }) =>\n this._messageMatches({\n allowedMessage,\n allowedOrigin,\n data,\n origin,\n }),\n );\n\n if (match === undefined) {\n reportError(\n `Ignored invalid port request for channel ${channel} from ${origin}`,\n );\n return;\n }\n\n if (this._handledRequests.has(requestId)) {\n return;\n }\n this._handledRequests.add(requestId);\n\n // If the source window has an opaque origin [1], `event.origin` will be\n // the string \"null\". This is not a legal value for the `targetOrigin`\n // parameter to `postMessage`, so remap it to \"*\".\n //\n // [1] https://html.spec.whatwg.org/multipage/origin.html#origin.\n // Documents with opaque origins include file:// URLs and\n // sandboxed iframes.\n const targetOrigin = origin === 'null' ? '*' : origin;\n\n // Create the channel for these two frames to communicate and send the\n // corresponding ports to them.\n const messageChannel =\n channel === 'sidebar-host'\n ? this._sidebarHostChannel\n : new MessageChannel();\n\n // The message that is sent to the target frame that the source wants to\n // connect to, as well as the source frame requesting the connection.\n // Each message is accompanied by a port for the appropriate end of the\n // connection.\n const message: Message = {\n frame1,\n frame2,\n type: 'offer',\n requestId,\n sourceId,\n };\n\n // The sidebar can only connect once. It might try to connect a second\n // time if something causes the iframe to reload. We can't recover from\n // this yet. Instead we just log a warning here. The port discovery\n // protocol doesn't have a way to return errors, so the sidebar will only\n // learn about this when it times out waiting for a response.\n if (messageChannel === this._sidebarHostChannel) {\n if (this._sidebarConnected) {\n console.warn(\n 'Ignoring second request from Hypothesis sidebar to connect to host frame',\n );\n message.error = 'Received duplicate port request';\n source.postMessage(message, targetOrigin);\n return;\n }\n this._sidebarConnected = true;\n }\n\n source.postMessage(message, targetOrigin, [messageChannel.port1]);\n\n if (frame2 === 'sidebar') {\n this._sidebarHostChannel.port2.postMessage(message, [\n messageChannel.port2,\n ]);\n } else if (frame2 === 'host') {\n this._emitter.emit('frameConnected', frame1, messageChannel.port2);\n }\n };\n\n this._listeners.add(\n window,\n 'message',\n captureErrors(handleRequest, errorContext),\n );\n }\n\n private _messageMatches({\n allowedMessage,\n allowedOrigin,\n data,\n origin,\n }: {\n /** Fields that the message must match. */\n allowedMessage: Partial<Message>;\n\n /** Origin must match this value. Accepts `*` wildcard. */\n allowedOrigin: string;\n\n /** Data to be compared with `allowedMessage` */\n data: unknown;\n\n /** Origin to be compared with `allowedOrigin`. */\n origin: string;\n }) {\n if (allowedOrigin !== '*' && origin !== allowedOrigin) {\n return false;\n }\n\n return isMessageEqual(data, allowedMessage);\n }\n\n on(\n eventName: 'frameConnected',\n handler: (source: 'guest' | 'sidebar', port: MessagePort) => void,\n ) {\n this._emitter.on(eventName, handler);\n }\n\n destroy() {\n this._listeners.removeAll();\n }\n}\n","import { ListenerCollection } from '@hypothesis/frontend-shared';\n\nimport type { Destroyable } from '../../types/annotator';\n\n/*\n This module was adapted from `index.js` in https://github.com/substack/frame-rpc.\n\n This software is released under the MIT license:\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in\n all copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n */\n\nconst VERSION = '1.0.0';\nconst PROTOCOL = 'frame-rpc';\n\n/**\n * Format of messages sent between frames.\n *\n * See https://github.com/substack/frame-rpc#protocol\n */\ntype RequestMessage = {\n arguments: unknown[];\n method: string;\n protocol: typeof PROTOCOL;\n sequence: number;\n version: typeof VERSION;\n};\n\ntype ResponseMessage = {\n arguments: unknown[];\n protocol: typeof PROTOCOL;\n response: number;\n version: typeof VERSION;\n};\n\ntype Message = RequestMessage | ResponseMessage;\n\nfunction makeRequestMessage(\n method: string,\n args: unknown[] = [],\n sequence = -1,\n): RequestMessage {\n return {\n protocol: PROTOCOL,\n version: VERSION,\n arguments: args,\n method,\n sequence,\n };\n}\n\n/**\n * Send a PortRPC method call.\n *\n * @param [sequence] - Sequence number used for replies\n */\nfunction sendCall(\n port: MessagePort,\n method: string,\n args: unknown[] = [],\n sequence = -1,\n) {\n port.postMessage(makeRequestMessage(method, args, sequence));\n}\n\n/**\n * Install a message listener that ensures proper delivery of \"close\" notifications\n * from {@link PortRPC}s in Safari <= 15.\n *\n * This must be called in the _parent_ frame of the frame that owns the port.\n * In Hypothesis this means for example that the workaround would be installed\n * in the host frame to ensure delivery of \"close\" notifications from \"guest\"\n * frames.\n *\n * @param [userAgent] - Test seam\n * @return Callback that removes the listener\n */\nexport function installPortCloseWorkaroundForSafari(\n /* istanbul ignore next */\n userAgent = navigator.userAgent,\n): () => void {\n if (!shouldUseSafariWorkaround(userAgent)) {\n return () => {};\n }\n\n const handler = (event: MessageEvent) => {\n if (event.data?.type === 'hypothesisPortClosed' && event.ports[0]) {\n const port = event.ports[0];\n sendCall(port, 'close');\n port.close();\n }\n };\n\n window.addEventListener('message', handler);\n return () => window.removeEventListener('message', handler);\n}\n\n/**\n * Test whether this browser needs the workaround for https://bugs.webkit.org/show_bug.cgi?id=231167.\n */\nfunction shouldUseSafariWorkaround(userAgent: string) {\n // Test whether this is a WebKit-based browser.\n const webkitVersionMatch = userAgent.match(/\\bAppleWebKit\\/([0-9]+)\\b/);\n if (!webkitVersionMatch) {\n return false;\n }\n const webkitVersion = parseInt(webkitVersionMatch[1]);\n\n // Chrome's user agent contains the token \"AppleWebKit/537.36\", where the\n // version number is frozen. This corresponds to a version of Safari from 2013,\n // which is older than all supported Safari versions.\n if (webkitVersion <= 537) {\n return false;\n }\n\n // The bug was fixed in Safari 16, which according to\n // https://github.com/mdn/browser-compat-data/blob/main/browsers/safari.json\n // corresponds to WebKit 614.* and later. However, the WebKit version was\n // frozen before then, so we instead need to check the `Version` token.\n // This identifies the _browser_, not the engine.\n const versionMatch = userAgent.match(/\\bVersion\\/([0-9]+)\\b/);\n if (!versionMatch) {\n // If no version info, we guess that the browser is a new version of\n // WebKit which is not affected.\n return false;\n }\n\n const version = parseInt(versionMatch[1]);\n\n // The bug was fixed in Safari 16.\n if (version >= 16) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Callback type used for RPC method handlers and result callbacks.\n */\ntype Callback = (...args: unknown[]) => void;\n\nfunction isCallback(value: any): value is Callback {\n return typeof value === 'function';\n}\n\nexport type CallMap = Record<string, (...args: any) => void>;\nexport type HandlerMap = Record<string, (...args: any) => void>;\n\n/**\n * PortRPC provides remote procedure calls between frames or workers. It uses\n * the Channel Messaging API [1] as a transport.\n *\n * To communicate between two frames with this class, construct a PortRPC\n * instance in each and register method handlers with {@link on}. Create a\n * {@link MessageChannel} and send one of its two ports to each frame. Then call\n * {@link connect} to make the PortRPC instance in each frame use the corresponding\n * port.\n *\n * In addition to the custom methods that a PortRPC handles, there are several\n * built-in handlers which are invoked automatically:\n *\n * - \"connect\" is sent when a PortRPC connects to a port. This event can\n * be used to confirm that the sending frame has received the port and will\n * send a \"close\" event when it goes away.\n * - \"close\" is sent when a PortRPC is destroyed or the owning frame is\n * unloaded. This event may not be dispatched if the guest frame terminates\n * abnormally (eg. due to an OS process crash).\n *\n * [1] https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API\n *\n * @template Handlers - Object type describing the calls handled by this port,\n * using {@link PortRPC.on}.\n * @template Calls - Object type describing the calls made by this port,\n * using {@link PortRPC.prototype.call}.\n */\nexport class PortRPC<Handlers extends HandlerMap, Calls extends CallMap>\n implements Destroyable\n{\n /**\n * Map of sequence number to response callback, for RPC calls sent from\n * this instance.\n */\n private _callbacks: Map<number, Callback>;\n\n private _destroyed: boolean;\n private _listeners: ListenerCollection;\n\n /** Map of method name to handler for RPC calls received by this instance. */\n private _methods: Map<keyof Handlers, Callback>;\n\n /** The underlying communication channel. */\n private _port: MessagePort | null;\n\n /** Sequence number for next call. */\n private _sequence: number;\n\n /**\n * Method and arguments of pending RPC calls made before a port was connected.\n */\n private _pendingCalls: Array<[keyof Calls, unknown[]]>;\n\n private _receivedCloseEvent: boolean;\n\n constructor({\n userAgent = navigator.userAgent,\n currentWindow = window,\n forceUnloadListener = false,\n }: {\n userAgent?: string;\n currentWindow?: Window;\n\n // Test seam. Force the use of a Window \"unload\" listener even if the\n // browser supports \"close\" events for MessagePort.\n forceUnloadListener?: boolean;\n } = {}) {\n this._port = null;\n this._methods = new Map();\n this._sequence = 1;\n this._callbacks = new Map();\n\n this._listeners = new ListenerCollection();\n\n // In browsers that emit a \"close\" event when the other end of a MessagePort\n // goes away, we can listen for that directly. In other browsers, we have to\n // send the \"close\" event through the message channel when the window\n // containing the sending port is unloaded.\n if (!('onclose' in MessagePort.prototype) || forceUnloadListener) {\n this._listeners.add(currentWindow, 'unload', event => {\n // Ignore custom events which use the same name. This works around an\n // issue in VitalSource.\n //\n // See https://github.com/hypothesis/support/issues/161#issuecomment-2454560641.\n if (event instanceof CustomEvent) {\n return;\n }\n\n if (this._port) {\n // Send \"close\" notification directly. This works in Chrome, Firefox and\n // Safari >= 16.\n sendCall(this._port, 'close');\n\n // To work around a bug in Safari <= 15 which prevents sending messages\n // while a window is unloading, try transferring the port to the parent frame\n // and re-sending the \"close\" event from there.\n if (\n currentWindow !== currentWindow.parent &&\n shouldUseSafariWorkaround(userAgent)\n ) {\n currentWindow.parent.postMessage(\n { type: 'hypothesisPortClosed' },\n '*',\n [this._port],\n );\n }\n }\n });\n }\n\n this._pendingCalls = [];\n\n this._destroyed = false;\n this._receivedCloseEvent = false;\n }\n\n /**\n * Register a method handler for incoming RPC requests.\n *\n * The arguments to the handler will be the arguments passed to {@link call}\n * plus a final callback arg that can be used to return results to the caller.\n *\n * This can also be used to register a handler for the built-in \"connect\"\n * and \"close\" events.\n *\n * All handlers must be registered before {@link connect} is invoked.\n */\n on<M extends keyof Handlers>(method: M, handler: Handlers[M]) {\n if (this._port) {\n throw new Error('Cannot add a method handler after a port is connected');\n }\n this._methods.set(method, handler);\n }\n\n /**\n * Connect to a MessagePort and process any queued RPC requests.\n */\n connect(port: MessagePort) {\n this._port = port;\n this._listeners.add(port, 'message', event => this._handle(event));\n\n // For browsers that support a `close` event for MessagePort, we use that\n // to identify when the other end disconnects. This is translated into a\n // message event that is similar to what we receive in older browsers\n // which use a Window unload handler instead.\n this._listeners.add(port, 'close', () => {\n port.dispatchEvent(\n new MessageEvent('message', {\n data: makeRequestMessage('close'),\n }),\n );\n });\n\n port.start();\n sendCall(port, 'connect');\n\n for (const [method, args] of this._pendingCalls) {\n this.call(method, ...(args as Parameters<Calls[keyof Calls]>));\n }\n this._pendingCalls = [];\n }\n\n /**\n * Disconnect the RPC channel and close the MessagePort.\n */\n destroy() {\n if (this._port) {\n sendCall(this._port, 'close');\n this._port.close();\n }\n this._destroyed = true;\n this._listeners.removeAll();\n }\n\n /**\n * Send an RPC request via the connected port.\n *\n * If this client is not yet connected to a port, the call will be queued and\n * sent when {@link connect} is called.\n *\n * If the final argument in `args` is a function, it is treated as a callback\n * which is invoked with the response in the form of (error, result) arguments.\n */\n call<M extends keyof Calls>(method: M, ...args: Parameters<Calls[M]>) {\n if (!this._port) {\n this._pendingCalls.push([method, args]);\n }\n\n if (!this._port || this._destroyed) {\n return;\n }\n\n const seq = this._sequence++;\n const finalArg = args[args.length - 1];\n if (isCallback(finalArg)) {\n this._callbacks.set(seq, finalArg);\n args = args.slice(0, -1) as Parameters<Calls[M]>;\n }\n\n sendCall(this._port, method as string, args, seq);\n }\n\n /**\n * Validate message\n */\n private _parseMessage({ data }: MessageEvent): Message | null {\n if (!data || typeof data !== 'object') {\n return null;\n }\n if (data.protocol !== PROTOCOL) {\n return null;\n }\n if (data.version !== VERSION) {\n return null;\n }\n if (!Array.isArray(data.arguments)) {\n return null;\n }\n\n return data;\n }\n\n private _handle(event: MessageEvent) {\n const msg = this._parseMessage(event);\n const port = this._port;\n\n if (!msg || !port) {\n return;\n }\n\n if ('method' in msg) {\n // Only allow close event to fire once.\n if (msg.method === 'close') {\n if (this._receivedCloseEvent) {\n return;\n } else {\n this._receivedCloseEvent = true;\n }\n }\n\n const handler = this._methods.get(msg.method);\n if (!handler) {\n return;\n }\n\n const callback = (...args: unknown[]) => {\n const message: ResponseMessage = {\n arguments: args,\n protocol: PROTOCOL,\n response: msg.sequence,\n version: VERSION,\n };\n\n port.postMessage(message);\n };\n handler(...msg.arguments, callback);\n } else if ('response' in msg) {\n const cb = this._callbacks.get(msg.response);\n this._callbacks.delete(msg.response);\n if (cb) {\n cb(...msg.arguments);\n }\n }\n }\n}\n","/**\n * Type conversion methods that coerce incoming configuration values to an\n * expected type or format that other parts of the UI may make assumptions\n * on. This is needed for incoming configuration values that are otherwise\n * not sanitized.\n *\n * Note that if the values passed are plain javascript values (such as ones\n * produced from JSON.parse), then these methods do not throw errors.\n */\nimport type { Ref } from 'preact';\n\nexport function toBoolean(value: any): boolean {\n if (typeof value === 'string') {\n if (value.trim().toLocaleLowerCase() === 'false') {\n // \"false\", \"False\", \" false\", \"FALSE\" all return false\n return false;\n }\n }\n const numericalVal = Number(value);\n if (!isNaN(numericalVal)) {\n return Boolean(numericalVal);\n }\n // Any non-numerical or falsely string should return true, otherwise return false\n return typeof value === 'string';\n}\n\n/**\n * Returns either an integer or NaN\n */\nexport function toInteger(value: any): number {\n // Acts as a simple wrapper\n return parseInt(value);\n}\n\n/**\n * Returns either the value if it's an object or an empty object\n */\nexport function toObject(value: any): object {\n if (typeof value === 'object' && value !== null) {\n return value;\n }\n // Don't attempt to fix the values, just ensure type correctness\n return {};\n}\n\n/**\n * Returns the value as a string or an empty string if the\n * value undefined, null or otherwise falsely.\n */\nexport function toString(value: any): string {\n if (value && typeof value.toString === 'function') {\n return value.toString();\n }\n return '';\n}\n\n/**\n * Helper for downcasting a ref to a more specific type, where that is safe\n * to do.\n *\n * This is mainly useful to cast a generic `Ref<HTMLElement>` to a more specific\n * element type (eg. `Ref<HTMLDivElement>`) for use with the `ref` prop of a JSX element.\n * Since Preact only writes to the `ref` prop, such a cast is safe.\n *\n * @fixme Ignoring from code coverage, as this function is currently not used\n */\n/* istanbul ignore next */\nexport function downcastRef<T, U>(ref: Ref<T> | undefined): Ref<U> | undefined {\n return ref as Ref<U> | undefined;\n}\n","/**\n * Return a parsed `js-hypothesis-config` object from the document, or `{}`.\n *\n * Find all `<script class=\"js-hypothesis-config\">` tags in the given document,\n * parse them as JSON, and return the parsed object.\n *\n * If there are no `js-hypothesis-config` tags in the document then return\n * `{}`.\n *\n * If there are multiple `js-hypothesis-config` tags in the document then merge\n * them into a single returned object (when multiple scripts contain the same\n * setting names, scripts further down in the document override those further\n * up).\n *\n * @param document - The root element to search.\n */\nexport function parseJsonConfig(document: Document | Element) {\n const config: Record<string, unknown> = {};\n const settingsElements = document.querySelectorAll(\n 'script.js-hypothesis-config',\n );\n\n for (let i = 0; i < settingsElements.length; i++) {\n let settings;\n try {\n settings = JSON.parse(settingsElements[i].textContent || '');\n } catch (err) {\n console.warn(\n 'Could not parse settings from js-hypothesis-config tags',\n err,\n );\n settings = {};\n }\n Object.assign(config, settings);\n }\n\n return config;\n}\n","/**\n * Polyfill for `Object.hasOwn`.\n *\n * `hasOwn(someObject, property)` should be used instead of\n * `someObject.hasOwnProperty(name)`.\n */\nexport function hasOwn(object: object, property: string): boolean {\n return Object.prototype.hasOwnProperty.call(object, property);\n}\n","/**\n * Return the intersection of two rects.\n */\nexport function intersectRects(rectA: DOMRect, rectB: DOMRect) {\n const left = Math.max(rectA.left, rectB.left);\n const right = Math.min(rectA.right, rectB.right);\n const top = Math.max(rectA.top, rectB.top);\n const bottom = Math.min(rectA.bottom, rectB.bottom);\n return new DOMRect(left, top, right - left, bottom - top);\n}\n\n/**\n * Return `true` if a rect is _empty_.\n *\n * An empty rect is defined as one with zero or negative width/height, eg.\n * as returned by `new DOMRect()` or `Element.getBoundingClientRect()` for a\n * hidden element.\n */\nexport function rectIsEmpty(rect: DOMRect) {\n return rect.width <= 0 || rect.height <= 0;\n}\n\n/**\n * Return true if the 1D lines a-b and c-d overlap (ie. the length of their\n * intersection is non-zero).\n *\n * For example, the following lines overlap:\n *\n * a----b\n * c------d\n *\n * The inputs must be normalized such that b >= a and d >= c.\n */\nfunction linesOverlap(a: number, b: number, c: number, d: number) {\n const maxStart = Math.max(a, c);\n const minEnd = Math.min(b, d);\n return maxStart < minEnd;\n}\n\n/**\n * Return true if the intersection of `rectB` and `rectA` is non-empty.\n */\nexport function rectIntersects(rectA: DOMRect, rectB: DOMRect) {\n if (rectIsEmpty(rectA) || rectIsEmpty(rectB)) {\n return false;\n }\n\n return (\n linesOverlap(rectA.left, rectA.right, rectB.left, rectB.right) &&\n linesOverlap(rectA.top, rectA.bottom, rectB.top, rectB.bottom)\n );\n}\n\n/**\n * Return true if `rectB` is fully contained within `rectA`\n */\nexport function rectContains(rectA: DOMRect, rectB: DOMRect) {\n if (rectIsEmpty(rectA) || rectIsEmpty(rectB)) {\n return false;\n }\n\n return (\n rectB.left >= rectA.left &&\n rectB.right <= rectA.right &&\n rectB.top >= rectA.top &&\n rectB.bottom <= rectA.bottom\n );\n}\n\n/**\n * Return true if two rects overlap vertically.\n */\nexport function rectsOverlapVertically(a: DOMRect, b: DOMRect) {\n return linesOverlap(a.top, a.bottom, b.top, b.bottom);\n}\n\n/**\n * Return true if two rects overlap horizontally.\n */\nexport function rectsOverlapHorizontally(a: DOMRect, b: DOMRect) {\n return linesOverlap(a.left, a.right, b.left, b.right);\n}\n\n/**\n * Return the union of two rects.\n *\n * The union of an empty rect (see {@link rectIsEmpty}) with a non-empty rect is\n * defined to be the non-empty rect. The union of two empty rects is an empty\n * rect.\n */\nexport function unionRects(a: DOMRect, b: DOMRect) {\n if (rectIsEmpty(a)) {\n return b;\n } else if (rectIsEmpty(b)) {\n return a;\n }\n\n const left = Math.min(a.left, b.left);\n const top = Math.min(a.top, b.top);\n const right = Math.max(a.right, b.right);\n const bottom = Math.max(a.bottom, b.bottom);\n\n return new DOMRect(left, top, right - left, bottom - top);\n}\n\n/**\n * Return the point at the center of a rect.\n */\nexport function rectCenter(rect: DOMRect) {\n return new DOMPoint(\n (rect.left + rect.right) / 2,\n (rect.top + rect.bottom) / 2,\n );\n}\n","export function nodeIsElement(node: Node): node is Element {\n return node.nodeType === Node.ELEMENT_NODE;\n}\n\nexport function nodeIsText(node: Node): node is Text {\n return node.nodeType === Node.TEXT_NODE;\n}\n","import type { SideBySideMode } from '../../types/annotator';\nimport { rectContains, rectIntersects } from '../util/geometry';\nimport { nodeIsElement, nodeIsText } from '../util/node';\n\n/**\n * CSS selectors used to find elements that are considered potentially part\n * of the main content of a page.\n */\nconst contentSelectors = [\n 'p',\n\n // Paragraphs in VitalSource \"Great Book\" format ebooks.\n '.para',\n];\n\n/**\n * Attempt to guess the region of the page that contains the main content.\n *\n * @return The left/right content margins or `null` if they could not be determined\n */\nexport function guessMainContentArea(\n root: Element,\n): { left: number; right: number } | null {\n // Maps of (margin X coord, votes) for margin positions.\n const leftMarginVotes = new Map<number, number>();\n const rightMarginVotes = new Map<number, number>();\n\n // Gather data about the paragraphs of text in the document.\n //\n // In future we might want to expand this to consider other text containers,\n // since some pages, especially eg. in ebooks, may not have any paragraphs\n // (eg. instead they may only contain tables or lists or headings).\n const contentSelector = contentSelectors.join(',');\n const paragraphs = Array.from(root.querySelectorAll(contentSelector))\n .map(p => {\n // Gather some data about them.\n const rect = p.getBoundingClientRect();\n const textLength = p.textContent!.length;\n return { rect, textLength };\n })\n .filter(({ rect }) => {\n // Filter out hidden paragraphs\n return rect.width > 0 && rect.height > 0;\n })\n // Select the paragraphs containing the most text.\n .sort((a, b) => b.textLength - a.textLength)\n .slice(0, 15);\n\n // Let these paragraphs \"vote\" for what the left and right margins of the\n // main content area in the document are.\n paragraphs.forEach(({ rect }) => {\n let leftVotes = leftMarginVotes.get(rect.left) ?? 0;\n leftVotes += 1;\n leftMarginVotes.set(rect.left, leftVotes);\n\n let rightVotes = rightMarginVotes.get(rect.right) ?? 0;\n rightVotes += 1;\n rightMarginVotes.set(rect.right, rightVotes);\n });\n\n // Find the margin values with the most votes.\n if (leftMarginVotes.size === 0 || rightMarginVotes.size === 0) {\n return null;\n }\n\n const leftMargin = [...leftMarginVotes.entries()].sort((a, b) => b[1] - a[1]);\n const rightMargin = [...rightMarginVotes.entries()].sort(\n (a, b) => b[1] - a[1],\n );\n\n const [leftPos] = leftMargin[0];\n const [rightPos] = rightMargin[0];\n\n return { left: leftPos, right: rightPos };\n}\n\nlet textRectRange: Range;\n\n/**\n * Return the viewport-relative rect occupied by part of a text node.\n */\nfunction textRect(text: Text, start = 0, end: number = text.data.length) {\n if (!textRectRange) {\n // Allocate a range only on the first call to avoid the overhead of\n // constructing and maintaining a large number of live ranges.\n textRectRange = document.createRange();\n }\n textRectRange.setStart(text, start);\n textRectRange.setEnd(text, end);\n return textRectRange.getBoundingClientRect();\n}\n\nfunction hasFixedPosition(element: Element) {\n switch (getComputedStyle(element).position) {\n case 'fixed':\n case 'sticky':\n return true;\n default:\n return false;\n }\n}\n\n/**\n * Return the bounding rect that contains the element's content. Unlike\n * `Element.getBoundingClientRect`, this includes content which overflows\n * the element's specified size.\n */\nfunction elementContentRect(element: Element) {\n const rect = element.getBoundingClientRect();\n rect.x -= element.scrollLeft;\n rect.y -= element.scrollTop;\n rect.height = Math.max(rect.height, element.scrollHeight);\n rect.width = Math.max(rect.width, element.scrollWidth);\n return rect;\n}\n\n/**\n * Yield all the text node descendants of `root` that intersect `rect`.\n *\n * @param shouldVisit - Optional filter that determines whether to visit a subtree\n */\nfunction* textNodesInRect(\n root: Element,\n rect: DOMRect,\n shouldVisit: (el: Element) => boolean,\n): Generator<Text> {\n let node: Node | null = root.firstChild;\n while (node) {\n if (nodeIsElement(node)) {\n const contentIntersectsRect = rectIntersects(\n elementContentRect(node),\n rect,\n );\n\n // Only examine subtrees which are visible.\n if (shouldVisit(node) && contentIntersectsRect) {\n yield* textNodesInRect(node, rect, shouldVisit);\n }\n } else if (nodeIsText(node)) {\n // Skip over text nodes which are entirely outside the viewport or empty.\n if (rectIntersects(textRect(node), rect)) {\n yield node;\n }\n }\n node = node.nextSibling;\n }\n}\n\n/**\n * Find content within an element to use as an anchor when applying a layout\n * change to the document.\n *\n * @return Range to use as an anchor or `null` if a suitable range could not be found\n */\nfunction getScrollAnchor(root: Element, viewport: DOMRect): Range | null {\n // Range representing the content whose position within the viewport we will\n // try to maintain after running the callback.\n let anchorRange: Range | null = null;\n\n // Find the first word (non-whitespace substring of a text node) that is fully\n // visible in the viewport.\n\n // Text inside fixed-position elements is ignored because its position won't\n // be affected by a layout change and so it makes a poor scroll anchor.\n const shouldVisit = (el: Element) => !hasFixedPosition(el);\n\n textNodeLoop: for (const textNode of textNodesInRect(\n root,\n viewport,\n shouldVisit,\n )) {\n let textLen = 0;\n\n // Visit all the non-whitespace substrings of the text node.\n for (const word of textNode.data.split(/\\b/)) {\n if (/\\S/.test(word)) {\n const start = textLen;\n const end = textLen + word.length;\n const wordBox = textRect(textNode, start, end);\n if (rectContains(viewport, wordBox)) {\n anchorRange = document.createRange();\n anchorRange.setStart(textNode, start);\n anchorRange.setEnd(textNode, end);\n break textNodeLoop;\n }\n }\n\n textLen += word.length;\n }\n }\n\n return anchorRange;\n}\n\n/**\n * Apply a layout change to the document and preserve the scroll position.\n *\n * This utility selects part of the content in the viewport as an _anchor_\n * and tries to preserve the position of this content within the viewport\n * after the callback is invoked.\n *\n * @param callback - Callback that will apply the layout change\n * @param [viewport] - Area to consider \"in the viewport\". Defaults to the\n * viewport of the current window.\n * @return Amount by which the scroll position was adjusted to keep the anchored\n * content in view\n */\nexport function preserveScrollPosition(\n callback: () => void,\n /* istanbul ignore next */\n scrollRoot: Element = document.documentElement,\n /* istanbul ignore next */\n viewport: DOMRect = new DOMRect(0, 0, window.innerWidth, window.innerHeight),\n): number {\n const anchor = getScrollAnchor(scrollRoot, viewport);\n if (!anchor) {\n callback();\n return 0;\n }\n\n const anchorTop = anchor.getBoundingClientRect().top;\n callback();\n const newAnchorTop = anchor.getBoundingClientRect().top;\n\n // Determine how far we scrolled as a result of the layout change.\n // This will be positive if the anchor element moved down or negative if it moved up.\n const scrollDelta = newAnchorTop - anchorTop;\n scrollRoot.scrollTop += scrollDelta;\n\n return scrollDelta;\n}\n\nexport function isSideBySideMode(mode: unknown): mode is SideBySideMode {\n return typeof mode === 'string' && ['auto', 'manual'].includes(mode);\n}\n","/**\n * Return the URL of a resource related to the Hypothesis client that has been stored in\n * the page via a `<link type=\"application/annotator+{type}\">` tag.\n *\n * These link tags are generally written to the page by the boot script, which may be executed\n * in a separate JavaScript realm (eg. in the browser extension), and thus can share information\n * with the annotator code via the DOM but not JS globals.\n *\n * @param rel - The `rel` attribute to match\n * @param type - Type of resource that the link refers to\n * @throws {Error} - If there's no link with the `rel` indicated, or the first\n * matching link has no `href`\n */\nexport function urlFromLinkTag(\n window_: Window,\n rel: string,\n type: 'javascript' | 'html',\n) {\n const link = window_.document.querySelector(\n `link[type=\"application/annotator+${type}\"][rel=\"${rel}\"]`,\n ) as HTMLLinkElement | undefined;\n\n if (!link) {\n throw new Error(\n `No application/annotator+${type} (rel=\"${rel}\") link in the document`,\n );\n }\n\n if (!link.href) {\n throw new Error(\n `application/annotator+${type} (rel=\"${rel}\") link has no href`,\n );\n }\n\n return link.href;\n}\n","import { parseJsonConfig } from '../../boot/parse-json-config';\nimport { hasOwn } from '../../shared/has-own';\nimport { isObject } from '../../shared/is-object';\nimport { toBoolean } from '../../shared/type-coercions';\nimport type { SideBySideOptions } from '../../types/annotator';\nimport { isSideBySideMode } from '../integrations/html-side-by-side';\nimport { configFuncSettingsFrom } from './config-func-settings-from';\nimport { urlFromLinkTag } from './url-from-link-tag';\n\nexport type SettingsGetters = {\n annotations: string | null;\n query: string | null;\n group: string | null;\n showHighlights: string;\n clientUrl: string;\n sidebarAppUrl: string;\n notebookAppUrl: string;\n profileAppUrl: string;\n hostPageSetting: (name: string) => unknown;\n sideBySide: SideBySideOptions;\n};\n\n/**\n * Discard a setting if it is not a string.\n */\nfunction checkIfString(value: unknown): string | null {\n return typeof value === 'string' ? value : null;\n}\n\nexport function settingsFrom(window_: Window): SettingsGetters {\n // Prioritize the `window.hypothesisConfig` function over the JSON format\n // Via uses `window.hypothesisConfig` and makes it non-configurable and non-writable.\n // In addition, Via sets the `ignoreOtherConfiguration` option to prevent configuration merging.\n const configFuncSettings = configFuncSettingsFrom(window_);\n\n const jsonConfigs: Record<string, unknown> = toBoolean(\n configFuncSettings.ignoreOtherConfiguration,\n )\n ? {}\n : parseJsonConfig(window_.document);\n\n /**\n * Return the `#annotations:*` ID from the given URL's fragment.\n *\n * If the URL contains a `#annotations:<ANNOTATION_ID>` fragment then return\n * the annotation ID extracted from the fragment. Otherwise, return `null`.\n *\n * @return The extracted ID, or null.\n */\n function annotations(): string | null {\n /** Return the annotations from the URL, or null. */\n function annotationsFromURL() {\n // Annotation IDs are url-safe-base64 identifiers\n // See https://tools.ietf.org/html/rfc4648#page-7\n const annotFragmentMatch = window_.location.href.match(\n /#annotations:([A-Za-z0-9_-]+)$/,\n );\n if (annotFragmentMatch) {\n return annotFragmentMatch[1];\n }\n return null;\n }\n\n return checkIfString(jsonConfigs.annotations) || annotationsFromURL();\n }\n\n /**\n * Return the `#annotations:group:*` ID from the given URL's fragment.\n *\n * If the URL contains a `#annotations:group:<GROUP_ID>` fragment then return\n * the group ID extracted from the fragment. Otherwise return `null`.\n *\n * @return The extracted ID, or null.\n */\n function group(): string | null {\n function groupFromURL() {\n const groupFragmentMatch = window_.location.href.match(\n /#annotations:group:([A-Za-z0-9_-]+)$/,\n );\n if (groupFragmentMatch) {\n return groupFragmentMatch[1];\n }\n return null;\n }\n\n return checkIfString(jsonConfigs.group) || groupFromURL();\n }\n\n function showHighlights() {\n const value = hostPageSetting('showHighlights');\n\n switch (value) {\n case 'always':\n case 'never':\n case 'whenSidebarOpen':\n return value;\n case true:\n return 'always';\n case false:\n return 'never';\n default:\n return 'always';\n }\n }\n\n function sideBySide(): SideBySideOptions {\n const value = hostPageSetting('sideBySide');\n if (!isObject(value)) {\n return { mode: 'auto' };\n }\n\n const mode =\n 'mode' in value && isSideBySideMode(value.mode) ? value.mode : 'auto';\n if (mode === 'auto') {\n return { mode };\n }\n\n const isActive =\n 'isActive' in value && typeof value.isActive === 'function'\n ? (value.isActive as () => boolean)\n : undefined;\n\n return {\n mode,\n isActive,\n };\n }\n\n /**\n * Return the config.query setting from the host page or from the URL.\n *\n * If the host page contains a js-hypothesis-config script containing a\n * query setting then return that.\n *\n * Otherwise, if the host page's URL has a `#annotations:query:*` (or\n * `#annotations:q:*`) fragment then return the query value from that.\n *\n * Otherwise, return null.\n *\n * @return The config.query setting, or null.\n */\n function query(): string | null {\n /** Return the query from the URL, or null. */\n function queryFromURL() {\n const queryFragmentMatch = window_.location.href.match(\n /#annotations:(query|q):(.+)$/i,\n );\n if (queryFragmentMatch) {\n try {\n return decodeURIComponent(queryFragmentMatch[2]);\n } catch {\n // URI Error should return the page unfiltered.\n }\n }\n return null;\n }\n\n return checkIfString(jsonConfigs.query) || queryFromURL();\n }\n\n /**\n * Returns the first setting value found from the respective sources in order.\n *\n * 1. window.hypothesisConfig()\n * 2. <script class=\"js-hypothesis-config\">\n *\n * If the setting is not found in either source, then return undefined.\n *\n * @param name - Unique name of the setting\n */\n function hostPageSetting(name: string) {\n if (hasOwn(configFuncSettings, name)) {\n return configFuncSettings[name];\n }\n\n if (hasOwn(jsonConfigs, name)) {\n return jsonConfigs[name];\n }\n\n return undefined;\n }\n\n return {\n get annotations() {\n return annotations();\n },\n get clientUrl() {\n return urlFromLinkTag(window_, 'hypothesis-client', 'javascript');\n },\n get group() {\n return group();\n },\n get notebookAppUrl() {\n return urlFromLinkTag(window_, 'notebook', 'html');\n },\n get profileAppUrl() {\n return urlFromLinkTag(window_, 'profile', 'html');\n },\n get showHighlights() {\n return showHighlights();\n },\n get sidebarAppUrl() {\n return urlFromLinkTag(window_, 'sidebar', 'html');\n },\n get query() {\n return query();\n },\n get sideBySide() {\n return sideBySide();\n },\n hostPageSetting,\n };\n}\n","import { hasOwn } from '../../shared/has-own';\n\ntype HypothesisWindowProps = {\n /** Function that returns configuration for the Hypothesis client */\n hypothesisConfig?: () => Record<string, unknown>;\n};\n\n/**\n * Return an object containing config settings from window.hypothesisConfig().\n *\n * Return an object containing config settings returned by the\n * window.hypothesisConfig() function provided by the host page:\n *\n * {\n * fooSetting: 'fooValue',\n * barSetting: 'barValue',\n * ...\n * }\n *\n * If there's no window.hypothesisConfig() function then return {}.\n *\n * @param window_ - The window to search for a hypothesisConfig() function\n * @return Any config settings returned by hypothesisConfig()\n */\nexport function configFuncSettingsFrom(\n window_: Window & HypothesisWindowProps,\n): Record<string, unknown> {\n if (!hasOwn(window_, 'hypothesisConfig')) {\n return {};\n }\n\n if (typeof window_.hypothesisConfig !== 'function') {\n const docs =\n 'https://h.readthedocs.io/projects/client/en/latest/publishers/config/#window.hypothesisConfig';\n console.warn('hypothesisConfig must be a function, see: ' + docs);\n return {};\n }\n\n return window_.hypothesisConfig();\n}\n","export function isObject(value: unknown): value is object {\n return value !== null && typeof value === 'object';\n}\n","import { toBoolean } from '../../shared/type-coercions';\nimport { isBrowserExtension } from './is-browser-extension';\nimport { settingsFrom } from './settings';\nimport type { SettingsGetters } from './settings';\nimport { urlFromLinkTag } from './url-from-link-tag';\n\ntype ValueGetter = (settings: SettingsGetters, name: string) => any;\n\ntype ConfigDefinition = {\n /** Method to retrieve the value from the incoming source */\n getValue: ValueGetter;\n\n /**\n * Allow this setting to be read in the browser extension. If this is false\n * and browser extension context is true, use `defaultValue` if provided\n * otherwise ignore the config key\n */\n allowInBrowserExt: boolean;\n\n /** Sets a default if `getValue` returns undefined */\n defaultValue?: any;\n /** Transform a value's type, value or both */\n coerce?: (value: any) => any;\n};\n\ntype ConfigDefinitionMap = Record<string, ConfigDefinition>;\n\n/**\n * Named subset of the Hypothesis client configuration that is relevant in\n * a particular context.\n */\ntype Context = 'sidebar' | 'notebook' | 'profile' | 'annotator' | 'all';\n\n/**\n * Returns the configuration keys that are relevant to a particular context.\n */\nfunction configurationKeys(context: Context): string[] {\n const contexts: Record<Exclude<Context, 'all'>, string[]> = {\n annotator: [\n 'clientUrl',\n 'contentInfoBanner',\n 'contentReady',\n 'subFrameIdentifier',\n 'sideBySide',\n ],\n sidebar: [\n 'appType',\n 'annotations',\n 'branding',\n 'bucketContainerSelector',\n 'enableExperimentalNewNoteButton',\n 'externalContainerSelector',\n 'focus',\n 'group',\n 'onLayoutChange',\n 'openSidebar',\n 'query',\n 'requestConfigFromFrame',\n 'services',\n 'showHighlights',\n 'sidebarAppUrl',\n 'theme',\n 'usernameUrl',\n ],\n notebook: [\n 'branding',\n 'group',\n 'notebookAppUrl',\n 'requestConfigFromFrame',\n 'services',\n 'theme',\n 'usernameUrl',\n ],\n profile: ['profileAppUrl'],\n };\n\n if (context === 'all') {\n // Complete list of configuration keys used for testing.\n return Object.values(contexts).flat();\n }\n\n return contexts[context];\n}\n\nconst getHostPageSetting: ValueGetter = (settings, name) =>\n settings.hostPageSetting(name);\n\n/**\n * Definitions of configuration keys\n */\nconst configDefinitions: ConfigDefinitionMap = {\n annotations: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.annotations,\n },\n appType: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n branding: {\n defaultValue: null,\n allowInBrowserExt: false,\n getValue: getHostPageSetting,\n },\n bucketContainerSelector: {\n defaultValue: null,\n allowInBrowserExt: false,\n getValue: getHostPageSetting,\n },\n // URL of the client's boot script. Used when injecting the client into\n // child iframes.\n clientUrl: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.clientUrl,\n },\n contentInfoBanner: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n contentReady: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n enableExperimentalNewNoteButton: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n group: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.group,\n },\n focus: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n theme: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n usernameUrl: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n onLayoutChange: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n openSidebar: {\n allowInBrowserExt: true,\n defaultValue: false,\n coerce: toBoolean,\n getValue: getHostPageSetting,\n },\n query: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.query,\n },\n requestConfigFromFrame: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n services: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n showHighlights: {\n allowInBrowserExt: false,\n defaultValue: 'always',\n getValue: settings => settings.showHighlights,\n },\n notebookAppUrl: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.notebookAppUrl,\n },\n profileAppUrl: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.profileAppUrl,\n },\n sidebarAppUrl: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.sidebarAppUrl,\n },\n // Sub-frame identifier given when a frame is being embedded into\n // by a top level client\n subFrameIdentifier: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n externalContainerSelector: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n sideBySide: {\n allowInBrowserExt: true,\n getValue: settings => settings.sideBySide,\n },\n};\n\n/**\n * Return the subset of Hypothesis client configuration that is relevant in\n * a particular context.\n *\n * See https://h.readthedocs.io/projects/client/en/latest/publishers/config/\n * for details of all available configuration and the different ways they\n * can be included on the page. In addition to the configuration provided by\n * the embedder, the boot script also passes some additional configuration\n * to the annotator, such as URLs of the various sub-applications and the\n * boot script itself.\n */\nexport function getConfig(context: Context, window_: Window = window) {\n const settings = settingsFrom(window_);\n const config: Record<string, unknown> = {};\n\n // Filter the config based on the application context as some config values\n // may be inappropriate or erroneous for some applications.\n for (const key of configurationKeys(context)) {\n const configDef = configDefinitions[key];\n const hasDefault = configDef.defaultValue !== undefined; // A default could be null\n const isURLFromBrowserExtension = isBrowserExtension(\n urlFromLinkTag(window_, 'sidebar', 'html'),\n );\n\n // Only allow certain values in the browser extension context\n if (!configDef.allowInBrowserExt && isURLFromBrowserExtension) {\n // If the value is not allowed here, then set to the default if provided, otherwise ignore\n // the key:value pair\n if (hasDefault) {\n config[key] = configDef.defaultValue;\n }\n continue;\n }\n\n // Get the value from the configuration source\n const value = configDef.getValue(settings, key);\n if (value === undefined) {\n // If there is no value (e.g. undefined), then set to the default if provided,\n // otherwise ignore the config key:value pair\n if (hasDefault) {\n config[key] = configDef.defaultValue;\n }\n continue;\n }\n\n // Finally, run the value through an optional coerce method\n config[key] = configDef.coerce ? configDef.coerce(value) : value;\n }\n\n return config;\n}\n","/**\n * Returns true if this instance of the Hypothesis client is one distributed in\n * a browser extension, false if it's one embedded in a website.\n */\nexport function isBrowserExtension(url: string): boolean {\n return !(url.startsWith('http://') || url.startsWith('https://'));\n}\n","import { useEffect } from 'preact/hooks';\n\n/**\n * Bit flags indicating modifiers required by a shortcut or pressed in a key event.\n */\nconst modifiers = {\n alt: 1,\n ctrl: 2,\n meta: 4,\n shift: 8,\n} as Record<string, number>;\n\n/**\n * Match a `shortcut` key sequence against a keydown event.\n *\n * A shortcut key sequence is a string of \"+\"-separated keyboard modifiers and\n * keys. The list may contain zero or more modifiers and must contain exactly\n * one non-modifier key. The key and modifier names are case-insensitive.\n * For example \"Ctrl+Enter\", \"shift+a\".\n *\n * Key names are matched against {@link KeyboardEvent.key}. Be aware that this\n * property is affected by the modifiers for certain keys. For example on a US\n * QWERTY keyboard, \"Shift+1\" would not match any event because the key value\n * would be \"!\" instead. See also https://github.com/w3c/uievents/issues/247.\n */\nexport function matchShortcut(event: KeyboardEvent, shortcut: string): boolean {\n // Work around an issue where Chrome autofill can dispatch \"keydown\" events\n // with an argument that is not a `KeyboardEvent`.\n //\n // See https://bugs.chromium.org/p/chromium/issues/detail?id=739792.\n if (!(event instanceof KeyboardEvent)) {\n return false;\n }\n\n const parts = shortcut.split('+').map(p => p.toLowerCase());\n\n let requiredModifiers = 0;\n let requiredKey = null;\n\n for (const part of parts) {\n const modifierFlag = modifiers[part];\n if (modifierFlag) {\n requiredModifiers |= modifierFlag;\n } else if (requiredKey === null) {\n requiredKey = part;\n } else {\n throw new Error('Multiple non-modifier keys specified');\n }\n }\n\n if (!requiredKey) {\n throw new Error(`Invalid shortcut: ${shortcut}`);\n }\n\n const actualModifiers =\n (event.ctrlKey ? modifiers.ctrl : 0) |\n (event.metaKey ? modifiers.meta : 0) |\n (event.altKey ? modifiers.alt : 0) |\n (event.shiftKey ? modifiers.shift : 0);\n\n return (\n actualModifiers === requiredModifiers &&\n event.key.toLowerCase() === requiredKey\n );\n}\n\nexport type ShortcutOptions = {\n /**\n * Element on which the key event listener should be installed. Defaults to\n * `document.body`.\n */\n rootElement?: HTMLElement;\n};\n\n/**\n * Install a shortcut key listener on the document.\n *\n * This can be used directly outside of a component. To use within a Preact\n * component, you probably want {@link useShortcut}.\n *\n * @param shortcut - Shortcut key sequence. See {@link matchShortcut}.\n * @param onPress - A function to call when the shortcut matches\n * @return A function that removes the shortcut\n */\nexport function installShortcut(\n shortcut: string,\n onPress: (e: KeyboardEvent) => void,\n {\n // We use `documentElement` as the root element rather than `document.body`\n // which is used as a root element in some other places because the body\n // element is not keyboard-focusable in XHTML documents in Safari/Chrome.\n // See https://github.com/hypothesis/client/issues/4364.\n //\n // nb. `documentElement` is non-null in TS types, but it can be null if\n // the root element is explicitly removed. We don't know how this happens,\n // but it has been observed on some ChromeOS devices. See\n // https://hypothesis.sentry.io/issues/3987992034.\n rootElement = (document.documentElement as HTMLElement | null) ?? undefined,\n }: ShortcutOptions = {},\n) {\n const onKeydown = (event: KeyboardEvent) => {\n if (matchShortcut(event, shortcut)) {\n onPress(event);\n }\n };\n /* istanbul ignore next */\n if (!rootElement) {\n return () => {};\n }\n rootElement.addEventListener('keydown', onKeydown);\n return () => rootElement.removeEventListener('keydown', onKeydown);\n}\n\n/**\n * An effect hook that installs a shortcut using {@link installShortcut} and\n * removes it when the component is unmounted.\n *\n * This provides a convenient way to enable a document-level shortcut while\n * a component is mounted. This differs from adding an `onKeyDown` handler to\n * one of the component's DOM elements in that it doesn't require the component\n * to have focus.\n *\n * To conditionally disable the shortcut, set `shortcut` to `null`.\n *\n * @param shortcut - A shortcut key sequence to match or `null` to disable. See {@link matchShortcut}.\n * @param onPress - A function to call when the shortcut matches\n */\nexport function useShortcut(\n shortcut: string | null,\n onPress: (e: KeyboardEvent) => void,\n { rootElement }: ShortcutOptions = {},\n) {\n useEffect(() => {\n if (!shortcut) {\n return undefined;\n }\n return installShortcut(shortcut, onPress, { rootElement });\n }, [shortcut, onPress, rootElement]);\n}\n","/**\n * Helper methods to identify browser versions and os types\n */\n\n/**\n * Returns true when the OS is Mac OS.\n *\n * @param _userAgent - Test seam\n */\nexport const isMacOS = (_userAgent: string = window.navigator.userAgent) => {\n return _userAgent.indexOf('Mac OS') >= 0;\n};\n\n/**\n * Returns true when device is iOS.\n * https://stackoverflow.com/a/9039885/14463679\n */\nexport const isIOS = (\n _navigator: { platform: string; userAgent: string } = window.navigator,\n _ontouchend: boolean = 'ontouchend' in document,\n): boolean => {\n return (\n [\n 'iPad Simulator',\n 'iPhone Simulator',\n 'iPod Simulator',\n 'iPad',\n 'iPhone',\n 'iPod',\n ].includes(_navigator.platform) ||\n // iPad on iOS 13 detection\n (_navigator.userAgent.includes('Mac') && _ontouchend)\n );\n};\n\n/**\n * Returns true when the device is a touch device such\n * as android or iOS.\n * https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer#browser_compatibility\n */\nexport const isTouchDevice = (_window: Window = window): boolean => {\n return _window.matchMedia('(pointer: coarse)').matches;\n};\n","import {\n AnnotateIcon,\n Button,\n HighlightIcon,\n PointerDownIcon,\n PointerUpIcon,\n} from '@hypothesis/frontend-shared';\nimport type { IconComponent } from '@hypothesis/frontend-shared/lib/types';\nimport classnames from 'classnames';\n\nimport { useShortcut } from '../../shared/shortcut';\n\n/**\n * Render an inverted light-on-dark \"pill\" with the given `badgeCount`\n * (annotation count). This is rendered instead of an icon on the toolbar\n * button for \"show\"-ing associated annotations for the current selection.\n */\nfunction NumberIcon({ badgeCount }: { badgeCount: number }) {\n return (\n <span\n className={classnames(\n 'rounded px-1 py-0.5',\n // The background color is inherited from the current text color in\n // the containing button and will vary depending on hover state.\n 'bg-current',\n )}\n >\n <span className=\"font-bold text-color-text-inverted\">{badgeCount}</span>\n </span>\n );\n}\n\n/**\n * Render an arrow pointing up or down from the AdderToolbar. This arrow\n * should point roughly to the end of the user selection in the document.\n */\nfunction AdderToolbarArrow({\n arrowDirection,\n}: {\n arrowDirection: 'up' | 'down';\n}) {\n return (\n <div\n className={classnames(\n // Position horizontally center of the AdderToolbar\n 'absolute left-1/2 -translate-x-1/2 z-2',\n 'fill-white text-grey-3',\n {\n // Move the pointer to the top of the AdderToolbar\n 'top-0 -translate-y-full': arrowDirection === 'up',\n },\n )}\n >\n {arrowDirection === 'up' ? <PointerUpIcon /> : <PointerDownIcon />}\n </div>\n );\n}\n\ntype ToolbarButtonProps = {\n badgeCount?: number;\n icon?: IconComponent;\n label: string;\n onClick: () => void;\n shortcut: string | null;\n};\n\nfunction ToolbarButton({\n badgeCount,\n icon: Icon,\n label,\n onClick,\n shortcut,\n}: ToolbarButtonProps) {\n useShortcut(shortcut, onClick);\n\n const title = shortcut ? `${label} (${shortcut})` : label;\n\n return (\n <Button\n classes={classnames(\n // Default color when the toolbar is not hovered\n 'text-grey-7',\n // When the parent .group element is hovered (but this element itself is\n // not), dim this button's text. This has the effect of dimming inactive\n // buttons.\n 'group-hover:text-grey-5',\n // When the parent .group element is hovered AND this element is\n // hovered, this is the \"active\" button. Intensify the text color, which\n // will also darken any descendant Icon\n 'hover:group-hover:text-grey-9',\n )}\n onClick={onClick}\n title={title}\n size=\"custom\"\n variant=\"custom\"\n >\n <div\n className={classnames(\n 'flex flex-col items-center gap-y-1 py-2.5 px-2',\n 'text-annotator-sm leading-none',\n )}\n >\n {Icon && <Icon className=\"text-annotator-lg\" title={title} />}\n {typeof badgeCount === 'number' && (\n <NumberIcon badgeCount={badgeCount} />\n )}\n <span data-testid=\"adder-button-label\">{label}</span>\n </div>\n </Button>\n );\n}\n\n/**\n * Render non-visible content for screen readers to announce adder keyboard\n * shortcuts and count of annotations associated with the current selection.\n */\nfunction AdderToolbarShortcuts({\n annotationCount,\n isVisible,\n}: {\n annotationCount: number;\n isVisible: boolean;\n}) {\n return (\n <div className=\"sr-only\">\n <span\n aria-live=\"polite\"\n aria-atomic=\"true\"\n role=\"status\"\n data-testid=\"annotation-count-announce\"\n >\n {annotationCount > 0 && (\n <span>\n {annotationCount}{' '}\n {annotationCount === 1 ? 'annotation' : 'annotations'} for this\n selection.\n </span>\n )}\n </span>\n <ul aria-live=\"polite\" data-testid=\"annotate-shortcuts-announce\">\n {isVisible && (\n <>\n {annotationCount > 0 && <li>Press {\"'S'\"} to show annotations.</li>}\n <li>Press {\"'A'\"} to annotate.</li>\n <li>Press {\"'H'\"} to highlight.</li>\n </>\n )}\n </ul>\n </div>\n );\n}\n\nexport type Command = 'annotate' | 'highlight' | 'show' | 'hide';\n\ntype AdderToolbarProps = {\n /**\n * Number of annotations associated with the selected text. If non-zero, a\n * Show\" button is displayed to allow the user to see the annotations that\n * correspond to the selection.\n */\n annotationCount?: number;\n /**\n * Whether the arrow pointing out of the toolbar towards the selected text\n * should appear above the toolbar pointing up or below the toolbar pointing\n * down.\n */\n arrowDirection: 'up' | 'down';\n /** The toolbar is always rendered, but is not always visible. */\n isVisible: boolean;\n /** Called when a toolbar button is clicked */\n onCommand: (c: Command) => void;\n};\n\n/**\n * The toolbar that is displayed above or below selected text in the document,\n * providing options to create annotations or highlights.\n *\n * @param {AdderToolbarProps} props\n * The toolbar has nuanced styling for hover. The component structure is:\n *\n * <AdderToolbar>\n * <div.group>\n * <button.hover-group><AnnotateIcon />Annotate</button>\n * <button.hover-group><HighlightIcon />Highlight</button>\n * <div>[vertical separator]</div>\n * <button.hover-group><span><NumberIcon /></span>[count]</button>\n * </div.group>\n * <AdderToolbarArrow />\n * </AdderToolbar>\n *\n * Behavior: When div.group is hovered, all descendant buttons and their\n * contents dim, except for the contents of the button that is directly hovered,\n * which are darkened. This is intended to make the hovered button stand out,\n * and the non-hovered buttons recede.\n *\n * This is achieved by:\n * - Setting the .group class on the div that contains the buttons. This allows\n * buttons to style themselves based on the combination of the div.group's\n * hover state and their own \"local\" hover state. `group` is available in\n * Tailwind out of the box; see\n * https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-parent-state\n * - The challenge is in getting the \"badge\" in NumberIcon to dim and darken its\n * background appropriately. `hover-group-hover` is a custom tailwind variant\n * that allows NumberIcon to style itself based on the hover states of\n * both div.group AND its parent button.hover-group. We need to ensure this\n * badge will darken when its parent button is hovered, even if it is not\n * hovered directly.\n *\n */\nexport default function AdderToolbar({\n arrowDirection,\n isVisible,\n onCommand,\n annotationCount = 0,\n}: AdderToolbarProps) {\n // Since the selection toolbar is only shown when there is a selection\n // of static text, we can use a plain key without any modifier as\n // the shortcut. This avoids conflicts with browser/OS shortcuts.\n const annotateShortcut = isVisible ? 'a' : null;\n const highlightShortcut = isVisible ? 'h' : null;\n const showShortcut = isVisible ? 's' : null;\n const hideShortcut = isVisible ? 'Escape' : null;\n\n // Add a shortcut to close the adder. Note, there is no button associated with this\n // shortcut because any outside click will also hide the adder.\n useShortcut(hideShortcut, () => onCommand('hide'));\n\n // nb. The adder is hidden using the `visibility` property rather than `display`\n // so that we can compute its size in order to position it before display.\n return (\n <div\n className={classnames(\n // Reset all inherited properties to their initial values. This prevents\n // CSS property values from the host page being inherited by elements of\n // the Adder, even when using Shadow DOM.\n 'all-initial',\n // As we've reset all properties to initial values, we cannot rely on\n // default border values from Tailwind and have to be explicit about all\n // border attributes.\n 'border border-solid border-grey-3',\n 'absolute select-none bg-white rounded shadow-intense',\n // Start at a very low opacity as we're going to fade-in in the animation\n 'opacity-5',\n {\n 'animate-adder-pop-up': arrowDirection === 'up' && isVisible,\n 'animate-adder-pop-down': arrowDirection === 'down' && isVisible,\n },\n )}\n data-component=\"AdderToolbar\"\n dir=\"ltr\"\n style={{\n visibility: isVisible ? 'visible' : 'hidden',\n }}\n >\n <div\n className={classnames(\n // This group is used to manage hover state styling for descendant\n // buttons\n 'flex group',\n )}\n >\n <ToolbarButton\n icon={AnnotateIcon}\n onClick={() => onCommand('annotate')}\n label=\"Annotate\"\n shortcut={annotateShortcut}\n />\n <ToolbarButton\n icon={HighlightIcon}\n onClick={() => onCommand('highlight')}\n label=\"Highlight\"\n shortcut={highlightShortcut}\n />\n {annotationCount > 0 && (\n <>\n <div\n className={classnames(\n // Style a vertical separator line\n 'm-1.5 border-r border-grey-4 border-solid',\n )}\n />\n <ToolbarButton\n badgeCount={annotationCount}\n onClick={() => onCommand('show')}\n label=\"Show\"\n shortcut={showShortcut}\n />\n </>\n )}\n </div>\n <AdderToolbarArrow arrowDirection={arrowDirection} />\n <AdderToolbarShortcuts\n annotationCount={annotationCount}\n isVisible={isVisible}\n />\n </div>\n );\n}\n","/**\n * Load stylesheets for annotator UI components into the shadow DOM root.\n */\nfunction loadStyles(shadowRoot: ShadowRoot) {\n // Find the preloaded stylesheet added by the boot script.\n const url = (\n document.querySelector(\n 'link[rel=\"preload\"][href*=\"/build/styles/annotator.css\"]',\n ) as HTMLLinkElement | undefined\n )?.href;\n\n if (!url) {\n return;\n }\n\n const linkEl = document.createElement('link');\n linkEl.rel = 'stylesheet';\n linkEl.href = url;\n shadowRoot.appendChild(linkEl);\n}\n\n/**\n * Create the shadow root for an annotator UI component and load the annotator\n * CSS styles into it.\n *\n * @param container - Container element to render the UI into\n */\nexport function createShadowRoot(container: HTMLElement): ShadowRoot {\n const shadowRoot = container.attachShadow({ mode: 'open' });\n loadStyles(shadowRoot);\n\n // @ts-ignore The window doesn't know about the polyfill\n // applyFocusVisiblePolyfill comes from the `focus-visible` package.\n const applyFocusVisible = window.applyFocusVisiblePolyfill;\n if (applyFocusVisible) {\n applyFocusVisible(shadowRoot);\n }\n\n return shadowRoot;\n}\n","import type { ComponentChild } from 'preact';\nimport { render } from 'preact';\n\nimport type { Destroyable } from '../../types/annotator';\nimport { createShadowRoot } from './shadow-root';\n\n/**\n * Manages the root `<hypothesis-*>` container for a top-level Hypothesis UI\n * element.\n *\n * This implements common functionality for these elements, such as:\n *\n * - Creating the `<hypothesis-{name}>` element with a shadow root, and loading\n * stylesheets into it.\n * - Re-rendering the Preact component tree when {@link PreactContainer.render} is called.\n * - Unmounting the component and removing the container element when\n * {@link PreactContainer.destroy} is called\n */\nexport class PreactContainer implements Destroyable {\n private _element: HTMLElement;\n private _shadowRoot: ShadowRoot;\n private _render: () => ComponentChild;\n\n /**\n * Create a new `<hypothesis-{name}>` container element.\n *\n * After constructing the container, {@link PreactContainer.render} should be\n * called to perform the initial render.\n *\n * @param name - Suffix for the element\n * @param render - Callback that renders the root JSX element for this container\n */\n constructor(name: string, render: () => ComponentChild) {\n const tag = `hypothesis-${name}`;\n this._element = document.createElement(tag);\n this._shadowRoot = createShadowRoot(this._element);\n this._render = render;\n }\n\n /** Unmount the Preact component and remove the container element from the DOM. */\n destroy() {\n render(null, this._shadowRoot);\n this._element.remove();\n }\n\n /** Return a reference to the container element. */\n get element(): HTMLElement {\n return this._element;\n }\n\n /** Re-render the root Preact component. */\n render() {\n render(this._render(), this._shadowRoot);\n }\n}\n","import { isTouchDevice } from '../shared/user-agent';\nimport type { Destroyable } from '../types/annotator';\nimport AdderToolbar from './components/AdderToolbar';\nimport type { Command } from './components/AdderToolbar';\nimport { PreactContainer } from './util/preact-container';\n\nexport enum ArrowDirection {\n DOWN = 1,\n UP = 2,\n}\n\ntype Target = {\n /** Offset from left edge of viewport */\n left: number;\n /** Offset from top edge of viewport */\n top: number;\n /** Direction of the adder's arrow */\n arrowDirection: ArrowDirection;\n};\n\nfunction toPx(pixels: number) {\n return pixels.toString() + 'px';\n}\n\nconst ARROW_HEIGHT = 10;\n\n// The preferred gap between the end of the text selection and the adder's\n// arrow position.\nconst ARROW_H_MARGIN = 20;\n\n/**\n * Return the closest ancestor of `el` which has been positioned.\n * If no ancestor has been positioned, returns the root element.\n */\nfunction nearestPositionedAncestor(el: Element): Element {\n let parentEl = el.parentElement!;\n while (parentEl.parentElement) {\n if (getComputedStyle(parentEl).position !== 'static') {\n break;\n }\n parentEl = parentEl.parentElement;\n }\n return parentEl;\n}\n\ntype AdderOptions = {\n /** Callback invoked when \"Annotate\" button is clicked */\n onAnnotate: () => void;\n /** Callback invoked when \"Highlight\" button is clicked */\n onHighlight: () => void;\n /** Callback invoked when \"Show\" button is clicked */\n onShowAnnotations: (tags: string[]) => void;\n};\n\n/**\n * Container for the 'adder' toolbar which provides controls for the user to\n * annotate and highlight the selected text.\n *\n * The toolbar implementation is split between this class, which is\n * the container for the toolbar that positions it on the page and isolates\n * it from the page's styles using shadow DOM, and the `AdderToolbar` Preact\n * component which actually renders the toolbar.\n */\nexport class Adder implements Destroyable {\n private _container: PreactContainer;\n private _view: Window;\n private _isVisible: boolean;\n private _arrowDirection: 'up' | 'down';\n private _onAnnotate: () => void;\n private _onHighlight: () => void;\n private _onShowAnnotations: (tags: string[]) => void;\n /** Annotation tags associated with the current selection. */\n private _annotationsForSelection: string[];\n\n /**\n * Create the toolbar's container and hide it.\n *\n * The adder is initially hidden.\n *\n * @param element - The DOM element into which the adder will be created\n * @param options - Options object specifying `onAnnotate` and `onHighlight`\n * event handlers.\n */\n constructor(element: HTMLElement, options: AdderOptions) {\n this._view = element.ownerDocument.defaultView!;\n this._isVisible = false;\n this._arrowDirection = 'up';\n this._annotationsForSelection = [];\n\n this._onAnnotate = options.onAnnotate;\n this._onHighlight = options.onHighlight;\n this._onShowAnnotations = options.onShowAnnotations;\n\n this._container = new PreactContainer('adder', () => this._render());\n element.appendChild(this._container.element);\n\n // Take position out of layout flow initially\n Object.assign(this._container.element.style, {\n position: 'absolute',\n top: 0,\n left: 0,\n });\n this._container.render();\n }\n\n get annotationsForSelection() {\n return this._annotationsForSelection;\n }\n\n /**\n * Set the annotation IDs associated with the current selection.\n *\n * Setting this to a non-empty list causes the \"Show\" button to appear in\n * the toolbar. Clicking the \"Show\" button triggers the `onShowAnnotations`\n * callback passed to the constructor.\n */\n set annotationsForSelection(ids) {\n this._annotationsForSelection = ids;\n this._container.render();\n }\n\n /** Hide the adder */\n hide() {\n this._isVisible = false;\n this._container.render();\n\n // Reposition the container because it affects the responsiveness of host page\n // https://github.com/hypothesis/client/issues/3193\n Object.assign(this._container.element.style, {\n top: 0,\n left: 0,\n });\n }\n\n destroy() {\n this._container.destroy();\n }\n\n /**\n * Display the adder in the best position in order to target the\n * selected text in `selectionRect`.\n *\n * @param selectionRect - The rect of text to target, in viewport coordinates.\n * @param isRTLselection - True if the selection was made right-to-left, such\n * that the focus point is mostly likely at the top-left edge of\n * `targetRect`.\n */\n show(selectionRect: DOMRect, isRTLselection: boolean) {\n const { left, top, arrowDirection } = this._calculateTarget(\n selectionRect,\n isRTLselection,\n );\n this._showAt(left, top);\n\n this._isVisible = true;\n this._arrowDirection = arrowDirection === ArrowDirection.UP ? 'up' : 'down';\n this._container.render();\n }\n\n private _firstChild(): Element {\n return this._container.element.shadowRoot!.firstChild as Element;\n }\n\n private _width(): number {\n return this._firstChild().getBoundingClientRect().width;\n }\n\n private _height(): number {\n return this._firstChild().getBoundingClientRect().height;\n }\n\n /**\n * Determine the best position for the Adder and its pointer-arrow.\n * - Position the pointer-arrow near the end of the selection (where the user's\n * cursor/input is most likely to be)\n * - Position the Adder to center horizontally on the pointer-arrow\n * - Position the Adder below the selection (arrow pointing up) for LTR selections\n * and above (arrow down) for RTL selections\n *\n * @param selectionRect - The rect of text to target, in viewport coordinates.\n * @param isRTLselection - True if the selection was made right-to-left, such\n * that the focus point is mostly likely at the top-left edge of\n * `targetRect`.\n */\n private _calculateTarget(\n selectionRect: DOMRect,\n isRTLselection: boolean,\n ): Target {\n // Set the initial arrow direction based on whether the selection was made\n // forwards/upwards or downwards/backwards.\n let arrowDirection: ArrowDirection;\n if (isRTLselection && !isTouchDevice()) {\n arrowDirection = ArrowDirection.DOWN;\n } else {\n // Render the adder below the selection for touch devices due to competing\n // space with the native copy/paste bar that typical (not always) renders above\n // the selection.\n arrowDirection = ArrowDirection.UP;\n }\n let top;\n let left;\n\n // Position the adder such that the arrow it is above or below the selection\n // and close to the end.\n const hMargin = Math.min(ARROW_H_MARGIN, selectionRect.width);\n const adderWidth = this._width();\n // Render the adder a little lower on touch devices to provide room for the native\n // selection handles so that the interactions with selection don't compete with the adder.\n const touchScreenOffset = isTouchDevice() ? 10 : 0;\n const adderHeight = this._height();\n if (isRTLselection) {\n left = selectionRect.left - adderWidth / 2 + hMargin;\n } else {\n left =\n selectionRect.left + selectionRect.width - adderWidth / 2 - hMargin;\n }\n\n // Flip arrow direction if adder would appear above the top or below the\n // bottom of the viewport.\n if (\n selectionRect.top - adderHeight < 0 &&\n arrowDirection === ArrowDirection.DOWN\n ) {\n arrowDirection = ArrowDirection.UP;\n } else if (selectionRect.top + adderHeight > this._view.innerHeight) {\n arrowDirection = ArrowDirection.DOWN;\n }\n\n if (arrowDirection === ArrowDirection.UP) {\n top =\n selectionRect.top +\n selectionRect.height +\n ARROW_HEIGHT +\n touchScreenOffset;\n } else {\n top = selectionRect.top - adderHeight - ARROW_HEIGHT;\n }\n\n // Constrain the adder to the viewport.\n left = Math.max(left, 0);\n left = Math.min(left, this._view.innerWidth - adderWidth);\n\n top = Math.max(top, 0);\n top = Math.min(top, this._view.innerHeight - adderHeight);\n\n return { top, left, arrowDirection };\n }\n\n /**\n * Find a Z index value that will cause the adder to appear on top of any\n * content in the document when the adder is shown at (left, top).\n *\n * @param left - Horizontal offset from left edge of viewport.\n * @param top - Vertical offset from top edge of viewport.\n * @return greatest zIndex (default value of 1)\n */\n private _findZindex(left: number, top: number): number {\n if (document.elementsFromPoint === undefined) {\n // In case of not being able to use `document.elementsFromPoint`,\n // default to the large arbitrary number (2^15)\n return 32768;\n }\n\n const adderWidth = this._width();\n const adderHeight = this._height();\n\n // Find the Z index of all the elements in the screen for five positions\n // around the adder (left-top, left-bottom, middle-center, right-top,\n // right-bottom) and use the greatest.\n\n // Unique elements so `getComputedStyle` is called the minimum amount of times.\n const elements = new Set([\n ...document.elementsFromPoint(left, top),\n ...document.elementsFromPoint(left, top + adderHeight),\n ...document.elementsFromPoint(\n left + adderWidth / 2,\n top + adderHeight / 2,\n ),\n ...document.elementsFromPoint(left + adderWidth, top),\n ...document.elementsFromPoint(left + adderWidth, top + adderHeight),\n ]);\n\n const zIndexes = [...elements]\n .map(element => +getComputedStyle(element).zIndex)\n .filter(Number.isInteger);\n\n // Make sure the array contains at least one element,\n // otherwise `Math.max(...[])` results in +Infinity\n zIndexes.push(0);\n\n return Math.max(...zIndexes) + 1;\n }\n\n /**\n * Show the adder at the given position and with the arrow pointing in\n * `arrowDirection`.\n *\n * @param left - Horizontal offset from left edge of viewport.\n * @param top - Vertical offset from top edge of viewport.\n */\n private _showAt(left: number, top: number) {\n // Translate the (left, top) viewport coordinates into positions relative to\n // the adder's nearest positioned ancestor (NPA).\n //\n // Typically, the adder is a child of the `<body>` and the NPA is the root\n // `<html>` element. However, page styling may make the `<body>` positioned.\n // See https://github.com/hypothesis/client/issues/487.\n const positionedAncestor = nearestPositionedAncestor(\n this._container.element,\n );\n const parentRect = positionedAncestor.getBoundingClientRect();\n\n const zIndex = this._findZindex(left, top);\n\n Object.assign(this._container.element.style, {\n left: toPx(left - parentRect.left),\n top: toPx(top - parentRect.top),\n zIndex,\n });\n }\n\n private _render() {\n const handleCommand = (command: Command) => {\n switch (command) {\n case 'annotate':\n this._onAnnotate();\n this.hide();\n break;\n case 'highlight':\n this._onHighlight();\n this.hide();\n break;\n case 'show':\n this._onShowAnnotations(this.annotationsForSelection);\n break;\n case 'hide':\n this.hide();\n break;\n }\n };\n\n return (\n <AdderToolbar\n isVisible={this._isVisible}\n arrowDirection={this._arrowDirection}\n onCommand={handleCommand}\n annotationCount={this.annotationsForSelection.length}\n />\n );\n }\n}\n","/**\n * From which direction to evaluate strings or nodes: from the start of a string\n * or range seeking Forwards, or from the end seeking Backwards.\n */\nenum TrimDirection {\n Forwards = 1,\n Backwards,\n}\n\n/**\n * An object representing metadata for a Range position (e.g. for use with\n * Range.setStart or Range.setEnd)\n */\ntype RangePosition = {\n offset: number;\n node: Node;\n};\n\n/**\n * Return the offset of the nearest non-whitespace character to `baseOffset`\n * within the string `text`, looking in the `direction` indicated. Return -1 if\n * no non-whitespace character exists between `baseOffset` (inclusive) and the\n * terminus of the string (start or end depending on `direction`).\n */\nfunction closestNonSpaceInString(\n text: string,\n baseOffset: number,\n direction: TrimDirection,\n): number {\n const nextChar =\n direction === TrimDirection.Forwards ? baseOffset : baseOffset - 1;\n if (text.charAt(nextChar).trim() !== '') {\n // baseOffset is already valid: it points at a non-whitespace character\n return baseOffset;\n }\n\n let availableChars: string;\n let availableNonWhitespaceChars: string;\n\n if (direction === TrimDirection.Backwards) {\n availableChars = text.substring(0, baseOffset);\n availableNonWhitespaceChars = availableChars.trimEnd();\n } else {\n availableChars = text.substring(baseOffset);\n availableNonWhitespaceChars = availableChars.trimStart();\n }\n\n if (!availableNonWhitespaceChars.length) {\n return -1;\n }\n\n const offsetDelta =\n availableChars.length - availableNonWhitespaceChars.length;\n\n return direction === TrimDirection.Backwards\n ? baseOffset - offsetDelta\n : baseOffset + offsetDelta;\n}\n\n/**\n * Calculate a new Range start position (TrimDirection.Forwards) or end position\n * (Backwards) for `range` that represents the nearest non-whitespace character,\n * moving into the `range` away from the relevant initial boundary node towards\n * the terminating boundary node.\n *\n * @throws {RangeError} If no text node with non-whitespace characters found\n */\nfunction closestNonSpaceInRange(\n range: Range,\n direction: TrimDirection,\n): RangePosition {\n const nodeIter =\n range.commonAncestorContainer.ownerDocument!.createNodeIterator(\n range.commonAncestorContainer,\n NodeFilter.SHOW_TEXT,\n );\n\n const initialBoundaryNode =\n direction === TrimDirection.Forwards\n ? range.startContainer\n : range.endContainer;\n\n const terminalBoundaryNode =\n direction === TrimDirection.Forwards\n ? range.endContainer\n : range.startContainer;\n\n let currentNode = nodeIter.nextNode();\n\n // Advance the NodeIterator to the `initialBoundaryNode`\n while (currentNode && currentNode !== initialBoundaryNode) {\n currentNode = nodeIter.nextNode();\n }\n\n if (direction === TrimDirection.Backwards) {\n // Reverse the NodeIterator direction. This will return the same node\n // as the previous `nextNode()` call (initial boundary node).\n currentNode = nodeIter.previousNode();\n }\n\n let trimmedOffset = -1;\n\n const advance = () => {\n currentNode =\n direction === TrimDirection.Forwards\n ? nodeIter.nextNode()\n : nodeIter.previousNode();\n\n if (currentNode) {\n const nodeText = currentNode.textContent!;\n const baseOffset =\n direction === TrimDirection.Forwards ? 0 : nodeText.length;\n trimmedOffset = closestNonSpaceInString(nodeText, baseOffset, direction);\n }\n };\n\n while (\n currentNode &&\n trimmedOffset === -1 &&\n currentNode !== terminalBoundaryNode\n ) {\n advance();\n }\n\n if (currentNode && trimmedOffset >= 0) {\n return { node: currentNode, offset: trimmedOffset };\n }\n /* istanbul ignore next */\n throw new RangeError('No text nodes with non-whitespace text found in range');\n}\n\n/**\n * Return a new DOM Range that adjusts the start and end positions of `range` as\n * needed such that:\n *\n * - `startContainer` and `endContainer` text nodes both contain at least one\n * non-whitespace character within the Range's text content\n * - `startOffset` and `endOffset` both reference non-whitespace characters,\n * with `startOffset` immediately before the first non-whitespace character\n * and `endOffset` immediately after the last\n *\n * Whitespace characters are those that are removed by `String.prototype.trim()`\n *\n * @param range - A DOM Range that whose `startContainer` and `endContainer` are\n * both text nodes, and which contains at least one non-whitespace character.\n * @throws {RangeError}\n */\nexport function trimRange(range: Range): Range {\n if (!range.toString().trim().length) {\n throw new RangeError('Range contains no non-whitespace text');\n }\n if (range.startContainer.nodeType !== Node.TEXT_NODE) {\n throw new RangeError('Range startContainer is not a text node');\n }\n if (range.endContainer.nodeType !== Node.TEXT_NODE) {\n throw new RangeError('Range endContainer is not a text node');\n }\n\n const trimmedRange = range.cloneRange();\n\n let startTrimmed = false;\n let endTrimmed = false;\n\n const trimmedOffsets = {\n start: closestNonSpaceInString(\n range.startContainer.textContent!,\n range.startOffset,\n TrimDirection.Forwards,\n ),\n end: closestNonSpaceInString(\n range.endContainer.textContent!,\n range.endOffset,\n TrimDirection.Backwards,\n ),\n };\n\n if (trimmedOffsets.start >= 0) {\n trimmedRange.setStart(range.startContainer, trimmedOffsets.start);\n startTrimmed = true;\n }\n\n // Note: An offset of 0 is invalid for an end offset, as no text in the\n // node would be included in the range.\n if (trimmedOffsets.end > 0) {\n trimmedRange.setEnd(range.endContainer, trimmedOffsets.end);\n endTrimmed = true;\n }\n\n if (startTrimmed && endTrimmed) {\n return trimmedRange;\n }\n\n if (!startTrimmed) {\n // There are no (non-whitespace) characters between `startOffset` and the\n // end of the `startContainer` node.\n const { node, offset } = closestNonSpaceInRange(\n trimmedRange,\n TrimDirection.Forwards,\n );\n\n if (node && offset >= 0) {\n trimmedRange.setStart(node, offset);\n }\n }\n\n if (!endTrimmed) {\n // There are no (non-whitespace) characters between the start of the Range's\n // `endContainer` text content and the `endOffset`.\n const { node, offset } = closestNonSpaceInRange(\n trimmedRange,\n TrimDirection.Backwards,\n );\n\n if (node && offset > 0) {\n trimmedRange.setEnd(node, offset);\n }\n }\n\n return trimmedRange;\n}\n","import { trimRange } from './trim-range';\n\n/**\n * Return the combined length of text nodes contained in `node`.\n */\nfunction nodeTextLength(node: Node): number {\n switch (node.nodeType) {\n case Node.ELEMENT_NODE:\n case Node.TEXT_NODE:\n // nb. `textContent` excludes text in comments and processing instructions\n // when called on a parent element, so we don't need to subtract that here.\n\n return node.textContent?.length ?? 0;\n default:\n return 0;\n }\n}\n\n/**\n * Return the total length of the text of all previous siblings of `node`.\n */\nfunction previousSiblingsTextLength(node: Node): number {\n let sibling = node.previousSibling;\n let length = 0;\n while (sibling) {\n length += nodeTextLength(sibling);\n sibling = sibling.previousSibling;\n }\n return length;\n}\n\n/**\n * Resolve one or more character offsets within an element to (text node,\n * position) pairs.\n *\n * @param element\n * @param offsets - Offsets, which must be sorted in ascending order\n * @throws {RangeError}\n */\nfunction resolveOffsets(\n element: Element,\n ...offsets: number[]\n): Array<{ node: Text; offset: number }> {\n let nextOffset = offsets.shift();\n const nodeIter = element.ownerDocument.createNodeIterator(\n element,\n NodeFilter.SHOW_TEXT,\n );\n const results = [];\n\n let currentNode = nodeIter.nextNode() as Text | null;\n let textNode;\n let length = 0;\n\n // Find the text node containing the `nextOffset`th character from the start\n // of `element`.\n while (nextOffset !== undefined && currentNode) {\n textNode = currentNode;\n if (length + textNode.data.length > nextOffset) {\n results.push({ node: textNode, offset: nextOffset - length });\n nextOffset = offsets.shift();\n } else {\n currentNode = nodeIter.nextNode() as Text | null;\n length += textNode.data.length;\n }\n }\n\n // Boundary case.\n while (nextOffset !== undefined && textNode && length === nextOffset) {\n results.push({ node: textNode, offset: textNode.data.length });\n nextOffset = offsets.shift();\n }\n\n if (nextOffset !== undefined) {\n throw new RangeError('Offset exceeds text length');\n }\n\n return results;\n}\n\n/**\n * When resolving a TextPosition, specifies the direction to search for the\n * nearest text node if `offset` is `0` and the element has no text.\n */\nexport enum ResolveDirection {\n FORWARDS = 1,\n BACKWARDS,\n}\n\n/**\n * Represents an offset within the text content of an element.\n *\n * This position can be resolved to a specific descendant node in the current\n * DOM subtree of the element using the `resolve` method.\n */\nexport class TextPosition {\n public element: Element;\n public offset: number;\n\n constructor(element: Element, offset: number) {\n if (offset < 0) {\n throw new Error('Offset is invalid');\n }\n\n /** Element that `offset` is relative to. */\n this.element = element;\n\n /** Character offset from the start of the element's `textContent`. */\n this.offset = offset;\n }\n\n /**\n * Return a copy of this position with offset relative to a given ancestor\n * element.\n *\n * @param parent - Ancestor of `this.element`\n */\n relativeTo(parent: Element): TextPosition {\n if (!parent.contains(this.element)) {\n throw new Error('Parent is not an ancestor of current element');\n }\n\n let el = this.element;\n let offset = this.offset;\n while (el !== parent) {\n offset += previousSiblingsTextLength(el);\n el = el.parentElement!;\n }\n\n return new TextPosition(el, offset);\n }\n\n /**\n * Resolve the position to a specific text node and offset within that node.\n *\n * Throws if `this.offset` exceeds the length of the element's text. In the\n * case where the element has no text and `this.offset` is 0, the `direction`\n * option determines what happens.\n *\n * Offsets at the boundary between two nodes are resolved to the start of the\n * node that begins at the boundary.\n *\n * @param options.direction - Specifies in which direction to search for the\n * nearest text node if `this.offset` is `0` and\n * `this.element` has no text. If not specified an\n * error is thrown.\n *\n * @throws {RangeError}\n */\n resolve(options: { direction?: ResolveDirection } = {}): {\n node: Text;\n offset: number;\n } {\n try {\n return resolveOffsets(this.element, this.offset)[0];\n } catch (err) {\n if (this.offset === 0 && options.direction !== undefined) {\n const tw = document.createTreeWalker(\n this.element.getRootNode(),\n NodeFilter.SHOW_TEXT,\n );\n tw.currentNode = this.element;\n const forwards = options.direction === ResolveDirection.FORWARDS;\n const text = forwards\n ? (tw.nextNode() as Text | null)\n : (tw.previousNode() as Text | null);\n if (!text) {\n throw err;\n }\n return { node: text, offset: forwards ? 0 : text.data.length };\n } else {\n throw err;\n }\n }\n }\n\n /**\n * Construct a `TextPosition` that refers to the `offset`th character within\n * `node`.\n */\n static fromCharOffset(node: Node, offset: number): TextPosition {\n switch (node.nodeType) {\n case Node.TEXT_NODE:\n return TextPosition.fromPoint(node, offset);\n case Node.ELEMENT_NODE:\n return new TextPosition(node as Element, offset);\n default:\n throw new Error('Node is not an element or text node');\n }\n }\n\n /**\n * Construct a `TextPosition` representing the range start or end point (node, offset).\n *\n * @param node\n * @param offset - Offset within the node\n */\n static fromPoint(node: Node, offset: number): TextPosition {\n switch (node.nodeType) {\n case Node.TEXT_NODE: {\n if (offset < 0 || offset > (node as Text).data.length) {\n throw new Error('Text node offset is out of range');\n }\n\n if (!node.parentElement) {\n throw new Error('Text node has no parent');\n }\n\n // Get the offset from the start of the parent element.\n const textOffset = previousSiblingsTextLength(node) + offset;\n\n return new TextPosition(node.parentElement, textOffset);\n }\n case Node.ELEMENT_NODE: {\n if (offset < 0 || offset > node.childNodes.length) {\n throw new Error('Child node offset is out of range');\n }\n\n // Get the text length before the `offset`th child of element.\n let textOffset = 0;\n for (let i = 0; i < offset; i++) {\n textOffset += nodeTextLength(node.childNodes[i]);\n }\n\n return new TextPosition(node as Element, textOffset);\n }\n default:\n throw new Error('Point is not in an element or text node');\n }\n }\n}\n\n/**\n * Represents a region of a document as a (start, end) pair of `TextPosition` points.\n *\n * Representing a range in this way allows for changes in the DOM content of the\n * range which don't affect its text content, without affecting the text content\n * of the range itself.\n */\nexport class TextRange {\n public start: TextPosition;\n public end: TextPosition;\n\n constructor(start: TextPosition, end: TextPosition) {\n this.start = start;\n this.end = end;\n }\n\n /**\n * Create a new TextRange whose `start` and `end` are computed relative to\n * `element`. `element` must be an ancestor of both `start.element` and\n * `end.element`.\n */\n relativeTo(element: Element): TextRange {\n return new TextRange(\n this.start.relativeTo(element),\n this.end.relativeTo(element),\n );\n }\n\n /**\n * Resolve this TextRange to a (DOM) Range.\n *\n * The resulting DOM Range will always start and end in a `Text` node.\n * Hence `TextRange.fromRange(range).toRange()` can be used to \"shrink\" a\n * range to the text it contains.\n *\n * May throw if the `start` or `end` positions cannot be resolved to a range.\n */\n toRange(): Range {\n let start;\n let end;\n\n if (\n this.start.element === this.end.element &&\n this.start.offset <= this.end.offset\n ) {\n // Fast path for start and end points in same element.\n [start, end] = resolveOffsets(\n this.start.element,\n this.start.offset,\n this.end.offset,\n );\n } else {\n start = this.start.resolve({\n direction: ResolveDirection.FORWARDS,\n });\n end = this.end.resolve({ direction: ResolveDirection.BACKWARDS });\n }\n\n const range = new Range();\n range.setStart(start.node, start.offset);\n range.setEnd(end.node, end.offset);\n return range;\n }\n\n /**\n * Create a TextRange from a (DOM) Range\n */\n static fromRange(range: Range): TextRange {\n const start = TextPosition.fromPoint(\n range.startContainer,\n range.startOffset,\n );\n const end = TextPosition.fromPoint(range.endContainer, range.endOffset);\n return new TextRange(start, end);\n }\n\n /**\n * Create a TextRange representing the `start`th to `end`th characters in\n * `root`\n */\n static fromOffsets(root: Element, start: number, end: number): TextRange {\n return new TextRange(\n new TextPosition(root, start),\n new TextPosition(root, end),\n );\n }\n\n /**\n * Return a new Range representing `range` trimmed of any leading or trailing\n * whitespace\n */\n static trimmedRange(range: Range): Range {\n return trimRange(TextRange.fromRange(range).toRange());\n }\n}\n","/**\n * CSS selector that will match the placeholder within a page/tile container.\n */\nconst placeholderSelector = '.annotator-placeholder';\n\n/**\n * Create or return a placeholder element for anchoring.\n *\n * In document viewers such as PDF.js which only render a subset of long\n * documents at a time, it may not be possible to anchor annotations to the\n * actual text in pages which are off-screen. For these non-rendered pages,\n * a \"placeholder\" element is created in the approximate X/Y location (eg.\n * middle of the page) where the content will appear. Any highlights for that\n * page are then rendered inside the placeholder.\n *\n * When the viewport is scrolled to the non-rendered page, the placeholder\n * is removed and annotations are re-anchored to the real content.\n *\n * @param container - The container element for the page or tile which is not\n * rendered.\n */\nexport function createPlaceholder(container: HTMLElement) {\n let placeholder = container.querySelector(placeholderSelector);\n if (placeholder) {\n return placeholder;\n }\n placeholder = document.createElement('span');\n placeholder.classList.add('annotator-placeholder');\n placeholder.textContent = 'Loading annotations...';\n container.appendChild(placeholder);\n return placeholder;\n}\n\n/**\n * Return true if a page/tile container has a placeholder.\n */\nexport function hasPlaceholder(container: HTMLElement): boolean {\n return container.querySelector(placeholderSelector) !== null;\n}\n\n/**\n * Remove the placeholder element in `container`, if present.\n */\nexport function removePlaceholder(container: HTMLElement) {\n container.querySelector(placeholderSelector)?.remove();\n}\n\n/**\n * Return true if `node` is inside a placeholder element created with `createPlaceholder`.\n *\n * This is typically used to test if a highlight element associated with an\n * anchor is inside a placeholder.\n */\nexport function isInPlaceholder(node: Node): boolean {\n if (!node.parentElement) {\n return false;\n }\n return node.parentElement.closest(placeholderSelector) !== null;\n}\n","import { nodeIsText } from './util/node';\n\n/**\n * Return a range that spans from the earlier of a or b's start point to\n * the later of a or b's end point, in document order.\n */\nfunction unionRanges(a: Range, b: Range): Range {\n const result = new Range();\n\n if (a.compareBoundaryPoints(Range.START_TO_START, b) <= 0) {\n result.setStart(a.startContainer, a.startOffset);\n } else {\n result.setStart(b.startContainer, b.startOffset);\n }\n\n if (a.compareBoundaryPoints(Range.END_TO_END, b) >= 0) {\n result.setEnd(a.endContainer, a.endOffset);\n } else {\n result.setEnd(b.endContainer, b.endOffset);\n }\n\n return result;\n}\n\n/**\n * Return the currently selected {@link Range} or `null` if there is no\n * selection.\n */\nexport function selectedRange(\n selection: Selection | null = document.getSelection(),\n): Range | null {\n if (!selection || selection.rangeCount === 0) {\n return null;\n }\n\n let range = selection.getRangeAt(0);\n\n // Work around a Firefox issue [1] where a selection can have multiple ranges,\n // in contradiction to the Selection API [2] spec. The workaround is to\n // union the ranges to produce the same single range as other browsers.\n //\n // [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1773065\n // [2] https://w3c.github.io/selection-api/#dom-selection-rangecount\n for (let i = 1; i < selection.rangeCount; i++) {\n range = unionRanges(range, selection.getRangeAt(i));\n }\n\n if (range.collapsed) {\n return null;\n }\n return range;\n}\n\n/**\n * Returns true if the start point of a selection occurs after the end point,\n * in document order.\n */\nexport function isSelectionBackwards(selection: Selection) {\n if (selection.focusNode === selection.anchorNode) {\n return selection.focusOffset < selection.anchorOffset;\n }\n\n const range = selectedRange(selection)!;\n\n // Does not work correctly on iOS when selecting nodes backwards.\n // https://bugs.webkit.org/show_bug.cgi?id=220523\n return range.startContainer === selection.focusNode;\n}\n\n/**\n * Returns true if any part of `node` lies within `range`.\n */\nexport function isNodeInRange(range: Range, node: Node) {\n try {\n const length = node.nodeValue?.length ?? node.childNodes.length;\n return (\n // Check start of node is before end of range.\n range.comparePoint(node, 0) <= 0 &&\n // Check end of node is after start of range.\n range.comparePoint(node, length) >= 0\n );\n } catch {\n // `comparePoint` may fail if the `range` and `node` do not share a common\n // ancestor or `node` is a doctype.\n return false;\n }\n}\n\n/**\n * Iterate over all Node(s) which overlap `range` in document order and invoke\n * `callback` for each of them.\n */\nexport function forEachNodeInRange(range: Range, callback: (n: Node) => void) {\n const root = range.commonAncestorContainer;\n const nodeIter: NodeIterator = root.ownerDocument!.createNodeIterator(\n root,\n NodeFilter.SHOW_ALL,\n );\n\n let currentNode;\n while ((currentNode = nodeIter.nextNode())) {\n if (isNodeInRange(range, currentNode)) {\n callback(currentNode);\n }\n }\n}\n\nfunction textNodeContainsText(textNode: Text): boolean {\n const whitespaceOnly = /^\\s*$/;\n return !textNode.textContent!.match(whitespaceOnly);\n}\n\n/**\n * Returns the bounding rectangles of non-whitespace text nodes in `range`.\n *\n * @return Array of bounding rects in viewport coordinates.\n */\nexport function getTextBoundingBoxes(range: Range): DOMRect[] {\n const textNodes: Text[] = [];\n forEachNodeInRange(range, node => {\n if (nodeIsText(node) && textNodeContainsText(node)) {\n textNodes.push(node);\n }\n });\n\n return textNodes.flatMap(node => {\n const nodeRange = node.ownerDocument.createRange();\n nodeRange.selectNodeContents(node);\n if (node === range.startContainer) {\n nodeRange.setStart(node, range.startOffset);\n }\n if (node === range.endContainer) {\n nodeRange.setEnd(node, range.endOffset);\n }\n if (nodeRange.collapsed) {\n // If the range ends at the start of this text node or starts at the end\n // of this node then do not include it.\n return [];\n }\n\n // Measure the range and translate from viewport to document coordinates\n const viewportRects = Array.from(nodeRange.getClientRects());\n nodeRange.detach();\n return viewportRects;\n });\n}\n\n/**\n * Returns the rectangle, in viewport coordinates, for the line of text\n * containing the focus point of a Selection.\n *\n * Returns null if the selection is empty.\n */\nexport function selectionFocusRect(selection: Selection): DOMRect | null {\n const range = selectedRange(selection);\n if (!range) {\n return null;\n }\n const textBoxes = getTextBoundingBoxes(range);\n if (textBoxes.length === 0) {\n return null;\n }\n\n if (isSelectionBackwards(selection)) {\n return textBoxes[0];\n } else {\n return textBoxes[textBoxes.length - 1];\n }\n}\n\n/**\n * Retrieve a set of items associated with nodes in a given range.\n *\n * An `item` can be any data that the caller wishes to compute from or associate\n * with a node. Only unique items, as determined by `Object.is`, are returned.\n *\n * @param itemForNode - Callback returning the item for a given node\n */\nexport function itemsForRange<T>(\n range: Range,\n itemForNode: (n: Node) => NonNullable<T> | null | undefined,\n): NonNullable<T>[] {\n const checkedNodes = new Set<Node>();\n const items = new Set<NonNullable<T>>();\n\n forEachNodeInRange(range, (current: Node | null) => {\n while (current) {\n if (checkedNodes.has(current)) {\n break;\n }\n checkedNodes.add(current);\n\n const item = itemForNode(current);\n if (item !== null && item !== undefined) {\n items.add(item);\n }\n\n current = current.parentNode;\n }\n });\n\n return [...items];\n}\n","import classnames from 'classnames';\n\nimport { generateHexString } from '../shared/random';\nimport type { ShapeAnchor } from '../types/annotator';\nimport type { HighlightCluster } from '../types/shared';\nimport { isInPlaceholder } from './anchoring/placeholder';\nimport { isNodeInRange } from './range-util';\n\nconst SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n\ntype HighlightProps = {\n // Associated SVG rect drawn to represent this highlight (in PDFs)\n svgHighlight?: SVGRectElement;\n};\n\nexport type HighlightElement = HTMLElement & HighlightProps;\n\nexport const clusterValues: HighlightCluster[] = [\n 'user-annotations',\n 'user-highlights',\n 'other-content',\n];\n\n/**\n * Return the canvas element underneath a highlight element in a PDF page's\n * text layer.\n *\n * Returns `null` if the highlight is not above a PDF canvas.\n */\nfunction getPDFCanvas(highlightEl: HighlightElement): HTMLCanvasElement | null {\n // This code assumes that PDF.js renders pages with a structure like:\n //\n // <div class=\"page\">\n // <div class=\"canvasWrapper\">\n // <canvas></canvas> <!-- The rendered PDF page -->\n // </div>\n // <div class=\"textLayer\">\n // <!-- Transparent text layer with text spans used to enable text selection -->\n // </div>\n // </div>\n //\n // It also assumes that the `highlightEl` element is somewhere under\n // the `.textLayer` div.\n\n const pageEl = highlightEl.closest('.page');\n if (!pageEl) {\n return null;\n }\n\n const canvasEl = pageEl.querySelector('.canvasWrapper > canvas');\n if (!canvasEl) {\n return null;\n }\n\n return canvasEl as HTMLCanvasElement;\n}\n\n/**\n * Draw highlights in an SVG layer overlaid on top of a PDF.js canvas.\n *\n * The created SVG elements are stored in the `svgHighlight` property of\n * each `HighlightElement`.\n *\n * @param highlightEls -\n * An element that wraps the highlighted text in the transparent text layer\n * above the PDF.\n * @param [cssClass] - CSS class(es) to add to the SVG highlight elements\n */\nfunction drawHighlightsAbovePDFCanvas(\n highlightEls: HighlightElement[],\n cssClass?: string,\n) {\n if (highlightEls.length === 0) {\n return;\n }\n\n // Get the <canvas> for the PDF page containing the highlight. We assume all\n // the highlights are on the same page.\n const canvasEl = getPDFCanvas(highlightEls[0]);\n if (!canvasEl || !canvasEl.parentElement) {\n return;\n }\n\n let svgHighlightLayer = canvasEl.parentElement.querySelector(\n '.hypothesis-highlight-layer',\n ) as SVGSVGElement | null;\n\n if (!svgHighlightLayer) {\n // Create SVG layer. This must be in the same stacking context as\n // the canvas so that CSS `mix-blend-mode` can be used to control how SVG\n // content blends with the canvas below.\n svgHighlightLayer = document.createElementNS(SVG_NAMESPACE, 'svg');\n svgHighlightLayer.setAttribute('class', 'hypothesis-highlight-layer');\n canvasEl.parentElement.appendChild(svgHighlightLayer);\n\n // Overlay SVG layer above canvas.\n canvasEl.parentElement.style.position = 'relative';\n\n const svgStyle = svgHighlightLayer.style;\n svgStyle.position = 'absolute';\n svgStyle.left = '0';\n svgStyle.top = '0';\n svgStyle.width = '100%';\n svgStyle.height = '100%';\n\n // Use multiply blending so that highlights drawn on top of text darken it\n // rather than making it lighter. This improves contrast and thus readability\n // of highlighted text, especially for overlapping highlights.\n //\n // This choice optimizes for the common case of dark text on a light background.\n svgStyle.mixBlendMode = 'multiply';\n }\n\n const canvasRect = canvasEl.getBoundingClientRect();\n const highlightRects = highlightEls.map(highlightEl => {\n const highlightRect = highlightEl.getBoundingClientRect();\n\n // Create SVG element for the current highlight element.\n const rect = document.createElementNS(SVG_NAMESPACE, 'rect');\n rect.setAttribute('x', (highlightRect.left - canvasRect.left).toString());\n rect.setAttribute('y', (highlightRect.top - canvasRect.top).toString());\n rect.setAttribute('width', highlightRect.width.toString());\n rect.setAttribute('height', highlightRect.height.toString());\n rect.setAttribute(\n 'class',\n classnames('hypothesis-svg-highlight', cssClass),\n );\n\n // Make the highlight in the text layer transparent.\n highlightEl.classList.add('is-transparent');\n\n // Associate SVG element with highlight for use by `removeHighlights`.\n highlightEl.svgHighlight = rect;\n\n return rect;\n });\n\n svgHighlightLayer.append(...highlightRects);\n}\n\n/**\n * Return text nodes which are entirely inside `range`.\n *\n * If a range starts or ends part-way through a text node, the node is split\n * and the part inside the range is returned.\n */\nfunction wholeTextNodesInRange(range: Range): Text[] {\n if (range.collapsed) {\n // Exit early for an empty range to avoid an edge case that breaks the algorithm\n // below. Splitting a text node at the start of an empty range can leave the\n // range ending in the left part rather than the right part.\n return [];\n }\n\n let root = range.commonAncestorContainer as Node | null;\n if (root && root.nodeType !== Node.ELEMENT_NODE) {\n // If the common ancestor is not an element, set it to the parent element to\n // ensure that the loop below visits any text nodes generated by splitting\n // the common ancestor.\n //\n // Note that `parentElement` may be `null`.\n root = root.parentElement;\n }\n if (!root) {\n // If there is no root element then we won't be able to insert highlights,\n // so exit here.\n return [];\n }\n\n const textNodes = [];\n const nodeIter = root!.ownerDocument!.createNodeIterator(\n root,\n NodeFilter.SHOW_TEXT, // Only return `Text` nodes.\n );\n let node;\n while ((node = nodeIter.nextNode())) {\n if (!isNodeInRange(range, node)) {\n continue;\n }\n const text = node as Text;\n\n if (text === range.startContainer && range.startOffset > 0) {\n // Split `text` where the range starts. The split will create a new `Text`\n // node which will be in the range and will be visited in the next loop iteration.\n text.splitText(range.startOffset);\n continue;\n }\n\n if (text === range.endContainer && range.endOffset < text.data.length) {\n // Split `text` where the range ends, leaving it as the part in the range.\n text.splitText(range.endOffset);\n }\n\n textNodes.push(text);\n }\n\n return textNodes;\n}\n\n/**\n * Create highlights for an annotated region defined by a shape.\n */\nexport function highlightShape(region: ShapeAnchor): HighlightElement[] {\n const { shape, anchor } = region;\n\n const anchorStyles = getComputedStyle(anchor);\n const borderWidth = parseInt(anchorStyles.borderWidth);\n\n // \"Outer\" rect of the anchor that includes the border.\n const anchorOuterBox = anchor.getBoundingClientRect();\n\n // Inner rect of the anchor that does not include the border.\n const anchorBox = new DOMRect(\n anchorOuterBox.left,\n anchorOuterBox.top,\n anchorOuterBox.width - 2 * borderWidth,\n anchorOuterBox.height - 2 * borderWidth,\n );\n\n const highlightEl = document.createElement('hypothesis-highlight');\n\n // Should match the width used by the `hypothesis-shape-highlight` class.\n const highlightBorderWidth = 3;\n highlightEl.className = 'hypothesis-shape-highlight';\n\n if (shape.type === 'rect') {\n const left = shape.left * anchorBox.width - borderWidth;\n const top = shape.top * anchorBox.height - borderWidth;\n const width = (shape.right - shape.left) * anchorBox.width;\n const height = (shape.bottom - shape.top) * anchorBox.height;\n highlightEl.style.left = `${left}px`;\n highlightEl.style.top = `${top}px`;\n highlightEl.style.width = `${width - 2 * highlightBorderWidth}px`;\n highlightEl.style.height = `${height - 2 * highlightBorderWidth}px`;\n } else if (shape.type === 'point') {\n const x = shape.x * anchorBox.width - borderWidth;\n const y = shape.y * anchorBox.height - borderWidth;\n highlightEl.style.left = `${x}px`;\n highlightEl.style.top = `${y}px`;\n highlightEl.style.width = '10px';\n highlightEl.style.height = '10px';\n }\n\n anchor.append(highlightEl);\n\n return [highlightEl];\n}\n\n/**\n * Wraps the DOM Nodes within the provided range with a highlight\n * element of the specified class and returns the highlight Elements.\n *\n * @param range - Range to be highlighted\n * @param [cssClass] - CSS class(es) to add to the highlight elements\n * @return Elements wrapping text in `normedRange` to add a highlight effect\n */\nexport function highlightRange(\n range: Range,\n cssClass?: string,\n): HighlightElement[] {\n const textNodes = wholeTextNodesInRange(range);\n\n // Check if this range refers to a placeholder for not-yet-rendered content in\n // a PDF. These highlights should be invisible.\n const inPlaceholder = textNodes.length > 0 && isInPlaceholder(textNodes[0]);\n\n // Group text nodes into spans of adjacent nodes. If a group of text nodes are\n // adjacent, we only need to create one highlight element for the group.\n let textNodeSpans: Text[][] = [];\n let prevNode: Node | null = null;\n let currentSpan = null;\n\n textNodes.forEach(node => {\n if (prevNode && prevNode.nextSibling === node) {\n currentSpan.push(node);\n } else {\n currentSpan = [node];\n textNodeSpans.push(currentSpan);\n }\n prevNode = node;\n });\n\n // Filter out text node spans that consist only of white space. This avoids\n // inserting highlight elements in places that can only contain a restricted\n // subset of nodes such as table rows and lists.\n const whitespace = /^\\s*$/;\n textNodeSpans = textNodeSpans.filter(span => {\n const parentElement = span[0].parentElement;\n return (\n // Whitespace <span>s should be highlighted since they affect layout in\n // some code editors\n (parentElement?.childNodes.length === 1 &&\n parentElement?.tagName === 'SPAN') ||\n // Otherwise ignore white-space only Text node spans\n span.some(node => !whitespace.test(node.data))\n );\n });\n\n // Wrap each text node span with a `<hypothesis-highlight>` element.\n const highlights: HighlightElement[] = [];\n textNodeSpans.forEach(nodes => {\n // A custom element name is used here rather than `<span>` to reduce the\n // likelihood of highlights being hidden by page styling.\n\n const highlightEl = document.createElement('hypothesis-highlight');\n highlightEl.className = classnames('hypothesis-highlight', cssClass);\n\n const parent = nodes[0].parentNode as ParentNode;\n parent.replaceChild(highlightEl, nodes[0]);\n nodes.forEach(node => highlightEl.appendChild(node));\n\n highlights.push(highlightEl);\n });\n\n // For PDF highlights, create the highlight effect by using an SVG placed\n // above the page's canvas rather than CSS `background-color` on the highlight\n // element. This enables more control over blending of the highlight with the\n // content below.\n //\n // Drawing these SVG highlights involves measuring the `<hypothesis-highlight>`\n // elements, so we create them only after those elements have all been created\n // to reduce the number of forced reflows. We also skip creating them for\n // unrendered pages for performance reasons.\n if (!inPlaceholder) {\n drawHighlightsAbovePDFCanvas(highlights, cssClass);\n }\n\n return highlights;\n}\n\n/**\n * Replace a child `node` with `replacements`.\n *\n * nb. This is like `ChildNode.replaceWith` but it works in older browsers.\n */\nfunction replaceWith(node: ChildNode, replacements: Node[]) {\n const parent = node.parentNode as ParentNode;\n replacements.forEach(r => parent.insertBefore(r, node));\n node.remove();\n}\n\n/**\n * Remove all highlights under a given root element.\n */\nexport function removeAllHighlights(root: HTMLElement) {\n const highlights = Array.from(root.querySelectorAll('hypothesis-highlight'));\n removeHighlights(highlights as HighlightElement[]);\n}\n\n/**\n * Remove highlights from a range previously highlighted with `highlightRange`.\n */\nexport function removeHighlights(highlights: HighlightElement[]) {\n // Explicitly un-focus highlights to be removed. This ensures associated\n // focused elements are removed from the document.\n setHighlightsFocused(highlights, false);\n for (const h of highlights) {\n if (h.parentNode) {\n const children = Array.from(h.childNodes);\n replaceWith(h, children);\n }\n if (h.svgHighlight) {\n h.svgHighlight.remove();\n }\n }\n}\n\n/**\n * Focus or un-focus an individual SVG highlight element.\n *\n * When focusing an SVG highlight, make sure it is not obscured by other SVG\n * highlight elements. As SVG highlights are siblings, this can be accomplished\n * by putting the highlight at the end the set of highlights contained its\n * parent. SVG highlight elements are cloned instead of moved so that their\n * original stacking (nesting) order is not lost when later unfocused. A data\n * attribute is added to associate the original SVG highlight element with its\n * clone.\n */\nfunction setSVGHighlightFocused(svgEl: SVGElement, focused: boolean) {\n const parent = svgEl.parentNode as SVGElement;\n // This attribute allows lookup of an associated, \"focused\" element. It is\n // set if the highlight is already focused.\n const focusedId = svgEl.getAttribute('data-focused-id');\n\n const isFocused = Boolean(focusedId);\n if (isFocused === focused) {\n return;\n }\n\n if (focused) {\n svgEl.setAttribute('data-focused-id', generateHexString(8));\n const focusedHighlight = svgEl.cloneNode() as SVGElement;\n // The cloned element will include the `data-focused-id` attribute\n // for association with its original highlight. Set additional attribute\n // to mark this as the focused clone of a highlight.\n focusedHighlight.setAttribute('data-is-focused', 'data-is-focused');\n parent.append(focusedHighlight);\n } else {\n const focusedHighlight = parent.querySelector(\n `[data-focused-id=\"${focusedId}\"][data-is-focused]`,\n );\n focusedHighlight?.remove();\n svgEl.removeAttribute('data-focused-id');\n }\n}\n\n/**\n * Set whether the given highlight elements should appear \"focused\".\n *\n * A highlight can be displayed in a different (\"focused\") style to indicate\n * that it is current in some other context - for example the user has selected\n * the corresponding annotation in the sidebar.\n */\nexport function setHighlightsFocused(\n highlights: HighlightElement[],\n focused: boolean,\n) {\n highlights.forEach(h => {\n // In PDFs the visible highlight is created by an SVG element, so the focused\n // effect is applied to that. In other documents the effect is applied to the\n // `<hypothesis-highlight>` element.\n if (h.svgHighlight) {\n setSVGHighlightFocused(h.svgHighlight, focused);\n } else {\n h.classList.toggle('hypothesis-highlight-focused', focused);\n }\n });\n}\n\n/**\n * Set whether highlights under the given root element should be visible.\n */\nexport function setHighlightsVisible(root: HTMLElement, visible: boolean) {\n const showHighlightsClass = 'hypothesis-highlights-always-on';\n root.classList.toggle(showHighlightsClass, visible);\n}\n\n/**\n * Get the highlight elements at the given client coordinates.\n */\nexport function getHighlightsFromPoint(\n x: number,\n y: number,\n): HighlightElement[] {\n // Text highlights can be found via `elementsFromPoint`.\n const textHighlights = document\n .elementsFromPoint(x, y)\n .filter(\n el => el.localName === 'hypothesis-highlight',\n ) as HighlightElement[];\n\n // Shape highlights have `pointer-events: none` so users can interact with\n // the content underneath. This makes them invisible to `elementsFromPoint`.\n // To handle this test each shape highlight individually.\n const shapeHighlights = [];\n for (const highlight of document.querySelectorAll(\n 'hypothesis-highlight.hypothesis-shape-highlight',\n )) {\n // Approximate the shape by its bounding rect. This works for the shapes we\n // currently support, but won't work for more complex shapes (eg.\n // arbitrary polygons) that we might introduce in future.\n const rect = highlight.getBoundingClientRect();\n if (x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom) {\n shapeHighlights.push(highlight as HighlightElement);\n }\n }\n\n return [...textHighlights, ...shapeHighlights];\n}\n\n// Subset of `DOMRect` interface\ntype Rect = {\n top: number;\n left: number;\n bottom: number;\n right: number;\n};\n\n/**\n * Get the bounding client rectangle of a collection in viewport coordinates.\n * Unfortunately, Chrome has issues ([1]) with Range.getBoundingClient rect or we\n * could just use that.\n *\n * [1] https://bugs.chromium.org/p/chromium/issues/detail?id=324437\n */\nexport function getBoundingClientRect(collection: HTMLElement[]): Rect {\n // Reduce the client rectangles of the highlights to a bounding box\n const rects = collection.map(n => n.getBoundingClientRect() as Rect);\n return rects.reduce((acc, r) => ({\n top: Math.min(acc.top, r.top),\n left: Math.min(acc.left, r.left),\n bottom: Math.max(acc.bottom, r.bottom),\n right: Math.max(acc.right, r.right),\n }));\n}\n\n/**\n * Add metadata and manipulate ordering of all highlights in `element` to\n * allow styling of nested, clustered highlights.\n */\nexport function updateClusters(element: Element) {\n setNestingData(getHighlights(element));\n updateSVGHighlightOrdering(element);\n}\n\n/**\n * Is `el` a highlight element? Work around inconsistency between HTML documents\n * (`tagName` is upper-case) and XHTML documents (`tagName` is lower-case)\n */\nconst isHighlightElement = (el: Element): boolean =>\n el.tagName.toLowerCase() === 'hypothesis-highlight';\n\n/**\n * Return the closest generation of HighlightElements to `element`.\n *\n * If `element` is itself a HighlightElement, return immediate children that\n * are also HighlightElements.\n *\n * Otherwise, return all HighlightElements that have no parent HighlightElement,\n * i.e. the outermost highlights within `element`.\n */\nfunction getHighlights(element: Element) {\n let highlights;\n if (isHighlightElement(element)) {\n highlights = Array.from(element.children).filter(isHighlightElement);\n } else {\n highlights = Array.from(\n element.getElementsByTagName('hypothesis-highlight'),\n ).filter(\n highlight =>\n !highlight.parentElement ||\n !isHighlightElement(highlight.parentElement),\n );\n }\n return highlights as HighlightElement[];\n}\n\n/**\n * Get all of the SVG highlights within `root`, grouped by layer. A PDF\n * document may have multiple layers of SVG highlights, typically one per page.\n *\n * @return a Map of layer Elements to all SVG highlights within that\n * layer Element\n */\nfunction getSVGHighlights(root?: Element): Map<Element, HighlightElement[]> {\n const svgHighlights: Map<Element, HighlightElement[]> = new Map();\n\n for (const layer of (root ?? document).getElementsByClassName(\n 'hypothesis-highlight-layer',\n )) {\n svgHighlights.set(\n layer,\n Array.from(\n layer.querySelectorAll('.hypothesis-svg-highlight'),\n ) as HighlightElement[],\n );\n }\n\n return svgHighlights;\n}\n\n/**\n * Walk a tree of <hypothesis-highlight> elements, adding `data-nesting-level`\n * and `data-cluster-level` data attributes to <hypothesis-highlight>s and\n * their associated SVG highlight (<rect>) elements.\n *\n * - `data-nesting-level` - generational depth of the applicable\n * `<hypothesis-highlight>` relative to outermost `<hypothesis-highlight>`.\n * - `data-cluster-level` - number of `<hypothesis-highlight>` generations\n * since the cluster value changed.\n *\n * @param highlightEls - A collection of sibling <hypothesis-highlight>\n * elements\n * @param parentCluster - The cluster value of the parent highlight to\n * `highlightEls`, if any\n * @param nestingLevel - The nesting \"level\", relative to the outermost\n * <hypothesis-highlight> element (0-based)\n * @param parentClusterLevel - The parent's nesting depth, per its cluster\n * value (`parentCluster`). i.e. How many levels since the cluster value\n * changed? This allows for nested styling of highlights of the same cluster\n * value.\n */\nfunction setNestingData(\n highlightEls: HighlightElement[],\n parentCluster = '',\n nestingLevel = 0,\n parentClusterLevel = 0,\n) {\n for (const hEl of highlightEls) {\n const elCluster =\n clusterValues.find(cv => hEl.classList.contains(cv)) ?? 'other-content';\n\n const elClusterLevel =\n parentCluster && elCluster === parentCluster ? parentClusterLevel + 1 : 0;\n\n hEl.setAttribute('data-nesting-level', `${nestingLevel}`);\n hEl.setAttribute('data-cluster-level', `${elClusterLevel}`);\n\n if (hEl.svgHighlight) {\n hEl.svgHighlight.setAttribute('data-nesting-level', `${nestingLevel}`);\n hEl.svgHighlight.setAttribute('data-cluster-level', `${elClusterLevel}`);\n }\n\n setNestingData(\n getHighlights(hEl),\n elCluster /* parentCluster */,\n nestingLevel + 1 /* nestingLevel */,\n elClusterLevel /* parentClusterLevel */,\n );\n }\n}\n\n/**\n * Get the highlight nesting level of `el`. This is typically set with the\n * `data-nesting-level` attribute on highlight elements. Focused SVG highlight\n * elements should always have the highest nesting level — they should always\n * come last when sorted, so as not to be obscured by any other highlights.\n * These elements are indicated by the presence of the `data-is-focused`\n * attribute.\n */\nfunction nestingLevel(el: Element): number {\n if (el.getAttribute('data-is-focused')) {\n return Number.MAX_SAFE_INTEGER;\n }\n return parseInt(el.getAttribute('data-nesting-level') ?? '0', 10);\n}\n\n/**\n * Ensure that SVG <rect> elements are ordered correctly: inner (nested)\n * highlights should be visible on top of outer highlights.\n *\n * All SVG <rect>s drawn for a PDF page are siblings. To ensure that the\n * <rect>s associated with outer highlights don't show up on top of (and thus\n * obscure) nested highlights, order the <rects> by their `data-nesting-level`\n * value if they are not already.\n */\nfunction updateSVGHighlightOrdering(element: Element) {\n for (const [layer, layerHighlights] of getSVGHighlights(element)) {\n const correctlyOrdered = layerHighlights.every((svgEl, idx, allEls) => {\n if (idx === 0) {\n return true;\n }\n return nestingLevel(svgEl) >= nestingLevel(allEls[idx - 1]);\n });\n\n if (!correctlyOrdered) {\n layerHighlights.sort((a, b) => nestingLevel(a) - nestingLevel(b));\n layer.replaceChildren(...layerHighlights);\n }\n }\n}\n","import type { Anchor, AnchorPosition } from '../../types/annotator';\nimport { getBoundingClientRect } from '../highlighter';\n\nexport type Bucket = {\n /** The anchors in this bucket. */\n anchors: AnchorPosition[];\n /** The vertical pixel offset where this bucket should appear in the bucket bar */\n position: number;\n};\n\nexport type BucketSet = {\n /**\n * A single bucket containing all the annotation tags whose anchors are\n * offscreen upwards\n */\n above: Bucket;\n\n /**\n * A single bucket containing all the annotation tags which anchors are\n * offscreen downwards\n */\n below: Bucket;\n\n /** On-screen buckets */\n buckets: Bucket[];\n};\n\nexport type WorkingBucket = {\n /** The anchors in this bucket. */\n anchors: AnchorPosition[];\n\n /**\n * The computed position (offset) for this bucket, based on the current anchors.\n * This is centered between `top` and `bottom`\n */\n position: number;\n\n /**\n * The uppermost (lowest) vertical offset for the anchors in this bucket —\n * the lowest `top` position value, akin to the top offset of a theoretical\n * box drawn around all the anchor highlights in this bucket\n */\n top: number;\n\n /**\n * The bottommost (highest) vertical offset for the anchors in this bucket —\n * the highest `top` position value, akin to the bottom of a theoretical box\n * drawn around all the anchor highlights in this bucket\n */\n bottom: number;\n};\n\n// Generated buckets of annotation anchor highlights should be spaced by\n// at least this amount, in pixels\nconst BUCKET_GAP_SIZE = 60;\n\n/**\n * Compute the top and bottom positions for the set of anchors' highlights, sorted\n * vertically, from top to bottom.\n */\nexport function computeAnchorPositions(anchors: Anchor[]): AnchorPosition[] {\n const positions: AnchorPosition[] = [];\n\n anchors.forEach(({ annotation, highlights }) => {\n if (!highlights?.length) {\n return;\n }\n\n const { top, bottom } = getBoundingClientRect(highlights);\n\n if (top >= bottom) {\n // Empty rect. The highlights may be disconnected from the document or hidden.\n return;\n }\n\n positions.push({\n tag: annotation.$tag,\n top,\n bottom,\n });\n });\n\n // Sort anchors vertically from top to bottom\n positions.sort((anchor1, anchor2) => anchor1.top - anchor2.top);\n\n return positions;\n}\n\n/**\n * Gap between the top/bottom of the container and the top/bottom of buckets.\n */\nexport const BUCKET_BAR_VERTICAL_MARGIN = 30;\n\n/**\n * Group anchors into buckets and determine a suitable vertical position\n * for them within {@link container}.\n *\n * @param anchorPositions - Positions of anchors relative to viewport\n * @param container - Container into which buckets will be rendered\n */\nexport function computeBuckets(\n anchorPositions: AnchorPosition[],\n container: Element,\n): BucketSet {\n const aboveAnchors = [] as AnchorPosition[];\n const belowAnchors = [] as AnchorPosition[];\n const buckets: Bucket[] = [];\n\n // Hold current working anchors and positions as we build each bucket\n let currentBucket: WorkingBucket | null = null;\n\n /**\n * Create a new working bucket based on the provided `AnchorPosition`\n */\n function newBucket(anchor: AnchorPosition): WorkingBucket {\n const { bottom, top } = anchor;\n const anchorHeight = bottom - top;\n const bucketPosition = top + anchorHeight / 2;\n return {\n bottom,\n position: bucketPosition,\n anchors: [anchor],\n top,\n };\n }\n\n const containerRect = container.getBoundingClientRect();\n const vMargin = BUCKET_BAR_VERTICAL_MARGIN;\n\n // Compute positions of buckets relative to bucket bar instead of viewport.\n const relativePositions = anchorPositions.map(aPos => ({\n tag: aPos.tag,\n top: aPos.top - containerRect.top,\n bottom: aPos.bottom - containerRect.top,\n }));\n\n // Build buckets from position information\n for (const aPos of relativePositions) {\n const center = (aPos.top + aPos.bottom) / 2;\n\n if (center < vMargin) {\n aboveAnchors.push(aPos);\n continue;\n } else if (center > containerRect.height - vMargin) {\n belowAnchors.push(aPos);\n continue;\n }\n\n if (!currentBucket) {\n // We've encountered our first on-screen anchor position:\n // We'll need a bucket!\n currentBucket = newBucket(aPos);\n continue;\n }\n // We want to contain overlapping highlights and those near each other\n // within a shared bucket\n const isContainedWithin =\n aPos.top > currentBucket.top && aPos.bottom < currentBucket.bottom;\n\n // The new anchor's position is far enough below the bottom of the current\n // bucket to justify starting a new bucket\n const isLargeGap = aPos.top - currentBucket.bottom > BUCKET_GAP_SIZE;\n\n if (isLargeGap && !isContainedWithin) {\n // We need to start a new bucket; push the working bucket and create\n // a new bucket\n buckets.push(currentBucket);\n currentBucket = newBucket(aPos);\n } else {\n // We'll add this anchor to the current working bucket and update\n // offset properties accordingly.\n // We can be confident that `aPos.top` is >= `currentBucket.top` because\n // AnchorPositions are sorted by their `top` offset — meaning that\n // `currentBucket.top` still accurately represents the `top` offset of\n // the virtual rectangle enclosing all anchors in this bucket. But\n // let's check to see if the bottom is larger/lower:\n const updatedBottom =\n aPos.bottom > currentBucket.bottom ? aPos.bottom : currentBucket.bottom;\n const updatedHeight = updatedBottom - currentBucket.top;\n\n currentBucket.anchors.push(aPos);\n currentBucket.bottom = updatedBottom;\n currentBucket.position = currentBucket.top + updatedHeight / 2;\n }\n }\n\n if (currentBucket) {\n buckets.push(currentBucket);\n }\n\n // Add an upper \"navigation\" bucket with offscreen-above anchors\n const above: Bucket = {\n anchors: aboveAnchors,\n position: vMargin - 5,\n };\n\n // Add a lower \"navigation\" bucket with offscreen-below anchors\n const below: Bucket = {\n anchors: belowAnchors,\n position: containerRect.height - vMargin,\n };\n\n return {\n above,\n below,\n buckets,\n };\n}\n","import { ListenerCollection } from '@hypothesis/frontend-shared';\n\nimport type { PortRPC } from '../shared/messaging';\nimport type { Anchor, Destroyable } from '../types/annotator';\nimport type {\n HostToGuestCalls,\n GuestToHostCalls,\n} from '../types/port-rpc-calls';\nimport { computeAnchorPositions } from './util/buckets';\n\ntype HostRPC = PortRPC<HostToGuestCalls, GuestToHostCalls>;\n\nexport type BucketBarClientOptions = {\n /**\n * The scrollable container element for the document content. All the highlights\n * that the bucket bar's buckets point at should be contained within this element.\n */\n contentContainer: Element;\n\n hostRPC: HostRPC;\n};\n\n/**\n * Communicate to the host frame when:\n *\n * 1. The set of anchors has been changed (due to annotations being added or removed)\n * 2. The position of anchors relative to the viewport of the guest has changed\n */\nexport class BucketBarClient implements Destroyable {\n private _hostRPC: HostRPC;\n private _updatePending: boolean;\n private _anchors: Anchor[];\n private _listeners: ListenerCollection;\n\n constructor({ contentContainer, hostRPC }: BucketBarClientOptions) {\n this._hostRPC = hostRPC;\n this._updatePending = false;\n this._anchors = [];\n this._listeners = new ListenerCollection();\n\n this._listeners.add(window, 'resize', () => this.update());\n this._listeners.add(window, 'scroll', () => this.update());\n\n // Update bucket positions when container or scrollable descendants are\n // scrolled.\n this._listeners.add(contentContainer, 'scroll', () => this.update(), {\n // \"scroll\" event does not bubble, so use a capture listener to observe\n // event in descendants.\n capture: true,\n });\n }\n\n destroy() {\n this._listeners.removeAll();\n }\n\n /**\n * Notifies the BucketBar in the host frame when:\n * 1. The set of anchors has been changed (due to annotations being added or removed)\n * 2. The position of anchors relative to the viewport of the guest has changed\n *\n * Updates are debounced to reduce the overhead of gathering and sending anchor\n * position data across frames.\n *\n * @param anchors - pass this option when anchors are added or deleted\n */\n update(anchors?: Anchor[]) {\n if (anchors) {\n this._anchors = anchors;\n }\n\n if (this._updatePending) {\n return;\n }\n\n this._updatePending = true;\n requestAnimationFrame(() => {\n const positions = computeAnchorPositions(this._anchors);\n this._hostRPC.call('anchorsChanged', positions);\n this._updatePending = false;\n });\n }\n}\n","import { render } from 'preact';\n\nimport { promiseWithResolvers } from '../shared/promise-with-resolvers';\nimport type { Destroyable, Rect, Shape } from '../types/annotator';\n\n/** Normalize a rect so that `left <= right` and `top <= bottom`. */\nfunction normalizeRect(r: Rect): Rect {\n const minX = Math.min(r.left, r.right);\n const maxX = Math.max(r.left, r.right);\n const minY = Math.min(r.top, r.bottom);\n const maxY = Math.max(r.top, r.bottom);\n return {\n type: 'rect',\n left: minX,\n top: minY,\n right: maxX,\n bottom: maxY,\n };\n}\n\n/**\n * Errors while drawing a shape using {@link DrawTool}.\n */\nexport class DrawError extends Error {\n constructor(message = 'Drawing failed') {\n super(message);\n }\n}\n\n/** Specifies the type of shape to draw. */\nexport type Tool = 'rect' | 'point';\n\n/**\n * Tool for drawing shapes for use as the target region of an annotation.\n *\n * When drawing is active, DrawTool creates an overlay into which the incomplete\n * shape is drawn. The shape is updated in response to user gestures.\n *\n * Drawing is initiated using {@link DrawTool.draw}. Only one shape can be\n * drawn at a time.\n */\nexport class DrawTool implements Destroyable {\n private _container: HTMLElement;\n\n /** Surface for current drawing. */\n private _surface?: SVGSVGElement;\n\n private _tool: Tool;\n\n /** Current drawing shape. */\n private _shape?: Rect;\n\n /** Callback for when draw operation ends successfully. */\n private _drawEnd?: (s: Shape) => void;\n\n /** Callback for when draw operation ends with an error. */\n private _drawError?: (e: DrawError) => void;\n\n /** Controller for ending the drawing operation. */\n private _abortDraw?: AbortController;\n\n /**\n * @param root - Container in which the user can draw a shape. The drawing\n * layer is positioned to fill the container using `position: absolute`.\n * It is the caller's responsibility to make sure the container is\n * positioned if needed.\n */\n constructor(root: HTMLElement) {\n this._container = root;\n this._tool = 'rect';\n }\n\n destroy() {\n this.cancel();\n }\n\n /**\n * Begin drawing a shape.\n *\n * @param tool - Type of shape to draw\n * @return - Promise for the shape drawn by the user\n */\n async draw(tool: Tool): Promise<Shape> {\n this._tool = tool;\n\n // Only one drawing operation can be in progress at a time.\n this.cancel();\n\n // Create a transparent SVG canvas overlaid on top of the container, with\n // a crosshair cursor to indicate the user can click to draw.\n const surface = document.createElementNS(\n 'http://www.w3.org/2000/svg',\n 'svg',\n );\n surface.setAttribute('data-testid', 'surface');\n surface.style.cursor = 'crosshair';\n\n // Make the drawing surface fill the container.\n surface.style.position = 'absolute';\n surface.setAttribute('width', '100%');\n surface.setAttribute('height', '100%');\n surface.style.left = '0px';\n surface.style.top = '0px';\n\n // Raise the drawing surface above other content. The initial value here is\n // \"good enough\" for use in PDF.js but will have to change when we support\n // image annotation in other formats.\n surface.style.zIndex = '10';\n\n this._container.append(surface);\n this._surface = surface;\n\n const { promise: shape, resolve, reject } = promiseWithResolvers<Shape>();\n this._drawEnd = resolve;\n this._drawError = reject;\n\n this._surface.addEventListener('mousedown', e => {\n switch (this._tool) {\n case 'rect':\n this._shape = {\n type: 'rect',\n left: e.clientX,\n top: e.clientY,\n right: e.clientX,\n bottom: e.clientY,\n };\n this._renderSurface();\n break;\n case 'point':\n resolve({\n type: 'point',\n x: e.clientX,\n y: e.clientY,\n });\n this._abortDraw?.abort();\n break;\n }\n });\n\n this._surface.addEventListener('mousemove', e => {\n if (!this._shape) {\n return;\n }\n\n this._shape.right = e.clientX;\n this._shape.bottom = e.clientY;\n this._renderSurface();\n });\n\n this._surface.addEventListener('mouseup', e => {\n if (!this._shape) {\n return;\n }\n this._shape.right = e.clientX;\n this._shape.bottom = e.clientY;\n resolve(normalizeRect(this._shape));\n this._abortDraw?.abort();\n });\n\n // Enable user to scroll elements under the drawing surface by translating\n // wheel events to scroll actions.\n this._surface.addEventListener('wheel', e => {\n // Remaining amount of scroll delta.\n let scrollDeltaY = Math.abs(e.deltaY);\n let scrollDeltaX = Math.abs(e.deltaX);\n\n // Visit elements from top-most to bottom-most and transfer remaining\n // unused scroll delta to them.\n for (const elem of document.elementsFromPoint(e.clientX, e.clientY)) {\n const prevScrollLeft = elem.scrollLeft;\n elem.scrollLeft += scrollDeltaX * Math.sign(e.deltaX);\n scrollDeltaX -= Math.abs(elem.scrollLeft - prevScrollLeft);\n\n const prevScrollTop = elem.scrollTop;\n elem.scrollTop += scrollDeltaY * Math.sign(e.deltaY);\n scrollDeltaY -= Math.abs(elem.scrollTop - prevScrollTop);\n }\n });\n\n // Cancel drawing if user presses \"Escape\"\n this._abortDraw = new AbortController();\n document.body.addEventListener(\n 'keydown',\n (e: KeyboardEvent) => {\n if (e.key !== 'Escape') {\n return;\n }\n if (this._drawError) {\n this._drawError(new DrawError('Drawing canceled'));\n }\n this._abortDraw?.abort();\n },\n {\n signal: this._abortDraw.signal,\n },\n );\n\n // Cleanup when drawing is aborted\n this._abortDraw.signal.onabort = () => {\n this._surface?.remove();\n this._shape = undefined;\n this._surface = undefined;\n this._drawError = undefined;\n this._drawEnd = undefined;\n this._abortDraw = undefined;\n };\n\n // Draw the initial empty surface\n this._renderSurface();\n\n return shape;\n }\n\n /**\n * Cancel any drawing which is in progress.\n *\n * Pending promises returned by {@link DrawTool.draw} will reject.\n */\n cancel() {\n if (this._drawError) {\n this._drawError(new DrawError('Drawing canceled'));\n }\n this._abortDraw?.abort();\n }\n\n private _renderSurface() {\n /* istanbul ignore next */\n if (!this._surface) {\n return;\n }\n if (this._shape?.type === 'rect') {\n // Normalize rect because SVG rects must have positive width and height.\n const rect = normalizeRect(this._shape);\n render(\n // Draw rects with dashed strokes that combine to form one rect with a\n // border of alternating colors.\n <>\n <rect\n stroke=\"white\"\n stroke-dasharray=\"5\"\n stroke-width=\"1px\"\n fill=\"grey\"\n fill-opacity=\"0.5\"\n x={rect.left}\n y={rect.top}\n width={rect.right - rect.left}\n height={rect.bottom - rect.top}\n />\n <rect\n stroke=\"grey\"\n stroke-dasharray=\"5\"\n stroke-dashoffset=\"5\"\n stroke-width=\"1px\"\n fill=\"none\"\n x={rect.left}\n y={rect.top}\n width={rect.right - rect.left}\n height={rect.bottom - rect.top}\n />\n </>,\n this._surface,\n );\n } else {\n render(null, this._surface);\n }\n }\n}\n","export type PromiseWithResolvers<T> = {\n promise: Promise<T>;\n resolve: (value: T) => void;\n reject: (error: any) => void;\n};\n\n/**\n * This function behaves like Promise.withResolvers\n * See https://tc39.es/proposal-promise-with-resolvers/\n */\nexport function promiseWithResolvers<T = unknown>(): PromiseWithResolvers<T> {\n let resolve: PromiseWithResolvers<T>['resolve'];\n let reject: PromiseWithResolvers<T>['reject'];\n const promise = new Promise<T>((resolve_, reject_) => {\n resolve = resolve_;\n reject = reject_;\n });\n\n return { promise, resolve: resolve!, reject: reject! };\n}\n","import type { SidebarLayout } from '../types/annotator';\n\ntype LayoutChangeEventDetail = {\n sideBySideActive: boolean;\n sidebarLayout: SidebarLayout;\n};\n\nexport class LayoutChangeEvent extends CustomEvent<LayoutChangeEventDetail> {\n constructor(detail: LayoutChangeEventDetail) {\n super('hypothesis:layoutchange', {\n bubbles: true,\n cancelable: false,\n detail,\n });\n }\n}\n","const shownWarnings = new Set<string>();\n\n/**\n * Log a warning if it has not already been reported.\n *\n * This is useful to avoid spamming the console if a warning is emitted in a\n * context that may be called frequently.\n *\n * @param args -\n * Arguments to forward to `console.warn`. The arguments `toString()` values\n * are concatenated into a string key which is used to determine if the warning\n * has been logged before.\n */\nexport function warnOnce(...args: unknown[]) {\n const key = args.join();\n if (shownWarnings.has(key)) {\n return;\n }\n console.warn(...args);\n shownWarnings.add(key);\n}\n\nwarnOnce.reset = () => {\n shownWarnings.clear();\n};\n","import { EventEmitter } from '../shared/event-emitter';\nimport { warnOnce } from '../shared/warn-once';\nimport type {\n FeatureFlags as IFeatureFlags,\n FeatureFlagsEvents,\n} from '../types/annotator';\n\n/**\n * List of feature flags that annotator code tests for.\n */\nconst annotatorFlags = ['pdf_image_annotation', 'styled_highlight_clusters'];\n\n/**\n * An observable container of feature flags.\n */\nexport class FeatureFlags\n extends EventEmitter<FeatureFlagsEvents>\n implements IFeatureFlags\n{\n /** Map of flag name to enabled state. */\n private _flags: Map<string, boolean>;\n private _knownFlags: string[];\n\n /**\n * @param knownFlags - Test seam. This is a list of known flags used to catch\n * mistakes where code checks for an obsolete feature flag.\n */\n constructor(knownFlags: string[] = annotatorFlags) {\n super();\n\n this._flags = new Map<string, boolean>();\n this._knownFlags = knownFlags;\n }\n\n /**\n * Update the stored flags and notify observers via a \"flagsChanged\" event.\n */\n update(flags: Record<string, boolean>) {\n this._flags.clear();\n for (const [flag, on] of Object.entries(flags)) {\n this._flags.set(flag, on);\n }\n this.emit('flagsChanged');\n }\n\n /**\n * Test if a feature flag is enabled.\n *\n * This will return false if the feature flags have not yet been received from\n * the backend. Code that uses a feature flag should handle subsequent changes\n * to the flag's state by listening for the \"flagsChanged\" event.\n */\n flagEnabled(flag: string): boolean {\n if (!this._knownFlags.includes(flag)) {\n warnOnce('Looked up unknown feature', flag);\n return false;\n }\n return this._flags.get(flag) ?? false;\n }\n\n /**\n * Return the state of all feature flags.\n *\n * To test whether an individual flag is enabled, use {@link flagEnabled}\n * instead.\n */\n allFlags(): Record<string, boolean> {\n return Object.fromEntries(this._flags);\n }\n}\n","import {\n Button,\n Card,\n CardContent,\n CaretDownIcon,\n CaretRightIcon,\n HideIcon,\n HighlightIcon,\n} from '@hypothesis/frontend-shared';\nimport classnames from 'classnames';\nimport { useCallback, useState } from 'preact/hooks';\n\nimport type { HighlightCluster } from '../../types/shared';\nimport type { AppliedStyles, HighlightStyles } from '../highlight-clusters';\n\ntype ClusterStyleControlProps = {\n cluster: HighlightCluster;\n label: string;\n onChange: (e: Event) => void;\n currentStyles: AppliedStyles;\n highlightStyles: HighlightStyles;\n};\n\n/**\n * Render controls for changing a single highlight cluster's style\n */\nfunction ClusterStyleControl({\n cluster,\n label,\n onChange,\n currentStyles,\n highlightStyles,\n}: ClusterStyleControlProps) {\n const appliedStyleName = currentStyles[cluster];\n const isHidden = appliedStyleName === 'transparent'; // This style is somewhat special\n return (\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-x-2 text-annotator-base\">\n <div\n className=\"grow text-color-text px-2 py-1 rounded\"\n style={{\n backgroundColor: highlightStyles[appliedStyleName].color,\n }}\n >\n {label}\n </div>\n </div>\n <div className=\"flex items-center gap-x-2\">\n {Object.keys(highlightStyles).map(styleName => (\n <div className=\"relative\" key={`${cluster}-${styleName}`}>\n <input\n className={classnames(\n // Position this atop its label and size it to the same dimensions\n 'absolute w-6 h-6',\n // Make radio input visually hidden, but\n // some screen readers won't read out elements with 0 opacity\n 'opacity-[.00001]',\n 'cursor-pointer',\n )}\n name={cluster}\n id={`hypothesis-${cluster}-${styleName}`}\n checked={appliedStyleName === styleName}\n onChange={onChange}\n type=\"radio\"\n value={styleName}\n />\n <label className=\"block\" htmlFor={`${cluster}-${styleName}`}>\n <div\n style={{\n backgroundColor: highlightStyles[styleName].color,\n }}\n className={classnames(\n 'block w-6 h-6 rounded-full flex items-center justify-center',\n {\n 'border-2 border-slate-0': appliedStyleName !== styleName,\n 'border-2 border-slate-3': appliedStyleName === styleName,\n },\n )}\n >\n {styleName === 'transparent' && (\n <HideIcon\n className={classnames('w-3 h-3', {\n 'text-slate-3': !isHidden,\n 'text-slate-7': isHidden,\n })}\n />\n )}\n </div>\n <span className=\"sr-only\">{styleName}</span>\n </label>\n </div>\n ))}\n </div>\n </div>\n );\n}\n\nexport type ClusterToolbarProps = {\n /** Is cluster highlight styling active? Do not render the toolbar if not */\n active: boolean;\n availableStyles: HighlightStyles;\n currentStyles: AppliedStyles;\n onStyleChange: (cluster: HighlightCluster, styleName: string) => void;\n};\n\n/**\n * Render controls to change highlight-cluster styling.\n */\nexport default function ClusterToolbar({\n active,\n availableStyles,\n currentStyles,\n onStyleChange,\n}: ClusterToolbarProps) {\n const handleStyleChange = useCallback(\n (changeEvent: Event) => {\n const input = changeEvent.target as HTMLInputElement;\n const cluster = input.name as HighlightCluster;\n const styleName = input.value;\n\n onStyleChange(cluster, styleName);\n },\n [onStyleChange],\n );\n\n const [isOpen, setOpen] = useState(false);\n\n if (!active) {\n return null;\n }\n\n return (\n <Card classes=\"overflow-hidden\">\n <div className=\"flex flex-col text-annotator-base text-color-text\">\n <Button\n classes=\"rounded-none\"\n data-testid=\"control-toggle-button\"\n onClick={() => setOpen(!isOpen)}\n title={isOpen ? 'Hide highlight settings' : 'Show highlight settings'}\n >\n {isOpen ? (\n <>\n <CaretDownIcon />\n <span>Highlight Appearance</span>\n </>\n ) : (\n <>\n <CaretRightIcon />\n <HighlightIcon />\n </>\n )}\n </Button>\n {isOpen && (\n <CardContent data-testid=\"cluster-style-controls\" size=\"sm\">\n <ClusterStyleControl\n highlightStyles={availableStyles}\n label=\"My annotations\"\n cluster=\"user-annotations\"\n onChange={handleStyleChange}\n currentStyles={currentStyles}\n />\n <ClusterStyleControl\n highlightStyles={availableStyles}\n label=\"My highlights\"\n cluster=\"user-highlights\"\n onChange={handleStyleChange}\n currentStyles={currentStyles}\n />\n <ClusterStyleControl\n highlightStyles={availableStyles}\n label=\"Everybody's content\"\n cluster=\"other-content\"\n onChange={handleStyleChange}\n currentStyles={currentStyles}\n />\n </CardContent>\n )}\n </div>\n </Card>\n );\n}\n","import type {\n Destroyable,\n FeatureFlags as IFeatureFlags,\n} from '../types/annotator';\nimport type { HighlightCluster } from '../types/shared';\nimport ClusterToolbar from './components/ClusterToolbar';\nimport { updateClusters } from './highlighter';\nimport { PreactContainer } from './util/preact-container';\n\nexport type HighlightStyle = {\n color: string;\n secondColor: string;\n thirdColor: string;\n};\n\nexport type HighlightStyles = Record<string, HighlightStyle>;\nexport type AppliedStyles = Record<HighlightCluster, keyof HighlightStyles>;\n\n// Available styles that users can apply to highlight clusters\nexport const highlightStyles: HighlightStyles = {\n transparent: {\n color: 'transparent',\n secondColor: 'transparent',\n thirdColor: 'transparent',\n },\n pink: {\n color: 'var(--hypothesis-color-pink)',\n secondColor: 'var(--hypothesis-color-pink-1)',\n thirdColor: 'var(--hypothesis-color-pink-2)',\n },\n orange: {\n color: 'var(--hypothesis-color-orange)',\n secondColor: 'var(--hypothesis-color-orange-1)',\n thirdColor: 'var(--hypothesis-color-orange-2)',\n },\n yellow: {\n color: 'var(--hypothesis-color-yellow)',\n secondColor: 'var(--hypothesis-color-yellow-1)',\n thirdColor: 'var(--hypothesis-color-yellow-2)',\n },\n green: {\n color: 'var(--hypothesis-color-green)',\n secondColor: 'var(--hypothesis-color-green-1)',\n thirdColor: 'var(--hypothesis-color-green-2)',\n },\n purple: {\n color: 'var(--hypothesis-color-purple)',\n secondColor: 'var(--hypothesis-color-purple-1)',\n thirdColor: 'var(--hypothesis-color-purple-2)',\n },\n grey: {\n color: 'var(--hypothesis-color-grey)',\n secondColor: 'var(--hypothesis-color-grey-1)',\n thirdColor: 'var(--hypothesis-color-grey-2)',\n },\n};\n\n// The default styles applied to each highlight cluster. For now, this is\n// hard-coded.\nexport const defaultClusterStyles: AppliedStyles = {\n 'other-content': 'yellow',\n 'user-annotations': 'orange',\n 'user-highlights': 'purple',\n};\n\nexport class HighlightClusterController implements Destroyable {\n appliedStyles: AppliedStyles;\n private _container: PreactContainer;\n private _element: HTMLElement;\n private _features: IFeatureFlags;\n private _updateTimeout?: number;\n\n constructor(element: HTMLElement, options: { features: IFeatureFlags }) {\n this._element = element;\n this._features = options.features;\n\n this._container = new PreactContainer('highlight-cluster-toolbar', () =>\n this._render(),\n );\n this._element.appendChild(this._container.element);\n\n // For now, the controls are fixed at top-left of screen. This is temporary.\n Object.assign(this._container.element.style, {\n position: 'fixed',\n top: `${this._element.offsetTop + 4}px`,\n left: '4px',\n });\n\n this.appliedStyles = defaultClusterStyles;\n\n this._init();\n\n this._features.on('flagsChanged', () => {\n this._activate(this._isActive());\n });\n\n this._container.render();\n }\n\n destroy() {\n clearTimeout(this._updateTimeout);\n this._activate(false); // De-activate cluster styling\n this._container.destroy();\n }\n\n /**\n * Indicate that the set of highlights in the document has been dirtied and we\n * should schedule an update to highlight data attributes and stacking order.\n */\n scheduleClusterUpdates() {\n clearTimeout(this._updateTimeout);\n this._updateTimeout = setTimeout(() => this._updateClusters(), 100);\n }\n\n /**\n * Set initial values for :root CSS custom properties (variables) based on the\n * applied styles for each cluster. This has no effect if this feature\n * is not active.\n */\n _init() {\n for (const cluster of Object.keys(this.appliedStyles) as Array<\n keyof typeof this.appliedStyles\n >) {\n this._setClusterStyles(cluster, this.appliedStyles[cluster]);\n }\n\n this._activate(this._isActive());\n }\n\n _updateClusters() {\n if (!this._isActive()) {\n /* istanbul ignore next */\n return;\n }\n updateClusters(this._element);\n }\n\n _isActive() {\n return this._features.flagEnabled('styled_highlight_clusters');\n }\n\n /**\n * Activate cluster highlighting if `active` is set.\n */\n _activate(active: boolean) {\n this._element.classList.toggle('hypothesis-highlights-clustered', active);\n this._container.render();\n }\n\n /**\n * Set a value for an individual CSS variable at :root\n */\n _setClusterStyle(key: string, value: string) {\n document.documentElement.style.setProperty(key, value);\n }\n\n /**\n * Set CSS variables for the highlight `cluster` to apply the\n * {@link HighlightStyle} `highlightStyles[styleName]`\n */\n _setClusterStyles(\n cluster: HighlightCluster,\n styleName: keyof typeof highlightStyles,\n ) {\n const styleRules = highlightStyles[styleName];\n\n for (const ruleName of Object.keys(styleRules) as Array<\n keyof HighlightStyle\n >) {\n this._setClusterStyle(\n `--hypothesis-${cluster}-${ruleName}`,\n styleRules[ruleName] as string,\n );\n }\n }\n\n /**\n * Respond to user input to change the applied style for a cluster\n */\n _onChangeClusterStyle(\n cluster: HighlightCluster,\n styleName: keyof typeof highlightStyles,\n ) {\n this.appliedStyles[cluster] = styleName;\n this._setClusterStyles(cluster, styleName);\n this._container.render();\n }\n\n _render() {\n return (\n <ClusterToolbar\n active={this._isActive()}\n availableStyles={highlightStyles}\n currentStyles={this.appliedStyles}\n onStyleChange={(cluster, styleName) =>\n this._onChangeClusterStyle(cluster, styleName)\n }\n />\n );\n }\n}\n","/**\n * Implementation of Myers' online approximate string matching algorithm [1],\n * with additional optimizations suggested by [2].\n *\n * This has O((k/w) * n) expected-time where `n` is the length of the\n * text, `k` is the maximum number of errors allowed (always <= the pattern\n * length) and `w` is the word size. Because JS only supports bitwise operations\n * on 32 bit integers, `w` is 32.\n *\n * As far as I am aware, there aren't any online algorithms which are\n * significantly better for a wide range of input parameters. The problem can be\n * solved faster using \"filter then verify\" approaches which first filter out\n * regions of the text that cannot match using a \"cheap\" check and then verify\n * the remaining potential matches. The verify step requires an algorithm such\n * as this one however.\n *\n * The algorithm's approach is essentially to optimize the classic dynamic\n * programming solution to the problem by computing columns of the matrix in\n * word-sized chunks (ie. dealing with 32 chars of the pattern at a time) and\n * avoiding calculating regions of the matrix where the minimum error count is\n * guaranteed to exceed the input threshold.\n *\n * The paper consists of two parts, the first describes the core algorithm for\n * matching patterns <= the size of a word (implemented by `advanceBlock` here).\n * The second uses the core algorithm as part of a larger block-based algorithm\n * to handle longer patterns.\n *\n * [1] G. Myers, “A Fast Bit-Vector Algorithm for Approximate String Matching\n * Based on Dynamic Programming,” vol. 46, no. 3, pp. 395–415, 1999.\n *\n * [2] Šošić, M. (2014). An simd dynamic programming c/c++ library (Doctoral\n * dissertation, Fakultet Elektrotehnike i računarstva, Sveučilište u Zagrebu).\n */\nfunction reverse(s) {\n return s.split(\"\").reverse().join(\"\");\n}\n/**\n * Given the ends of approximate matches for `pattern` in `text`, find\n * the start of the matches.\n *\n * @param findEndFn - Function for finding the end of matches in\n * text.\n * @return Matches with the `start` property set.\n */\nfunction findMatchStarts(text, pattern, matches) {\n const patRev = reverse(pattern);\n return matches.map((m) => {\n // Find start of each match by reversing the pattern and matching segment\n // of text and searching for an approx match with the same number of\n // errors.\n const minStart = Math.max(0, m.end - pattern.length - m.errors);\n const textRev = reverse(text.slice(minStart, m.end));\n // If there are multiple possible start points, choose the one that\n // maximizes the length of the match.\n const start = findMatchEnds(textRev, patRev, m.errors).reduce((min, rm) => {\n if (m.end - rm.end < min) {\n return m.end - rm.end;\n }\n return min;\n }, m.end);\n return {\n start,\n end: m.end,\n errors: m.errors,\n };\n });\n}\n/**\n * Return 1 if a number is non-zero or zero otherwise, without using\n * conditional operators.\n *\n * This should get inlined into `advanceBlock` below by the JIT.\n *\n * Adapted from https://stackoverflow.com/a/3912218/434243\n */\nfunction oneIfNotZero(n) {\n return ((n | -n) >> 31) & 1;\n}\n/**\n * Block calculation step of the algorithm.\n *\n * From Fig 8. on p. 408 of [1], additionally optimized to replace conditional\n * checks with bitwise operations as per Section 4.2.3 of [2].\n *\n * @param ctx - The pattern context object\n * @param peq - The `peq` array for the current character (`ctx.peq.get(ch)`)\n * @param b - The block level\n * @param hIn - Horizontal input delta ∈ {1,0,-1}\n * @return Horizontal output delta ∈ {1,0,-1}\n */\nfunction advanceBlock(ctx, peq, b, hIn) {\n let pV = ctx.P[b];\n let mV = ctx.M[b];\n const hInIsNegative = hIn >>> 31; // 1 if hIn < 0 or 0 otherwise.\n const eq = peq[b] | hInIsNegative;\n // Step 1: Compute horizontal deltas.\n const xV = eq | mV;\n const xH = (((eq & pV) + pV) ^ pV) | eq;\n let pH = mV | ~(xH | pV);\n let mH = pV & xH;\n // Step 2: Update score (value of last row of this block).\n const hOut = oneIfNotZero(pH & ctx.lastRowMask[b]) -\n oneIfNotZero(mH & ctx.lastRowMask[b]);\n // Step 3: Update vertical deltas for use when processing next char.\n pH <<= 1;\n mH <<= 1;\n mH |= hInIsNegative;\n pH |= oneIfNotZero(hIn) - hInIsNegative; // set pH[0] if hIn > 0\n pV = mH | ~(xV | pH);\n mV = pH & xV;\n ctx.P[b] = pV;\n ctx.M[b] = mV;\n return hOut;\n}\n/**\n * Find the ends and error counts for matches of `pattern` in `text`.\n *\n * Only the matches with the lowest error count are reported. Other matches\n * with error counts <= maxErrors are discarded.\n *\n * This is the block-based search algorithm from Fig. 9 on p.410 of [1].\n */\nfunction findMatchEnds(text, pattern, maxErrors) {\n if (pattern.length === 0) {\n return [];\n }\n // Clamp error count so we can rely on the `maxErrors` and `pattern.length`\n // rows being in the same block below.\n maxErrors = Math.min(maxErrors, pattern.length);\n const matches = [];\n // Word size.\n const w = 32;\n // Index of maximum block level.\n const bMax = Math.ceil(pattern.length / w) - 1;\n // Context used across block calculations.\n const ctx = {\n P: new Uint32Array(bMax + 1),\n M: new Uint32Array(bMax + 1),\n lastRowMask: new Uint32Array(bMax + 1),\n };\n ctx.lastRowMask.fill(1 << 31);\n ctx.lastRowMask[bMax] = 1 << (pattern.length - 1) % w;\n // Dummy \"peq\" array for chars in the text which do not occur in the pattern.\n const emptyPeq = new Uint32Array(bMax + 1);\n // Map of UTF-16 character code to bit vector indicating positions in the\n // pattern that equal that character.\n const peq = new Map();\n // Version of `peq` that only stores mappings for small characters. This\n // allows faster lookups when iterating through the text because a simple\n // array lookup can be done instead of a hash table lookup.\n const asciiPeq = [];\n for (let i = 0; i < 256; i++) {\n asciiPeq.push(emptyPeq);\n }\n // Calculate `ctx.peq` - a map of character values to bitmasks indicating\n // positions of that character within the pattern, where each bit represents\n // a position in the pattern.\n for (let c = 0; c < pattern.length; c += 1) {\n const val = pattern.charCodeAt(c);\n if (peq.has(val)) {\n // Duplicate char in pattern.\n continue;\n }\n const charPeq = new Uint32Array(bMax + 1);\n peq.set(val, charPeq);\n if (val < asciiPeq.length) {\n asciiPeq[val] = charPeq;\n }\n for (let b = 0; b <= bMax; b += 1) {\n charPeq[b] = 0;\n // Set all the bits where the pattern matches the current char (ch).\n // For indexes beyond the end of the pattern, always set the bit as if the\n // pattern contained a wildcard char in that position.\n for (let r = 0; r < w; r += 1) {\n const idx = b * w + r;\n if (idx >= pattern.length) {\n continue;\n }\n const match = pattern.charCodeAt(idx) === val;\n if (match) {\n charPeq[b] |= 1 << r;\n }\n }\n }\n }\n // Index of last-active block level in the column.\n let y = Math.max(0, Math.ceil(maxErrors / w) - 1);\n // Initialize maximum error count at bottom of each block.\n const score = new Uint32Array(bMax + 1);\n for (let b = 0; b <= y; b += 1) {\n score[b] = (b + 1) * w;\n }\n score[bMax] = pattern.length;\n // Initialize vertical deltas for each block.\n for (let b = 0; b <= y; b += 1) {\n ctx.P[b] = ~0;\n ctx.M[b] = 0;\n }\n // Process each char of the text, computing the error count for `w` chars of\n // the pattern at a time.\n for (let j = 0; j < text.length; j += 1) {\n // Lookup the bitmask representing the positions of the current char from\n // the text within the pattern.\n const charCode = text.charCodeAt(j);\n let charPeq;\n if (charCode < asciiPeq.length) {\n // Fast array lookup.\n charPeq = asciiPeq[charCode];\n }\n else {\n // Slower hash table lookup.\n charPeq = peq.get(charCode);\n if (typeof charPeq === \"undefined\") {\n charPeq = emptyPeq;\n }\n }\n // Calculate error count for blocks that we definitely have to process for\n // this column.\n let carry = 0;\n for (let b = 0; b <= y; b += 1) {\n carry = advanceBlock(ctx, charPeq, b, carry);\n score[b] += carry;\n }\n // Check if we also need to compute an additional block, or if we can reduce\n // the number of blocks processed for the next column.\n if (score[y] - carry <= maxErrors &&\n y < bMax &&\n (charPeq[y + 1] & 1 || carry < 0)) {\n // Error count for bottom block is under threshold, increase the number of\n // blocks processed for this column & next by 1.\n y += 1;\n ctx.P[y] = ~0;\n ctx.M[y] = 0;\n let maxBlockScore;\n if (y === bMax) {\n const remainder = pattern.length % w;\n maxBlockScore = remainder === 0 ? w : remainder;\n }\n else {\n maxBlockScore = w;\n }\n score[y] =\n score[y - 1] +\n maxBlockScore -\n carry +\n advanceBlock(ctx, charPeq, y, carry);\n }\n else {\n // Error count for bottom block exceeds threshold, reduce the number of\n // blocks processed for the next column.\n while (y > 0 && score[y] >= maxErrors + w) {\n y -= 1;\n }\n }\n // If error count is under threshold, report a match.\n if (y === bMax && score[y] <= maxErrors) {\n if (score[y] < maxErrors) {\n // Discard any earlier, worse matches.\n matches.splice(0, matches.length);\n }\n matches.push({\n start: -1,\n end: j + 1,\n errors: score[y],\n });\n // Because `search` only reports the matches with the lowest error count,\n // we can \"ratchet down\" the max error threshold whenever a match is\n // encountered and thereby save a small amount of work for the remainder\n // of the text.\n maxErrors = score[y];\n }\n }\n return matches;\n}\n/**\n * Search for matches for `pattern` in `text` allowing up to `maxErrors` errors.\n *\n * Returns the start, and end positions and error counts for each lowest-cost\n * match. Only the \"best\" matches are returned.\n */\nexport default function search(text, pattern, maxErrors) {\n const matches = findMatchEnds(text, pattern, maxErrors);\n return findMatchStarts(text, pattern, matches);\n}\n","import approxSearch from 'approx-string-match';\nimport type { Match as StringMatch } from 'approx-string-match';\n\ntype Match = {\n /** Start offset of match in text */\n start: number;\n /** End offset of match in text */\n end: number;\n\n /**\n * Score for the match between 0 and 1.0, where 1.0 indicates a perfect match\n * for the quote and context.\n */\n score: number;\n};\n\n/**\n * Find the best approximate matches for `str` in `text` allowing up to\n * `maxErrors` errors.\n */\nfunction search(text: string, str: string, maxErrors: number): StringMatch[] {\n // Do a fast search for exact matches. The `approx-string-match` library\n // doesn't currently incorporate this optimization itself.\n let matchPos = 0;\n const exactMatches: StringMatch[] = [];\n while (matchPos !== -1) {\n matchPos = text.indexOf(str, matchPos);\n if (matchPos !== -1) {\n exactMatches.push({\n start: matchPos,\n end: matchPos + str.length,\n errors: 0,\n });\n matchPos += 1;\n }\n }\n if (exactMatches.length > 0) {\n return exactMatches;\n }\n\n // If there are no exact matches, do a more expensive search for matches\n // with errors.\n return approxSearch(text, str, maxErrors);\n}\n\n/**\n * Compute a score between 0 and 1.0 for the similarity between `text` and `str`.\n */\nfunction textMatchScore(text: string, str: string) {\n // `search` will return no matches if either the text or pattern is empty,\n // otherwise it will return at least one match if the max allowed error count\n // is at least `str.length`.\n if (str.length === 0 || text.length === 0) {\n return 0.0;\n }\n\n const matches = search(text, str, str.length);\n\n // prettier-ignore\n return 1 - (matches[0].errors / str.length);\n}\n\ntype Context = {\n /** Expected text before the quote */\n prefix?: string;\n /** Expected text after the quote */\n suffix?: string;\n /** Expected offset of match within text */\n hint?: number;\n};\n\n/**\n * Find the best approximate match for `quote` in `text`.\n *\n * @param text - Document text to search\n * @param quote - String to find within `text`\n * @param context - Context in which the quote originally appeared. This is\n * used to choose the best match.\n * @return `null` if no match exceeding the minimum quality threshold was found.\n */\nexport function matchQuote(\n text: string,\n quote: string,\n context: Context = {},\n): Match | null {\n if (quote.length === 0) {\n return null;\n }\n\n // Choose the maximum number of errors to allow for the initial search.\n // This choice involves a tradeoff between:\n //\n // - Recall (proportion of \"good\" matches found)\n // - Precision (proportion of matches found which are \"good\")\n // - Cost of the initial search and of processing the candidate matches [1]\n //\n // [1] Specifically, the expected-time complexity of the initial search is\n // `O((maxErrors / 32) * text.length)`. See `approx-string-match` docs.\n const maxErrors = Math.min(256, quote.length / 2);\n\n // Find the closest matches for `quote` in `text` based on edit distance.\n const matches = search(text, quote, maxErrors);\n\n if (matches.length === 0) {\n return null;\n }\n\n /**\n * Compute a score between 0 and 1.0 for a match candidate.\n */\n const scoreMatch = (match: StringMatch) => {\n const quoteWeight = 50; // Similarity of matched text to quote.\n const prefixWeight = 20; // Similarity of text before matched text to `context.prefix`.\n const suffixWeight = 20; // Similarity of text after matched text to `context.suffix`.\n const posWeight = 2; // Proximity to expected location. Used as a tie-breaker.\n\n const quoteScore = 1 - match.errors / quote.length;\n\n const prefixScore = context.prefix\n ? textMatchScore(\n text.slice(\n Math.max(0, match.start - context.prefix.length),\n match.start,\n ),\n context.prefix,\n )\n : 1.0;\n const suffixScore = context.suffix\n ? textMatchScore(\n text.slice(match.end, match.end + context.suffix.length),\n context.suffix,\n )\n : 1.0;\n\n let posScore = 1.0;\n if (typeof context.hint === 'number') {\n const offset = Math.abs(match.start - context.hint);\n posScore = 1.0 - offset / text.length;\n }\n\n const rawScore =\n quoteWeight * quoteScore +\n prefixWeight * prefixScore +\n suffixWeight * suffixScore +\n posWeight * posScore;\n const maxScore = quoteWeight + prefixWeight + suffixWeight + posWeight;\n const normalizedScore = rawScore / maxScore;\n\n return normalizedScore;\n };\n\n // Rank matches based on similarity of actual and expected surrounding text\n // and actual/expected offset in the document text.\n const scoredMatches = matches.map(m => ({\n start: m.start,\n end: m.end,\n score: scoreMatch(m),\n }));\n\n // Choose match with the highest score.\n scoredMatches.sort((a, b) => b.score - a.score);\n return scoredMatches[0];\n}\n","/**\n * Get the node name for use in generating an xpath expression.\n */\nfunction getNodeName(node: Node): string {\n const nodeName = node.nodeName.toLowerCase();\n return nodeName === '#text' ? 'text()' : nodeName;\n}\n\n/**\n * Get the index of the node as it appears in its parent's child list\n */\nfunction getNodePosition(node: Node): number {\n let pos = 0;\n let tmp: Node | null = node;\n while (tmp) {\n if (tmp.nodeName === node.nodeName) {\n pos += 1;\n }\n tmp = tmp.previousSibling;\n }\n return pos;\n}\n\nfunction getPathSegment(node: Node): string {\n const name = getNodeName(node);\n const pos = getNodePosition(node);\n return `${name}[${pos}]`;\n}\n\n/**\n * A simple XPath generator which can generate XPaths of the form\n * /tag[index]/tag[index].\n *\n * @param node - The node to generate a path to\n * @param root - Root node to which the returned path is relative\n */\nexport function xpathFromNode(node: Node, root: Node) {\n let xpath = '';\n\n let elem: Node | null = node;\n while (elem !== root) {\n if (!elem) {\n throw new Error('Node is not a descendant of root');\n }\n xpath = getPathSegment(elem) + '/' + xpath;\n elem = elem.parentNode;\n }\n xpath = '/' + xpath;\n xpath = xpath.replace(/\\/$/, ''); // Remove trailing slash\n\n return xpath;\n}\n\n/**\n * Return the `index`'th immediate child of `element` whose tag name is\n * `nodeName` (case insensitive).\n */\nfunction nthChildOfType(\n element: Element,\n nodeName: string,\n index: number,\n): Element | null {\n nodeName = nodeName.toUpperCase();\n\n let matchIndex = -1;\n for (let i = 0; i < element.children.length; i++) {\n const child = element.children[i];\n if (child.nodeName.toUpperCase() === nodeName) {\n ++matchIndex;\n if (matchIndex === index) {\n return child;\n }\n }\n }\n\n return null;\n}\n\n/**\n * Evaluate a _simple XPath_ relative to a `root` element and return the\n * matching element.\n *\n * A _simple XPath_ is a sequence of one or more `/tagName[index]` strings.\n *\n * Unlike `document.evaluate` this function:\n *\n * - Only supports simple XPaths\n * - Is not affected by the document's _type_ (HTML or XML/XHTML)\n * - Ignores element namespaces when matching element names in the XPath against\n * elements in the DOM tree\n * - Is case-insensitive for all elements, not just HTML elements\n *\n * The matching element is returned or `null` if no such element is found.\n * An error is thrown if `xpath` is not a simple XPath.\n */\nfunction evaluateSimpleXPath(xpath: string, root: Element): Element | null {\n const isSimpleXPath =\n xpath.match(/^(\\/[A-Za-z0-9-]+(\\[[0-9]+\\])?)+$/) !== null;\n if (!isSimpleXPath) {\n throw new Error('Expression is not a simple XPath');\n }\n\n const segments = xpath.split('/');\n let element = root;\n\n // Remove leading empty segment. The regex above validates that the XPath\n // has at least two segments, with the first being empty and the others non-empty.\n segments.shift();\n\n for (const segment of segments) {\n let elementName;\n let elementIndex;\n\n const separatorPos = segment.indexOf('[');\n if (separatorPos !== -1) {\n elementName = segment.slice(0, separatorPos);\n\n const indexStr = segment.slice(separatorPos + 1, segment.indexOf(']'));\n elementIndex = parseInt(indexStr) - 1;\n if (elementIndex < 0) {\n return null;\n }\n } else {\n elementName = segment;\n elementIndex = 0;\n }\n\n const child = nthChildOfType(element, elementName, elementIndex);\n if (!child) {\n return null;\n }\n\n element = child;\n }\n\n return element;\n}\n\n/**\n * Finds an element node using an XPath relative to `root`\n *\n * Example:\n * node = nodeFromXPath('/main/article[1]/p[3]', document.body)\n */\nexport function nodeFromXPath(\n xpath: string,\n /* istanbul ignore next */\n root: Element = document.body,\n): Node | null {\n try {\n return evaluateSimpleXPath(xpath, root);\n } catch {\n return document.evaluate(\n '.' + xpath,\n root,\n\n // nb. The `namespaceResolver` and `result` arguments are optional in the spec\n // but required in Edge Legacy.\n null /* namespaceResolver */,\n XPathResult.FIRST_ORDERED_NODE_TYPE,\n null /* result */,\n ).singleNodeValue;\n }\n}\n","/**\n * This module exports a set of classes for converting between DOM `Range`\n * objects and different types of selectors. It is mostly a thin wrapper around a\n * set of anchoring libraries. It serves two main purposes:\n *\n * 1. Providing a consistent interface across different types of anchors.\n * 2. Insulating the rest of the code from API changes in the underlying anchoring\n * libraries.\n */\nimport type {\n MediaTimeSelector,\n RangeSelector,\n TextPositionSelector,\n TextQuoteSelector,\n} from '../../types/api';\nimport { matchQuote } from './match-quote';\nimport { TextRange, TextPosition } from './text-range';\nimport { nodeFromXPath, xpathFromNode } from './xpath';\n\n/**\n * Converts between `RangeSelector` selectors and `Range` objects.\n */\nexport class RangeAnchor {\n root: Node;\n range: Range;\n\n /**\n * @param root - A root element from which to anchor.\n * @param range - A range describing the anchor.\n */\n constructor(root: Node, range: Range) {\n this.root = root;\n this.range = range;\n }\n\n /**\n * @param root - A root element from which to anchor.\n * @param range - A range describing the anchor.\n */\n static fromRange(root: Node, range: Range): RangeAnchor {\n return new RangeAnchor(root, range);\n }\n\n /**\n * Create an anchor from a serialized `RangeSelector` selector.\n *\n * @param root - A root element from which to anchor.\n */\n static fromSelector(root: Element, selector: RangeSelector): RangeAnchor {\n const startContainer = nodeFromXPath(selector.startContainer, root);\n if (!startContainer) {\n throw new Error('Failed to resolve startContainer XPath');\n }\n\n const endContainer = nodeFromXPath(selector.endContainer, root);\n if (!endContainer) {\n throw new Error('Failed to resolve endContainer XPath');\n }\n\n const startPos = TextPosition.fromCharOffset(\n startContainer,\n selector.startOffset,\n );\n const endPos = TextPosition.fromCharOffset(\n endContainer,\n selector.endOffset,\n );\n\n const range = new TextRange(startPos, endPos).toRange();\n return new RangeAnchor(root, range);\n }\n\n toRange(): Range {\n return this.range;\n }\n\n toSelector(): RangeSelector {\n // \"Shrink\" the range so that it tightly wraps its text. This ensures more\n // predictable output for a given text selection.\n const normalizedRange = TextRange.fromRange(this.range).toRange();\n\n const textRange = TextRange.fromRange(normalizedRange);\n const startContainer = xpathFromNode(textRange.start.element, this.root);\n const endContainer = xpathFromNode(textRange.end.element, this.root);\n\n return {\n type: 'RangeSelector',\n startContainer,\n startOffset: textRange.start.offset,\n endContainer,\n endOffset: textRange.end.offset,\n };\n }\n}\n\n/**\n * Converts between `TextPositionSelector` selectors and `Range` objects.\n */\nexport class TextPositionAnchor {\n root: Element;\n start: number;\n end: number;\n\n constructor(root: Element, start: number, end: number) {\n this.root = root;\n this.start = start;\n this.end = end;\n }\n\n static fromRange(root: Element, range: Range): TextPositionAnchor {\n const textRange = TextRange.fromRange(range).relativeTo(root);\n return new TextPositionAnchor(\n root,\n textRange.start.offset,\n textRange.end.offset,\n );\n }\n\n static fromSelector(\n root: Element,\n selector: TextPositionSelector,\n ): TextPositionAnchor {\n return new TextPositionAnchor(root, selector.start, selector.end);\n }\n\n toSelector(): TextPositionSelector {\n return {\n type: 'TextPositionSelector',\n start: this.start,\n end: this.end,\n };\n }\n\n toRange(): Range {\n return TextRange.fromOffsets(this.root, this.start, this.end).toRange();\n }\n}\n\ntype QuoteMatchOptions = {\n /** Expected position of match in text. See `matchQuote`. */\n hint?: number;\n};\n\nexport type TextQuoteAnchorContext = {\n prefix?: string;\n suffix?: string;\n};\n\n/**\n * Converts between `TextQuoteSelector` selectors and `Range` objects.\n */\nexport class TextQuoteAnchor {\n root: Element;\n exact: string;\n context: TextQuoteAnchorContext;\n\n /**\n * @param root - A root element from which to anchor.\n */\n constructor(\n root: Element,\n exact: string,\n context: TextQuoteAnchorContext = {},\n ) {\n this.root = root;\n this.exact = exact;\n this.context = context;\n }\n\n /**\n * Create a `TextQuoteAnchor` from a range.\n *\n * Will throw if `range` does not contain any text nodes.\n */\n static fromRange(root: Element, range: Range): TextQuoteAnchor {\n const text = root.textContent!;\n const textRange = TextRange.fromRange(range).relativeTo(root);\n\n const start = textRange.start.offset;\n const end = textRange.end.offset;\n\n // Number of characters around the quote to capture as context. We currently\n // always use a fixed amount, but it would be better if this code was aware\n // of logical boundaries in the document (paragraph, article etc.) to avoid\n // capturing text unrelated to the quote.\n //\n // In regular prose the ideal content would often be the surrounding sentence.\n // This is a natural unit of meaning which enables displaying quotes in\n // context even when the document is not available. We could use `Intl.Segmenter`\n // for this when available.\n const contextLen = 32;\n\n return new TextQuoteAnchor(root, text.slice(start, end), {\n prefix: text.slice(Math.max(0, start - contextLen), start),\n suffix: text.slice(end, Math.min(text.length, end + contextLen)),\n });\n }\n\n static fromSelector(\n root: Element,\n selector: TextQuoteSelector,\n ): TextQuoteAnchor {\n const { prefix, suffix } = selector;\n return new TextQuoteAnchor(root, selector.exact, { prefix, suffix });\n }\n\n toSelector(): TextQuoteSelector {\n return {\n type: 'TextQuoteSelector',\n exact: this.exact,\n prefix: this.context.prefix,\n suffix: this.context.suffix,\n };\n }\n\n toRange(options: QuoteMatchOptions = {}): Range {\n return this.toPositionAnchor(options).toRange();\n }\n\n toPositionAnchor(options: QuoteMatchOptions = {}): TextPositionAnchor {\n const text = this.root.textContent!;\n const match = matchQuote(text, this.exact, {\n ...this.context,\n hint: options.hint,\n });\n if (!match) {\n throw new Error('Quote not found');\n }\n return new TextPositionAnchor(this.root, match.start, match.end);\n }\n}\n\n/**\n * Parse a string containing a time offset in seconds, since the start of some\n * media, into a float.\n */\nfunction parseMediaTime(timeStr: string): number | null {\n const val = parseFloat(timeStr);\n if (!Number.isFinite(val) || val < 0) {\n return null;\n }\n return val;\n}\n\n/** Implementation of {@link Array.prototype.findLastIndex} */\nfunction findLastIndex<T>(ary: T[], pred: (val: T) => boolean): number {\n for (let i = ary.length - 1; i >= 0; i--) {\n if (pred(ary[i])) {\n return i;\n }\n }\n return -1;\n}\n\nfunction closestElement(node: Node) {\n return node instanceof Element ? node : node.parentElement;\n}\n\n/**\n * Get the media time range associated with an element or pair of elements,\n * from `data-time-{start, end}` attributes on them.\n */\nfunction getMediaTimeRange(\n start: Element | undefined | null,\n end: Element | undefined | null = start,\n): [number, number] | null {\n const startTime = parseMediaTime(\n start?.getAttribute('data-time-start') ?? '',\n );\n const endTime = parseMediaTime(end?.getAttribute('data-time-end') ?? '');\n if (\n typeof startTime !== 'number' ||\n typeof endTime !== 'number' ||\n endTime < startTime\n ) {\n return null;\n }\n return [startTime, endTime];\n}\n\nexport class MediaTimeAnchor {\n root: Element;\n\n /** Offset from start of media in seconds. */\n start: number;\n /** Offset from end of media in seconds. */\n end: number;\n\n constructor(root: Element, start: number, end: number) {\n this.root = root;\n this.start = start;\n this.end = end;\n }\n\n /**\n * Return a {@link MediaTimeAnchor} that represents a range, or `null` if\n * no time range information is present on elements in the range.\n */\n static fromRange(root: Element, range: Range): MediaTimeAnchor | null {\n const start = closestElement(range.startContainer)?.closest(\n '[data-time-start]',\n );\n const end = closestElement(range.endContainer)?.closest('[data-time-end]');\n const timeRange = getMediaTimeRange(start, end);\n if (!timeRange) {\n return null;\n }\n const [startTime, endTime] = timeRange;\n return new MediaTimeAnchor(root, startTime, endTime);\n }\n\n /**\n * Convert this anchor to a DOM range.\n *\n * This returned range will start from the beginning of the element whose\n * associated time range includes `start` and continue to the end of the\n * element whose associated time range includes `end`.\n */\n toRange(): Range {\n // Find the segments that span the start and end times of this anchor.\n // This is inefficient since we re-find all segments for each annotation\n // that is anchored. Changing this will involve revising the anchoring\n // API however.\n type Segment = { element: Element; start: number; end: number };\n const segments = [...this.root.querySelectorAll('[data-time-start]')]\n .map(element => {\n const timeRange = getMediaTimeRange(element);\n if (!timeRange) {\n return null;\n }\n const [start, end] = timeRange;\n return { element, start, end };\n })\n .filter(s => s !== null) as Segment[];\n segments.sort((a, b) => a.start - b.start);\n\n const startIdx = findLastIndex(\n segments,\n s => s.start <= this.start && s.end >= this.start,\n );\n if (startIdx === -1) {\n throw new Error('Start segment not found');\n }\n const endIdx =\n startIdx +\n segments\n .slice(startIdx)\n .findIndex(s => s.start <= this.end && s.end >= this.end);\n if (endIdx === -1) {\n throw new Error('End segment not found');\n }\n\n const range = new Range();\n range.setStart(segments[startIdx].element, 0);\n\n const endEl = segments[endIdx].element;\n range.setEnd(endEl, endEl.childNodes.length);\n\n return range;\n }\n\n static fromSelector(\n root: Element,\n selector: MediaTimeSelector,\n ): MediaTimeAnchor {\n const { start, end } = selector;\n return new MediaTimeAnchor(root, start, end);\n }\n\n toSelector(): MediaTimeSelector {\n return {\n type: 'MediaTimeSelector',\n start: this.start,\n end: this.end,\n };\n }\n}\n","import type {\n MediaTimeSelector,\n RangeSelector,\n Selector,\n TextPositionSelector,\n TextQuoteSelector,\n} from '../../types/api';\nimport {\n MediaTimeAnchor,\n RangeAnchor,\n TextPositionAnchor,\n TextQuoteAnchor,\n} from './types';\n\ntype Options = {\n hint?: number;\n};\n\nasync function querySelector(\n anchor: MediaTimeAnchor | RangeAnchor | TextPositionAnchor | TextQuoteAnchor,\n options: Options,\n) {\n return anchor.toRange(options);\n}\n\n/**\n * Anchor a set of selectors.\n *\n * This function converts a set of selectors into a document range.\n * It encapsulates the core anchoring algorithm, using the selectors alone or\n * in combination to establish the best anchor within the document.\n *\n * @param root - The root element of the anchoring context\n * @param selectors - The selectors to try\n */\nexport function anchor(\n root: Element,\n selectors: Selector[],\n options: Options = {},\n) {\n let mediaTime: MediaTimeSelector | null = null;\n let position: TextPositionSelector | null = null;\n let quote: TextQuoteSelector | null = null;\n let range: RangeSelector | null = null;\n\n // Collect all the selectors\n for (const selector of selectors) {\n switch (selector.type) {\n case 'TextPositionSelector':\n position = selector;\n options.hint = position.start; // TextQuoteAnchor hint\n break;\n case 'TextQuoteSelector':\n quote = selector;\n break;\n case 'RangeSelector':\n range = selector;\n break;\n case 'MediaTimeSelector':\n mediaTime = selector;\n break;\n }\n }\n\n /**\n * Assert the quote matches the stored quote, if applicable\n */\n const maybeAssertQuote = (range: Range) => {\n if (quote?.exact && range.toString() !== quote.exact) {\n throw new Error('quote mismatch');\n } else {\n return range;\n }\n };\n\n // From a default of failure, we build up catch clauses to try selectors in\n // order, from simple to complex.\n let promise: Promise<Range> = Promise.reject('unable to anchor');\n\n if (range) {\n // Const binding assures TS that it won't be re-assigned when callback runs.\n const range_ = range;\n promise = promise.catch(() => {\n const anchor = RangeAnchor.fromSelector(root, range_);\n return querySelector(anchor, options).then(maybeAssertQuote);\n });\n }\n\n if (position) {\n const position_ = position;\n promise = promise.catch(() => {\n const anchor = TextPositionAnchor.fromSelector(root, position_);\n return querySelector(anchor, options).then(maybeAssertQuote);\n });\n }\n\n if (quote) {\n const quote_ = quote;\n promise = promise.catch(() => {\n const anchor = TextQuoteAnchor.fromSelector(root, quote_);\n return querySelector(anchor, options);\n });\n }\n\n if (mediaTime) {\n const mediaTime_ = mediaTime;\n promise = promise.catch(() =>\n MediaTimeAnchor.fromSelector(root, mediaTime_).toRange(),\n );\n }\n\n return promise;\n}\n\nexport function describe(root: Element, range: Range) {\n const types = [\n MediaTimeAnchor,\n RangeAnchor,\n TextPositionAnchor,\n TextQuoteAnchor,\n ];\n const result = [];\n for (const type of types) {\n try {\n const anchor = type.fromRange(root, range);\n if (anchor) {\n result.push(anchor.toSelector());\n }\n } catch {\n // If resolving some anchor fails, we just want to skip it silently\n }\n }\n return result;\n}\n","/* global Navigation */\nimport { ListenerCollection } from '@hypothesis/frontend-shared';\n\n/**\n * Monkey-patch an object to observe calls to a method.\n *\n * The `handler` is not invoked if the observed method throws.\n *\n * @param handler - Handler that is invoked after the monitored method has been called.\n * @return Callback that removes the observer and restores `object[method]`.\n */\nfunction observeCalls<T>(\n object: T,\n method: keyof T,\n handler: (...args: unknown[]) => void,\n): () => void {\n const origHandler = object[method];\n\n /* istanbul ignore next */\n if (typeof origHandler !== 'function') {\n throw new Error('Can only intercept functions');\n }\n\n const wrapper = (...args: unknown[]) => {\n const result = origHandler.call(object, ...args);\n handler(...args);\n return result;\n };\n // @ts-expect-error Already checked type is function some lines above\n object[method] = wrapper;\n\n return () => {\n object[method] = origHandler;\n };\n}\n\nfunction stripFragment(url: string): string {\n return url.replace(/#.*/, '');\n}\n\n/**\n * Return the Navigation API entry point for the current window.\n *\n * This is a wrapper around `window.navigation` which checks both that the\n * object exists and has the expected type. See also\n * https://github.com/hypothesis/client/issues/5324.\n */\nexport function getNavigation(): EventTarget | null {\n const navigation = (window as any).navigation;\n if (\n // @ts-expect-error - Navigation API is missing from TS\n typeof Navigation === 'function' &&\n // @ts-expect-error\n navigation instanceof Navigation\n ) {\n return navigation;\n }\n return null;\n}\n\n/**\n * Utility for detecting client-side navigations of an HTML document.\n *\n * This uses the Navigation API [1] if available, or falls back to\n * monkey-patching the History API [2] otherwise.\n *\n * Only navigations which change the path or query params are reported. URL\n * updates which change only the hash fragment are assumed to be navigations to\n * different parts of the same logical document. Also Hypothesis in general\n * ignores the hash fragment when comparing URLs.\n *\n * [1] https://wicg.github.io/navigation-api/\n * [2] https://developer.mozilla.org/en-US/docs/Web/API/History\n */\nexport class NavigationObserver {\n private _listeners: ListenerCollection;\n private _unpatchHistory: (() => void) | undefined;\n\n /**\n * Begin observing navigation changes.\n *\n * @param onNavigate - Callback invoked when a navigation\n * occurs. The callback is fired after the navigation has completed and the\n * new URL is reflected in `location.href`.\n * @param getLocation - Test seam that returns the current URL\n */\n constructor(\n onNavigate: (url: string) => void,\n /* istanbul ignore next - default overridden in tests */\n getLocation = () => location.href,\n ) {\n this._listeners = new ListenerCollection();\n\n let lastURL = getLocation();\n const checkForURLChange = (newURL = getLocation()) => {\n if (stripFragment(lastURL) !== stripFragment(newURL)) {\n lastURL = newURL;\n onNavigate(newURL);\n }\n };\n\n const navigation = getNavigation();\n if (navigation) {\n this._listeners.add(navigation, 'navigatesuccess', () =>\n checkForURLChange(),\n );\n } else {\n const unpatchers = [\n observeCalls(window.history, 'pushState', () => checkForURLChange()),\n observeCalls(window.history, 'replaceState', () => checkForURLChange()),\n ];\n this._unpatchHistory = () => unpatchers.forEach(cleanup => cleanup());\n this._listeners.add(window, 'popstate', () => checkForURLChange());\n }\n }\n\n /** Stop observing navigation changes. */\n disconnect() {\n this._unpatchHistory?.();\n this._listeners.removeAll();\n }\n}\n","var COMPLETE = 'complete',\n CANCELED = 'canceled';\n\nfunction raf(task){\n if('requestAnimationFrame' in window){\n return window.requestAnimationFrame(task);\n }\n\n setTimeout(task, 16);\n}\n\nfunction setElementScroll(element, x, y){\n Math.max(0, x);\n Math.max(0, y);\n\n if(element.self === element){\n element.scrollTo(x, y);\n }else{\n element.scrollLeft = x;\n element.scrollTop = y;\n }\n}\n\nfunction getTargetScrollLocation(scrollSettings, parent){\n var align = scrollSettings.align,\n target = scrollSettings.target,\n targetPosition = target.getBoundingClientRect(),\n parentPosition,\n x,\n y,\n differenceX,\n differenceY,\n targetWidth,\n targetHeight,\n leftAlign = align && align.left != null ? align.left : 0.5,\n topAlign = align && align.top != null ? align.top : 0.5,\n leftOffset = align && align.leftOffset != null ? align.leftOffset : 0,\n topOffset = align && align.topOffset != null ? align.topOffset : 0,\n leftScalar = leftAlign,\n topScalar = topAlign;\n\n if(scrollSettings.isWindow(parent)){\n targetWidth = Math.min(targetPosition.width, parent.innerWidth);\n targetHeight = Math.min(targetPosition.height, parent.innerHeight);\n x = targetPosition.left + parent.pageXOffset - parent.innerWidth * leftScalar + targetWidth * leftScalar;\n y = targetPosition.top + parent.pageYOffset - parent.innerHeight * topScalar + targetHeight * topScalar;\n x -= leftOffset;\n y -= topOffset;\n x = scrollSettings.align.lockX ? parent.pageXOffset : x;\n y = scrollSettings.align.lockY ? parent.pageYOffset : y;\n differenceX = x - parent.pageXOffset;\n differenceY = y - parent.pageYOffset;\n }else{\n targetWidth = targetPosition.width;\n targetHeight = targetPosition.height;\n parentPosition = parent.getBoundingClientRect();\n var offsetLeft = targetPosition.left - (parentPosition.left - parent.scrollLeft);\n var offsetTop = targetPosition.top - (parentPosition.top - parent.scrollTop);\n x = offsetLeft + (targetWidth * leftScalar) - parent.clientWidth * leftScalar;\n y = offsetTop + (targetHeight * topScalar) - parent.clientHeight * topScalar;\n x -= leftOffset;\n y -= topOffset;\n x = Math.max(Math.min(x, parent.scrollWidth - parent.clientWidth), 0);\n y = Math.max(Math.min(y, parent.scrollHeight - parent.clientHeight), 0);\n x = scrollSettings.align.lockX ? parent.scrollLeft : x;\n y = scrollSettings.align.lockY ? parent.scrollTop : y;\n differenceX = x - parent.scrollLeft;\n differenceY = y - parent.scrollTop;\n }\n\n return {\n x: x,\n y: y,\n differenceX: differenceX,\n differenceY: differenceY\n };\n}\n\nfunction animate(parent){\n var scrollSettings = parent._scrollSettings;\n\n if(!scrollSettings){\n return;\n }\n\n var maxSynchronousAlignments = scrollSettings.maxSynchronousAlignments;\n\n var location = getTargetScrollLocation(scrollSettings, parent),\n time = Date.now() - scrollSettings.startTime,\n timeValue = Math.min(1 / scrollSettings.time * time, 1);\n\n if(scrollSettings.endIterations >= maxSynchronousAlignments){\n setElementScroll(parent, location.x, location.y);\n parent._scrollSettings = null;\n return scrollSettings.end(COMPLETE);\n }\n\n var easeValue = 1 - scrollSettings.ease(timeValue);\n\n setElementScroll(parent,\n location.x - location.differenceX * easeValue,\n location.y - location.differenceY * easeValue\n );\n\n if(time >= scrollSettings.time){\n scrollSettings.endIterations++;\n // Align ancestor synchronously\n scrollSettings.scrollAncestor && animate(scrollSettings.scrollAncestor);\n animate(parent);\n return;\n }\n\n raf(animate.bind(null, parent));\n}\n\nfunction defaultIsWindow(target){\n return target.self === target\n}\n\nfunction transitionScrollTo(target, parent, settings, scrollAncestor, callback){\n var idle = !parent._scrollSettings,\n lastSettings = parent._scrollSettings,\n now = Date.now(),\n cancelHandler,\n passiveOptions = { passive: true };\n\n if(lastSettings){\n lastSettings.end(CANCELED);\n }\n\n function end(endType){\n parent._scrollSettings = null;\n\n if(parent.parentElement && parent.parentElement._scrollSettings){\n parent.parentElement._scrollSettings.end(endType);\n }\n\n if(settings.debug){\n console.log('Scrolling ended with type', endType, 'for', parent)\n }\n\n callback(endType);\n if(cancelHandler){\n parent.removeEventListener('touchstart', cancelHandler, passiveOptions);\n parent.removeEventListener('wheel', cancelHandler, passiveOptions);\n }\n }\n\n var maxSynchronousAlignments = settings.maxSynchronousAlignments;\n\n if(maxSynchronousAlignments == null){\n maxSynchronousAlignments = 3;\n }\n\n parent._scrollSettings = {\n startTime: now,\n endIterations: 0,\n target: target,\n time: settings.time,\n ease: settings.ease,\n align: settings.align,\n isWindow: settings.isWindow || defaultIsWindow,\n maxSynchronousAlignments: maxSynchronousAlignments,\n end: end,\n scrollAncestor\n };\n\n if(!('cancellable' in settings) || settings.cancellable){\n cancelHandler = end.bind(null, CANCELED);\n parent.addEventListener('touchstart', cancelHandler, passiveOptions);\n parent.addEventListener('wheel', cancelHandler, passiveOptions);\n }\n\n if(idle){\n animate(parent);\n }\n\n return cancelHandler\n}\n\nfunction defaultIsScrollable(element){\n return (\n 'pageXOffset' in element ||\n (\n element.scrollHeight !== element.clientHeight ||\n element.scrollWidth !== element.clientWidth\n ) &&\n getComputedStyle(element).overflow !== 'hidden'\n );\n}\n\nfunction defaultValidTarget(){\n return true;\n}\n\nfunction findParentElement(el){\n if (el.assignedSlot) {\n return findParentElement(el.assignedSlot);\n }\n\n if (el.parentElement) {\n if(el.parentElement.tagName.toLowerCase() === 'body'){\n return el.parentElement.ownerDocument.defaultView || el.parentElement.ownerDocument.ownerWindow;\n }\n return el.parentElement;\n }\n\n if (el.getRootNode){\n var parent = el.getRootNode()\n if(parent.nodeType === 11) {\n return parent.host;\n }\n }\n}\n\nmodule.exports = function(target, settings, callback){\n if(!target){\n return;\n }\n\n if(typeof settings === 'function'){\n callback = settings;\n settings = null;\n }\n\n if(!settings){\n settings = {};\n }\n\n settings.time = isNaN(settings.time) ? 1000 : settings.time;\n settings.ease = settings.ease || function(v){return 1 - Math.pow(1 - v, v / 2);};\n settings.align = settings.align || {};\n\n var parent = findParentElement(target),\n parents = 1;\n\n function done(endType){\n parents--;\n if(!parents){\n callback && callback(endType);\n }\n }\n\n var validTarget = settings.validTarget || defaultValidTarget;\n var isScrollable = settings.isScrollable;\n\n if(settings.debug){\n console.log('About to scroll to', target)\n\n if(!parent){\n console.error('Target did not have a parent, is it mounted in the DOM?')\n }\n }\n\n var scrollingElements = [];\n\n while(parent){\n if(settings.debug){\n console.log('Scrolling parent node', parent)\n }\n\n if(validTarget(parent, parents) && (isScrollable ? isScrollable(parent, defaultIsScrollable) : defaultIsScrollable(parent))){\n parents++;\n scrollingElements.push(parent);\n }\n\n parent = findParentElement(parent);\n\n if(!parent){\n done(COMPLETE)\n break;\n }\n }\n\n return scrollingElements.reduce((cancel, parent, index) => transitionScrollTo(target, parent, settings, scrollingElements[index + 1], done), null);\n};\n","import scrollIntoView from 'scroll-into-view';\n\n/**\n * Return a promise that resolves on the next animation frame.\n */\nfunction nextAnimationFrame(): Promise<number> {\n return new Promise(resolve => {\n requestAnimationFrame(resolve);\n });\n}\n\n/**\n * Linearly interpolate between two values.\n *\n * @param fraction - Value in [0, 1]\n */\nfunction interpolate(a: number, b: number, fraction: number): number {\n return a + fraction * (b - a);\n}\n\n/**\n * Return the offset of `element` from the top of a positioned ancestor `parent`.\n *\n * @param parent - Positioned ancestor of `element`\n */\nexport function offsetRelativeTo(\n element: HTMLElement,\n parent: HTMLElement,\n): number {\n let offset = 0;\n while (element !== parent && parent.contains(element)) {\n offset += element.offsetTop;\n element = element.offsetParent as HTMLElement;\n }\n return offset;\n}\n\nexport type DurationOptions = { maxDuration?: number };\n\n/**\n * Scroll `element` until its `scrollTop` offset reaches a target value.\n *\n * @param element - Container element to scroll\n * @param offset - Target value for the scroll offset\n * @return A promise that resolves once the scroll animation is complete\n */\nexport async function scrollElement(\n element: Element,\n offset: number,\n /* istanbul ignore next - defaults are overridden in tests */\n { maxDuration = 500 }: DurationOptions = {},\n): Promise<void> {\n const startOffset = element.scrollTop;\n const endOffset = offset;\n const scrollStart = Date.now();\n\n // Choose a scroll duration proportional to the scroll distance, but capped\n // to avoid it being too slow.\n const pixelsPerMs = 3;\n const scrollDuration = Math.min(\n Math.abs(endOffset - startOffset) / pixelsPerMs,\n maxDuration,\n );\n\n let scrollFraction = 0.0;\n while (scrollFraction < 1.0) {\n await nextAnimationFrame();\n scrollFraction = Math.min(1.0, (Date.now() - scrollStart) / scrollDuration);\n element.scrollTop = interpolate(startOffset, endOffset, scrollFraction);\n }\n}\n\n/**\n * Smoothly scroll an element into view.\n */\nexport async function scrollElementIntoView(\n element: HTMLElement,\n /* istanbul ignore next - defaults are overridden in tests */\n { maxDuration = 500 }: DurationOptions = {},\n): Promise<void> {\n // Make the body's `tagName` return an upper-case string in XHTML documents\n // like it does in HTML documents. This is a workaround for\n // `scrollIntoView`'s detection of the <body> element. See\n // https://github.com/KoryNunn/scroll-into-view/issues/101.\n const body = element.closest('body');\n if (body && body.tagName !== 'BODY') {\n Object.defineProperty(body, 'tagName', {\n value: 'BODY',\n configurable: true,\n });\n }\n\n // Ensure that the details are open before scrolling, in case the annotation\n // is within the details tag. This guarantees that the user can promptly view\n // the content on the screen.\n const details = element.closest('details');\n if (details && !details.hasAttribute('open')) {\n details.open = true;\n }\n\n await new Promise(resolve =>\n scrollIntoView(element, { time: maxDuration }, resolve),\n );\n}\n","/**\n * Return a normalized version of a URI.\n *\n * This makes it absolute and strips the fragment identifier.\n *\n * @param uri - Relative or absolute URL\n * @param base - Base URL to resolve relative to. Defaults to the document's base URL.\n */\nexport function normalizeURI(\n uri: string,\n base: string = document.baseURI,\n): string {\n const absUrl = new URL(uri, base).href;\n\n // Remove the fragment identifier.\n // This is done on the serialized URL rather than modifying `url.hash` due to\n // a bug in Safari.\n // See https://github.com/hypothesis/h/issues/3471#issuecomment-226713750\n return absUrl.toString().replace(/#.*/, '');\n}\n","/*\n ** Adapted from:\n ** https://github.com/openannotation/annotator/blob/v1.2.x/src/plugin/document.coffee\n **\n ** Annotator v1.2.10\n ** https://github.com/openannotation/annotator\n **\n ** Copyright 2015, the Annotator project contributors.\n ** Dual licensed under the MIT and GPLv3 licenses.\n ** https://github.com/openannotation/annotator/blob/master/LICENSE\n */\nimport { normalizeURI } from '../util/url';\n\ntype Link = {\n href: string;\n rel?: string;\n type?: string;\n};\n\n/**\n * Extension of the `Metadata` type with non-optional fields for `dc`, `eprints` etc.\n */\ntype HTMLDocumentMetadata = {\n title: string;\n link: Link[];\n dc: Record<string, string[]>;\n eprints: Record<string, string[]>;\n facebook: Record<string, string[]>;\n highwire: Record<string, string[]>;\n prism: Record<string, string[]>;\n twitter: Record<string, string[]>;\n favicon?: string;\n documentFingerprint?: string;\n};\n\n/**\n * HTMLMetadata reads metadata/links from the current HTML document.\n */\nexport class HTMLMetadata {\n document: Document;\n\n constructor(options: { document?: Document } = {}) {\n this.document = options.document || document;\n }\n\n /**\n * Returns the primary URI for the document being annotated\n */\n uri(): string {\n let uri = this._getDocumentHref(); // Get the URI without decoding it first\n\n // Attempt to decode the URI, handle exceptions if the URI is malformed\n try {\n uri = decodeURIComponent(uri);\n } catch (error) {\n // Log error for debugging. After this point we fall back to the original URI\n console.error('Error decoding URI:', error);\n }\n\n // Use the `link[rel=canonical]` element's href as the URI if present.\n const links = this._getLinks();\n for (const link of links) {\n if (link.rel === 'canonical') {\n uri = link.href; // Assuming canonical hrefs are correctly encoded\n }\n }\n return uri;\n }\n\n /**\n * Return metadata for the current page.\n */\n getDocumentMetadata(): HTMLDocumentMetadata {\n const metadata: HTMLDocumentMetadata = {\n title: document.title,\n link: [],\n\n dc: this._getMetaTags('name', 'dc.'),\n eprints: this._getMetaTags('name', 'eprints.'),\n facebook: this._getMetaTags('property', 'og:'),\n highwire: this._getMetaTags('name', 'citation_'),\n prism: this._getMetaTags('name', 'prism.'),\n twitter: this._getMetaTags('name', 'twitter:'),\n };\n\n const favicon = this._getFavicon();\n if (favicon) {\n metadata.favicon = favicon;\n }\n\n metadata.title = this._getTitle(metadata);\n metadata.link = this._getLinks(metadata);\n\n const dcLink = metadata.link.find(link => link.href.startsWith('urn:x-dc'));\n if (dcLink) {\n metadata.documentFingerprint = dcLink.href;\n }\n\n return metadata;\n }\n\n /**\n * Return an array of all the `content` values of `<meta>` tags on the page\n * where the value of the attribute begins with `<prefix>`.\n *\n * @param prefix - it is interpreted as a regex\n */\n private _getMetaTags(\n attribute: string,\n prefix: string,\n ): Record<string, string[]> {\n const tags: Record<string, string[]> = {};\n for (const meta of Array.from(this.document.querySelectorAll('meta'))) {\n const name = meta.getAttribute(attribute);\n const { content } = meta;\n if (name && content) {\n const match = name.match(RegExp(`^${prefix}(.+)$`, 'i'));\n if (match) {\n const key = match[1].toLowerCase();\n if (tags[key]) {\n tags[key].push(content);\n } else {\n tags[key] = [content];\n }\n }\n }\n }\n return tags;\n }\n\n private _getTitle(metadata: HTMLDocumentMetadata): string {\n if (metadata.highwire.title) {\n return metadata.highwire.title[0];\n } else if (metadata.eprints.title) {\n return metadata.eprints.title[0];\n } else if (metadata.prism.title) {\n return metadata.prism.title[0];\n } else if (metadata.facebook.title) {\n return metadata.facebook.title[0];\n } else if (metadata.twitter.title) {\n return metadata.twitter.title[0];\n } else if (metadata.dc.title) {\n return metadata.dc.title[0];\n } else {\n return this.document.title;\n }\n }\n\n /**\n * Get document URIs from `<link>` and `<meta>` elements on the page.\n *\n * @param [metadata] - Dublin Core and Highwire metadata parsed from `<meta>` tags.\n */\n private _getLinks(\n metadata: Pick<HTMLDocumentMetadata, 'highwire' | 'dc'> = {\n dc: {},\n highwire: {},\n },\n ): Link[] {\n const links: Link[] = [{ href: this._getDocumentHref() }];\n\n // Extract links from `<link>` tags with certain `rel` values.\n const linkElements = Array.from(this.document.querySelectorAll('link'));\n for (const link of linkElements) {\n if (\n !['alternate', 'canonical', 'bookmark', 'shortlink'].includes(link.rel)\n ) {\n continue;\n }\n\n if (link.rel === 'alternate') {\n // Ignore RSS feed links.\n if (link.type && link.type.match(/^application\\/(rss|atom)\\+xml/)) {\n continue;\n }\n // Ignore alternate languages.\n if (link.hreflang) {\n continue;\n }\n }\n\n try {\n const href = this._absoluteUrl(link.href);\n links.push({ href, rel: link.rel, type: link.type });\n } catch {\n // Ignore URIs which cannot be parsed.\n }\n }\n\n // Look for links in scholar metadata\n for (const name of Object.keys(metadata.highwire)) {\n const values = metadata.highwire[name];\n if (name === 'pdf_url') {\n for (const url of values) {\n try {\n links.push({\n href: this._absoluteUrl(url),\n type: 'application/pdf',\n });\n } catch {\n // Ignore URIs which cannot be parsed.\n }\n }\n }\n\n // Kind of a hack to express DOI identifiers as links but it's a\n // convenient place to look them up later, and somewhat sane since\n // they don't have a type.\n if (name === 'doi') {\n for (let doi of values) {\n if (doi.slice(0, 4) !== 'doi:') {\n doi = `doi:${doi}`;\n }\n links.push({ href: doi });\n }\n }\n }\n\n // Look for links in Dublin Core data\n for (const name of Object.keys(metadata.dc)) {\n const values = metadata.dc[name];\n if (name === 'identifier') {\n for (const id of values) {\n if (id.slice(0, 4) === 'doi:') {\n links.push({ href: id });\n }\n }\n }\n }\n\n // Look for a link to identify the resource in Dublin Core metadata\n const dcRelationValues = metadata.dc['relation.ispartof'];\n const dcIdentifierValues = metadata.dc.identifier;\n if (dcRelationValues && dcIdentifierValues) {\n const dcUrnRelationComponent =\n dcRelationValues[dcRelationValues.length - 1];\n const dcUrnIdentifierComponent =\n dcIdentifierValues[dcIdentifierValues.length - 1];\n const dcUrn =\n 'urn:x-dc:' +\n encodeURIComponent(dcUrnRelationComponent) +\n '/' +\n encodeURIComponent(dcUrnIdentifierComponent);\n links.push({ href: dcUrn });\n }\n\n return links;\n }\n\n private _getFavicon(): string | null {\n let favicon = null;\n for (const link of Array.from(this.document.querySelectorAll('link'))) {\n if (['shortcut icon', 'icon'].includes(link.rel)) {\n try {\n favicon = this._absoluteUrl(link.href);\n } catch {\n // Ignore URIs which cannot be parsed.\n }\n }\n }\n return favicon;\n }\n\n /**\n * Convert a possibly relative URI to an absolute one. This will throw an\n * exception if the URL cannot be parsed.\n */\n private _absoluteUrl(url: string): string {\n return normalizeURI(url, this.document.baseURI);\n }\n\n /**\n * Get the true URI record when it's masked via a different protocol.\n * This happens when an href is set with a uri using the 'blob:' protocol\n * but the document can set a different uri through a <base> tag.\n */\n private _getDocumentHref(): string {\n const { href } = this.document.location;\n const allowedSchemes = ['http:', 'https:', 'file:'];\n\n // Use the current document location if it has a recognized scheme.\n const scheme = new URL(href).protocol;\n if (allowedSchemes.includes(scheme)) {\n return href;\n }\n\n // Otherwise, try using the location specified by the <base> element.\n if (\n this.document.baseURI &&\n allowedSchemes.includes(new URL(this.document.baseURI).protocol)\n ) {\n return this.document.baseURI;\n }\n\n // Fall back to returning the document URI, even though the scheme is not\n // in the allowed list.\n return href;\n }\n}\n","import { EventEmitter } from '../../shared/event-emitter';\nimport type {\n Anchor,\n AnnotationTool,\n FeatureFlags,\n Integration,\n IntegrationEvents,\n Shape,\n SidebarLayout,\n SideBySideOptions,\n} from '../../types/annotator';\nimport type { Selector } from '../../types/api';\nimport { anchor, describe } from '../anchoring/html';\nimport { TextRange } from '../anchoring/text-range';\nimport { NavigationObserver } from '../util/navigation-observer';\nimport { scrollElementIntoView } from '../util/scroll';\nimport { HTMLMetadata } from './html-metadata';\nimport {\n guessMainContentArea,\n preserveScrollPosition,\n} from './html-side-by-side';\n\n// When activating side-by-side mode, make sure there is at least this amount\n// of space (in pixels) left for the document's content. Any narrower and the\n// content line lengths and scale are too short to be readable.\nconst MIN_HTML_WIDTH = 480;\n\n/**\n * Document type integration for ordinary web pages.\n *\n * This integration is used for web pages and applications that are not handled\n * by a more specific integration (eg. for PDFs).\n */\nexport class HTMLIntegration\n extends EventEmitter<IntegrationEvents>\n implements Integration\n{\n container: HTMLElement;\n featureFlags: FeatureFlags;\n\n private _flagsChanged: () => void;\n private _htmlMeta: HTMLMetadata;\n private _prevURI: string;\n\n /** Controls how we resize the document to fit alongside sidebar. */\n private _sideBySideOptions: SideBySideOptions;\n private _sideBySideEnabled: boolean;\n\n /**\n * Whether the document is currently being resized to fit alongside an\n * open sidebar.\n */\n private _sideBySideActive: boolean;\n\n private _lastLayout: SidebarLayout | null;\n\n private _navObserver: NavigationObserver;\n private _metaObserver: MutationObserver;\n\n constructor({\n features,\n container = document.body,\n sideBySideOptions,\n }: {\n features: FeatureFlags;\n container?: HTMLElement;\n sideBySideOptions?: SideBySideOptions;\n }) {\n super();\n\n this.featureFlags = features;\n this.container = container;\n\n this._htmlMeta = new HTMLMetadata();\n this._prevURI = this._htmlMeta.uri();\n\n // Side-by-side was originally behind a feature flag. This property\n // remains in case it is useful to turn off for debugging etc.\n this._sideBySideEnabled = true;\n\n this._sideBySideOptions = sideBySideOptions ?? { mode: 'auto' };\n this._sideBySideActive = false;\n this._lastLayout = null;\n\n // Watch for changes to `location.href`.\n this._navObserver = new NavigationObserver(() => this._checkForURIChange());\n\n // Watch for potential changes to location information in `<head>`, eg.\n // `<link rel=canonical>`.\n this._metaObserver = new MutationObserver(() => this._checkForURIChange());\n this._metaObserver.observe(document.head, {\n childList: true,\n subtree: true,\n attributes: true,\n\n attributeFilter: [\n // Keys and values of <link> elements\n 'rel',\n 'href',\n\n // Keys and values of <meta> elements\n 'name',\n 'content',\n ],\n });\n\n this._flagsChanged = () => {\n // There are currently no feature flags that the integration responds to.\n };\n this.featureFlags.on('flagsChanged', this._flagsChanged);\n }\n\n anchor(root: Element, selectors: Selector[]): Promise<Range> {\n return anchor(root, selectors);\n }\n\n describe(root: Element, region: Range | Shape): Selector[] {\n if (region instanceof Range) {\n return describe(root, region);\n } else {\n throw new Error('Unsupported region type');\n }\n }\n\n _checkForURIChange() {\n const currentURI = this._htmlMeta.uri();\n if (currentURI !== this._prevURI) {\n this._prevURI = currentURI;\n this.emit('uriChanged', currentURI);\n }\n }\n\n /**\n * Return a Range trimmed to remove any leading or trailing whitespace, or\n * `null` if no valid trimmed Range can be created from `range`\n */\n getAnnotatableRange(range: Range) {\n try {\n return TextRange.trimmedRange(range);\n } catch (err) {\n if (err instanceof RangeError) {\n return null;\n }\n throw err;\n }\n }\n\n canStyleClusteredHighlights() {\n return true;\n }\n\n destroy() {\n this._deactivateSideBySide();\n this._navObserver.disconnect();\n this._metaObserver.disconnect();\n this.featureFlags.off('flagsChanged', this._flagsChanged);\n super.destroy();\n }\n\n contentContainer() {\n return this.container;\n }\n\n fitSideBySide(layout: SidebarLayout) {\n this._lastLayout = layout;\n\n const maximumWidthToFit = window.innerWidth - layout.width;\n const active =\n this._sideBySideEnabled &&\n this._sideBySideOptions.mode === 'auto' &&\n layout.expanded &&\n maximumWidthToFit >= MIN_HTML_WIDTH;\n\n if (active) {\n // nb. We call `_activateSideBySide` regardless of whether side-by-side\n // is already active because the sidebar width might be different.\n this._activateSideBySide(layout.width);\n } else if (this._sideBySideActive) {\n this._deactivateSideBySide();\n }\n this._sideBySideActive = active;\n this.container.classList.toggle(\n 'hypothesis-sidebyside-active',\n this._sideBySideActive,\n );\n return active;\n }\n\n sideBySideActive() {\n return this._sideBySideActive;\n }\n\n supportedTools(): AnnotationTool[] {\n return ['selection'];\n }\n\n /**\n * Resize the document content after side-by-side mode is activated.\n */\n _activateSideBySide(sidebarWidth: number) {\n // When side-by-side mode is activated, what we want to achieve is that the\n // main content of the page is fully visible alongside the sidebar, with\n // as much space given to the main content as possible. A challenge is that\n // we don't know how the page will respond to reducing the width of the body.\n //\n // - The content might have margins which automatically get reduced as the\n // available width is reduced. For example a blog post with a fixed-width\n // article in the middle and `margin: auto` for both margins.\n //\n // In this scenario we'd want to reduce the document width by the full\n // width of the sidebar.\n //\n // - There might be sidebars to the left and/or right of the main content\n // which cause the main content to be squashed when the width is reduced.\n // For example a news website with a column of ads on the right.\n //\n // In this scenario we'd want to not reduce the document width or reduce\n // it by a smaller amount and let the Hypothesis sidebar cover up the\n // document's sidebar, leaving as much space as possible to the content.\n //\n // Therefore what we do is to initially reduce the width of the document by\n // the full width of the sidebar, then we use heuristics to analyze the\n // resulting page layout and determine whether there is significant \"free space\"\n // (ie. anything that is not the main content of the document, such as ads or\n // links to related stories) to the right of the main content. If there is,\n // we make the document wider again to allow more space for the main content.\n //\n // These heuristics assume a typical \"article\" page with one central block\n // of content. If we can't find the \"main content\" then we just assume that\n // everything on the page is potentially content that the user might want\n // to annotate and so try to keep it all visible.\n\n // nb. 12px padding is a multiple of the 4px grid unit in our design system.\n const padding = 12;\n const rightMargin = sidebarWidth + padding;\n\n const computeLeftMargin = (element: HTMLElement) =>\n parseInt(window.getComputedStyle(element).marginLeft, 10);\n\n preserveScrollPosition(() => {\n // nb. Adjusting the body size this way relies on the page not setting a\n // width on the body. For sites that do this won't work.\n\n // Remove any margins we've previously set\n document.body.style.marginLeft = '';\n document.body.style.marginRight = '';\n\n // Keep track of what left margin would be naturally without right margin set\n const beforeBodyLeft = computeLeftMargin(document.body);\n\n document.body.style.marginRight = `${rightMargin}px`;\n\n const contentArea = guessMainContentArea(document.body);\n if (contentArea) {\n // Check if we can give the main content more space by letting the\n // sidebar overlap stuff in the document to the right of the main content.\n const freeSpace = Math.max(\n 0,\n window.innerWidth - rightMargin - contentArea.right,\n );\n if (freeSpace > 0) {\n const adjustedMargin = Math.max(0, rightMargin - freeSpace);\n document.body.style.marginRight = `${adjustedMargin}px`;\n }\n\n // Changes to right margin can affect left margin in cases where body\n // has `margin:auto`. It's OK to move the body to the left to make\n // space, but avoid moving it to the right.\n // See https://github.com/hypothesis/client/issues/4280\n const afterBodyLeft = computeLeftMargin(document.body);\n if (afterBodyLeft > beforeBodyLeft) {\n document.body.style.marginLeft = `${beforeBodyLeft}px`;\n }\n\n // If the main content appears to be right up against the edge of the\n // window, add padding for readability.\n if (contentArea.left < padding) {\n document.body.style.marginLeft = `${padding}px`;\n }\n } else {\n document.body.style.marginLeft = '';\n document.body.style.marginRight = '';\n }\n });\n }\n\n /**\n * Undo the effects of `activateSideBySide`.\n */\n _deactivateSideBySide() {\n preserveScrollPosition(() => {\n document.body.style.marginLeft = '';\n document.body.style.marginRight = '';\n });\n }\n\n async getMetadata() {\n return this._htmlMeta.getDocumentMetadata();\n }\n\n async uri() {\n return this._htmlMeta.uri();\n }\n\n async scrollToAnchor(anchor: Anchor) {\n const highlight = anchor.highlights?.[0];\n if (!highlight) {\n return;\n }\n await scrollElementIntoView(highlight);\n }\n}\n","/**\n * lodash (Custom Build) <https://lodash.com/>\n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright jQuery Foundation and other contributors <https://jquery.org/>\n * Released under MIT license <https://lodash.com/license>\n * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>\n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n\n/** Used as the `TypeError` message for \"Functions\" methods. */\nvar FUNC_ERROR_TEXT = 'Expected a function';\n\n/** Used as references for various `Number` constants. */\nvar NAN = 0 / 0;\n\n/** `Object#toString` result references. */\nvar symbolTag = '[object Symbol]';\n\n/** Used to match leading and trailing whitespace. */\nvar reTrim = /^\\s+|\\s+$/g;\n\n/** Used to detect bad signed hexadecimal string values. */\nvar reIsBadHex = /^[-+]0x[0-9a-f]+$/i;\n\n/** Used to detect binary string values. */\nvar reIsBinary = /^0b[01]+$/i;\n\n/** Used to detect octal string values. */\nvar reIsOctal = /^0o[0-7]+$/i;\n\n/** Built-in method references without a dependency on `root`. */\nvar freeParseInt = parseInt;\n\n/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar objectToString = objectProto.toString;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeMax = Math.max,\n nativeMin = Math.min;\n\n/**\n * Gets the timestamp of the number of milliseconds that have elapsed since\n * the Unix epoch (1 January 1970 00:00:00 UTC).\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Date\n * @returns {number} Returns the timestamp.\n * @example\n *\n * _.defer(function(stamp) {\n * console.log(_.now() - stamp);\n * }, _.now());\n * // => Logs the number of milliseconds it took for the deferred invocation.\n */\nvar now = function() {\n return root.Date.now();\n};\n\n/**\n * Creates a debounced function that delays invoking `func` until after `wait`\n * milliseconds have elapsed since the last time the debounced function was\n * invoked. The debounced function comes with a `cancel` method to cancel\n * delayed `func` invocations and a `flush` method to immediately invoke them.\n * Provide `options` to indicate whether `func` should be invoked on the\n * leading and/or trailing edge of the `wait` timeout. The `func` is invoked\n * with the last arguments provided to the debounced function. Subsequent\n * calls to the debounced function return the result of the last `func`\n * invocation.\n *\n * **Note:** If `leading` and `trailing` options are `true`, `func` is\n * invoked on the trailing edge of the timeout only if the debounced function\n * is invoked more than once during the `wait` timeout.\n *\n * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred\n * until to the next tick, similar to `setTimeout` with a timeout of `0`.\n *\n * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n * for details over the differences between `_.debounce` and `_.throttle`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to debounce.\n * @param {number} [wait=0] The number of milliseconds to delay.\n * @param {Object} [options={}] The options object.\n * @param {boolean} [options.leading=false]\n * Specify invoking on the leading edge of the timeout.\n * @param {number} [options.maxWait]\n * The maximum time `func` is allowed to be delayed before it's invoked.\n * @param {boolean} [options.trailing=true]\n * Specify invoking on the trailing edge of the timeout.\n * @returns {Function} Returns the new debounced function.\n * @example\n *\n * // Avoid costly calculations while the window size is in flux.\n * jQuery(window).on('resize', _.debounce(calculateLayout, 150));\n *\n * // Invoke `sendMail` when clicked, debouncing subsequent calls.\n * jQuery(element).on('click', _.debounce(sendMail, 300, {\n * 'leading': true,\n * 'trailing': false\n * }));\n *\n * // Ensure `batchLog` is invoked once after 1 second of debounced calls.\n * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });\n * var source = new EventSource('/stream');\n * jQuery(source).on('message', debounced);\n *\n * // Cancel the trailing debounced invocation.\n * jQuery(window).on('popstate', debounced.cancel);\n */\nfunction debounce(func, wait, options) {\n var lastArgs,\n lastThis,\n maxWait,\n result,\n timerId,\n lastCallTime,\n lastInvokeTime = 0,\n leading = false,\n maxing = false,\n trailing = true;\n\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n wait = toNumber(wait) || 0;\n if (isObject(options)) {\n leading = !!options.leading;\n maxing = 'maxWait' in options;\n maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;\n trailing = 'trailing' in options ? !!options.trailing : trailing;\n }\n\n function invokeFunc(time) {\n var args = lastArgs,\n thisArg = lastThis;\n\n lastArgs = lastThis = undefined;\n lastInvokeTime = time;\n result = func.apply(thisArg, args);\n return result;\n }\n\n function leadingEdge(time) {\n // Reset any `maxWait` timer.\n lastInvokeTime = time;\n // Start the timer for the trailing edge.\n timerId = setTimeout(timerExpired, wait);\n // Invoke the leading edge.\n return leading ? invokeFunc(time) : result;\n }\n\n function remainingWait(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime,\n result = wait - timeSinceLastCall;\n\n return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;\n }\n\n function shouldInvoke(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime;\n\n // Either this is the first call, activity has stopped and we're at the\n // trailing edge, the system time has gone backwards and we're treating\n // it as the trailing edge, or we've hit the `maxWait` limit.\n return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||\n (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));\n }\n\n function timerExpired() {\n var time = now();\n if (shouldInvoke(time)) {\n return trailingEdge(time);\n }\n // Restart the timer.\n timerId = setTimeout(timerExpired, remainingWait(time));\n }\n\n function trailingEdge(time) {\n timerId = undefined;\n\n // Only invoke if we have `lastArgs` which means `func` has been\n // debounced at least once.\n if (trailing && lastArgs) {\n return invokeFunc(time);\n }\n lastArgs = lastThis = undefined;\n return result;\n }\n\n function cancel() {\n if (timerId !== undefined) {\n clearTimeout(timerId);\n }\n lastInvokeTime = 0;\n lastArgs = lastCallTime = lastThis = timerId = undefined;\n }\n\n function flush() {\n return timerId === undefined ? result : trailingEdge(now());\n }\n\n function debounced() {\n var time = now(),\n isInvoking = shouldInvoke(time);\n\n lastArgs = arguments;\n lastThis = this;\n lastCallTime = time;\n\n if (isInvoking) {\n if (timerId === undefined) {\n return leadingEdge(lastCallTime);\n }\n if (maxing) {\n // Handle invocations in a tight loop.\n timerId = setTimeout(timerExpired, wait);\n return invokeFunc(lastCallTime);\n }\n }\n if (timerId === undefined) {\n timerId = setTimeout(timerExpired, wait);\n }\n return result;\n }\n debounced.cancel = cancel;\n debounced.flush = flush;\n return debounced;\n}\n\n/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n var type = typeof value;\n return !!value && (type == 'object' || type == 'function');\n}\n\n/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n return !!value && typeof value == 'object';\n}\n\n/**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\nfunction isSymbol(value) {\n return typeof value == 'symbol' ||\n (isObjectLike(value) && objectToString.call(value) == symbolTag);\n}\n\n/**\n * Converts `value` to a number.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n * @example\n *\n * _.toNumber(3.2);\n * // => 3.2\n *\n * _.toNumber(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toNumber(Infinity);\n * // => Infinity\n *\n * _.toNumber('3.2');\n * // => 3.2\n */\nfunction toNumber(value) {\n if (typeof value == 'number') {\n return value;\n }\n if (isSymbol(value)) {\n return NAN;\n }\n if (isObject(value)) {\n var other = typeof value.valueOf == 'function' ? value.valueOf() : value;\n value = isObject(other) ? (other + '') : other;\n }\n if (typeof value != 'string') {\n return value === 0 ? value : +value;\n }\n value = value.replace(reTrim, '');\n var isBinary = reIsBinary.test(value);\n return (isBinary || reIsOctal.test(value))\n ? freeParseInt(value.slice(2), isBinary ? 2 : 8)\n : (reIsBadHex.test(value) ? NAN : +value);\n}\n\nmodule.exports = debounce;\n","/**\n * Find the smallest offset in `str` which contains at least `count` chars\n * that match `filter` before it.\n */\nfunction advance(\n str: string,\n count: number,\n filter: (char: string) => boolean,\n startPos = 0,\n): number {\n let pos = startPos;\n while (pos < str.length && count > 0) {\n if (filter(str[pos])) {\n --count;\n }\n ++pos;\n }\n return pos;\n}\n\n/**\n * Count characters which match `filter` in `str`.\n */\nfunction countChars(\n str: string,\n filter: (char: string) => boolean,\n startPos: number,\n endPos: number,\n): number {\n let count = 0;\n for (let pos = startPos; pos < endPos; pos++) {\n if (filter(str[pos])) {\n ++count;\n }\n }\n return count;\n}\n\n/**\n * Translate a (start, end) pair of offsets for an \"input\" string into\n * corresponding offsets in an \"output\" string.\n *\n * Positions in the input and output strings are related by counting\n * the number of \"important\" characters before them, as determined by a\n * filter function.\n *\n * An example usage would be to find equivalent positions in two strings which\n * contain the same text content except for the addition or removal of\n * whitespace at arbitrary locations in the output string.\n *\n * Where there are multiple possible offsets in the output string that\n * correspond to the input offsets, the largest start offset and smallest end\n * offset are chosen. In other words, leading and trailing ignored characters\n * are trimmed from the output.\n *\n * @example\n * // The input offsets (1, 3) select the substring \"bc\" in the \"input\" argument.\n * // The returned offsets select the substring \"b c\" in the \"output\" argument.\n * translateOffsets('abcd', ' a b c d ', 1, 3, char => char !== ' ')\n *\n * @param start - Start offset in `input`\n * @param end - End offset in `input`\n * @param filter - Filter function that returns true if a character should be\n * counted when relating positions between `input` and `output`.\n * @return Start and end offsets in `output`\n */\nexport function translateOffsets(\n input: string,\n output: string,\n start: number,\n end: number,\n filter: (ch: string) => boolean,\n): [number, number] {\n const beforeStartCount = countChars(input, filter, 0, start);\n const startToEndCount = countChars(input, filter, start, end);\n\n // Find the smallest offset in `output` with same number of non-ignored characters\n // before it as before `start` in the input. This offset might correspond to\n // an ignored character.\n let outputStart = advance(output, beforeStartCount, filter);\n\n // Increment this offset until it points to a non-ignored character. This\n // \"trims\" leading ignored characters from the result.\n while (outputStart < output.length && !filter(output[outputStart])) {\n ++outputStart;\n }\n\n // Find smallest offset in `output` with same number of non-ignored characters\n // before it as before `end` in the input.\n const outputEnd = advance(output, startToEndCount, filter, outputStart);\n\n return [outputStart, outputEnd];\n}\n","/* global PDFViewerApplication */\nimport { warnOnce } from '../../shared/warn-once';\nimport type { Shape, ShapeAnchor } from '../../types/annotator';\nimport type {\n PageSelector,\n TextPositionSelector,\n TextQuoteSelector,\n Selector,\n ShapeSelector,\n} from '../../types/api';\nimport type {\n PDFPageProxy,\n PDFPageView,\n PDFViewer,\n TextLayer,\n} from '../../types/pdfjs';\nimport { translateOffsets } from '../util/normalize';\nimport { matchQuote } from './match-quote';\nimport { createPlaceholder } from './placeholder';\nimport { TextPosition, TextRange } from './text-range';\nimport { TextQuoteAnchor } from './types';\n\ntype PDFTextRange = {\n pageIndex: number;\n anchor: {\n /** Start character offset within the page's text. */\n start: number;\n /** End character offset within the page's text. */\n end: number;\n };\n};\n\n/**\n * Enum values for page rendering states (IRenderableView#renderingState)\n * in PDF.js. Taken from web/pdf_rendering_queue.js in the PDF.js library.\n *\n * Reproduced here because this enum is not exported consistently across\n * different versions of PDF.js\n */\nexport const RenderingStates = {\n INITIAL: 0,\n RUNNING: 1,\n PAUSED: 2,\n FINISHED: 3,\n};\n\n// Caches for performance.\n\n/**\n * Map of page index to page text content.\n */\nconst pageTextCache = new Map<number, Promise<string>>();\n\n/**\n * A cache that maps a `{quote}:{offset}` key to a specific\n * location in the document.\n *\n * The components of the key come from an annotation's selectors. This is used\n * to speed up re-anchoring an annotation that was previously anchored in the\n * current session.\n */\nconst quotePositionCache = new Map<string, PDFTextRange>();\n\n/**\n * Return a cache key for lookups in `quotePositionCache`.\n *\n * @param [pos] - Offset in document text\n */\nfunction quotePositionCacheKey(quote: string, pos?: number) {\n return `${quote}:${pos}`;\n}\n\n/**\n * Return offset of `node` among its siblings.\n */\nfunction getSiblingIndex(node: Node) {\n let index = 0;\n while (node.previousSibling) {\n ++index;\n node = node.previousSibling;\n }\n return index;\n}\n\n/**\n * Return the text layer element of the PDF page containing `node`.\n */\nfunction getNodeTextLayer(node: Node | Element): Element | null {\n const el = 'closest' in node ? node : node.parentElement;\n return el?.closest('.textLayer') ?? null;\n}\n\n/**\n * Get the PDF.js viewer application.\n */\nfunction getPDFViewer(): PDFViewer {\n // @ts-ignore - TS doesn't know about PDFViewerApplication global.\n return PDFViewerApplication.pdfViewer;\n}\n\n/**\n * Returns the view into which a PDF page is drawn.\n *\n * If called while the PDF document is still loading, this will delay until\n * the document loading has progressed far enough for a `PDFPageView` and its\n * associated `PDFPage` to be ready.\n */\nasync function getPageView(pageIndex: number): Promise<PDFPageView> {\n const pdfViewer = getPDFViewer();\n let pageView = pdfViewer.getPageView(pageIndex);\n\n if (!pageView || !pageView.pdfPage) {\n // If the document is still loading, wait for the `pagesloaded` event.\n //\n // Note that loading happens in several stages. Initially the page view\n // objects do not exist (`pageView` will be nullish), then after the\n // \"pagesinit\" event, the page view exists but it does not have a `pdfPage`\n // property set, then finally after the \"pagesloaded\" event, it will have\n // a \"pdfPage\" property.\n pageView = await new Promise(resolve => {\n const onPagesLoaded = () => {\n if (pdfViewer.eventBus) {\n pdfViewer.eventBus.off('pagesloaded', onPagesLoaded);\n } else {\n document.removeEventListener('pagesloaded', onPagesLoaded);\n }\n\n resolve(pdfViewer.getPageView(pageIndex));\n };\n\n if (pdfViewer.eventBus) {\n pdfViewer.eventBus.on('pagesloaded', onPagesLoaded);\n } else {\n // Old PDF.js versions (< 1.6.210) use DOM events.\n document.addEventListener('pagesloaded', onPagesLoaded);\n }\n });\n }\n\n return pageView!;\n}\n\n/**\n * Return true if the document has selectable text.\n */\nexport async function documentHasText() {\n const viewer = getPDFViewer();\n let hasText = false;\n for (let i = 0; i < viewer.pagesCount; i++) {\n const pageText = await getPageTextContent(i);\n if (pageText.trim().length > 0) {\n hasText = true;\n break;\n }\n }\n return hasText;\n}\n\n/**\n * Return the text of a given PDF page.\n *\n * The text returned by this function should match the `textContent` of the text\n * layer element that PDF.js creates for rendered pages, with the exception\n * that differences in whitespace are tolerated.\n */\nfunction getPageTextContent(pageIndex: number): Promise<string> {\n // If we already have or are fetching the text for this page, return the\n // existing result.\n const cachedText = pageTextCache.get(pageIndex);\n if (cachedText) {\n return cachedText;\n }\n\n const getPageText = async () => {\n const pageView = await getPageView(pageIndex);\n const textContent = await pageView.pdfPage.getTextContent({\n // Deprecated option, set for compatibility with older PDF.js releases.\n normalizeWhitespace: true,\n });\n return textContent.items.map(it => it.str).join('');\n };\n\n // This function synchronously populates the cache with a promise so that\n // multiple calls don't call `PDFPageProxy.getTextContent` twice.\n const pageText = getPageText();\n pageTextCache.set(pageIndex, pageText);\n return pageText;\n}\n\n/**\n * Find the offset within the document's text at which a page begins.\n *\n * @return - Offset of page's text within document text\n */\nasync function getPageOffset(pageIndex: number): Promise<number> {\n const viewer = getPDFViewer();\n if (pageIndex >= viewer.pagesCount) {\n /* istanbul ignore next - This should never be triggered */\n throw new Error('Invalid page index');\n }\n let offset = 0;\n for (let i = 0; i < pageIndex; i++) {\n const text = await getPageTextContent(i);\n offset += text.length;\n }\n return offset;\n}\n\ntype PageOffset = {\n /** Page index. */\n index: number;\n /** Character offset of start of page within document text. */\n offset: number;\n /** Text of page. */\n text: string;\n};\n\n/**\n * Find the page containing a text offset within the document.\n *\n * If the offset is invalid (less than 0 or greater than the length of the document)\n * then the nearest (first or last) page is returned.\n */\nasync function findPageByOffset(offset: number): Promise<PageOffset> {\n const viewer = getPDFViewer();\n\n let pageStartOffset = 0;\n let pageEndOffset = 0;\n let text = '';\n\n for (let i = 0; i < viewer.pagesCount; i++) {\n text = await getPageTextContent(i);\n pageStartOffset = pageEndOffset;\n pageEndOffset += text.length;\n\n if (pageEndOffset >= offset) {\n return { index: i, offset: pageStartOffset, text };\n }\n }\n\n // If the offset is beyond the end of the document, just pretend it was on\n // the last page.\n return { index: viewer.pagesCount - 1, offset: pageStartOffset, text };\n}\n\n/**\n * Return true if `char` is an ASCII space.\n *\n * This is more efficient than `/\\s/.test(char)` but does not handle Unicode\n * spaces.\n */\nfunction isSpace(char: string) {\n switch (char) {\n case ' ':\n case '\\f':\n case '\\n':\n case '\\r':\n case '\\t':\n case '\\v':\n case '\\u00a0': // nbsp\n return true;\n default:\n return false;\n }\n}\n\nconst isNotSpace = (char: string) => !isSpace(char);\n\n/**\n * Determines if provided text layer is done rendering.\n * It works on older PDF.js versions which expose a public `renderingDone` prop,\n * and newer versions as well\n */\nexport function isTextLayerRenderingDone(textLayer: TextLayer): boolean {\n if (textLayer.renderingDone !== undefined) {\n return textLayer.renderingDone;\n }\n\n if (!textLayer.div) {\n return false;\n }\n\n // When a Page is rendered, the div gets an element with the class\n // endOfContent appended to it. If that element exists, we can consider the\n // text layer is done rendering.\n // See https://github.com/mozilla/pdf.js/blob/1ab9ab67eed886f27127bd801bc349949af5054e/web/text_layer_builder.js#L103-L107\n return textLayer.div.querySelector('.endOfContent') !== null;\n}\n\n/**\n * Locate the DOM Range which a position selector refers to.\n *\n * If the page is off-screen it may be in an unrendered state, in which case\n * the text layer will not have been created. In that case a placeholder\n * DOM element is created and the returned range refers to that placeholder.\n * In that case, the selector will need to be re-anchored when the page is\n * scrolled into view.\n *\n * @param pageIndex - The PDF page index\n * @param start - Character offset within the page's text\n * @param end - Character offset within the page's text\n */\nasync function anchorByPosition(\n pageIndex: number,\n start: number,\n end: number,\n): Promise<Range> {\n const [page, pageText] = await Promise.all([\n getPageView(pageIndex),\n getPageTextContent(pageIndex),\n ]);\n\n if (\n page.renderingState === RenderingStates.FINISHED &&\n page.textLayer &&\n isTextLayerRenderingDone(page.textLayer)\n ) {\n // The page has been rendered. Locate the position in the text layer.\n //\n // We allow for differences in whitespace between the text returned by\n // `getPageTextContent` and the text layer content. Any other differences\n // will cause mis-anchoring.\n\n const root = page.textLayer.textLayerDiv ?? page.textLayer.div;\n if (!root) {\n /* istanbul ignore next */\n throw new Error('Unable to find PDF.js text layer root');\n }\n\n const textLayerStr = root.textContent!;\n\n const [textLayerStart, textLayerEnd] = translateOffsets(\n pageText,\n textLayerStr,\n start,\n end,\n isNotSpace,\n );\n\n const textLayerQuote = stripSpaces(\n textLayerStr.slice(textLayerStart, textLayerEnd),\n );\n const pageTextQuote = stripSpaces(pageText.slice(start, end));\n if (textLayerQuote !== pageTextQuote) {\n warnOnce(\n 'Text layer text does not match page text. Highlights will be mis-aligned.',\n );\n }\n\n const startPos = new TextPosition(root, textLayerStart);\n const endPos = new TextPosition(root, textLayerEnd);\n return new TextRange(startPos, endPos).toRange();\n }\n\n // The page has not been rendered yet. Create a placeholder element and\n // anchor to that instead.\n const placeholder = createPlaceholder(page.div);\n const range = document.createRange();\n range.setStartBefore(placeholder);\n range.setEndAfter(placeholder);\n return range;\n}\n\n/**\n * Return a string with spaces stripped.\n *\n * This function optimizes for performance of stripping the main space chars\n * that PDF.js generates over handling all kinds of whitespace that could\n * occur in a string.\n */\nfunction stripSpaces(str: string) {\n let stripped = '';\n for (let i = 0; i < str.length; i++) {\n const char = str[i];\n if (isSpace(char)) {\n continue;\n }\n stripped += char;\n }\n return stripped;\n}\n\n/**\n * Search for a quote in the given pages.\n *\n * When comparing quote selectors to document text, ASCII whitespace characters\n * are ignored. This is because text extracted from a PDF by different PDF\n * viewers, including different versions of PDF.js, can often differ in the\n * whitespace between characters and words. For a long time PDF.js in particular\n * had issues where it would often produce extra spaces between characters that\n * should not be there or omit spaces between words.\n *\n * @param [positionHint] - Expected start offset of quote\n * @return - Location of quote\n */\nasync function anchorQuote(\n quoteSelector: TextQuoteSelector,\n positionHint?: number,\n): Promise<Range> {\n // Determine which pages to search and in what order. If we have a position\n // hint we'll try to use that. Otherwise we'll just search all pages in order.\n const pageCount = getPDFViewer().pagesCount;\n const pageIndexes = Array(pageCount)\n .fill(0)\n .map((_, i) => i);\n\n let expectedPageIndex;\n let expectedOffsetInPage;\n\n if (positionHint) {\n const { index, offset } = await findPageByOffset(positionHint);\n expectedPageIndex = index;\n expectedOffsetInPage = positionHint - offset;\n\n // Sort pages by distance from the page where we expect to find the quote,\n // based on the position hint.\n pageIndexes.sort((a, b) => {\n const distA = Math.abs(a - index);\n const distB = Math.abs(b - index);\n return distA - distB;\n });\n }\n\n // Search pages for the best match, ignoring whitespace differences.\n const strippedPrefix =\n quoteSelector.prefix !== undefined\n ? stripSpaces(quoteSelector.prefix)\n : undefined;\n const strippedSuffix =\n quoteSelector.suffix !== undefined\n ? stripSpaces(quoteSelector.suffix)\n : undefined;\n const strippedQuote = stripSpaces(quoteSelector.exact);\n\n let bestMatch;\n for (const page of pageIndexes) {\n const text = await getPageTextContent(page);\n const strippedText = stripSpaces(text);\n\n // Determine expected offset of quote in current page based on position hint.\n let strippedHint;\n if (expectedPageIndex !== undefined && expectedOffsetInPage !== undefined) {\n if (page < expectedPageIndex) {\n strippedHint = strippedText.length; // Prefer matches closer to end of page.\n } else if (page === expectedPageIndex) {\n // Translate expected offset in whitespace-inclusive version of page\n // text into offset in whitespace-stripped version of page text.\n [strippedHint] = translateOffsets(\n text,\n strippedText,\n expectedOffsetInPage,\n expectedOffsetInPage,\n isNotSpace,\n );\n } else {\n strippedHint = 0; // Prefer matches closer to start of page.\n }\n }\n\n const match = matchQuote(strippedText, strippedQuote, {\n prefix: strippedPrefix,\n suffix: strippedSuffix,\n hint: strippedHint,\n });\n\n if (!match) {\n continue;\n }\n\n if (!bestMatch || match.score > bestMatch.match.score) {\n // Translate match offset from whitespace-stripped version of page text\n // back to original text.\n const [start, end] = translateOffsets(\n strippedText,\n text,\n match.start,\n match.end,\n isNotSpace,\n );\n bestMatch = {\n page,\n match: {\n start,\n end,\n score: match.score,\n },\n };\n\n // If we find a very good match, stop early.\n //\n // There is a tradeoff here between optimizing search performance and\n // ensuring that we have found the best match in the document.\n //\n // The current heuristics are that we require an exact match for the quote\n // and either the preceding or following context. The context matching\n // helps to avoid incorrectly stopping the search early if the quote is\n // a word or phrase that is common in the document.\n const exactQuoteMatch =\n strippedText.slice(match.start, match.end) === strippedQuote;\n\n const exactPrefixMatch =\n strippedPrefix !== undefined &&\n strippedText.slice(\n Math.max(0, match.start - strippedPrefix.length),\n match.start,\n ) === strippedPrefix;\n\n const exactSuffixMatch =\n strippedSuffix !== undefined &&\n strippedText.slice(match.end, strippedSuffix.length) === strippedSuffix;\n\n const hasContext =\n strippedPrefix !== undefined || strippedSuffix !== undefined;\n\n if (\n exactQuoteMatch &&\n (exactPrefixMatch || exactSuffixMatch || !hasContext)\n ) {\n break;\n }\n }\n }\n\n if (bestMatch) {\n const { page, match } = bestMatch;\n\n // If we found a match, optimize future anchoring of this selector in the\n // same session by caching the match location.\n if (positionHint) {\n const cacheKey = quotePositionCacheKey(quoteSelector.exact, positionHint);\n quotePositionCache.set(cacheKey, {\n pageIndex: page,\n anchor: match,\n });\n }\n\n // Convert the (start, end) position match into a DOM range.\n return anchorByPosition(page, match.start, match.end);\n }\n\n throw new Error('Quote not found');\n}\n\n/**\n * Anchor a set of selectors to a DOM Range.\n *\n * `selectors` must include a `TextQuoteSelector` and may include other selector\n * types.\n */\nasync function anchorRange(selectors: Selector[]): Promise<Range> {\n const quote = selectors.find(s => s.type === 'TextQuoteSelector') as\n | TextQuoteSelector\n | undefined;\n\n // The quote selector is required in order to check that text position\n // selector results are still valid.\n if (!quote) {\n throw new Error('No quote selector found');\n }\n\n const position = selectors.find(s => s.type === 'TextPositionSelector') as\n | TextPositionSelector\n | undefined;\n\n if (position) {\n // If we have a position selector, try using that first as it is the fastest\n // anchoring method.\n try {\n const { index, offset, text } = await findPageByOffset(position.start);\n const start = position.start - offset;\n const end = position.end - offset;\n\n const matchedText = text.substring(start, end);\n if (quote.exact !== matchedText) {\n throw new Error('quote mismatch');\n }\n\n const range = await anchorByPosition(index, start, end);\n return range;\n } catch {\n // Fall back to quote selector\n }\n\n // If anchoring with the position failed, check for a cached quote-based\n // match using the quote + position as a cache key.\n try {\n const cacheKey = quotePositionCacheKey(quote.exact, position.start);\n const cachedPos = quotePositionCache.get(cacheKey);\n if (cachedPos) {\n const { pageIndex, anchor } = cachedPos;\n const range = await anchorByPosition(\n pageIndex,\n anchor.start,\n anchor.end,\n );\n return range;\n }\n } catch {\n // Fall back to uncached quote selector match\n }\n }\n\n return anchorQuote(quote, position?.start);\n}\n\n/**\n * Anchor a set of selectors to either a DOM Range or a shape anchor.\n */\nexport async function anchor(\n selectors: Selector[],\n): Promise<Range | ShapeAnchor> {\n const pageSelector = selectors.find(s => s.type === 'PageSelector') as\n | PageSelector\n | undefined;\n const shapeSelector = selectors.find(s => s.type === 'ShapeSelector') as\n | ShapeSelector\n | undefined;\n\n if (shapeSelector) {\n if (!pageSelector) {\n throw new Error('Cannot anchor a shape selector without a page');\n }\n return anchorShape(pageSelector, shapeSelector);\n } else {\n return anchorRange(selectors);\n }\n}\n\nasync function anchorShape(\n pageSelector: PageSelector,\n shapeSelector: ShapeSelector,\n): Promise<ShapeAnchor> {\n const viewer = getPDFViewer();\n if (\n typeof pageSelector.index !== 'number' ||\n pageSelector.index >= viewer.pagesCount\n ) {\n throw new Error('PDF page index is invalid');\n }\n\n const pageView = await getPageView(pageSelector.index);\n const anchor = pageView.div;\n\n // Get page bounding box in user-space coordinates.\n //\n // Note that the origin is at the bottom-left corner of the page, with Y\n // going up.\n const [pageLeft, pageBottom, pageRight, pageTop] = pageView.pdfPage.view;\n const pageWidth = pageRight - pageLeft;\n const pageHeight = pageTop - pageBottom;\n\n const mapX = (x: number) => (x - pageLeft) / pageWidth;\n const mapY = (y: number) => (pageTop - y) / pageHeight;\n\n // Map the user-space coordinates of the shape to coordinates relative to the\n // PDF page container, where the top-left is (0, 0) and the bottom right is\n // (1, 1).\n let shape: Shape;\n switch (shapeSelector.shape.type) {\n case 'rect':\n {\n const s = shapeSelector.shape;\n shape = {\n type: 'rect',\n left: mapX(s.left),\n right: mapX(s.right),\n top: mapY(s.top),\n bottom: mapY(s.bottom),\n };\n }\n break;\n case 'point':\n {\n const s = shapeSelector.shape;\n shape = { type: 'point', x: mapX(s.x), y: mapY(s.y) };\n }\n break;\n default:\n throw new Error('Unsupported shape in shape selector');\n }\n\n return {\n anchor,\n shape,\n coordinates: 'anchor',\n };\n}\n\n/**\n * Prepare a DOM range for generating selectors and find the containing text layer.\n *\n * @throws If the range cannot be annotated\n */\nfunction getTextLayerForRange(range: Range): [Range, Element] {\n // \"Shrink\" the range so that the start and endpoints are at offsets within\n // text nodes rather than any containing nodes.\n try {\n range = TextRange.fromRange(range).toRange();\n } catch {\n throw new Error('Selection does not contain text');\n }\n\n const startTextLayer = getNodeTextLayer(range.startContainer);\n const endTextLayer = getNodeTextLayer(range.endContainer);\n\n if (!startTextLayer || !endTextLayer) {\n throw new Error('Selection is outside page text');\n }\n\n if (startTextLayer !== endTextLayer) {\n throw new Error('Selecting across page breaks is not supported');\n }\n\n return [range, startTextLayer];\n}\n\n/**\n * Return true if selectors can be generated for a range using `describe`.\n *\n * This function is faster than calling `describe` if the selectors are not\n * required.\n */\nexport function canDescribe(range: Range) {\n try {\n getTextLayerForRange(range);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Convert a DOM Range object into a set of selectors.\n *\n * Converts a DOM `Range` object into a `[position, quote]` tuple of selectors\n * which can be saved with an annotation and later passed to `anchor` to\n * convert the selectors back to a `Range`.\n *\n * @param root - The root element\n */\nexport async function describe(range: Range): Promise<Selector[]> {\n const [textRange, textLayer] = getTextLayerForRange(range);\n\n const startPos = TextPosition.fromPoint(\n textRange.startContainer,\n textRange.startOffset,\n ).relativeTo(textLayer);\n\n const endPos = TextPosition.fromPoint(\n textRange.endContainer,\n textRange.endOffset,\n ).relativeTo(textLayer);\n\n const startPageIndex = getSiblingIndex(textLayer.parentNode!);\n const pageOffset = await getPageOffset(startPageIndex);\n\n const pageView = await getPageView(startPageIndex);\n\n const position = {\n type: 'TextPositionSelector',\n start: pageOffset + startPos.offset,\n end: pageOffset + endPos.offset,\n } as TextPositionSelector;\n\n const quote = TextQuoteAnchor.fromRange(pageView.div, textRange).toSelector();\n const pageSelector = createPageSelector(pageView, startPageIndex);\n\n return [position, quote, pageSelector];\n}\n\ntype PDFPoint = {\n pageIndex: number;\n x: number;\n y: number;\n};\n\n/**\n * Map a point in viewport coordinates to a point in PDF user space coordinates.\n *\n * Returns `null` if the specified viewport coordinates are not in any PDF page.\n */\nasync function mapViewportToPDF(\n x: number,\n y: number,\n): Promise<PDFPoint | null> {\n const elements = document.elementsFromPoint(x, y);\n for (const el of elements) {\n if (!el.classList.contains('page')) {\n continue;\n }\n const pageIndex = getSiblingIndex(el);\n const pageViewRect = el.getBoundingClientRect();\n const pageViewX = x - pageViewRect.left;\n const pageViewY = y - pageViewRect.top;\n const pdfPageView = await getPageView(pageIndex);\n const [userX, userY] = pdfPageView.getPagePoint(pageViewX, pageViewY);\n\n return {\n pageIndex,\n x: userX,\n y: userY,\n };\n }\n return null;\n}\n\nfunction createPageSelector(\n view: PDFPageView,\n pageIndex: number,\n): PageSelector {\n return {\n type: 'PageSelector',\n index: pageIndex,\n label: view.pageLabel ?? `${pageIndex + 1}`,\n };\n}\n\nexport async function describeShape(shape: Shape): Promise<Selector[]> {\n const pageBoundingBox = (page: PDFPageProxy) => {\n const [viewLeft, viewBottom, viewRight, viewTop] = page.view;\n return {\n left: viewLeft,\n top: viewTop,\n right: viewRight,\n bottom: viewBottom,\n };\n };\n\n switch (shape.type) {\n case 'rect': {\n const [topLeft, bottomRight] = await Promise.all([\n mapViewportToPDF(shape.left, shape.top),\n mapViewportToPDF(shape.right, shape.bottom),\n ]);\n if (!topLeft) {\n throw new Error('Top-left point is not in a page');\n }\n if (!bottomRight) {\n throw new Error('Bottom-right point is not in a page');\n }\n\n if (topLeft.pageIndex !== bottomRight.pageIndex) {\n throw new Error('Shape must start and end on same page');\n }\n\n const pageView = await getPageView(topLeft.pageIndex);\n\n return [\n createPageSelector(pageView, topLeft.pageIndex),\n {\n type: 'ShapeSelector',\n anchor: 'page',\n shape: {\n type: 'rect',\n left: topLeft.x,\n top: topLeft.y,\n right: bottomRight.x,\n bottom: bottomRight.y,\n },\n view: pageBoundingBox(pageView.pdfPage),\n },\n ];\n }\n case 'point': {\n const point = await mapViewportToPDF(shape.x, shape.y);\n if (!point) {\n throw new Error('Point is not in a page');\n }\n\n const pageView = await getPageView(point.pageIndex);\n\n return [\n {\n type: 'PageSelector',\n index: point.pageIndex,\n },\n {\n type: 'ShapeSelector',\n anchor: 'page',\n shape: {\n type: 'point',\n x: point.x,\n y: point.y,\n },\n view: pageBoundingBox(pageView.pdfPage),\n },\n ];\n }\n default:\n throw new Error('Unsupported shape');\n }\n}\n\n/**\n * Clear this module's internal caches.\n *\n * This exists mainly as a helper for use in tests.\n */\nexport function purgeCache() {\n pageTextCache.clear();\n quotePositionCache.clear();\n}\n","import type { ComponentChildren } from 'preact';\n\nexport type BannersProps = { children: ComponentChildren };\n\n/**\n * Render banners at the top of a document in a stacked column.\n */\nexport default function Banners({ children }: BannersProps) {\n return <div className=\"flex flex-col\">{children}</div>;\n}\n","import {\n Link,\n CaretLeftIcon,\n CaretRightIcon,\n} from '@hypothesis/frontend-shared';\nimport classnames from 'classnames';\n\nimport type { ContentInfoConfig } from '../../types/annotator';\n\nexport type ContentInfoBannerProps = { info: ContentInfoConfig };\n\n/**\n * A banner that displays information about the current document and the entity\n * that is providing access to it (eg. JSTOR).\n *\n * Layout columns:\n * - Logo\n * - Container title (only shown on screens at `2xl` breakpoint and wider)\n * - Item title with previous and next links\n */\nexport default function ContentInfoBanner({ info }: ContentInfoBannerProps) {\n // Format item title to show subtitle\n let itemTitle = info.item.title;\n if (info.item.subtitle) {\n itemTitle += `: ${info.item.subtitle}`;\n }\n return (\n <div\n className={classnames(\n 'h-10 bg-white px-4 text-slate-7 text-annotator-base border-b',\n 'grid items-center',\n // Two columns in narrower viewports; three in wider\n 'grid-cols-[100px_minmax(0,auto)]',\n '2xl:grid-cols-[100px_minmax(0,auto)_minmax(0,auto)] 2xl:gap-x-3',\n )}\n >\n <div data-testid=\"content-logo\">\n {info.logo && (\n <Link\n href={info.logo.link}\n target=\"_blank\"\n data-testid=\"logo-link\"\n underline=\"none\"\n >\n <img\n alt={info.logo.title}\n src={info.logo.logo}\n data-testid=\"logo-image\"\n />\n </Link>\n )}\n </div>\n <div\n className={classnames(\n // Container title (this element) is not shown on narrow screens\n 'hidden',\n '2xl:block 2xl:whitespace-nowrap 2xl:overflow-hidden 2xl:text-ellipsis',\n 'font-semibold',\n )}\n data-testid=\"content-container-info\"\n title={info.container.title}\n >\n {info.container.title}\n </div>\n <div\n className={classnames(\n // Flex layout for item title, next and previous links\n 'flex justify-center items-center gap-x-2',\n )}\n data-testid=\"content-item-info\"\n >\n <div\n className={classnames(\n // Narrower viewports center this flex content:\n // this element is not needed for alignment\n 'hidden',\n // Wider viewports align this flex content to the right:\n // This empty element is needed to fill extra space at left\n '2xl:block 2xl:grow',\n )}\n />\n {info.links.previousItem && (\n <>\n <Link\n title=\"Open previous item\"\n href={info.links.previousItem}\n underline=\"always\"\n target=\"_blank\"\n data-testid=\"content-previous-link\"\n >\n <div className=\"flex gap-x-1 items-center text-annotator-sm whitespace-nowrap\">\n <CaretLeftIcon className=\"w-em h-em\" />\n <span>Previous</span>\n </div>\n </Link>\n <div className=\"text-annotator-sm\">|</div>\n </>\n )}\n <div\n className={classnames(\n // This element will shrink and truncate fluidly.\n // Overriding min-width `auto` prevents the content from overflowing\n // See https://stackoverflow.com/a/66689926/434243.\n 'min-w-0 whitespace-nowrap overflow-hidden text-ellipsis shrink font-medium',\n )}\n >\n <Link\n title={itemTitle}\n href={info.links.currentItem}\n data-testid=\"content-item-link\"\n target=\"_blank\"\n unstyled\n >\n {itemTitle}\n </Link>\n </div>\n\n {info.links.nextItem && (\n <>\n <div className=\"text-annotator-sm\">|</div>\n <Link\n title=\"Open next item\"\n href={info.links.nextItem}\n underline=\"always\"\n target=\"_blank\"\n data-testid=\"content-next-link\"\n >\n <div className=\"flex gap gap-x-1 items-center text-annotator-sm whitespace-nowrap\">\n <span>Next</span>\n <CaretRightIcon className=\"w-em h-em\" />\n </div>\n </Link>\n </>\n )}\n </div>\n </div>\n );\n}\n","import { CautionIcon, Link } from '@hypothesis/frontend-shared';\nimport classnames from 'classnames';\n\n/**\n * A banner shown at the top of the PDF viewer if the PDF cannot be annotated\n * by Hypothesis.\n */\nexport default function WarningBanner() {\n return (\n <div className=\"bg-white\" role=\"alert\">\n <div\n className={classnames(\n 'flex items-center gap-x-2',\n 'border border-yellow-notice bg-yellow-notice/10 text-annotator-base',\n )}\n >\n <div className=\"bg-yellow-notice text-white p-2\">\n <CautionIcon className=\"text-annotator-xl\" />\n </div>\n <div>\n <strong>This PDF does not contain selectable text:</strong>{' '}\n <Link\n target=\"_blank\"\n href=\"https://web.hypothes.is/help/how-to-ocr-optimize-pdfs/\"\n underline=\"always\"\n >\n Learn how to fix this\n </Link>{' '}\n in order to annotate with Hypothesis.\n </div>\n </div>\n </div>\n );\n}\n","import type { PDFViewerApplication } from '../../types/pdfjs';\nimport { normalizeURI } from '../util/url';\n\ntype Link = { href: string };\n\ntype Metadata = {\n /** The document title */\n title: string;\n /** Array of URIs associated with this document */\n link: Link[];\n\n /**\n * The fingerprint of this PDF. This is referred to as the \"File Identifier\"\n * in the PDF spec. It may be a hash of part of the content if the PDF file\n * does not have a File Identifier.\n *\n * PDFs may have two file identifiers. The first is the \"original\" identifier\n * which is not supposed to change if the file is updated and the second\n * one is the \"last modified\" identifier. This property is the original\n * identifier.\n */\n documentFingerprint: string;\n};\n\n/**\n * Wait for a PDFViewerApplication to be initialized.\n */\nfunction pdfViewerInitialized(app: PDFViewerApplication): Promise<void> {\n // `initializedPromise` was added in PDF.js v2.4.456.\n // See https://github.com/mozilla/pdf.js/pull/11607. In earlier versions the\n // `initialized` property can be queried.\n if (app.initializedPromise) {\n return app.initializedPromise;\n } else if (app.initialized) {\n return Promise.resolve();\n } else {\n // PDF.js < v2.4.456. The recommended approach is to listen for a `localized`\n // DOM event, but this assumes that PDF.js has been configured to publish\n // events to the DOM. Here we simply poll `app.initialized` because it is\n // easier.\n return new Promise(resolve => {\n const timeout = setInterval(() => {\n if (app.initialized) {\n clearTimeout(timeout);\n resolve();\n }\n }, 5);\n });\n }\n}\n\n/**\n * Wait for PDF to be downloaded.\n *\n * For PDF.js versions older than v4.5, we rely on\n * `PDFViewerApplication.downloadComplete`.\n * For newer PDF.js versions we wait for\n * `PDFViewerApplication.pdfDocument.getDownloadInfo()` to resolve.\n */\nasync function isPDFDownloaded(app: PDFViewerApplication): Promise<boolean> {\n if (app.downloadComplete !== undefined) {\n return app.downloadComplete;\n }\n\n await app.pdfDocument.getDownloadInfo();\n return true;\n}\n\n/**\n * PDFMetadata extracts metadata about a loading/loaded PDF document from a\n * PDF.js PDFViewerApplication object.\n *\n * @example\n * // Invoke in a PDF.js viewer, before or after the PDF has finished loading.\n * const meta = new PDFMetadata(window.PDFViewerApplication)\n * meta.getUri().then(uri => {\n * // Do something with the URL of the PDF.\n * })\n */\nexport class PDFMetadata {\n private _loaded: Promise<PDFViewerApplication>;\n\n /**\n * Construct a `PDFMetadata` that returns URIs/metadata associated with a\n * given PDF viewer.\n *\n * @param app - The `PDFViewerApplication` global from PDF.js\n */\n constructor(app: PDFViewerApplication) {\n this._loaded = pdfViewerInitialized(app).then(async () => {\n // Check if document has already loaded.\n const isDownloadComplete = await isPDFDownloaded(app);\n if (isDownloadComplete) {\n return app;\n }\n\n return new Promise(resolve => {\n const finish = () => {\n if (app.eventBus) {\n app.eventBus.off('documentload', finish);\n app.eventBus.off('documentloaded', finish);\n } else {\n window.removeEventListener('documentload', finish);\n }\n resolve(app);\n };\n\n // Listen for \"documentloaded\" event which signals that the document\n // has been downloaded and the first page has been rendered.\n if (app.eventBus) {\n // PDF.js >= v1.6.210 dispatch events via an internal event bus.\n // PDF.js < v2.5.207 also dispatches events to the DOM.\n\n // `documentloaded` is the preferred event in PDF.js >= v2.0.943.\n // See https://github.com/mozilla/pdf.js/commit/7bc4bfcc8b7f52b14107f0a551becdf01643c5c2\n app.eventBus.on('documentloaded', finish);\n\n // `documentload` is dispatched by PDF.js < v2.1.266.\n app.eventBus.on('documentload', finish);\n } else {\n // PDF.js < v1.6.210 dispatches events only to the DOM.\n window.addEventListener('documentload', finish);\n }\n });\n });\n }\n\n /**\n * Return the URI of the PDF.\n *\n * If the PDF is currently loading, the returned promise resolves once loading\n * is complete.\n */\n getUri(): Promise<string> {\n return this._loaded.then(app => {\n let uri = getPDFURL(app);\n if (!uri) {\n uri = fingerprintToURN(getFingerprint(app));\n }\n return uri;\n });\n }\n\n /**\n * Returns metadata about the document.\n *\n * If the PDF is currently loading, the returned promise resolves once loading\n * is complete.\n */\n async getMetadata(): Promise<Metadata> {\n const app = await this._loaded;\n const {\n info: documentInfo,\n contentDispositionFilename,\n metadata,\n } = await app.pdfDocument.getMetadata();\n\n const documentFingerprint = getFingerprint(app);\n const url = getPDFURL(app);\n\n // Return the title metadata embedded in the PDF if available, otherwise\n // fall back to values from the `Content-Disposition` header or URL.\n //\n // PDFs contain two embedded metadata sources, the metadata stream and\n // the document info dictionary. Per the specification, the metadata stream\n // is preferred if available.\n //\n // This logic is similar to how PDF.js sets `document.title`.\n let title;\n if (metadata?.has('dc:title') && metadata.get('dc:title') !== 'Untitled') {\n title = metadata.get('dc:title');\n } else if (documentInfo?.Title) {\n title = documentInfo.Title;\n } else if (contentDispositionFilename) {\n title = contentDispositionFilename;\n } else if (url) {\n title = filenameFromURL(url);\n } else {\n title = '';\n }\n\n const link = [{ href: fingerprintToURN(documentFingerprint) }];\n if (url) {\n link.push({ href: url });\n }\n\n return {\n title,\n link,\n documentFingerprint,\n };\n }\n}\n\n/**\n * Get the fingerprint/file identifier of the currently loaded PDF.\n */\nfunction getFingerprint(app: PDFViewerApplication): string {\n if (Array.isArray(app.pdfDocument.fingerprints)) {\n return app.pdfDocument.fingerprints[0];\n } else {\n return app.pdfDocument.fingerprint!;\n }\n}\n\n/**\n * Generate a URI from a PDF fingerprint suitable for storing as the main\n * or associated URI of an annotation.\n */\nfunction fingerprintToURN(fingerprint: string) {\n return `urn:x-pdf:${fingerprint}`;\n}\n\nfunction getPDFURL(app: PDFViewerApplication): string | null {\n if (!app.url) {\n return null;\n }\n\n const url = normalizeURI(app.url);\n\n // Local file:// URLs should not be saved in document metadata.\n // Entries in document.link should be URIs. In the case of\n // local files, omit the URL.\n if (url.indexOf('file://') !== 0) {\n return url;\n }\n\n return null;\n}\n\n/**\n * Return the last component of the path part of a URL.\n */\nfunction filenameFromURL(url: string): string {\n const parsed = new URL(url);\n const pathSegments = parsed.pathname.split('/');\n return pathSegments[pathSegments.length - 1];\n}\n","import { ListenerCollection } from '@hypothesis/frontend-shared';\nimport debounce from 'lodash.debounce';\n\nimport { EventEmitter } from '../../shared/event-emitter';\nimport type {\n Anchor,\n AnnotationData,\n AnnotationTool,\n Annotator,\n ContentInfoConfig,\n Destroyable,\n FeatureFlags,\n Integration,\n IntegrationEvents,\n RenderToBitmapOptions,\n Shape,\n ShapeAnchor,\n SidebarLayout,\n} from '../../types/annotator';\nimport type { Selector } from '../../types/api';\nimport type {\n PageViewport,\n PDFViewer,\n PDFViewerApplication,\n} from '../../types/pdfjs';\nimport {\n RenderingStates,\n anchor,\n canDescribe,\n describe,\n describeShape,\n documentHasText,\n isTextLayerRenderingDone,\n} from '../anchoring/pdf';\nimport { isInPlaceholder, removePlaceholder } from '../anchoring/placeholder';\nimport { TextRange } from '../anchoring/text-range';\nimport Banners from '../components/Banners';\nimport ContentInfoBanner from '../components/ContentInfoBanner';\nimport WarningBanner from '../components/WarningBanner';\nimport { PreactContainer } from '../util/preact-container';\nimport { offsetRelativeTo, scrollElement } from '../util/scroll';\nimport { PDFMetadata } from './pdf-metadata';\n\n/**\n * Window with additional globals set by PDF.js.\n */\ntype PDFWindow = Window & { PDFViewerApplication: PDFViewerApplication };\n\n// The viewport and controls for PDF.js start breaking down below about 670px\n// of available space, so only render PDF and sidebar side-by-side if there\n// is enough room. Otherwise, allow sidebar to overlap PDF\nconst MIN_PDF_WIDTH = 680;\n\n/**\n * Return true if `anchor` is in an un-rendered page.\n */\nfunction anchorIsInPlaceholder(anchor: Anchor) {\n const highlight = anchor.highlights?.[0];\n return highlight && isInPlaceholder(highlight);\n}\n\nfunction delay(ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Is the current document the PDF.js viewer application?\n */\nexport function isPDF() {\n const maybePDFJS: Window & { PDFViewerApplication?: PDFViewerApplication } =\n window;\n return typeof maybePDFJS.PDFViewerApplication !== 'undefined';\n}\n\n/**\n * Controller for the `<hypothesis-banner>` UI element that contains various\n * notices related to the PDF (eg. warning if PDF has no selectable text,\n * showing info about where the PDF came from).\n *\n * This element is created lazily when there is content to show.\n */\nclass BannerController implements Destroyable {\n /** Top-level DOM element associated with the PDF.js viewer. */\n private _pdfjsContainer: HTMLElement;\n private _container: PreactContainer | null;\n private _contentInfo: ContentInfoConfig | null;\n\n /** Warning that the current PDF does not have selectable text. */\n private _noTextWarning: boolean;\n\n constructor() {\n this._pdfjsContainer = document.querySelector(\n '#outerContainer',\n ) as HTMLElement;\n\n this._contentInfo = null;\n this._noTextWarning = false;\n this._container = null;\n }\n\n /**\n * Show a banner with information about the provider of the PDF.\n *\n * This is a contractual requirement for some LMS content providers.\n */\n setContentInfo(info: ContentInfoConfig) {\n this._contentInfo = info;\n this._update();\n }\n\n /**\n * Set whether the \"PDF has no selectable text\" notice is shown.\n */\n showNoTextWarning(show: boolean) {\n this._noTextWarning = show;\n this._update();\n }\n\n destroy() {\n this._container?.destroy();\n }\n\n private _update() {\n const show = this._noTextWarning || this._contentInfo;\n if (!show) {\n this._container?.destroy();\n this._container = null;\n\n // Undo inline styles applied when the banner is shown. The banner will\n // then gets its normal 100% height set by PDF.js's CSS.\n this._pdfjsContainer.style.height = '';\n\n return;\n }\n\n if (!this._container) {\n this._container = new PreactContainer('banner', () => this._render());\n document.body.prepend(this._container.element);\n }\n\n this._container.render();\n\n // The `#outerContainer` element normally has height set to 100% of the body.\n //\n // Reduce this by the height of the banner so that it doesn't extend beyond\n // the bottom of the viewport.\n //\n // We don't currently handle the height of the banner changing here.\n const bannerHeight = this._container.element.getBoundingClientRect().height;\n this._pdfjsContainer.style.height = `calc(100% - ${bannerHeight}px)`;\n }\n\n private _render() {\n return (\n <Banners>\n {this._contentInfo && <ContentInfoBanner info={this._contentInfo} />}\n {this._noTextWarning && <WarningBanner />}\n </Banners>\n );\n }\n}\n\nexport type Options = {\n annotator: Annotator;\n features: FeatureFlags;\n\n /** Max time to wait for re-anchoring to complete when scrolling to an un-rendered page. */\n reanchoringMaxWait?: number;\n};\n\n/**\n * Integration that works with PDF.js\n */\nexport class PDFIntegration\n extends EventEmitter<IntegrationEvents>\n implements Integration\n{\n private _annotator: Annotator;\n\n /** Banners shown at the top of the PDF viewer. */\n private _banner: BannerController;\n\n /**\n * A flag that indicates whether `destroy` has been called. Used to handle\n * `destroy` being called during async code elsewhere in the class.\n */\n private _destroyed: boolean;\n private _features: FeatureFlags;\n private _listeners: ListenerCollection;\n private _observer: MutationObserver;\n private _pdfContainer: HTMLElement;\n private _pdfMetadata: PDFMetadata;\n private _pdfViewer: PDFViewer;\n\n /**\n * Amount of time to wait for re-anchoring to complete when scrolling to\n * an anchor in a not-yet-rendered page.\n */\n private _reanchoringMaxWait: number;\n private _updateAnnotationLayerVisibility: () => void;\n\n private _sideBySideActive: boolean;\n\n constructor({ annotator, features, reanchoringMaxWait }: Options) {\n super();\n\n this._annotator = annotator;\n\n // Assume this class is only used if we're in the PDF.js viewer.\n const pdfWindow = window as unknown as PDFWindow;\n const pdfViewerApp = pdfWindow.PDFViewerApplication;\n\n this._pdfViewer = pdfViewerApp.pdfViewer;\n this._pdfViewer.viewer.classList.add('has-transparent-text-layer');\n\n // Get the element that contains all of the PDF.js UI. This is typically\n // `document.body`.\n this._pdfContainer = pdfViewerApp.appConfig?.appContainer ?? document.body;\n\n this._pdfMetadata = new PDFMetadata(pdfViewerApp);\n\n this._observer = new MutationObserver(debounce(() => this._update(), 100));\n this._observer.observe(this._pdfViewer.viewer, {\n attributes: true,\n attributeFilter: ['data-loaded'],\n childList: true,\n subtree: true,\n });\n\n this._reanchoringMaxWait = reanchoringMaxWait ?? 3000;\n this._banner = new BannerController();\n this._checkForSelectableText();\n this._sideBySideActive = false;\n\n // Hide annotation layer when the user is making a selection. The annotation\n // layer appears above the invisible text layer and can interfere with text\n // selection. See https://github.com/hypothesis/client/issues/1464.\n this._updateAnnotationLayerVisibility = () => {\n const selection = pdfWindow.getSelection()!;\n\n // Add CSS class to indicate whether there is a selection. Annotation\n // layers are then hidden by a CSS rule in `pdfjs-overrides.scss`.\n this._pdfViewer.viewer.classList.toggle(\n 'is-selecting',\n !selection.isCollapsed,\n );\n };\n\n this._listeners = new ListenerCollection();\n this._listeners.add(\n document,\n 'selectionchange',\n this._updateAnnotationLayerVisibility,\n );\n\n this._destroyed = false;\n\n this._features = features;\n this._features.on('flagsChanged', () => {\n this.emit('supportedToolsChanged', this.supportedTools());\n });\n }\n\n destroy() {\n this.fitSideBySide({\n // Dummy layout that will cause side-by-side mode to be undone.\n expanded: false,\n width: 0,\n toolbarWidth: 0,\n height: window.innerHeight,\n });\n\n this._listeners.removeAll();\n this._pdfViewer.viewer.classList.remove('has-transparent-text-layer');\n this._observer.disconnect();\n this._banner.destroy();\n this._destroyed = true;\n\n super.destroy();\n }\n\n /**\n * Return the URL of the currently loaded PDF document.\n */\n uri() {\n return this._pdfMetadata.getUri();\n }\n\n /**\n * Return the metadata (eg. title) for the currently loaded PDF document.\n */\n getMetadata() {\n return this._pdfMetadata.getMetadata();\n }\n\n /**\n * Display a banner at the top of the PDF viewer showing information about the\n * current document.\n */\n showContentInfo(info: ContentInfoConfig) {\n this._banner.setContentInfo(info);\n }\n\n /**\n * Resolve serialized `selectors` from an annotation to a range.\n */\n anchor(\n root: HTMLElement,\n selectors: Selector[],\n ): Promise<Range | ShapeAnchor> {\n return anchor(selectors);\n }\n\n /**\n * Trim `range` to remove leading or trailing empty content, then check to see\n * if that trimmed Range lies within a single PDF page's text layer. If so,\n * return the trimmed Range.\n */\n getAnnotatableRange(range: Range) {\n try {\n const trimmedRange = TextRange.trimmedRange(range);\n if (canDescribe(trimmedRange)) {\n return trimmedRange;\n }\n } catch (err) {\n if (!(err instanceof RangeError)) {\n throw err;\n }\n }\n return null;\n }\n\n /* istanbul ignore next */\n canStyleClusteredHighlights() {\n return true;\n }\n\n /**\n * Generate selectors for the text in `region`.\n */\n describe(root: HTMLElement, region: Range | Shape): Promise<Selector[]> {\n if (region instanceof Range) {\n return describe(region);\n } else {\n return describeShape(region);\n }\n }\n\n /**\n * Check whether the PDF has selectable text and show a warning if not.\n */\n async _checkForSelectableText() {\n // Wait for PDF to load.\n try {\n await this.uri();\n } catch {\n return;\n }\n\n // Handle `PDF` instance being destroyed while URI is fetched. This is only\n // expected to happen in synchronous tests.\n if (this._destroyed) {\n return;\n }\n\n try {\n const hasText = await documentHasText();\n this._banner.showNoTextWarning(!hasText);\n } catch (err) {\n /* istanbul ignore next */\n console.warn('Unable to check for text in PDF:', err);\n }\n }\n\n // This method (re-)anchors annotations when pages are rendered and destroyed.\n _update() {\n // A list of annotations that need to be refreshed.\n const refreshAnnotations = [] as AnnotationData[];\n\n const pageCount = this._pdfViewer.pagesCount;\n for (let pageIndex = 0; pageIndex < pageCount; pageIndex++) {\n const page = this._pdfViewer.getPageView(pageIndex);\n if (!page?.textLayer || !isTextLayerRenderingDone(page.textLayer)) {\n continue;\n }\n\n // Detect what needs to be done by checking the rendering state.\n switch (page.renderingState) {\n case RenderingStates.INITIAL:\n // This page has been reset to its initial state so its text layer\n // is no longer valid. Null it out so that we don't process it again.\n page.textLayer = null;\n break;\n case RenderingStates.FINISHED:\n // This page is still rendered. If it has a placeholder node that\n // means the PDF anchoring module anchored annotations before it was\n // rendered. Remove this, which will cause the annotations to anchor\n // again, below.\n removePlaceholder(page.div);\n break;\n }\n }\n\n // Find all the anchors that have been invalidated by page state changes.\n for (const anchor of this._annotator.anchors) {\n // Skip any we already know about.\n if (anchor.highlights) {\n if (refreshAnnotations.includes(anchor.annotation)) {\n continue;\n }\n\n // If the highlights are no longer in the document it means that either\n // the page was destroyed by PDF.js or the placeholder was removed above.\n // The annotations for these anchors need to be refreshed.\n for (let index = 0; index < anchor.highlights.length; index++) {\n const hl = anchor.highlights[index];\n if (!document.body.contains(hl)) {\n anchor.highlights.splice(index, 1);\n delete anchor.region;\n refreshAnnotations.push(anchor.annotation);\n break;\n }\n }\n }\n }\n\n refreshAnnotations.map(annotation => this._annotator.anchor(annotation));\n }\n\n /**\n * Return the scrollable element which contains the document content.\n */\n contentContainer(): HTMLElement {\n return document.querySelector('#viewerContainer') as HTMLElement;\n }\n\n /**\n * Attempt to make the PDF viewer and the sidebar fit side-by-side without\n * overlap if there is enough room in the viewport to do so reasonably.\n * Resize the PDF viewer container element to leave the right amount of room\n * for the sidebar, and prompt PDF.js to re-render the PDF pages to scale\n * within that resized container.\n *\n * @return - True if side-by-side mode was activated\n */\n fitSideBySide(sidebarLayout: SidebarLayout): boolean {\n const maximumWidthToFit = window.innerWidth - sidebarLayout.width;\n const active = sidebarLayout.expanded && maximumWidthToFit >= MIN_PDF_WIDTH;\n\n // If the sidebar is closed, we reserve enough space for the toolbar controls\n // so that they don't overlap a) the chevron-menu on the right side of\n // PDF.js's top toolbar and b) the document's scrollbar.\n //\n // If the sidebar is open, we reserve space for the whole sidebar if there is\n // room, otherwise we reserve the same space as in the closed state to\n // prevent the PDF content shifting when opening and closing the sidebar.\n const reservedSpace = active\n ? sidebarLayout.width\n : sidebarLayout.toolbarWidth;\n this._pdfContainer.style.width = `calc(100% - ${reservedSpace}px)`;\n\n // The following logic is pulled from PDF.js `webViewerResize`\n const currentScaleValue = this._pdfViewer.currentScaleValue;\n if (\n currentScaleValue === 'auto' ||\n currentScaleValue === 'page-fit' ||\n currentScaleValue === 'page-width'\n ) {\n // NB: There is logic within the setter for `currentScaleValue`\n // Setting this scale value will prompt PDF.js to recalculate viewport\n this._pdfViewer.currentScaleValue = currentScaleValue;\n }\n // This will cause PDF pages to re-render if their scaling has changed\n this._pdfViewer.update();\n\n this._sideBySideActive = active;\n\n return active;\n }\n\n sideBySideActive() {\n return this._sideBySideActive;\n }\n\n supportedTools(): AnnotationTool[] {\n const imageAnnotation = this._features?.flagEnabled('pdf_image_annotation');\n if (imageAnnotation) {\n return ['selection', 'rect', 'point'];\n } else {\n return ['selection'];\n }\n }\n\n /**\n * Scroll to the location of an anchor in the PDF.\n *\n * If the anchor refers to a location that is an un-rendered page far from\n * the viewport, then scrolling happens in three phases. First the document\n * scrolls to the approximate location indicated by the placeholder anchor,\n * then `scrollToAnchor` waits until the page's text layer is rendered and\n * the annotation is re-anchored in the fully rendered page. Then it scrolls\n * again to the final location.\n */\n async scrollToAnchor(anchor: Anchor) {\n const annotation = anchor.annotation;\n const inPlaceholder = anchorIsInPlaceholder(anchor);\n const offset = this._anchorOffset(anchor);\n if (offset === null) {\n return;\n }\n\n // nb. We only compute the scroll offset once at the start of scrolling.\n // This is important as the highlight may be removed from the document during\n // the scroll due to a page transitioning from rendered <-> un-rendered.\n await scrollElement(this.contentContainer(), offset);\n\n if (inPlaceholder) {\n const anchor = await this._waitForAnnotationToBeAnchored(\n annotation,\n this._reanchoringMaxWait,\n );\n if (!anchor) {\n return;\n }\n const offset = this._anchorOffset(anchor);\n if (offset === null) {\n return;\n }\n await scrollElement(this.contentContainer(), offset);\n }\n }\n\n /**\n * Wait for an annotation to be anchored in a rendered page.\n */\n async _waitForAnnotationToBeAnchored(\n annotation: AnnotationData,\n maxWait: number,\n ): Promise<Anchor | null> {\n const start = Date.now();\n let anchor;\n do {\n // nb. Re-anchoring might result in a different anchor object for the\n // same annotation.\n anchor = this._annotator.anchors.find(a => a.annotation === annotation);\n if (!anchor || anchorIsInPlaceholder(anchor)) {\n anchor = null;\n\n // If no anchor was found, wait a bit longer and check again to see if\n // re-anchoring completed.\n await delay(20);\n }\n } while (!anchor && Date.now() - start < maxWait);\n return anchor ?? null;\n }\n\n /**\n * Return the offset that the PDF content container would need to be scrolled\n * to, in order to make an anchor visible.\n *\n * @return - Target offset or `null` if this anchor was not resolved\n */\n _anchorOffset(anchor: Anchor): number | null {\n if (!anchor.highlights) {\n // This anchor was not resolved to a location in the document.\n return null;\n }\n const highlight = anchor.highlights[0];\n return offsetRelativeTo(highlight, this.contentContainer());\n }\n\n async renderToBitmap(\n anchor: Anchor,\n opts: RenderToBitmapOptions,\n ): Promise<ImageBitmap> {\n const shape = anchor.target.selector?.find(s => s.type === 'ShapeSelector');\n const page = anchor.target.selector?.find(s => s.type === 'PageSelector');\n if (!page || !shape) {\n throw new Error('Can only render bitmaps for anchors with shapes');\n }\n\n const pageView = this._pdfViewer.getPageView(page.index);\n if (!pageView) {\n throw new Error('Failed to get page view');\n }\n\n let left;\n let right;\n let top;\n let bottom;\n\n switch (shape.shape.type) {\n case 'rect':\n ({ left, right, top, bottom } = shape.shape);\n break;\n case 'point':\n {\n const { x, y } = shape.shape;\n const [viewLeft, , viewRight] = pageView.pdfPage.view;\n const pageWidth = Math.abs(viewRight - viewLeft);\n const thumbnailSize = pageWidth * 0.1;\n left = x - thumbnailSize;\n top = y + thumbnailSize;\n right = x + thumbnailSize;\n bottom = y - thumbnailSize;\n }\n break;\n default:\n throw new Error('Unsupported shape type');\n }\n\n // Conversion factor from PDF pixels per inch to CSS pixels per inch.\n // See https://github.com/mozilla/pdf.js/blob/2f7d163dfdf40225479d1cc8f6d8ebd9e5273ca6/src/display/display_utils.js#L31.\n const CSS_PPI = 96.0;\n const PDF_PPI = 72.0;\n const PDF_TO_CSS_UNITS = CSS_PPI / PDF_PPI;\n\n const devicePixelRatio = opts.devicePixelRatio ?? 1;\n\n // Width of rect if rendered at 100% zoom, in CSS units.\n const naturalWidth = (right - left) * PDF_TO_CSS_UNITS * devicePixelRatio;\n\n // Create a `PageViewport` specifying which part of the page to draw and\n // the scale, rotation etc.\n const aspectRatio = (right - left) / (top - bottom);\n\n const width =\n typeof opts.maxWidth === 'number'\n ? Math.min(opts.maxWidth, naturalWidth)\n : naturalWidth;\n const height = width / aspectRatio;\n const viewport = pageView.pdfPage.getViewport({ scale: 1.0 });\n\n // Set scale so that rendered bitmap matches PDF canvas if zoom level is\n // set to `width / naturalWidth` (100% if width == naturalWidth).\n const scaleFactor =\n (width / naturalWidth) * PDF_TO_CSS_UNITS * devicePixelRatio;\n\n // `PageViewport` has a method to clone it with different parameters, but\n // that doesn't allow us to customize the `viewBox`. Hence we grab the\n // constructor and invoke it manually.\n const PageViewport = viewport.constructor as PageViewport;\n const boxView = new PageViewport({\n rotation: 0,\n scale: scaleFactor,\n userUnit: viewport.userUnit,\n viewBox: [left, bottom, right, top],\n });\n\n // Render page into an offscreen canvas\n const canvas = new OffscreenCanvas(width, height);\n const ctx = canvas.getContext('2d')!;\n const task = pageView.pdfPage.render({\n canvasContext: ctx as unknown as CanvasRenderingContext2D,\n viewport: boxView,\n });\n await task.promise;\n\n // For point annotations, draw a dot to indicate where the annotated point\n // is within the thumbnail.\n if (shape.shape.type === 'point') {\n ctx.save();\n\n ctx.scale(scaleFactor, scaleFactor);\n const x = shape.shape.x - left;\n const y = shape.shape.y - bottom;\n const radius = 5;\n\n ctx.strokeStyle = 'black';\n ctx.fillStyle = 'yellow';\n ctx.beginPath();\n ctx.arc(x, y, radius, 0, Math.PI * 2);\n ctx.fill();\n ctx.stroke();\n\n ctx.restore();\n }\n\n return canvas.transferToImageBitmap();\n }\n}\n","/**\n * Functions for working with EPUB Canonical Fragment Identifiers.\n *\n * See https://idpf.org/epub/linking/cfi/.\n */\n\n/**\n * Compare two arrays.\n *\n * Arrays are compared as a sequence of values in priority order. If the two\n * arrays are of different length but their common indexes have the same values,\n * the shorter array is considered less than the longer one.\n *\n * This logic is similar to how eg. tuples are compared in Python.\n */\nfunction compareArrays(\n a: Array<number | string>,\n b: Array<number | string>,\n): number {\n for (let i = 0; i < Math.min(a.length, b.length); i++) {\n if (a[i] === b[i]) {\n continue;\n } else if (typeof a[i] !== typeof b[i]) {\n // The result of comparing a number with a string is undefined if the\n // string cannot be coerced to a number. To simplify things, we just\n // decide that numbers sort before strings.\n return typeof a[i] === 'number' ? -1 : 1;\n } else if (a[i] < b[i]) {\n return -1;\n } else if (a[i] > b[i]) {\n return 1;\n }\n }\n return a.length - b.length;\n}\n\n/**\n * Split a hyphen-separated CFI range.\n *\n * CFI assertions are stripped in the process. If `range` does not contain a\n * hyphen, the result will be an empty range with the end point being the same\n * as the start point.\n *\n * @example\n * splitCFIRange(\"/2/4[chap-02]-/2/6[chap-03]\") // returns `[\"/2/4\", \"/2/6\"]`.\n */\nexport function splitCFIRange(range: string): [string, string] {\n const rangeWithoutAssertions = stripCFIAssertions(range);\n const [start, end] = rangeWithoutAssertions.split('-', 2);\n return [start, end ?? start];\n}\n\n/**\n * Strip assertions from a Canonical Fragment Identifier.\n *\n * Assertions are `[...]` enclosed sections which act as checks on the validity\n * of numbers but do not affect the sort order.\n *\n * @example\n * stripCFIAssertions(\"/6/14[chap05ref]\") // returns \"/6/14\"\n */\nexport function stripCFIAssertions(cfi: string): string {\n // Fast path for CFIs with no assertions.\n if (!cfi.includes('[')) {\n return cfi;\n }\n\n let result = '';\n\n // Has next char been escaped?\n let escaped = false;\n\n // Are we in a `[...]` assertion section?\n let inAssertion = false;\n\n for (const ch of cfi) {\n if (!escaped && ch === '^') {\n escaped = true;\n continue;\n }\n\n if (!escaped && ch === '[') {\n inAssertion = true;\n } else if (!escaped && inAssertion && ch === ']') {\n inAssertion = false;\n } else if (!inAssertion) {\n result += ch;\n }\n\n escaped = false;\n }\n\n return result;\n}\n\n/**\n * Compare two Canonical Fragment Identifiers.\n *\n * The full sorting rules for CFIs are specified by https://idpf.org/epub/linking/cfi/#sec-sorting.\n *\n * This function only considers the part of the CFI up to the first step\n * indirection (\"!\"), which identify a location within the EPUB's Package\n * Document. These portions of CFIs consist of a \"/\"-delimited sequence of\n * numbers, with optional assertions in `[...]` brackets (eg.\n * \"/2/4[chapter2ref]\").\n *\n * Per the sorting rules linked above, the input CFIs are assumed to be\n * unescaped. This means that they may contain circumflex (^) escape characters,\n * but don't have the additional escaping that is needed when CFIs are used\n * inside URIs or HTML.\n *\n * @example\n * compareCFIs(\"/2/3[chap3ref]\", \"/2/10[chap10ref]\") // returns -1\n *\n * @param a - The first CFI\n * @param b - The second CFI\n * @return A value that is negative, zero or positive depending on\n * whether `a` is less-than, equal-to or greater-than `b`\n */\nexport function compareCFIs(a: string, b: string): number {\n const parseCFI = (cfi: string) => {\n return documentCFI(cfi)\n .split('/')\n .map(str => {\n // CFI step values _should_ always be integers. We currently handle\n // invalid values by using a string comparison instead. We could\n // alternatively treat all invalid CFIs as equal.\n const intVal = parseInt(str, 10);\n return Number.isNaN(intVal) ? str : intVal;\n });\n };\n return compareArrays(parseCFI(a), parseCFI(b));\n}\n\n/**\n * Return true if the CFI `cfi` lies in the range [start, end).\n *\n * Only the part of the CFI up to the first step indirection (\"!\") is\n * considered. See {@link documentCFI}.\n */\nexport function cfiInRange(cfi: string, start: string, end: string): boolean {\n return compareCFIs(cfi, start) >= 0 && compareCFIs(cfi, end) < 0;\n}\n\n/**\n * Return a slice of `cfi` up to the first step indirection [1], with assertions\n * removed.\n *\n * A typical CFI consists of a path within the table of contents to indicate\n * a content document, a step indirection (\"!\"), then the path of an element\n * within the content document. For such a CFI, this function will retain only\n * the content document path.\n *\n * [1] https://idpf.org/epub/linking/cfi/#sec-path-indirection\n *\n * @example\n * documentCFI('/6/152[;vnd.vst.idref=ch13_01]!/4/2[ch13_sec_1]') // Returns \"/6/152\"\n */\nexport function documentCFI(cfi: string): string {\n const stripped = stripCFIAssertions(cfi);\n const sepIndex = stripped.indexOf('!');\n return sepIndex === -1 ? stripped : stripped.slice(0, sepIndex);\n}\n","import debounce from 'lodash.debounce';\n\nexport const DEBOUNCE_WAIT = 40;\n\ntype FrameCallback = (frame: HTMLIFrameElement) => void;\n\n/**\n * FrameObserver detects iframes added and deleted from the document.\n *\n * To enable annotation, an iframe must be opted-in by adding the\n * `enable-annotation` attribute.\n *\n * We require the `enable-annotation` attribute to avoid the overhead of loading\n * the client into frames which are not useful to annotate. See\n * https://github.com/hypothesis/client/issues/530\n */\nexport class FrameObserver {\n private _element: Element;\n private _onFrameAdded: FrameCallback;\n private _onFrameRemoved: FrameCallback;\n private _annotatableFrames: Set<HTMLIFrameElement>;\n private _isDisconnected: boolean;\n private _mutationObserver: MutationObserver;\n\n /**\n * @param element - root of the DOM subtree to watch for the addition and\n * removal of annotatable iframes\n * @param onFrameAdded - callback fired when an annotatable iframe is added\n * @param onFrameRemoved - callback triggered when the annotatable iframe is removed\n */\n constructor(\n element: Element,\n onFrameAdded: FrameCallback,\n onFrameRemoved: FrameCallback,\n ) {\n this._element = element;\n this._onFrameAdded = onFrameAdded;\n this._onFrameRemoved = onFrameRemoved;\n this._annotatableFrames = new Set<HTMLIFrameElement>();\n this._isDisconnected = false;\n\n this._mutationObserver = new MutationObserver(\n debounce(() => {\n this._discoverFrames();\n }, DEBOUNCE_WAIT),\n );\n this._discoverFrames();\n this._mutationObserver.observe(this._element, {\n childList: true,\n subtree: true,\n attributeFilter: ['enable-annotation'],\n });\n }\n\n disconnect() {\n this._isDisconnected = true;\n this._mutationObserver.disconnect();\n }\n\n private async _addFrame(frame: HTMLIFrameElement) {\n this._annotatableFrames.add(frame);\n try {\n await onNextDocumentReady(frame);\n if (this._isDisconnected) {\n return;\n }\n const frameWindow = frame.contentWindow;\n // This line raises an exception if the iframe is from a different origin\n frameWindow!.addEventListener('unload', () => {\n this._removeFrame(frame);\n });\n this._onFrameAdded(frame);\n } catch {\n console.warn(\n `Unable to inject the Hypothesis client (from '${document.location.href}' into a cross-origin frame '${frame.src}')`,\n );\n }\n }\n\n private _removeFrame(frame: HTMLIFrameElement) {\n this._annotatableFrames.delete(frame);\n this._onFrameRemoved(frame);\n }\n\n private _discoverFrames() {\n const frames = new Set<HTMLIFrameElement>(\n this._element.querySelectorAll('iframe[enable-annotation]'),\n );\n\n for (const frame of frames) {\n if (!this._annotatableFrames.has(frame)) {\n this._addFrame(frame);\n }\n }\n\n for (const frame of this._annotatableFrames) {\n if (!frames.has(frame)) {\n this._removeFrame(frame);\n }\n }\n }\n}\n\n/**\n * Test if this is the empty document that a new iframe has before the URL\n * specified by its `src` attribute loads.\n */\nfunction hasBlankDocumentThatWillNavigate(frame: HTMLIFrameElement): boolean {\n return (\n frame.contentDocument?.location.href === 'about:blank' &&\n // Do we expect the frame to navigate away from about:blank?\n frame.hasAttribute('src') &&\n frame.src !== 'about:blank'\n );\n}\n\n/**\n * Wrapper around {@link onDocumentReady} which returns a promise that resolves\n * the first time that a document in `frame` becomes ready.\n *\n * See {@link onDocumentReady} for the definition of _ready_.\n */\nexport function onNextDocumentReady(\n frame: HTMLIFrameElement,\n): Promise<Document> {\n return new Promise((resolve, reject) => {\n const unsubscribe = onDocumentReady(frame, (err, doc) => {\n unsubscribe();\n if (doc) {\n resolve(doc);\n } else {\n reject(err);\n }\n });\n });\n}\n\n/**\n * Register a callback that is invoked when the content document\n * (`frame.contentDocument`) in a same-origin iframe becomes _ready_.\n *\n * A document is _ready_ when its `readyState` is either \"interactive\" or\n * \"complete\". It must also not be the empty document with URL \"about:blank\"\n * that iframes have before they navigate to the URL specified by their \"src\"\n * attribute.\n *\n * The callback is fired both for the document that is in the frame when\n * `onDocumentReady` is called, as well as for new documents that are\n * subsequently loaded into the same frame.\n *\n * If at any time the frame navigates to an iframe that is cross-origin,\n * the callback will fire with an error. It will fire again for subsequent\n * navigations, but due to platform limitations, it will only fire after the\n * next document fully loads (ie. when the frame's `load` event fires).\n *\n * @return Callback that unsubscribes from future changes\n */\nexport function onDocumentReady(\n frame: HTMLIFrameElement,\n callback: (err: Error | null, document?: Document) => void,\n { pollInterval = 10 }: { pollInterval?: number } = {},\n): () => void {\n let pollTimer: number | undefined;\n // Two linting rules are conflicting here, so muting one of them.\n // This should be fixable by refactoring the whole function, as there are\n // crossed dependencies between local callbacks, that rely on each other\n // having been called in a specific order.\n // eslint-disable-next-line prefer-const\n let pollForDocumentChange: () => void;\n\n // Visited documents for which we have fired the callback or are waiting\n // to become ready.\n const documents = new WeakSet();\n\n const cancelPoll = () => {\n clearTimeout(pollTimer);\n pollTimer = undefined;\n };\n\n // Begin polling for a document change when the current document is about\n // to go away.\n const pollOnUnload = () => {\n if (frame.contentDocument) {\n frame.contentWindow?.addEventListener('unload', pollForDocumentChange);\n }\n };\n\n const checkForDocumentChange = () => {\n const currentDocument = frame.contentDocument;\n\n // `contentDocument` may be null if the frame navigated to a URL that is\n // cross-origin, or if the `<iframe>` was removed from the document.\n if (!currentDocument) {\n cancelPoll();\n const errorMessage = frame.isConnected\n ? 'Frame is cross-origin'\n : 'Frame is disconnected';\n callback(new Error(errorMessage));\n return;\n }\n\n if (documents.has(currentDocument)) {\n return;\n }\n documents.add(currentDocument);\n cancelPoll();\n\n if (!hasBlankDocumentThatWillNavigate(frame)) {\n const isReady =\n currentDocument.readyState === 'interactive' ||\n currentDocument.readyState === 'complete';\n\n if (isReady) {\n callback(null, currentDocument);\n } else {\n currentDocument.addEventListener('DOMContentLoaded', () =>\n callback(null, currentDocument),\n );\n }\n }\n\n // Poll for the next document change.\n pollOnUnload();\n };\n\n let canceled = false;\n pollForDocumentChange = () => {\n cancelPoll();\n if (!canceled) {\n pollTimer = setInterval(checkForDocumentChange, pollInterval);\n }\n };\n\n // Set up observers for signals that the document either has changed or will\n // soon change. There are two signals with different trade-offs:\n //\n // - Polling after the current document is about to be unloaded. This allows\n // us to detect the new document quickly, but may not fire in some\n // situations (exact circumstances unclear, but eg. MDN warns about this).\n //\n // This is set up in the first call to `checkForDocumentChange`.\n //\n // - The iframe's \"load\" event. This is guaranteed to fire but only after the\n // new document is fully loaded.\n frame.addEventListener('load', checkForDocumentChange);\n\n // Notify caller about the current document. This fires asynchronously so that\n // the caller will have received the unsubscribe callback first.\n const initialCheckTimer = setTimeout(checkForDocumentChange, 0);\n\n return () => {\n canceled = true;\n clearTimeout(initialCheckTimer);\n cancelPoll();\n frame.removeEventListener('load', checkForDocumentChange);\n };\n}\n","import { parseJsonConfig } from '../boot/parse-json-config';\nimport { generateHexString } from '../shared/random';\nimport type { Destroyable } from '../types/annotator';\nimport { onNextDocumentReady, FrameObserver } from './frame-observer';\n\n/**\n * Options for injecting the client into child frames.\n *\n * This includes the URL of the client's boot script, plus configuration\n * for the client when it loads in the child frame.\n */\nexport type InjectConfig = { clientUrl: string } & Record<string, unknown>;\n\n/**\n * HypothesisInjector injects the Hypothesis client into same-origin iframes.\n *\n * The client will be injected automatically into frames that have the\n * `enable-annotation` attribute set (see {@link FrameObserver}) and can be\n * manually injected into other frames using {@link injectClient}.\n */\nexport class HypothesisInjector implements Destroyable {\n private _config: InjectConfig;\n private _frameObserver: FrameObserver;\n\n /**\n * @param element - root of the DOM subtree to watch for the addition and\n * removal of annotatable iframes\n */\n constructor(element: Element, config: InjectConfig) {\n this._config = config;\n this._frameObserver = new FrameObserver(\n element,\n frame => injectClient(frame, config), // Frame added callback\n () => {}, // Frame removed callback\n );\n }\n\n /**\n * Disables the injection of the Hypothesis client into child iframes.\n */\n destroy() {\n this._frameObserver.disconnect();\n }\n}\n\n/**\n * Check if the client was added to a frame by {@link injectClient}.\n */\nfunction hasHypothesis(iframe: HTMLIFrameElement) {\n const iframeDocument = iframe.contentDocument!;\n return iframeDocument.querySelector('script.js-hypothesis-config') !== null;\n}\n\n/**\n * Remove the temporary client configuration added to a document by\n * {@link injectClient} or {@link HypothesisInjector}.\n */\nexport function removeTemporaryClientConfig(document_: Document = document) {\n const tempConfigEls = Array.from(\n document_.querySelectorAll(\n 'script.js-hypothesis-config[data-remove-on-unload]',\n ),\n );\n tempConfigEls.forEach(el => el.remove());\n}\n\n/**\n * Inject Hypothesis client into a frame.\n *\n * IMPORTANT: This method requires that the iframe is same-origin\n * (frame.contentDocument|contentWindow is not null).\n *\n * This waits for the frame to finish loading before injecting the client.\n * See {@link onDocumentReady}.\n *\n * This function does nothing if the client has already been added to the frame.\n * This is determined by the presence of temporary configuration `<script>`s\n * added by this function, which can be removed with {@link removeTemporaryClientConfig}.\n *\n * @param frameId - The ID for the guest frame. If none is provided, the guest\n * will use a new randomly-generated ID.\n */\nexport async function injectClient(\n frame: HTMLIFrameElement,\n config: InjectConfig,\n frameId?: string,\n) {\n if (hasHypothesis(frame)) {\n return;\n }\n\n await onNextDocumentReady(frame);\n\n // Propagate the client resource locations from the current frame.\n //\n // These settings are set only in the browser extension and not by the\n // embedded client (served by h).\n //\n // We could potentially do this by allowing these settings to be part of\n // the \"annotator\" config (see `annotator/config/index.js`) which gets passed\n // to the constructor.\n const { assetRoot, notebookAppUrl, profileAppUrl, sidebarAppUrl } =\n parseJsonConfig(document);\n\n const injectedConfig = {\n ...config,\n\n assetRoot,\n\n // FIXME - We propagate these settings because the boot script expects them,\n // but they shouldn't actually be needed when launching the client in a\n // frame as a guest only (ie. no sidebar). A caveat is that the\n // `<link>` element generated using the `sidebarAppUrl` value does also get\n // used for other purposes by the annotator entry point.\n notebookAppUrl,\n profileAppUrl,\n sidebarAppUrl,\n\n // Tell the client that it should load as a guest only (no sidebar).\n subFrameIdentifier: frameId ?? generateHexString(10),\n };\n\n const configElement = document.createElement('script');\n configElement.className = 'js-hypothesis-config';\n configElement.setAttribute('data-remove-on-unload', '');\n configElement.type = 'application/json';\n configElement.innerText = JSON.stringify(injectedConfig);\n\n const bootScript = document.createElement('script');\n bootScript.async = true;\n bootScript.src = config.clientUrl;\n\n const iframeDocument = frame.contentDocument!;\n iframeDocument.body.appendChild(configElement);\n iframeDocument.body.appendChild(bootScript);\n}\n","import { ListenerCollection } from '@hypothesis/frontend-shared';\nimport debounce from 'lodash.debounce';\nimport type { DebouncedFunction } from 'lodash.debounce';\n\nimport {\n rectCenter,\n rectsOverlapHorizontally,\n rectsOverlapVertically,\n unionRects,\n} from '../util/geometry';\n\ntype WordBox = {\n text: string;\n\n /** Bounding rect of all glyphs in word. */\n rect: DOMRect;\n};\n\ntype LineBox = {\n words: WordBox[];\n /** Bounding rect of all words in line. */\n rect: DOMRect;\n};\n\ntype ColumnBox = {\n lines: LineBox[];\n /** Bounding rect of all lines in column. */\n rect: DOMRect;\n};\n\n/**\n * Group characters in a page into words, lines and columns.\n *\n * The input is assumed to be _roughly_ reading order, more so at the low (word,\n * line) level. When the input is not in reading order, the output may be\n * divided into more lines / columns than expected. Downstream code is expected\n * to tolerate over-segmentation. This function should try to avoid producing\n * lines or columns that significantly intersect, as this can impair text\n * selection.\n *\n * @param charBoxes - Bounding rectangle associated with each character on the page\n * @param text - Text that corresponds to `charBoxes`\n */\nfunction analyzeLayout(charBoxes: DOMRect[], text: string): ColumnBox[] {\n const words = [] as WordBox[];\n\n let currentWord = { text: '', rect: new DOMRect() } as WordBox;\n\n // Group characters into words.\n const addWord = () => {\n if (currentWord.text.length > 0) {\n words.push(currentWord);\n currentWord = { text: '', rect: new DOMRect() };\n }\n };\n for (const [i, rect] of charBoxes.entries()) {\n const char = text[i];\n const isSpace = /\\s/.test(char);\n\n if (\n currentWord.text.length > 0 &&\n !rectsOverlapVertically(currentWord.rect, rect)\n ) {\n addWord();\n }\n\n currentWord.rect = unionRects(currentWord.rect, rect);\n\n // To simplify downstream logic, normalize whitespace.\n currentWord.text += isSpace ? ' ' : char;\n\n if (isSpace) {\n addWord();\n }\n }\n addWord();\n\n const lines = [] as LineBox[];\n\n let currentLine = { words: [], rect: new DOMRect() } as LineBox;\n\n // Group words into lines.\n const addLine = () => {\n if (currentLine.words.length > 0) {\n lines.push(currentLine);\n currentLine = { words: [], rect: new DOMRect() };\n }\n };\n for (const word of words) {\n const prevWord = currentLine.words[currentLine.words.length - 1];\n if (prevWord) {\n const prevCenter = rectCenter(prevWord.rect);\n const currentCenter = rectCenter(word.rect);\n const xDist = currentCenter.x - prevCenter.x;\n if (\n !rectsOverlapVertically(prevWord.rect, word.rect) ||\n // Break line if current word is left of previous word\n xDist < 0\n ) {\n addLine();\n }\n }\n currentLine.words.push(word);\n currentLine.rect = unionRects(currentLine.rect, word.rect);\n }\n addLine();\n\n const columns = [] as ColumnBox[];\n\n let currentColumn = { lines: [], rect: new DOMRect() } as ColumnBox;\n\n // Group lines into columns.\n const addColumn = () => {\n if (currentColumn.lines.length > 0) {\n columns.push(currentColumn);\n currentColumn = { lines: [], rect: new DOMRect() };\n }\n };\n for (const line of lines) {\n const prevLine = currentColumn.lines[currentColumn.lines.length - 1];\n\n if (prevLine) {\n const prevCenter = rectCenter(prevLine.rect);\n const currentCenter = rectCenter(line.rect);\n const yDist = currentCenter.y - prevCenter.y;\n\n if (\n !rectsOverlapHorizontally(prevLine.rect, line.rect) ||\n rectsOverlapVertically(prevLine.rect, line.rect) ||\n // Break column if current line is above previous line.\n //\n // In the case of a two column layout for example, this happens when\n // moving from the bottom of one column to the top of the next.\n yDist < 0 ||\n // Break column if there is a large gap between the previous and current lines.\n //\n // This helps to avoid generating intersecting columns if there happens\n // to be other content between the lines that comes later in the input.\n yDist > line.rect.height * 4\n ) {\n addColumn();\n }\n }\n currentColumn.lines.push(line);\n currentColumn.rect = unionRects(currentColumn.rect, line.rect);\n }\n addColumn();\n\n return columns;\n}\n\n/**\n * ImageTextLayer maintains a transparent text layer on top of an image\n * which contains text. This enables the text in the image to be selected\n * and highlighted.\n *\n * This is similar to the one that PDF.js creates for us in our standard PDF\n * viewer.\n */\nexport class ImageTextLayer {\n container: HTMLElement;\n\n private _imageSizeObserver?: ResizeObserver;\n private _listeners: ListenerCollection;\n private _updateTextLayerSize: DebouncedFunction<[]>;\n\n /**\n * Create a text layer which is displayed on top of `image`.\n *\n * @param image - Rendered image on which to overlay the text layer.\n * The text layer will be inserted into the DOM as the next sibling of `image`.\n * @param charBoxes - Bounding boxes for characters in the image.\n * Coordinates should be in the range [0-1], where 0 is the top/left corner\n * of the image and 1 is the bottom/right.\n * @param text - Characters in the image corresponding to `charBoxes`\n */\n constructor(image: Element, charBoxes: DOMRect[], text: string) {\n if (charBoxes.length !== text.length) {\n throw new Error('Char boxes length does not match text length');\n }\n\n // Create container for text layer and position it above the image.\n const containerParent = image.parentNode as HTMLElement;\n const container = document.createElement('hypothesis-text-layer');\n containerParent.insertBefore(container, image.nextSibling);\n\n // Position text layer over image. We assume the image's top-left corner\n // aligns with the top-left corner of its container.\n containerParent.style.position = 'relative';\n container.style.position = 'absolute';\n container.style.top = '0';\n container.style.left = '0';\n container.style.color = 'transparent';\n\n // Prevent inherited text alignment from affecting positioning.\n // VitalSource sets `text-align: center` for example.\n container.style.textAlign = 'left';\n\n // Use multiply blending to make text in the image more readable when\n // the corresponding text in the text layer is selected or highlighted.\n // We apply a similar effect in PDF.js.\n container.style.mixBlendMode = 'multiply';\n\n // Set a fixed font style on the container and create a canvas using the same\n // font which we can use to measure the \"natural\" size of the text. This is\n // later used when scaling the text to fit the underlying part of the image.\n const fontSize = 16;\n const fontFamily = 'sans-serif';\n container.style.fontSize = fontSize + 'px';\n container.style.fontFamily = fontFamily;\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d') as CanvasRenderingContext2D;\n context.font = `${fontSize}px ${fontFamily}`;\n\n /** Generate a CSS value that scales with the `--x-scale` or `--y-scale` CSS variables. */\n const scaledValue = (\n dimension: 'x' | 'y',\n value: number,\n unit = 'px' as string,\n ) => `calc(var(--${dimension}-scale) * ${value}${unit})`;\n\n // Group characters into words, lines and columns. Then use the result to\n // create a hierarchical DOM structure in the text layer:\n //\n // 1. Columns are positioned absolutely\n // 2. Columns stack lines vertically using a block layout\n // 3. Lines arrange words horizontally using an inline layout\n //\n // This allows the browser to select the expected text when the cursor is\n // in-between lines or words.\n const columns = analyzeLayout(charBoxes, text);\n\n for (const column of columns) {\n const columnEl = document.createElement('hypothesis-text-column');\n columnEl.style.display = 'block';\n columnEl.style.position = 'absolute';\n columnEl.style.left = scaledValue('x', column.rect.left);\n columnEl.style.top = scaledValue('y', column.rect.top);\n\n let prevLine = null;\n for (const line of column.lines) {\n const lineEl = document.createElement('hypothesis-text-line');\n lineEl.style.display = 'block';\n lineEl.style.marginLeft = scaledValue(\n 'x',\n line.rect.left - column.rect.left,\n );\n lineEl.style.height = scaledValue('y', line.rect.height);\n\n if (prevLine) {\n lineEl.style.marginTop = scaledValue(\n 'y',\n line.rect.top - prevLine.rect.bottom,\n );\n }\n prevLine = line;\n\n // Prevent line breaks if the word elements don't quite fit the line.\n lineEl.style.whiteSpace = 'nowrap';\n\n let prevWord = null;\n for (const word of line.words) {\n const wordEl = document.createElement('hypothesis-text-word');\n wordEl.style.display = 'inline-block';\n wordEl.style.transformOrigin = 'top left';\n wordEl.textContent = word.text;\n\n if (prevWord) {\n wordEl.style.marginLeft = scaledValue(\n 'x',\n word.rect.left - prevWord.rect.right,\n );\n }\n prevWord = word;\n\n // Set the size of this box used for layout. This does not affect the\n // rendered size of the content.\n wordEl.style.width = scaledValue('x', word.rect.width);\n wordEl.style.height = scaledValue('y', word.rect.height);\n\n // Don't collapse whitespace at end of words, so it remains visible\n // in selected text. Also prevent line breaks due to overflows.\n wordEl.style.whiteSpace = 'pre';\n\n // Scale content using a transform. This affects the rendered size\n // of the text, used by text selection and\n // `Element.getBoundingClientRect`, but not layout.\n const metrics = context.measureText(word.text);\n const xScale = scaledValue('x', word.rect.width / metrics.width, '');\n const yScale = scaledValue('y', word.rect.height / fontSize, '');\n wordEl.style.transform = `scale(${xScale}, ${yScale})`;\n\n lineEl.append(wordEl);\n }\n\n columnEl.append(lineEl);\n }\n\n container.append(columnEl);\n }\n\n const updateTextLayerSize = () => {\n const { width: imageWidth, height: imageHeight } =\n image.getBoundingClientRect();\n container.style.width = imageWidth + 'px';\n container.style.height = imageHeight + 'px';\n\n container.style.setProperty('--x-scale', `${imageWidth}`);\n container.style.setProperty('--y-scale', `${imageHeight}`);\n };\n\n updateTextLayerSize();\n\n /**\n * Container element for the text layer.\n *\n * This is exposed so that callers can tweak the style if needed (eg.\n * to set a z-index value).\n */\n this.container = container;\n\n this._updateTextLayerSize = debounce(updateTextLayerSize, { maxWait: 50 });\n this._listeners = new ListenerCollection();\n\n if (typeof ResizeObserver !== 'undefined') {\n this._imageSizeObserver = new ResizeObserver(() => {\n this._updateTextLayerSize();\n });\n this._imageSizeObserver.observe(image);\n }\n\n // Fallback for browsers that don't support ResizeObserver (Safari < 13.4).\n // Due to the debouncing, we can register this listener in all browsers for\n // simplicity, without downsides.\n this._listeners.add(window, 'resize', this._updateTextLayerSize);\n }\n\n /**\n * Synchronously update the text layer to match the size and position of\n * the image.\n *\n * Normally the text layer is resized automatically but asynchronously when\n * the image size changes (eg. due to the window being resized) and updates\n * are debounced. This method can be used to force an immediate update if\n * needed.\n */\n updateSync() {\n this._updateTextLayerSize();\n this._updateTextLayerSize.flush();\n }\n\n destroy() {\n this.container.remove();\n this._listeners.removeAll();\n this._updateTextLayerSize.cancel();\n this._imageSizeObserver?.disconnect();\n }\n}\n","import { ListenerCollection } from '@hypothesis/frontend-shared';\n\nimport { documentCFI, stripCFIAssertions } from '../../shared/cfi';\nimport { EventEmitter } from '../../shared/event-emitter';\nimport type {\n Anchor,\n AnnotationData,\n AnnotationTool,\n Integration,\n IntegrationEvents,\n SegmentInfo,\n Shape,\n SidebarLayout,\n} from '../../types/annotator';\nimport type {\n EPUBContentSelector,\n PageSelector,\n Selector,\n} from '../../types/api';\nimport type {\n ContentFrameGlobals,\n MosaicBookElement,\n PageBreakInfo,\n} from '../../types/vitalsource';\nimport { FeatureFlags } from '../features';\nimport { onDocumentReady } from '../frame-observer';\nimport { injectClient } from '../hypothesis-injector';\nimport type { InjectConfig } from '../hypothesis-injector';\nimport { HTMLIntegration } from './html';\nimport { preserveScrollPosition } from './html-side-by-side';\nimport { ImageTextLayer } from './image-text-layer';\n\n// When activating side-by-side mode for VitalSource PDF documents, make sure\n// at least this much space (in pixels) is left for the PDF document. Any\n// smaller and it feels unreadable or too-zoomed-out\nconst MIN_CONTENT_WIDTH = 480;\n\n/**\n * Return the custom DOM element that contains the book content iframe.\n */\nfunction findBookElement(document_ = document): MosaicBookElement | null {\n return document_.querySelector('mosaic-book') as MosaicBookElement | null;\n}\n\n/**\n * Return the role of the current frame in the VitalSource Bookshelf reader or\n * `null` if the frame is not part of Bookshelf.\n *\n * @return `container` if this is the parent of the content frame, `content` if\n * this is the frame that contains the book content or `null` if the document is\n * not part of the Bookshelf reader.\n */\nexport function vitalSourceFrameRole(\n window_ = window,\n): 'container' | 'content' | null {\n if (findBookElement(window_.document)) {\n return 'container';\n }\n\n const parentDoc = window_.frameElement?.ownerDocument;\n if (parentDoc && findBookElement(parentDoc)) {\n return 'content';\n }\n\n return null;\n}\n\n/**\n * VitalSourceInjector runs in the book container frame and loads the client into\n * book content frames.\n *\n * The frame structure of the VitalSource book reader looks like this:\n *\n * [VitalSource top frame - bookshelf.vitalsource.com]\n * |\n * [Book container frame - jigsaw.vitalsource.com]\n * |\n * [Book content frame - jigsaw.vitalsource.com]\n *\n * The Hypothesis client can be initially loaded in the container frame or the\n * content frame. As the user navigates around the book, the container frame\n * remains the same but the content frame is swapped out. When used in the\n * container frame, this class handles initial injection of the client as a\n * guest in the current content frame, and re-injecting the client into new\n * content frames when they are created.\n */\nexport class VitalSourceInjector {\n private _frameObserver: MutationObserver;\n\n /**\n * @param config - Configuration for injecting the client into\n * book content frames\n */\n constructor(config: InjectConfig) {\n const bookElement = findBookElement();\n if (!bookElement) {\n throw new Error('Book container element not found');\n }\n\n const contentFrames = new WeakSet<HTMLIFrameElement>();\n\n const shadowRoot = bookElement.shadowRoot!;\n const injectClientIntoContentFrame = () => {\n const frame = shadowRoot.querySelector('iframe');\n if (!frame || contentFrames.has(frame)) {\n // Either there is no content frame or we are already watching it.\n return;\n }\n contentFrames.add(frame);\n onDocumentReady(frame, (err, document_) => {\n if (err) {\n return;\n }\n\n // If `err` is null, then `document_` will be set.\n const body = document_!.body;\n\n const isBookContent =\n body &&\n // Check that this is not the temporary page containing encrypted and\n // invisible book content, which is replaced with the real content after\n // a form submission. These pages look something like:\n //\n // ```\n // <html>\n // <title>content</title>\n // <body><div id=\"page-content\">{ Base64 encoded data }</div></body>\n // </html>\n // ```\n !body.querySelector('#page-content');\n\n if (isBookContent) {\n injectClient(frame, config, 'vitalsource-content');\n }\n });\n };\n\n injectClientIntoContentFrame();\n\n // Re-inject client into content frame after a chapter navigation.\n this._frameObserver = new MutationObserver(injectClientIntoContentFrame);\n this._frameObserver.observe(shadowRoot, { childList: true, subtree: true });\n }\n\n destroy() {\n this._frameObserver.disconnect();\n }\n}\n\nfunction getPDFPageImage() {\n return document.querySelector('img#pbk-page') as HTMLImageElement | null;\n}\n\n/**\n * Fix how a VitalSource book content frame scrolls, so that various related\n * Hypothesis behaviors (the bucket bar, scrolling annotations into view) work\n * as intended.\n *\n * Some VitalSource books (PDFs) make content scrolling work by making the\n * content iframe really tall and having the parent frame scroll. This stops the\n * Hypothesis bucket bar and scrolling annotations into view from working.\n */\nfunction makeContentFrameScrollable(frame: HTMLIFrameElement) {\n if (frame.getAttribute('scrolling') !== 'no') {\n // This is a book (eg. EPUB) where the workaround is not required.\n return;\n }\n\n // Override inline styles of iframe (hence `!important`). The iframe lives\n // in Shadow DOM, so the element styles won't affect the rest of the app.\n const style = document.createElement('style');\n style.textContent = `iframe { height: 100% !important; }`;\n frame.insertAdjacentElement('beforebegin', style);\n\n const removeScrollingAttr = () => frame.removeAttribute('scrolling');\n removeScrollingAttr();\n\n // Sometimes the attribute gets re-added by VS. Remove it if that\n // happens.\n const attrObserver = new MutationObserver(removeScrollingAttr);\n attrObserver.observe(frame, { attributes: true });\n}\n\n/**\n * Lookup options for fetching page metadata from VitalSource.\n *\n * Enabling this options involves some extra work, so should be skipped if\n * the data is not needed.\n */\ntype PageInfoOptions = {\n /** Whether to fetch the title. */\n includeTitle?: boolean;\n\n /**\n * Whether to use a fallback strategy to get the page index if not available\n * in the {@link MosaicBookElement.getCurrentPage} response.\n */\n includePageIndex?: boolean;\n};\n\n/**\n * Return a copy of URL with the origin removed.\n *\n * eg. \"https://jigsaw.vitalsource.com/books/123/chapter.html?foo\" =>\n * \"/books/123/chapter.html\"\n */\nfunction stripOrigin(url: string) {\n // Resolve input URL in case it doesn't already have an origin.\n const parsed = new URL(url, document.baseURI);\n return parsed.pathname + parsed.search;\n}\n\n/**\n * Integration for the content frame in VitalSource's Bookshelf ebook reader.\n *\n * This integration delegates to the standard HTML integration for most\n * functionality, but it adds logic to:\n *\n * - Customize the document URI and metadata that is associated with annotations\n * - Prevent VitalSource's built-in selection menu from getting in the way\n * of the adder.\n * - Create a hidden text layer in PDF-based books, so the user can select text\n * in the PDF image. This is similar to what PDF.js does for us in PDFs.\n */\nexport class VitalSourceContentIntegration\n extends EventEmitter<IntegrationEvents>\n implements Integration\n{\n private _bookElement: MosaicBookElement;\n private _htmlIntegration: HTMLIntegration;\n private _listeners: ListenerCollection;\n\n /** Hidden text layer. Only used in PDF books. */\n private _textLayer?: ImageTextLayer;\n\n /**\n * Whether side-by-side is active. Only used in PDF books. For EPUB books\n * side-by-side delegates to the HTML integration.\n */\n private _sideBySideActive?: boolean;\n\n constructor(\n /* istanbul ignore next - defaults are overridden in tests */\n container: HTMLElement = document.body,\n /* istanbul ignore next - defaults are overridden in tests */\n options: {\n // Test seam\n bookElement?: MosaicBookElement;\n } = {},\n ) {\n super();\n\n const bookElement =\n options.bookElement ?? findBookElement(window.parent.document);\n if (!bookElement) {\n /* istanbul ignore next */\n throw new Error(\n 'Failed to find <mosaic-book> element in container frame',\n );\n }\n this._bookElement = bookElement;\n\n const htmlFeatures = new FeatureFlags();\n\n this._htmlIntegration = new HTMLIntegration({\n container,\n features: htmlFeatures,\n });\n\n this._listeners = new ListenerCollection();\n\n // Prevent mouse events from reaching the window. This prevents VitalSource\n // from showing its native selection menu, which obscures the client's\n // annotation toolbar.\n //\n // To avoid interfering with the client's own selection handling, this\n // event blocking must happen at the same level or higher in the DOM tree\n // than where SelectionObserver listens.\n const stopEvents = ['mouseup', 'mousedown', 'mouseout'];\n for (const event of stopEvents) {\n this._listeners.add(document.documentElement, event, e => {\n e.stopPropagation();\n });\n }\n\n // Install scrolling workaround for PDFs. We do this in the content frame\n // so that it works whether Hypothesis is loaded directly into the content\n // frame or injected by VitalSourceInjector from the parent frame.\n const frame = window.frameElement as HTMLIFrameElement | null;\n if (frame) {\n makeContentFrameScrollable(frame);\n }\n\n // If this is a PDF, create the hidden text layer above the rendered PDF\n // image.\n const bookImage = getPDFPageImage();\n\n const pageData = (window as ContentFrameGlobals).innerPageData;\n\n if (bookImage && pageData) {\n const charRects = pageData.glyphs.glyphs.map(glyph => {\n const left = glyph.l / 100;\n const right = glyph.r / 100;\n const top = glyph.t / 100;\n const bottom = glyph.b / 100;\n return new DOMRect(left, top, right - left, bottom - top);\n });\n\n this._textLayer = new ImageTextLayer(\n bookImage,\n charRects,\n pageData.words,\n );\n\n // VitalSource has several DOM elements in the page which are raised\n // above the image using z-index. One of these is used to handle VS's\n // own text selection functionality.\n //\n // Set a z-index on our text layer to raise it above VS's own one.\n this._textLayer.container.style.zIndex = '100';\n\n this._sideBySideActive = false;\n }\n }\n\n getAnnotatableRange(range: Range) {\n return this._htmlIntegration.getAnnotatableRange(range);\n }\n\n destroy() {\n if (this._textLayer) {\n this._textLayer.destroy();\n\n // Turn off side-by-side for PDF books. For EPUBs this is handled by\n // `this._htmlIntegration.destroy()`.\n this.fitSideBySide({\n // Dummy layout. Setting `expanded: false` disables side-by-side mode.\n expanded: false,\n height: window.innerHeight,\n width: 0,\n toolbarWidth: 0,\n });\n }\n this._listeners.removeAll();\n this._htmlIntegration.destroy();\n super.destroy();\n }\n\n anchor(root: HTMLElement, selectors: Selector[]) {\n return this._htmlIntegration.anchor(root, selectors);\n }\n\n async describe(root: HTMLElement, region: Range | Shape) {\n const selectors: Selector[] = this._htmlIntegration.describe(root, region);\n\n const {\n cfi,\n index: pageIndex,\n page: pageLabel,\n title,\n url,\n } = await this._getPageInfo({ includeTitle: true, includePageIndex: true });\n\n // We generate an \"EPUBContentSelector\" with a CFI for all VS books,\n // although for PDF-based books the CFI is a string generated from the\n // page number.\n const cfiSelector: EPUBContentSelector = {\n type: 'EPUBContentSelector',\n cfi,\n url,\n title,\n };\n const extraSelectors: Selector[] = [cfiSelector];\n\n // Add page number if available. PDF-based books always have them.\n // Publishers are encouraged to provide page numbers for EPUB-based books,\n // but not all do. See mentions of page numbers in the \"VitalSource ePub3\n // Submission Guide\" [1].\n //\n // [1] https://www.vitalsource.com/en-uk/products/vitalsource-epub3-implementation-guide-vitalsource-vvstdocsepub3implementguide?term=VST-DOCS-EPUB3IMPLEMENTGUIDE\n if (typeof pageIndex === 'number') {\n const pageSelector: PageSelector = {\n type: 'PageSelector',\n index: pageIndex,\n label: pageLabel,\n };\n extraSelectors.push(pageSelector);\n }\n\n selectors.push(...extraSelectors);\n\n return selectors;\n }\n\n contentContainer() {\n return this._htmlIntegration.contentContainer();\n }\n\n fitSideBySide(layout: SidebarLayout) {\n // For PDF books, handle side-by-side mode in this integration. For EPUBs,\n // delegate to the HTML integration.\n const bookImage = getPDFPageImage();\n if (bookImage && this._textLayer) {\n const bookContainer = bookImage.parentElement as HTMLElement;\n const textLayer = this._textLayer;\n\n // Update the PDF image size and alignment to fit alongside the sidebar.\n // `ImageTextLayer` will handle adjusting the text layer to match.\n const newWidth = window.innerWidth - layout.width;\n\n this._sideBySideActive = false;\n preserveScrollPosition(() => {\n if (layout.expanded && newWidth > MIN_CONTENT_WIDTH) {\n // The VS book viewer sets `text-align: center` on the <body> element\n // by default, which centers the book image in the page. When the sidebar\n // is open we need the image to be left-aligned.\n bookContainer.style.textAlign = 'left';\n bookImage.style.width = `${newWidth}px`;\n this._sideBySideActive = true;\n } else {\n bookContainer.style.textAlign = '';\n bookImage.style.width = '';\n }\n\n // Update text layer to match new image dimensions immediately. This\n // is needed so that `preserveScrollPosition` can see how the content\n // has shifted when this callback returns.\n textLayer.updateSync();\n });\n\n return this._sideBySideActive;\n } else {\n return this._htmlIntegration.fitSideBySide(layout);\n }\n }\n\n sideBySideActive() {\n if (typeof this._sideBySideActive === 'boolean') {\n return this._sideBySideActive;\n } else {\n return this._htmlIntegration.sideBySideActive();\n }\n }\n\n /* istanbul ignore next */\n supportedTools(): AnnotationTool[] {\n return ['selection'];\n }\n\n async getMetadata() {\n const bookInfo = this._bookElement.getBookInfo();\n return {\n title: bookInfo.title,\n link: [],\n };\n }\n\n navigateToSegment(ann: AnnotationData) {\n const selector = ann.target[0].selector?.find(\n s => s.type === 'EPUBContentSelector',\n ) as EPUBContentSelector | undefined;\n if (selector?.cfi) {\n this._bookElement.goToCfi(selector.cfi);\n } else if (selector?.url) {\n this._bookElement.goToURL(stripOrigin(selector.url));\n } else {\n throw new Error('No segment information available');\n }\n }\n\n persistFrame() {\n // Hint to the sidebar that it should not unload annotations when the\n // guest frame using this integration unloads.\n return true;\n }\n\n /**\n * Retrieve information about the currently displayed content document or\n * page.\n */\n async _getPageInfo({\n includeTitle = false,\n includePageIndex = false,\n }: PageInfoOptions = {}) {\n const [pageInfo, toc, pages] = await Promise.all([\n this._bookElement.getCurrentPage(),\n includeTitle ? this._bookElement.getTOC() : undefined,\n includePageIndex ? this._bookElement.getPages() : undefined,\n ]);\n\n // If changes in VitalSource ever mean that critical chapter/page metadata\n // fields are missing, fail loudly. Otherwise we might create annotations\n // that cannot be re-anchored in future.\n const requiredFields = ['absoluteURL', 'cfi'];\n for (const field of requiredFields) {\n // nb. We intentionally allow properties anywhere on the prototype chain,\n // rather than requiring `hasOwnProperty`.\n if (!(field in pageInfo)) {\n throw new Error(`Page metadata field \"${field}\" is missing`);\n }\n }\n\n let title;\n if (toc) {\n title = pageInfo.chapterTitle;\n\n // Find the first table of contents entry that corresponds to the current page,\n // and use its title instead of `pageInfo.chapterTitle`. This works around\n // https://github.com/hypothesis/client/issues/4986.\n const pageCFI = documentCFI(pageInfo.cfi);\n const tocEntry = toc.data?.find(\n entry => documentCFI(entry.cfi) === pageCFI,\n );\n if (tocEntry) {\n title = tocEntry.title;\n }\n }\n\n // For PDF-based books, the `pageInfo.index` property should be populated.\n // For EPUB-based books, it may not be. In that case we try to find a\n // page number in the complete page list instead.\n let pageIndex = pageInfo.index;\n if (pageIndex === undefined && pages) {\n const index = pages.data?.findIndex(page => page.cfi === pageInfo.cfi);\n if (index !== -1) {\n pageIndex = index;\n }\n }\n\n return {\n cfi: pageInfo.cfi,\n index: pageIndex,\n page: pageInfo.page,\n title,\n\n // The `pageInfo.absoluteURL` URL is an absolute path that does not\n // include the origin of VitalSource's CDN.\n url: new URL(pageInfo.absoluteURL, document.baseURI).toString(),\n };\n }\n\n /**\n * Get the page range for the current segment.\n */\n private async _getPageRange(\n cfi: string,\n ): Promise<{ start: string; end: string } | undefined> {\n let pageBreaks: PageBreakInfo[] = [];\n try {\n const pageBreaksResponse = await this._bookElement.getPageBreaks();\n if (pageBreaksResponse.ok && pageBreaksResponse.data) {\n pageBreaks = pageBreaksResponse.data;\n }\n } catch (err) {\n /* istanbul ignore next */\n console.error('Failed to get page breaks', err);\n }\n\n const cfiWithoutAssertions = stripCFIAssertions(cfi);\n const segmentPageBreaks = pageBreaks.filter(\n page => page.cfiWithoutAssertions.split('!')[0] === cfiWithoutAssertions,\n );\n\n let pages;\n if (segmentPageBreaks.length > 0) {\n const start = segmentPageBreaks[0].label;\n const end = segmentPageBreaks[segmentPageBreaks.length - 1].label;\n pages = { start, end };\n }\n return pages;\n }\n\n async segmentInfo(): Promise<SegmentInfo> {\n const { cfi, url } = await this._getPageInfo();\n const pages = await this._getPageRange(cfi);\n return { cfi, pages, url };\n }\n\n async uri() {\n const bookInfo = this._bookElement.getBookInfo();\n const bookId = bookInfo.isbn;\n if (!bookId) {\n throw new Error('Unable to get book ID from VitalSource');\n }\n return `https://bookshelf.vitalsource.com/reader/books/${bookId}`;\n }\n\n async scrollToAnchor(anchor: Anchor) {\n return this._htmlIntegration.scrollToAnchor(anchor);\n }\n}\n","import type { Annotator, Integration } from '../../types/annotator';\nimport { HTMLIntegration } from './html';\nimport { PDFIntegration, isPDF } from './pdf';\nimport {\n VitalSourceContentIntegration,\n vitalSourceFrameRole,\n} from './vitalsource';\n\n/**\n * Create the integration that handles document-type specific aspects of\n * guest functionality.\n */\nexport function createIntegration(annotator: Annotator): Integration {\n if (isPDF()) {\n return new PDFIntegration({\n annotator: annotator,\n features: annotator.features,\n });\n }\n\n const vsFrameRole = vitalSourceFrameRole();\n if (vsFrameRole === 'content') {\n return new VitalSourceContentIntegration(document.body);\n }\n\n return new HTMLIntegration({\n features: annotator.features,\n sideBySideOptions: annotator.sideBySide,\n });\n}\n","import { Callout } from '@hypothesis/frontend-shared';\n\nexport default function OutsideAssignmentNotice() {\n return (\n <Callout\n classes=\"fixed left-[10px] top-[10px]\"\n data-testid=\"outside-assignment-notice\"\n status=\"notice\"\n variant=\"raised\"\n >\n You are outside the page range for this assignment. Annotations made here\n may not be counted.\n </Callout>\n );\n}\n","import type { Destroyable } from '../types/annotator';\nimport OutsideAssignmentNotice from './components/OutsideAssignmentNotice';\nimport { PreactContainer } from './util/preact-container';\n\n/**\n * Notice displayed in the guest frame informing users that they are viewing\n * part of the document that is outside the scope of the current activity (eg.\n * a classroom assignment).\n */\nexport class OutsideAssignmentNoticeController implements Destroyable {\n private _container: PreactContainer;\n private _visible: boolean;\n\n constructor(container: HTMLElement) {\n this._visible = false;\n this._container = new PreactContainer('notice', () => this._render());\n container.appendChild(this._container.element);\n }\n\n destroy() {\n this._container.destroy();\n }\n\n setVisible(visible: boolean) {\n this._visible = visible;\n this._container.render();\n }\n\n private _render() {\n if (!this._visible) {\n return null;\n }\n return <OutsideAssignmentNotice />;\n }\n}\n","import { ListenerCollection } from '@hypothesis/frontend-shared';\n\nimport { selectedRange } from './range-util';\n\n/**\n * An observer that watches for and buffers changes to the document's current selection.\n */\nexport class SelectionObserver {\n /** Tracks the timeout ID of the last scheduled callback */\n private _pendingCallback: number | null;\n private _document: Document;\n private _listeners: ListenerCollection;\n\n /**\n * Start observing changes to the current selection in the document.\n *\n * @param callback - Callback invoked with the selected region of the document\n * when it has changed.\n * @param document_ - Test seam\n */\n constructor(\n callback: (range: Range | null) => void,\n document_: Document = document,\n ) {\n let isMouseDown = false;\n\n this._pendingCallback = null;\n\n const scheduleCallback = (delay = 10) => {\n this._pendingCallback = setTimeout(() => {\n callback(selectedRange(document_.getSelection()));\n }, delay);\n };\n\n const eventHandler = (event: Event) => {\n if (event.type === 'mousedown') {\n isMouseDown = true;\n }\n if (event.type === 'mouseup') {\n isMouseDown = false;\n }\n\n // If the user makes a selection with the mouse, wait until they release\n // it before reporting a selection change.\n if (isMouseDown) {\n return;\n }\n\n this._cancelPendingCallback();\n\n // Schedule a notification after a short delay. The delay serves two\n // purposes:\n //\n // - If this handler was called as a result of a 'mouseup' event then the\n // selection will not be updated until the next tick of the event loop.\n // In this case we only need a short delay.\n //\n // - If the user is changing the selection with a non-mouse input (eg.\n // keyboard or selection handles on mobile) this buffers updates and\n // makes sure that we only report one when the update has stopped\n // changing. In this case we want a longer delay.\n\n const delay = event.type === 'mouseup' ? 10 : 100;\n scheduleCallback(delay);\n };\n\n this._document = document_;\n this._listeners = new ListenerCollection();\n\n this._listeners.add(document_, 'selectionchange', eventHandler);\n\n // Mouse events are handled on the body because propagation may be stopped\n // before they reach the document in some environments (eg. VitalSource).\n this._listeners.add(document_.body, 'mousedown', eventHandler);\n this._listeners.add(document_.body, 'mouseup', eventHandler);\n\n // Report the initial selection.\n scheduleCallback(1);\n }\n\n disconnect() {\n this._listeners.removeAll();\n this._cancelPendingCallback();\n }\n\n private _cancelPendingCallback() {\n if (this._pendingCallback) {\n clearTimeout(this._pendingCallback);\n this._pendingCallback = null;\n }\n }\n}\n","/**\n * Test whether an iframe fills the viewport of an ancestor frame.\n */\nexport function frameFillsAncestor(frame: Window, ancestor: Window): boolean {\n if (frame === ancestor) {\n return true;\n }\n\n if (frame.parent !== ancestor) {\n // To keep things simple, we initially only support direct ancestors.\n return false;\n }\n\n if (!frame.frameElement) {\n // This is a cross-origin iframe. In this case we can't tell if it fills\n // the parent frame or not.\n return false;\n }\n\n const frameBox = frame.frameElement.getBoundingClientRect();\n\n // Threshold for deciding when a frame occupies enough of its parent's width\n // to count as filling the viewport.\n const fullWidthThreshold = 0.8;\n\n return frameBox.width / frame.parent.innerWidth >= fullWidthThreshold;\n}\n","import { ListenerCollection } from '@hypothesis/frontend-shared';\n\nimport { EventEmitter } from '../shared/event-emitter';\nimport { PortFinder, PortRPC } from '../shared/messaging';\nimport { generateHexString } from '../shared/random';\nimport { matchShortcut } from '../shared/shortcut';\nimport type {\n AbstractRange,\n AnnotationData,\n AnnotationTool,\n Annotator,\n Anchor,\n ContentInfoConfig,\n Destroyable,\n DocumentInfo,\n Integration,\n ShapeAnchor,\n SidebarLayout,\n SideBySideOptions,\n} from '../types/annotator';\nimport type { Target } from '../types/api';\nimport type {\n HostToGuestCalls,\n GuestToHostCalls,\n GuestToSidebarCalls,\n SidebarToGuestCalls,\n} from '../types/port-rpc-calls';\nimport { Adder } from './adder';\nimport { TextRange } from './anchoring/text-range';\nimport { BucketBarClient } from './bucket-bar-client';\nimport { DrawTool } from './draw-tool';\nimport { LayoutChangeEvent } from './events';\nimport { FeatureFlags } from './features';\nimport { HighlightClusterController } from './highlight-clusters';\nimport {\n getHighlightsFromPoint,\n highlightRange,\n highlightShape,\n removeAllHighlights,\n removeHighlights,\n setHighlightsFocused,\n setHighlightsVisible,\n} from './highlighter';\nimport { createIntegration } from './integrations';\nimport { OutsideAssignmentNoticeController } from './outside-assignment-notice';\nimport {\n itemsForRange,\n isSelectionBackwards,\n selectionFocusRect,\n selectedRange,\n} from './range-util';\nimport { SelectionObserver } from './selection-observer';\nimport { frameFillsAncestor } from './util/frame';\nimport { normalizeURI } from './util/url';\n\n/** HTML element created by the highlighter with an associated annotation. */\ntype AnnotationHighlight = HTMLElement & { _annotation?: AnnotationData };\n\n/** Return all the annotations tags associated with the selected text. */\nfunction annotationsForSelection(): string[] {\n const tags = itemsForRange(\n selectedRange() ?? new Range(),\n node => (node as AnnotationHighlight)._annotation?.$tag,\n );\n return tags;\n}\n\n/**\n * Return the annotation tags associated with highlights at given (clientX,\n * clientY) coordinates.\n */\nfunction annotationsAtPoint(x: number, y: number): string[] {\n return getHighlightsFromPoint(x, y)\n .map(h => (h as AnnotationHighlight)._annotation?.$tag)\n .filter(tag => tag !== undefined) as string[];\n}\n\nfunction isRange(r: AbstractRange | ShapeAnchor): r is AbstractRange {\n return r !== undefined && 'toRange' in r && typeof r.toRange === 'function';\n}\n\n/**\n * Resolve an anchor's associated document region to a concrete `Range`.\n *\n * This may fail if anchoring failed or if the document has been mutated since\n * the anchor was created in a way that invalidates the anchor.\n */\nfunction resolveAnchor(anchor: Anchor): Range | ShapeAnchor | null {\n if (!anchor.region) {\n return null;\n }\n\n if (!isRange(anchor.region)) {\n return anchor.region;\n }\n\n try {\n return anchor.region.toRange();\n } catch {\n return null;\n }\n}\n\nfunction removeTextSelection() {\n document.getSelection()?.removeAllRanges();\n}\n\n/**\n * Subset of the Hypothesis client configuration that is used by {@link Guest}.\n */\nexport type GuestConfig = {\n /**\n * An identifier used by this guest to identify the current frame when\n * communicating with the sidebar. This is only set in non-host frames.\n */\n subFrameIdentifier?: string;\n\n /** Configures a banner or other indicators showing where the content has come from. */\n contentInfoBanner?: ContentInfoConfig;\n\n /**\n * Promise that the guest should wait for before it attempts to anchor\n * annotations.\n */\n contentReady?: Promise<void>;\n\n sideBySide?: SideBySideOptions;\n};\n\n/**\n * Event dispatched by the client when it is about to scroll a highlight into\n * view.\n *\n * The host page can listen for this event in order to reveal the content if\n * not already visible. If the content will be revealed asynchronously,\n * {@link waitUntil} can be used to notify the client when it is ready.\n *\n * For more flexibility the host page can completely take over scrolling to the\n * range by calling {@link Event.preventDefault} on the event.\n */\nexport class ScrollToRangeEvent extends CustomEvent<Range> {\n private _ready: Promise<void> | null;\n\n /**\n * @param range - The DOM range that Hypothesis will scroll into view.\n */\n constructor(range: Range) {\n super('scrolltorange', {\n bubbles: true,\n cancelable: true,\n detail: range,\n });\n\n this._ready = null;\n }\n\n /**\n * If scrolling was deferred using {@link waitUntil}, returns the promise\n * that must resolve before the highlight is scrolled to.\n */\n get ready(): Promise<void> | null {\n return this._ready;\n }\n\n /**\n * Provide Hypothesis with a promise that resolves when the content\n * associated with the event's range is ready to be scrolled into view.\n */\n waitUntil(ready: Promise<void>) {\n this._ready = ready;\n }\n}\n\nexport type Events = {\n hostDisconnected(): void;\n};\n\n/**\n * `Guest` is the central class of the annotator that handles anchoring (locating)\n * annotations in the document when they are fetched by the sidebar, rendering\n * highlights for them and handling subsequent interactions with the highlights.\n *\n * It is also responsible for listening to changes in the current selection\n * and triggering the display of controls to create new annotations. When one\n * of these controls is clicked, it creates the new annotation and sends it to\n * the sidebar.\n *\n * Within a browser tab, there is typically one `Guest` instance per frame that\n * loads Hypothesis (not all frames will be annotation-enabled). In one frame,\n * usually the top-level one, there will also be an instance of the `Sidebar`\n * class that shows the sidebar app and surrounding UI. The `Guest` instance in\n * each frame connects to the sidebar and host frames as part of its\n * initialization.\n */\nexport class Guest\n extends EventEmitter<Events>\n implements Annotator, Destroyable\n{\n public element: HTMLElement;\n\n /** Ranges of the current text selection. */\n public selectedRanges: Range[];\n\n /**\n * The anchors generated by resolving annotation selectors to locations in the\n * document. These are added by `anchor` and removed by `detach`.\n *\n * There is one anchor per annotation `Target`, which typically means one\n * anchor per annotation.\n */\n public anchors: Anchor[];\n\n public features: FeatureFlags;\n\n public sideBySide?: SideBySideOptions;\n\n /** Promise that resolves when feature flags are received from the sidebar. */\n private _featureFlagsReceived: Promise<void>;\n\n /**\n * Promise that the guest will wait for before attempting to anchor\n * annotations.\n */\n private _contentReady?: Promise<void>;\n\n private _adder: Adder;\n private _clusterToolbar?: HighlightClusterController;\n private _drawTool: DrawTool;\n private _hostFrame: Window;\n private _highlightsVisible: boolean;\n private _isAdderVisible: boolean;\n private _informHostOnNextSelectionClear: boolean;\n private _selectionObserver: SelectionObserver;\n\n /**\n * Tags of annotations that are currently anchored or being anchored in\n * the guest.\n */\n private _annotations: Set<string>;\n private _frameIdentifier: string | null;\n private _portFinder: PortFinder;\n\n /**\n * Integration that handles document-type specific functionality in the\n * guest.\n */\n private _integration: Integration;\n\n /** Channel for host-guest communication. */\n private _hostRPC: PortRPC<HostToGuestCalls, GuestToHostCalls>;\n\n /** Channel for guest-sidebar communication. */\n private _sidebarRPC: PortRPC<SidebarToGuestCalls, GuestToSidebarCalls>;\n\n /**\n * The most recently received sidebar layout information from the host frame.\n */\n private _sidebarLayout: SidebarLayout | null;\n\n private _bucketBarClient: BucketBarClient;\n\n private _listeners: ListenerCollection;\n\n /**\n * Tags of currently hovered annotations. This is used to set the hovered\n * state correctly for new highlights if the associated annotation is already\n * hovered in the sidebar.\n */\n private _hoveredAnnotations: Set<string>;\n\n private _outsideAssignmentNotice: OutsideAssignmentNoticeController | null;\n\n /**\n * @param element -\n * The root element in which the `Guest` instance should be able to anchor\n * or create annotations. In an ordinary web page this typically `document.body`.\n * @param [config]\n * @param [hostFrame] -\n * Host frame which this guest is associated with. This is expected to be\n * an ancestor of the guest frame. It may be same or cross origin.\n */\n constructor(\n element: HTMLElement,\n config: GuestConfig = {},\n hostFrame: Window = window,\n ) {\n super();\n\n this.element = element;\n this._contentReady = config.contentReady;\n this._hostFrame = hostFrame;\n this._highlightsVisible = false;\n this._isAdderVisible = false;\n this._informHostOnNextSelectionClear = true;\n this.selectedRanges = [];\n this._outsideAssignmentNotice = null;\n\n this._adder = new Adder(this.element, {\n onAnnotate: () => this.createAnnotationFromSelection(),\n onHighlight: () =>\n this.createAnnotationFromSelection({ highlight: true }),\n\n // When the \"Show\" button is triggered, open the sidebar and select the\n // annotations. Also give keyboard focus to the first selected annotation.\n // This is an important affordance for eg. screen reader users as it gives\n // them an efficient way to navigate from highlights in the document to\n // the corresponding comments in the sidebar.\n onShowAnnotations: tags =>\n this.selectAnnotations(tags, { focusInSidebar: true }),\n });\n this._drawTool = new DrawTool(this.element);\n\n this._selectionObserver = new SelectionObserver(range => {\n if (range) {\n this._onSelection(range);\n } else {\n this._onClearSelection();\n }\n });\n\n this.anchors = [];\n this._annotations = new Set();\n\n // Set the frame identifier if it's available.\n // The \"top\" guest instance will have this as null since it's in a top frame not a sub frame\n this._frameIdentifier = config.subFrameIdentifier || null;\n\n this._portFinder = new PortFinder({\n hostFrame: this._hostFrame,\n source: 'guest',\n sourceId: this._frameIdentifier ?? undefined,\n });\n\n this.features = new FeatureFlags();\n this._featureFlagsReceived = new Promise(resolve => {\n this.features.on('flagsChanged', resolve);\n });\n\n this.sideBySide = config.sideBySide;\n\n this._integration = createIntegration(this);\n this._integration.on('uriChanged', () => this._sendDocumentInfo());\n if (config.contentInfoBanner) {\n this._integration.showContentInfo?.(config.contentInfoBanner);\n }\n\n if (this._integration.canStyleClusteredHighlights?.()) {\n this._clusterToolbar = new HighlightClusterController(\n this._integration.contentContainer(),\n {\n features: this.features,\n },\n );\n }\n\n this._integration.on('supportedToolsChanged', () =>\n this._notifySupportedToolsChanged(),\n );\n\n this._hostRPC = new PortRPC();\n this._connectHost(hostFrame);\n\n this._sidebarRPC = new PortRPC();\n this._sidebarLayout = null;\n this._connectSidebar();\n\n this._bucketBarClient = new BucketBarClient({\n contentContainer: this._integration.contentContainer(),\n hostRPC: this._hostRPC,\n });\n\n // Setup event handlers on the root element\n this._listeners = new ListenerCollection();\n this._setupElementEvents();\n\n this._hoveredAnnotations = new Set();\n }\n\n /** Return true if the sidebar is shown alongside the page content. */\n private _sideBySideActive(): boolean {\n if (this.sideBySide?.mode === 'manual' && this.sideBySide.isActive) {\n // Host page is handling side-by-side.\n return this.sideBySide.isActive();\n }\n // Hypothesis is handling side-by-side.\n return this._integration.sideBySideActive();\n }\n\n // Add DOM event listeners for clicks, taps etc. on the document and\n // highlights.\n _setupElementEvents() {\n // Hide the sidebar in response to a document click or tap, so it doesn't obscure\n // the document content.\n const maybeCloseSidebar = (event: PointerEvent) => {\n // Don't hide the sidebar if event was disabled because the sidebar\n // doesn't overlap the content.\n if (this._sideBySideActive()) {\n return;\n }\n\n // Don't hide the sidebar if clicking inside a `<hypothesis-*>` UI\n // element. This includes the controls that open and close the sidebar.\n if (\n event\n .composedPath()\n .some(\n target =>\n target instanceof Element &&\n target.localName.startsWith('hypothesis-'),\n )\n ) {\n return;\n }\n\n // Don't hide the sidebar if the event comes from an element that contains a highlight\n if (annotationsAtPoint(event.clientX, event.clientY).length) {\n return;\n }\n\n // If the click is within the bounds of the sidebar, ignore it. We don't\n // want to close the sidebar if the user clicks eg. in transparent areas\n // of the toolbar / bucket bar along the edge. Clicks within the sidebar\n // iframe won't be received by the guest frame(s) at all.\n if (\n frameFillsAncestor(window, this._hostFrame) &&\n this._sidebarLayout?.expanded &&\n window.innerWidth - event.clientX < this._sidebarLayout.width\n ) {\n return;\n }\n\n this._sidebarRPC.call('closeSidebar');\n };\n\n this._listeners.add(this.element, 'mouseup', event => {\n const { clientX, clientY, metaKey, ctrlKey } = event;\n const tags = annotationsAtPoint(clientX, clientY);\n if (tags.length && this._highlightsVisible) {\n const toggle = metaKey || ctrlKey;\n this.selectAnnotations(tags, { toggle });\n }\n });\n\n this._listeners.add(this.element, 'pointerdown', maybeCloseSidebar);\n\n this._listeners.add(this.element, 'mouseover', ({ clientX, clientY }) => {\n const tags = annotationsAtPoint(clientX, clientY);\n if (tags.length && this._highlightsVisible) {\n this._sidebarRPC.call('hoverAnnotations', tags);\n }\n });\n\n this._listeners.add(this.element, 'mouseout', () => {\n if (this._highlightsVisible) {\n this._sidebarRPC.call('hoverAnnotations', []);\n }\n });\n\n this._listeners.add(this.element, 'keydown', event => {\n this._handleShortcut(event);\n });\n\n this._listeners.add(window, 'resize', () => this._repositionAdder());\n }\n\n /**\n * Retrieve metadata for the current document.\n */\n async getDocumentInfo(): Promise<DocumentInfo> {\n const [uri, metadata, segmentInfo] = await Promise.all([\n this._integration.uri(),\n this._integration.getMetadata(),\n this._integration.segmentInfo?.(),\n ]);\n\n return {\n uri: normalizeURI(uri),\n metadata,\n segmentInfo,\n persistent: this._integration.persistFrame?.() ?? false,\n };\n }\n\n /** Send the current document URI and metadata to the sidebar. */\n async _sendDocumentInfo() {\n if (this._integration.waitForFeatureFlags?.()) {\n await this._featureFlagsReceived;\n }\n const metadata = await this.getDocumentInfo();\n this._sidebarRPC.call('documentInfoChanged', metadata);\n }\n\n /**\n * Shift the position of the adder on window 'resize' events\n */\n _repositionAdder() {\n if (!this._isAdderVisible) {\n return;\n }\n const range = selectedRange();\n if (range) {\n this._onSelection(range);\n }\n }\n\n async _connectHost(hostFrame: Window) {\n this._hostRPC.on('clearSelection', () => {\n if (selectedRange()) {\n this._informHostOnNextSelectionClear = false;\n removeTextSelection();\n }\n });\n\n this._hostRPC.on('createAnnotation', ({ tool }: { tool: AnnotationTool }) =>\n this.createAnnotation(tool),\n );\n\n this._hostRPC.on('hoverAnnotations', (tags: string[]) =>\n this._hoverAnnotations(tags),\n );\n\n this._hostRPC.on('scrollToAnnotation', (tag: string) => {\n this._scrollToAnnotation(tag);\n });\n\n this._hostRPC.on('selectAnnotations', (tags: string[], toggle: boolean) =>\n this.selectAnnotations(tags, { toggle }),\n );\n\n this._hostRPC.on('sidebarLayoutChanged', (sidebarLayout: SidebarLayout) => {\n if (frameFillsAncestor(window, hostFrame)) {\n this.fitSideBySide(sidebarLayout);\n }\n\n // Emit a custom event that the host page can respond to. This is useful\n // if the host app needs to change its layout depending on the sidebar's\n // visibility and size.\n this.element.dispatchEvent(\n new LayoutChangeEvent({\n sidebarLayout,\n sideBySideActive: this._sideBySideActive(),\n }),\n );\n });\n\n this._hostRPC.on('close', () => this.emit('hostDisconnected'));\n\n // Schedule messages to be sent after connection to host is established.\n this._notifySupportedToolsChanged();\n\n // Discover and connect to the host frame. All RPC events must be\n // registered before creating the channel.\n const hostPort = await this._portFinder.discover('host');\n this._hostRPC.connect(hostPort);\n }\n\n /** Report the tools supported by the current document type to the host frame. */\n private _notifySupportedToolsChanged() {\n this._hostRPC.call(\n 'supportedToolsChanged',\n this._integration.supportedTools(),\n );\n }\n\n /**\n * Scroll an anchor into view and notify the host page.\n *\n * Returns a promise that resolves when scrolling has completed. See\n * {@link Integration.scrollToAnchor}.\n */\n private async _scrollToAnchor(anchor: Anchor) {\n const region = resolveAnchor(anchor);\n if (!region) {\n return;\n }\n\n let range;\n if (region instanceof Range) {\n range = region;\n } else {\n range = new Range();\n range.selectNodeContents(region.anchor);\n }\n\n // Emit a custom event that the host page can respond to. This is useful\n // if the content is in a hidden section of the page that needs to be\n // revealed before it can be scrolled to.\n const event = new ScrollToRangeEvent(range);\n\n const defaultNotPrevented = this.element.dispatchEvent(event);\n\n if (defaultNotPrevented) {\n await event.ready;\n await this._integration.scrollToAnchor(anchor);\n }\n }\n\n private async _scrollToAnnotation(tag: string) {\n const anchor = this.anchors.find(a => a.annotation.$tag === tag);\n if (!anchor?.highlights) {\n return;\n }\n await this._scrollToAnchor(anchor);\n }\n\n async _connectSidebar() {\n this._sidebarRPC.on(\n 'featureFlagsUpdated',\n (flags: Record<string, boolean>) => this.features.update(flags),\n );\n\n // Handlers for events sent when user hovers or clicks on an annotation card\n // in the sidebar.\n this._sidebarRPC.on('hoverAnnotations', (tags: string[]) =>\n this._hoverAnnotations(tags),\n );\n\n this._sidebarRPC.on('scrollToAnnotation', (tag: string) => {\n this._scrollToAnnotation(tag);\n });\n\n // Handler for controls on the sidebar\n this._sidebarRPC.on('setHighlightsVisible', (showHighlights: boolean) => {\n this.setHighlightsVisible(showHighlights, false /* notifyHost */);\n });\n\n this._sidebarRPC.on('deleteAnnotation', (tag: string) => this.detach(tag));\n\n this._sidebarRPC.on(\n 'loadAnnotations',\n async (annotations: AnnotationData[]) => {\n try {\n await Promise.all(annotations.map(ann => this.anchor(ann)));\n } catch (e) {\n /* istanbul ignore next */\n console.warn('Failed to anchor annotations:', e);\n }\n },\n );\n\n this._sidebarRPC.on('showContentInfo', (info: ContentInfoConfig) =>\n this._integration.showContentInfo?.(info),\n );\n\n this._sidebarRPC.on(\n 'setOutsideAssignmentNoticeVisible',\n (show: boolean) => {\n this._setOutsideAssignmentNoticeVisible(show);\n },\n );\n\n this._sidebarRPC.on('navigateToSegment', (annotation: AnnotationData) =>\n this._integration.navigateToSegment?.(annotation),\n );\n\n this._sidebarRPC.on('renderThumbnail', (tag, options, callback) => {\n const renderThumbnail = async () => {\n if (!this._integration.renderToBitmap) {\n throw new Error(\n 'Thumbnail rendering not supported for document type',\n );\n }\n\n // There is a race condition here that thumbnail rendering can fail if\n // anchoring is still in progress. What we should do is defer rendering\n // until anchoring completes.\n const anchor = this.anchors.find(a => a.annotation.$tag === tag);\n if (!anchor) {\n throw new Error('Annotation not anchored in guest');\n }\n\n return this._integration.renderToBitmap(anchor, options);\n };\n\n renderThumbnail()\n .then(bitmap => callback({ ok: true, value: bitmap }))\n .catch(error => callback({ ok: false, error: error.message }));\n });\n\n // Connect to sidebar and send document info/URIs to it.\n //\n // RPC calls are deferred until a connection is made, so these steps can\n // complete in either order.\n this._portFinder.discover('sidebar').then(port => {\n this._sidebarRPC.connect(port);\n });\n\n this._sendDocumentInfo();\n }\n\n destroy() {\n this._drawTool.destroy();\n this._portFinder.destroy();\n this._hostRPC.destroy();\n this._sidebarRPC.destroy();\n\n this._listeners.removeAll();\n\n this._selectionObserver.disconnect();\n this._adder.destroy();\n this._bucketBarClient.destroy();\n this._clusterToolbar?.destroy();\n this._outsideAssignmentNotice?.destroy();\n\n removeAllHighlights(this.element);\n\n this._integration.destroy();\n\n super.destroy();\n }\n\n /**\n * Anchor an annotation's selectors in the document.\n *\n * _Anchoring_ resolves a set of selectors to a concrete region of the document\n * which is then highlighted.\n *\n * Any existing anchors associated with `annotation` will be removed before\n * re-anchoring the annotation.\n */\n async anchor(annotation: AnnotationData): Promise<Anchor[]> {\n if (this._contentReady) {\n await this._contentReady;\n this._contentReady = undefined;\n }\n\n /**\n * Resolve an annotation's selectors to a concrete range.\n */\n const locate = async (target: Target): Promise<Anchor> => {\n // Annotations must have either a quote or a shape selector.\n //\n // For annotations of text, the quote is used to verify anchoring with\n // other selector types.\n if (\n !target.selector ||\n !target.selector.some(\n s => s.type === 'TextQuoteSelector' || s.type === 'ShapeSelector',\n )\n ) {\n return { annotation, target };\n }\n\n let anchor: Anchor;\n try {\n const region = await this._integration.anchor(\n this.element,\n target.selector,\n );\n if (region instanceof Range) {\n // Convert the `Range` to a `TextRange` which can be converted back to\n // a `Range` later. The `TextRange` representation allows for highlights\n // to be inserted during anchoring other annotations without \"breaking\"\n // this anchor.\n const textRange = TextRange.fromRange(region);\n anchor = { annotation, target, region: textRange };\n } else {\n anchor = { annotation, target, region };\n }\n } catch {\n anchor = { annotation, target };\n }\n return anchor;\n };\n\n /**\n * Highlight the text range that `anchor` refers to.\n */\n const highlight = (anchor: Anchor) => {\n const region = resolveAnchor(anchor);\n if (!region) {\n return;\n }\n\n let highlights;\n if (region instanceof Range) {\n highlights = highlightRange(\n region,\n anchor.annotation?.$cluster /* cssClass */,\n ) as AnnotationHighlight[];\n } else {\n highlights = highlightShape(region) as AnnotationHighlight[];\n }\n highlights.forEach(h => {\n h._annotation = anchor.annotation;\n });\n anchor.highlights = highlights;\n\n if (this._hoveredAnnotations.has(anchor.annotation.$tag)) {\n setHighlightsFocused(highlights, true);\n }\n };\n\n // Remove existing anchors for this annotation.\n this.detach(annotation.$tag, false /* notify */);\n\n this._annotations.add(annotation.$tag);\n\n // Resolve selectors to ranges and insert highlights.\n if (!annotation.target) {\n annotation.target = [];\n }\n const anchors = await Promise.all(annotation.target.map(locate));\n\n // If the annotation was removed while anchoring, don't save the anchors.\n if (!this._annotations.has(annotation.$tag)) {\n return [];\n }\n\n for (const anchor of anchors) {\n highlight(anchor);\n }\n\n // Set flag indicating whether anchoring succeeded. For each target,\n // anchoring is successful either if there are no selectors (ie. this is a\n // Page Note) or we successfully resolved the selectors to a range.\n annotation.$orphan =\n anchors.length > 0 &&\n anchors.every(anchor => anchor.target.selector && !anchor.region);\n\n this._updateAnchors(this.anchors.concat(anchors), true /* notify */);\n\n // Let other frames (eg. the sidebar) know about the new annotation.\n this._sidebarRPC.call('syncAnchoringStatus', annotation);\n\n return anchors;\n }\n\n /**\n * Remove the anchors and associated highlights for an annotation from the document.\n *\n * @param [notify] - For internal use. Whether to inform the host\n * frame about the removal of an anchor.\n */\n detach(tag: string, notify = true) {\n this._annotations.delete(tag);\n\n const anchors = [] as Anchor[];\n for (const anchor of this.anchors) {\n if (anchor.annotation.$tag !== tag) {\n anchors.push(anchor);\n } else if (anchor.highlights) {\n removeHighlights(anchor.highlights);\n }\n }\n this._updateAnchors(anchors, notify);\n }\n\n _updateAnchors(anchors: Anchor[], notify: boolean) {\n this.anchors = anchors;\n this._clusterToolbar?.scheduleClusterUpdates();\n if (notify) {\n this._bucketBarClient.update(this.anchors);\n }\n }\n\n /** Create a new annotation using the specified tool. */\n async createAnnotation(tool: AnnotationTool): Promise<AnnotationData> {\n if (tool === 'selection') {\n return this.createAnnotationFromSelection();\n } else if (['rect', 'point'].includes(tool)) {\n // Draw the shape for the new annotation's region.\n const shape = await this._drawTool.draw(tool);\n\n // Create annotation data and send to sidebar.\n const info = await this.getDocumentInfo();\n const target: Target[] = [\n {\n source: info.uri,\n selector: await this._integration.describe(this.element, shape),\n },\n ];\n\n const annotation: AnnotationData = {\n uri: info.uri,\n document: info.metadata,\n target,\n $tag: 'a:' + generateHexString(8),\n };\n\n this._sidebarRPC.call('createAnnotation', annotation);\n this.anchor(annotation);\n\n return annotation;\n } else {\n throw new Error('Unsupported annotation tool');\n }\n }\n\n /**\n * Create a new annotation that is associated with the selected region of\n * the current document.\n *\n * @param options\n * @param [options.highlight] - If true, the new annotation has\n * the `$highlight` flag set, causing it to be saved immediately without\n * prompting for a comment.\n * @return The new annotation\n */\n async createAnnotationFromSelection({\n highlight = false,\n } = {}): Promise<AnnotationData> {\n const ranges = this.selectedRanges;\n this.selectedRanges = [];\n\n const info = await this.getDocumentInfo();\n const root = this.element;\n const rangeSelectors = await Promise.all(\n ranges.map(range => this._integration.describe(root, range)),\n );\n const target = rangeSelectors.map(selectors => ({\n source: info.uri,\n\n // In the Hypothesis API the field containing the selectors is called\n // `selector`, despite being a list.\n selector: selectors,\n }));\n\n const annotation: AnnotationData = {\n uri: info.uri,\n document: info.metadata,\n target,\n $highlight: highlight,\n $cluster: highlight ? 'user-highlights' : 'user-annotations',\n $tag: 'a:' + generateHexString(8),\n };\n\n this._sidebarRPC.call('createAnnotation', annotation);\n this.anchor(annotation);\n\n // Removing the text selection triggers the `SelectionObserver` callback,\n // which causes the adder to be removed after some delay.\n removeTextSelection();\n\n return annotation;\n }\n\n /**\n * Indicate in the sidebar that certain annotations are focused (ie. the\n * associated document region(s) is hovered).\n */\n _hoverAnnotations(tags: string[]) {\n this._hoveredAnnotations.clear();\n tags.forEach(tag => this._hoveredAnnotations.add(tag));\n\n for (const anchor of this.anchors) {\n if (anchor.highlights) {\n const toggle = tags.includes(anchor.annotation.$tag);\n setHighlightsFocused(anchor.highlights, toggle);\n }\n }\n\n this._sidebarRPC.call('hoverAnnotations', tags);\n }\n\n /**\n * Show or hide the adder toolbar when the selection changes.\n */\n _onSelection(range: Range) {\n const annotatableRange = this._integration.getAnnotatableRange(range);\n if (!annotatableRange) {\n this._onClearSelection();\n return;\n }\n\n const selection = document.getSelection()!;\n const isBackwards = isSelectionBackwards(selection);\n const focusRect = selectionFocusRect(selection);\n if (!focusRect) {\n // The selected range does not contain any text\n this._onClearSelection();\n return;\n }\n\n this.selectedRanges = [annotatableRange];\n this._hostRPC.call('textSelected');\n\n this._adder.annotationsForSelection = annotationsForSelection();\n this._isAdderVisible = true;\n this._adder.show(focusRect, isBackwards);\n }\n\n _onClearSelection() {\n this._isAdderVisible = false;\n this._adder.hide();\n this.selectedRanges = [];\n if (this._informHostOnNextSelectionClear) {\n this._hostRPC.call('textUnselected');\n }\n this._informHostOnNextSelectionClear = true;\n }\n\n /**\n * Show the given annotations in the sidebar.\n *\n * This sets up a filter in the sidebar to show only the selected annotations\n * and opens the sidebar. Optionally it can also transfer keyboard focus to\n * the annotation card for the first selected annotation.\n *\n * @param tags\n * @param options\n * @param [options.toggle] - Toggle whether the annotations are\n * selected, as opposed to just selecting them\n * @param [options.focusInSidebar] - Whether to transfer keyboard\n * focus to the card for the first annotation in the selection. This\n * option has no effect if {@link toggle} is true.\n */\n selectAnnotations(\n tags: string[],\n { toggle = false, focusInSidebar = false } = {},\n ) {\n if (toggle) {\n this._sidebarRPC.call('toggleAnnotationSelection', tags);\n } else {\n this._sidebarRPC.call('showAnnotations', tags, focusInSidebar);\n }\n this._sidebarRPC.call('openSidebar');\n }\n\n /**\n * Set whether highlights are visible in the document or not.\n *\n * @param visible\n * @param notifyHost - Whether to notify the host frame about this\n * change. This should be true unless the request to change highlight\n * visibility is coming from the host frame.\n */\n setHighlightsVisible(visible: boolean, notifyHost = true) {\n setHighlightsVisible(this.element, visible);\n this._highlightsVisible = visible;\n if (notifyHost) {\n this._hostRPC.call('highlightsVisibleChanged', visible);\n }\n }\n\n get highlightsVisible() {\n return this._highlightsVisible;\n }\n\n /**\n * Attempt to fit the document content alongside the sidebar.\n *\n * @param sidebarLayout\n */\n fitSideBySide(sidebarLayout: SidebarLayout) {\n this._sidebarLayout = sidebarLayout;\n this._integration.fitSideBySide(sidebarLayout);\n }\n\n /**\n * Return the tags of annotations that are currently displayed in a hovered\n * state.\n */\n get hoveredAnnotationTags(): Set<string> {\n return this._hoveredAnnotations;\n }\n\n /**\n * Handle a potential shortcut trigger.\n */\n private _handleShortcut(event: KeyboardEvent) {\n if (matchShortcut(event, 'Ctrl+Shift+H')) {\n this.setHighlightsVisible(!this._highlightsVisible);\n }\n }\n\n /** Show or hide banner warning user they are outside page range for assignment. */\n private _setOutsideAssignmentNoticeVisible(show: boolean) {\n if (!this._outsideAssignmentNotice) {\n this._outsideAssignmentNotice = new OutsideAssignmentNoticeController(\n this.element,\n );\n }\n this._outsideAssignmentNotice.setVisible(show);\n }\n}\n","/**\n * Encode app configuration in a URL fragment.\n *\n * This is used by the annotator to pass configuration to the sidebar and\n * notebook apps, which they can easily read on startup. The configuration is\n * passed in the fragment to avoid invalidating cache entries for the URL\n * or adding noise to server logs.\n *\n * @return URL with added fragment\n */\nexport function addConfigFragment(baseURL: string, config: object): string {\n const url = new URL(baseURL);\n const params = new URLSearchParams();\n params.append('config', JSON.stringify(config));\n url.hash = params.toString();\n return url.toString();\n}\n\n/**\n * Parse configuration from a URL generated by {@link addConfigFragment}.\n */\nexport function parseConfigFragment(url: string): Record<string, unknown> {\n const configStr = new URL(url).hash.slice(1);\n const configJSON = new URLSearchParams(configStr).get('config');\n return JSON.parse(configJSON || '{}');\n}\n","/**\n * Create the JSON-serializable subset of annotator configuration that should\n * be passed to the sidebar or notebook applications.\n *\n * @param appURL - URL from which the application will be served\n */\nexport function createAppConfig(\n appURL: string,\n config: Record<string, unknown>,\n): Record<string, unknown> {\n const appConfig: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(config)) {\n // Remove several annotator-only properties.\n //\n // nb. We don't currently strip all the annotator-only properties here.\n // That's OK because validation / filtering happens in the sidebar app itself.\n // It just results in unnecessary content in the sidebar iframe's URL string.\n if (key === 'notebookAppUrl' || key === 'sidebarAppUrl') {\n continue;\n }\n\n // Strip nullish properties, as these are ignored by the application and\n // they add noise to logs etc.\n //\n // eslint-disable-next-line eqeqeq\n if (value == null) {\n continue;\n }\n\n appConfig[key] = value;\n }\n\n // Pass the expected origin of the app. This is used to detect when it is\n // served from a different location than expected, which may stop it working.\n appConfig.origin = new URL(appURL).origin;\n\n // Pass the version of the client, so we can check if it is the same as the\n // one used in the sidebar/notebook/profile.\n appConfig.version = '__VERSION__';\n\n // Pass the URL of the page that embedded the client.\n const hostURL = new URL(window.location.href);\n hostURL.hash = '';\n appConfig.hostURL = hostURL.toString();\n\n // Some config settings are not JSON-stringifiable (e.g. JavaScript\n // functions) and will be omitted when the config is JSON-stringified.\n // Add a JSON-stringifiable option for each of these so that the sidebar can\n // at least know whether the callback functions were provided or not.\n if (Array.isArray(appConfig.services) && appConfig.services?.length > 0) {\n const service = appConfig.services[0];\n if (service.onLoginRequest) {\n service.onLoginRequestProvided = true;\n }\n if (service.onLogoutRequest) {\n service.onLogoutRequestProvided = true;\n }\n if (service.onSignupRequest) {\n service.onSignupRequestProvided = true;\n }\n if (service.onProfileRequest) {\n service.onProfileRequestProvided = true;\n }\n if (service.onHelpRequest) {\n service.onHelpRequestProvided = true;\n }\n }\n\n return appConfig;\n}\n","import classnames from 'classnames';\nimport type { ComponentChildren } from 'preact';\nimport { useEffect, useMemo, useRef } from 'preact/hooks';\n\ntype DialogProps = {\n closed: boolean;\n children: ComponentChildren;\n onClose: () => void;\n 'data-testid'?: string;\n 'aria-label'?: string;\n};\n\nfunction NativeDialog({\n closed,\n onClose,\n children,\n 'data-testid': testId,\n 'aria-label': label,\n}: DialogProps) {\n const dialogRef = useRef<HTMLDialogElement | null>(null);\n\n useEffect(() => {\n if (closed) {\n dialogRef.current?.close();\n } else {\n dialogRef.current?.showModal();\n }\n }, [closed]);\n\n useEffect(() => {\n const dialogElement = dialogRef.current;\n\n dialogElement?.addEventListener('cancel', onClose);\n return () => {\n dialogElement?.removeEventListener('cancel', onClose);\n };\n }, [onClose]);\n\n return (\n <dialog\n ref={dialogRef}\n className=\"relative w-full h-full backdrop:bg-black/50\"\n data-testid={testId}\n aria-label={label}\n >\n {children}\n </dialog>\n );\n}\n\n/**\n * Temporary fallback used in browsers not supporting `dialog` element.\n * It can be removed once all browsers we support can use it.\n */\nfunction FallbackDialog({ closed, children, ...rest }: DialogProps) {\n return (\n <div\n {...rest}\n className={classnames(\n 'fixed z-max top-0 left-0 right-0 bottom-0 p-3 bg-black/50',\n { hidden: closed },\n )}\n >\n <div className=\"relative w-full h-full\">{children}</div>\n </div>\n );\n}\n\n/** Checks if the browser supports native modal dialogs */\nfunction isModalDialogSupported(document: Document) {\n const dialog = document.createElement('dialog');\n return typeof dialog.showModal === 'function';\n}\n\nexport type ModalDialogProps = DialogProps & {\n document_?: Document;\n};\n\nexport default function ModalDialog({\n /* istanbul ignore next - test seam */\n document_ = document,\n ...rest\n}: ModalDialogProps) {\n const Dialog = useMemo(\n () => (isModalDialogSupported(document_) ? NativeDialog : FallbackDialog),\n [document_],\n );\n\n return <Dialog {...rest} />;\n}\n","import { IconButton, CancelIcon } from '@hypothesis/frontend-shared';\nimport classnames from 'classnames';\nimport { useCallback, useEffect, useRef, useState } from 'preact/hooks';\n\nimport { addConfigFragment } from '../../shared/config-fragment';\nimport { createAppConfig } from '../config/app';\nimport type { EventBus, Emitter } from '../util/emitter';\nimport ModalDialog from './ModalDialog';\n\n/**\n * Configuration used to launch the notebook application.\n *\n * This includes the URL for the iframe and configuration to pass to the\n * application on launch.\n */\nexport type NotebookConfig = {\n notebookAppUrl: string;\n} & Record<string, unknown>;\n\ntype NotebookIframeProps = {\n config: NotebookConfig;\n groupId: string;\n};\n/**\n * Create the iframe that will load the notebook application.\n */\nfunction NotebookIframe({ config, groupId }: NotebookIframeProps) {\n const notebookAppSrc = addConfigFragment(config.notebookAppUrl, {\n ...createAppConfig(config.notebookAppUrl, config),\n\n // Explicitly set the \"focused\" group\n group: groupId,\n });\n\n return (\n <iframe\n title={'Hypothesis annotation notebook'}\n className=\"h-full w-full border-0\"\n allow=\"fullscreen; clipboard-write\"\n src={notebookAppSrc}\n />\n );\n}\n\nexport type NotebookEvents = {\n openNotebook(groupId: string): void;\n closeNotebook(): void;\n};\n\nexport type NotebookModalProps = {\n eventBus: EventBus<NotebookEvents>;\n config: NotebookConfig;\n};\n\n/**\n * Create a modal component that hosts (1) the notebook iframe and (2) a button to close the modal.\n */\nexport default function NotebookModal({\n eventBus,\n config,\n}: NotebookModalProps) {\n // Temporary solution: while there is no mechanism to sync new annotations in\n // the notebook, we force re-rendering of the iframe on every 'openNotebook'\n // event, so that the new annotations are displayed.\n // https://github.com/hypothesis/client/issues/3182\n const [iframeKey, setIframeKey] = useState(0);\n const [isHidden, setIsHidden] = useState(true);\n const [groupId, setGroupId] = useState<string | null>(null);\n const originalDocumentOverflowStyle = useRef('');\n const emitterRef = useRef<Emitter<NotebookEvents> | null>(null);\n\n // Stores the original overflow CSS property of document.body and reset it\n // when the component is destroyed\n useEffect(() => {\n originalDocumentOverflowStyle.current = document.body.style.overflow;\n\n return () => {\n document.body.style.overflow = originalDocumentOverflowStyle.current;\n };\n }, []);\n\n // The overflow CSS property is set to hidden to prevent scrolling of the host page,\n // while the notebook modal is open. It is restored when the modal is closed.\n useEffect(() => {\n if (isHidden) {\n document.body.style.overflow = originalDocumentOverflowStyle.current;\n } else {\n document.body.style.overflow = 'hidden';\n }\n }, [isHidden]);\n\n useEffect(() => {\n const emitter = eventBus.createEmitter();\n emitter.subscribe('openNotebook', (groupId: string) => {\n setIsHidden(false);\n setIframeKey(iframeKey => iframeKey + 1);\n setGroupId(groupId);\n });\n emitterRef.current = emitter;\n\n return () => {\n emitter.destroy();\n };\n }, [eventBus]);\n\n const onClose = useCallback(() => {\n setIsHidden(true);\n emitterRef.current?.publish('closeNotebook');\n }, []);\n\n if (groupId === null) {\n return null;\n }\n\n return (\n <ModalDialog\n closed={isHidden}\n onClose={onClose}\n data-testid=\"notebook-outer\"\n aria-label=\"Hypothesis notebook\"\n >\n <div className=\"absolute right-0 m-3\">\n <IconButton\n title=\"Close notebook\"\n onClick={onClose}\n variant=\"dark\"\n classes={classnames(\n // Remove the dark variant's background color to avoid\n // interfering with modal overlays. Re-activate the dark variant's\n // background color on hover.\n // See https://github.com/hypothesis/client/issues/3676\n '!bg-transparent enabled:hover:!bg-grey-3',\n )}\n data-testid=\"close-button\"\n >\n <CancelIcon className=\"w-4 h-4\" />\n </IconButton>\n </div>\n <NotebookIframe key={iframeKey} config={config} groupId={groupId} />\n </ModalDialog>\n );\n}\n","import type { Destroyable } from '../types/annotator';\nimport NotebookModal from './components/NotebookModal';\nimport type {\n NotebookConfig,\n NotebookEvents,\n} from './components/NotebookModal';\nimport type { EventBus } from './util/emitter';\nimport { PreactContainer } from './util/preact-container';\n\nexport class Notebook implements Destroyable {\n private _container: PreactContainer;\n\n /**\n * @param eventBus - Enables communication between components sharing the\n * same eventBus\n */\n constructor(\n element: HTMLElement,\n eventBus: EventBus<NotebookEvents>,\n config: NotebookConfig,\n ) {\n this._container = new PreactContainer('notebook', () => (\n <NotebookModal eventBus={eventBus} config={config} />\n ));\n element.append(this._container.element);\n this._container.render();\n }\n\n destroy() {\n this._container.destroy();\n }\n}\n","import { CancelIcon, IconButton } from '@hypothesis/frontend-shared';\nimport classnames from 'classnames';\nimport { useEffect, useRef, useState } from 'preact/hooks';\n\nimport type { Emitter, EventBus } from '../util/emitter';\nimport ModalDialog from './ModalDialog';\n\nexport type ProfileConfig = { profileAppUrl: string } & Record<string, unknown>;\n\nexport type ProfileEvents = {\n openProfile(): void;\n closeProfile(): void;\n};\n\ntype ProfileModalProps = {\n eventBus: EventBus<ProfileEvents>;\n config: ProfileConfig;\n};\n\nexport default function ProfileModal({ eventBus, config }: ProfileModalProps) {\n const [isHidden, setIsHidden] = useState(true);\n const emitterRef = useRef<Emitter<ProfileEvents> | null>(null);\n\n useEffect(() => {\n const emitter = eventBus.createEmitter();\n emitter.subscribe('openProfile', () => {\n setIsHidden(false);\n });\n emitterRef.current = emitter;\n\n return () => {\n emitter.destroy();\n };\n }, [eventBus]);\n\n const onClose = () => {\n setIsHidden(true);\n emitterRef.current?.publish('closeProfile');\n };\n\n if (isHidden) {\n return null;\n }\n\n return (\n <ModalDialog\n closed={isHidden}\n onClose={onClose}\n data-testid=\"profile-outer\"\n aria-label=\"Hypothesis profile\"\n >\n <div className=\"absolute right-0 m-3\">\n <IconButton\n title=\"Close profile dialog\"\n onClick={onClose}\n variant=\"dark\"\n classes={classnames(\n // Remove the dark variant's background color to avoid\n // interfering with modal overlays. Re-activate the dark variant's\n // background color on hover.\n // See https://github.com/hypothesis/client/issues/3676\n '!bg-transparent enabled:hover:!bg-grey-3',\n )}\n >\n <CancelIcon className=\"w-4 h-4\" />\n </IconButton>\n </div>\n <iframe\n title={'Hypothesis profile'}\n className=\"h-full w-full border-0\"\n src={config.profileAppUrl}\n />\n </ModalDialog>\n );\n}\n","import type { Destroyable } from '../types/annotator';\nimport type { ProfileConfig, ProfileEvents } from './components/ProfileModal';\nimport ProfileModal from './components/ProfileModal';\nimport type { EventBus } from './util/emitter';\nimport { PreactContainer } from './util/preact-container';\n\nexport class Profile implements Destroyable {\n private _container: PreactContainer;\n\n constructor(\n element: HTMLElement,\n eventBus: EventBus<ProfileEvents>,\n config: ProfileConfig,\n ) {\n this._container = new PreactContainer('profile', () => (\n <ProfileModal eventBus={eventBus} config={config} />\n ));\n element.append(this._container.element);\n this._container.render();\n }\n\n destroy(): void {\n this._container.destroy();\n }\n}\n","import { PointerButton } from '@hypothesis/frontend-shared';\n\nimport type { Bucket } from '../util/buckets';\n\nexport type BucketsProps = {\n /**\n * Bucket containing anchors that are above the bucket bar. If non-empty,\n * an \"Up\" bucket will be rendered.\n */\n above: Bucket;\n\n /**\n * Bucket containing anchors that are below the bucket bar. If non-empty,\n * a \"Down\" bucket will be rendered.\n */\n below: Bucket;\n\n /**\n * A list of buckets visible on-screen. A left-pointing arrow will be\n * rendered for each bucket.\n */\n buckets: Bucket[];\n onFocusAnnotations: ($tags: string[]) => void;\n onScrollToAnnotation: ($tag: string) => void;\n onSelectAnnotations: ($tags: string[], toggle: boolean) => void;\n};\n\n/**\n * A list of buckets, including up and down navigation (when applicable) and\n * on-screen buckets\n *\n * This component and its buttons are sized with absolute units such that they\n * don't scale with changes to the host page's root font size. They will still\n * properly scale with user/browser zooming.\n */\nexport default function Buckets({\n above,\n below,\n buckets,\n onFocusAnnotations,\n onScrollToAnnotation,\n onSelectAnnotations,\n}: BucketsProps) {\n const showUpNavigation = above.anchors.length > 0;\n const showDownNavigation = below.anchors.length > 0;\n const bucketTags = (b: Bucket) => b.anchors.map(a => a.tag);\n\n return (\n <ul className=\"relative\">\n {showUpNavigation && (\n <li\n className=\"absolute right-0 pointer-events-auto mt-[-11px]\"\n style={{ top: above.position }}\n >\n <PointerButton\n data-testid=\"up-navigation-button\"\n direction=\"up\"\n onClick={() => {\n const anchors = [...above.anchors].sort(\n (a, b) => a.bottom - b.bottom,\n );\n const bottomAnchor = anchors[anchors.length - 1];\n onScrollToAnnotation(bottomAnchor.tag);\n }}\n onBlur={() => onFocusAnnotations([])}\n onFocus={() => onFocusAnnotations(bucketTags(above))}\n onMouseEnter={() => onFocusAnnotations(bucketTags(above))}\n onMouseOut={() => onFocusAnnotations([])}\n title={`Go up to next annotations (${above.anchors.length})`}\n >\n {above.anchors.length}\n </PointerButton>\n </li>\n )}\n {buckets.map((bucket, index) => (\n <li\n className=\"absolute right-0 pointer-events-auto mt-[-8px]\"\n key={index}\n style={{ top: bucket.position }}\n >\n <PointerButton\n direction=\"left\"\n onClick={event =>\n onSelectAnnotations(\n bucketTags(bucket),\n event.metaKey || event.ctrlKey,\n )\n }\n onBlur={() => onFocusAnnotations([])}\n onFocus={() => onFocusAnnotations(bucketTags(bucket))}\n onMouseEnter={() => onFocusAnnotations(bucketTags(bucket))}\n onMouseOut={() => onFocusAnnotations([])}\n title={`Select nearby annotations (${bucket.anchors.length})`}\n >\n {bucket.anchors.length}\n </PointerButton>\n </li>\n ))}\n {showDownNavigation && (\n <li\n className=\"absolute right-0 pointer-events-auto\"\n style={{ top: below.position }}\n >\n <PointerButton\n data-testid=\"down-navigation-button\"\n direction=\"down\"\n onClick={() => {\n const anchors = [...below.anchors].sort((a, b) => a.top - b.top);\n const topAnchor = anchors[0];\n onScrollToAnnotation(topAnchor.tag);\n }}\n onBlur={() => onFocusAnnotations([])}\n onFocus={() => onFocusAnnotations(bucketTags(below))}\n onMouseEnter={() => onFocusAnnotations(bucketTags(below))}\n onMouseOut={() => onFocusAnnotations([])}\n title={`Go up to next annotations (${below.anchors.length})`}\n >\n {below.anchors.length}\n </PointerButton>\n </li>\n )}\n </ul>\n );\n}\n","import type { AnchorPosition, Destroyable } from '../types/annotator';\nimport Buckets from './components/Buckets';\nimport { computeBuckets } from './util/buckets';\nimport { PreactContainer } from './util/preact-container';\n\nexport type BucketBarOptions = {\n onFocusAnnotations: (tags: string[]) => void;\n onScrollToAnnotation: (tag: string) => void;\n onSelectAnnotations: (tags: string[], toggle: boolean) => void;\n};\n\n/**\n * Controller for the \"bucket bar\" showing where annotations are in the document.\n *\n * This is usually positioned along the edge of the sidebar but can be\n * rendered elsewhere for certain content viewers.\n */\nexport class BucketBar implements Destroyable {\n private _container: PreactContainer;\n private _positions: AnchorPosition[];\n private _onFocusAnnotations: BucketBarOptions['onFocusAnnotations'];\n private _onScrollToAnnotation: BucketBarOptions['onScrollToAnnotation'];\n private _onSelectAnnotations: BucketBarOptions['onSelectAnnotations'];\n\n constructor(\n container: HTMLElement,\n {\n onFocusAnnotations,\n onScrollToAnnotation,\n onSelectAnnotations,\n }: BucketBarOptions,\n ) {\n this._positions = [];\n this._container = new PreactContainer('bucket-bar', () => this._render());\n Object.assign(this._container.element.style, {\n display: 'block',\n flexGrow: '1',\n\n // The bucket bar uses absolute positioning for the buckets and does not\n // currently have an intrinsic width. This should be revisited so that\n // host pages using a custom bucket bar container don't need to hardcode\n // assumptions about its width.\n width: '100%',\n });\n\n container.appendChild(this._container.element);\n this._onFocusAnnotations = onFocusAnnotations;\n this._onScrollToAnnotation = onScrollToAnnotation;\n this._onSelectAnnotations = onSelectAnnotations;\n\n this._container.render();\n }\n\n destroy() {\n this._container.destroy();\n }\n\n /** Update the set of anchors from which buckets are generated. */\n update(positions: AnchorPosition[]) {\n this._positions = positions;\n this._container.render();\n }\n\n private _render() {\n const buckets = computeBuckets(this._positions, this._container.element);\n return (\n <Buckets\n above={buckets.above}\n below={buckets.below}\n buckets={buckets.buckets}\n onFocusAnnotations={tags => this._onFocusAnnotations(tags)}\n onScrollToAnnotation={tag => this._onScrollToAnnotation(tag)}\n onSelectAnnotations={(tags, toogle) =>\n this._onSelectAnnotations(tags, toogle)\n }\n />\n );\n }\n}\n","import { ToastMessages as BaseToastMessages } from '@hypothesis/frontend-shared';\nimport type { ToastMessage } from '@hypothesis/frontend-shared';\nimport { useCallback, useEffect, useState } from 'preact/hooks';\n\nimport type { Emitter } from '../util/emitter';\n\nexport type ToastMessagesEvents = {\n toastMessageAdded(msg: ToastMessage): void;\n toastMessageDismissed(id: string): void;\n};\n\nexport type ToastMessagesProps = {\n emitter: Emitter<ToastMessagesEvents>;\n};\n\n/**\n * A component that renders toast messages published from the sidebar, in a way\n * that they \"appear\" in the viewport even when the sidebar is collapsed.\n * This is useful to make sure screen readers announce hidden messages.\n */\nexport default function ToastMessages({ emitter }: ToastMessagesProps) {\n const [messages, setMessages] = useState<ToastMessage[]>([]);\n const addMessage = useCallback(\n (newMessage: ToastMessage) => setMessages(prev => [...prev, newMessage]),\n [],\n );\n const dismissMessage = useCallback(\n (messageId: string) =>\n setMessages(prev => prev.filter(message => message.id !== messageId)),\n [],\n );\n\n useEffect(() => {\n emitter.subscribe('toastMessageAdded', addMessage);\n emitter.subscribe('toastMessageDismissed', dismissMessage);\n\n return () => {\n emitter.unsubscribe('toastMessageAdded', addMessage);\n emitter.unsubscribe('toastMessageDismissed', dismissMessage);\n };\n }, [emitter, dismissMessage, addMessage]);\n\n return (\n <BaseToastMessages messages={messages} onMessageDismiss={dismissMessage} />\n );\n}\n","import {\n Button,\n AnnotateIcon,\n CancelIcon,\n CaretRightIcon,\n CaretLeftIcon,\n HideIcon,\n SelectionIcon,\n NoteIcon,\n PinIcon,\n ShowIcon,\n} from '@hypothesis/frontend-shared';\nimport type { ButtonProps } from '@hypothesis/frontend-shared/lib/components/input/Button';\nimport type {\n IconComponent,\n PresentationalProps,\n} from '@hypothesis/frontend-shared/lib/types';\nimport classnames from 'classnames';\nimport type { JSX, RefObject } from 'preact';\n\nimport type { AnnotationTool } from '../../types/annotator';\n\n// TODO: ToolbarButton should be extracted as a shared design pattern or\n// component\ntype ToolbarButtonProps = PresentationalProps &\n ButtonProps &\n Omit<JSX.HTMLAttributes<HTMLButtonElement>, 'icon' | 'size'> & {\n icon: IconComponent;\n };\n\n/**\n * Style an IconButton for use on the Toolbar\n */\nfunction ToolbarButton({ icon: Icon, ...buttonProps }: ToolbarButtonProps) {\n return (\n <Button\n classes={classnames(\n 'justify-center rounded',\n 'w-[30px] h-[30px]',\n 'shadow border bg-white text-grey-6 hover:text-grey-9',\n )}\n {...buttonProps}\n size=\"custom\"\n variant=\"custom\"\n >\n <Icon />\n </Button>\n );\n}\n\n/**\n * Hidden component that announces certain Hypothesis states.\n *\n * This is useful to inform assistive technology users when these states\n * have been changed (eg. whether highlights are visible), given that they can\n * be changed in multiple ways (keyboard shortcuts, toolbar button) etc.\n */\nfunction StatusNotifier({ highlightsVisible }: { highlightsVisible: boolean }) {\n return (\n <div className=\"sr-only\" role=\"status\" data-testid=\"toolbar-status\">\n {highlightsVisible ? 'Highlights visible' : 'Highlights hidden'}\n </div>\n );\n}\n\nexport type ToolbarProps = {\n /**\n * Callback for the \"Close sidebar\" button. This button is only shown when\n * `useMinimalControls` is true and the sidebar is open.\n */\n closeSidebar: () => void;\n\n /**\n * Callback for a button that creates an annotation.\n */\n createAnnotation: (tool: AnnotationTool) => void;\n\n /** Is the sidebar currently open? */\n isSidebarOpen: boolean;\n\n /**\n * The id attribute for the sidebar container to reference from the sidebar\n * toggle's aria-controls attribute\n */\n sidebarContainerId?: string;\n\n /**\n * Informs which icon to show on the \"Create annotation\" button and what type\n * of annotation should be created by the `createAnnotation` callback. The\n * type of annotation depends on whether there is a text selection in the\n * document.\n */\n newAnnotationType: 'annotation' | 'note';\n\n /** Are highlights currently visible in the document? */\n showHighlights: boolean;\n\n /** Callback for the show/hide highlights button */\n toggleHighlights: () => void;\n\n /**\n * Callback for toggling the visibility of the sidebar when the show/hide\n * sidebar button is clicked\n */\n toggleSidebar: () => void;\n\n /**\n * Ref to apply to the show/hide-sidebar button. This is exposed to enable\n * drag-to-resize functionality.\n */\n toggleSidebarRef?: RefObject<HTMLElement>;\n\n /**\n * When true, all controls are hidden except for the \"Close sidebar\" button\n * when the sidebar is open. This is enabled for the \"clean\" theme.\n */\n useMinimalControls?: boolean;\n\n /**\n * Specifies which tools are supported for creating new annotations.\n */\n supportedTools: AnnotationTool[];\n};\n\n/**\n * Controls on the edge of the sidebar for opening/closing the sidebar,\n * controlling highlight visibility and creating new page notes.\n *\n * This component and its buttons are sized with absolute units such that they\n * don't scale with changes to the host page's root font size. They will still\n * properly scale with user/browser zooming.\n */\nexport default function Toolbar({\n closeSidebar,\n createAnnotation,\n isSidebarOpen,\n sidebarContainerId,\n newAnnotationType,\n showHighlights,\n supportedTools = ['selection'],\n toggleHighlights,\n toggleSidebar,\n toggleSidebarRef,\n useMinimalControls = false,\n}: ToolbarProps) {\n return (\n <div\n className={classnames(\n {\n // For minimal controls, display the toolbar to the left, fully\n // outside the sidebar\n 'absolute right-full': useMinimalControls,\n // When the full toolbar is displayed, we position it relative to its\n // container, so that it takes vertical space and pushes next elements\n // down (eg. the buckets list).\n // The toolbar is wider than its parent, so we need to adjust the\n // right position so that the right edge of the toolbar aligns with\n // the right edge of the parent.\n 'relative right-[11px]': !useMinimalControls,\n },\n 'w-[33px] z-2',\n 'text-px-base leading-none', // non-scaling sizing\n )}\n >\n {/* In the clean theme (`useMinimalControls` is `true`),\n the only button that should appear is a button\n to close the sidebar, and only if the sidebar is open. This button is\n absolutely positioned some way down the edge of the sidebar.\n */}\n {useMinimalControls && isSidebarOpen && (\n <Button\n classes={classnames(\n 'transition-colors focus-visible-ring ring-inset',\n 'w-[27px] h-[27px] mt-[140px] ml-px-1.5',\n 'flex items-center justify-center bg-white border',\n 'text-grey-6 hover:text-grey-9 transition-colors',\n // Turn off right border to blend with sidebar\n 'border-r-0',\n // A more intense shadow than other ToolbarButtons, to match that\n // of the edge of the sidebar in clean theme\n 'shadow-sidebar',\n )}\n title=\"Close annotation sidebar\"\n onClick={closeSidebar}\n unstyled\n >\n <CancelIcon />\n </Button>\n )}\n {!useMinimalControls && (\n <>\n <Button\n classes={classnames(\n 'transition-colors focus-visible-ring ring-inset',\n // Height and width to align with the sidebar's top bar\n 'h-[40px] w-[33px] pl-[6px] rounded-bl',\n 'bg-white text-grey-5 hover:text-grey-9',\n // Turn on left and bottom borders to continue the\n // border of the sidebar's top bar\n 'border-l border-b',\n )}\n elementRef={toggleSidebarRef}\n title=\"Annotation sidebar\"\n expanded={isSidebarOpen}\n aria-controls={sidebarContainerId}\n onClick={toggleSidebar}\n unstyled\n >\n {isSidebarOpen ? <CaretRightIcon /> : <CaretLeftIcon />}\n </Button>\n <div className=\"space-y-px-1.5 mt-px-2\">\n <ToolbarButton\n title=\"Show highlights\"\n icon={showHighlights ? ShowIcon : HideIcon}\n pressed={showHighlights}\n onClick={toggleHighlights}\n />\n {supportedTools.includes('selection') && (\n <ToolbarButton\n data-testid=\"text-annotation\"\n title={\n newAnnotationType === 'note'\n ? 'New page note'\n : 'New annotation'\n }\n icon={newAnnotationType === 'note' ? NoteIcon : AnnotateIcon}\n onClick={() => createAnnotation('selection')}\n />\n )}\n {supportedTools.includes('rect') && (\n <ToolbarButton\n data-testid=\"rect-annotation\"\n title=\"Rectangle annotation\"\n icon={SelectionIcon}\n onClick={() => createAnnotation('rect')}\n />\n )}\n {supportedTools.includes('point') && (\n <ToolbarButton\n data-testid=\"point-annotation\"\n title=\"Pin annotation\"\n icon={PinIcon}\n onClick={() => createAnnotation('point')}\n />\n )}\n </div>\n <StatusNotifier highlightsVisible={showHighlights} />\n </>\n )}\n </div>\n );\n}\n","import { createRef, render } from 'preact';\nimport type { RefObject } from 'preact';\n\nimport type { AnnotationTool } from '../types/annotator';\nimport Toolbar from './components/Toolbar';\n\nexport type ToolbarOptions = {\n createAnnotation: (tool: AnnotationTool) => void;\n setSidebarOpen: (open: boolean) => void;\n setHighlightsVisible: (visible: boolean) => void;\n sidebarContainerId?: string;\n};\n\n/**\n * Controller for the toolbar on the edge of the sidebar.\n *\n * This toolbar provides controls for opening and closing the sidebar, toggling\n * highlight visibility etc.\n */\nexport class ToolbarController {\n private _container: HTMLElement;\n private _newAnnotationType: 'annotation' | 'note';\n private _useMinimalControls: boolean;\n private _highlightsVisible: boolean;\n private _sidebarOpen: boolean;\n private _sidebarContainerId?: string;\n private _closeSidebar: () => void;\n private _toggleSidebar: () => void;\n private _toggleHighlights: () => void;\n private _createAnnotation: (tool: AnnotationTool) => void;\n private _sidebarToggleButton: RefObject<HTMLButtonElement>;\n private _supportedAnnotationTools: AnnotationTool[];\n\n /**\n * @param container - Element into which the toolbar is rendered\n */\n constructor(container: HTMLElement, options: ToolbarOptions) {\n const { createAnnotation, setSidebarOpen, setHighlightsVisible } = options;\n\n this._container = container;\n this._useMinimalControls = false;\n this._newAnnotationType = 'note';\n this._highlightsVisible = false;\n this._sidebarOpen = false;\n this._sidebarContainerId = options.sidebarContainerId;\n this._supportedAnnotationTools = ['selection'];\n\n this._closeSidebar = () => setSidebarOpen(false);\n this._toggleSidebar = () => setSidebarOpen(!this._sidebarOpen);\n this._toggleHighlights = () =>\n setHighlightsVisible(!this._highlightsVisible);\n this._createAnnotation = (tool: AnnotationTool) => {\n createAnnotation(tool);\n\n // For the text selection tool, the selection already exists so we can\n // create the new annotation immediately and open the sidebar for the\n // user to type. For other tools the user will first need to make a\n // selection (eg. by drawing a shape), then we can open the sidebar for\n // them to add text.\n if (tool === 'selection') {\n setSidebarOpen(true);\n }\n };\n\n /** Reference to the sidebar toggle button. */\n this._sidebarToggleButton = createRef();\n\n this.render();\n }\n\n getWidth() {\n const content = this._container.firstChild as HTMLElement;\n return content.getBoundingClientRect().width;\n }\n\n /**\n * Set whether the toolbar is in the \"minimal controls\" mode where\n * only the \"Close\" button is shown.\n */\n set useMinimalControls(minimal) {\n this._useMinimalControls = minimal;\n this.render();\n }\n\n get useMinimalControls() {\n return this._useMinimalControls;\n }\n\n /**\n * Update the toolbar to reflect whether the sidebar is open or not.\n */\n set sidebarOpen(open) {\n this._sidebarOpen = open;\n this.render();\n }\n\n get sidebarOpen() {\n return this._sidebarOpen;\n }\n\n /**\n * Update the toolbar to reflect whether the \"Create annotation\" button will\n * create a page note (if there is no selection) or an annotation (if there is\n * a selection).\n */\n set newAnnotationType(type) {\n this._newAnnotationType = type;\n this.render();\n }\n\n get newAnnotationType() {\n return this._newAnnotationType;\n }\n\n /**\n * Update the toolbar to reflect whether highlights are currently visible.\n */\n set highlightsVisible(visible) {\n this._highlightsVisible = visible;\n this.render();\n }\n\n get highlightsVisible() {\n return this._highlightsVisible;\n }\n\n /**\n * Return the DOM element that toggles the sidebar's visibility.\n *\n * This will be `null` if {@link useMinimalControls} is true.\n */\n get sidebarToggleButton(): HTMLButtonElement | null {\n return this._sidebarToggleButton.current;\n }\n\n /** Set which tools are supported for creating new annotations. */\n set supportedAnnotationTools(tools) {\n this._supportedAnnotationTools = tools;\n this.render();\n }\n\n get supportedAnnotationTools() {\n return this._supportedAnnotationTools;\n }\n\n render() {\n render(\n <Toolbar\n closeSidebar={this._closeSidebar}\n createAnnotation={this._createAnnotation}\n newAnnotationType={this._newAnnotationType}\n isSidebarOpen={this._sidebarOpen}\n sidebarContainerId={this._sidebarContainerId}\n showHighlights={this._highlightsVisible}\n supportedTools={this._supportedAnnotationTools}\n toggleHighlights={this._toggleHighlights}\n toggleSidebar={this._toggleSidebar}\n toggleSidebarRef={this._sidebarToggleButton}\n useMinimalControls={this.useMinimalControls}\n />,\n this._container,\n );\n }\n}\n","import { ListenerCollection } from '@hypothesis/frontend-shared';\n\nimport type { Destroyable } from '../../types/annotator';\n\n/**\n * Events emitted by {@link DragHandler}.\n *\n * This is named `DragHandlerEvent` to avoid confusion with {@link DragEvent}.\n */\nexport type DragHandlerEvent = {\n type: 'dragstart' | 'dragend' | 'dragmove';\n\n /** Distance that the pointer has moved by since the start of the drag. */\n deltaX: number;\n};\n\nexport type DragOptions = {\n /** Element where the pointer must be pressed to start the drag. */\n target: HTMLElement;\n\n /** Callback to invoke when drag events occur. */\n onDrag: (event: DragHandlerEvent) => void;\n\n /**\n * Threshold that pointer must move from where it is initially pressed before\n * a drag starts.\n */\n threshold?: number;\n};\n\n/**\n * Utility which recognizes drag/pan gestures on a control and reports events\n * when a drag is in progress.\n */\nexport class DragHandler implements Destroyable {\n private _listeners: ListenerCollection;\n\n /** Pointer position in the viewport at the start of the drag operation. */\n private _startX: number | null;\n\n private _dragActive: boolean;\n\n private _threshold: number;\n\n /**\n * Construct a drag handler which triggers drag events when the user presses\n * `target` and moves the pointer.\n */\n constructor({ target, threshold = 10, onDrag }: DragOptions) {\n // Disable the browser's own pan/scroll gestures on the target. Otherwise\n // the drag action will not work on mobile.\n target.style.touchAction = 'none';\n\n this._listeners = new ListenerCollection();\n\n this._startX = null;\n this._dragActive = false;\n this._threshold = threshold;\n\n this._listeners.add(target, 'pointerdown', event => {\n this._startX = event.clientX;\n });\n\n const onCancel = (event: PointerEvent) => {\n if (this._startX !== null && this._dragActive) {\n const deltaX = event.clientX - this._startX;\n onDrag({ type: 'dragend', deltaX });\n }\n this._startX = null;\n this._dragActive = false;\n };\n this._listeners.add(window, 'pointercancel', onCancel);\n this._listeners.add(window, 'pointerup', onCancel);\n\n this._listeners.add(window, 'pointermove', event => {\n if (this._startX === null) {\n return;\n }\n\n const deltaX = event.clientX - this._startX;\n if (!this._dragActive && Math.abs(deltaX) >= this._threshold) {\n this._dragActive = true;\n onDrag({ type: 'dragstart', deltaX });\n }\n\n if (this._dragActive) {\n onDrag({ type: 'dragmove', deltaX });\n }\n });\n }\n\n destroy() {\n this._listeners.removeAll();\n }\n}\n","import type { ToastMessage } from '@hypothesis/frontend-shared';\nimport { ListenerCollection } from '@hypothesis/frontend-shared';\nimport classnames from 'classnames';\nimport { render } from 'preact';\n\nimport { addConfigFragment } from '../shared/config-fragment';\nimport { sendErrorsTo } from '../shared/frame-error-capture';\nimport { PortRPC } from '../shared/messaging';\nimport type {\n AnchorPosition,\n AnnotationTool,\n SidebarLayout,\n Destroyable,\n Events,\n} from '../types/annotator';\nimport type { Service } from '../types/config';\nimport type {\n GuestToHostCalls,\n HostToGuestCalls,\n HostToSidebarCalls,\n SidebarToHostCalls,\n} from '../types/port-rpc-calls';\nimport { annotationCounts } from './annotation-counts';\nimport { BucketBar } from './bucket-bar';\nimport ToastMessages from './components/ToastMessages';\nimport { createAppConfig } from './config/app';\nimport { FeatureFlags } from './features';\nimport { sidebarTrigger } from './sidebar-trigger';\nimport { ToolbarController } from './toolbar';\nimport { DragHandler } from './util/drag-handler';\nimport type { DragHandlerEvent } from './util/drag-handler';\nimport type { Emitter, EventBus } from './util/emitter';\nimport { createShadowRoot } from './util/shadow-root';\n\n// Minimum width to which the iframeContainer can be resized.\nexport const MIN_RESIZE = 280;\n\n/**\n * Client configuration used to launch the sidebar application.\n *\n * This includes the URL for the iframe and configuration to pass to the\n * application on launch.\n */\nexport type SidebarConfig = { sidebarAppUrl: string } & Record<string, unknown>;\n\n/**\n * Client configuration used by the sidebar container ({@link Sidebar}).\n */\nexport type SidebarContainerConfig = {\n /** CSS selector for the container of the bucket bar. */\n bucketContainerSelector?: string;\n\n /**\n * Details of the annotation service the client should connect to.\n * This includes callbacks provided by the host page to handle certain actions\n * in the sidebar (eg. the Login button).\n */\n services?: Service[];\n\n /**\n * CSS selector of a container element in the host page which the sidebar\n * should be added into, instead of creating a new container.\n */\n externalContainerSelector?: string;\n\n /**\n * Callback that allows the host page to react to the sidebar being opened,\n * closed or resized\n */\n onLayoutChange?: (layout: SidebarLayout) => void;\n};\n\n/**\n * Create the iframe that will load the sidebar application.\n */\nfunction createSidebarIframe(config: SidebarConfig): HTMLIFrameElement {\n const sidebarURL = config.sidebarAppUrl;\n const sidebarAppSrc = addConfigFragment(\n sidebarURL,\n createAppConfig(sidebarURL, config),\n );\n\n const sidebarFrame = document.createElement('iframe');\n\n sidebarFrame.src = sidebarAppSrc;\n sidebarFrame.title = 'Hypothesis annotation viewer';\n sidebarFrame.className = 'sidebar-frame';\n\n // Enable media in annotations to be shown fullscreen, and allow copying to\n // the clipboard.\n sidebarFrame.allow = 'fullscreen; clipboard-write';\n\n return sidebarFrame;\n}\n\ntype DragResizeState = {\n /** Initial position at the start of a drag/pan resize event (in pixels). */\n initial: number | null;\n /** Final position at end of drag resize event. */\n final: number | null;\n};\n\n/**\n * The `Sidebar` class creates (1) the sidebar application iframe, (2) its container,\n * as well as (3) the adjacent controls.\n */\nexport class Sidebar implements Destroyable {\n private _emitter: Emitter<Events>;\n private _config: SidebarContainerConfig & SidebarConfig;\n private _dragResizeHandler: DragHandler | undefined;\n private _dragResizeState: DragResizeState;\n private _listeners: ListenerCollection;\n private _layoutState: SidebarLayout;\n private _hypothesisSidebar: HTMLElement | undefined;\n private _messagesElement: HTMLElement | undefined;\n private _toolbarWidth: number;\n private _renderFrame: number | undefined;\n\n /**\n * Tracks which `Guest` has a text selection. `null` indicates to default to\n * the first connected guest frame.\n */\n private _guestWithSelection: PortRPC<\n GuestToHostCalls,\n HostToGuestCalls\n > | null;\n\n /** Channel for host-sidebar communication. */\n private _sidebarRPC: PortRPC<SidebarToHostCalls, HostToSidebarCalls>;\n\n /** Channels for host-guest communication. */\n private _guestRPC: PortRPC<GuestToHostCalls, HostToGuestCalls>[];\n\n bucketBar: BucketBar | null;\n features: FeatureFlags;\n externalFrame: Element | undefined;\n iframeContainer: HTMLDivElement | undefined;\n toolbar: ToolbarController;\n onLoginRequest: Service['onLoginRequest'];\n onLogoutRequest: Service['onLogoutRequest'];\n onSignupRequest: Service['onSignupRequest'];\n onProfileRequest: Service['onProfileRequest'];\n onHelpRequest: Service['onHelpRequest'];\n onLayoutChange: SidebarContainerConfig['onLayoutChange'];\n\n /** The `<iframe>` element containing the sidebar application. */\n iframe: HTMLIFrameElement;\n\n /**\n * @param eventBus - Enables communication between components sharing the same\n * eventBus\n */\n constructor(\n element: HTMLElement,\n eventBus: EventBus<Events>,\n config: SidebarContainerConfig & SidebarConfig,\n ) {\n this._emitter = eventBus.createEmitter();\n this._guestWithSelection = null;\n this._guestRPC = [];\n this._sidebarRPC = new PortRPC();\n this.iframe = createSidebarIframe(config);\n this._config = config;\n this.bucketBar = null;\n this.features = new FeatureFlags();\n\n const iframeContainerId = 'sidebar-container';\n\n // Set up the toolbar on the left edge of the sidebar.\n const toolbarContainer = document.createElement('div');\n toolbarContainer.setAttribute('data-testid', 'toolbar-container');\n this.toolbar = new ToolbarController(toolbarContainer, {\n sidebarContainerId: iframeContainerId,\n createAnnotation: (tool: AnnotationTool) => {\n if (this._guestRPC.length === 0) {\n return;\n }\n\n const rpc = this._guestWithSelection ?? this._guestRPC[0];\n rpc.call('createAnnotation', { tool });\n },\n setSidebarOpen: open => (open ? this.open() : this.close()),\n setHighlightsVisible: show => this.setHighlightsVisible(show),\n });\n\n if (config.externalContainerSelector) {\n this.externalFrame =\n document.querySelector(config.externalContainerSelector) ?? element;\n this.externalFrame.appendChild(this.iframe);\n } else {\n this.iframeContainer = document.createElement('div');\n this.iframeContainer.style.display = 'none';\n this.iframeContainer.className = 'sidebar-container';\n this.iframeContainer.id = iframeContainerId;\n\n if (config.theme === 'clean') {\n this.iframeContainer.classList.add('theme-clean');\n // Append toolbar directly to the iframe container when clean theme is\n // enabled\n this.iframeContainer.append(toolbarContainer);\n this.toolbar.useMinimalControls = true;\n } else {\n let bucketBarContainer: HTMLElement | undefined;\n if (config.bucketContainerSelector) {\n bucketBarContainer = document.querySelector(\n config.bucketContainerSelector,\n ) as HTMLElement | undefined;\n if (!bucketBarContainer) {\n console.warn(\n `Custom bucket container \"${config.bucketContainerSelector}\" not found`,\n );\n }\n }\n\n // Create the background for the bucket bar and toolbar. This also\n // serves as the default container for the bucket bar.\n const sidebarEdge = document.createElement('div');\n sidebarEdge.setAttribute('data-testid', 'sidebar-edge');\n sidebarEdge.className = classnames(\n // Position the background along the left edge of the sidebar.\n //\n // `width` is 1px more than `left` to avoid a gap on iOS.\n // See https://github.com/hypothesis/client/pull/2750.\n 'absolute top-0 bottom-0 w-[23px] left-[-22px]',\n\n // Make the bucket bar fill the container, with small padding on the\n // right to align the right edge of the buckets with the right edge\n // of toolbar icons.\n 'flex flex-col pr-[5px]',\n\n // Use a grey background, with lower opacity with the sidebar is\n // collapsed, so the page content behind it can be read.\n 'bg-grey-2 sidebar-collapsed:bg-black/[.08]',\n\n // Allow pointer events to go through this container to page elements\n // (eg. scroll bar thumbs) which are behind it.\n 'pointer-events-none',\n );\n\n // Allow pointer events in the toolbar\n toolbarContainer.className = 'pointer-events-auto';\n sidebarEdge.append(toolbarContainer);\n\n this.iframeContainer.append(sidebarEdge);\n\n if (!bucketBarContainer) {\n bucketBarContainer = sidebarEdge;\n }\n\n this.bucketBar = new BucketBar(bucketBarContainer, {\n onFocusAnnotations: tags =>\n this._guestRPC.forEach(rpc => rpc.call('hoverAnnotations', tags)),\n onScrollToAnnotation: tag =>\n this._guestRPC.forEach(rpc => rpc.call('scrollToAnnotation', tag)),\n onSelectAnnotations: (tags, toggle) =>\n this._guestRPC.forEach(rpc =>\n rpc.call('selectAnnotations', tags, toggle),\n ),\n });\n }\n\n this.iframeContainer.appendChild(this.iframe);\n\n // Wrap up the 'iframeContainer' element into a shadow DOM, so it is not\n // affected by host CSS styles\n this._hypothesisSidebar = document.createElement('hypothesis-sidebar');\n const shadowRoot = createShadowRoot(this._hypothesisSidebar);\n shadowRoot.appendChild(this.iframeContainer);\n\n element.appendChild(this._hypothesisSidebar);\n\n // Render a container for toast messages in the host frame. The sidebar\n // will forward messages to render here while it is collapsed.\n this._messagesElement = document.createElement('div');\n shadowRoot.appendChild(this._messagesElement);\n render(<ToastMessages emitter={this._emitter} />, this._messagesElement);\n }\n\n // Register the sidebar as a handler for Hypothesis errors in this frame.\n if (this.iframe.contentWindow) {\n sendErrorsTo(this.iframe.contentWindow);\n }\n\n this._listeners = new ListenerCollection();\n\n if (this.iframeContainer) {\n // If using our own container frame for the sidebar, infer the width from\n // it.\n this._toolbarWidth = this.toolbar.getWidth();\n } else {\n // If using a host-page provided container for the sidebar, the toolbar is\n // not shown.\n this._toolbarWidth = 0;\n }\n\n this._listeners.add(window, 'resize', () => this._onResize());\n\n this._dragResizeState = {\n initial: null,\n final: null,\n };\n\n const toggleButton = this.toolbar.sidebarToggleButton;\n if (toggleButton) {\n this._dragResizeHandler = new DragHandler({\n target: toggleButton,\n onDrag: event => this._onDragSidebarToggleButton(event),\n });\n }\n\n this.close();\n\n // Publisher-provided callback functions\n const [serviceConfig] = config.services || [];\n if (serviceConfig) {\n this.onLoginRequest = serviceConfig.onLoginRequest;\n this.onLogoutRequest = serviceConfig.onLogoutRequest;\n this.onSignupRequest = serviceConfig.onSignupRequest;\n this.onProfileRequest = serviceConfig.onProfileRequest;\n this.onHelpRequest = serviceConfig.onHelpRequest;\n }\n\n this.onLayoutChange = config.onLayoutChange;\n\n this._layoutState = {\n expanded: false,\n width: 0,\n height: 0,\n toolbarWidth: 0,\n };\n\n // Initial layout notification\n this._updateLayoutState(false);\n this._setupSidebarEvents();\n }\n\n destroy() {\n this._guestRPC.forEach(rpc => rpc.destroy());\n this._sidebarRPC.destroy();\n this.bucketBar?.destroy();\n this._listeners.removeAll();\n this._dragResizeHandler?.destroy();\n if (this._hypothesisSidebar) {\n // Explicitly unmounting the \"messages\" element, to make sure effects are clean-up\n render(null, this._messagesElement!);\n this._hypothesisSidebar.remove();\n } else {\n this.iframe.remove();\n }\n this._emitter.destroy();\n\n // Unregister the sidebar iframe as a handler for errors in this frame.\n sendErrorsTo(null);\n }\n\n /**\n * Setup communication with a frame that has connected to the host.\n */\n onFrameConnected(source: 'guest' | 'sidebar', port: MessagePort) {\n switch (source) {\n case 'guest':\n this._connectGuest(port);\n break;\n case 'sidebar':\n this._sidebarRPC.connect(port);\n break;\n }\n }\n\n _connectGuest(port: MessagePort) {\n const guestRPC = new PortRPC<GuestToHostCalls, HostToGuestCalls>();\n\n guestRPC.on('textSelected', () => {\n this._guestWithSelection = guestRPC;\n this.toolbar.newAnnotationType = 'annotation';\n this._guestRPC\n .filter(port => port !== guestRPC)\n .forEach(rpc => rpc.call('clearSelection'));\n });\n\n guestRPC.on('textUnselected', () => {\n this._guestWithSelection = null;\n this.toolbar.newAnnotationType = 'note';\n this._guestRPC\n .filter(port => port !== guestRPC)\n .forEach(rpc => rpc.call('clearSelection'));\n });\n\n guestRPC.on('highlightsVisibleChanged', (visible: boolean) => {\n this.setHighlightsVisible(visible);\n });\n\n // The listener will do nothing if the sidebar doesn't have a bucket bar\n // (clean theme)\n const bucketBar = this.bucketBar;\n // Currently, we ignore `anchorsChanged` for all the guests except the first connected guest.\n if (bucketBar) {\n guestRPC.on('anchorsChanged', (positions: AnchorPosition[]) => {\n if (this._guestRPC.indexOf(guestRPC) === 0) {\n bucketBar.update(positions);\n }\n });\n }\n\n guestRPC.on('close', () => {\n guestRPC.destroy();\n if (guestRPC === this._guestWithSelection) {\n this._guestWithSelection = null;\n }\n this._guestRPC = this._guestRPC.filter(rpc => rpc !== guestRPC);\n });\n\n guestRPC.on('supportedToolsChanged', (tools: AnnotationTool[]) => {\n this.toolbar.supportedAnnotationTools = tools;\n });\n\n guestRPC.connect(port);\n this._guestRPC.push(guestRPC);\n\n guestRPC.call('sidebarLayoutChanged', this._layoutState);\n }\n\n _setupSidebarEvents() {\n annotationCounts(document.body, this._sidebarRPC);\n sidebarTrigger(document.body, () => this.open());\n\n this._sidebarRPC.on(\n 'featureFlagsUpdated',\n (flags: Record<string, boolean>) => this.features.update(flags),\n );\n\n this._sidebarRPC.on('connect', () => {\n // Show the UI\n if (this.iframeContainer) {\n this.iframeContainer.style.display = '';\n }\n\n const showHighlights = this._config.showHighlights === 'always';\n this.setHighlightsVisible(showHighlights);\n\n if (\n this._config.openSidebar ||\n this._config.annotations ||\n this._config.query ||\n this._config.group\n ) {\n this.open();\n }\n });\n\n this._sidebarRPC.on('showHighlights', () =>\n this.setHighlightsVisible(true),\n );\n\n this._sidebarRPC.on('openSidebar', () => this.open());\n\n this._sidebarRPC.on('closeSidebar', () => this.close());\n\n // Sidebar listens to the `openNotebook` and `openProfile` events coming\n // from the sidebar's iframe and re-publishes them via the emitter to the\n // Notebook/Profile\n this._sidebarRPC.on('openNotebook', (groupId: string) => {\n this.hide();\n this._emitter.publish('openNotebook', groupId);\n });\n this._sidebarRPC.on('openProfile', () => {\n this.hide();\n this._emitter.publish('openProfile');\n });\n this._emitter.subscribe('closeProfile', () => {\n this.show();\n });\n\n this._emitter.subscribe('closeNotebook', () => {\n this.show();\n });\n\n // Sidebar listens to the `toastMessageAdded` and `toastMessageDismissed`\n // events coming from the sidebar's iframe and re-publishes them via the\n // emitter\n this._sidebarRPC.on('toastMessageAdded', (newMessage: ToastMessage) => {\n this._emitter.publish('toastMessageAdded', newMessage);\n });\n this._sidebarRPC.on('toastMessageDismissed', (messageId: string) => {\n this._emitter.publish('toastMessageDismissed', messageId);\n });\n\n // Suppressing ban-types here because the functions are originally defined\n // as `Function` somewhere else. To be fixed when that is migrated to TS\n const eventHandlers: Array<\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n [keyof SidebarToHostCalls, Function | undefined]\n > = [\n ['loginRequested', this.onLoginRequest],\n ['logoutRequested', this.onLogoutRequest],\n ['signupRequested', this.onSignupRequest],\n ['profileRequested', this.onProfileRequest],\n ['helpRequested', this.onHelpRequest],\n ];\n eventHandlers.forEach(([event, handler]) => {\n if (handler) {\n this._sidebarRPC.on(event, () => handler());\n }\n });\n }\n\n _resetDragResizeState() {\n this._dragResizeState = { initial: null, final: null };\n }\n\n // Schedule any changes needed to update the sidebar layout.\n _updateLayout() {\n // Only schedule one frame at a time.\n if (this._renderFrame) {\n return;\n }\n\n // Schedule a frame.\n this._renderFrame = requestAnimationFrame(() => {\n this._renderFrame = undefined;\n\n if (\n typeof this._dragResizeState.final === 'number' &&\n this._dragResizeState.final !== this._dragResizeState.initial &&\n this.iframeContainer\n ) {\n const margin = this._dragResizeState.final;\n const width = -margin;\n this.iframeContainer.style.marginLeft = `${margin}px`;\n if (width >= MIN_RESIZE) {\n this.iframeContainer.style.width = `${width}px`;\n }\n this._updateLayoutState();\n }\n });\n }\n\n /**\n * Update the current layout state and notify the embedder if they provided\n * an `onLayoutChange` callback in the Hypothesis config, as well as guests\n * so they can enable/adapt side-by-side mode.\n *\n * This is called when the sidebar is opened, closed or resized.\n *\n * @param expanded -\n * `true` or `false` if the sidebar is being directly opened or closed, as\n * opposed to being resized via the sidebar's drag handles\n */\n _updateLayoutState(expanded?: boolean) {\n // The sidebar structure is:\n //\n // [ Toolbar ][ ]\n // [ ---------- ][ Sidebar iframe container (@frame) ]\n // [ Bucket Bar ][ ]\n //\n // The sidebar iframe is hidden or shown by adjusting the left margin of\n // its container.\n\n const toolbarWidth = (this.iframeContainer && this.toolbar.getWidth()) || 0;\n const frame: Element = this.iframeContainer ?? this.externalFrame!;\n const { height } = frame.getBoundingClientRect();\n const computedStyle = window.getComputedStyle(frame);\n const width = parseInt(computedStyle.width);\n const leftMargin = parseInt(computedStyle.marginLeft);\n\n // The width of the sidebar that is visible on screen, including the\n // toolbar, which is always visible.\n let frameVisibleWidth = toolbarWidth;\n\n if (typeof expanded === 'boolean') {\n if (expanded) {\n frameVisibleWidth += width;\n }\n } else {\n if (leftMargin < MIN_RESIZE) {\n frameVisibleWidth -= leftMargin;\n } else {\n frameVisibleWidth += width;\n }\n\n // Infer expanded state based on whether at least part of the sidebar\n // frame is visible.\n expanded = frameVisibleWidth > toolbarWidth;\n }\n\n const layoutState: SidebarLayout = {\n expanded,\n width: expanded ? frameVisibleWidth : toolbarWidth,\n height,\n toolbarWidth,\n };\n\n this._layoutState = layoutState;\n this.onLayoutChange?.(layoutState);\n\n this._guestRPC.forEach(rpc =>\n rpc.call('sidebarLayoutChanged', layoutState),\n );\n }\n\n /**\n * On window resize events, update the marginLeft of the sidebar by calling hide/show methods.\n */\n _onResize() {\n if (this.toolbar.sidebarOpen === true) {\n if (window.innerWidth < MIN_RESIZE) {\n this.close();\n } else {\n this.open();\n }\n }\n }\n\n /** Return true if the user is currently resizing the sidebar. */\n isResizing() {\n return this._dragResizeState.initial !== null;\n }\n\n /**\n * Event handler invoked when user drags the sidebar toggle button in order\n * to resize the sidebar.\n */\n _onDragSidebarToggleButton(event: DragHandlerEvent) {\n const frame = this.iframeContainer;\n if (!frame) {\n return;\n }\n\n switch (event.type) {\n case 'dragstart':\n this._resetDragResizeState();\n\n // Disable animated transition of sidebar position\n frame.classList.add('sidebar-no-transition');\n\n // Disable pointer events on the iframe.\n frame.style.pointerEvents = 'none';\n\n this._dragResizeState.initial = parseInt(\n getComputedStyle(frame).marginLeft,\n );\n\n break;\n case 'dragend':\n frame.classList.remove('sidebar-no-transition');\n\n // Re-enable pointer events on the iframe.\n frame.style.pointerEvents = '';\n\n // Snap open or closed.\n if (\n this._dragResizeState.final === null ||\n this._dragResizeState.final <= -MIN_RESIZE\n ) {\n this.open();\n } else {\n this.close();\n }\n this._resetDragResizeState();\n break;\n case 'dragmove': {\n if (typeof this._dragResizeState.initial !== 'number') {\n return;\n }\n\n const margin = this._dragResizeState.initial;\n const delta = event.deltaX;\n this._dragResizeState.final = Math.min(Math.round(margin + delta), 0);\n this._updateLayout();\n break;\n }\n }\n }\n\n open() {\n this._sidebarRPC.call('sidebarOpened');\n\n if (this.iframeContainer) {\n const width = this.iframeContainer.getBoundingClientRect().width;\n this.iframeContainer.style.marginLeft = `${-1 * width}px`;\n this.iframeContainer.classList.remove('sidebar-collapsed');\n }\n\n this.toolbar.sidebarOpen = true;\n\n if (this._config.showHighlights === 'whenSidebarOpen') {\n this.setHighlightsVisible(true);\n }\n\n this._updateLayoutState(true);\n }\n\n close() {\n this._sidebarRPC.call('sidebarClosed');\n\n if (this.iframeContainer) {\n this.iframeContainer.style.marginLeft = '';\n this.iframeContainer.classList.add('sidebar-collapsed');\n }\n\n this.toolbar.sidebarOpen = false;\n\n if (this._config.showHighlights === 'whenSidebarOpen') {\n this.setHighlightsVisible(false);\n }\n\n this._updateLayoutState(false);\n }\n\n /**\n * Set whether highlights are visible in guest frames.\n */\n setHighlightsVisible(visible: boolean) {\n this.toolbar.highlightsVisible = visible;\n\n // Notify sidebar app of change which will in turn reflect state to guest frames.\n this._sidebarRPC.call('setHighlightsVisible', visible);\n }\n\n /**\n * Shows the sidebar's controls\n */\n show() {\n this.iframeContainer?.classList.remove('is-hidden');\n }\n\n /**\n * Hides the sidebar's controls\n */\n hide() {\n this.iframeContainer?.classList.add('is-hidden');\n }\n}\n","import type { CallMap, PortRPC } from '../shared/messaging';\nimport type { SidebarToHostCalls } from '../types/port-rpc-calls';\n\nconst ANNOTATION_COUNT_ATTR = 'data-hypothesis-annotation-count';\n\n/**\n * Show the current count of public annotations in designated elements.\n *\n * Any time the count of public annotations changes, find all elements within\n * `rootEl` that have the `data-hypothesis-annotation-count` attribute and\n * replace their text content with the current count of public annotations.\n *\n * This allows publishers to add a count of annotations to their web pages.\n *\n * See:\n * https://h.readthedocs.io/projects/client/en/latest/publishers/host-page-integration.html#cmdoption-arg-data-hypothesis-annotation-count\n *\n */\nexport function annotationCounts(\n rootEl: Element,\n rpc: PortRPC<SidebarToHostCalls, CallMap>,\n) {\n rpc.on('publicAnnotationCountChanged', updateAnnotationCountElems);\n\n function updateAnnotationCountElems(newCount: number) {\n const elems = rootEl.querySelectorAll(`[${ANNOTATION_COUNT_ATTR}]`);\n Array.from(elems).forEach(elem => {\n elem.textContent = newCount.toString();\n });\n }\n}\n","const SIDEBAR_TRIGGER_BTN_ATTR = 'data-hypothesis-trigger';\n\n/**\n * Show the sidebar when user clicks on an element with the\n * trigger data attribute.\n *\n * @param rootEl - The DOM element which contains the trigger elements.\n * @param showFn - Function which shows the sidebar.\n */\nexport function sidebarTrigger(rootEl: Element, showFn: () => void) {\n const triggerElems = rootEl.querySelectorAll(\n '[' + SIDEBAR_TRIGGER_BTN_ATTR + ']',\n );\n\n Array.from(triggerElems).forEach(triggerElem => {\n triggerElem.addEventListener('click', e => {\n showFn();\n e.stopPropagation();\n });\n });\n}\n","/*\n * Disable @typescript-eslint/ban-types for the whole file, as changing the\n * event's callback type away from `Function` has multiple implications that\n * should be addressed separately\n */\nimport { EventEmitter } from '../../shared/event-emitter';\nimport type { EventMap } from '../../shared/event-emitter';\nimport type { Destroyable } from '../../types/annotator';\n\ntype Callback = (...args: any[]) => void;\n\n/**\n * Emitter is a communication class that implements the publisher/subscriber\n * pattern. It allows sending and listening events through a shared EventBus.\n * The different elements of the application can communicate with each other\n * without being tightly coupled.\n */\nexport class Emitter<Event extends EventMap> implements Destroyable {\n private _emitter: EventEmitter<Event>;\n private _subscriptions: [event: string, callback: Callback][];\n\n constructor(emitter: EventEmitter<Event>) {\n this._emitter = emitter;\n this._subscriptions = [];\n }\n\n /**\n * Fire an event.\n */\n publish<K extends keyof Event>(event: K, ...args: Parameters<Event[K]>) {\n this._emitter.emit(event, ...args);\n }\n\n /**\n * Register an event listener.\n */\n subscribe<K extends keyof Event & string>(event: K, callback: Event[K]) {\n this._emitter.on(event, callback);\n this._subscriptions.push([event as string, callback]);\n }\n\n /**\n * Remove an event listener.\n */\n unsubscribe<K extends keyof Event>(event: K, callback: Callback) {\n this._emitter.off(event, callback as Event[K]);\n this._subscriptions = this._subscriptions.filter(\n ([subEvent, subCallback]) =>\n subEvent !== event || subCallback !== callback,\n );\n }\n\n /**\n * Remove all event listeners.\n */\n destroy() {\n for (const [event, callback] of this._subscriptions) {\n this._emitter.off(event, callback as any);\n }\n this._subscriptions = [];\n }\n}\n\nexport class EventBus<Event extends EventMap> {\n private _emitter: EventEmitter<Event>;\n\n constructor() {\n this._emitter = new EventEmitter();\n }\n\n createEmitter() {\n return new Emitter(this._emitter);\n }\n}\n","// Load polyfill for :focus-visible pseudo-class.\nimport 'focus-visible';\n// Enable debug checks for Preact. Removed in prod builds by Rollup config.\nimport 'preact/debug';\n\nimport {\n PortProvider,\n installPortCloseWorkaroundForSafari,\n} from '../shared/messaging';\nimport type { Destroyable, Events } from '../types/annotator';\nimport type { NotebookConfig } from './components/NotebookModal';\nimport type { ProfileConfig } from './components/ProfileModal';\nimport { getConfig } from './config/index';\nimport { Guest } from './guest';\nimport type { GuestConfig } from './guest';\nimport {\n HypothesisInjector,\n removeTemporaryClientConfig,\n} from './hypothesis-injector';\nimport type { InjectConfig } from './hypothesis-injector';\nimport {\n VitalSourceInjector,\n vitalSourceFrameRole,\n} from './integrations/vitalsource';\nimport { Notebook } from './notebook';\nimport { Profile } from './profile';\nimport { Sidebar } from './sidebar';\nimport type { SidebarConfig } from './sidebar';\nimport { EventBus } from './util/emitter';\n\n// Look up the URL of the sidebar. This element is added to the page by the\n// boot script before the \"annotator\" bundle loads.\nconst sidebarLinkElement = document.querySelector(\n 'link[type=\"application/annotator+html\"][rel=\"sidebar\"]',\n) as HTMLLinkElement;\n\n/**\n * Find and remove existing `<hypothesis-sidebar>` elements, and other\n * Hypothesis application containers, which are created in the host frame.\n *\n * These might exist if the current page is a local snapshot of a web page saved\n * with the browser's \"Save Page As\" feature. In that case the snapshot can\n * include both the annotator bundle JS and the DOM elements it created. See\n * https://github.com/hypothesis/client/issues/5827.\n *\n * Having duplicates of these elements is problematic because they contain\n * iframed apps which will try to communicate with the host frame, and the\n * host frame assumes there is only one of each.\n *\n * Returns true if any such elements were found.\n */\nfunction removeExistingHypothesisAppElements(): boolean {\n const appElements = document.querySelectorAll(\n ['hypothesis-sidebar', 'hypothesis-notebook', 'hypothesis-profile'].join(\n ',',\n ),\n );\n appElements.forEach(el => el.remove());\n return appElements.length > 0;\n}\n\n/**\n * Entry point for the part of the Hypothesis client that runs in the page being\n * annotated.\n *\n * Depending on the client configuration in the current frame, this can\n * initialize different functionality. In \"host\" frames the sidebar controls and\n * iframe containing the sidebar application are created. In \"guest\" frames the\n * functionality to support anchoring and creating annotations is loaded. An\n * instance of Hypothesis will have one host frame, one sidebar frame and one or\n * more guest frames. The most common case is that the host frame, where the\n * client is initially loaded, is also the only guest frame.\n */\nfunction init() {\n const annotatorConfig = getConfig('annotator') as GuestConfig & InjectConfig;\n\n let resolveUnloadRequested = () => {};\n const unloadRequested = new Promise<void>(resolve => {\n resolveUnloadRequested = resolve;\n });\n sidebarLinkElement.addEventListener('destroy', resolveUnloadRequested);\n\n const hostFrame = annotatorConfig.subFrameIdentifier ? window.parent : window;\n\n const destroyables = [] as Destroyable[];\n\n if (hostFrame === window) {\n if (removeExistingHypothesisAppElements()) {\n // If there were existing `<hypothesis-sidebar>` etc. elements, we are in\n // an \"abnormal\" environment such as a snapshot of a web page where\n // Hypothesis was loaded. We assume we can't function in such an\n // environment, so we clean up the previous elements and abort.\n console.warn(\n 'Hypothesis did not load because it found an existing instance on the page.',\n );\n return;\n }\n\n // Ensure port \"close\" notifications from eg. guest frames are delivered properly.\n const removeWorkaround = installPortCloseWorkaroundForSafari();\n destroyables.push({ destroy: removeWorkaround });\n\n const sidebarConfig = getConfig('sidebar') as SidebarConfig;\n\n const hypothesisAppsOrigin = new URL(sidebarConfig.sidebarAppUrl).origin;\n const portProvider = new PortProvider(hypothesisAppsOrigin);\n\n const eventBus = new EventBus<Events>();\n const sidebar = new Sidebar(document.body, eventBus, sidebarConfig);\n const notebook = new Notebook(\n document.body,\n eventBus,\n getConfig('notebook') as NotebookConfig,\n );\n const profile = new Profile(\n document.body,\n eventBus,\n getConfig('profile') as ProfileConfig,\n );\n\n portProvider.on('frameConnected', (source, port) =>\n sidebar.onFrameConnected(source, port),\n );\n destroyables.push(portProvider, sidebar, notebook, profile);\n }\n\n const vsFrameRole = vitalSourceFrameRole();\n if (vsFrameRole === 'container') {\n const vitalSourceInjector = new VitalSourceInjector(annotatorConfig);\n destroyables.push(vitalSourceInjector);\n } else {\n // Set up automatic injection of the client into iframes in this frame.\n const hypothesisInjector = new HypothesisInjector(\n document.body,\n annotatorConfig,\n );\n\n // Create the guest that handles creating annotations and displaying highlights.\n const guest = new Guest(document.body, annotatorConfig, hostFrame);\n\n // When the client is unloaded in the host frame, also unload it from any\n // connected iframes.\n guest.on('hostDisconnected', resolveUnloadRequested);\n\n destroyables.push(hypothesisInjector, guest);\n }\n\n unloadRequested.then(() => {\n destroyables.forEach(instance => instance.destroy());\n\n // Remove all the `<link>`, `<script>` and `<style>` elements added to the\n // page by the boot script.\n const clientAssets = document.querySelectorAll('[data-hypothesis-asset]');\n clientAssets.forEach(el => el.remove());\n\n // If this is a guest-only frame, remove client config added by the host\n // frame. This enables the client to later be re-loaded in this frame.\n removeTemporaryClientConfig();\n });\n}\n\n/**\n * Returns a Promise that resolves when the document has loaded (but subresources\n * may still be loading).\n */\nfunction documentReady(): Promise<void> {\n return new Promise(resolve => {\n if (document.readyState !== 'loading') {\n resolve();\n }\n // nb. `readystatechange` may be emitted twice, but `resolve` only resolves\n // on the first call.\n document.addEventListener('readystatechange', () => resolve());\n });\n}\n\ndocumentReady().then(init);\n"],"names":["applyFocusVisiblePolyfill","scope","hadKeyboardEvent","hadFocusVisibleRecently","hadFocusVisibleRecentlyTimeout","inputTypesAllowlist","text","search","url","tel","email","password","number","date","month","week","time","datetime","isValidFocusTarget","el","document","nodeName","classList","focusTriggersKeyboardModality","type","tagName","readOnly","isContentEditable","addFocusVisibleClass","contains","add","setAttribute","removeFocusVisibleClass","hasAttribute","remove","removeAttribute","onKeyDown","e","metaKey","altKey","ctrlKey","activeElement","onPointerDown","onFocus","target","onBlur","window","clearTimeout","setTimeout","onVisibilityChange","visibilityState","addInitialPointerMoveListeners","addEventListener","onInitialPointerMove","removeInitialPointerMoveListeners","removeEventListener","toLowerCase","nodeType","Node","DOCUMENT_FRAGMENT_NODE","host","DOCUMENT_NODE","documentElement","event","CustomEvent","error","createEvent","initCustomEvent","dispatchEvent","factory","n","l","u","i","r","o","f","c","s","a","h","p","y","v","w","Array","isArray","d","g","parentNode","removeChild","m","t","props","key","ref","__k","__","__b","__e","__c","constructor","__v","__i","__u","vnode","k","children","x","this","context","S","length","C","base","M","__d","push","$","__r","debounceRendering","sort","shift","__P","O","__n","namespaceURI","z","I","_","String","L","B","P","q","A","nextSibling","insertBefore","T","setProperty","test","j","style","cssText","replace","slice","F","b","H","prototype","render","contextType","value","__E","D","sub","state","__h","_sb","__s","getDerivedStateFromProps","componentWillMount","componentDidMount","componentWillReceiveProps","shouldComponentUpdate","some","componentWillUpdate","componentDidUpdate","getChildContext","getSnapshotBeforeUpdate","N","then","indexOf","V","diffed","call","map","localName","createTextNode","createElementNS","is","__m","data","childNodes","attributes","name","__html","innerHTML","content","current","unmount","componentWillUnmount","E","arguments","defaultProps","firstChild","K","Set","forEach","delete","Provider","__l","Consumer","getDerivedStateFromError","setState","componentDidCatch","forceUpdate","Promise","bind","resolve","__H","__N","__f","filter","every","requestAnimationFrame","cancelAnimationFrame","_defineProperty","Symbol","toPrimitive","TypeError","Number","_toPrimitive","_toPropertyKey","Object","defineProperty","enumerable","configurable","writable","ListenerCollection","_listeners","Map","eventTarget","eventType","listener","options","symbol","set","listenerId","get","removeAll","clear","defaultSetFocus","element","focus","useArrowKeyNavigation","containerRef","autofocus","loop","horizontal","vertical","selector","containerVisible","focusElement","focusElement_","lastFocusedItem","useRef","callback","wrapper","args","useStableCallback","useEffect","Error","container","getNavigableElements","filtered","from","querySelectorAll","offsetParent","disabled","isElementDisabled","unshift","updateTabIndexes","elements","currentIndex","setFocus","keyEvent","findIndex","tabIndex","index","entries","navigableElements","initialIndex","listeners","targetIndex","item","handled","preventDefault","stopPropagation","mo","MutationObserver","observe","subtree","attributeFilter","childList","disconnect","SyncedRef","_target","_value","_updateTarget","useSyncedRef","targetRef","hasOwn","hasOwnProperty","classNames","classes","arg","appendClass","parseValue","apply","toString","includes","newClass","module","exports","default","__source","__self","createContext","_jsxFileName","Card","elementRef","active","variant","width","htmlAttributes","_jsxDEV","className","classnames","fileName","lineNumber","columnNumber","CardContent","size","AnnotateIcon","xmlns","height","viewBox","fill","CancelIcon","CaretDownIcon","CaretLeftIcon","CaretRightIcon","CautionIcon","CheckIcon","CheckboxIcon","CheckboxCheckedFilledIcon","HideIcon","HighlightIcon","MenuCollapseIcon","MenuExpandIcon","NoteIcon","PinIcon","PointerDownIcon","stroke","PointerUpIcon","RadioIcon","RadioCheckedIcon","SelectionIcon","ShowIcon","Button","expanded","pressed","title","icon","Icon","unstyled","role","styled","themed","sized","ariaProps","inputGroupStyles","IconButton","ToggleInput","checked","UncheckedIcon","checkedIcon","CheckedIcon","onChange","id","containerClasses","htmlFor","Checkbox","defaultChecked","rest","isControlled","uncontrolledChecked","setUncontrolledChecked","useState","RadioGroupContext","Radio","subtitle","radioDisabled","radioGroupContext","useContext","selected","isSelected","onClick","undefined","displayName","assign","direction","label","labelledBy","dataset","POPOVER_ANCHOR_EL_GAP","POPOVER_VIEWPORT_HORIZONTAL_GAP","useOnClose","popoverRef","anchorElementRef","onClose","popoverOpen","asNativePopover","popover","toggleListener","oldState","newState","enabled","body","handleAwayClick","composedPath","useClickAway","keys","useKeyPress","Popover","open","align","onScroll","HTMLElement","alignToRight","adjustPopoverPositioning","useCallback","popoverEl","anchorEl","setPopoverCSSProps","prop","viewportHeight","innerHeight","top","anchorElDistanceToTop","bottom","anchorElBottom","left","anchorElLeft","anchorElHeight","anchorElWidth","getBoundingClientRect","anchorElDistanceToBottom","popoverHeight","popoverWidth","shouldBeAbove","marginBottom","marginTop","bodyTop","bodyWidth","absBodyTop","Math","abs","availableSpace","minWidth","max","useLayoutEffect","togglePopover","cleanup","capture","observer","ResizeObserver","usePopoverPositioning","restoreFocusTo","currentFocus","useRestoreFocusOnClose","hidden","SelectContext","optionChildren","status","SelectOption","checkboxRef","checkboxContainerRef","optionRef","eventTriggeredInCheckbox","selectContext","selectValue","currentValue","multiple","useMemo","selectOneValue","closeListbox","toggleValue","copy","splice","truncate","listboxOverflow","_optionRef$current","SelectMain","buttonContent","buttonId","buttonClasses","popoverClasses","onPopoverScroll","alignListbox","ariaLabel","ariaLabelledBy","listboxAsPopover","wrapperRef","listboxRef","listboxOpen","setListboxOpen","listboxId","useId","buttonRef","defaultButtonId","relatedTarget","useFocusAway","prev","Option","Callout","StatusIcon","withIcon","ToastMessageItem","message","onDismiss","visuallyHidden","ToastMessageTransition","onTransitionEnd","transitionClasses","isDismissed","transitionIn","transitionOut","onAnimationEnd","ToastMessages","messages","onMessageDismiss","setTimeout_","dismissedMessages","setDismissedMessages","messageSchedules","dismissMessage","ids","scheduleMessageDismiss","timeout","_message$autoDismiss","autoDismiss","pendingTimeouts","Link","underline","rel","PointerButton","htmlAndButtonProps","byteToHex","val","str","generateHexString","len","bytes","Uint8Array","crypto","getRandomValues","join","isMessage","property","PortRequestError","super","PortFinder","hostFrame","source","sourceId","_hostFrame","_source","_sourceId","destroy","async","requestId","reject","postRequest","postMessage","frame1","frame2","intervalId","setInterval","timeoutId","clearInterval","ports","EventEmitter","on","off","ln","emit","errorDestination","serializeError","err","stack","sendError","postErr","DOMException","sendErr","console","warn","sendErrorsTo","destination","PortProvider","hypothesisAppsOrigin","_hypothesisAppsOrigin","_emitter","_handledRequests","_sidebarHostChannel","MessageChannel","_sidebarConnected","_allowedMessages","allowedOrigin","_listen","errorContext","sentErrors","reportError","has","origin","channel","MessagePort","ServiceWorker","isSourceWindow","find","allowedMessage","_messageMatches","targetOrigin","messageChannel","port1","port2","JSON","stringify","isMessageEqual","eventName","handler","VERSION","PROTOCOL","makeRequestMessage","method","sequence","protocol","version","sendCall","port","shouldUseSafariWorkaround","userAgent","webkitVersionMatch","match","parseInt","versionMatch","PortRPC","navigator","currentWindow","forceUnloadListener","_port","_methods","_sequence","_callbacks","parent","_pendingCalls","_destroyed","_receivedCloseEvent","connect","_handle","MessageEvent","start","close","seq","finalArg","_parseMessage","msg","response","cb","toBoolean","trim","toLocaleLowerCase","numericalVal","isNaN","Boolean","parseJsonConfig","config","settingsElements","settings","parse","textContent","object","rectIsEmpty","rect","linesOverlap","min","rectIntersects","rectA","rectB","right","rectsOverlapVertically","rectsOverlapHorizontally","unionRects","DOMRect","rectCenter","DOMPoint","nodeIsElement","node","ELEMENT_NODE","nodeIsText","TEXT_NODE","contentSelectors","textRectRange","textRect","end","createRange","setStart","setEnd","elementContentRect","scrollLeft","scrollTop","scrollHeight","scrollWidth","textNodesInRect","root","shouldVisit","contentIntersectsRect","getScrollAnchor","viewport","anchorRange","getComputedStyle","position","hasFixedPosition","textNodeLoop","textNode","textLen","word","split","wordBox","preserveScrollPosition","scrollRoot","innerWidth","anchor","anchorTop","scrollDelta","urlFromLinkTag","window_","link","querySelector","href","checkIfString","settingsFrom","configFuncSettings","hypothesisConfig","docs","configFuncSettingsFrom","jsonConfigs","ignoreOtherConfiguration","hostPageSetting","annotations","annotFragmentMatch","location","annotationsFromURL","clientUrl","group","groupFragmentMatch","groupFromURL","notebookAppUrl","profileAppUrl","showHighlights","sidebarAppUrl","query","queryFragmentMatch","decodeURIComponent","queryFromURL","sideBySide","isObject","mode","isSideBySideMode","isActive","getHostPageSetting","configDefinitions","allowInBrowserExt","defaultValue","getValue","appType","branding","bucketContainerSelector","contentInfoBanner","contentReady","enableExperimentalNewNoteButton","theme","usernameUrl","onLayoutChange","openSidebar","coerce","requestConfigFromFrame","services","subFrameIdentifier","externalContainerSelector","getConfig","contexts","annotator","sidebar","notebook","profile","values","flat","configurationKeys","configDef","hasDefault","isURLFromBrowserExtension","startsWith","modifiers","alt","ctrl","meta","matchShortcut","shortcut","KeyboardEvent","parts","requiredModifiers","requiredKey","part","modifierFlag","shiftKey","useShortcut","onPress","rootElement","onKeydown","installShortcut","isTouchDevice","_window","matchMedia","matches","NumberIcon","badgeCount","_jsx","AdderToolbarArrow","arrowDirection","ToolbarButton","_jsxs","AdderToolbarShortcuts","annotationCount","isVisible","_Fragment","AdderToolbar","onCommand","annotateShortcut","highlightShortcut","showShortcut","dir","visibility","createShadowRoot","shadowRoot","attachShadow","linkEl","createElement","appendChild","loadStyles","applyFocusVisible","PreactContainer","tag","_element","_shadowRoot","_render","ArrowDirection","toPx","pixels","Adder","_view","ownerDocument","defaultView","_isVisible","_arrowDirection","_annotationsForSelection","_onAnnotate","onAnnotate","_onHighlight","onHighlight","_onShowAnnotations","onShowAnnotations","_container","annotationsForSelection","hide","show","selectionRect","isRTLselection","_calculateTarget","_showAt","UP","_firstChild","_width","_height","DOWN","hMargin","adderWidth","touchScreenOffset","adderHeight","_findZindex","elementsFromPoint","zIndexes","zIndex","isInteger","parentRect","parentEl","parentElement","nearestPositionedAncestor","command","TrimDirection","closestNonSpaceInString","baseOffset","nextChar","Forwards","charAt","availableChars","availableNonWhitespaceChars","Backwards","substring","trimEnd","trimStart","offsetDelta","closestNonSpaceInRange","range","nodeIter","commonAncestorContainer","createNodeIterator","NodeFilter","SHOW_TEXT","initialBoundaryNode","startContainer","endContainer","terminalBoundaryNode","currentNode","nextNode","previousNode","trimmedOffset","advance","nodeText","offset","RangeError","nodeTextLength","previousSiblingsTextLength","sibling","previousSibling","resolveOffsets","offsets","nextOffset","results","ResolveDirection","TextPosition","relativeTo","tw","createTreeWalker","getRootNode","forwards","FORWARDS","static","fromPoint","textOffset","TextRange","toRange","BACKWARDS","Range","startOffset","endOffset","trimmedRange","cloneRange","startTrimmed","endTrimmed","trimmedOffsets","trimRange","fromRange","placeholderSelector","isInPlaceholder","closest","unionRanges","result","compareBoundaryPoints","START_TO_START","END_TO_END","selectedRange","selection","getSelection","rangeCount","getRangeAt","collapsed","isSelectionBackwards","focusNode","anchorNode","focusOffset","anchorOffset","isNodeInRange","nodeValue","comparePoint","forEachNodeInRange","SHOW_ALL","selectionFocusRect","textBoxes","textNodes","flatMap","nodeRange","selectNodeContents","viewportRects","getClientRects","detach","getTextBoundingBoxes","SVG_NAMESPACE","clusterValues","highlightRange","cssClass","splitText","wholeTextNodesInRange","inPlaceholder","textNodeSpans","prevNode","currentSpan","whitespace","span","highlights","nodes","highlightEl","replaceChild","highlightEls","canvasEl","pageEl","getPDFCanvas","svgHighlightLayer","svgStyle","mixBlendMode","canvasRect","highlightRects","highlightRect","svgHighlight","append","drawHighlightsAbovePDFCanvas","replaceWith","replacements","removeHighlights","setHighlightsFocused","focused","svgEl","focusedId","getAttribute","focusedHighlight","cloneNode","setSVGHighlightFocused","toggle","updateClusters","setNestingData","getHighlights","layer","layerHighlights","svgHighlights","getElementsByClassName","getSVGHighlights","idx","allEls","nestingLevel","replaceChildren","updateSVGHighlightOrdering","isHighlightElement","getElementsByTagName","highlight","parentCluster","parentClusterLevel","hEl","elCluster","cv","elClusterLevel","MAX_SAFE_INTEGER","computeAnchorPositions","anchors","positions","annotation","collection","rects","reduce","acc","$tag","anchor1","anchor2","BucketBarClient","contentContainer","hostRPC","_hostRPC","_updatePending","_anchors","update","normalizeRect","minX","maxX","DrawError","DrawTool","_tool","cancel","tool","surface","cursor","_surface","promise","shape","resolve_","reject_","promiseWithResolvers","_drawEnd","_drawError","_shape","clientX","clientY","_renderSurface","_abortDraw","abort","scrollDeltaY","deltaY","scrollDeltaX","deltaX","elem","prevScrollLeft","sign","prevScrollTop","AbortController","signal","onabort","LayoutChangeEvent","detail","bubbles","cancelable","shownWarnings","warnOnce","reset","annotatorFlags","FeatureFlags","knownFlags","_flags","_knownFlags","flags","flag","flagEnabled","allFlags","fromEntries","ClusterStyleControl","cluster","currentStyles","highlightStyles","appliedStyleName","isHidden","backgroundColor","color","styleName","ClusterToolbar","availableStyles","onStyleChange","handleStyleChange","changeEvent","input","isOpen","setOpen","transparent","secondColor","thirdColor","pink","orange","yellow","green","purple","grey","defaultClusterStyles","HighlightClusterController","_features","features","offsetTop","appliedStyles","_init","_activate","_isActive","_updateTimeout","scheduleClusterUpdates","_updateClusters","_setClusterStyles","_setClusterStyle","styleRules","ruleName","_onChangeClusterStyle","reverse","oneIfNotZero","advanceBlock","ctx","peq","hIn","pV","mV","hInIsNegative","eq","xV","xH","pH","mH","hOut","lastRowMask","findMatchEnds","pattern","maxErrors","bMax","ceil","Uint32Array","emptyPeq","asciiPeq","charCodeAt","charPeq","score","charCode","carry","maxBlockScore","remainder","errors","patRev","minStart","rm","findMatchStarts","matchPos","exactMatches","approxSearch","textMatchScore","matchQuote","quote","scoreMatch","quoteScore","prefixScore","prefix","suffixScore","suffix","posScore","hint","quoteWeight","scoredMatches","getPathSegment","getNodeName","pos","tmp","getNodePosition","xpathFromNode","xpath","nthChildOfType","toUpperCase","matchIndex","child","nodeFromXPath","segments","segment","elementName","elementIndex","separatorPos","indexStr","evaluateSimpleXPath","evaluate","XPathResult","FIRST_ORDERED_NODE_TYPE","singleNodeValue","RangeAnchor","startPos","fromCharOffset","endPos","toSelector","normalizedRange","textRange","TextPositionAnchor","fromOffsets","TextQuoteAnchor","exact","toPositionAnchor","parseMediaTime","timeStr","parseFloat","isFinite","closestElement","Element","getMediaTimeRange","startTime","endTime","MediaTimeAnchor","timeRange","startIdx","ary","pred","findLastIndex","endIdx","endEl","observeCalls","origHandler","stripFragment","NavigationObserver","onNavigate","getLocation","lastURL","checkForURLChange","newURL","navigation","Navigation","getNavigation","unpatchers","history","_unpatchHistory","COMPLETE","CANCELED","setElementScroll","self","scrollTo","animate","scrollSettings","_scrollSettings","maxSynchronousAlignments","parentPosition","differenceX","differenceY","targetWidth","targetHeight","targetPosition","leftAlign","topAlign","leftOffset","topOffset","leftScalar","topScalar","isWindow","pageXOffset","pageYOffset","lockX","lockY","offsetLeft","clientWidth","clientHeight","getTargetScrollLocation","Date","now","timeValue","endIterations","easeValue","ease","scrollAncestor","task","raf","defaultIsWindow","defaultIsScrollable","overflow","defaultValidTarget","findParentElement","assignedSlot","ownerWindow","scrollIntoView","pow","parents","validTarget","isScrollable","debug","log","scrollingElements","done","cancelHandler","idle","lastSettings","passiveOptions","passive","endType","cancellable","transitionScrollTo","interpolate","fraction","scrollElement","maxDuration","scrollStart","scrollDuration","scrollFraction","normalizeURI","uri","baseURI","URL","HTMLMetadata","_getDocumentHref","links","_getLinks","getDocumentMetadata","metadata","dc","_getMetaTags","eprints","facebook","highwire","prism","twitter","favicon","_getFavicon","_getTitle","dcLink","documentFingerprint","attribute","tags","RegExp","linkElements","hreflang","_absoluteUrl","doi","dcRelationValues","dcIdentifierValues","identifier","dcUrnRelationComponent","dcUrnIdentifierComponent","dcUrn","encodeURIComponent","allowedSchemes","scheme","HTMLIntegration","sideBySideOptions","featureFlags","_htmlMeta","_prevURI","_sideBySideEnabled","_sideBySideOptions","_sideBySideActive","_lastLayout","_navObserver","_checkForURIChange","_metaObserver","head","_flagsChanged","selectors","mediaTime","maybeAssertQuote","range_","catch","fromSelector","position_","quote_","mediaTime_","describe","region","types","currentURI","getAnnotatableRange","canStyleClusteredHighlights","_deactivateSideBySide","fitSideBySide","layout","maximumWidthToFit","_activateSideBySide","sideBySideActive","supportedTools","sidebarWidth","rightMargin","computeLeftMargin","marginLeft","marginRight","beforeBodyLeft","contentArea","leftMarginVotes","rightMarginVotes","contentSelector","paragraphs","textLength","leftVotes","rightVotes","leftMargin","leftPos","rightPos","guessMainContentArea","freeSpace","adjustedMargin","details","scrollElementIntoView","NAN","symbolTag","reTrim","reIsBadHex","reIsBinary","reIsOctal","freeParseInt","freeGlobal","global","freeSelf","Function","objectToString","nativeMax","nativeMin","toNumber","isObjectLike","isSymbol","other","valueOf","isBinary","lodash_debounce","func","wait","lastArgs","lastThis","maxWait","timerId","lastCallTime","lastInvokeTime","leading","maxing","trailing","invokeFunc","thisArg","shouldInvoke","timeSinceLastCall","timerExpired","trailingEdge","remainingWait","debounced","isInvoking","leadingEdge","flush","count","countChars","translateOffsets","output","beforeStartCount","startToEndCount","outputStart","RenderingStates","INITIAL","FINISHED","pageTextCache","quotePositionCache","quotePositionCacheKey","getSiblingIndex","getNodeTextLayer","getPDFViewer","PDFViewerApplication","pdfViewer","getPageView","pageIndex","pageView","pdfPage","onPagesLoaded","eventBus","getPageTextContent","cachedText","pageText","getTextContent","normalizeWhitespace","items","it","getPageText","findPageByOffset","viewer","pageStartOffset","pageEndOffset","pagesCount","isSpace","char","isNotSpace","isTextLayerRenderingDone","textLayer","renderingDone","div","anchorByPosition","page","all","renderingState","textLayerDiv","textLayerStr","textLayerStart","textLayerEnd","stripSpaces","placeholder","createPlaceholder","setStartBefore","setEndAfter","stripped","matchedText","cacheKey","cachedPos","quoteSelector","positionHint","pageCount","pageIndexes","expectedPageIndex","expectedOffsetInPage","strippedPrefix","strippedSuffix","strippedQuote","bestMatch","strippedText","strippedHint","exactQuoteMatch","exactPrefixMatch","exactSuffixMatch","anchorQuote","pageSelector","shapeSelector","pageLeft","pageBottom","pageRight","pageTop","view","pageWidth","pageHeight","mapX","mapY","coordinates","anchorShape","getTextLayerForRange","startTextLayer","endTextLayer","startPageIndex","pageOffset","getPageOffset","createPageSelector","mapViewportToPDF","pageViewRect","pageViewX","pageViewY","pdfPageView","userX","userY","getPagePoint","pageLabel","Banners","ContentInfoBanner","info","itemTitle","logo","src","previousItem","currentItem","nextItem","WarningBanner","PDFMetadata","app","_loaded","initializedPromise","initialized","pdfViewerInitialized","isDownloadComplete","downloadComplete","pdfDocument","getDownloadInfo","isPDFDownloaded","finish","getUri","getPDFURL","fingerprintToURN","getFingerprint","documentInfo","contentDispositionFilename","getMetadata","Title","parsed","pathSegments","pathname","filenameFromURL","fingerprints","fingerprint","anchorIsInPlaceholder","delay","ms","BannerController","_pdfjsContainer","_contentInfo","_noTextWarning","setContentInfo","_update","showNoTextWarning","prepend","bannerHeight","PDFIntegration","reanchoringMaxWait","_annotator","pdfWindow","pdfViewerApp","_pdfViewer","_pdfContainer","appConfig","appContainer","_pdfMetadata","_observer","debounce","_reanchoringMaxWait","_banner","_checkForSelectableText","_updateAnnotationLayerVisibility","isCollapsed","toolbarWidth","showContentInfo","canDescribe","pageBoundingBox","viewLeft","viewBottom","viewRight","viewTop","topLeft","bottomRight","point","describeShape","hasText","documentHasText","refreshAnnotations","hl","sidebarLayout","reservedSpace","currentScaleValue","imageAnnotation","_anchorOffset","_waitForAnnotationToBeAnchored","offsetRelativeTo","opts","thumbnailSize","PDF_TO_CSS_UNITS","devicePixelRatio","naturalWidth","aspectRatio","maxWidth","getViewport","scale","scaleFactor","boxView","PageViewport","rotation","userUnit","canvas","OffscreenCanvas","getContext","canvasContext","save","radius","strokeStyle","fillStyle","beginPath","arc","PI","restore","transferToImageBitmap","stripCFIAssertions","cfi","escaped","inAssertion","ch","documentCFI","sepIndex","FrameObserver","onFrameAdded","onFrameRemoved","_onFrameAdded","_onFrameRemoved","_annotatableFrames","_isDisconnected","_mutationObserver","_discoverFrames","frame","onNextDocumentReady","contentWindow","_removeFrame","frames","_addFrame","unsubscribe","onDocumentReady","doc","pollInterval","pollTimer","pollForDocumentChange","documents","WeakSet","cancelPoll","checkForDocumentChange","currentDocument","contentDocument","hasBlankDocumentThatWillNavigate","readyState","errorMessage","isConnected","canceled","initialCheckTimer","HypothesisInjector","_config","_frameObserver","injectClient","frameId","assetRoot","injectedConfig","configElement","innerText","bootScript","iframeDocument","ImageTextLayer","image","charBoxes","containerParent","textAlign","fontFamily","fontSize","font","scaledValue","dimension","unit","columns","words","currentWord","addWord","lines","currentLine","addLine","prevWord","prevCenter","xDist","currentColumn","addColumn","line","prevLine","yDist","analyzeLayout","column","columnEl","display","lineEl","whiteSpace","wordEl","transformOrigin","metrics","measureText","xScale","yScale","transform","updateTextLayerSize","imageWidth","imageHeight","_updateTextLayerSize","_imageSizeObserver","updateSync","findBookElement","document_","vitalSourceFrameRole","parentDoc","frameElement","VitalSourceInjector","bookElement","contentFrames","injectClientIntoContentFrame","getPDFPageImage","VitalSourceContentIntegration","_bookElement","htmlFeatures","_htmlIntegration","stopEvents","insertAdjacentElement","removeScrollingAttr","makeContentFrameScrollable","bookImage","pageData","innerPageData","charRects","glyphs","glyph","_textLayer","_getPageInfo","includeTitle","includePageIndex","extraSelectors","bookContainer","newWidth","getBookInfo","navigateToSegment","ann","goToCfi","goToURL","stripOrigin","persistFrame","pageInfo","toc","pages","getCurrentPage","getTOC","getPages","requiredFields","field","chapterTitle","pageCFI","tocEntry","entry","absoluteURL","pageBreaks","pageBreaksResponse","getPageBreaks","ok","cfiWithoutAssertions","segmentPageBreaks","_getPageRange","bookId","isbn","scrollToAnchor","createIntegration","OutsideAssignmentNotice","OutsideAssignmentNoticeController","_visible","setVisible","visible","SelectionObserver","isMouseDown","_pendingCallback","scheduleCallback","eventHandler","_cancelPendingCallback","_document","frameFillsAncestor","ancestor","itemForNode","checkedNodes","itemsForRange","_annotation","annotationsAtPoint","textHighlights","shapeHighlights","getHighlightsFromPoint","resolveAnchor","isRange","removeTextSelection","removeAllRanges","ScrollToRangeEvent","_ready","ready","waitUntil","Guest","_contentReady","_highlightsVisible","_isAdderVisible","_informHostOnNextSelectionClear","selectedRanges","_outsideAssignmentNotice","_adder","createAnnotationFromSelection","selectAnnotations","focusInSidebar","_drawTool","_selectionObserver","_onSelection","_onClearSelection","_annotations","_frameIdentifier","_portFinder","_featureFlagsReceived","_integration","_sendDocumentInfo","_clusterToolbar","_notifySupportedToolsChanged","_connectHost","_sidebarRPC","_sidebarLayout","_connectSidebar","_bucketBarClient","_setupElementEvents","_hoveredAnnotations","_handleShortcut","_repositionAdder","segmentInfo","persistent","waitForFeatureFlags","getDocumentInfo","createAnnotation","_hoverAnnotations","_scrollToAnnotation","hostPort","discover","_scrollToAnchor","setHighlightsVisible","_setOutsideAssignmentNoticeVisible","renderToBitmap","renderThumbnail","bitmap","$cluster","anchorStyles","borderWidth","anchorOuterBox","anchorBox","highlightShape","$orphan","_updateAnchors","concat","notify","draw","ranges","$highlight","annotatableRange","isBackwards","focusRect","notifyHost","highlightsVisible","hoveredAnnotationTags","addConfigFragment","baseURL","params","URLSearchParams","hash","createAppConfig","appURL","hostURL","service","onLoginRequest","onLoginRequestProvided","onLogoutRequest","onLogoutRequestProvided","onSignupRequest","onSignupRequestProvided","onProfileRequest","onProfileRequestProvided","onHelpRequest","onHelpRequestProvided","NativeDialog","closed","testId","dialogRef","showModal","dialogElement","FallbackDialog","ModalDialog","isModalDialogSupported","NotebookIframe","groupId","allow","NotebookModal","iframeKey","setIframeKey","setIsHidden","setGroupId","originalDocumentOverflowStyle","emitterRef","emitter","createEmitter","subscribe","publish","Notebook","ProfileModal","Profile","Buckets","above","below","buckets","onFocusAnnotations","onScrollToAnnotation","onSelectAnnotations","showUpNavigation","showDownNavigation","bucketTags","bottomAnchor","onMouseEnter","onMouseOut","bucket","topAnchor","BucketBar","_positions","flexGrow","_onFocusAnnotations","_onScrollToAnnotation","_onSelectAnnotations","anchorPositions","aboveAnchors","belowAnchors","currentBucket","newBucket","containerRect","relativePositions","aPos","center","isContainedWithin","updatedBottom","updatedHeight","vMargin","computeBuckets","toogle","setMessages","addMessage","newMessage","messageId","BaseToastMessages","buttonProps","StatusNotifier","Toolbar","closeSidebar","isSidebarOpen","sidebarContainerId","newAnnotationType","toggleHighlights","toggleSidebar","toggleSidebarRef","useMinimalControls","ToolbarController","setSidebarOpen","_useMinimalControls","_newAnnotationType","_sidebarOpen","_sidebarContainerId","_supportedAnnotationTools","_closeSidebar","_toggleSidebar","_toggleHighlights","_createAnnotation","_sidebarToggleButton","getWidth","minimal","sidebarOpen","sidebarToggleButton","supportedAnnotationTools","tools","DragHandler","threshold","onDrag","touchAction","_startX","_dragActive","_threshold","onCancel","Sidebar","_guestWithSelection","_guestRPC","iframe","sidebarURL","sidebarAppSrc","sidebarFrame","createSidebarIframe","bucketBar","iframeContainerId","toolbarContainer","toolbar","externalFrame","iframeContainer","bucketBarContainer","sidebarEdge","rpc","_hypothesisSidebar","_messagesElement","_toolbarWidth","_onResize","_dragResizeState","initial","final","toggleButton","_dragResizeHandler","_onDragSidebarToggleButton","serviceConfig","_layoutState","_updateLayoutState","_setupSidebarEvents","onFrameConnected","_connectGuest","guestRPC","rootEl","newCount","elems","showFn","triggerElems","triggerElem","sidebarTrigger","_resetDragResizeState","_updateLayout","_renderFrame","margin","computedStyle","frameVisibleWidth","layoutState","isResizing","pointerEvents","delta","round","Emitter","_subscriptions","subEvent","subCallback","EventBus","sidebarLinkElement","annotatorConfig","resolveUnloadRequested","unloadRequested","destroyables","appElements","removeExistingHypothesisAppElements","removeWorkaround","installPortCloseWorkaroundForSafari","sidebarConfig","portProvider","vitalSourceInjector","hypothesisInjector","guest","instance","removeTemporaryClientConfig"],"mappings":"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","x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,92,98,103]}
|