@internetarchive/bookreader 5.0.0-12-fixsorting-7 → 5.0.0-120

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 (420) hide show
  1. package/BookReader/474.js +2 -0
  2. package/BookReader/474.js.map +1 -0
  3. package/BookReader/BookReader.css +670 -1190
  4. package/BookReader/bergamot-translator-worker.js +2966 -0
  5. package/BookReader/bergamot-translator-worker.wasm +0 -0
  6. package/BookReader/hypothesis/LICENSE +50 -0
  7. package/BookReader/hypothesis/README.md +55 -0
  8. package/BookReader/hypothesis/build/boot-template.js +1 -0
  9. package/BookReader/hypothesis/build/boot.js +1 -0
  10. package/BookReader/hypothesis/build/manifest.json +20 -0
  11. package/BookReader/hypothesis/build/scripts/annotator.bundle.js +199 -0
  12. package/BookReader/hypothesis/build/scripts/annotator.bundle.js.map +1 -0
  13. package/BookReader/hypothesis/build/scripts/sidebar.bundle.js +811 -0
  14. package/BookReader/hypothesis/build/scripts/sidebar.bundle.js.map +1 -0
  15. package/BookReader/hypothesis/build/scripts/ui-playground.bundle.js +437 -0
  16. package/BookReader/hypothesis/build/scripts/ui-playground.bundle.js.map +1 -0
  17. package/BookReader/hypothesis/build/styles/annotator.css +4 -0
  18. package/BookReader/hypothesis/build/styles/annotator.css.map +1 -0
  19. package/BookReader/hypothesis/build/styles/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  20. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  21. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  22. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  23. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  24. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Bold.woff2 +0 -0
  25. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  26. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Italic.woff2 +0 -0
  27. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Regular.woff2 +0 -0
  28. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  29. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Math-Italic.woff2 +0 -0
  30. package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  31. package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  32. package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  33. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Script-Regular.woff2 +0 -0
  34. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  35. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  36. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  37. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  38. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  39. package/BookReader/hypothesis/build/styles/highlights.css +2 -0
  40. package/BookReader/hypothesis/build/styles/highlights.css.map +1 -0
  41. package/BookReader/hypothesis/build/styles/katex.min.css +3 -0
  42. package/BookReader/hypothesis/build/styles/katex.min.css.map +1 -0
  43. package/BookReader/hypothesis/build/styles/pdfjs-overrides.css +14 -0
  44. package/BookReader/hypothesis/build/styles/pdfjs-overrides.css.map +1 -0
  45. package/BookReader/hypothesis/build/styles/sidebar.css +4 -0
  46. package/BookReader/hypothesis/build/styles/sidebar.css.map +1 -0
  47. package/BookReader/hypothesis/build/styles/ui-playground.css +4 -0
  48. package/BookReader/hypothesis/build/styles/ui-playground.css.map +1 -0
  49. package/BookReader/hypothesis/package.json +126 -0
  50. package/BookReader/ia-bookreader.js +1886 -0
  51. package/BookReader/{BookReader.js.LICENSE.txt → ia-bookreader.js.LICENSE.txt} +26 -20
  52. package/BookReader/ia-bookreader.js.map +1 -0
  53. package/BookReader/icons/1up.svg +1 -12
  54. package/BookReader/icons/2up.svg +1 -15
  55. package/BookReader/icons/advance.svg +3 -26
  56. package/BookReader/icons/chevron-right.svg +1 -1
  57. package/BookReader/icons/close-circle-dark.svg +1 -1
  58. package/BookReader/icons/close-circle.svg +1 -1
  59. package/BookReader/icons/fullscreen.svg +1 -17
  60. package/BookReader/icons/fullscreen_exit.svg +1 -17
  61. package/BookReader/icons/hamburger.svg +1 -15
  62. package/BookReader/icons/left-arrow.svg +1 -12
  63. package/BookReader/icons/magnify-minus.svg +1 -16
  64. package/BookReader/icons/magnify-plus.svg +1 -17
  65. package/BookReader/icons/magnify.svg +1 -15
  66. package/BookReader/icons/pause.svg +1 -23
  67. package/BookReader/icons/play.svg +1 -22
  68. package/BookReader/icons/playback-speed.svg +1 -34
  69. package/BookReader/icons/read-aloud.svg +1 -22
  70. package/BookReader/icons/review.svg +3 -22
  71. package/BookReader/icons/slider-toggle.svg +1 -0
  72. package/BookReader/icons/thumbnails.svg +1 -17
  73. package/BookReader/icons/voice.svg +1 -0
  74. package/BookReader/icons/volume-full.svg +1 -22
  75. package/BookReader/images/BRicons.svg +5 -94
  76. package/BookReader/images/books_graphic.svg +1 -177
  77. package/BookReader/images/hypothesis.ico +0 -0
  78. package/BookReader/images/icon_book.svg +1 -12
  79. package/BookReader/images/icon_bookmark.svg +1 -12
  80. package/BookReader/images/icon_experiment.svg +1 -0
  81. package/BookReader/images/icon_gear.svg +1 -14
  82. package/BookReader/images/icon_hamburger.svg +1 -20
  83. package/BookReader/images/icon_home.svg +1 -21
  84. package/BookReader/images/icon_info.svg +1 -11
  85. package/BookReader/images/icon_one_page.svg +1 -8
  86. package/BookReader/images/icon_pause.svg +1 -1
  87. package/BookReader/images/icon_play.svg +1 -1
  88. package/BookReader/images/icon_playback-rate.svg +1 -15
  89. package/BookReader/images/icon_search_button.svg +1 -8
  90. package/BookReader/images/icon_share.svg +1 -9
  91. package/BookReader/images/icon_skip-ahead.svg +1 -6
  92. package/BookReader/images/icon_skip-back.svg +2 -13
  93. package/BookReader/images/icon_speaker.svg +1 -18
  94. package/BookReader/images/icon_speaker_open.svg +1 -10
  95. package/BookReader/images/icon_thumbnails.svg +1 -12
  96. package/BookReader/images/icon_toc.svg +1 -5
  97. package/BookReader/images/icon_two_pages.svg +1 -9
  98. package/BookReader/images/marker_chap-off.svg +1 -11
  99. package/BookReader/images/marker_chap-on.svg +1 -11
  100. package/BookReader/images/marker_srch-on.svg +1 -11
  101. package/BookReader/images/translate.svg +1 -0
  102. package/BookReader/images/unviewable_page.png +0 -0
  103. package/BookReader/jquery-3.js +2 -0
  104. package/BookReader/jquery-3.js.LICENSE.txt +24 -0
  105. package/BookReader/plugins/plugin.archive_analytics.js +1 -172
  106. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  107. package/BookReader/plugins/plugin.autoplay.js +1 -165
  108. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  109. package/BookReader/plugins/plugin.chapters.js +19 -301
  110. package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -0
  111. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  112. package/BookReader/plugins/plugin.experiments.js +3 -0
  113. package/BookReader/plugins/plugin.experiments.js.LICENSE.txt +1 -0
  114. package/BookReader/plugins/plugin.experiments.js.map +1 -0
  115. package/BookReader/plugins/plugin.iframe.js +1 -74
  116. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  117. package/BookReader/plugins/plugin.iiif.js +2 -0
  118. package/BookReader/plugins/plugin.iiif.js.map +1 -0
  119. package/BookReader/plugins/plugin.resume.js +1 -368
  120. package/BookReader/plugins/plugin.resume.js.map +1 -1
  121. package/BookReader/plugins/plugin.search.js +2 -1385
  122. package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
  123. package/BookReader/plugins/plugin.search.js.map +1 -1
  124. package/BookReader/plugins/plugin.text_selection.js +2 -1080
  125. package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
  126. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  127. package/BookReader/plugins/plugin.translate.js +3 -0
  128. package/BookReader/plugins/plugin.translate.js.LICENSE.txt +1 -0
  129. package/BookReader/plugins/plugin.translate.js.map +1 -0
  130. package/BookReader/plugins/plugin.tts.js +2 -9193
  131. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +2 -0
  132. package/BookReader/plugins/plugin.tts.js.map +1 -1
  133. package/BookReader/plugins/plugin.url.js +1 -269
  134. package/BookReader/plugins/plugin.url.js.map +1 -1
  135. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -379
  136. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  137. package/BookReader/plugins/translator-worker.js +2 -0
  138. package/BookReader/plugins/translator-worker.js.map +1 -0
  139. package/BookReader/silence.mp3 +0 -0
  140. package/BookReader/translator-worker.js +475 -0
  141. package/BookReader/webcomponents-bundle.js +3 -0
  142. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  143. package/BookReader/webcomponents-bundle.js.map +1 -0
  144. package/README.md +17 -4
  145. package/dist/esm/ia-bookreader.js +3 -0
  146. package/dist/esm/ia-bookreader.js.LICENSE.txt +17 -0
  147. package/dist/esm/ia-bookreader.js.map +1 -0
  148. package/dist/esm/plugins/plugin.archive_analytics.js +2 -0
  149. package/dist/esm/plugins/plugin.archive_analytics.js.map +1 -0
  150. package/dist/esm/plugins/plugin.autoplay.js +2 -0
  151. package/dist/esm/plugins/plugin.autoplay.js.map +1 -0
  152. package/dist/esm/plugins/plugin.chapters.js +2 -0
  153. package/dist/esm/plugins/plugin.chapters.js.map +1 -0
  154. package/dist/esm/plugins/plugin.experiments.js +2 -0
  155. package/dist/esm/plugins/plugin.experiments.js.map +1 -0
  156. package/dist/esm/plugins/plugin.iframe.js +2 -0
  157. package/dist/esm/plugins/plugin.iframe.js.map +1 -0
  158. package/dist/esm/plugins/plugin.iiif.js +2 -0
  159. package/dist/esm/plugins/plugin.iiif.js.map +1 -0
  160. package/dist/esm/plugins/plugin.resume.js +2 -0
  161. package/dist/esm/plugins/plugin.resume.js.map +1 -0
  162. package/dist/esm/plugins/plugin.search.js +2 -0
  163. package/dist/esm/plugins/plugin.search.js.map +1 -0
  164. package/dist/esm/plugins/plugin.text_selection.js +2 -0
  165. package/dist/esm/plugins/plugin.text_selection.js.map +1 -0
  166. package/dist/esm/plugins/plugin.translate.js +2 -0
  167. package/dist/esm/plugins/plugin.translate.js.map +1 -0
  168. package/dist/esm/plugins/plugin.tts.js +2 -0
  169. package/dist/esm/plugins/plugin.tts.js.map +1 -0
  170. package/dist/esm/plugins/plugin.url.js +2 -0
  171. package/dist/esm/plugins/plugin.url.js.map +1 -0
  172. package/dist/esm/plugins/plugin.vendor-fullscreen.js +2 -0
  173. package/dist/esm/plugins/plugin.vendor-fullscreen.js.map +1 -0
  174. package/jsconfig.json +18 -0
  175. package/package.json +113 -71
  176. package/src/BookReader/BookModel.js +92 -46
  177. package/src/BookReader/DragScrollable.js +233 -0
  178. package/src/BookReader/ImageCache.js +49 -16
  179. package/src/BookReader/Mode1Up.js +66 -365
  180. package/src/BookReader/Mode1UpLit.js +392 -0
  181. package/src/BookReader/Mode2Up.js +87 -1315
  182. package/src/BookReader/Mode2UpLit.js +786 -0
  183. package/src/BookReader/ModeAbstract.js +43 -0
  184. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  185. package/src/BookReader/ModeSmoothZoom.js +312 -0
  186. package/src/BookReader/ModeThumb.js +29 -15
  187. package/src/BookReader/Navbar/Navbar.js +204 -84
  188. package/src/BookReader/PageContainer.js +120 -23
  189. package/src/BookReader/ReduceSet.js +2 -2
  190. package/src/BookReader/Toolbar/Toolbar.js +18 -40
  191. package/src/BookReader/events.js +3 -3
  192. package/src/BookReader/options.js +105 -18
  193. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  194. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  195. package/src/BookReader/utils/SelectionObserver.js +72 -0
  196. package/src/BookReader/utils/classes.js +1 -1
  197. package/src/BookReader/utils.js +136 -12
  198. package/src/BookReader.js +732 -1231
  199. package/src/BookReaderPlugin.js +52 -0
  200. package/src/assets/icons/magnify-minus.svg +3 -7
  201. package/src/assets/icons/magnify-plus.svg +3 -7
  202. package/src/assets/icons/slider-toggle.svg +1 -0
  203. package/src/assets/icons/voice.svg +1 -0
  204. package/src/assets/images/hypothesis.ico +0 -0
  205. package/src/assets/images/icon_experiment.svg +1 -0
  206. package/src/assets/images/translate.svg +1 -0
  207. package/src/assets/images/unviewable_page.png +0 -0
  208. package/src/assets/silence.mp3 +0 -0
  209. package/src/css/BookReader.scss +1 -5
  210. package/src/css/_BRBookmarks.scss +1 -1
  211. package/src/css/_BRComponent.scss +1 -1
  212. package/src/css/_BRicon.scss +8 -2
  213. package/src/css/_BRmain.scss +16 -3
  214. package/src/css/_BRnav.scss +74 -70
  215. package/src/css/_BRpages.scss +174 -42
  216. package/src/css/_BRsearch.scss +69 -30
  217. package/src/css/_BRtoolbar.scss +5 -5
  218. package/src/css/_TextSelection.scss +249 -25
  219. package/src/css/_colorbox.scss +2 -2
  220. package/src/css/_controls.scss +24 -7
  221. package/src/css/_icons.scss +8 -1
  222. package/src/{BookNavigator/assets → css}/button-base.js +2 -2
  223. package/src/css/icon_checkmark.js +9 -0
  224. package/src/css/sharedStyles.js +15 -0
  225. package/src/ia-bookreader/downloads/downloads-provider.js +81 -0
  226. package/src/{BookNavigator → ia-bookreader}/downloads/downloads.js +25 -5
  227. package/src/ia-bookreader/ia-bookreader.js +686 -0
  228. package/src/ia-bookreader/sharing.js +27 -0
  229. package/src/ia-bookreader/viewable-files.js +98 -0
  230. package/src/{BookNavigator → ia-bookreader}/visual-adjustments/visual-adjustments-provider.js +17 -17
  231. package/src/{BookNavigator → ia-bookreader}/visual-adjustments/visual-adjustments.js +75 -67
  232. package/src/{BookNavigator → plugins}/bookmarks/bookmark-button.js +4 -3
  233. package/src/{BookNavigator/assets → plugins/bookmarks}/bookmark-colors.js +1 -1
  234. package/src/{BookNavigator → plugins}/bookmarks/bookmark-edit.js +43 -31
  235. package/src/{BookNavigator → plugins}/bookmarks/bookmarks-list.js +48 -49
  236. package/src/{BookNavigator → plugins}/bookmarks/bookmarks-loginCTA.js +3 -3
  237. package/src/plugins/bookmarks/bookmarks-provider.js +63 -0
  238. package/src/{BookNavigator → plugins/bookmarks}/delete-modal-actions.js +1 -1
  239. package/src/{BookNavigator → plugins}/bookmarks/ia-bookmarks.js +117 -68
  240. package/src/plugins/plugin.archive_analytics.js +84 -78
  241. package/src/plugins/plugin.autoplay.js +99 -104
  242. package/src/plugins/plugin.chapters.js +323 -205
  243. package/src/plugins/plugin.experiments.js +375 -0
  244. package/src/plugins/plugin.iframe.js +36 -37
  245. package/src/plugins/plugin.iiif.js +147 -0
  246. package/src/plugins/plugin.resume.js +54 -51
  247. package/src/plugins/plugin.text_selection.js +464 -219
  248. package/src/plugins/plugin.vendor-fullscreen.js +5 -5
  249. package/src/plugins/search/plugin.search.js +347 -397
  250. package/src/{BookNavigator → plugins}/search/search-provider.js +59 -27
  251. package/src/{BookNavigator → plugins}/search/search-results.js +105 -76
  252. package/src/plugins/search/utils.js +50 -0
  253. package/src/plugins/search/view.js +51 -69
  254. package/src/plugins/translate/TranslationManager.js +164 -0
  255. package/src/plugins/translate/plugin.translate.js +559 -0
  256. package/src/plugins/tts/AbstractTTSEngine.js +78 -49
  257. package/src/plugins/tts/FestivalTTSEngine.js +20 -30
  258. package/src/plugins/tts/PageChunk.js +117 -23
  259. package/src/plugins/tts/PageChunkIterator.js +11 -17
  260. package/src/plugins/tts/WebTTSEngine.js +132 -92
  261. package/src/plugins/tts/plugin.tts.js +341 -351
  262. package/src/plugins/tts/utils.js +76 -67
  263. package/src/plugins/url/UrlPlugin.js +213 -0
  264. package/src/plugins/{plugin.url.js → url/plugin.url.js} +54 -18
  265. package/src/util/TextSelectionManager.js +1464 -0
  266. package/src/util/browserSniffing.js +42 -1
  267. package/src/util/cache.js +20 -0
  268. package/src/util/docCookies.js +21 -2
  269. package/src/util/dom.js +88 -0
  270. package/src/util/generators.js +89 -0
  271. package/src/util/lit.js +15 -0
  272. package/src/util/strings.js +11 -0
  273. package/.babelrc +0 -12
  274. package/.dependabot/config.yml +0 -6
  275. package/.eslintrc.js +0 -50
  276. package/.gitattributes +0 -2
  277. package/.github/ISSUE_TEMPLATE/bug.md +0 -32
  278. package/.github/ISSUE_TEMPLATE/feature-request.md +0 -30
  279. package/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -15
  280. package/.github/workflows/node.js.yml +0 -37
  281. package/.github/workflows/npm-publish.yml +0 -47
  282. package/.testcaferc.json +0 -5
  283. package/BookReader/BookReader.js +0 -21613
  284. package/BookReader/BookReader.js.map +0 -1
  285. package/BookReader/bookreader-component-bundle.js +0 -14497
  286. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  287. package/BookReader/bookreader-component-bundle.js.map +0 -1
  288. package/BookReader/jquery-1.10.1.js +0 -108
  289. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  290. package/BookReader/plugins/plugin.menu_toggle.js +0 -369
  291. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  292. package/BookReader/plugins/plugin.mobile_nav.js +0 -335
  293. package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
  294. package/BookReaderDemo/BookReaderDemo.css +0 -41
  295. package/BookReaderDemo/BookReaderJSAdvanced.js +0 -115
  296. package/BookReaderDemo/BookReaderJSAutoplay.js +0 -56
  297. package/BookReaderDemo/BookReaderJSSimple.js +0 -55
  298. package/BookReaderDemo/IIIFBookReader.js +0 -207
  299. package/BookReaderDemo/assets/v5/Bookreader-logo-cool-grad.svg +0 -1
  300. package/BookReaderDemo/assets/v5/Bookreader-logo-flat.svg +0 -1
  301. package/BookReaderDemo/assets/v5/Bookreader-logo-hex-cool-grad.png +0 -0
  302. package/BookReaderDemo/assets/v5/Bookreader-logo-hex-flat.png +0 -0
  303. package/BookReaderDemo/assets/v5/Bookreader-logo-lines.png +0 -0
  304. package/BookReaderDemo/assets/v5/Bookreader-logo-lines.svg +0 -1
  305. package/BookReaderDemo/assets/v5/Bookreader-logo-warm.svg +0 -1
  306. package/BookReaderDemo/assets/v5/bookreader-logo-renders@1x.png +0 -0
  307. package/BookReaderDemo/assets/v5/bookreader-logo-renders@2x.png +0 -0
  308. package/BookReaderDemo/assets/v5/bookreader-v5-screenshot.png +0 -0
  309. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  310. package/BookReaderDemo/demo-advanced.html +0 -33
  311. package/BookReaderDemo/demo-autoplay.html +0 -38
  312. package/BookReaderDemo/demo-embed-iframe-src.html +0 -84
  313. package/BookReaderDemo/demo-embed.html +0 -26
  314. package/BookReaderDemo/demo-fullscreen-mobile.html +0 -36
  315. package/BookReaderDemo/demo-fullscreen.html +0 -33
  316. package/BookReaderDemo/demo-iiif.html +0 -34
  317. package/BookReaderDemo/demo-iiif.js +0 -26
  318. package/BookReaderDemo/demo-internetarchive.html +0 -74
  319. package/BookReaderDemo/demo-multiple.html +0 -43
  320. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  321. package/BookReaderDemo/demo-preview-pages.html +0 -1092
  322. package/BookReaderDemo/demo-simple.html +0 -34
  323. package/BookReaderDemo/demo-vendor-fullscreen.html +0 -36
  324. package/BookReaderDemo/immersion-1up.html +0 -64
  325. package/BookReaderDemo/immersion-mode.html +0 -35
  326. package/BookReaderDemo/toggle_controls.html +0 -53
  327. package/BookReaderDemo/view_mode.html +0 -39
  328. package/BookReaderDemo/viewmode-cycle.html +0 -41
  329. package/CHANGELOG.md +0 -499
  330. package/CONTRIBUTING.md +0 -7
  331. package/codecov.yml +0 -17
  332. package/index.html +0 -31
  333. package/karma.conf.js +0 -23
  334. package/screenshot.png +0 -0
  335. package/scripts/postversion.js +0 -10
  336. package/scripts/preversion.js +0 -14
  337. package/scripts/version.js +0 -26
  338. package/src/BookNavigator/BookModel.js +0 -14
  339. package/src/BookNavigator/BookNavigator.js +0 -459
  340. package/src/BookNavigator/assets/book-loader.js +0 -27
  341. package/src/BookNavigator/assets/icon_checkmark.js +0 -6
  342. package/src/BookNavigator/assets/icon_close.js +0 -3
  343. package/src/BookNavigator/assets/icon_sort_ascending.js +0 -5
  344. package/src/BookNavigator/assets/icon_sort_descending.js +0 -5
  345. package/src/BookNavigator/assets/icon_sort_neutral.js +0 -5
  346. package/src/BookNavigator/assets/icon_volumes.js +0 -11
  347. package/src/BookNavigator/bookmarks/bookmarks-provider.js +0 -53
  348. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  349. package/src/BookNavigator/downloads/downloads-provider.js +0 -66
  350. package/src/BookNavigator/search/a-search-result.js +0 -55
  351. package/src/BookNavigator/volumes/volumes-provider.js +0 -83
  352. package/src/BookNavigator/volumes/volumes.js +0 -178
  353. package/src/BookReader/DebugConsole.js +0 -54
  354. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  355. package/src/ItemNavigator/ItemNavigator.js +0 -376
  356. package/src/ItemNavigator/providers/sharing.js +0 -33
  357. package/src/css/_MobileNav.scss +0 -194
  358. package/src/dragscrollable-br.js +0 -261
  359. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  360. package/src/plugins/plugin.mobile_nav.js +0 -287
  361. package/tests/BookReader/BookModel.test.js +0 -312
  362. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  363. package/tests/BookReader/DebugConsole.test.js +0 -25
  364. package/tests/BookReader/ImageCache.test.js +0 -150
  365. package/tests/BookReader/Mode1Up.test.js +0 -164
  366. package/tests/BookReader/Mode2Up.test.js +0 -247
  367. package/tests/BookReader/Navbar/Navbar.test.js +0 -169
  368. package/tests/BookReader/PageContainer.test.js +0 -115
  369. package/tests/BookReader/ReduceSet.test.js +0 -38
  370. package/tests/BookReader/Toolbar/Toolbar.test.js +0 -26
  371. package/tests/BookReader/utils/classes.test.js +0 -88
  372. package/tests/BookReader/utils.test.js +0 -109
  373. package/tests/BookReader.options.test.js +0 -39
  374. package/tests/BookReader.test.js +0 -301
  375. package/tests/e2e/README.md +0 -75
  376. package/tests/e2e/autoplay.test.js +0 -13
  377. package/tests/e2e/base.test.js +0 -35
  378. package/tests/e2e/helpers/base.js +0 -263
  379. package/tests/e2e/helpers/debug.js +0 -13
  380. package/tests/e2e/helpers/desktopSearch.js +0 -72
  381. package/tests/e2e/helpers/mobileSearch.js +0 -85
  382. package/tests/e2e/helpers/mockSearch.js +0 -93
  383. package/tests/e2e/helpers/rightToLeft.js +0 -29
  384. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  385. package/tests/e2e/models/BookReader.js +0 -11
  386. package/tests/e2e/models/Navigation.js +0 -56
  387. package/tests/e2e/rightToLeft.test.js +0 -20
  388. package/tests/e2e/viewmode.test.js +0 -37
  389. package/tests/karma/BookNavigator/book-navigator.test.js +0 -139
  390. package/tests/karma/BookNavigator/bookmarks/bookmark-edit.test.js +0 -133
  391. package/tests/karma/BookNavigator/bookmarks/bookmarks-list.test.js +0 -222
  392. package/tests/karma/BookNavigator/search/search-provider.test.js +0 -23
  393. package/tests/karma/BookNavigator/search/search-results.test.js +0 -240
  394. package/tests/karma/BookNavigator/sharing/sharing-provider.test.js +0 -40
  395. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  396. package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +0 -141
  397. package/tests/karma/BookNavigator/volumes/volumes.test.js +0 -98
  398. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  399. package/tests/plugins/plugin.archive_analytics.test.js +0 -23
  400. package/tests/plugins/plugin.autoplay.test.js +0 -52
  401. package/tests/plugins/plugin.chapters.test.js +0 -130
  402. package/tests/plugins/plugin.iframe.test.js +0 -42
  403. package/tests/plugins/plugin.mobile_nav.test.js +0 -66
  404. package/tests/plugins/plugin.resume.test.js +0 -98
  405. package/tests/plugins/plugin.text_selection.test.js +0 -203
  406. package/tests/plugins/plugin.url.test.js +0 -129
  407. package/tests/plugins/plugin.vendor-fullscreen.test.js +0 -65
  408. package/tests/plugins/search/plugin.search.test.js +0 -173
  409. package/tests/plugins/search/plugin.search.view.test.js +0 -106
  410. package/tests/plugins/tts/AbstractTTSEngine.test.js +0 -153
  411. package/tests/plugins/tts/FestivalTTSEngine.test.js +0 -59
  412. package/tests/plugins/tts/PageChunk.test.js +0 -57
  413. package/tests/plugins/tts/PageChunkIterator.test.js +0 -179
  414. package/tests/plugins/tts/WebTTSEngine.test.js +0 -126
  415. package/tests/plugins/tts/utils.test.js +0 -133
  416. package/tests/util/browserSniffing.test.js +0 -56
  417. package/tests/util/docCookies.test.js +0 -15
  418. package/tests/util/strings.test.js +0 -63
  419. package/tests/utils.js +0 -42
  420. package/webpack.config.js +0 -86
