@internetarchive/bookreader 5.0.0-11 → 5.0.0-110

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (380) hide show
  1. package/BookReader/474.js +2 -0
  2. package/BookReader/474.js.map +1 -0
  3. package/BookReader/BookReader.css +604 -1239
  4. package/BookReader/BookReader.js +1 -1
  5. package/BookReader/BookReader.js.LICENSE.txt +20 -20
  6. package/BookReader/BookReader.js.map +1 -1
  7. package/BookReader/bergamot-translator-worker.js +2966 -0
  8. package/BookReader/bergamot-translator-worker.wasm +0 -0
  9. package/BookReader/hypothesis/LICENSE +50 -0
  10. package/BookReader/hypothesis/README.md +55 -0
  11. package/BookReader/hypothesis/build/boot.js +1 -0
  12. package/BookReader/hypothesis/build/manifest.json +20 -0
  13. package/BookReader/hypothesis/build/scripts/annotator.bundle.js +184 -0
  14. package/BookReader/hypothesis/build/scripts/annotator.bundle.js.map +1 -0
  15. package/BookReader/hypothesis/build/scripts/sidebar.bundle.js +798 -0
  16. package/BookReader/hypothesis/build/scripts/sidebar.bundle.js.map +1 -0
  17. package/BookReader/hypothesis/build/scripts/ui-playground.bundle.js +711 -0
  18. package/BookReader/hypothesis/build/scripts/ui-playground.bundle.js.map +1 -0
  19. package/BookReader/hypothesis/build/styles/annotator.css +2235 -0
  20. package/BookReader/hypothesis/build/styles/annotator.css.map +1 -0
  21. package/BookReader/hypothesis/build/styles/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  22. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  23. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  24. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  25. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  26. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Bold.woff2 +0 -0
  27. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  28. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Italic.woff2 +0 -0
  29. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Regular.woff2 +0 -0
  30. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  31. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Math-Italic.woff2 +0 -0
  32. package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  33. package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  34. package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  35. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Script-Regular.woff2 +0 -0
  36. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  37. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  38. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  39. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  40. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  41. package/BookReader/hypothesis/build/styles/highlights.css +2 -0
  42. package/BookReader/hypothesis/build/styles/highlights.css.map +1 -0
  43. package/BookReader/hypothesis/build/styles/katex.min.css +2 -0
  44. package/BookReader/hypothesis/build/styles/katex.min.css.map +1 -0
  45. package/BookReader/hypothesis/build/styles/pdfjs-overrides.css +2 -0
  46. package/BookReader/hypothesis/build/styles/pdfjs-overrides.css.map +1 -0
  47. package/BookReader/hypothesis/build/styles/sidebar.css +2731 -0
  48. package/BookReader/hypothesis/build/styles/sidebar.css.map +1 -0
  49. package/BookReader/hypothesis/build/styles/ui-playground.css +2659 -0
  50. package/BookReader/hypothesis/build/styles/ui-playground.css.map +1 -0
  51. package/BookReader/hypothesis/package.json +126 -0
  52. package/BookReader/ia-bookreader-bundle.js +1907 -0
  53. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +19 -0
  54. package/BookReader/ia-bookreader-bundle.js.map +1 -0
  55. package/BookReader/icons/1up.svg +1 -1
  56. package/BookReader/icons/2up.svg +1 -1
  57. package/BookReader/icons/advance.svg +1 -1
  58. package/BookReader/icons/chevron-right.svg +1 -1
  59. package/BookReader/icons/close-circle-dark.svg +1 -1
  60. package/BookReader/icons/close-circle.svg +1 -1
  61. package/BookReader/icons/fullscreen.svg +1 -1
  62. package/BookReader/icons/fullscreen_exit.svg +1 -1
  63. package/BookReader/icons/hamburger.svg +1 -1
  64. package/BookReader/icons/left-arrow.svg +1 -1
  65. package/BookReader/icons/magnify-minus.svg +1 -1
  66. package/BookReader/icons/magnify-plus.svg +1 -1
  67. package/BookReader/icons/magnify.svg +1 -1
  68. package/BookReader/icons/pause.svg +1 -1
  69. package/BookReader/icons/play.svg +1 -1
  70. package/BookReader/icons/playback-speed.svg +1 -1
  71. package/BookReader/icons/read-aloud.svg +1 -1
  72. package/BookReader/icons/review.svg +1 -1
  73. package/BookReader/icons/slider-toggle.svg +1 -0
  74. package/BookReader/icons/thumbnails.svg +1 -1
  75. package/BookReader/icons/voice.svg +1 -0
  76. package/BookReader/icons/volume-full.svg +1 -1
  77. package/BookReader/images/BRicons.svg +3 -3
  78. package/BookReader/images/books_graphic.svg +1 -1
  79. package/BookReader/images/hypothesis.ico +0 -0
  80. package/BookReader/images/icon_book.svg +1 -1
  81. package/BookReader/images/icon_bookmark.svg +1 -1
  82. package/BookReader/images/icon_experiment.svg +1 -0
  83. package/BookReader/images/icon_gear.svg +1 -1
  84. package/BookReader/images/icon_hamburger.svg +1 -1
  85. package/BookReader/images/icon_home.svg +1 -1
  86. package/BookReader/images/icon_info.svg +1 -1
  87. package/BookReader/images/icon_one_page.svg +1 -1
  88. package/BookReader/images/icon_pause.svg +1 -1
  89. package/BookReader/images/icon_play.svg +1 -1
  90. package/BookReader/images/icon_playback-rate.svg +1 -1
  91. package/BookReader/images/icon_search_button.svg +1 -1
  92. package/BookReader/images/icon_share.svg +1 -1
  93. package/BookReader/images/icon_skip-ahead.svg +1 -1
  94. package/BookReader/images/icon_skip-back.svg +1 -1
  95. package/BookReader/images/icon_speaker.svg +1 -1
  96. package/BookReader/images/icon_speaker_open.svg +1 -1
  97. package/BookReader/images/icon_thumbnails.svg +1 -1
  98. package/BookReader/images/icon_toc.svg +1 -1
  99. package/BookReader/images/icon_two_pages.svg +1 -1
  100. package/BookReader/images/marker_chap-off.svg +1 -1
  101. package/BookReader/images/marker_chap-on.svg +1 -1
  102. package/BookReader/images/marker_srch-on.svg +1 -1
  103. package/BookReader/images/translate.svg +1 -0
  104. package/BookReader/images/unviewable_page.png +0 -0
  105. package/BookReader/jquery-3.js +2 -0
  106. package/BookReader/jquery-3.js.LICENSE.txt +24 -0
  107. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  108. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  109. package/BookReader/plugins/plugin.autoplay.js +1 -1
  110. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  111. package/BookReader/plugins/plugin.chapters.js +22 -1
  112. package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -0
  113. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  114. package/BookReader/plugins/plugin.experiments.js +3 -0
  115. package/BookReader/plugins/plugin.experiments.js.LICENSE.txt +1 -0
  116. package/BookReader/plugins/plugin.experiments.js.map +1 -0
  117. package/BookReader/plugins/plugin.iframe.js +1 -1
  118. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  119. package/BookReader/plugins/plugin.iiif.js +2 -0
  120. package/BookReader/plugins/plugin.iiif.js.map +1 -0
  121. package/BookReader/plugins/plugin.resume.js +1 -1
  122. package/BookReader/plugins/plugin.resume.js.map +1 -1
  123. package/BookReader/plugins/plugin.search.js +2 -1
  124. package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
  125. package/BookReader/plugins/plugin.search.js.map +1 -1
  126. package/BookReader/plugins/plugin.text_selection.js +2 -1
  127. package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
  128. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  129. package/BookReader/plugins/plugin.translate.js +137 -0
  130. package/BookReader/plugins/plugin.translate.js.LICENSE.txt +1 -0
  131. package/BookReader/plugins/plugin.translate.js.map +1 -0
  132. package/BookReader/plugins/plugin.tts.js +1 -1
  133. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +2 -0
  134. package/BookReader/plugins/plugin.tts.js.map +1 -1
  135. package/BookReader/plugins/plugin.url.js +1 -1
  136. package/BookReader/plugins/plugin.url.js.map +1 -1
  137. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
  138. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  139. package/BookReader/plugins/translator-worker.js +2 -0
  140. package/BookReader/plugins/translator-worker.js.map +1 -0
  141. package/BookReader/silence.mp3 +0 -0
  142. package/BookReader/translator-worker.js +475 -0
  143. package/BookReader/webcomponents-bundle.js +3 -0
  144. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  145. package/BookReader/webcomponents-bundle.js.map +1 -0
  146. package/README.md +14 -3
  147. package/jsconfig.json +19 -0
  148. package/package.json +92 -70
  149. package/src/BookReader/BookModel.js +92 -46
  150. package/src/BookReader/DragScrollable.js +233 -0
  151. package/src/BookReader/ImageCache.js +49 -16
  152. package/src/BookReader/Mode1Up.js +66 -365
  153. package/src/BookReader/Mode1UpLit.js +392 -0
  154. package/src/BookReader/Mode2Up.js +87 -1315
  155. package/src/BookReader/Mode2UpLit.js +788 -0
  156. package/src/BookReader/ModeAbstract.js +43 -0
  157. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  158. package/src/BookReader/ModeSmoothZoom.js +312 -0
  159. package/src/BookReader/ModeThumb.js +29 -15
  160. package/src/BookReader/Navbar/Navbar.js +201 -76
  161. package/src/BookReader/PageContainer.js +120 -23
  162. package/src/BookReader/ReduceSet.js +2 -2
  163. package/src/BookReader/Toolbar/Toolbar.js +18 -40
  164. package/src/BookReader/events.js +3 -3
  165. package/src/BookReader/options.js +94 -17
  166. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  167. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  168. package/src/BookReader/utils/SelectionObserver.js +45 -0
  169. package/src/BookReader/utils/classes.js +1 -1
  170. package/src/BookReader/utils.js +136 -12
  171. package/src/BookReader.js +678 -1226
  172. package/src/BookReaderPlugin.js +52 -0
  173. package/src/assets/icons/magnify-minus.svg +3 -7
  174. package/src/assets/icons/magnify-plus.svg +3 -7
  175. package/src/assets/icons/slider-toggle.svg +1 -0
  176. package/src/assets/icons/voice.svg +1 -0
  177. package/src/assets/images/hypothesis.ico +0 -0
  178. package/src/assets/images/icon_experiment.svg +1 -0
  179. package/src/assets/images/translate.svg +1 -0
  180. package/src/assets/images/unviewable_page.png +0 -0
  181. package/src/assets/silence.mp3 +0 -0
  182. package/src/css/BookReader.scss +1 -5
  183. package/src/css/_BRBookmarks.scss +1 -1
  184. package/src/css/_BRComponent.scss +1 -1
  185. package/src/css/_BRicon.scss +8 -2
  186. package/src/css/_BRmain.scss +16 -3
  187. package/src/css/_BRnav.scss +74 -70
  188. package/src/css/_BRpages.scss +171 -42
  189. package/src/css/_BRsearch.scss +69 -30
  190. package/src/css/_BRtoolbar.scss +5 -5
  191. package/src/css/_TextSelection.scss +129 -24
  192. package/src/css/_colorbox.scss +2 -2
  193. package/src/css/_controls.scss +24 -7
  194. package/src/css/_icons.scss +8 -1
  195. package/src/{BookNavigator/assets → css}/button-base.js +2 -2
  196. package/src/css/icon_checkmark.js +9 -0
  197. package/src/css/sharedStyles.js +15 -0
  198. package/src/ia-bookreader/downloads/downloads-provider.js +81 -0
  199. package/src/{BookNavigator → ia-bookreader}/downloads/downloads.js +25 -5
  200. package/src/ia-bookreader/ia-bookreader.js +666 -0
  201. package/src/ia-bookreader/sharing.js +27 -0
  202. package/src/ia-bookreader/viewable-files.js +98 -0
  203. package/src/{BookNavigator → ia-bookreader}/visual-adjustments/visual-adjustments-provider.js +17 -17
  204. package/src/{BookNavigator → ia-bookreader}/visual-adjustments/visual-adjustments.js +75 -67
  205. package/src/{BookNavigator → plugins}/bookmarks/bookmark-button.js +4 -3
  206. package/src/{BookNavigator/assets → plugins/bookmarks}/bookmark-colors.js +1 -1
  207. package/src/{BookNavigator → plugins}/bookmarks/bookmark-edit.js +43 -31
  208. package/src/{BookNavigator → plugins}/bookmarks/bookmarks-list.js +48 -49
  209. package/src/{BookNavigator → plugins}/bookmarks/bookmarks-loginCTA.js +3 -3
  210. package/src/plugins/bookmarks/bookmarks-provider.js +63 -0
  211. package/src/{BookNavigator → plugins/bookmarks}/delete-modal-actions.js +1 -1
  212. package/src/{BookNavigator → plugins}/bookmarks/ia-bookmarks.js +117 -68
  213. package/src/plugins/plugin.archive_analytics.js +84 -78
  214. package/src/plugins/plugin.autoplay.js +99 -104
  215. package/src/plugins/plugin.chapters.js +314 -205
  216. package/src/plugins/plugin.experiments.js +321 -0
  217. package/src/plugins/plugin.iframe.js +1 -1
  218. package/src/plugins/plugin.iiif.js +141 -0
  219. package/src/plugins/plugin.resume.js +54 -51
  220. package/src/plugins/plugin.text_selection.js +522 -219
  221. package/src/plugins/plugin.vendor-fullscreen.js +5 -5
  222. package/src/plugins/search/plugin.search.js +374 -392
  223. package/src/{BookNavigator → plugins}/search/search-provider.js +59 -27
  224. package/src/{BookNavigator → plugins}/search/search-results.js +105 -76
  225. package/src/plugins/search/utils.js +50 -0
  226. package/src/plugins/search/view.js +50 -68
  227. package/src/plugins/translate/TranslationManager.js +164 -0
  228. package/src/plugins/translate/plugin.translate.js +512 -0
  229. package/src/plugins/tts/AbstractTTSEngine.js +78 -49
  230. package/src/plugins/tts/FestivalTTSEngine.js +20 -30
  231. package/src/plugins/tts/PageChunk.js +33 -21
  232. package/src/plugins/tts/PageChunkIterator.js +11 -17
  233. package/src/plugins/tts/WebTTSEngine.js +131 -91
  234. package/src/plugins/tts/plugin.tts.js +345 -350
  235. package/src/plugins/tts/utils.js +77 -49
  236. package/src/plugins/url/UrlPlugin.js +191 -0
  237. package/src/plugins/{plugin.url.js → url/plugin.url.js} +44 -15
  238. package/src/util/TextSelectionManager.js +282 -0
  239. package/src/util/browserSniffing.js +33 -1
  240. package/src/util/cache.js +20 -0
  241. package/src/util/docCookies.js +21 -2
  242. package/src/util/lit.js +15 -0
  243. package/src/util/strings.js +1 -0
  244. package/.babelrc +0 -12
  245. package/.dependabot/config.yml +0 -6
  246. package/.eslintrc.js +0 -50
  247. package/.gitattributes +0 -2
  248. package/.github/ISSUE_TEMPLATE/bug.md +0 -32
  249. package/.github/ISSUE_TEMPLATE/feature-request.md +0 -30
  250. package/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -15
  251. package/.github/workflows/node.js.yml +0 -37
  252. package/.github/workflows/npm-publish.yml +0 -47
  253. package/.testcaferc.json +0 -5
  254. package/BookReader/bookreader-component-bundle.js +0 -1450
  255. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  256. package/BookReader/bookreader-component-bundle.js.map +0 -1
  257. package/BookReader/jquery-1.10.1.js +0 -2
  258. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  259. package/BookReader/plugins/plugin.menu_toggle.js +0 -2
  260. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  261. package/BookReader/plugins/plugin.mobile_nav.js +0 -2
  262. package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
  263. package/BookReaderDemo/BookReaderDemo.css +0 -41
  264. package/BookReaderDemo/BookReaderJSAdvanced.js +0 -115
  265. package/BookReaderDemo/BookReaderJSAutoplay.js +0 -56
  266. package/BookReaderDemo/BookReaderJSSimple.js +0 -55
  267. package/BookReaderDemo/IIIFBookReader.js +0 -207
  268. package/BookReaderDemo/assets/v5/Bookreader-logo-cool-grad.svg +0 -1
  269. package/BookReaderDemo/assets/v5/Bookreader-logo-flat.svg +0 -1
  270. package/BookReaderDemo/assets/v5/Bookreader-logo-hex-cool-grad.png +0 -0
  271. package/BookReaderDemo/assets/v5/Bookreader-logo-hex-flat.png +0 -0
  272. package/BookReaderDemo/assets/v5/Bookreader-logo-lines.png +0 -0
  273. package/BookReaderDemo/assets/v5/Bookreader-logo-lines.svg +0 -1
  274. package/BookReaderDemo/assets/v5/Bookreader-logo-warm.svg +0 -1
  275. package/BookReaderDemo/assets/v5/bookreader-logo-renders@1x.png +0 -0
  276. package/BookReaderDemo/assets/v5/bookreader-logo-renders@2x.png +0 -0
  277. package/BookReaderDemo/assets/v5/bookreader-v5-screenshot.png +0 -0
  278. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  279. package/BookReaderDemo/demo-advanced.html +0 -33
  280. package/BookReaderDemo/demo-autoplay.html +0 -38
  281. package/BookReaderDemo/demo-embed-iframe-src.html +0 -84
  282. package/BookReaderDemo/demo-embed.html +0 -26
  283. package/BookReaderDemo/demo-fullscreen-mobile.html +0 -36
  284. package/BookReaderDemo/demo-fullscreen.html +0 -33
  285. package/BookReaderDemo/demo-iiif.html +0 -34
  286. package/BookReaderDemo/demo-iiif.js +0 -26
  287. package/BookReaderDemo/demo-internetarchive.html +0 -74
  288. package/BookReaderDemo/demo-multiple.html +0 -43
  289. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  290. package/BookReaderDemo/demo-preview-pages.html +0 -1092
  291. package/BookReaderDemo/demo-simple.html +0 -34
  292. package/BookReaderDemo/demo-vendor-fullscreen.html +0 -36
  293. package/BookReaderDemo/immersion-1up.html +0 -64
  294. package/BookReaderDemo/immersion-mode.html +0 -35
  295. package/BookReaderDemo/toggle_controls.html +0 -53
  296. package/BookReaderDemo/view_mode.html +0 -39
  297. package/BookReaderDemo/viewmode-cycle.html +0 -41
  298. package/CHANGELOG.md +0 -495
  299. package/CONTRIBUTING.md +0 -7
  300. package/codecov.yml +0 -17
  301. package/index.html +0 -31
  302. package/karma.conf.js +0 -23
  303. package/screenshot.png +0 -0
  304. package/scripts/postversion.js +0 -10
  305. package/scripts/preversion.js +0 -14
  306. package/scripts/version.js +0 -26
  307. package/src/BookNavigator/BookModel.js +0 -14
  308. package/src/BookNavigator/BookNavigator.js +0 -446
  309. package/src/BookNavigator/assets/book-loader.js +0 -27
  310. package/src/BookNavigator/assets/icon_checkmark.js +0 -6
  311. package/src/BookNavigator/assets/icon_close.js +0 -3
  312. package/src/BookNavigator/bookmarks/bookmarks-provider.js +0 -53
  313. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  314. package/src/BookNavigator/downloads/downloads-provider.js +0 -66
  315. package/src/BookNavigator/search/a-search-result.js +0 -55
  316. package/src/BookReader/DebugConsole.js +0 -54
  317. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  318. package/src/ItemNavigator/ItemNavigator.js +0 -376
  319. package/src/ItemNavigator/providers/sharing.js +0 -29
  320. package/src/css/_MobileNav.scss +0 -194
  321. package/src/dragscrollable-br.js +0 -261
  322. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  323. package/src/plugins/plugin.mobile_nav.js +0 -287
  324. package/tests/BookReader/BookModel.test.js +0 -312
  325. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  326. package/tests/BookReader/DebugConsole.test.js +0 -25
  327. package/tests/BookReader/ImageCache.test.js +0 -150
  328. package/tests/BookReader/Mode1Up.test.js +0 -164
  329. package/tests/BookReader/Mode2Up.test.js +0 -247
  330. package/tests/BookReader/Navbar/Navbar.test.js +0 -169
  331. package/tests/BookReader/PageContainer.test.js +0 -115
  332. package/tests/BookReader/ReduceSet.test.js +0 -38
  333. package/tests/BookReader/Toolbar/Toolbar.test.js +0 -26
  334. package/tests/BookReader/utils/classes.test.js +0 -88
  335. package/tests/BookReader/utils.test.js +0 -109
  336. package/tests/BookReader.options.test.js +0 -39
  337. package/tests/BookReader.test.js +0 -301
  338. package/tests/e2e/README.md +0 -75
  339. package/tests/e2e/autoplay.test.js +0 -13
  340. package/tests/e2e/base.test.js +0 -35
  341. package/tests/e2e/helpers/base.js +0 -263
  342. package/tests/e2e/helpers/debug.js +0 -13
  343. package/tests/e2e/helpers/desktopSearch.js +0 -72
  344. package/tests/e2e/helpers/mobileSearch.js +0 -85
  345. package/tests/e2e/helpers/mockSearch.js +0 -93
  346. package/tests/e2e/helpers/rightToLeft.js +0 -29
  347. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  348. package/tests/e2e/models/BookReader.js +0 -11
  349. package/tests/e2e/models/Navigation.js +0 -56
  350. package/tests/e2e/rightToLeft.test.js +0 -20
  351. package/tests/e2e/viewmode.test.js +0 -37
  352. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  353. package/tests/karma/BookNavigator/bookmarks/bookmark-edit.test.js +0 -133
  354. package/tests/karma/BookNavigator/bookmarks/bookmarks-list.test.js +0 -222
  355. package/tests/karma/BookNavigator/search/search-provider.test.js +0 -23
  356. package/tests/karma/BookNavigator/search/search-results.test.js +0 -240
  357. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  358. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  359. package/tests/plugins/plugin.archive_analytics.test.js +0 -23
  360. package/tests/plugins/plugin.autoplay.test.js +0 -52
  361. package/tests/plugins/plugin.chapters.test.js +0 -130
  362. package/tests/plugins/plugin.iframe.test.js +0 -42
  363. package/tests/plugins/plugin.mobile_nav.test.js +0 -66
  364. package/tests/plugins/plugin.resume.test.js +0 -98
  365. package/tests/plugins/plugin.text_selection.test.js +0 -203
  366. package/tests/plugins/plugin.url.test.js +0 -129
  367. package/tests/plugins/plugin.vendor-fullscreen.test.js +0 -65
  368. package/tests/plugins/search/plugin.search.test.js +0 -173
  369. package/tests/plugins/search/plugin.search.view.test.js +0 -106
  370. package/tests/plugins/tts/AbstractTTSEngine.test.js +0 -153
  371. package/tests/plugins/tts/FestivalTTSEngine.test.js +0 -59
  372. package/tests/plugins/tts/PageChunk.test.js +0 -57
  373. package/tests/plugins/tts/PageChunkIterator.test.js +0 -179
  374. package/tests/plugins/tts/WebTTSEngine.test.js +0 -126
  375. package/tests/plugins/tts/utils.test.js +0 -133
  376. package/tests/util/browserSniffing.test.js +0 -56
  377. package/tests/util/docCookies.test.js +0 -15
  378. package/tests/util/strings.test.js +0 -63
  379. package/tests/utils.js +0 -42
  380. package/webpack.config.js +0 -86
