@internetarchive/bookreader 5.0.0-18

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 (428) hide show
  1. package/.eslintrc.js +58 -0
  2. package/.gitattributes +2 -0
  3. package/.github/ISSUE_TEMPLATE/bug.md +32 -0
  4. package/.github/ISSUE_TEMPLATE/feature-request.md +30 -0
  5. package/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +15 -0
  6. package/.github/dependabot.yml +8 -0
  7. package/.github/workflows/node.js.yml +37 -0
  8. package/.github/workflows/npm-publish.yml +47 -0
  9. package/.testcaferc.json +5 -0
  10. package/BookReader/BookReader.css +2983 -0
  11. package/BookReader/BookReader.js +3 -0
  12. package/BookReader/BookReader.js.LICENSE.txt +117 -0
  13. package/BookReader/BookReader.js.map +1 -0
  14. package/BookReader/bookreader-component-bundle.js +1436 -0
  15. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +27 -0
  16. package/BookReader/bookreader-component-bundle.js.map +1 -0
  17. package/BookReader/icons/1up.svg +1 -0
  18. package/BookReader/icons/2up.svg +1 -0
  19. package/BookReader/icons/advance.svg +3 -0
  20. package/BookReader/icons/chevron-right.svg +1 -0
  21. package/BookReader/icons/close-circle-dark.svg +1 -0
  22. package/BookReader/icons/close-circle.svg +1 -0
  23. package/BookReader/icons/fullscreen.svg +1 -0
  24. package/BookReader/icons/fullscreen_exit.svg +1 -0
  25. package/BookReader/icons/hamburger.svg +1 -0
  26. package/BookReader/icons/left-arrow.svg +1 -0
  27. package/BookReader/icons/magnify-minus.svg +1 -0
  28. package/BookReader/icons/magnify-plus.svg +1 -0
  29. package/BookReader/icons/magnify.svg +1 -0
  30. package/BookReader/icons/pause.svg +1 -0
  31. package/BookReader/icons/play.svg +1 -0
  32. package/BookReader/icons/playback-speed.svg +1 -0
  33. package/BookReader/icons/read-aloud.svg +1 -0
  34. package/BookReader/icons/review.svg +3 -0
  35. package/BookReader/icons/thumbnails.svg +1 -0
  36. package/BookReader/icons/volume-full.svg +1 -0
  37. package/BookReader/images/BRicons.png +0 -0
  38. package/BookReader/images/BRicons.svg +5 -0
  39. package/BookReader/images/BRicons_ia.png +0 -0
  40. package/BookReader/images/back_pages.png +0 -0
  41. package/BookReader/images/book_bottom_icon.png +0 -0
  42. package/BookReader/images/book_down_icon.png +0 -0
  43. package/BookReader/images/book_left_icon.png +0 -0
  44. package/BookReader/images/book_leftmost_icon.png +0 -0
  45. package/BookReader/images/book_right_icon.png +0 -0
  46. package/BookReader/images/book_rightmost_icon.png +0 -0
  47. package/BookReader/images/book_top_icon.png +0 -0
  48. package/BookReader/images/book_up_icon.png +0 -0
  49. package/BookReader/images/books_graphic.svg +1 -0
  50. package/BookReader/images/booksplit.png +0 -0
  51. package/BookReader/images/control_pause_icon.png +0 -0
  52. package/BookReader/images/control_play_icon.png +0 -0
  53. package/BookReader/images/embed_icon.png +0 -0
  54. package/BookReader/images/icon-home-ia.png +0 -0
  55. package/BookReader/images/icon_OL-logo-xs.png +0 -0
  56. package/BookReader/images/icon_alert-xs.png +0 -0
  57. package/BookReader/images/icon_book.svg +1 -0
  58. package/BookReader/images/icon_bookmark.svg +1 -0
  59. package/BookReader/images/icon_close-pop.png +0 -0
  60. package/BookReader/images/icon_download.png +0 -0
  61. package/BookReader/images/icon_gear.svg +1 -0
  62. package/BookReader/images/icon_hamburger.svg +1 -0
  63. package/BookReader/images/icon_home.png +0 -0
  64. package/BookReader/images/icon_home.svg +1 -0
  65. package/BookReader/images/icon_home_ia.png +0 -0
  66. package/BookReader/images/icon_indicator.png +0 -0
  67. package/BookReader/images/icon_info.svg +1 -0
  68. package/BookReader/images/icon_one_page.svg +1 -0
  69. package/BookReader/images/icon_pause.svg +1 -0
  70. package/BookReader/images/icon_play.svg +1 -0
  71. package/BookReader/images/icon_playback-rate.svg +1 -0
  72. package/BookReader/images/icon_return.png +0 -0
  73. package/BookReader/images/icon_search_button.svg +1 -0
  74. package/BookReader/images/icon_share.svg +1 -0
  75. package/BookReader/images/icon_skip-ahead.svg +1 -0
  76. package/BookReader/images/icon_skip-back.svg +2 -0
  77. package/BookReader/images/icon_speaker.svg +1 -0
  78. package/BookReader/images/icon_speaker_open.svg +1 -0
  79. package/BookReader/images/icon_thumbnails.svg +1 -0
  80. package/BookReader/images/icon_toc.svg +1 -0
  81. package/BookReader/images/icon_two_pages.svg +1 -0
  82. package/BookReader/images/icon_zoomer.png +0 -0
  83. package/BookReader/images/loading.gif +0 -0
  84. package/BookReader/images/logo_icon.png +0 -0
  85. package/BookReader/images/marker_chap-off.png +0 -0
  86. package/BookReader/images/marker_chap-off.svg +1 -0
  87. package/BookReader/images/marker_chap-off_ia.png +0 -0
  88. package/BookReader/images/marker_chap-on.png +0 -0
  89. package/BookReader/images/marker_chap-on.svg +1 -0
  90. package/BookReader/images/marker_srch-on.svg +1 -0
  91. package/BookReader/images/marker_srchchap-off.png +0 -0
  92. package/BookReader/images/marker_srchchap-on.png +0 -0
  93. package/BookReader/images/nav_control-dn.png +0 -0
  94. package/BookReader/images/nav_control-dn_ia.png +0 -0
  95. package/BookReader/images/nav_control-up.png +0 -0
  96. package/BookReader/images/nav_control-up_ia.png +0 -0
  97. package/BookReader/images/nav_control.png +0 -0
  98. package/BookReader/images/one_page_mode_icon.png +0 -0
  99. package/BookReader/images/paper-badge.png +0 -0
  100. package/BookReader/images/print_icon.png +0 -0
  101. package/BookReader/images/progressbar.gif +0 -0
  102. package/BookReader/images/right_edges.png +0 -0
  103. package/BookReader/images/slider.png +0 -0
  104. package/BookReader/images/slider_ia.png +0 -0
  105. package/BookReader/images/thumbnail_mode_icon.png +0 -0
  106. package/BookReader/images/transparent.png +0 -0
  107. package/BookReader/images/two_page_mode_icon.png +0 -0
  108. package/BookReader/images/zoom_in_icon.png +0 -0
  109. package/BookReader/images/zoom_out_icon.png +0 -0
  110. package/BookReader/jquery-1.10.1.js +2 -0
  111. package/BookReader/jquery-1.10.1.js.LICENSE.txt +24 -0
  112. package/BookReader/plugins/plugin.archive_analytics.js +2 -0
  113. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -0
  114. package/BookReader/plugins/plugin.autoplay.js +2 -0
  115. package/BookReader/plugins/plugin.autoplay.js.map +1 -0
  116. package/BookReader/plugins/plugin.chapters.js +2 -0
  117. package/BookReader/plugins/plugin.chapters.js.map +1 -0
  118. package/BookReader/plugins/plugin.iframe.js +2 -0
  119. package/BookReader/plugins/plugin.iframe.js.map +1 -0
  120. package/BookReader/plugins/plugin.mobile_nav.js +2 -0
  121. package/BookReader/plugins/plugin.mobile_nav.js.map +1 -0
  122. package/BookReader/plugins/plugin.resume.js +2 -0
  123. package/BookReader/plugins/plugin.resume.js.map +1 -0
  124. package/BookReader/plugins/plugin.search.js +2 -0
  125. package/BookReader/plugins/plugin.search.js.map +1 -0
  126. package/BookReader/plugins/plugin.text_selection.js +2 -0
  127. package/BookReader/plugins/plugin.text_selection.js.map +1 -0
  128. package/BookReader/plugins/plugin.tts.js +3 -0
  129. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +27 -0
  130. package/BookReader/plugins/plugin.tts.js.map +1 -0
  131. package/BookReader/plugins/plugin.url.js +2 -0
  132. package/BookReader/plugins/plugin.url.js.map +1 -0
  133. package/BookReader/plugins/plugin.vendor-fullscreen.js +2 -0
  134. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -0
  135. package/BookReaderDemo/BookReaderDemo.css +41 -0
  136. package/BookReaderDemo/BookReaderJSAdvanced.js +115 -0
  137. package/BookReaderDemo/BookReaderJSAutoplay.js +56 -0
  138. package/BookReaderDemo/BookReaderJSSimple.js +55 -0
  139. package/BookReaderDemo/IIIFBookReader.js +207 -0
  140. package/BookReaderDemo/assets/v5/Bookreader-logo-cool-grad.svg +1 -0
  141. package/BookReaderDemo/assets/v5/Bookreader-logo-flat.svg +1 -0
  142. package/BookReaderDemo/assets/v5/Bookreader-logo-hex-cool-grad.png +0 -0
  143. package/BookReaderDemo/assets/v5/Bookreader-logo-hex-flat.png +0 -0
  144. package/BookReaderDemo/assets/v5/Bookreader-logo-lines.png +0 -0
  145. package/BookReaderDemo/assets/v5/Bookreader-logo-lines.svg +1 -0
  146. package/BookReaderDemo/assets/v5/Bookreader-logo-warm.svg +1 -0
  147. package/BookReaderDemo/assets/v5/bookreader-logo-renders@1x.png +0 -0
  148. package/BookReaderDemo/assets/v5/bookreader-logo-renders@2x.png +0 -0
  149. package/BookReaderDemo/assets/v5/bookreader-v5-screenshot.png +0 -0
  150. package/BookReaderDemo/bookreader-template-bundle.js +7178 -0
  151. package/BookReaderDemo/demo-advanced.html +32 -0
  152. package/BookReaderDemo/demo-autoplay.html +38 -0
  153. package/BookReaderDemo/demo-embed-iframe-src.html +84 -0
  154. package/BookReaderDemo/demo-embed.html +26 -0
  155. package/BookReaderDemo/demo-fullscreen-mobile.html +36 -0
  156. package/BookReaderDemo/demo-fullscreen.html +33 -0
  157. package/BookReaderDemo/demo-iiif.html +34 -0
  158. package/BookReaderDemo/demo-iiif.js +26 -0
  159. package/BookReaderDemo/demo-internetarchive.html +74 -0
  160. package/BookReaderDemo/demo-multiple.html +43 -0
  161. package/BookReaderDemo/demo-preview-pages.html +1092 -0
  162. package/BookReaderDemo/demo-simple.html +34 -0
  163. package/BookReaderDemo/demo-vendor-fullscreen.html +36 -0
  164. package/BookReaderDemo/immersion-1up.html +64 -0
  165. package/BookReaderDemo/immersion-mode.html +35 -0
  166. package/BookReaderDemo/toggle_controls.html +53 -0
  167. package/BookReaderDemo/view_mode.html +39 -0
  168. package/BookReaderDemo/viewmode-cycle.html +41 -0
  169. package/CHANGELOG.md +540 -0
  170. package/CONTRIBUTING.md +7 -0
  171. package/LICENSE +661 -0
  172. package/README.md +205 -0
  173. package/babel.config.js +18 -0
  174. package/codecov.yml +17 -0
  175. package/index.html +31 -0
  176. package/jsconfig.json +14 -0
  177. package/karma.conf.js +23 -0
  178. package/package.json +129 -0
  179. package/screenshot.png +0 -0
  180. package/scripts/postversion.js +10 -0
  181. package/scripts/preversion.js +14 -0
  182. package/scripts/version.js +26 -0
  183. package/src/BookNavigator/BookModel.js +14 -0
  184. package/src/BookNavigator/BookNavigator.js +468 -0
  185. package/src/BookNavigator/assets/book-loader.js +27 -0
  186. package/src/BookNavigator/assets/bookmark-colors.js +15 -0
  187. package/src/BookNavigator/assets/button-base.js +61 -0
  188. package/src/BookNavigator/assets/icon_checkmark.js +6 -0
  189. package/src/BookNavigator/assets/icon_close.js +3 -0
  190. package/src/BookNavigator/assets/icon_sort_asc.js +5 -0
  191. package/src/BookNavigator/assets/icon_sort_desc.js +5 -0
  192. package/src/BookNavigator/assets/icon_sort_neutral.js +5 -0
  193. package/src/BookNavigator/assets/icon_volumes.js +11 -0
  194. package/src/BookNavigator/bookmarks/bookmark-button.js +64 -0
  195. package/src/BookNavigator/bookmarks/bookmark-edit.js +215 -0
  196. package/src/BookNavigator/bookmarks/bookmarks-list.js +285 -0
  197. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +28 -0
  198. package/src/BookNavigator/bookmarks/bookmarks-provider.js +53 -0
  199. package/src/BookNavigator/bookmarks/ia-bookmarks.js +500 -0
  200. package/src/BookNavigator/br-fullscreen-mgr.js +83 -0
  201. package/src/BookNavigator/delete-modal-actions.js +49 -0
  202. package/src/BookNavigator/downloads/downloads-provider.js +76 -0
  203. package/src/BookNavigator/downloads/downloads.js +138 -0
  204. package/src/BookNavigator/search/a-search-result.js +55 -0
  205. package/src/BookNavigator/search/search-provider.js +180 -0
  206. package/src/BookNavigator/search/search-results.js +360 -0
  207. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +93 -0
  208. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +280 -0
  209. package/src/BookNavigator/volumes/volumes-provider.js +83 -0
  210. package/src/BookNavigator/volumes/volumes.js +178 -0
  211. package/src/BookReader/BookModel.js +518 -0
  212. package/src/BookReader/DebugConsole.js +54 -0
  213. package/src/BookReader/ImageCache.js +116 -0
  214. package/src/BookReader/Mode1Up.js +90 -0
  215. package/src/BookReader/Mode1UpLit.js +434 -0
  216. package/src/BookReader/Mode2Up.js +1372 -0
  217. package/src/BookReader/ModeSmoothZoom.js +177 -0
  218. package/src/BookReader/ModeThumb.js +336 -0
  219. package/src/BookReader/Navbar/Navbar.js +339 -0
  220. package/src/BookReader/PageContainer.js +120 -0
  221. package/src/BookReader/ReduceSet.js +26 -0
  222. package/src/BookReader/Toolbar/Toolbar.js +384 -0
  223. package/src/BookReader/events.js +20 -0
  224. package/src/BookReader/options.js +320 -0
  225. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  226. package/src/BookReader/utils/classes.js +36 -0
  227. package/src/BookReader/utils.js +240 -0
  228. package/src/BookReader.js +2546 -0
  229. package/src/BookReaderComponent/BookReaderComponent.js +112 -0
  230. package/src/ItemNavigator/ItemNavigator.js +376 -0
  231. package/src/ItemNavigator/providers/sharing.js +33 -0
  232. package/src/assets/icons/1up.svg +12 -0
  233. package/src/assets/icons/2up.svg +15 -0
  234. package/src/assets/icons/advance.svg +26 -0
  235. package/src/assets/icons/chevron-right.svg +1 -0
  236. package/src/assets/icons/close-circle-dark.svg +1 -0
  237. package/src/assets/icons/close-circle.svg +1 -0
  238. package/src/assets/icons/fullscreen.svg +17 -0
  239. package/src/assets/icons/fullscreen_exit.svg +17 -0
  240. package/src/assets/icons/hamburger.svg +15 -0
  241. package/src/assets/icons/left-arrow.svg +12 -0
  242. package/src/assets/icons/magnify-minus.svg +16 -0
  243. package/src/assets/icons/magnify-plus.svg +17 -0
  244. package/src/assets/icons/magnify.svg +15 -0
  245. package/src/assets/icons/pause.svg +23 -0
  246. package/src/assets/icons/play.svg +22 -0
  247. package/src/assets/icons/playback-speed.svg +34 -0
  248. package/src/assets/icons/read-aloud.svg +22 -0
  249. package/src/assets/icons/review.svg +22 -0
  250. package/src/assets/icons/thumbnails.svg +17 -0
  251. package/src/assets/icons/volume-full.svg +22 -0
  252. package/src/assets/images/BRicons.png +0 -0
  253. package/src/assets/images/BRicons.svg +94 -0
  254. package/src/assets/images/BRicons_ia.png +0 -0
  255. package/src/assets/images/back_pages.png +0 -0
  256. package/src/assets/images/book_bottom_icon.png +0 -0
  257. package/src/assets/images/book_down_icon.png +0 -0
  258. package/src/assets/images/book_left_icon.png +0 -0
  259. package/src/assets/images/book_leftmost_icon.png +0 -0
  260. package/src/assets/images/book_right_icon.png +0 -0
  261. package/src/assets/images/book_rightmost_icon.png +0 -0
  262. package/src/assets/images/book_top_icon.png +0 -0
  263. package/src/assets/images/book_up_icon.png +0 -0
  264. package/src/assets/images/books_graphic.svg +177 -0
  265. package/src/assets/images/booksplit.png +0 -0
  266. package/src/assets/images/control_pause_icon.png +0 -0
  267. package/src/assets/images/control_play_icon.png +0 -0
  268. package/src/assets/images/embed_icon.png +0 -0
  269. package/src/assets/images/icon-home-ia.png +0 -0
  270. package/src/assets/images/icon_OL-logo-xs.png +0 -0
  271. package/src/assets/images/icon_alert-xs.png +0 -0
  272. package/src/assets/images/icon_book.svg +12 -0
  273. package/src/assets/images/icon_bookmark.svg +12 -0
  274. package/src/assets/images/icon_close-pop.png +0 -0
  275. package/src/assets/images/icon_download.png +0 -0
  276. package/src/assets/images/icon_gear.svg +14 -0
  277. package/src/assets/images/icon_hamburger.svg +20 -0
  278. package/src/assets/images/icon_home.png +0 -0
  279. package/src/assets/images/icon_home.svg +21 -0
  280. package/src/assets/images/icon_home_ia.png +0 -0
  281. package/src/assets/images/icon_indicator.png +0 -0
  282. package/src/assets/images/icon_info.svg +11 -0
  283. package/src/assets/images/icon_one_page.svg +8 -0
  284. package/src/assets/images/icon_pause.svg +1 -0
  285. package/src/assets/images/icon_play.svg +1 -0
  286. package/src/assets/images/icon_playback-rate.svg +15 -0
  287. package/src/assets/images/icon_return.png +0 -0
  288. package/src/assets/images/icon_search_button.svg +8 -0
  289. package/src/assets/images/icon_share.svg +9 -0
  290. package/src/assets/images/icon_skip-ahead.svg +6 -0
  291. package/src/assets/images/icon_skip-back.svg +13 -0
  292. package/src/assets/images/icon_speaker.svg +18 -0
  293. package/src/assets/images/icon_speaker_open.svg +10 -0
  294. package/src/assets/images/icon_thumbnails.svg +12 -0
  295. package/src/assets/images/icon_toc.svg +5 -0
  296. package/src/assets/images/icon_two_pages.svg +9 -0
  297. package/src/assets/images/icon_zoomer.png +0 -0
  298. package/src/assets/images/loading.gif +0 -0
  299. package/src/assets/images/logo_icon.png +0 -0
  300. package/src/assets/images/marker_chap-off.png +0 -0
  301. package/src/assets/images/marker_chap-off.svg +11 -0
  302. package/src/assets/images/marker_chap-off_ia.png +0 -0
  303. package/src/assets/images/marker_chap-on.png +0 -0
  304. package/src/assets/images/marker_chap-on.svg +11 -0
  305. package/src/assets/images/marker_srch-on.svg +11 -0
  306. package/src/assets/images/marker_srchchap-off.png +0 -0
  307. package/src/assets/images/marker_srchchap-on.png +0 -0
  308. package/src/assets/images/nav_control-dn.png +0 -0
  309. package/src/assets/images/nav_control-dn_ia.png +0 -0
  310. package/src/assets/images/nav_control-up.png +0 -0
  311. package/src/assets/images/nav_control-up_ia.png +0 -0
  312. package/src/assets/images/nav_control.png +0 -0
  313. package/src/assets/images/one_page_mode_icon.png +0 -0
  314. package/src/assets/images/paper-badge.png +0 -0
  315. package/src/assets/images/print_icon.png +0 -0
  316. package/src/assets/images/progressbar.gif +0 -0
  317. package/src/assets/images/right_edges.png +0 -0
  318. package/src/assets/images/slider.png +0 -0
  319. package/src/assets/images/slider_ia.png +0 -0
  320. package/src/assets/images/thumbnail_mode_icon.png +0 -0
  321. package/src/assets/images/transparent.png +0 -0
  322. package/src/assets/images/two_page_mode_icon.png +0 -0
  323. package/src/assets/images/zoom_in_icon.png +0 -0
  324. package/src/assets/images/zoom_out_icon.png +0 -0
  325. package/src/css/BookReader.scss +89 -0
  326. package/src/css/_BRBookmarks.scss +29 -0
  327. package/src/css/_BRComponent.scss +13 -0
  328. package/src/css/_BRfloat.scss +197 -0
  329. package/src/css/_BRicon.scss +48 -0
  330. package/src/css/_BRmain.scss +251 -0
  331. package/src/css/_BRnav.scss +382 -0
  332. package/src/css/_BRpages.scss +139 -0
  333. package/src/css/_BRsearch.scss +226 -0
  334. package/src/css/_BRtoolbar.scss +84 -0
  335. package/src/css/_BRvendor.scss +5 -0
  336. package/src/css/_MobileNav.scss +194 -0
  337. package/src/css/_TextSelection.scss +32 -0
  338. package/src/css/_colorbox.scss +52 -0
  339. package/src/css/_controls.scss +244 -0
  340. package/src/css/_icons.scss +121 -0
  341. package/src/dragscrollable-br.js +261 -0
  342. package/src/jquery-wrapper.js +4 -0
  343. package/src/plugins/plugin.archive_analytics.js +86 -0
  344. package/src/plugins/plugin.autoplay.js +129 -0
  345. package/src/plugins/plugin.chapters.js +251 -0
  346. package/src/plugins/plugin.iframe.js +48 -0
  347. package/src/plugins/plugin.mobile_nav.js +287 -0
  348. package/src/plugins/plugin.resume.js +68 -0
  349. package/src/plugins/plugin.text_selection.js +291 -0
  350. package/src/plugins/plugin.url.js +198 -0
  351. package/src/plugins/plugin.vendor-fullscreen.js +247 -0
  352. package/src/plugins/search/plugin.search.js +439 -0
  353. package/src/plugins/search/view.js +440 -0
  354. package/src/plugins/tts/AbstractTTSEngine.js +242 -0
  355. package/src/plugins/tts/FestivalTTSEngine.js +169 -0
  356. package/src/plugins/tts/PageChunk.js +107 -0
  357. package/src/plugins/tts/PageChunkIterator.js +163 -0
  358. package/src/plugins/tts/WebTTSEngine.js +352 -0
  359. package/src/plugins/tts/plugin.tts.js +335 -0
  360. package/src/plugins/tts/tooltip_dict.js +15 -0
  361. package/src/plugins/tts/utils.js +91 -0
  362. package/src/util/browserSniffing.js +30 -0
  363. package/src/util/debouncer.js +26 -0
  364. package/src/util/docCookies.js +67 -0
  365. package/src/util/strings.js +34 -0
  366. package/tests/BookReader/BookModel.test.js +312 -0
  367. package/tests/BookReader/BookReaderPublicFunctions.test.js +164 -0
  368. package/tests/BookReader/DebugConsole.test.js +25 -0
  369. package/tests/BookReader/ImageCache.test.js +150 -0
  370. package/tests/BookReader/Mode1UpLit.test.js +87 -0
  371. package/tests/BookReader/Mode2Up.test.js +245 -0
  372. package/tests/BookReader/ModeSmoothZoom.test.js +149 -0
  373. package/tests/BookReader/Navbar/Navbar.test.js +169 -0
  374. package/tests/BookReader/PageContainer.test.js +187 -0
  375. package/tests/BookReader/ReduceSet.test.js +38 -0
  376. package/tests/BookReader/Toolbar/Toolbar.test.js +26 -0
  377. package/tests/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  378. package/tests/BookReader/utils/classes.test.js +88 -0
  379. package/tests/BookReader/utils.test.js +136 -0
  380. package/tests/BookReader.options.test.js +39 -0
  381. package/tests/BookReader.test.js +301 -0
  382. package/tests/e2e/README.md +75 -0
  383. package/tests/e2e/autoplay.test.js +13 -0
  384. package/tests/e2e/base.test.js +35 -0
  385. package/tests/e2e/helpers/base.js +263 -0
  386. package/tests/e2e/helpers/debug.js +13 -0
  387. package/tests/e2e/helpers/desktopSearch.js +72 -0
  388. package/tests/e2e/helpers/mobileSearch.js +85 -0
  389. package/tests/e2e/helpers/mockSearch.js +93 -0
  390. package/tests/e2e/helpers/rightToLeft.js +29 -0
  391. package/tests/e2e/ia-production/ia-prod-base.js +17 -0
  392. package/tests/e2e/models/BookReader.js +11 -0
  393. package/tests/e2e/models/Navigation.js +56 -0
  394. package/tests/e2e/rightToLeft.test.js +20 -0
  395. package/tests/e2e/viewmode.test.js +37 -0
  396. package/tests/karma/BookNavigator/book-navigator.test.js +180 -0
  397. package/tests/karma/BookNavigator/bookmarks/bookmark-edit.test.js +133 -0
  398. package/tests/karma/BookNavigator/bookmarks/bookmarks-list.test.js +222 -0
  399. package/tests/karma/BookNavigator/downloads/downloads-provider.test.js +64 -0
  400. package/tests/karma/BookNavigator/downloads/downloads.test.js +54 -0
  401. package/tests/karma/BookNavigator/search/search-provider.test.js +23 -0
  402. package/tests/karma/BookNavigator/search/search-results.test.js +240 -0
  403. package/tests/karma/BookNavigator/sharing/sharing-provider.test.js +40 -0
  404. package/tests/karma/BookNavigator/visual-adjustments.test.js +201 -0
  405. package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +160 -0
  406. package/tests/karma/BookNavigator/volumes/volumes.test.js +98 -0
  407. package/tests/plugins/plugin.archive_analytics.test.js +23 -0
  408. package/tests/plugins/plugin.autoplay.test.js +52 -0
  409. package/tests/plugins/plugin.chapters.test.js +130 -0
  410. package/tests/plugins/plugin.iframe.test.js +42 -0
  411. package/tests/plugins/plugin.mobile_nav.test.js +66 -0
  412. package/tests/plugins/plugin.resume.test.js +98 -0
  413. package/tests/plugins/plugin.text_selection.test.js +193 -0
  414. package/tests/plugins/plugin.url.test.js +129 -0
  415. package/tests/plugins/plugin.vendor-fullscreen.test.js +65 -0
  416. package/tests/plugins/search/plugin.search.test.js +173 -0
  417. package/tests/plugins/search/plugin.search.view.test.js +106 -0
  418. package/tests/plugins/tts/AbstractTTSEngine.test.js +153 -0
  419. package/tests/plugins/tts/FestivalTTSEngine.test.js +59 -0
  420. package/tests/plugins/tts/PageChunk.test.js +57 -0
  421. package/tests/plugins/tts/PageChunkIterator.test.js +179 -0
  422. package/tests/plugins/tts/WebTTSEngine.test.js +126 -0
  423. package/tests/plugins/tts/utils.test.js +133 -0
  424. package/tests/util/browserSniffing.test.js +56 -0
  425. package/tests/util/docCookies.test.js +15 -0
  426. package/tests/util/strings.test.js +63 -0
  427. package/tests/utils.js +80 -0
  428. package/webpack.config.js +85 -0