@@ -1,12 +1,12 @@
1
1
  import PageChunkIterator from './PageChunkIterator.js';
2
+ import { hasLocalStorage } from './utils.js';
2
3
  /** @typedef {import('./utils.js').ISO6391} ISO6391 */
3
4
  /** @typedef {import('./PageChunk.js')} PageChunk */
4
5
 
5
6
  /**
6
7
  * @export
7
8
  * @typedef {Object} TTSEngineOptions
8
- * @property {String} server
9
- * @property {String} bookPath
9
+ * @property {import('@/src/util/strings.js').StringWithVars} pageChunkUrl
10
10
  * @property {ISO6391} bookLanguage
11
11
  * @property {Function} onLoadingStart
12
12
  * @property {Function} onLoadingComplete
@@ -28,6 +28,7 @@ import PageChunkIterator from './PageChunkIterator.js';
28
28
  * @property {() => void} resume
29
29
  * @property {() => void} finish force the sound to 'finish'
30
30
  * @property {number => void} setPlaybackRate
31
+ * @property {SpeechSynthesisVoice => void} setVoice
31
32
  **/
32
33
 
33
34
  /** Handling bookreader's text-to-speech */
@@ -39,6 +40,7 @@ export default class AbstractTTSEngine {
39
40
  constructor(options) {
40
41
  this.playing = false;
41
42
  this.paused = false;
43
+ /** @type {TTSEngineOptions} */
42
44
  this.opts = options;
43
45
  /** @type {PageChunkIterator} */
44
46
  this._chunkIterator = null;
@@ -50,9 +52,7 @@ export default class AbstractTTSEngine {
50
52
  /** @type {SpeechSynthesisVoice} */
51
53
  this.voice = null;
52
54
  // Listen for voice changes (fired by subclasses)
53
- this.events.on('voiceschanged', () => {
54
- this.voice = AbstractTTSEngine.getBestBookVoice(this.getVoices(), this.opts.bookLanguage);
55
- });
55
+ this.events.on('voiceschanged', this.updateBestVoice);
56
56
  this.events.trigger('voiceschanged');
57
57
  }
58
58
 
@@ -71,20 +71,23 @@ export default class AbstractTTSEngine {
71
71
  /** @abstract */
72
72
  init() { return null; }
73
73
 
74
+ updateBestVoice = () => {
75
+ this.voice = AbstractTTSEngine.getBestBookVoice(this.getVoices(), this.opts.bookLanguage);
76
+ }
77
+
74
78
  /**
75
79
  * @param {number} leafIndex
76
80
  * @param {number} numLeafs total number of leafs in the current book
77
81
  */
78
82
  start(leafIndex, numLeafs) {
79
83
  this.playing = true;
84
+ this.paused = false;
80
85
  this.opts.onLoadingStart();
81
86
 
82
87
  this._chunkIterator = new PageChunkIterator(numLeafs, leafIndex, {
83
- server: this.opts.server,
84
- bookPath: this.opts.bookPath,
85
- pageBufferSize: 5,
88
+ pageChunkUrl: this.opts.pageChunkUrl,
89
+ pageBufferSize: 3,
86
90
  });
87
-
88
91
  this.step();
89
92
  this.events.trigger('start');
90
93
  }
@@ -92,6 +95,7 @@ export default class AbstractTTSEngine {
92
95
  stop() {
93
96
  if (this.activeSound) this.activeSound.stop();
94
97
  this.playing = false;
98
+ this.paused = true;
95
99
  this._chunkIterator = null;
96
100
  this.activeSound = null;
97
101
  this.events.trigger('stop');
@@ -124,13 +128,26 @@ export default class AbstractTTSEngine {
124
128
  }
125
129
 
126
130
  /** @public */
127
- jumpBackward() {
128
- Promise.all([
131
+ async jumpBackward() {
132
+ await Promise.all([
129
133
  this.activeSound.stop(),
130
134
  this._chunkIterator.decrement()
131
- .then(() => this._chunkIterator.decrement())
132
- ])
133
- .then(() => this.step());
135
+ .then(() => this._chunkIterator.decrement()),
136
+ ]);
137
+ this.step();
138
+ }
139
+
140
+ /** @param {string} voiceURI */
141
+ setVoice(voiceURI) {
142
+ // if the user actively selects a voice, don't re-choose best voice anymore
143
+ // MS Edge fires voices changed randomly very often
144
+ this.events.off('voiceschanged', this.updateBestVoice);
145
+ this.voice = this.getVoices().find(voice => voice.voiceURI === voiceURI);
146
+ // if the current book has a language set, store the selected voice with the book language as a suffix
147
+ if (this.opts.bookLanguage && hasLocalStorage()) {
148
+ localStorage.setItem(`BRtts-voice-${this.opts.bookLanguage}`, this.voice.voiceURI);
149
+ }
150
+ if (this.activeSound) this.activeSound.setVoice(this.voice);
134
151
  }
135
152
 
136
153
  /** @param {number} newRate */
@@ -140,36 +157,33 @@ export default class AbstractTTSEngine {
140
157
  }
141
158
 
142
159
  /** @private */
143
- step() {
144
- this._chunkIterator.next()
145
- .then(chunk => {
146
- if (chunk == PageChunkIterator.AT_END) {
147
- this.stop();
148
- this.opts.onDone();
149
- return;
150
- }
151
-
152
- this.opts.onLoadingStart();
153
- const sound = this.createSound(chunk);
154
- sound.chunk = chunk;
155
- sound.rate = this.playbackRate;
156
- sound.voice = this.voice;
157
- sound.load(() => this.opts.onLoadingComplete());
158
-
159
- this.opts.onLoadingComplete();
160
- return this.opts.beforeChunkPlay(chunk).then(() => sound);
161
- })
162
- .then(sound => {
163
- if (!this.playing) return;
164
-
165
- const playPromise = this.playSound(sound)
166
- .then(() => this.opts.afterChunkPlay(sound.chunk));
167
- if (this.paused) this.pause();
168
- return playPromise;
169
- })
170
- .then(() => {
171
- if (this.playing) return this.step();
172
- });
160
+ async step() {
161
+ const chunk = await this._chunkIterator.next();
162
+ if (chunk == PageChunkIterator.AT_END) {
163
+ this.stop();
164
+ this.opts.onDone();
165
+ return;
166
+ }
167
+ this.opts.onLoadingStart();
168
+ const sound = this.createSound(chunk);
169
+ sound.chunk = chunk;
170
+ sound.rate = this.playbackRate;
171
+ sound.voice = this.voice;
172
+ sound.load(() => this.opts.onLoadingComplete());
173
+
174
+ this.opts.onLoadingComplete();
175
+
176
+ await this.opts.beforeChunkPlay(chunk);
177
+
178
+ if (!this.playing) return;
179
+
180
+ const playPromise = await this.playSound(sound)
181
+ .then(()=> this.opts.afterChunkPlay(sound.chunk));
182
+
183
+ if (this.paused) this.pause();
184
+ await playPromise;
185
+
186
+ if (this.playing) return this.step();
173
187
  }
174
188
 
175
189
  /**
@@ -190,8 +204,8 @@ export default class AbstractTTSEngine {
190
204
  }
191
205
 
192
206
  /** Convenience wrapper for {@see AbstractTTSEngine.getBestVoice} */
193
- getBestVoice() {
194
- return AbstractTTSEngine.getBestBookVoice(this.getVoices(), this.opts.bookLanguage);
207
+ getBestVoice(languageOverride) {
208
+ return AbstractTTSEngine.getBestBookVoice(this.getVoices(), languageOverride || this.opts.bookLanguage);
195
209
  }
196
210
 
197
211
  /**
@@ -212,10 +226,12 @@ export default class AbstractTTSEngine {
212
226
  // user languages that match the book language
213
227
  const matchingUserLangs = userLanguages.filter(lang => lang.startsWith(bookLanguage));
214
228
 
215
- // Try to find voices that intersect these two sets
216
- return AbstractTTSEngine.getMatchingVoice(matchingUserLangs, bookLangVoices) ||
229
+ // First try to find the last chosen voice from localStorage for the current book language
230
+ return AbstractTTSEngine.getMatchingStoredVoice(bookLangVoices, bookLanguage)
231
+ // Try to find voices that intersect these two sets
232
+ || AbstractTTSEngine.getMatchingVoice(matchingUserLangs, bookLangVoices)
217
233
  // no user languages match the books; let's return the best voice for the book language
218
- (bookLangVoices.find(v => v.default) || bookLangVoices[0])
234
+ || (bookLangVoices.find(v => v.default) || bookLangVoices[0])
219
235
  // No voices match the book language? let's find a voice in the user's language
220
236
  // and ignore book lang
221
237
  || AbstractTTSEngine.getMatchingVoice(userLanguages, voices)
@@ -223,6 +239,19 @@ export default class AbstractTTSEngine {
223
239
  || (voices.find(v => v.default) || voices[0]);
224
240
  }
225
241
 
242
+ /**
243
+ * @private
244
+ * Get the voice last selected by the user for the book language from localStorage.
245
+ * Returns undefined if no voice is stored or found.
246
+ * @param {SpeechSynthesisVoice[]} voices browser voices to choose from
247
+ * @param {ISO6391} bookLanguage book language to look for
248
+ * @return {SpeechSynthesisVoice | undefined}
249
+ */
250
+ static getMatchingStoredVoice(voices, bookLanguage) {
251
+ const storedVoice = hasLocalStorage() && localStorage.getItem(`BRtts-voice-${bookLanguage}`);
252
+ return (storedVoice ? voices.find(v => v.voiceURI === storedVoice) : undefined);
253
+ }
254
+
226
255
  /**
227
256
  * @private
228
257
  * Get the best voice that matches one of the BCP47 languages (order by preference)
@@ -1,5 +1,6 @@
1
1
  import AbstractTTSEngine from './AbstractTTSEngine.js';
2
- import { sleep } from './utils.js';
2
+ import { sleep } from '../../BookReader/utils.js';
3
+ import { applyVariables } from '../../util/strings.js';
3
4
  /* global soundManager */
4
5
  import 'soundmanager2';
5
6
  import 'jquery.browser';
@@ -17,19 +18,21 @@ export default class FestivalTTSEngine extends AbstractTTSEngine {
17
18
  return typeof(soundManager) !== 'undefined' && soundManager.supported();
18
19
  }
19
20
 
20
- /** @param {TTSEngineOptions} options */
21
+ /** @param {TTSEngineOptions | { festivalUrl: import('@/src/util/strings.js').StringWithVars }} options */
21
22
  constructor(options) {
22
23
  super(options);
23
24
  // $.browsers is sometimes undefined on some Android browsers :/
24
25
  // Likely related to when $.browser was moved to npm
25
26
  /** @type {'mp3' | 'ogg'} format of audio to get */
26
- this.audioFormat = $.browser?.mozilla ? 'ogg' : 'mp3';
27
+ this.audioFormat = $.browser?.mozilla ? 'ogg' : 'mp3'; //eslint-disable-line no-jquery/no-browser
28
+ /** @type {import('@/src/util/strings.js').StringWithVars} */
29
+ this.festivalUrl = options.festivalUrl;
27
30
  }
28
31
 
29
32
  /** @override */
30
33
  getVoices() {
31
34
  return [
32
- { default: true, lang: "en-US", localService: false, name: "Festival - English (US)", voiceURI: null }
35
+ { default: true, lang: "en-US", localService: false, name: "Festival - English (US)", voiceURI: null },
33
36
  ];
34
37
  }
35
38
 
@@ -45,7 +48,7 @@ export default class FestivalTTSEngine extends AbstractTTSEngine {
45
48
  url: '/bookreader/BookReader/soundmanager/swf',
46
49
  useHTML5Audio: true,
47
50
  //flash 8 version of swf is buggy when calling play() on a sound that is still loading
48
- flashVersion: 9
51
+ flashVersion: 9,
49
52
  });
50
53
  }
51
54
 
@@ -68,19 +71,7 @@ export default class FestivalTTSEngine extends AbstractTTSEngine {
68
71
 
69
72
  /** @override */
70
73
  createSound(chunk) {
71
- return new FestivalTTSSound(this.getSoundUrl(chunk.text));
72
- }
73
-
74
- /**
75
- * @private
76
- * Get URL for audio that says this text
77
- * @param {String} dataString the thing to say
78
- * @return {String} url
79
- */
80
- getSoundUrl(dataString) {
81
- return 'https://' + this.opts.server + '/BookReader/BookReaderGetTTS.php?string='
82
- + encodeURIComponent(dataString)
83
- + '&format=.' + this.audioFormat;
74
+ return new FestivalTTSSound(applyVariables(this.festivalUrl, { text: chunk.text, format: this.audioFormat }));
84
75
  }
85
76
 
86
77
  /**
@@ -91,10 +82,10 @@ export default class FestivalTTSEngine extends AbstractTTSEngine {
91
82
  * See https://stackoverflow.com/questions/12206631/html5-audio-cant-play-through-javascript-unless-triggered-manually-once
92
83
  * @return {PromiseLike}
93
84
  */
94
- iOSCaptureUserIntentHack() {
85
+ async iOSCaptureUserIntentHack() {
95
86
  const sound = soundManager.createSound({ url: SILENCE_1MS[this.audioFormat] });
96
- return new Promise(res => sound.play({onfinish: res}))
97
- .then(() => sound.destruct());
87
+ await new Promise(res => sound.play({onfinish: res}));
88
+ sound.destruct();
98
89
  }
99
90
  }
100
91
 
@@ -122,21 +113,20 @@ class FestivalTTSSound {
122
113
  if (this.rate != 1) this.sound.setPlaybackRate(this.rate);
123
114
  onload();
124
115
  },
125
- onresume: () => {
126
- sleep(25).then(() => {
127
- if (this.rate != 1) this.sound.setPlaybackRate(this.rate);
128
- });
129
- }
116
+ onresume: async () => {
117
+ await sleep(25);
118
+ if (this.rate != 1) this.sound.setPlaybackRate(this.rate);
119
+ },
130
120
  });
131
121
  return this.sound.load();
132
122
  }
133
123
 
134
- play() {
135
- return new Promise(res => {
124
+ async play() {
125
+ await new Promise(res => {
136
126
  this._finishResolver = res;
137
127
  this.sound.play({ onfinish: res });
138
- })
139
- .then(() => this.sound.destruct());
128
+ });
129
+ this.sound.destruct();
140
130
  }
141
131
 
142
132
  /** @override */
@@ -1,3 +1,10 @@
1
+ import { applyVariables } from "../../util/strings.js";
2
+
3
+ /** A chunk ends at the first sentence end after this many words */
4
+ const MIN_WORDS_IN_CHUNK = 25;
5
+ /** A chunk with no sentence end is cut at the first line end after this many words */
6
+ const MAX_WORDS_IN_CHUNK = 50;
7
+
1
8
  /**
2
9
  * Class to manage a 'chunk' (approximately a paragraph) of text on a page.
3
10
  */
@@ -16,32 +23,61 @@ export default class PageChunk {
16
23
  }
17
24
 
18
25
  /**
19
- * @param {string} server
20
- * @param {string} bookPath
26
+ * @param {import('@/src/util/strings.js').StringWithVars} pageChunkUrl
21
27
  * @param {number} leafIndex
22
28
  * @return {Promise<PageChunk[]>}
23
29
  */
24
- static fetch(server, bookPath, leafIndex) {
25
- // jquery's ajax "PromiseLike" implementation is inconsistent with
26
- // modern Promises, so convert it to a full promise (it doesn't forward
27
- // a returned promise to the next handler in the chain, which kind of
28
- // defeats the entire point of using promises to avoid "callback hell")
29
- return new Promise((res, rej) => {
30
- $.ajax({
31
- type: 'GET',
32
- url: `https://${server}/BookReader/BookReaderGetTextWrapper.php`,
33
- dataType:'jsonp',
34
- cache: true,
35
- data: {
36
- path: `${bookPath}_djvu.xml`,
37
- page: leafIndex
38
- },
39
- error: rej,
40
- })
41
- .then(chunks => {
42
- res(PageChunk._fromTextWrapperResponse(leafIndex, chunks));
43
- });
30
+ static async fetch(pageChunkUrl, leafIndex) {
31
+ if (window.br.plugins.translate?.translationManager.active) {
32
+ const translateLayers = await window.br.plugins.translate.getTranslateLayers(leafIndex);
33
+ const paragraphs = Array.from(translateLayers[0].childNodes);
34
+
35
+ const pageChunks = [];
36
+ for (const [idx, item] of paragraphs.entries()) {
37
+ // Should not read paragraphs w/ header or footer roles
38
+ if (!item.classList.contains('ocr-role-header-footer')) {
39
+ const translatedChunk = new PageChunk(leafIndex, idx, item.textContent, []);
40
+ pageChunks.push(translatedChunk);
41
+ }
42
+ }
43
+ if (pageChunks.length === 0) {
44
+ const placeholder = new PageChunk(leafIndex, 0, "", []);
45
+ pageChunks.push(placeholder);
46
+ }
47
+ return pageChunks;
48
+ }
49
+
50
+ const ocrPage = await PageChunk._getTextSelectionOcrPage(leafIndex);
51
+ if (ocrPage) {
52
+ return PageChunk._fromTextWrapperResponse(leafIndex, PageChunk._chunkOcrPage(ocrPage));
53
+ }
54
+
55
+ const chunks = await $.ajax({
56
+ type: 'GET',
57
+ url: applyVariables(pageChunkUrl, { pageIndex: leafIndex }),
58
+ cache: true,
59
+ xhrFields: {
60
+ withCredentials: window.br.protected,
61
+ },
44
62
  });
63
+ return PageChunk._fromTextWrapperResponse(leafIndex, chunks);
64
+ }
65
+
66
+ /**
67
+ * The text selection plugin's OCR for the page, if it has any. It's cached
68
+ * and shared with the text layer, so using it avoids requesting the page's
69
+ * text a second time.
70
+ * @param {number} leafIndex
71
+ * @return {Promise<Element | undefined>}
72
+ */
73
+ static async _getTextSelectionOcrPage(leafIndex) {
74
+ const textSelection = window.br.plugins.textSelection;
75
+ if (!textSelection?.options.enabled) return undefined;
76
+ try {
77
+ return await textSelection.getPageText(leafIndex);
78
+ } catch (e) {
79
+ return undefined;
80
+ }
45
81
  }
46
82
 
47
83
  /**
@@ -58,6 +94,61 @@ export default class PageChunk {
58
94
  });
59
95
  }
60
96
 
97
+ /**
98
+ * Split a page of djvu xml OCR into roughly paragraph-sized chunks, in the
99
+ * same shape as BookReaderGetTextWrapper.php's response: each chunk is its
100
+ * text followed by a box per line (or part of a line) it covers. Running
101
+ * headers/footers are skipped.
102
+ * @param {Element} ocrPage djvu xml `OBJECT` element
103
+ * @return {Array<[String, ...DJVURect[]]>}
104
+ */
105
+ static _chunkOcrPage(ocrPage) {
106
+ /** @type {Array<[String, ...DJVURect[]]>} */
107
+ const chunks = [];
108
+ /** @type {string[]} */
109
+ let words = [];
110
+ /** @type {DJVURect[]} */
111
+ let rects = [];
112
+ /** @type {DJVURect | null} bounds of the current line's words in this chunk */
113
+ let lineRect = null;
114
+
115
+ const endChunk = () => {
116
+ if (lineRect) rects.push(lineRect);
117
+ chunks.push([words.join(' '), ...rects]);
118
+ words = [];
119
+ rects = [];
120
+ lineRect = null;
121
+ };
122
+
123
+ for (const line of ocrPage.querySelectorAll('LINE')) {
124
+ if (line.closest('PARAGRAPH')?.getAttribute('x-role') === 'header-footer') continue;
125
+
126
+ for (const word of line.querySelectorAll('WORD')) {
127
+ const [left, bottom, right, top] = (word.getAttribute('coords') ?? '').split(',').map(parseFloat);
128
+ const text = word.textContent.trim();
129
+ // Same words the text layer drops as unpositionable
130
+ if (!text || isNaN(top) || (left === 0 && top === 0)) continue;
131
+
132
+ lineRect = lineRect ? [
133
+ Math.min(lineRect[0], left),
134
+ Math.max(lineRect[1], bottom),
135
+ Math.max(lineRect[2], right),
136
+ Math.min(lineRect[3], top),
137
+ ] : [left, bottom, right, top];
138
+ words.push(text);
139
+
140
+ if (text.endsWith('.') && words.length > MIN_WORDS_IN_CHUNK) endChunk();
141
+ }
142
+
143
+ if (lineRect) rects.push(lineRect);
144
+ lineRect = null;
145
+ if (words.length > MAX_WORDS_IN_CHUNK) endChunk();
146
+ }
147
+
148
+ if (words.length) endChunk();
149
+ return chunks;
150
+ }
151
+
61
152
  /**
62
153
  * @private
63
154
  * Sometimes the first rectangle will be ridiculously wide/tall. Find those and fix them
@@ -97,7 +188,10 @@ export default class PageChunk {
97
188
  * @return {string}
98
189
  */
99
190
  static _removeDanglingHyphens(text) {
100
- return text.replace(/-\s+/g, '');
191
+ // Some books mis-OCR a dangling hyphen as a ¬ (mathematical not sign) . Since in math
192
+ // the not sign should not appear followed by a space, we think we can safely assume
193
+ // this should be replaced.
194
+ return text.replace(/[-¬]\s+/g, '');
101
195
  }
102
196
  }
103
197
 
@@ -53,22 +53,18 @@ export default class PageChunkIterator {
53
53
  * in the correct order.
54
54
  * @return {PromiseLike<"__PageChunkIterator.AT_END__" | PageChunk>}
55
55
  */
56
- _nextUncontrolled() {
56
+ async _nextUncontrolled() {
57
57
  if (this._cursor.page == this.pageCount) {
58
58
  return Promise.resolve(PageChunkIterator.AT_END);
59
59
  }
60
-
61
60
  this._recenterBuffer(this._cursor.page);
62
-
63
- return this._fetchPageChunks(this._cursor.page)
64
- .then(chunks => {
65
- if (this._cursor.chunk == chunks.length) {
66
- this._cursor.page++;
67
- this._cursor.chunk = 0;
68
- return this._nextUncontrolled();
69
- }
70
- return chunks[this._cursor.chunk++];
71
- });
61
+ const chunks = await this._fetchPageChunks(this._cursor.page);
62
+ if (this._cursor.chunk == chunks.length) {
63
+ this._cursor.page++;
64
+ this._cursor.chunk = 0;
65
+ return this._nextUncontrolled();
66
+ }
67
+ return chunks[this._cursor.chunk++];
72
68
  }
73
69
 
74
70
  /**
@@ -142,7 +138,7 @@ export default class PageChunkIterator {
142
138
  * @param {number} index
143
139
  */
144
140
  _fetchPageChunksDirect(index) {
145
- return PageChunk.fetch(this.opts.server, this.opts.bookPath, index);
141
+ return PageChunk.fetch(this.opts.pageChunkUrl, index);
146
142
  }
147
143
  }
148
144
 
@@ -150,14 +146,12 @@ PageChunkIterator.AT_END = "__PageChunkIterator.AT_END__";
150
146
 
151
147
  /** @type {PageChunkIteratorOptions} */
152
148
  const DEFAULT_OPTS = {
153
- server: null,
154
- bookPath: null,
149
+ pageChunkUrl: null,
155
150
  pageBufferSize: 2,
156
151
  };
157
152
 
158
153
  /**
159
154
  * @typedef {Object} PageChunkIteratorOptions
160
- * @property {string} server
161
- * @property {string} bookPath
155
+ * @property {import('@/src/util/strings.js').StringWithVars} pageChunkUrl
162
156
  * @property {number} [pageBufferSize] number of pages to buffer before/after the current page
163
157
  */