@internetarchive/bookreader 5.0.0-10-alpha-3 → 5.0.0-101

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