@@ -0,0 +1,518 @@
1
+ // @ts-check
2
+ import { DEFAULT_OPTIONS } from './options.js';
3
+ import { clamp } from './utils.js';
4
+ /** @typedef {import('./options.js').PageData} PageData */
5
+ /** @typedef {import('../BookReader.js').default} BookReader */
6
+
7
+ // URI to display when a page is not viewable.
8
+ // TODO Render configurable html for the user instead.
9
+ // FIXME Don't reference files on archive.org
10
+ const UNVIEWABLE_PAGE_URI = '/bookreader/static/preview-default.png';
11
+
12
+ /**
13
+ * Contains information about the Book/Document independent of the way it is
14
+ * being rendering. Nothing here should reference e.g. the mode, zoom, etc.
15
+ * It's just information about the book and its pages (usually as specified
16
+ * in the BookReader data option.)
17
+ */
18
+ export class BookModel {
19
+ /**
20
+ * @param {BookReader} br
21
+ */
22
+ constructor(br) {
23
+ this.br = br;
24
+ this.reduceSet = br.reduceSet;
25
+ this.ppi = br.options?.ppi ?? DEFAULT_OPTIONS.ppi;
26
+
27
+ /** @type {{width: number, height: number}} memoize storage */
28
+ this._medianPageSize = null;
29
+ /** @deprecated @type {{width: number, height: number}} memoize storage */
30
+ this._medianPageSizePixels = null;
31
+ /** @type {[PageData[], number]} */
32
+ this._getDataFlattenedCached = null;
33
+ }
34
+
35
+ /**
36
+ * @deprecated Use getMedianPageSizeInches
37
+ * Memoized
38
+ * @return {{width: number, height: number}}
39
+ */
40
+ getMedianPageSize() {
41
+ if (this._medianPageSizePixels) {
42
+ return this._medianPageSizePixels;
43
+ }
44
+
45
+ // A little expensive but we just do it once
46
+ const widths = [];
47
+ const heights = [];
48
+ for (let i = 0; i < this.getNumLeafs(); i++) {
49
+ widths.push(this.getPageWidth(i));
50
+ heights.push(this.getPageHeight(i));
51
+ }
52
+
53
+ widths.sort();
54
+ heights.sort();
55
+ this._medianPageSizePixels = {
56
+ width: widths[Math.floor(widths.length / 2)],
57
+ height: heights[Math.floor(heights.length / 2)]
58
+ };
59
+ return this._medianPageSizePixels;
60
+ }
61
+
62
+ /** Get median width/height of page in inches. Memoized for performance. */
63
+ getMedianPageSizeInches() {
64
+ if (this._medianPageSize) {
65
+ return this._medianPageSize;
66
+ }
67
+
68
+ const widths = [];
69
+ const heights = [];
70
+ for (const page of this.pagesIterator()) {
71
+ widths.push(page.widthInches);
72
+ heights.push(page.heightInches);
73
+ }
74
+
75
+ widths.sort();
76
+ heights.sort();
77
+
78
+ this._medianPageSize = {
79
+ width: widths[Math.floor(widths.length / 2)],
80
+ height: heights[Math.floor(heights.length / 2)]
81
+ };
82
+ return this._medianPageSize;
83
+ }
84
+
85
+ /**
86
+ * Returns the page width for the given index, or first or last page if out of range
87
+ * @deprecated see getPageWidth
88
+ * @param {PageIndex} index
89
+ */
90
+ _getPageWidth(index) {
91
+ // Synthesize a page width for pages not actually present in book.
92
+ // May or may not be the best approach.
93
+ // If index is out of range we return the width of first or last page
94
+ index = clamp(index, 0, this.getNumLeafs() - 1);
95
+ return this.getPageWidth(index);
96
+ }
97
+
98
+ /**
99
+ * Returns the page height for the given index, or first or last page if out of range
100
+ * @deprecated see getPageHeight
101
+ * @param {PageIndex} index
102
+ */
103
+ _getPageHeight(index) {
104
+ const clampedIndex = clamp(index, 0, this.getNumLeafs() - 1);
105
+ return this.getPageHeight(clampedIndex);
106
+ }
107
+
108
+ /**
109
+ * Returns the *highest* index the given page number, or undefined
110
+ * @param {PageNumString} pageNum
111
+ * @return {PageIndex|undefined}
112
+ */
113
+ getPageIndex(pageNum) {
114
+ const pageIndices = this.getPageIndices(pageNum);
115
+ return pageIndices.length ? pageIndices[pageIndices.length - 1] : undefined;
116
+ }
117
+
118
+ /**
119
+ * Returns an array (possibly empty) of the indices with the given page number
120
+ * @param {PageNumString} pageNum
121
+ * @return {PageIndex[]}
122
+ */
123
+ getPageIndices(pageNum) {
124
+ const indices = [];
125
+
126
+ // Check for special "nXX" page number
127
+ if (pageNum.slice(0,1) == 'n') {
128
+ try {
129
+ const pageIntStr = pageNum.slice(1, pageNum.length);
130
+ const pageIndex = parseInt(pageIntStr);
131
+ indices.push(pageIndex);
132
+ return indices;
133
+ } catch (err) {
134
+ // Do nothing... will run through page names and see if one matches
135
+ }
136
+ }
137
+
138
+ for (let i = 0; i < this.getNumLeafs(); i++) {
139
+ if (this.getPageNum(i) == pageNum) {
140
+ indices.push(i);
141
+ }
142
+ }
143
+
144
+ return indices;
145
+ }
146
+
147
+ /**
148
+ * Returns the name of the page as it should be displayed in the user interface
149
+ * @param {PageIndex} index
150
+ * @return {string}
151
+ */
152
+ getPageName(index) {
153
+ return 'Page ' + this.getPageNum(index);
154
+ }
155
+
156
+ /**
157
+ * @return {number} the total number of leafs (like an array length)
158
+ */
159
+ getNumLeafs() {
160
+ // For deprecated interface support, if numLeafs is set, use that.
161
+ if (this.br.numLeafs !== undefined)
162
+ return this.br.numLeafs;
163
+ return this._getDataFlattened().length;
164
+ }
165
+
166
+ /**
167
+ * @param {PageIndex} index
168
+ * @return {Number|undefined}
169
+ */
170
+ getPageWidth(index) {
171
+ return this.getPageProp(index, 'width');
172
+ }
173
+
174
+ /**
175
+ * @param {PageIndex} index
176
+ * @return {Number|undefined}
177
+ */
178
+ getPageHeight(index) {
179
+ return this.getPageProp(index, 'height');
180
+ }
181
+
182
+ /**
183
+ * @param {PageIndex} index
184
+ * @param {number} reduce - not used in default implementation
185
+ * @param {number} rotate - not used in default implementation
186
+ * @return {string|undefined}
187
+ */
188
+ // eslint-disable-next-line no-unused-vars
189
+ getPageURI(index, reduce, rotate) {
190
+ return !this.getPageProp(index, 'viewable', true) ? UNVIEWABLE_PAGE_URI : this.getPageProp(index, 'uri');
191
+ }
192
+
193
+ /**
194
+ * @param {PageIndex} index
195
+ * @return {'L' | 'R'}
196
+ */
197
+ getPageSide(index) {
198
+ return this.getPageProp(index, 'pageSide') || (index % 2 === 0 ? 'R' : 'L');
199
+ }
200
+
201
+ /**
202
+ * @param {PageIndex} index
203
+ * @return {PageNumString}
204
+ */
205
+ getPageNum(index) {
206
+ const pageNum = this.getPageProp(index, 'pageNum');
207
+ return pageNum === undefined ? `n${index}` : pageNum;
208
+ }
209
+
210
+ /**
211
+ * Generalized property accessor.
212
+ * @param {PageIndex} index
213
+ * @param {keyof PageData} propName
214
+ * @param {*} [fallbackValue] return if undefined
215
+ * @return {*|undefined}
216
+ */
217
+ getPageProp(index, propName, fallbackValue = undefined) {
218
+ return this._getDataProp(index, propName, fallbackValue);
219
+ }
220
+
221
+ /**
222
+ * This function returns the left and right indices for the user-visible
223
+ * spread that contains the given index.
224
+ * @note Can return indices out of range of what's in the book.
225
+ * @param {PageIndex} pindex
226
+ * @return {[PageIndex, PageIndex]} eg [0, 1]
227
+ */
228
+ getSpreadIndices(pindex) {
229
+ if (this.br.pageProgression == 'rl') {
230
+ return this.getPageSide(pindex) == 'R' ? [pindex + 1, pindex] : [pindex, pindex - 1];
231
+ } else {
232
+ return this.getPageSide(pindex) == 'L' ? [pindex, pindex + 1] : [pindex - 1, pindex];
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Single images in the Internet Archive scandata.xml metadata are (somewhat incorrectly)
238
+ * given a "leaf" number. Some of these images from the scanning process should not
239
+ * be displayed in the BookReader (for example colour calibration cards). Since some
240
+ * of the scanned images will not be displayed in the BookReader (those marked with
241
+ * addToAccessFormats false in the scandata.xml) leaf numbers and BookReader page
242
+ * indexes are generally not the same. This function returns the BookReader page
243
+ * index given a scanned leaf number.
244
+ *
245
+ * This function is used, for example, to map between search results (that use the
246
+ * leaf numbers) and the displayed pages in the BookReader.
247
+ * @param {LeafNum} leafNum
248
+ * @return {PageIndex}
249
+ */
250
+ leafNumToIndex(leafNum) {
251
+ const index = this._getDataFlattened()
252
+ .findIndex(d => d.leafNum == leafNum);
253
+ // If no match is found, fall back to the leafNum provide (leafNum == index)
254
+ return index > -1 ? index : leafNum;
255
+ }
256
+
257
+ /**
258
+ * Parses the pageString format
259
+ * @param {PageString} pageString
260
+ * @return {PageIndex|undefined}
261
+ */
262
+ parsePageString(pageString) {
263
+ let pageIndex;
264
+ // Check for special "leaf"
265
+ const leafMatch = /^leaf(\d+)/.exec(pageString);
266
+ if (leafMatch) {
267
+ pageIndex = this.leafNumToIndex(parseInt(leafMatch[1], 10));
268
+ if (pageIndex === null) {
269
+ pageIndex = undefined; // to match return type of getPageIndex
270
+ }
271
+ } else {
272
+ pageIndex = this.getPageIndex(pageString);
273
+ }
274
+ return pageIndex;
275
+ }
276
+
277
+ /**
278
+ * @param {number} index use negatives to get page relative to end
279
+ * @param loop whether to loop (i.e. -1 == last page)
280
+ */
281
+ getPage(index, loop = true) {
282
+ const numLeafs = this.getNumLeafs();
283
+ if (!loop && (index < 0 || index >= numLeafs)) {
284
+ return undefined;
285
+ }
286
+ if (index < 0 && index >= -numLeafs) {
287
+ index += numLeafs;
288
+ }
289
+ index = index % numLeafs;
290
+ return new PageModel(this, index);
291
+ }
292
+
293
+ /**
294
+ * @param {object} [arg0]
295
+ * @param {number} [arg0.start] inclusive
296
+ * @param {number} [arg0.end] exclusive
297
+ * @param {boolean} [arg0.combineConsecutiveUnviewables] Yield only first unviewable
298
+ * of a chunk of unviewable pages instead of each page
299
+ */
300
+ * pagesIterator({ start = 0, end = Infinity, combineConsecutiveUnviewables = false } = {}) {
301
+ start = Math.max(0, start);
302
+ end = Math.min(end, this.getNumLeafs());
303
+
304
+ for (let i = start; i < end; i++) {
305
+ const page = this.getPage(i);
306
+ if (combineConsecutiveUnviewables && page.isConsecutiveUnviewable) continue;
307
+
308
+ yield page;
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Flatten the nested structure (make 1d array), and also add pageSide prop
314
+ * @return {PageData[]}
315
+ */
316
+ _getDataFlattened() {
317
+ if (this._getDataFlattenedCached && this._getDataFlattenedCached[1] === this.br.data.length)
318
+ return this._getDataFlattenedCached[0];
319
+
320
+ let prevPageSide = null;
321
+ /** @type {number|null} */
322
+ let unviewablesChunkStart = null;
323
+ let index = 0;
324
+ // @ts-ignore TS doesn't know about flatMap for some reason
325
+ const flattened = this.br.data.flatMap(spread => {
326
+ return spread.map(page => {
327
+ if (!page.pageSide) {
328
+ if (prevPageSide === null) {
329
+ page.pageSide = spread.length === 2 ? 'L' : 'R';
330
+ } else {
331
+ page.pageSide = prevPageSide === 'L' ? 'R' : 'L';
332
+ }
333
+ }
334
+ prevPageSide = page.pageSide;
335
+
336
+ if (page.viewable === false) {
337
+ if (unviewablesChunkStart === null) {
338
+ page.unviewablesStart = unviewablesChunkStart = index;
339
+ } else {
340
+ page.unviewablesStart = unviewablesChunkStart;
341
+ }
342
+ } else {
343
+ unviewablesChunkStart = null;
344
+ }
345
+
346
+ index++;
347
+ return page;
348
+ });
349
+ });
350
+
351
+ // length is used as a cache breaker
352
+ this._getDataFlattenedCached = [flattened, this.br.data.length];
353
+ return flattened;
354
+ }
355
+
356
+ /**
357
+ * Helper. Return a prop for a given index. Returns `fallbackValue` if index is invalid or
358
+ * property not on page.
359
+ * @param {PageIndex} index
360
+ * @param {keyof PageData} prop
361
+ * @param {*} fallbackValue return if property not on the record
362
+ * @return {*}
363
+ */
364
+ _getDataProp(index, prop, fallbackValue = undefined) {
365
+ const dataf = this._getDataFlattened();
366
+ const invalidIndex = isNaN(index) || index < 0 || index >= dataf.length;
367
+ if (invalidIndex || 'undefined' == typeof(dataf[index][prop]))
368
+ return fallbackValue;
369
+ return dataf[index][prop];
370
+ }
371
+ }
372
+
373
+ /**
374
+ * A controlled schema for page data.
375
+ */
376
+ export class PageModel {
377
+ /**
378
+ * @param {BookModel} book
379
+ * @param {PageIndex} index
380
+ */
381
+ constructor(book, index) {
382
+ // TODO: Get default from config
383
+ this.ppi = book._getDataProp(index, 'ppi', book.ppi);
384
+ this.book = book;
385
+ this.index = index;
386
+ this.width = book.getPageWidth(index);
387
+ this.widthInches = this.width / this.ppi;
388
+ this.height = book.getPageHeight(index);
389
+ this.heightInches = this.height / this.ppi;
390
+ this.pageSide = book.getPageSide(index);
391
+ this.leafNum = book._getDataProp(index, 'leafNum', this.index);
392
+
393
+ /** @type {boolean} */
394
+ this.isViewable = book._getDataProp(index, 'viewable', true);
395
+ /** @type {PageIndex} The first in the series of unviewable pages this is in. */
396
+ this.unviewablesStart = book._getDataProp(index, 'unviewablesStart') || null;
397
+ /**
398
+ * Consecutive unviewable pages are pages in an unviewable "chunk" which are not the first
399
+ * of that chunk.
400
+ */
401
+ this.isConsecutiveUnviewable = !this.isViewable && this.unviewablesStart != this.index;
402
+
403
+ this._rawData = this.book._getDataFlattened()[this.index];
404
+ }
405
+
406
+ /**
407
+ * Updates the page to no longer be unviewable. Assumes the
408
+ * Page's URI is already set/correct.
409
+ */
410
+ makeViewable(newViewableState = true) {
411
+ if (this.isViewable == newViewableState) return;
412
+
413
+ if (newViewableState) {
414
+ this._rawData.viewable = true;
415
+ delete this._rawData.unviewablesStart;
416
+ // Update any subsequent page to now point to the right "start"
417
+ for (const page of this.book.pagesIterator({ start: this.index + 1 })) {
418
+ if (page.isViewable) break;
419
+ page._rawData.unviewablesStart = this.index + 1;
420
+ }
421
+ } else {
422
+ this._rawData.viewable = false;
423
+ this._rawData.unviewablesStart = (this.prev && !this.prev.isViewable) ? this.prev.unviewablesStart : this.index;
424
+ // Update any subsequent page to now point to the right "start"
425
+ for (const page of this.book.pagesIterator({ start: this.index + 1 })) {
426
+ if (!page.isViewable) break;
427
+ page._rawData.unviewablesStart = this._rawData.unviewablesStart;
428
+ }
429
+ }
430
+ }
431
+
432
+ get prev() {
433
+ return this.findPrev();
434
+ }
435
+
436
+ get next() {
437
+ return this.findNext();
438
+ }
439
+
440
+ /**
441
+ * @param {number} reduce
442
+ * @param {number} rotate
443
+ */
444
+ getURI(reduce, rotate) {
445
+ return this.book.getPageURI(this.index, reduce, rotate);
446
+ }
447
+
448
+ /**
449
+ * Returns the srcset with correct URIs or void string if out of range
450
+ * @param {number} reduce
451
+ * @param {number} [rotate]
452
+ */
453
+ getURISrcSet(reduce, rotate = 0) {
454
+ const { reduceSet } = this.book;
455
+ const initialReduce = reduceSet.floor(reduce);
456
+ // We don't need to repeat the initial reduce in the srcset
457
+ const topReduce = reduceSet.decr(initialReduce);
458
+ const reduces = [];
459
+ for (let r = topReduce; r >= 1; r = reduceSet.decr(r)) {
460
+ reduces.push(r);
461
+ }
462
+ return reduces
463
+ .map(r => `${this.getURI(r, rotate)} ${initialReduce / r}x`)
464
+ .join(', ');
465
+ }
466
+
467
+ /**
468
+ * @param {object} [arg0]
469
+ * @param {boolean} [arg0.combineConsecutiveUnviewables] Whether to only yield the first page
470
+ * of a series of unviewable pages instead of each page
471
+ * @return {PageModel|void}
472
+ */
473
+ findNext({ combineConsecutiveUnviewables = false } = {}) {
474
+ return this.book
475
+ .pagesIterator({ start: this.index + 1, combineConsecutiveUnviewables })
476
+ .next().value;
477
+ }
478
+
479
+ /**
480
+ * @param {object} [arg0]
481
+ * @param {boolean} [arg0.combineConsecutiveUnviewables] Whether to only yield the first page
482
+ * of a series of unviewable pages instead of each page
483
+ * @return {PageModel|void}
484
+ */
485
+ findPrev({ combineConsecutiveUnviewables = false } = {}) {
486
+ if (this.index == 0) return undefined;
487
+
488
+ if (combineConsecutiveUnviewables) {
489
+ if (this.isConsecutiveUnviewable) {
490
+ return this.book.getPage(this.unviewablesStart);
491
+ } else {
492
+ // Recursively goes backward through the book
493
+ // TODO make a reverse iterator to make it look identical to findNext
494
+ const prev = new PageModel(this.book, this.index - 1);
495
+ return prev.isViewable ? prev : prev.findPrev({ combineConsecutiveUnviewables });
496
+ }
497
+ } else {
498
+ return new PageModel(this.book, this.index - 1);
499
+ }
500
+ }
501
+ }
502
+
503
+ // There are a few main ways we can reference a specific page in a book:
504
+ /**
505
+ * @typedef {string} PageNumString
506
+ * Possible values: /^n?\d+$/. Example: 'n7', '18'
507
+ * Not necessarily unique
508
+ */
509
+ /**
510
+ * @typedef {number} LeafNum
511
+ * No clue if 0 or 1 indexed or consecutive; generally from IA book info.
512
+ */
513
+ /**
514
+ * @typedef {string} PageString
515
+ * Possible values: /^(leaf)?\d+$/ Example: 'leaf7', '18'
516
+ * If leaf-prefixed, then the number is a LeafNum. Otherwise it's a PageNumString
517
+ */
518
+ /** @typedef {number} PageIndex 0-based index of all the pages */
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Displays a console on the document for debugging devices where remote
3
+ * debugging is not feasible, and forwards all console.log's to be displayed
4
+ * on screen.
5
+ */
6
+ export class DebugConsole {
7
+ constructor() {
8
+ /** How many times we've seen the same line in a row */
9
+ this.currentRun = 0;
10
+ }
11
+
12
+ init() {
13
+ this.$log = $(`<div id="_debugLog" style="width: 100%; height: 300px; overflow: auto" />`);
14
+ $(document.body).prepend(this.$log);
15
+
16
+ this.$form = $(`
17
+ <form>
18
+ <input style="width:100%; font-family: monospace;" id="_debugLogInput">
19
+ </form>`);
20
+ this.$log.append(this.$form);
21
+
22
+ this.$form.submit(ev => {
23
+ ev.preventDefault();
24
+ const result = eval(this.$form.find('input').val());
25
+ this.logToScreen([result]);
26
+ });
27
+
28
+ const _realLog = console.log.bind(console);
29
+ console.log = (...args) => {
30
+ _realLog(...args);
31
+ this.logToScreen(args);
32
+ };
33
+
34
+ window.onerror = (...args) => this.logToScreen(args);
35
+ }
36
+
37
+ /**
38
+ * Log the provided array onto the on screen console
39
+ * @param {Array} args
40
+ */
41
+ logToScreen(args) {
42
+ const html = args.map(JSON.stringify).join(',');
43
+ const $lastEntry = this.$log.children('.log-entry:last-child');
44
+ if ($lastEntry.find('.entry-code').html() == html) {
45
+ $lastEntry.find('.count').text(`(${++this.currentRun})`);
46
+ } else {
47
+ this.currentRun = 1;
48
+ this.$log.append($(`
49
+ <div class="log-entry">
50
+ <code class="count"></code> <code class="entry-code">${html}</code>
51
+ </div>`));
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,116 @@
1
+ // @ts-check
2
+ /**
3
+ * Creates an image cache dictionary
4
+ * storing images in `<img>` tags so that
5
+ * BookReader can leverage browser caching
6
+ */
7
+ /** @typedef {import("./BookModel").BookModel} BookModel */
8
+ /** @typedef {import("./BookModel").PageIndex} PageIndex */
9
+ /** @typedef {import("./ReduceSet").ReduceSet} ReduceSet */
10
+
11
+ import { Pow2ReduceSet } from "./ReduceSet";
12
+
13
+ export class ImageCache {
14
+ /**
15
+ * @param {BookModel} book
16
+ * @param {object} opts
17
+ * @param {boolean} [opts.useSrcSet]
18
+ * @param {ReduceSet} [opts.reduceSet]
19
+ */
20
+ constructor(book, { useSrcSet = false, reduceSet = Pow2ReduceSet } = {}) {
21
+ this.book = book;
22
+ this.useSrcSet = useSrcSet;
23
+ this.reduceSet = reduceSet;
24
+ /** @type {{ [index: number]: { reduce: number, loaded: boolean }[] }} */
25
+ this.cache = {};
26
+ this.defaultScale = 8;
27
+ }
28
+
29
+ /**
30
+ * Get an image
31
+ * Checks cache first if image is available & of equal/better scale,
32
+ * if not, a new image gets created
33
+ *
34
+ * @param {PageIndex} index
35
+ * @param {Number} reduce
36
+ */
37
+ image(index, reduce) {
38
+ const cachedImages = this.cache[index] || [];
39
+ const sufficientImages = cachedImages
40
+ .filter(x => x.loaded && x.reduce <= reduce);
41
+ if (sufficientImages.length) {
42
+ // Choose the largest reduction factor that meets our needs
43
+ const bestReduce = Math.max(...sufficientImages.map(e => e.reduce));
44
+ return this._serveImageElement(index, bestReduce);
45
+ } else {
46
+ // Don't use a cache entry; i.e. a fresh fetch will be made
47
+ // for this reduce
48
+ return this._serveImageElement(index, reduce);
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Checks if an image of equal or greater quality has been loaded
54
+ * @param {PageIndex} index
55
+ * @param {Number} reduce
56
+ * @returns {Boolean}
57
+ */
58
+ imageLoaded(index, reduce) {
59
+ const cacheImg = this.cache[index]?.find(e => e.reduce <= reduce);
60
+ return cacheImg?.loaded ?? false;
61
+ }
62
+
63
+ /**
64
+ * Get the best image that's already loaded for the given index,
65
+ * trying to choose values less that the given reduce
66
+ * @param {PageIndex} index
67
+ * @param idealMaxReduce
68
+ * @returns {null | number}
69
+ */
70
+ getBestLoadedReduce(index, idealMaxReduce = Infinity) {
71
+ const candidates = this.cache[index]?.filter(x => x.loaded) || [];
72
+ if (!candidates.length) return null;
73
+
74
+ const lowerResImages = candidates.filter(e => e.reduce >= idealMaxReduce);
75
+ if (lowerResImages.length) {
76
+ // Choose the highest quality loaded lower res image
77
+ return Math.min(...lowerResImages.map(e => e.reduce));
78
+ }
79
+ // Otherwise choose whatever is closest to the reduce
80
+ const higherRestImages = candidates.filter(e => e.reduce < idealMaxReduce);
81
+ return Math.max(...higherRestImages.map(e => e.reduce));
82
+ }
83
+
84
+ /**
85
+ * @private
86
+ * Generates an image element on the fly from image info in cache
87
+ *
88
+ * @param {PageIndex} index
89
+ * @param {number} reduce
90
+ * @returns {JQuery<HTMLImageElement>} with base image classes
91
+ */
92
+ _serveImageElement(index, reduce) {
93
+ const validReduce = this.reduceSet.floor(reduce);
94
+ let cacheEntry = this.cache[index]?.find(e => e.reduce == validReduce);
95
+ if (!cacheEntry) {
96
+ cacheEntry = { reduce: validReduce, loaded: false };
97
+ const entries = this.cache[index] || (this.cache[index] = []);
98
+ entries.push(cacheEntry);
99
+ }
100
+ const page = this.book.getPage(index);
101
+
102
+ const $img = $('<img />', {
103
+ 'class': 'BRpageimage',
104
+ 'alt': 'Book page image',
105
+ src: page.getURI(validReduce, 0),
106
+ })
107
+ .data('reduce', validReduce);
108
+ if (this.useSrcSet) {
109
+ $img.attr('srcset', page.getURISrcSet(validReduce));
110
+ }
111
+ if (!cacheEntry.loaded) {
112
+ $img.one('load', () => cacheEntry.loaded = true);
113
+ }
114
+ return $img;
115
+ }
116
+ }