@@ -1,9 +1,6 @@
1
- /* global BookReader */
1
+ // @ts-check
2
2
  /**
3
3
  * Plugin for Archive.org book search
4
- * NOTE: This script must be loaded AFTER `plugin.mobile_nav.js`
5
- * as it mutates mobile nav drawer
6
- *
7
4
  * Events fired at various points throughout search processing are published
8
5
  * on the document DOM element. These can be subscribed to using jQuery's event
9
6
  * binding method `$.fn.on`. All of the events are prefixed with a BookReader
@@ -23,447 +20,432 @@
23
20
  * @event BookReader:SearchCanceled - When no results found. Receives
24
21
  * `instance`
25
22
  */
23
+ import { poll } from '../../BookReader/utils.js';
24
+ import { renderBoxesInPageContainerLayer } from '../../BookReader/PageContainer.js';
26
25
  import SearchView from './view.js';
26
+ import { marshallSearchResults } from './utils.js';
27
+ import { BookReaderPlugin } from '../../BookReaderPlugin.js';
28
+ import { applyVariables } from '../../util/strings.js';
29
+ import { toISO6391 } from '../tts/utils.js';
30
+ /** @typedef {import('../../BookReader/PageContainer').PageContainer} PageContainer */
31
+ /** @typedef {import('../../BookReader/BookModel').PageIndex} PageIndex */
32
+ /** @typedef {import('../../BookReader/BookModel').LeafNum} LeafNum */
33
+ /** @typedef {import('../../BookReader/BookModel').PageNumString} PageNumString */
34
+
35
+ // @ts-ignore
36
+ const BookReader = /** @type {typeof import('@/src/BookReader.js').default} */(window.BookReader);
37
+
38
+ export class SearchPlugin extends BookReaderPlugin {
39
+ options = {
40
+ enabled: true,
41
+ preTag: '{{{',
42
+ postTag: '}}}',
43
+ /**
44
+ * @type {import('@/src/util/strings.js').StringWithVars}
45
+ * Provides the variables: `query`, `preTag`, and `postTag` (from these options)
46
+ */
47
+ searchInsideUrl: '/fulltext/inside.php?' + [
48
+ 'q={{query|urlencode}}',
49
+ 'pre_tag={{preTag|urlencode}}',
50
+ 'post_tag={{postTag|urlencode}}',
51
+ ].join('&'),
52
+ /** @type {string} */
53
+ initialSearchTerm: null,
54
+ goToFirstResult: false,
55
+ }
27
56
 
28
- jQuery.extend(BookReader.defaultOptions, {
29
- server: 'ia600609.us.archive.org',
30
- bookId: '',
31
- subPrefix: '',
32
- bookPath: '',
33
- enableSearch: true,
34
- searchInsideUrl: '/fulltext/inside.php',
35
- initialSearchTerm: null,
36
- });
37
-
38
- /** @override */
39
- BookReader.prototype.setup = (function (super_) {
40
- return function (options) {
41
- super_.call(this, options);
57
+ /**
58
+ * @override
59
+ * @param {Partial<SearchPlugin['options']>} options
60
+ **/
61
+ setup(options) {
62
+ super.setup(options);
42
63
 
43
64
  this.searchTerm = '';
65
+ /** @type {SearchInsideResults | null} */
44
66
  this.searchResults = null;
45
- this.searchInsideUrl = options.searchInsideUrl;
46
- this.enableSearch = options.enableSearch;
47
-
48
- // Base server used by some api calls
49
- this.bookId = options.bookId;
50
- this.server = options.server;
51
- this.subPrefix = options.subPrefix;
52
- this.bookPath = options.bookPath;
53
-
67
+ this.searchInsideUrl = this.options.searchInsideUrl;
68
+ this.enabled = this.options.enabled;
54
69
  this.searchXHR = null;
55
- this._cancelSearch.bind(this);
56
- this.cancelSearchRequest.bind(this);
57
-
58
- if (this.searchView) { return; }
59
- this.searchView = new SearchView({
60
- br: this,
61
- searchCancelledCallback: () => {
62
- this._cancelSearch();
63
- this.trigger('SearchCanceled', { term: this.searchTerm, instance: this });
64
- }
65
- });
66
- };
67
- })(BookReader.prototype.setup);
68
70
 
69
- /** @override */
70
- BookReader.prototype.init = (function (super_) {
71
- return function () {
72
- super_.call(this);
71
+ /** @type { {[pageIndex: number]: SearchInsideMatchBox[]} } */
72
+ this._searchBoxesByIndex = {};
73
+
74
+ if (this.enabled) {
75
+ this.searchView = new SearchView({
76
+ br: this.br,
77
+ searchCancelledCallback: () => {
78
+ this._cancelSearch();
79
+ this.br.trigger('SearchCanceled', { term: this.searchTerm, instance: this.br });
80
+ },
81
+ });
82
+ } else {
83
+ this.searchView = null;
84
+ }
85
+ }
73
86
 
74
- if (this.options.enableSearch && this.options.initialSearchTerm) {
87
+ /** @override */
88
+ init() {
89
+ super.init();
90
+
91
+ if (!this.enabled) return;
92
+
93
+ this.searchView.init();
94
+ if (this.options.initialSearchTerm) {
95
+ /**
96
+ * this.search() take two parameter
97
+ * 1. this.options.initialSearchTerm - search term
98
+ * 2. {
99
+ * goToFirstResult: this.options.goToFirstResult,
100
+ * suppressFragmentChange: false // always want to change fragment in URL
101
+ * }
102
+ */
75
103
  this.search(
76
104
  this.options.initialSearchTerm,
77
- { goToFirstResult: this.options.goToFirstResult, suppressFragmentChange: true }
105
+ { goToFirstResult: this.options.goToFirstResult, suppressFragmentChange: false },
78
106
  );
79
107
  }
80
- };
81
- })(BookReader.prototype.init);
82
-
83
- /** @override */
84
- BookReader.prototype.buildToolbarElement = (function (super_) {
85
- return function () {
86
- const $el = super_.call(this);
87
- if (!this.enableSearch) { return; }
108
+ }
109
+
110
+ /**
111
+ * @override
112
+ * @protected
113
+ * @param {JQuery<HTMLElement>} $toolbarElement
114
+ */
115
+ _configureToolbar($toolbarElement) {
116
+ if (!this.enabled) { return; }
88
117
  if (this.searchView.dom.toolbarSearch) {
89
- $el.find('.BRtoolbarSectionInfo').after(this.searchView.dom.toolbarSearch);
118
+ $toolbarElement.find('.BRtoolbarSectionInfo').after(this.searchView.dom.toolbarSearch);
90
119
  }
91
- return $el;
92
- };
93
- })(BookReader.prototype.buildToolbarElement);
94
-
95
- /**
96
- * @typedef {object} SearchOptions
97
- * @property {boolean} goToFirstResult
98
- * @property {boolean} disablePopup
99
- * @property {(null|function)} error - @deprecated at v.5.0
100
- * @property {(null|function)} success - @deprecated at v.5.0
101
- */
102
-
103
- /**
104
- * Submits search request
105
- *
106
- * @param {string} term
107
- * @param {SearchOptions} overrides
108
- */
109
- BookReader.prototype.search = function(term = '', overrides = {}) {
110
- /** @type {SearchOptions} */
111
- const defaultOptions = {
112
- goToFirstResult: false, /* jump to the first result (default=false) */
113
- disablePopup: false, /* don't show the modal progress (default=false) */
114
- suppressFragmentChange: false, /* don't change the URL on initial load */
115
- error: null, /* optional error handler (default=null) */
116
- success: null, /* optional success handler (default=null) */
117
-
118
- };
119
- const options = jQuery.extend({}, defaultOptions, overrides);
120
- this.suppressFragmentChange = options.suppressFragmentChange;
121
-
122
- // strip slashes, since this goes in the url
123
- this.searchTerm = term.replace(/\//g, ' ');
124
-
125
- if (!options.suppressFragmentChange) {
126
- this.trigger(BookReader.eventNames.fragmentChange);
120
+ return $toolbarElement;
127
121
  }
128
122
 
129
- // Add quotes to the term. This is to compenstate for the backends default OR query
130
- // term = term.replace(/['"]+/g, '');
131
- // term = '"' + term + '"';
132
-
133
- // Remove the port and userdir
134
- const serverPath = this.server.replace(/:.+/, '');
135
- const baseUrl = `https://${serverPath}${this.searchInsideUrl}?`;
136
-
137
- // Remove subPrefix from end of path
138
- let path = this.bookPath;
139
- const subPrefixWithSlash = `/${this.subPrefix}`;
140
- if (this.bookPath.length - this.bookPath.lastIndexOf(subPrefixWithSlash) == subPrefixWithSlash.length) {
141
- path = this.bookPath.substr(0, this.bookPath.length - subPrefixWithSlash.length);
123
+ /**
124
+ * @override
125
+ * @param {import ("@/src/BookReader/PageContainer.js").PageContainer} pageContainer
126
+ */
127
+ _configurePageContainer(pageContainer) {
128
+ if (this.enabled && pageContainer.page && pageContainer.page.index in this._searchBoxesByIndex) {
129
+ const pageIndex = pageContainer.page.index;
130
+ const boxes = this._searchBoxesByIndex[pageIndex];
131
+ renderBoxesInPageContainerLayer(
132
+ 'searchHiliteLayer',
133
+ boxes,
134
+ pageContainer.page,
135
+ pageContainer.$container[0],
136
+ boxes.map(b => `match-index-${b.matchIndex}`),
137
+ );
138
+ }
139
+ return pageContainer;
142
140
  }
143
141
 
144
- const urlParams = {
145
- item_id: this.bookId,
146
- doc: this.subPrefix,
147
- path,
148
- q: term,
149
- };
142
+ /**
143
+ * Submits search request
144
+ *
145
+ * @param {string} term
146
+ * @param {Partial<SearchOptions>} overrides
147
+ */
148
+ async search(term = '', overrides = {}) {
149
+ /** @type {SearchOptions} */
150
+ const defaultOptions = {
151
+ goToFirstResult: false, /* jump to the first result (default=false) */
152
+ disablePopup: false, /* don't show the modal progress (default=false) */
153
+ suppressFragmentChange: false, /* don't change the URL on initial load */
154
+ error: null, /* optional error handler (default=null) */
155
+ success: null, /* optional success handler (default=null) */
156
+
157
+ };
158
+ const options = jQuery.extend({}, defaultOptions, overrides);
159
+ this.suppressFragmentChange = options.suppressFragmentChange;
160
+ this.searchCancelled = false;
161
+
162
+ // strip slashes, since this goes in the url
163
+ this.searchTerm = term.replace(/\//g, ' ');
164
+
165
+ if (!options.suppressFragmentChange) {
166
+ this.br.trigger(BookReader.eventNames.fragmentChange);
167
+ }
168
+
169
+ // Add quotes to the term. This is to compenstate for the backends default OR query
170
+ // term = term.replace(/['"]+/g, '');
171
+ // term = '"' + term + '"';
150
172
 
151
- // NOTE that the API does not expect / (slashes) to be encoded. (%2F) won't work
152
- const paramStr = $.param(urlParams).replace(/%2F/g, '/');
173
+ const url = applyVariables(this.options.searchInsideUrl, this.br.options.vars, {
174
+ query: term,
175
+ preTag: this.options.preTag,
176
+ postTag: this.options.postTag,
177
+ });
153
178
 
154
- const url = `${baseUrl}${paramStr}`;
179
+ const callSearchResultsCallback = (searchInsideResults) => {
180
+ if (this.searchCancelled) {
181
+ return;
182
+ }
183
+ const responseHasError = searchInsideResults.error || !searchInsideResults.matches.length;
184
+ const hasCustomError = typeof options.error === 'function';
185
+ const hasCustomSuccess = typeof options.success === 'function';
186
+
187
+ if (responseHasError) {
188
+ console.error('Search Inside Response Error', searchInsideResults.error || 'matches.length == 0');
189
+ hasCustomError
190
+ ? options.error.call(this, searchInsideResults, options)
191
+ : this.BRSearchCallbackError(searchInsideResults);
192
+ } else {
193
+ hasCustomSuccess
194
+ ? options.success.call(this, searchInsideResults, options)
195
+ : this.BRSearchCallback(searchInsideResults, options);
196
+ }
197
+ };
198
+
199
+ this.br.trigger('SearchStarted', { term: this.searchTerm, instance: this.br });
200
+ callSearchResultsCallback(await $.ajax({
201
+ url: url,
202
+ cache: true,
203
+ xhrFields: {
204
+ withCredentials: this.br.protected,
205
+ },
206
+ beforeSend: xhr => { this.searchXHR = xhr; },
207
+ }));
208
+ }
155
209
 
156
- const cleanup = () => {
210
+ /**
211
+ * cancels AJAX Call
212
+ * emits custom event
213
+ */
214
+ _cancelSearch() {
215
+ this.searchXHR?.abort();
216
+ this.searchView.clearSearchFieldAndResults(false);
217
+ this.searchTerm = '';
157
218
  this.searchXHR = null;
158
- window.BRSearchInProgress = () => {};
159
- };
219
+ this.searchCancelled = true;
220
+ this.searchResults = null;
221
+ }
160
222
 
161
- const processSearchResults = (searchInsideResults) => {
162
- if (!this.searchXHR) {
163
- return;
164
- }
165
- const responseHasError = searchInsideResults.error || !searchInsideResults.matches.length;
166
- const hasCustomError = typeof options.error === 'function';
167
- const hasCustomSuccess = typeof options.success === 'function';
168
-
169
- if (responseHasError) {
170
- hasCustomError
171
- ? options.error.call(this, searchInsideResults, options)
172
- : this.BRSearchCallbackError(searchInsideResults, options);
173
- } else {
174
- hasCustomSuccess
175
- ? options.success.call(this, searchInsideResults, options)
176
- : this.BRSearchCallback(searchInsideResults, options);
223
+ /**
224
+ * External function to cancel search
225
+ * checks for term & xhr in flight before running
226
+ */
227
+ cancelSearchRequest() {
228
+ this.searchCancelled = true;
229
+ if (this.searchXHR !== null) {
230
+ this._cancelSearch();
231
+ this.searchView.toggleSearchPending();
232
+ this.br.trigger('SearchCanceled', { term: this.searchTerm, instance: this.br });
177
233
  }
178
- cleanup();
179
- };
180
-
181
- const beforeSend = (xhr) => {
182
- this.searchXHR = xhr;
183
- window.BRSearchInProgress = processSearchResults;
184
- };
185
-
186
- this.trigger('SearchStarted', { term: this.searchTerm, instance: this });
187
- return $.ajax({
188
- url: url,
189
- dataType: 'jsonp',
190
- beforeSend,
191
- jsonpCallback: 'BRSearchInProgress'
192
- }).then(processSearchResults);
193
- };
194
-
195
- /**
196
- * cancels AJAX Call
197
- * emits custom event
198
- */
199
- BookReader.prototype._cancelSearch = function () {
200
- this.searchXHR?.abort();
201
- this.searchView.clearSearchFieldAndResults(false);
202
- this.searchTerm = '';
203
- this.searchXHR = null;
204
- this.searchResults = [];
205
- window.BRSearchInProgress = () => {};
206
- };
207
-
208
- /**
209
- * External function to cancel search
210
- * checks for term & xhr in flight before running
211
- */
212
- BookReader.prototype.cancelSearchRequest = function () {
213
- if (this.searchXHR !== null) {
214
- this._cancelSearch();
215
- this.searchView.toggleSearchPending();
216
- this.trigger('SearchCanceled', { term: this.searchTerm, instance: this });
217
234
  }
218
- };
219
-
220
- /**
221
- * @typedef {object} SearchInsideMatchBox
222
- * @property {number} page
223
- * @property {number} r
224
- * @property {number} l
225
- * @property {number} b
226
- * @property {number} t
227
- * @property {HTMLDivElement} [div]
228
- */
229
235
 
230
- /**
231
- * @typedef {object} SearchInsideMatch
232
- * @property {string} text
233
- * @property {Array<{ page: number, boxes: SearchInsideMatchBox[] }>} par
234
- */
236
+ /**
237
+ * Search Results return handler
238
+ * @param {SearchInsideResults} results
239
+ * @param {object} options
240
+ * @param {boolean} options.goToFirstResult
241
+ */
242
+ BRSearchCallback(results, options) {
243
+ const bookLangCode = toISO6391(this.br.options.bookLanguage);
244
+ marshallSearchResults(
245
+ results,
246
+ pageNum => this.br.book.getPageNum(this.br.book.leafNumToIndex(pageNum)),
247
+ this.options.preTag,
248
+ this.options.postTag,
249
+ bookLangCode,
250
+ );
251
+ this.searchResults = results || null;
252
+
253
+ this.updateSearchHilites();
254
+ this.br.removeProgressPopup();
255
+ if (options.goToFirstResult) {
256
+ this.jumpToMatch(0);
257
+ }
258
+ this.br.trigger('SearchCallback', { results, options, instance: this.br });
259
+ }
235
260
 
236
- /**
237
- * @typedef {object} SearchInsideResults
238
- * @property {string} error
239
- * @property {SearchInsideMatch[]} matches
240
- * @property {boolean} indexed
241
- */
261
+ /**
262
+ * Main search results error handler
263
+ * @param {SearchInsideResults} results
264
+ */
265
+ BRSearchCallbackError(results) {
266
+ this._BRSearchCallbackError(results);
267
+ }
242
268
 
243
- /**
244
- * Search Results return handler
245
- * @callback
246
- * @param {SearchInsideResults} results
247
- * @param {object} options
248
- * @param {boolean} options.goToFirstResult
249
- */
250
- BookReader.prototype.BRSearchCallback = function(results, options) {
251
- this.searchResults = results || [];
252
-
253
- this.updateSearchHilites();
254
- this.removeProgressPopup();
255
- if (options.goToFirstResult) {
256
- const pageIndex = this._models.book.leafNumToIndex(results.matches[0].par[0].page);
257
- this._searchPluginGoToResult(pageIndex);
269
+ /**
270
+ * @private draws search results error
271
+ * @param {SearchInsideResults} results
272
+ */
273
+ _BRSearchCallbackError(results) {
274
+ this.searchResults = results;
275
+ const payload = {
276
+ term: this.searchTerm,
277
+ results,
278
+ instance: this.br,
279
+ };
280
+ if (results.error) {
281
+ this.br.trigger('SearchCallbackError', payload);
282
+ } else if (0 == results.matches.length) {
283
+ if (false === results.indexed) {
284
+ this.br.trigger('SearchCallbackBookNotIndexed', payload);
285
+ return;
286
+ }
287
+ this.br.trigger('SearchCallbackEmpty', payload);
288
+ }
258
289
  }
259
- this.trigger('SearchCallback', { results, options, instance: this });
260
- };
261
290
 
262
- /**
263
- * Main search results error handler
264
- * @callback
265
- * @param {SearchInsideResults} results
266
- */
267
- BookReader.prototype.BRSearchCallbackError = function(results) {
268
- this._BRSearchCallbackError(results);
269
- };
291
+ /**
292
+ * updates search on-page highlights controller
293
+ */
294
+ updateSearchHilites() {
295
+ /** @type {SearchInsideMatch[]} */
296
+ const matches = this.searchResults?.matches || [];
297
+ /** @type { {[pageIndex: number]: SearchInsideMatchBox[]} } */
298
+ const boxesByIndex = {};
299
+
300
+ // Clear any existing svg layers
301
+ this.removeSearchHilites();
302
+
303
+ // Group by pageIndex
304
+ for (const match of matches) {
305
+ for (const box of match.par[0].boxes) {
306
+ const pageIndex = this.br.book.leafNumToIndex(box.page);
307
+ const pageBoxes = boxesByIndex[pageIndex] || (boxesByIndex[pageIndex] = []);
308
+ pageBoxes.push(box);
309
+ }
310
+ }
270
311
 
271
- /**
272
- * @private draws search results error
273
- * @callback
274
- * @param {SearchInsideResults} results
275
- * @param {jQuery} $el
276
- * @param {boolean} fade
277
- */
278
- BookReader.prototype._BRSearchCallbackError = function(results) {
279
- this.searchResults = results;
280
- const basePayload = {
281
- term: this.searchTerm,
282
- instance: this,
283
- };
284
- if (results.error) {
285
- const payload = Object.assign({}, basePayload, { results });
286
- this.trigger('SearchCallbackError', payload);
287
- } else if (0 == results.matches.length) {
288
- if (false === results.indexed) {
289
- this.trigger('SearchCallbackBookNotIndexed', basePayload);
290
- return;
312
+ // update any already created pages
313
+ for (const [pageIndexString, boxes] of Object.entries(boxesByIndex)) {
314
+ const pageIndex = parseFloat(pageIndexString);
315
+ const page = this.br.book.getPage(pageIndex);
316
+ const pageContainers = this.br.getActivePageContainerElementsForIndex(pageIndex);
317
+ for (const container of pageContainers) {
318
+ renderBoxesInPageContainerLayer('searchHiliteLayer', boxes, page, container, boxes.map(b => `match-index-${b.matchIndex}`));
319
+ }
291
320
  }
292
- this.trigger('SearchCallbackEmpty', basePayload);
321
+
322
+ this._searchBoxesByIndex = boxesByIndex;
293
323
  }
294
- };
295
324
 
296
- /**
297
- * updates search on-page highlights controller
298
- */
299
- BookReader.prototype.updateSearchHilites = function() {
300
- if (this.constMode2up == this.mode) {
301
- this.updateSearchHilites2UP();
302
- return;
325
+ /**
326
+ * remove search highlights
327
+ */
328
+ removeSearchHilites() {
329
+ $(this.br.getActivePageContainerElements()).find('.searchHiliteLayer').remove();
303
330
  }
304
- this.updateSearchHilites1UP();
305
- };
306
331
 
307
- /**
308
- * update search on-page highlights in 1up mode
309
- */
310
- BookReader.prototype.updateSearchHilites1UP = function() {
311
- const results = this.searchResults;
312
- if (null == results) return;
313
- results.matches?.forEach(match => {
314
- match.par[0].boxes.forEach(box => {
315
- const pageIndex = this.leafNumToIndex(box.page);
316
- const pageIsInView = jQuery.inArray(pageIndex, this.displayedIndices) >= 0;
317
- if (pageIsInView) {
318
- if (!box.div) {
319
- //create a div for the search highlight, and stash it in the box object
320
- box.div = document.createElement('div');
321
- $(box.div).prop('className', 'BookReaderSearchHilite').appendTo(this.$(`.pagediv${pageIndex}`));
322
- }
323
- const page = this._models.book.getPage(pageIndex);
324
- const highlight = {
325
- width: this._modes.mode1Up.physicalInchesToDisplayPixels((box.r - box.l) / page.ppi),
326
- height: this._modes.mode1Up.physicalInchesToDisplayPixels((box.b - box.t) / page.ppi),
327
- left: this._modes.mode1Up.physicalInchesToDisplayPixels(box.l / page.ppi),
328
- top: this._modes.mode1Up.physicalInchesToDisplayPixels(box.t / page.ppi),
329
- };
330
- $(box.div).css(highlight);
331
- } else {
332
- if (box.div) {
333
- $(box.div).remove();
334
- box.div = null;
335
- }
332
+ /**
333
+ * Goes to the page specified. If the page is not viewable, tries to load the page
334
+ * FIXME Most of this logic is IA specific, and should be less integrated into here
335
+ * or at least more configurable.
336
+ * @param {number} matchIndex
337
+ */
338
+ async jumpToMatch(matchIndex) {
339
+ const match = this.searchResults?.matches[matchIndex];
340
+ const book = this.br.book;
341
+ const pageIndex = book.leafNumToIndex(match.par[0].page);
342
+ const page = book.getPage(pageIndex);
343
+ const onNearbyPage = Math.abs(this.br.currentIndex() - pageIndex) < 3;
344
+ let makeUnviewableAtEnd = false;
345
+ if (!page.isViewable) {
346
+ const resp = await fetch('/services/bookreader/request_page?' + new URLSearchParams({
347
+ id: this.br.options.bookId,
348
+ subprefix: this.br.options.subPrefix,
349
+ leafNum: page.leafNum,
350
+ })).then(r => r.json());
351
+
352
+ for (const leafNum of resp.value) {
353
+ book.getPage(book.leafNumToIndex(leafNum)).makeViewable();
336
354
  }
337
- });
338
- });
339
- };
340
355
 
341
- /**
342
- * update search on-page highlights in 2up mode
343
- */
344
- BookReader.prototype.updateSearchHilites2UP = function() {
345
- const results = this.searchResults;
346
-
347
- if (results === null) return;
348
-
349
- const { matches = [] } = results;
350
- matches.forEach((match) => {
351
- match.par[0].boxes.forEach(box => {
352
- const pageIndex = this.leafNumToIndex(match.par[0].page);
353
- const pageIsInView = jQuery.inArray(pageIndex, this.displayedIndices) >= 0;
354
- const { isViewable } = this._models.book.getPage(pageIndex);
355
-
356
- if (pageIsInView && isViewable) {
357
- if (!box.div) {
358
- //create a div for the search highlight, and stash it in the box object
359
- box.div = document.createElement('div');
360
- $(box.div).addClass('BookReaderSearchHilite')
361
- .appendTo(this.refs.$brTwoPageView);
362
- }
363
- this.setHilightCss2UP(box.div, pageIndex, box.l, box.r, box.t, box.b);
364
- } else {
365
- // clear stale reference
366
- if (box.div) {
367
- $(box.div).remove();
368
- box.div = null;
369
- }
356
+ // not able to show page; make the page viewable anyways so that it can
357
+ // actually open. On IA, it has a fallback to a special error page.
358
+ if (!resp.value.length) {
359
+ book.getPage(pageIndex).makeViewable();
360
+ makeUnviewableAtEnd = true;
370
361
  }
371
- });
372
- });
373
- };
374
362
 
375
- /**
376
- * remove search highlights
377
- */
378
- BookReader.prototype.removeSearchHilites = function() {
379
- const results = this.searchResults;
380
- if (null == results || !results.matches) { return; }
381
- results.matches.forEach(match => {
382
- match.par[0].boxes.forEach(box => {
383
- if (null != box.div) {
384
- $(box.div).remove();
385
- box.div = null;
363
+ // Trigger an update of book
364
+ this.br._modes.mode1Up.mode1UpLit.updatePages();
365
+ if (this.br.activeMode == this.br._modes.mode1Up) {
366
+ await this.br._modes.mode1Up.mode1UpLit.updateComplete;
386
367
  }
387
- });
388
- });
389
- };
368
+ }
369
+ /* this updates the URL */
370
+ if (!this.br._isIndexDisplayed(pageIndex)) {
371
+ this.suppressFragmentChange = false;
372
+ this.br.jumpToIndex(pageIndex, { ariaLive: true });
373
+ }
390
374
 
391
- /**
392
- * @private
393
- * Goes to the page specified. If the page is not viewable, tries to load the page
394
- * FIXME Most of this logic is IA specific, and should be less integrated into here
395
- * or at least more configurable.
396
- * @param {PageIndex} pageIndex
397
- */
398
- BookReader.prototype._searchPluginGoToResult = async function (pageIndex) {
399
- const { book } = this._models;
400
- const page = book.getPage(pageIndex);
401
- let makeUnviewableAtEnd = false;
402
- if (!page.isViewable) {
403
- const resp = await fetch('/services/bookreader/request_page?' + new URLSearchParams({
404
- id: this.options.bookId,
405
- subprefix: this.options.subPrefix,
406
- leafNum: page.leafNum,
407
- })).then(r => r.json());
408
-
409
- for (const leafNum of resp.value) {
410
- book.getPage(book.leafNumToIndex(leafNum)).makeViewable();
375
+ // Reset it to unviewable if it wasn't resolved
376
+ if (makeUnviewableAtEnd) {
377
+ book.getPage(pageIndex).makeViewable(false);
411
378
  }
412
379
 
413
- // not able to show page; make the page viewable anyways so that it can
414
- // actually open. On IA, it has a fallback to a special error page.
415
- if (!resp.value.length) {
416
- book.getPage(pageIndex).makeViewable();
417
- makeUnviewableAtEnd = true;
380
+ // Scroll/flash in the ui
381
+ const $boxes = await poll(() => $(`rect.match-index-${match.matchIndex}`), { until: result => result.length > 0 });
382
+ if ($boxes.length) {
383
+ $boxes.css('animation', 'none');
384
+ $boxes[0].scrollIntoView({
385
+ // Only vertically center the highlight if we're in 1up or in full screen. In
386
+ // 2up, if we're not fullscreen, the whole body gets scrolled around to try to
387
+ // center the highlight 🙄 See:
388
+ // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move/11041376
389
+ // Note: nearest doesn't quite work great, because the ReadAloud toolbar is now
390
+ // full-width, and covers up the last line of the highlight.
391
+ block: this.br.constMode1up == this.br.mode || this.br.isFullscreenActive ? 'center' : 'nearest',
392
+ inline: 'center',
393
+ behavior: onNearbyPage ? 'smooth' : 'auto',
394
+ });
395
+ // wait for animation to start
396
+ await new Promise(resolve => setTimeout(resolve, 100));
397
+ $boxes.removeAttr("style");
418
398
  }
419
399
  }
420
- /* this updates the URL */
421
- this.suppressFragmentChange = false;
422
- this.jumpToIndex(pageIndex);
423
400
 
424
- // Reset it to unviewable if it wasn't resolved
425
- if (makeUnviewableAtEnd) {
426
- book.getPage(pageIndex).makeViewable(false);
401
+ /**
402
+ * Removes all search pins
403
+ */
404
+ removeSearchResults(suppressFragmentChange = false) {
405
+ this.removeSearchHilites(); //be sure to set all box.divs to null
406
+ this.searchTerm = null;
407
+ this.searchResults = null;
408
+ if (!suppressFragmentChange) {
409
+ this.br.trigger(BookReader.eventNames.fragmentChange);
410
+ }
427
411
  }
428
- };
412
+ }
413
+ BookReader?.registerPlugin('search', SearchPlugin);
429
414
 
430
415
  /**
431
- * Removes all search pins
416
+ * @typedef {object} SearchOptions
417
+ * @property {boolean} goToFirstResult
418
+ * @property {boolean} disablePopup
419
+ * @property {boolean} suppressFragmentChange
420
+ * @property {(null|function)} error (deprecated)
421
+ * @property {(null|function)} success (deprecated)
432
422
  */
433
- BookReader.prototype.removeSearchResults = function(suppressFragmentChange = false) {
434
- this.removeSearchHilites(); //be sure to set all box.divs to null
435
- this.searchTerm = null;
436
- this.searchResults = null;
437
- if (!suppressFragmentChange) {
438
- this.trigger(BookReader.eventNames.fragmentChange);
439
- }
440
- };
441
423
 
442
424
  /**
443
- * Returns true if a search highlight is currently being displayed
444
- * @returns {boolean}
425
+ * @typedef {object} SearchInsideMatchBox
426
+ * @property {number} page
427
+ * @property {number} r
428
+ * @property {number} l
429
+ * @property {number} b
430
+ * @property {number} t
431
+ * @property {HTMLDivElement} [div]
432
+ * @property {number} matchIndex This is a fake field! not part of the API response. The index of the match that contains this box in total search results matches.
445
433
  */
446
- BookReader.prototype.searchHighlightVisible = function() {
447
- const results = this.searchResults;
448
- let visiblePages = [];
449
- if (null == results) return false;
450
-
451
- if (this.constMode2up == this.mode) {
452
- visiblePages = [this.twoPage.currentIndexL, this.twoPage.currentIndexR];
453
- } else if (this.constMode1up == this.mode) {
454
- visiblePages = [this.currentIndex()];
455
- } else {
456
- return false;
457
- }
458
434
 
459
- results.matches.some(match => {
460
- return match.par[0].boxes.some(box => {
461
- const pageIndex = this.leafNumToIndex(box.page);
462
- if (jQuery.inArray(pageIndex, visiblePages) >= 0) {
463
- return true;
464
- }
465
- });
466
- });
435
+ /**
436
+ * @typedef {object} SearchInsideMatch
437
+ * @property {number} matchIndex This is a fake field! Not part of the API response. It is added by the JS.
438
+ * @property {string} displayPageNumber (fake field) The page number as it should be displayed in the UI.
439
+ * @property {string} html (computed field) The html-escaped raw html to display in the UI.
440
+ * @property {string} text
441
+ * @property {string} lang (fake field) The ISO 639-1 language code of the book
442
+ * @property {Array<{ page: number, boxes: SearchInsideMatchBox[] }>} par
443
+ */
467
444
 
468
- return false;
469
- };
445
+ /**
446
+ * @typedef {object} SearchInsideResults
447
+ * @property {string} q
448
+ * @property {string} error
449
+ * @property {SearchInsideMatch[]} matches
450
+ * @property {boolean} indexed
451
+ */