@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,1372 @@
1
+ // @ts-check
2
+ // effect.js gives acces to extra easing function (e.g. easeInSine)
3
+ import 'jquery-ui/ui/effect.js';
4
+ import '../dragscrollable-br.js';
5
+ import { clamp } from './utils.js';
6
+ import { EVENTS } from './events.js';
7
+ import { ModeSmoothZoom } from "./ModeSmoothZoom.js";
8
+ import { HTMLDimensionsCacher } from './utils/HTMLDimensionsCacher.js';
9
+
10
+ /** @typedef {import('../BookReader.js').default} BookReader */
11
+ /** @typedef {import('./BookModel.js').BookModel} BookModel */
12
+ /** @typedef {import('./BookModel.js').PageIndex} PageIndex */
13
+ /** @typedef {import('./options.js').BookReaderOptions} BookReaderOptions */
14
+ /** @typedef {import('./PageContainer.js').PageContainer} PageContainer */
15
+ /** @typedef {import('./ModeSmoothZoom').SmoothZoomable} SmoothZoomable */
16
+
17
+ /** @implements {SmoothZoomable} */
18
+ export class Mode2Up {
19
+ /**
20
+ * @param {BookReader} br
21
+ * @param {BookModel} bookModel
22
+ */
23
+ constructor(br, bookModel) {
24
+ this.br = br;
25
+ this.book = bookModel;
26
+
27
+ /** @type {HTMLDivElement} */
28
+ this.leafEdgeL = null;
29
+ /** @type {HTMLDivElement} */
30
+ this.leafEdgeR = null;
31
+
32
+ /** @type {{ [index: number]: PageContainer }} */
33
+ this.pageContainers = {};
34
+
35
+ /** @type {ModeSmoothZoom} */
36
+ this.smoothZoomer = null;
37
+ this._scale = 1;
38
+ this.scaleCenter = { x: 0.5, y: 0.5 };
39
+ }
40
+
41
+ get $container() {
42
+ return this.br.refs.$brContainer[0];
43
+ }
44
+ get $visibleWorld() {
45
+ return this.br.refs.$brTwoPageView?.[0];
46
+ }
47
+
48
+ get scale() { return this._scale; }
49
+ set scale(newVal) {
50
+ this.$visibleWorld.style.transform = `scale(${newVal})`;
51
+ this.updateViewportOnZoom(newVal, this._scale);
52
+ this._scale = newVal;
53
+ }
54
+
55
+ /**
56
+ * @param {PageIndex} index
57
+ */
58
+ jumpToIndex(index) {
59
+ // By checking against min/max we do nothing if requested index
60
+ // is current
61
+ if (index < Math.min(this.br.twoPage.currentIndexL, this.br.twoPage.currentIndexR)) {
62
+ this.flipBackToIndex(index);
63
+ } else if (index > Math.max(this.br.twoPage.currentIndexL, this.br.twoPage.currentIndexR)) {
64
+ this.flipFwdToIndex(index);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Draws book spread,
70
+ * sets event handlers,
71
+ * sets: `this.br.displayedIndices`
72
+ * updates toolbar zoom
73
+ * Important: `this.br.refs.$brTwoPageView` parent container must be emptied before calling
74
+ */
75
+ drawLeafs() {
76
+ const $twoPageViewEl = this.br.refs.$brTwoPageView;
77
+ const indexL = this.br.twoPage.currentIndexL;
78
+ const indexR = this.br.twoPage.currentIndexR;
79
+
80
+ this.createPageContainer(indexL).$container
81
+ .css(this.leftLeafCss)
82
+ .appendTo($twoPageViewEl);
83
+ this.createPageContainer(indexR).$container
84
+ .css(this.rightLeafCss)
85
+ .appendTo($twoPageViewEl);
86
+
87
+ this.displayedIndices = [this.br.twoPage.currentIndexL, this.br.twoPage.currentIndexR];
88
+ this.br.displayedIndices = this.displayedIndices;
89
+ this.br.updateToolbarZoom(this.br.reduce);
90
+ this.br.trigger('pageChanged');
91
+ }
92
+
93
+ /**
94
+ * @param {1} direction
95
+ */
96
+ zoom(direction) {
97
+ this.br.stopFlipAnimations();
98
+
99
+ // Recalculate autofit factors
100
+ this.calculateReductionFactors();
101
+
102
+ // Get new zoom state
103
+ const reductionFactor = this.br.nextReduce(this.br.reduce, direction, this.br.twoPage.reductionFactors);
104
+ if ((this.br.reduce == reductionFactor.reduce) && (this.br.twoPage.autofit == reductionFactor.autofit)) {
105
+ // Same zoom
106
+ return;
107
+ }
108
+ this.br.twoPage.autofit = reductionFactor.autofit;
109
+ this.br.reduce = reductionFactor.reduce;
110
+ this.br.pageScale = this.br.reduce; // preserve current reduce
111
+
112
+ // Preserve view center position
113
+ const oldCenter = this.getViewCenter();
114
+
115
+ // Prepare view with new center to minimize visual glitches
116
+ const drawNewSpread = true;
117
+ this.prepareTwoPageView(oldCenter.percentageX, oldCenter.percentageY, drawNewSpread);
118
+ }
119
+
120
+ /**
121
+ * Resize spread containers, does not prefetch
122
+ * uses `this.br.twoPage` properties
123
+ */
124
+ resizeSpread() {
125
+ this.br.resizeBRcontainer(false); // no animation
126
+ this.calculateSpreadSize();
127
+
128
+ this.br.refs?.$brTwoPageView.css(this.mainContainerCss);
129
+ this.centerView(); // let function self adjust
130
+
131
+ $(this.br.twoPage.coverDiv).css(this.spreadCoverCss); // click sheath is memoized somehow
132
+ const $spreadLayers = this.br.refs.$brTwoPageView;
133
+
134
+ $spreadLayers.find('.BRleafEdgeR')?.css(this.leafEdgeRCss);
135
+ $spreadLayers.find('.BRleafEdgeL')?.css(this.leafEdgeLCss);
136
+ $spreadLayers.find('.BRgutter')?.css(this.spineCss);
137
+
138
+ const indexL = this.br.twoPage.currentIndexL;
139
+ const indexR = this.br.twoPage.currentIndexR;
140
+ this.pageContainers[indexL].$container.css(this.leftLeafCss);
141
+ this.pageContainers[indexR].$container.css(this.rightLeafCss);
142
+ }
143
+
144
+ /**
145
+ * @param {number} centerPercentageX
146
+ * @param {number} centerPercentageY
147
+ * @param {Boolean} drawNewSpread
148
+ */
149
+ prepareTwoPageView(centerPercentageX, centerPercentageY, drawNewSpread = false) {
150
+ // Some decisions about two page view:
151
+ //
152
+ // Both pages will be displayed at the same height, even if they were different physical/scanned
153
+ // sizes. This simplifies the animation (from a design as well as technical standpoint). We
154
+ // examine the page aspect ratios (in calculateSpreadSize) and use the page with the most "normal"
155
+ // aspect ratio to determine the height.
156
+ //
157
+ // The two page view div is resized to keep the middle of the book in the middle of the div
158
+ // even as the page sizes change. To e.g. keep the middle of the book in the middle of the BRcontent
159
+ // div requires adjusting the offset of BRtwpageview and/or scrolling in BRcontent.
160
+ const startingReduce = this.br.reduce;
161
+ const startingIndices = this.br.displayedIndices;
162
+
163
+ this.br.refs.$brContainer.empty();
164
+ this.br.refs.$brContainer.css('overflow', 'auto');
165
+
166
+ // We want to display two facing pages. We may be missing
167
+ // one side of the spread because it is the first/last leaf,
168
+ // foldouts, missing pages, etc
169
+
170
+ const targetLeaf = clamp(this.br.firstIndex, this.br.firstDisplayableIndex(), this.br.lastDisplayableIndex());
171
+ const currentSpreadIndices = this.book.getSpreadIndices(targetLeaf);
172
+ this.br.twoPage.currentIndexL = currentSpreadIndices[0];
173
+ this.br.twoPage.currentIndexR = currentSpreadIndices[1];
174
+
175
+ this.calculateSpreadSize(); //sets this.br.reduce, twoPage.width, twoPage.height and others
176
+
177
+ /* check if calculations have changed that warrant a new book draw */
178
+ const sameReducer = startingReduce == this.br.reduce;
179
+ const sameStart = startingIndices == this.br.displayedIndices;
180
+ const hasNewDisplayPagesOrDimensions = !sameStart || (sameStart && !sameReducer);
181
+
182
+ if (drawNewSpread || hasNewDisplayPagesOrDimensions) {
183
+ this.prunePageContainers();
184
+ this.prefetch();
185
+ }
186
+
187
+ // Add the two page view
188
+ // $$$ Can we get everything set up and then append?
189
+ this.br.refs.$brTwoPageView = this.br.refs.$brTwoPageView || $('<div class="BRtwopageview"></div>');
190
+ const $twoPageViewEl = this.br.refs.$brTwoPageView;
191
+ $twoPageViewEl.empty();
192
+ $twoPageViewEl[0].style.transformOrigin = '0 0';
193
+ this.br.refs.$brContainer.append($twoPageViewEl);
194
+
195
+ // Attaches to first child, so must come after we add the page view
196
+ this.br.refs.$brContainer.dragscrollable({
197
+ preventDefault:true,
198
+ // Only handle mouse events; let browser/HammerJS handle touch
199
+ dragstart: 'mousedown',
200
+ dragcontinue: 'mousemove',
201
+ dragend: 'mouseup',
202
+ });
203
+
204
+ this.attachMouseHandlers();
205
+
206
+ // $$$ calculate container size first
207
+ this.br.refs?.$brTwoPageView.css(this.mainContainerCss);
208
+
209
+ // This will trump the incoming coordinates
210
+ // in order to center book when zooming out
211
+ if (this.br.twoPage.totalWidth < this.br.refs.$brContainer.prop('clientWidth')) {
212
+ centerPercentageX = 0.5;
213
+ }
214
+ if (this.br.twoPage.totalHeight < this.br.refs.$brContainer.prop('clientHeight')) {
215
+ centerPercentageY = 0.5;
216
+ }
217
+
218
+ this.centerView(centerPercentageX, centerPercentageY);
219
+
220
+ // then set
221
+ this.br.twoPage.coverDiv = document.createElement('div');
222
+ $(this.br.twoPage.coverDiv).attr('class', 'BRbookcover').css(this.spreadCoverCss).appendTo(this.br.refs.$brTwoPageView);
223
+
224
+ this.leafEdgeR = document.createElement('div');
225
+ this.leafEdgeR.className = 'BRleafEdgeR';
226
+ $(this.leafEdgeR).css(this.leafEdgeRCss).appendTo(this.br.refs.$brTwoPageView);
227
+
228
+ this.leafEdgeL = document.createElement('div');
229
+ this.leafEdgeL.className = 'BRleafEdgeL';
230
+ $(this.leafEdgeL).css(this.leafEdgeLCss).appendTo(this.br.refs.$brTwoPageView);
231
+
232
+ const div = document.createElement('div');
233
+ $(div).attr('class', 'BRgutter').css(this.spineCss).appendTo(this.br.refs.$brTwoPageView);
234
+
235
+ this.preparePopUp();
236
+
237
+ this.br.displayedIndices = [];
238
+
239
+ this.drawLeafs();
240
+ this.br.updateToolbarZoom(this.br.reduce);
241
+ this.br.updateBrClasses();
242
+
243
+ this.smoothZoomer = this.smoothZoomer || new ModeSmoothZoom(this);
244
+ this.smoothZoomer.attach();
245
+
246
+ this.htmlDimensionsCacher = this.htmlDimensionsCacher || new HTMLDimensionsCacher(this.$container);
247
+ }
248
+
249
+ unprepare() {
250
+ // Mode2Up attaches these listeners to the main BR container, so we need to
251
+ // detach these or it will cause issues for the other modes.
252
+ this.smoothZoomer.detach();
253
+ }
254
+
255
+ /**
256
+ * @param {number} newScale
257
+ * @param {number} oldScale
258
+ */
259
+ updateViewportOnZoom(newScale, oldScale) {
260
+ const container = this.br.refs.$brContainer[0];
261
+ const { scrollTop: T, scrollLeft: L } = container;
262
+ const W = this.htmlDimensionsCacher.clientWidth;
263
+ const H = this.htmlDimensionsCacher.clientHeight;
264
+
265
+ // Scale factor change
266
+ const F = newScale / oldScale;
267
+
268
+ // Where in the viewport the zoom is centered on
269
+ const XPOS = this.scaleCenter.x;
270
+ const YPOS = this.scaleCenter.y;
271
+ const oldCenter = {
272
+ x: L + XPOS * W,
273
+ y: T + YPOS * H,
274
+ };
275
+ const newCenter = {
276
+ x: F * oldCenter.x,
277
+ y: F * oldCenter.y,
278
+ };
279
+ container.scrollTop = newCenter.y - YPOS * H;
280
+ container.scrollLeft = newCenter.x - XPOS * W;
281
+
282
+ // Also update the visible page containers to load in highres if necessary
283
+ this.pageContainers[this.br.twoPage.currentIndexL]?.update({ reduce: this.br.reduce / newScale });
284
+ this.pageContainers[this.br.twoPage.currentIndexR]?.update({ reduce: this.br.reduce / newScale });
285
+ }
286
+
287
+ prunePageContainers() {
288
+ for (const index in this.pageContainers) {
289
+ if ((index != this.br.twoPage.currentIndexL) && (index != this.br.twoPage.currentIndexR)) {
290
+ $(this.pageContainers[index].$container).remove();
291
+ }
292
+ if ((index < this.br.twoPage.currentIndexL - 4) || (index > this.br.twoPage.currentIndexR + 4)) {
293
+ delete this.pageContainers[index];
294
+ }
295
+ }
296
+ }
297
+
298
+ /**
299
+ * This function prepares the "View Page n" popup that shows while the mouse is
300
+ * over the left/right "stack of sheets" edges. It also binds the mouse
301
+ * events for these divs.
302
+ */
303
+ preparePopUp() {
304
+ this.br.twoPagePopUp = document.createElement('div');
305
+ this.br.twoPagePopUp.className = 'BRtwoPagePopUp';
306
+ $(this.br.twoPagePopUp).css({
307
+ zIndex: '1000'
308
+ }).appendTo(this.br.refs.$brContainer);
309
+ $(this.br.twoPagePopUp).hide();
310
+
311
+ const leafEdges = [
312
+ {
313
+ $leafEdge: $(this.leafEdgeL),
314
+ /** @type {function(number): PageIndex} */
315
+ jumpIndexForPageX: this.jumpIndexForLeftEdgePageX.bind(this),
316
+ leftOffset: () => -$(this.br.twoPagePopUp).width() + 120,
317
+ },
318
+ {
319
+ $leafEdge: $(this.leafEdgeR),
320
+ /** @type {function(number): PageIndex} */
321
+ jumpIndexForPageX: this.jumpIndexForRightEdgePageX.bind(this),
322
+ leftOffset: () => -120,
323
+ },
324
+ ];
325
+
326
+ for (const { $leafEdge, jumpIndexForPageX, leftOffset } of leafEdges) {
327
+ $leafEdge.on('mouseenter', () => $(this.br.twoPagePopUp).show());
328
+ $leafEdge.on('mouseleave', () => $(this.br.twoPagePopUp).hide());
329
+
330
+ $leafEdge.on('click', e => {
331
+ this.br.trigger(EVENTS.stop);
332
+ this.br.jumpToIndex(jumpIndexForPageX(e.pageX));
333
+ });
334
+
335
+ $leafEdge.on('mousemove', e => {
336
+ const jumpIndex = clamp(jumpIndexForPageX(e.pageX), 0, this.book.getNumLeafs() - 1);
337
+ $(this.br.twoPagePopUp).text(`View ${this.book.getPageName(jumpIndex)}`);
338
+
339
+ // $$$ TODO: Make sure popup is positioned so that it is in view
340
+ // (https://bugs.edge.launchpad.net/gnubook/+bug/327456)
341
+ $(this.br.twoPagePopUp).css({
342
+ left: `${e.pageX - this.br.refs.$brContainer.offset().left + this.br.refs.$brContainer.scrollLeft() + leftOffset()}px`,
343
+ top: `${e.pageY - this.br.refs.$brContainer.offset().top + this.br.refs.$brContainer.scrollTop()}px`
344
+ });
345
+ });
346
+ }
347
+ }
348
+
349
+ setSpreadIndices() {
350
+ const targetLeaf = clamp(this.br.firstIndex, this.br.firstDisplayableIndex(), this.br.lastDisplayableIndex());
351
+ const currentSpreadIndices = this.book.getSpreadIndices(targetLeaf);
352
+ this.br.twoPage.currentIndexL = currentSpreadIndices[0];
353
+ this.br.twoPage.currentIndexR = currentSpreadIndices[1];
354
+ }
355
+
356
+ /**
357
+ * Calculates 2-page spread dimensions based on this.br.twoPage.currentIndexL and
358
+ * this.br.twoPage.currentIndexR
359
+ * This function sets this.br.twoPage.height, twoPage.width
360
+ */
361
+ calculateSpreadSize() {
362
+ const firstIndex = this.br.twoPage.currentIndexL;
363
+ const secondIndex = this.br.twoPage.currentIndexR;
364
+
365
+ // Calculate page sizes and total leaf width
366
+ let spreadSize;
367
+ if ( this.br.twoPage.autofit) {
368
+ spreadSize = this.getIdealSpreadSize(firstIndex, secondIndex);
369
+ } else {
370
+ // set based on reduction factor
371
+ spreadSize = this.getSpreadSizeFromReduce(firstIndex, secondIndex, this.br.reduce);
372
+ }
373
+ // Both pages together
374
+ this.br.twoPage.height = spreadSize.height || 0;
375
+ this.br.twoPage.width = spreadSize.width || 0;
376
+
377
+ // Individual pages
378
+ this.br.twoPage.scaledWL = this.getPageWidth(firstIndex) || 0;
379
+ this.br.twoPage.scaledWR = this.getPageWidth(secondIndex) || 0;
380
+
381
+ // Leaf edges
382
+ this.br.twoPage.edgeWidth = spreadSize.totalLeafEdgeWidth; // The combined width of both edges
383
+ this.br.twoPage.leafEdgeWidthL = this.br.leafEdgeWidth(this.br.twoPage.currentIndexL);
384
+ this.br.twoPage.leafEdgeWidthR = this.br.twoPage.edgeWidth - this.br.twoPage.leafEdgeWidthL;
385
+
386
+
387
+ // Book cover
388
+ // The width of the book cover div. The combined width of both pages, twice the width
389
+ // of the book cover internal padding (2*10) and the page edges
390
+ this.br.twoPage.bookCoverDivWidth = this.coverWidth(this.br.twoPage.scaledWL + this.br.twoPage.scaledWR);
391
+ // The height of the book cover div
392
+ this.br.twoPage.bookCoverDivHeight = this.br.twoPage.height + 2 * this.br.twoPage.coverInternalPadding;
393
+
394
+
395
+ // We calculate the total width and height for the div so that we can make the book
396
+ // spine centered
397
+ const leftGutterOffset = this.gutterOffsetForIndex(firstIndex);
398
+ const leftWidthFromCenter = this.br.twoPage.scaledWL - leftGutterOffset + this.br.twoPage.leafEdgeWidthL;
399
+ const rightWidthFromCenter = this.br.twoPage.scaledWR + leftGutterOffset + this.br.twoPage.leafEdgeWidthR;
400
+ const largestWidthFromCenter = Math.max( leftWidthFromCenter, rightWidthFromCenter );
401
+ this.br.twoPage.totalWidth = 2 * (largestWidthFromCenter + this.br.twoPage.coverInternalPadding + this.br.twoPage.coverExternalPadding);
402
+ this.br.twoPage.totalHeight = this.br.twoPage.height + 2 * (this.br.twoPage.coverInternalPadding + this.br.twoPage.coverExternalPadding);
403
+
404
+ // We want to minimize the unused space in two-up mode (maximize the amount of page
405
+ // shown). We give width to the leaf edges and these widths change (though the sum
406
+ // of the two remains constant) as we flip through the book. With the book
407
+ // cover centered and fixed in the BRcontainer div the page images will meet
408
+ // at the "gutter" which is generally offset from the center.
409
+ this.br.twoPage.middle = this.br.twoPage.totalWidth >> 1;
410
+ this.br.twoPage.gutter = this.br.twoPage.middle + this.gutterOffsetForIndex(firstIndex);
411
+
412
+ // The left edge of the book cover moves depending on the width of the pages
413
+ // $$$ change to getter
414
+ this.br.twoPage.bookCoverDivLeft = this.br.twoPage.gutter - this.br.twoPage.scaledWL - this.br.twoPage.leafEdgeWidthL - this.br.twoPage.coverInternalPadding;
415
+ // The top edge of the book cover stays a fixed distance from the top
416
+ this.br.twoPage.bookCoverDivTop = this.br.twoPage.coverExternalPadding;
417
+
418
+ // Book spine
419
+ this.br.twoPage.bookSpineDivHeight = this.br.twoPage.height + 2 * this.br.twoPage.coverInternalPadding;
420
+ this.br.twoPage.bookSpineDivLeft = this.br.twoPage.middle - (this.br.twoPage.bookSpineDivWidth >> 1);
421
+ this.br.twoPage.bookSpineDivTop = this.br.twoPage.bookCoverDivTop;
422
+
423
+ this.br.reduce = spreadSize.reduce < 0 ? this.br.reduce : spreadSize.reduce; // $$$ really set this here?
424
+ }
425
+
426
+ /**
427
+ *
428
+ * @param {number} firstIndex
429
+ * @param {number} secondIndex
430
+ * @return {{ width: number, height: number, totalLeafEdgeWidth: number, reduce: number}}
431
+ */
432
+ getIdealSpreadSize(firstIndex, secondIndex) {
433
+ const ideal = {};
434
+
435
+ // We check which page is closest to a "normal" page and use that to set the height
436
+ // for both pages. This means that foldouts and other odd size pages will be displayed
437
+ // smaller than the nominal zoom amount.
438
+ const canon5Dratio = 1.5;
439
+
440
+ const first = {
441
+ height: this.book._getPageHeight(firstIndex),
442
+ width: this.book._getPageWidth(firstIndex)
443
+ };
444
+
445
+ const second = {
446
+ height: this.book._getPageHeight(secondIndex),
447
+ width: this.book._getPageWidth(secondIndex)
448
+ };
449
+
450
+ const firstIndexRatio = first.height / first.width;
451
+ const secondIndexRatio = second.height / second.width;
452
+
453
+ let ratio;
454
+ if (Math.abs(firstIndexRatio - canon5Dratio) < Math.abs(secondIndexRatio - canon5Dratio)) {
455
+ ratio = firstIndexRatio;
456
+ } else {
457
+ ratio = secondIndexRatio;
458
+ }
459
+
460
+ const totalLeafEdgeWidth = Math.floor(this.book.getNumLeafs() * 0.1);
461
+ const maxLeafEdgeWidth = Math.floor(this.br.refs.$brContainer.prop('clientWidth') * 0.1);
462
+ ideal.totalLeafEdgeWidth = Math.min(totalLeafEdgeWidth, maxLeafEdgeWidth);
463
+
464
+ const widthOutsidePages = 2 * (this.br.twoPage.coverInternalPadding + this.br.twoPage.coverExternalPadding) + ideal.totalLeafEdgeWidth;
465
+ const heightOutsidePages = 2 * (this.br.twoPage.coverInternalPadding + this.br.twoPage.coverExternalPadding);
466
+
467
+ ideal.width = (this.br.refs.$brContainer.width() - widthOutsidePages) >> 1;
468
+ ideal.width = ideal.width > 10 ? ideal.width - 10 : 1; // $$$ fudge factor
469
+
470
+ ideal.height = this.br.refs.$brContainer.height() - heightOutsidePages;
471
+ ideal.height = ideal.height > 15 ? ideal.height - 15 : 1; // $$$ fudge factor
472
+
473
+ if (ideal.height / ratio <= ideal.width) {
474
+ //use height
475
+ ideal.width = Math.floor(ideal.height / ratio) || 1;
476
+ } else {
477
+ //use width
478
+ ideal.height = Math.floor(ideal.width * ratio) || 1;
479
+ }
480
+
481
+ // $$$ check this logic with large spreads
482
+ ideal.reduce = Math.round(((first.height + second.height) / 2) / ideal.height);
483
+
484
+ return ideal;
485
+ }
486
+
487
+ /**
488
+ * Returns the spread size calculated from the reduction factor for the given pages
489
+ * @param {number} firstIndex
490
+ * @param {number} secondIndex
491
+ * @return {Object}
492
+ */
493
+ getSpreadSizeFromReduce(firstIndex, secondIndex, reduce) {
494
+ const spreadSize = {};
495
+ // $$$ Scale this based on reduce?
496
+ const totalLeafEdgeWidth = Math.floor(this.book.getNumLeafs() * 0.1);
497
+ // $$$ Assumes leaf edge width constant at all zoom levels
498
+ const maxLeafEdgeWidth = Math.floor(this.br.refs.$brContainer.prop('clientWidth') * 0.1);
499
+ spreadSize.totalLeafEdgeWidth = Math.min(totalLeafEdgeWidth, maxLeafEdgeWidth);
500
+
501
+ // $$$ Possibly incorrect -- we should make height "dominant"
502
+ const nativeWidth = this.book._getPageWidth(firstIndex) + this.book._getPageWidth(secondIndex);
503
+ const nativeHeight = this.book._getPageHeight(firstIndex) + this.book._getPageHeight(secondIndex);
504
+ spreadSize.height = Math.floor( (nativeHeight / 2) / this.br.reduce );
505
+ spreadSize.width = Math.floor( (nativeWidth / 2) / this.br.reduce );
506
+ spreadSize.reduce = reduce;
507
+
508
+ return spreadSize;
509
+ }
510
+
511
+ /**
512
+ * Returns the current ideal reduction factor
513
+ * @return {number}
514
+ */
515
+ getAutofitReduce() {
516
+ const spreadSize = this.getIdealSpreadSize(this.br.twoPage.currentIndexL, this.br.twoPage.currentIndexR);
517
+ return spreadSize.reduce;
518
+ }
519
+
520
+ /**
521
+ * Returns true if the pages extend past the edge of the view
522
+ * @deprecated slated for deprecation by v5.0.0
523
+ * @return {boolean}
524
+ */
525
+ isZoomedIn() {
526
+ let isZoomedIn = false;
527
+ if (this.br.twoPage.autofit != 'auto') {
528
+ if (this.br.reduce < this.getAutofitReduce()) {
529
+ isZoomedIn = true;
530
+ }
531
+ }
532
+ return isZoomedIn;
533
+ }
534
+
535
+ calculateReductionFactors() {
536
+ this.br.twoPage.reductionFactors = this.br.reductionFactors.concat([
537
+ {
538
+ reduce: this.getIdealSpreadSize( this.br.twoPage.currentIndexL, this.br.twoPage.currentIndexR ).reduce,
539
+ autofit: 'auto'
540
+ }
541
+ ]);
542
+ this.br.twoPage.reductionFactors.sort(this.br._reduceSort);
543
+ }
544
+
545
+ /**
546
+ * Set the cursor for two page view
547
+ * @deprecated Since version 4.3.3. Will be deleted in version 5.0
548
+ */
549
+ setCursor() {
550
+ console.warn('Call to deprecated method, Mode2Up.setCursor. No-op.');
551
+ }
552
+
553
+ /**
554
+ * @param {Number|null} index to flip back one spread, pass index=null
555
+ */
556
+ flipBackToIndex(index) {
557
+ if (this.br.constMode1up == this.br.mode) return;
558
+ if (this.br.animating) return;
559
+
560
+ if (null != this.br.leafEdgeTmp) {
561
+ alert('error: leafEdgeTmp should be null!');
562
+ return;
563
+ }
564
+
565
+ if (null == index) {
566
+ const {currentIndexL, currentIndexR} = this.br.twoPage;
567
+ const minDisplayedIndex = Math.min(currentIndexL, currentIndexR);
568
+ const prev = this.book.getPage(minDisplayedIndex).findPrev({ combineConsecutiveUnviewables: true });
569
+ if (!prev) return;
570
+ index = prev.index;
571
+ // Can only flip to a left page
572
+ // (downstream code handles index = -1, so this is ok I guess)
573
+ if (prev.pageSide == 'R') index--;
574
+ }
575
+
576
+ this.br.updateNavIndexThrottled(index);
577
+
578
+ const previousIndices = this.book.getSpreadIndices(index);
579
+
580
+ if (previousIndices[0] < this.br.firstDisplayableIndex() || previousIndices[1] < this.br.firstDisplayableIndex()) {
581
+ return;
582
+ }
583
+
584
+ this.br.animating = true;
585
+
586
+ if ('rl' != this.br.pageProgression) {
587
+ // Assume LTR and we are going backward
588
+ this.prepareFlipLeftToRight(previousIndices[0], previousIndices[1]);
589
+ this.flipLeftToRight(previousIndices[0], previousIndices[1]);
590
+ } else {
591
+ // RTL and going backward
592
+ this.prepareFlipRightToLeft(previousIndices[0], previousIndices[1]);
593
+ this.flipRightToLeft(previousIndices[0], previousIndices[1]);
594
+ }
595
+ }
596
+
597
+ /**
598
+ * Flips the page on the left towards the page on the right
599
+ * @param {number} newIndexL
600
+ * @param {number} newIndexR
601
+ */
602
+ flipLeftToRight(newIndexL, newIndexR) {
603
+ this.br.refs.$brContainer.addClass("BRpageFlipping");
604
+ const leftLeaf = this.br.twoPage.currentIndexL;
605
+
606
+ const oldLeafEdgeWidthL = this.br.leafEdgeWidth(this.br.twoPage.currentIndexL);
607
+ const newLeafEdgeWidthL = this.br.leafEdgeWidth(newIndexL);
608
+ const leafEdgeTmpW = oldLeafEdgeWidthL - newLeafEdgeWidthL;
609
+
610
+ const currWidthL = this.getPageWidth(leftLeaf);
611
+ const newWidthL = this.getPageWidth(newIndexL);
612
+ const newWidthR = this.getPageWidth(newIndexR);
613
+
614
+ const top = this.top();
615
+ const gutter = this.br.twoPage.middle + this.gutterOffsetForIndex(newIndexL);
616
+
617
+ //animation strategy:
618
+ // 0. remove search highlight, if any.
619
+ // 1. create a new div, called leafEdgeTmp to represent the leaf edge between the leftmost edge
620
+ // of the left leaf and where the user clicked in the leaf edge.
621
+ // Note that if this function was triggered by left() and not a
622
+ // mouse click, the width of leafEdgeTmp is very small (zero px).
623
+ // 2. animate both leafEdgeTmp to the gutter (without changing its width) and animate
624
+ // leftLeaf to width=0.
625
+ // 3. When step 2 is finished, animate leafEdgeTmp to right-hand side of new right leaf
626
+ // (left=gutter+newWidthR) while also animating the new right leaf from width=0 to
627
+ // its new full width.
628
+ // 4. After step 3 is finished, do the following:
629
+ // - remove leafEdgeTmp from the dom.
630
+ // - resize and move the right leaf edge (leafEdgeR) to left=gutter+newWidthR
631
+ // and width=twoPage.edgeWidth-newLeafEdgeWidthL.
632
+ // - resize and move the left leaf edge (leafEdgeL) to left=gutter-newWidthL-newLeafEdgeWidthL
633
+ // and width=newLeafEdgeWidthL.
634
+ // - resize the back cover (twoPage.coverDiv) to left=gutter-newWidthL-newLeafEdgeWidthL-10
635
+ // and width=newWidthL+newWidthR+twoPage.edgeWidth+20
636
+ // - move new left leaf (newIndexL) forward to zindex=2 so it can receive clicks.
637
+ // - remove old left and right leafs from the dom [prunePageContainers()].
638
+ // - prefetch new adjacent leafs.
639
+ // - set up click handlers for both new left and right leafs.
640
+ // - redraw the search highlight.
641
+ // - update the pagenum box and the url.
642
+
643
+ const $twoPageViewEl = this.br.refs.$brTwoPageView;
644
+ const leftEdgeTmpLeft = gutter - currWidthL - leafEdgeTmpW;
645
+
646
+ this.br.leafEdgeTmp = document.createElement('div');
647
+ this.br.leafEdgeTmp.className = 'BRleafEdgeTmp';
648
+ $(this.br.leafEdgeTmp).css({
649
+ width: `${leafEdgeTmpW}px`,
650
+ height: `${this.br.twoPage.height}px`,
651
+ left: `${leftEdgeTmpLeft}px`,
652
+ top: `${top}px`,
653
+ zIndex: 1000,
654
+ }).appendTo($twoPageViewEl);
655
+
656
+ $(this.leafEdgeL).css({
657
+ width: `${newLeafEdgeWidthL}px`,
658
+ left: `${gutter - currWidthL - newLeafEdgeWidthL}px`
659
+ });
660
+
661
+ // Left gets the offset of the current left leaf from the document
662
+ const left = this.pageContainers[leftLeaf].$container.offset().left;
663
+ // $$$ This seems very similar to the gutter. May be able to consolidate the logic.
664
+ const right = `${$twoPageViewEl.prop('clientWidth') - left - this.pageContainers[leftLeaf].$container.width() + $twoPageViewEl.offset().left - 2}px`;
665
+
666
+ // We change the left leaf to right positioning
667
+ // $$$ This causes animation glitches during resize. See https://bugs.edge.launchpad.net/gnubook/+bug/328327
668
+ this.pageContainers[leftLeaf].$container.css({
669
+ right,
670
+ left: ''
671
+ });
672
+
673
+ $(this.br.leafEdgeTmp).animate({left: gutter}, this.br.flipSpeed, 'easeInSine');
674
+
675
+ this.pageContainers[leftLeaf].$container.animate({width: '0px'}, this.br.flipSpeed, 'easeInSine', () => {
676
+
677
+ $(this.br.leafEdgeTmp).animate({left: `${gutter + newWidthR}px`}, this.br.flipSpeed, 'easeOutSine');
678
+
679
+ this.br.$('.BRgutter').css({left: `${gutter - this.br.twoPage.bookSpineDivWidth * 0.5}px`});
680
+
681
+ this.pageContainers[newIndexR].$container.animate({width: `${newWidthR}px`}, this.br.flipSpeed, 'easeOutSine', () => {
682
+ this.pageContainers[newIndexL].$container.css('zIndex', 2);
683
+
684
+ $(this.leafEdgeR).css({
685
+ // Moves the right leaf edge
686
+ width: `${this.br.twoPage.edgeWidth - newLeafEdgeWidthL}px`,
687
+ left: `${gutter + newWidthR}px`
688
+ });
689
+
690
+ $(this.leafEdgeL).css({
691
+ // Moves and resizes the left leaf edge
692
+ width: `${newLeafEdgeWidthL}px`,
693
+ left: `${gutter - newWidthL - newLeafEdgeWidthL}px`
694
+ });
695
+
696
+ // Resizes the brown border div
697
+ $(this.br.twoPage.coverDiv).css({
698
+ width: `${this.coverWidth(newWidthL + newWidthR)}px`,
699
+ left: `${gutter - newWidthL - newLeafEdgeWidthL - this.br.twoPage.coverInternalPadding}px`
700
+ });
701
+
702
+ $(this.br.leafEdgeTmp).remove();
703
+ this.br.leafEdgeTmp = null;
704
+
705
+ // $$$ TODO refactor with opposite direction flip
706
+
707
+ this.br.twoPage.currentIndexL = newIndexL;
708
+ this.br.twoPage.currentIndexR = newIndexR;
709
+ this.br.twoPage.scaledWL = newWidthL;
710
+ this.br.twoPage.scaledWR = newWidthR;
711
+ this.br.twoPage.gutter = gutter;
712
+
713
+ this.br.updateFirstIndex(this.br.twoPage.currentIndexL);
714
+ this.br.displayedIndices = [newIndexL, newIndexR];
715
+ this.prunePageContainers();
716
+ this.br.animating = false;
717
+
718
+ this.resizeSpread();
719
+
720
+ if (this.br.animationFinishedCallback) {
721
+ this.br.animationFinishedCallback();
722
+ this.br.animationFinishedCallback = null;
723
+ }
724
+
725
+ this.br.refs.$brContainer.removeClass("BRpageFlipping");
726
+ this.br.textSelectionPlugin?.stopPageFlip(this.br.refs.$brContainer);
727
+ this.centerView();
728
+ this.br.trigger('pageChanged');
729
+
730
+ // get next previous batch immediately
731
+ this.prunePageContainers();
732
+ this.createPageContainer(newIndexL - 2);
733
+ this.createPageContainer(newIndexR - 2);
734
+ this.createPageContainer(newIndexL - 3);
735
+ this.createPageContainer(newIndexR - 3);
736
+ });
737
+ });
738
+ }
739
+
740
+ /**
741
+ * @param {PageIndex} index
742
+ */
743
+ createPageContainer(index) {
744
+ if (!this.pageContainers[index]) {
745
+ this.pageContainers[index] = this.br._createPageContainer(index);
746
+ }
747
+ this.pageContainers[index].update({ reduce: this.br.reduce / this.scale });
748
+ return this.pageContainers[index];
749
+ }
750
+
751
+ /**
752
+ * Whether we flip left or right is dependent on the page progression
753
+ * to flip forward one spread, pass index=null
754
+ * @param {number} index
755
+ */
756
+ flipFwdToIndex(index) {
757
+ if (this.br.animating) return;
758
+
759
+ if (null != this.br.leafEdgeTmp) {
760
+ alert('error: leafEdgeTmp should be null!');
761
+ return;
762
+ }
763
+
764
+ if (null == index) {
765
+ // Need to use the max here, since it could be a right to left book
766
+ const {currentIndexL, currentIndexR} = this.br.twoPage;
767
+ const maxDisplayedIndex = Math.max(currentIndexL, currentIndexR);
768
+ const nextPage = this.book.getPage(maxDisplayedIndex).findNext({ combineConsecutiveUnviewables: true });
769
+ if (!nextPage) return;
770
+ index = nextPage.index;
771
+ }
772
+ if (index > this.br.lastDisplayableIndex()) return;
773
+
774
+ this.br.updateNavIndexThrottled(index);
775
+
776
+ this.br.animating = true;
777
+
778
+ const nextIndices = this.book.getSpreadIndices(index);
779
+
780
+ if ('rl' != this.br.pageProgression) {
781
+ // We did not specify RTL
782
+ this.prepareFlipRightToLeft(nextIndices[0], nextIndices[1]);
783
+ this.flipRightToLeft(nextIndices[0], nextIndices[1]);
784
+ } else {
785
+ // RTL
786
+ this.prepareFlipLeftToRight(nextIndices[0], nextIndices[1]);
787
+ this.flipLeftToRight(nextIndices[0], nextIndices[1]);
788
+ }
789
+ }
790
+
791
+ /**
792
+ * Flip from left to right and show the nextL and nextR indices on those sides
793
+ * $$$ better not to have to pass gutter in
794
+ * @param {number} newIndexL
795
+ * @param {number} newIndexR
796
+ */
797
+ flipRightToLeft(newIndexL, newIndexR) {
798
+ this.br.refs.$brContainer.addClass("BRpageFlipping");
799
+
800
+ const oldLeafEdgeWidthL = this.br.leafEdgeWidth(this.br.twoPage.currentIndexL);
801
+ const oldLeafEdgeWidthR = this.br.twoPage.edgeWidth - oldLeafEdgeWidthL;
802
+ const newLeafEdgeWidthL = this.br.leafEdgeWidth(newIndexL);
803
+ const newLeafEdgeWidthR = this.br.twoPage.edgeWidth - newLeafEdgeWidthL;
804
+
805
+ const leafEdgeTmpW = oldLeafEdgeWidthR - newLeafEdgeWidthR;
806
+
807
+ const top = this.top();
808
+ const scaledW = this.getPageWidth(this.br.twoPage.currentIndexR);
809
+
810
+ const middle = this.br.twoPage.middle;
811
+ const gutter = middle + this.gutterOffsetForIndex(newIndexL);
812
+
813
+ const $twoPageViewEl = this.br.refs.$brTwoPageView;
814
+
815
+ this.br.leafEdgeTmp = document.createElement('div');
816
+ this.br.leafEdgeTmp.className = 'BRleafEdgeTmp';
817
+ $(this.br.leafEdgeTmp).css({
818
+ width: `${leafEdgeTmpW}px`,
819
+ height: `${this.br.twoPage.height}px`,
820
+ left: `${gutter + scaledW}px`,
821
+ top: `${top}px`,
822
+ zIndex:1000
823
+ }).appendTo($twoPageViewEl);
824
+
825
+ const newWidthL = this.getPageWidth(newIndexL);
826
+ const newWidthR = this.getPageWidth(newIndexR);
827
+
828
+ $(this.leafEdgeR).css({width: `${newLeafEdgeWidthR}px`, left: `${gutter + newWidthR}px` });
829
+ const speed = this.br.flipSpeed;
830
+
831
+ $(this.br.leafEdgeTmp).animate({left: gutter}, speed, 'easeInSine');
832
+ this.pageContainers[this.br.twoPage.currentIndexR].$container.animate({width: '0px'}, speed, 'easeInSine', () => {
833
+ this.br.$('BRgutter').css({left: `${gutter - this.br.twoPage.bookSpineDivWidth * 0.5}px`});
834
+ $(this.br.leafEdgeTmp).animate({left: `${gutter - newWidthL - leafEdgeTmpW}px`}, speed, 'easeOutSine');
835
+ this.pageContainers[newIndexL].$container.animate({width: `${newWidthL}px`}, speed, 'easeOutSine', () => {
836
+ this.pageContainers[newIndexR].$container.css('zIndex', 2);
837
+
838
+ $(this.leafEdgeL).css({
839
+ width: `${newLeafEdgeWidthL}px`,
840
+ left: `${gutter - newWidthL - newLeafEdgeWidthL}px`
841
+ });
842
+
843
+ // Resizes the book cover
844
+ $(this.br.twoPage.coverDiv).css({
845
+ width: `${this.coverWidth(newWidthL + newWidthR)}px`,
846
+ left: `${gutter - newWidthL - newLeafEdgeWidthL - this.br.twoPage.coverInternalPadding}px`
847
+ });
848
+
849
+ $(this.br.leafEdgeTmp).remove();
850
+ this.br.leafEdgeTmp = null;
851
+
852
+ this.br.twoPage.currentIndexL = newIndexL;
853
+ this.br.twoPage.currentIndexR = newIndexR;
854
+ this.br.twoPage.scaledWL = newWidthL;
855
+ this.br.twoPage.scaledWR = newWidthR;
856
+ this.br.twoPage.gutter = gutter;
857
+
858
+ this.br.updateFirstIndex(this.br.twoPage.currentIndexL);
859
+ this.br.displayedIndices = [newIndexL, newIndexR];
860
+ this.prunePageContainers();
861
+ this.br.animating = false;
862
+
863
+ this.resizeSpread();
864
+
865
+ if (this.br.animationFinishedCallback) {
866
+ this.br.animationFinishedCallback();
867
+ this.br.animationFinishedCallback = null;
868
+ }
869
+
870
+ this.br.refs.$brContainer.removeClass("BRpageFlipping");
871
+ this.br.textSelectionPlugin?.stopPageFlip(this.br.refs.$brContainer);
872
+ this.centerView();
873
+ this.br.trigger('pageChanged');
874
+
875
+ this.prunePageContainers();
876
+ this.createPageContainer(newIndexL + 2);
877
+ this.createPageContainer(newIndexR + 2);
878
+ this.createPageContainer(newIndexL + 3);
879
+ this.createPageContainer(newIndexR + 3);
880
+ });
881
+ });
882
+ }
883
+
884
+ attachMouseHandlers() {
885
+ this.br.refs.$brTwoPageView
886
+ .off('mouseup').on('mouseup', ev => {
887
+ if (ev.which == 3) {
888
+ // right click
889
+ return !this.br.protected;
890
+ }
891
+
892
+ const $page = $(ev.target).closest('.BRpagecontainer');
893
+ if ($page.data('side') == 'L') this.br.left();
894
+ else if ($page.data('side') == 'R') this.br.right();
895
+ });
896
+ }
897
+
898
+ /**
899
+ * Prepare to flip the left page towards the right. This corresponds to moving
900
+ * backward when the page progression is left to right.
901
+ * @param {number} prevL
902
+ * @param {number} prevR
903
+ */
904
+ prepareFlipLeftToRight(prevL, prevR) {
905
+ this.createPageContainer(prevL, true);
906
+ this.createPageContainer(prevR, true);
907
+
908
+ const $twoPageViewEl = this.br.refs.$brTwoPageView;
909
+ const height = this.book._getPageHeight(prevL);
910
+ const width = this.book._getPageWidth(prevL);
911
+ const middle = this.br.twoPage.middle;
912
+ const top = this.top();
913
+ const scaledW = this.br.twoPage.height * width / height; // $$$ assumes height of page is dominant
914
+
915
+ // The gutter is the dividing line between the left and right pages.
916
+ // It is offset from the middle to create the illusion of thickness to the pages
917
+ const gutter = middle + this.gutterOffsetForIndex(prevL);
918
+
919
+ const leftCSS = {
920
+ left: `${gutter - scaledW}px`,
921
+ right: '', // clear right property
922
+ top: `${top}px`,
923
+ height: this.br.twoPage.height,
924
+ width: `${scaledW}px`,
925
+ zIndex: 1
926
+ };
927
+
928
+ this.pageContainers[prevL].$container
929
+ .css(leftCSS)
930
+ .appendTo($twoPageViewEl);
931
+
932
+ const rightCSS = {
933
+ left: `${gutter}px`,
934
+ right: '',
935
+ top: `${top}px`,
936
+ height: this.br.twoPage.height,
937
+ width: '0',
938
+ zIndex: 2
939
+ };
940
+
941
+ this.pageContainers[prevR].$container
942
+ .css(rightCSS)
943
+ .appendTo($twoPageViewEl);
944
+ }
945
+
946
+ /**
947
+ * // $$$ mang we're adding an extra pixel in the middle. See https://bugs.edge.launchpad.net/gnubook/+bug/411667
948
+ */
949
+ prepareFlipRightToLeft(nextL, nextR) {
950
+ this.createPageContainer(nextL, true);
951
+ this.createPageContainer(nextR, true);
952
+
953
+ const $twoPageViewEl = this.br.refs.$brTwoPageView;
954
+ let height = this.book._getPageHeight(nextR);
955
+ let width = this.book._getPageWidth(nextR);
956
+ const middle = this.br.twoPage.middle;
957
+ const top = this.top();
958
+ let scaledW = this.br.twoPage.height * width / height;
959
+
960
+ const gutter = middle + this.gutterOffsetForIndex(nextL);
961
+
962
+ $(this.pageContainers[nextR].$container).css({
963
+ left: `${gutter}px`,
964
+ top: `${top}px`,
965
+ height: this.br.twoPage.height,
966
+ width: `${scaledW}px`,
967
+ zIndex: 1,
968
+ })
969
+ .appendTo($twoPageViewEl);
970
+
971
+ height = this.book._getPageHeight(nextL);
972
+ width = this.book._getPageWidth(nextL);
973
+ scaledW = this.br.twoPage.height * width / height;
974
+
975
+ $(this.pageContainers[nextL].$container).css({
976
+ right: `${$twoPageViewEl.prop('clientWidth') - gutter}px`,
977
+ top: `${top}px`,
978
+ height: this.br.twoPage.height,
979
+ width: '0px', // Start at 0 width, then grow to the left
980
+ zIndex: 2,
981
+ })
982
+ .appendTo($twoPageViewEl);
983
+ }
984
+
985
+ getPageWidth(index) {
986
+ // We return the width based on the dominant height
987
+ const height = this.book._getPageHeight(index);
988
+ const width = this.book._getPageWidth(index);
989
+ // $$$ we assume width is relative to current spread
990
+ return Math.floor(this.br.twoPage.height * width / height);
991
+ }
992
+
993
+ /**
994
+ * Returns the position of the gutter (line between the page images)
995
+ */
996
+ gutter() {
997
+ return this.br.twoPage.middle + this.gutterOffsetForIndex(this.br.twoPage.currentIndexL);
998
+ }
999
+
1000
+ /**
1001
+ * Returns the offset for the top of the page images
1002
+ */
1003
+ top() {
1004
+ return this.br.twoPage.coverExternalPadding + this.br.twoPage.coverInternalPadding; // $$$ + border?
1005
+ }
1006
+
1007
+ /**
1008
+ * Returns the width of the cover div given the total page width
1009
+ * @param {number} totalPageWidth
1010
+ * @return {number}
1011
+ */
1012
+ coverWidth(totalPageWidth) {
1013
+ return totalPageWidth + this.br.twoPage.edgeWidth + 2 * this.br.twoPage.coverInternalPadding;
1014
+ }
1015
+
1016
+ /**
1017
+ * Returns the percentage offset into twopageview div at the center of container div
1018
+ */
1019
+ getViewCenter() {
1020
+ const { $brContainer, $brTwoPageView } = this.br.refs;
1021
+ const center = {};
1022
+
1023
+ const containerOffset = $brContainer.offset();
1024
+ const viewOffset = $brTwoPageView.offset();
1025
+ center.percentageX = (containerOffset.left - viewOffset.left + ($brContainer.prop('clientWidth') >> 1)) / this.br.twoPage.totalWidth;
1026
+ center.percentageY = (containerOffset.top - viewOffset.top + ($brContainer.prop('clientHeight') >> 1)) / this.br.twoPage.totalHeight;
1027
+
1028
+ return center;
1029
+ }
1030
+
1031
+ /**
1032
+ * Centers the point given by percentage from left,top of twopageview
1033
+ * @param {number} [percentageX=0.5]
1034
+ * @param {number} [percentageY=0.5]
1035
+ */
1036
+ centerView(percentageX, percentageY) {
1037
+
1038
+ if ('undefined' == typeof(percentageX)) {
1039
+ percentageX = 0.5;
1040
+ }
1041
+ if ('undefined' == typeof(percentageY)) {
1042
+ percentageY = 0.5;
1043
+ }
1044
+
1045
+ const viewWidth = this.br.refs.$brTwoPageView.width();
1046
+ const containerClientWidth = this.br.refs.$brContainer.prop('clientWidth');
1047
+ const intoViewX = percentageX * viewWidth;
1048
+
1049
+ const viewHeight = this.br.refs.$brTwoPageView.height();
1050
+ const containerClientHeight = this.br.refs.$brContainer.prop('clientHeight');
1051
+ const intoViewY = percentageY * viewHeight;
1052
+
1053
+ if (viewWidth < containerClientWidth) {
1054
+ // Can fit width without scrollbars - center by adjusting offset
1055
+ this.br.refs.$brTwoPageView.css('left', `${(containerClientWidth >> 1) - intoViewX}px`);
1056
+ } else {
1057
+ // Need to scroll to center
1058
+ this.br.refs.$brTwoPageView.css('left', 0);
1059
+ this.br.refs.$brContainer.scrollLeft(intoViewX - (containerClientWidth >> 1));
1060
+ }
1061
+
1062
+ if (viewHeight < containerClientHeight) {
1063
+ // Fits with scrollbars - add offset
1064
+ this.br.refs.$brTwoPageView.css('top', `${(containerClientHeight >> 1) - intoViewY}px`);
1065
+ } else {
1066
+ this.br.refs.$brTwoPageView.css('top', 0);
1067
+ this.br.refs.$brContainer.scrollTop(intoViewY - (containerClientHeight >> 1));
1068
+ }
1069
+ }
1070
+
1071
+ /**
1072
+ * Returns the integer height of the click-to-flip areas at the edges of the book
1073
+ * @return {number}
1074
+ */
1075
+ flipAreaHeight() {
1076
+ return Math.floor(this.br.twoPage.height);
1077
+ }
1078
+
1079
+ /**
1080
+ * Returns the the integer width of the flip areas
1081
+ * @return {number}
1082
+ */
1083
+ flipAreaWidth() {
1084
+ const max = 100; // $$$ TODO base on view width?
1085
+ const min = 10;
1086
+
1087
+ const width = this.br.twoPage.width * 0.15;
1088
+ return Math.floor(clamp(width, min, max));
1089
+ }
1090
+
1091
+ /**
1092
+ * Returns integer top offset for flip areas
1093
+ * @return {number}
1094
+ */
1095
+ flipAreaTop() {
1096
+ return Math.floor(this.br.twoPage.bookCoverDivTop + this.br.twoPage.coverInternalPadding);
1097
+ }
1098
+
1099
+ /**
1100
+ * Left offset for left flip area
1101
+ * @return {number}
1102
+ */
1103
+ leftFlipAreaLeft() {
1104
+ return Math.floor(this.br.twoPage.gutter - this.br.twoPage.scaledWL);
1105
+ }
1106
+
1107
+ /**
1108
+ * Left offset for right flip area
1109
+ * @return {number}
1110
+ */
1111
+ rightFlipAreaLeft() {
1112
+ return Math.floor(this.br.twoPage.gutter + this.br.twoPage.scaledWR - this.flipAreaWidth());
1113
+ }
1114
+
1115
+ /**
1116
+ * Position calculation shared between search and text-to-speech functions
1117
+ */
1118
+ setHilightCss(div, index, left, right, top, bottom) {
1119
+ // We calculate the reduction factor for the specific page because it can be different
1120
+ // for each page in the spread
1121
+ const height = this.book._getPageHeight(index);
1122
+ const width = this.book._getPageWidth(index);
1123
+ const reduce = this.br.twoPage.height / height;
1124
+ const scaledW = Math.floor(width * reduce);
1125
+
1126
+ const gutter = this.gutter();
1127
+ let pageL;
1128
+ if ('L' == this.book.getPageSide(index)) {
1129
+ pageL = gutter - scaledW;
1130
+ } else {
1131
+ pageL = gutter;
1132
+ }
1133
+ const pageT = this.top();
1134
+
1135
+ $(div).css({
1136
+ width: `${(right - left) * reduce}px`,
1137
+ height: `${(bottom - top) * reduce}px`,
1138
+ left: `${pageL + left * reduce}px`,
1139
+ top: `${pageT + top * reduce}px`
1140
+ });
1141
+ }
1142
+
1143
+ /**
1144
+ * Returns the gutter offset for the spread containing the given index.
1145
+ * This function supports RTL
1146
+ * @param {number} pindex
1147
+ * @return {number}
1148
+ */
1149
+ gutterOffsetForIndex(pindex) {
1150
+ // To find the offset of the gutter from the middle we calculate our percentage distance
1151
+ // through the book (0..1), remap to (-0.5..0.5) and multiply by the total page edge width
1152
+ let offset = Math.floor(((pindex / this.book.getNumLeafs()) - 0.5) * this.br.twoPage.edgeWidth);
1153
+
1154
+ // But then again for RTL it's the opposite
1155
+ if ('rl' == this.br.pageProgression) {
1156
+ offset *= -1;
1157
+ }
1158
+
1159
+ return offset;
1160
+ }
1161
+
1162
+ /**
1163
+ * Returns the width of the leaf edge div for the page with index given
1164
+ * @param {number} pindex
1165
+ * @return {number}
1166
+ */
1167
+ leafEdgeWidth(pindex) {
1168
+ // $$$ could there be single pixel rounding errors for L vs R?
1169
+ if ((this.book.getPageSide(pindex) == 'L') && (this.br.pageProgression != 'rl')) {
1170
+ return Math.floor( (pindex / this.book.getNumLeafs()) * this.br.twoPage.edgeWidth + 0.5);
1171
+ } else {
1172
+ return Math.floor( (1 - pindex / this.book.getNumLeafs()) * this.br.twoPage.edgeWidth + 0.5);
1173
+ }
1174
+ }
1175
+
1176
+ /**
1177
+ * Returns the target jump leaf given a page coordinate (inside the left page edge div)
1178
+ * @param {number} pageX
1179
+ * @return {PageIndex}
1180
+ */
1181
+ jumpIndexForLeftEdgePageX(pageX) {
1182
+ let jumpIndex;
1183
+ if ('rl' != this.br.pageProgression) {
1184
+ // LTR - flipping backward
1185
+ jumpIndex = this.br.twoPage.currentIndexL - ($(this.leafEdgeL).offset().left + $(this.leafEdgeL).width() - pageX) * 10;
1186
+
1187
+ // browser may have resized the div due to font size change -- see https://bugs.launchpad.net/gnubook/+bug/333570
1188
+ jumpIndex = clamp(Math.round(jumpIndex), this.br.firstDisplayableIndex(), this.br.twoPage.currentIndexL - 2);
1189
+ return jumpIndex;
1190
+
1191
+ } else {
1192
+ jumpIndex = this.br.twoPage.currentIndexL + ($(this.leafEdgeL).offset().left + $(this.leafEdgeL).width() - pageX) * 10;
1193
+ jumpIndex = clamp(Math.round(jumpIndex), this.br.twoPage.currentIndexL + 2, this.br.lastDisplayableIndex());
1194
+ return jumpIndex;
1195
+ }
1196
+ }
1197
+
1198
+ /**
1199
+ * Returns the target jump leaf given a page coordinate (inside the right page edge div)
1200
+ * @param {number} pageX
1201
+ * @return {PageIndex}
1202
+ */
1203
+ jumpIndexForRightEdgePageX(pageX) {
1204
+ let jumpIndex;
1205
+ if ('rl' != this.br.pageProgression) {
1206
+ // LTR
1207
+ jumpIndex = this.br.twoPage.currentIndexL + (pageX - $(this.leafEdgeR).offset().left) * 10;
1208
+ jumpIndex = clamp(Math.round(jumpIndex), this.br.twoPage.currentIndexL + 2, this.br.lastDisplayableIndex());
1209
+ return jumpIndex;
1210
+ } else {
1211
+ jumpIndex = this.br.twoPage.currentIndexL - (pageX - $(this.leafEdgeR).offset().left) * 10;
1212
+ jumpIndex = clamp(Math.round(jumpIndex), this.br.firstDisplayableIndex(), this.br.twoPage.currentIndexL - 2);
1213
+ return jumpIndex;
1214
+ }
1215
+ }
1216
+
1217
+ /**
1218
+ * Fetches the currently displayed images (if not already fetching)
1219
+ * as wells as any nearby pages.
1220
+ */
1221
+ prefetch() {
1222
+ // $$$ We should check here if the current indices have finished
1223
+ // loading (with some timeout) before loading more page images
1224
+ // See https://bugs.edge.launchpad.net/bookreader/+bug/511391
1225
+ const { max, min } = Math;
1226
+ const { book } = this;
1227
+ const { currentIndexL, currentIndexR } = this.br.twoPage;
1228
+ const ADJACENT_PAGES_TO_LOAD = 2;
1229
+ // currentIndexL can be -1; getPage returns the last page of the book
1230
+ // when given -1, so need to prevent that.
1231
+ let lowPage = book.getPage(max(0, min(currentIndexL, currentIndexR)));
1232
+ let highPage = book.getPage(max(currentIndexL, currentIndexR));
1233
+
1234
+ for (let i = 0; i < ADJACENT_PAGES_TO_LOAD + 2; i++) {
1235
+ if (lowPage) {
1236
+ this.createPageContainer(lowPage.index);
1237
+ lowPage = lowPage.findPrev({ combineConsecutiveUnviewables: true });
1238
+ }
1239
+
1240
+ if (highPage) {
1241
+ this.createPageContainer(highPage.index);
1242
+ highPage = highPage.findNext({ combineConsecutiveUnviewables: true });
1243
+ }
1244
+ }
1245
+ }
1246
+
1247
+ /* 2up Container Sizes */
1248
+
1249
+ /** main positions for inner containers */
1250
+ get baseLeafCss() {
1251
+ return {
1252
+ position: 'absolute',
1253
+ right: '',
1254
+ top: `${this.top()}px`,
1255
+ zIndex: 2,
1256
+ };
1257
+ }
1258
+
1259
+ /** main height for inner containers */
1260
+ get heightCss() {
1261
+ return {
1262
+ height: `${this.br.twoPage.height}px`, // $$$ height forced the same for both pages
1263
+ };
1264
+ }
1265
+
1266
+ /** Left Page sizing */
1267
+ get leftLeafCss() {
1268
+ return {
1269
+ ...this.baseLeafCss,
1270
+ ...this.heightCss,
1271
+ left: `${this.br.twoPage.gutter - this.br.twoPage.scaledWL}px`,
1272
+ width: `${this.br.twoPage.scaledWL}px`,
1273
+ };
1274
+ }
1275
+
1276
+ /** Left side book thickness */
1277
+ get leafEdgeLCss() {
1278
+ return {
1279
+ ...this.heightCss,
1280
+ width: `${this.br.twoPage.leafEdgeWidthL}px`,
1281
+ left: `${this.br.twoPage.bookCoverDivLeft + this.br.twoPage.coverInternalPadding}px`,
1282
+ top: `${this.br.twoPage.bookCoverDivTop + this.br.twoPage.coverInternalPadding}px`,
1283
+ border: this.br.twoPage.leafEdgeWidthL === 0 ? 'none' : null
1284
+ };
1285
+ }
1286
+
1287
+ /** Right Page sizing */
1288
+ get rightLeafCss() {
1289
+ return {
1290
+ ...this.baseLeafCss,
1291
+ ...this.heightCss,
1292
+ left: `${this.br.twoPage.gutter}px`,
1293
+ width: `${this.br.twoPage.scaledWR}px`,
1294
+ };
1295
+ }
1296
+
1297
+ /** Right side book thickness */
1298
+ get leafEdgeRCss() {
1299
+ return {
1300
+ ...this.heightCss,
1301
+ width: `${this.br.twoPage.leafEdgeWidthR}px`,
1302
+ left: `${this.br.twoPage.scaledWL + this.br.twoPage.scaledWR + this.br.twoPage.leafEdgeWidthL}px`,
1303
+ top: `${this.br.twoPage.bookCoverDivTop + this.br.twoPage.coverInternalPadding}px`,
1304
+ border: this.br.twoPage.leafEdgeWidthR === 0 ? 'none' : null
1305
+ };
1306
+ }
1307
+
1308
+ /** main container sizing */
1309
+ get mainContainerCss() {
1310
+ return {
1311
+ height: `${this.br.twoPage.totalHeight}px`,
1312
+ width: `${this.br.twoPage.totalWidth}px`,
1313
+ position: 'absolute'
1314
+ };
1315
+ }
1316
+
1317
+ /** book cover sizing */
1318
+ get spreadCoverCss() {
1319
+ return {
1320
+ width: `${this.br.twoPage.bookCoverDivWidth}px`,
1321
+ height: `${this.br.twoPage.bookCoverDivHeight}px`,
1322
+ visibility: 'visible'
1323
+ };
1324
+ }
1325
+
1326
+ /** book spine sizing */
1327
+ get spineCss() {
1328
+ return {
1329
+ width: `${this.br.twoPage.bookSpineDivWidth}px`,
1330
+ height: `${this.br.twoPage.bookSpineDivHeight}px`,
1331
+ left: `${this.br.twoPage.gutter - (this.br.twoPage.bookSpineDivWidth / 2)}px`,
1332
+ top: `${this.br.twoPage.bookSpineDivTop}px`
1333
+ };
1334
+ }
1335
+ /** end CSS */
1336
+ }
1337
+
1338
+ /**
1339
+ * @implements {BookReaderOptions["twoPage"]}
1340
+ * @typedef {object} TwoPageState
1341
+ * @property {number} coverInternalPadding
1342
+ * @property {number} coverExternalPadding
1343
+ *
1344
+ * @property {import('./options.js').AutoFitValues} autofit
1345
+ * @property {number} width
1346
+ * @property {number} height
1347
+ * @property {number} currentIndexL
1348
+ * @property {number} currentIndexR
1349
+ * @property {number} scaledWL
1350
+ * @property {number} scaledWR
1351
+ * @property {number} gutter
1352
+ * @property {Array<{reduce: number, autofit: import('./options.js').AutoFitValues}>} reductionFactors
1353
+ * @property {number} totalHeight
1354
+ * @property {number} totalWidth
1355
+ *
1356
+ * @property {HTMLDivElement} coverDiv
1357
+ * @property {number} bookCoverDivTop
1358
+ * @property {number} bookCoverDivLeft
1359
+ * @property {number} bookCoverDivWidth
1360
+ * @property {number} bookCoverDivHeight
1361
+ *
1362
+ * @property {number} leafEdgeWidthL
1363
+ * @property {number} leafEdgeWidthR
1364
+ *
1365
+ * @property {number} bookSpineDivTop
1366
+ * @property {number} bookSpineDivLeft
1367
+ * @property {number} bookSpineDivWidth
1368
+ * @property {number} bookSpineDivHeight
1369
+ *
1370
+ * @property {number} edgeWidth
1371
+ * @property {number} middle
1372
+ */