@internetarchive/bookreader 5.0.0-9-multiple-files → 5.0.0-90

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 (333) hide show
  1. package/.eslintrc.js +21 -19
  2. package/.github/workflows/node.js.yml +81 -7
  3. package/.github/workflows/npm-publish.yml +6 -20
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +505 -1442
  6. package/BookReader/BookReader.js +2 -21564
  7. package/BookReader/BookReader.js.LICENSE.txt +20 -20
  8. package/BookReader/BookReader.js.map +1 -1
  9. package/BookReader/ia-bookreader-bundle.js +1782 -0
  10. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +7 -0
  11. package/BookReader/ia-bookreader-bundle.js.map +1 -0
  12. package/BookReader/icons/1up.svg +1 -12
  13. package/BookReader/icons/2up.svg +1 -15
  14. package/BookReader/icons/advance.svg +3 -26
  15. package/BookReader/icons/chevron-right.svg +1 -1
  16. package/BookReader/icons/close-circle-dark.svg +1 -0
  17. package/BookReader/icons/close-circle.svg +1 -1
  18. package/BookReader/icons/fullscreen.svg +1 -17
  19. package/BookReader/icons/fullscreen_exit.svg +1 -17
  20. package/BookReader/icons/hamburger.svg +1 -15
  21. package/BookReader/icons/left-arrow.svg +1 -12
  22. package/BookReader/icons/magnify-minus.svg +1 -16
  23. package/BookReader/icons/magnify-plus.svg +1 -17
  24. package/BookReader/icons/magnify.svg +1 -15
  25. package/BookReader/icons/pause.svg +1 -23
  26. package/BookReader/icons/play.svg +1 -22
  27. package/BookReader/icons/playback-speed.svg +1 -34
  28. package/BookReader/icons/read-aloud.svg +1 -22
  29. package/BookReader/icons/review.svg +3 -22
  30. package/BookReader/icons/thumbnails.svg +1 -17
  31. package/BookReader/icons/voice.svg +1 -0
  32. package/BookReader/icons/volume-full.svg +1 -22
  33. package/BookReader/images/BRicons.svg +5 -94
  34. package/BookReader/images/books_graphic.svg +1 -177
  35. package/BookReader/images/icon_book.svg +1 -12
  36. package/BookReader/images/icon_bookmark.svg +1 -12
  37. package/BookReader/images/icon_gear.svg +1 -14
  38. package/BookReader/images/icon_hamburger.svg +1 -20
  39. package/BookReader/images/icon_home.svg +1 -21
  40. package/BookReader/images/icon_info.svg +1 -11
  41. package/BookReader/images/icon_one_page.svg +1 -8
  42. package/BookReader/images/icon_pause.svg +1 -1
  43. package/BookReader/images/icon_play.svg +1 -1
  44. package/BookReader/images/icon_playback-rate.svg +1 -15
  45. package/BookReader/images/icon_search_button.svg +1 -8
  46. package/BookReader/images/icon_share.svg +1 -9
  47. package/BookReader/images/icon_skip-ahead.svg +1 -6
  48. package/BookReader/images/icon_skip-back.svg +2 -13
  49. package/BookReader/images/icon_speaker.svg +1 -18
  50. package/BookReader/images/icon_speaker_open.svg +1 -10
  51. package/BookReader/images/icon_thumbnails.svg +1 -12
  52. package/BookReader/images/icon_toc.svg +1 -5
  53. package/BookReader/images/icon_two_pages.svg +1 -9
  54. package/BookReader/images/marker_chap-off.svg +1 -11
  55. package/BookReader/images/marker_chap-on.svg +1 -11
  56. package/BookReader/images/marker_srch-on.svg +1 -11
  57. package/BookReader/images/unviewable_page.png +0 -0
  58. package/BookReader/jquery-3.js +2 -0
  59. package/BookReader/jquery-3.js.LICENSE.txt +24 -0
  60. package/BookReader/plugins/plugin.archive_analytics.js +1 -172
  61. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  62. package/BookReader/plugins/plugin.autoplay.js +1 -165
  63. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  64. package/BookReader/plugins/plugin.chapters.js +22 -301
  65. package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -0
  66. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  67. package/BookReader/plugins/plugin.iframe.js +1 -74
  68. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  69. package/BookReader/plugins/plugin.iiif.js +2 -0
  70. package/BookReader/plugins/plugin.iiif.js.map +1 -0
  71. package/BookReader/plugins/plugin.resume.js +1 -368
  72. package/BookReader/plugins/plugin.resume.js.map +1 -1
  73. package/BookReader/plugins/plugin.search.js +2 -1420
  74. package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
  75. package/BookReader/plugins/plugin.search.js.map +1 -1
  76. package/BookReader/plugins/plugin.text_selection.js +2 -1080
  77. package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
  78. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  79. package/BookReader/plugins/plugin.tts.js +2 -9193
  80. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +2 -0
  81. package/BookReader/plugins/plugin.tts.js.map +1 -1
  82. package/BookReader/plugins/plugin.url.js +1 -269
  83. package/BookReader/plugins/plugin.url.js.map +1 -1
  84. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -379
  85. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  86. package/BookReader/webcomponents-bundle.js +3 -0
  87. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  88. package/BookReader/webcomponents-bundle.js.map +1 -0
  89. package/BookReaderDemo/BookReaderDemo.css +18 -19
  90. package/BookReaderDemo/BookReaderJSAdvanced.js +0 -3
  91. package/BookReaderDemo/BookReaderJSSimple.js +1 -0
  92. package/BookReaderDemo/IADemoBr.js +144 -0
  93. package/BookReaderDemo/demo-advanced.html +2 -2
  94. package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
  95. package/BookReaderDemo/demo-fullscreen-mobile.html +3 -5
  96. package/BookReaderDemo/demo-fullscreen.html +2 -4
  97. package/BookReaderDemo/demo-iiif.html +99 -12
  98. package/BookReaderDemo/demo-internetarchive.html +214 -18
  99. package/BookReaderDemo/demo-multiple.html +2 -1
  100. package/BookReaderDemo/demo-preview-pages.html +526 -525
  101. package/BookReaderDemo/demo-simple.html +2 -1
  102. package/BookReaderDemo/demo-vendor-fullscreen.html +2 -4
  103. package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
  104. package/BookReaderDemo/immersion-1up.html +2 -2
  105. package/BookReaderDemo/immersion-mode.html +2 -4
  106. package/BookReaderDemo/toggle_controls.html +3 -2
  107. package/BookReaderDemo/view_mode.html +2 -1
  108. package/BookReaderDemo/viewmode-cycle.html +2 -3
  109. package/CHANGELOG.md +595 -33
  110. package/README.md +14 -1
  111. package/babel.config.js +20 -0
  112. package/codecov.yml +6 -0
  113. package/index.html +5 -2
  114. package/jsconfig.json +19 -0
  115. package/netlify.toml +9 -0
  116. package/package.json +70 -62
  117. package/renovate.json +52 -0
  118. package/scripts/preversion.js +0 -1
  119. package/src/BookNavigator/assets/bookmark-colors.js +1 -1
  120. package/src/BookNavigator/assets/button-base.js +10 -2
  121. package/src/BookNavigator/assets/ia-logo.js +17 -0
  122. package/src/BookNavigator/assets/icon_checkmark.js +1 -1
  123. package/src/BookNavigator/assets/icon_close.js +1 -1
  124. package/src/BookNavigator/book-navigator.js +590 -0
  125. package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
  126. package/src/BookNavigator/bookmarks/bookmark-edit.js +3 -4
  127. package/src/BookNavigator/bookmarks/bookmarks-list.js +2 -3
  128. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +4 -9
  129. package/src/BookNavigator/bookmarks/bookmarks-provider.js +27 -17
  130. package/src/BookNavigator/bookmarks/ia-bookmarks.js +116 -67
  131. package/src/BookNavigator/delete-modal-actions.js +1 -1
  132. package/src/BookNavigator/downloads/downloads-provider.js +36 -21
  133. package/src/BookNavigator/downloads/downloads.js +29 -25
  134. package/src/BookNavigator/search/search-provider.js +80 -28
  135. package/src/BookNavigator/search/search-results.js +29 -26
  136. package/src/BookNavigator/sharing.js +27 -0
  137. package/src/BookNavigator/viewable-files.js +95 -0
  138. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +13 -12
  139. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +7 -7
  140. package/src/BookReader/BookModel.js +76 -41
  141. package/src/BookReader/DragScrollable.js +233 -0
  142. package/src/BookReader/ImageCache.js +48 -15
  143. package/src/BookReader/Mode1Up.js +56 -351
  144. package/src/BookReader/Mode1UpLit.js +388 -0
  145. package/src/BookReader/Mode2Up.js +73 -1318
  146. package/src/BookReader/Mode2UpLit.js +777 -0
  147. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  148. package/src/BookReader/ModeSmoothZoom.js +312 -0
  149. package/src/BookReader/ModeThumb.js +19 -13
  150. package/src/BookReader/Navbar/Navbar.js +70 -54
  151. package/src/BookReader/PageContainer.js +116 -22
  152. package/src/BookReader/ReduceSet.js +3 -3
  153. package/src/BookReader/Toolbar/Toolbar.js +14 -41
  154. package/src/BookReader/events.js +2 -3
  155. package/src/BookReader/options.js +73 -15
  156. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  157. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  158. package/src/BookReader/utils/SelectionObserver.js +45 -0
  159. package/src/BookReader/utils/classes.js +1 -1
  160. package/src/BookReader/utils.js +128 -13
  161. package/src/BookReader.js +562 -1078
  162. package/src/BookReaderPlugin.js +44 -0
  163. package/src/assets/icons/close-circle-dark.svg +1 -0
  164. package/src/assets/icons/magnify-minus.svg +3 -7
  165. package/src/assets/icons/magnify-plus.svg +3 -7
  166. package/src/assets/icons/voice.svg +1 -0
  167. package/src/assets/images/unviewable_page.png +0 -0
  168. package/src/css/BookReader.scss +1 -17
  169. package/src/css/_BRBookmarks.scss +1 -1
  170. package/src/css/_BRComponent.scss +1 -1
  171. package/src/css/_BRicon.scss +8 -2
  172. package/src/css/_BRmain.scss +33 -27
  173. package/src/css/_BRnav.scss +12 -42
  174. package/src/css/_BRpages.scss +170 -42
  175. package/src/css/_BRsearch.scss +68 -230
  176. package/src/css/_BRtoolbar.scss +5 -5
  177. package/src/css/_TextSelection.scss +87 -27
  178. package/src/css/_colorbox.scss +2 -2
  179. package/src/css/_controls.scss +24 -7
  180. package/src/css/_icons.scss +7 -1
  181. package/src/ia-bookreader/ia-bookreader.js +224 -0
  182. package/src/plugins/plugin.archive_analytics.js +84 -78
  183. package/src/plugins/plugin.autoplay.js +99 -104
  184. package/src/plugins/plugin.chapters.js +237 -191
  185. package/src/plugins/plugin.iframe.js +1 -1
  186. package/src/plugins/plugin.iiif.js +141 -0
  187. package/src/plugins/plugin.resume.js +53 -50
  188. package/src/plugins/plugin.text_selection.js +503 -175
  189. package/src/plugins/plugin.vendor-fullscreen.js +7 -7
  190. package/src/plugins/search/plugin.search.js +183 -121
  191. package/src/plugins/search/utils.js +43 -0
  192. package/src/plugins/search/view.js +67 -202
  193. package/src/plugins/tts/AbstractTTSEngine.js +75 -45
  194. package/src/plugins/tts/FestivalTTSEngine.js +21 -31
  195. package/src/plugins/tts/PageChunk.js +16 -23
  196. package/src/plugins/tts/PageChunkIterator.js +11 -17
  197. package/src/plugins/tts/WebTTSEngine.js +88 -72
  198. package/src/plugins/tts/plugin.tts.js +310 -350
  199. package/src/plugins/tts/utils.js +16 -26
  200. package/src/plugins/url/UrlPlugin.js +191 -0
  201. package/src/plugins/{plugin.url.js → url/plugin.url.js} +47 -18
  202. package/src/util/browserSniffing.js +22 -0
  203. package/src/util/docCookies.js +21 -2
  204. package/src/util/strings.js +1 -0
  205. package/tests/e2e/README.md +37 -0
  206. package/tests/e2e/autoplay.test.js +9 -6
  207. package/tests/e2e/base.test.js +8 -16
  208. package/tests/e2e/helpers/base.js +55 -50
  209. package/tests/e2e/helpers/debug.js +1 -1
  210. package/tests/e2e/helpers/mockSearch.js +19 -22
  211. package/tests/e2e/helpers/params.js +17 -0
  212. package/tests/e2e/helpers/rightToLeft.js +8 -14
  213. package/tests/e2e/helpers/search.js +73 -0
  214. package/tests/e2e/models/Navigation.js +20 -37
  215. package/tests/e2e/rightToLeft.test.js +4 -5
  216. package/tests/e2e/viewmode.test.js +40 -33
  217. package/tests/jest/BookNavigator/book-navigator.test.js +661 -0
  218. package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
  219. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
  220. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
  221. package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
  222. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
  223. package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
  224. package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
  225. package/tests/{karma/BookNavigator → jest/BookNavigator/search}/search-results.test.js +109 -60
  226. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
  227. package/tests/jest/BookNavigator/viewable-files/viewable-files-provider.test.js +80 -0
  228. package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
  229. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +74 -14
  230. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +193 -0
  231. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  232. package/tests/jest/BookReader/Mode1UpLit.test.js +73 -0
  233. package/tests/jest/BookReader/Mode2Up.test.js +98 -0
  234. package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
  235. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
  236. package/tests/jest/BookReader/ModeSmoothZoom.test.js +218 -0
  237. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  238. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +42 -29
  239. package/tests/jest/BookReader/PageContainer.test.js +238 -0
  240. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  241. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +3 -3
  242. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  243. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  244. package/tests/jest/BookReader/utils/SelectionObserver.test.js +57 -0
  245. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  246. package/tests/jest/BookReader/utils.test.js +250 -0
  247. package/tests/jest/BookReader.keyboard.test.js +190 -0
  248. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +10 -2
  249. package/tests/{BookReader.test.js → jest/BookReader.test.js} +43 -53
  250. package/tests/jest/plugins/plugin.archive_analytics.test.js +20 -0
  251. package/tests/jest/plugins/plugin.autoplay.test.js +35 -0
  252. package/tests/jest/plugins/plugin.chapters.test.js +195 -0
  253. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +4 -4
  254. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +22 -35
  255. package/tests/jest/plugins/plugin.text_selection.test.js +316 -0
  256. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  257. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +26 -47
  258. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +42 -9
  259. package/tests/jest/plugins/search/utils.js +25 -0
  260. package/tests/jest/plugins/search/utils.test.js +29 -0
  261. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +30 -10
  262. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  263. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  264. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  265. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +47 -1
  266. package/tests/{plugins → jest/plugins}/tts/utils.test.js +1 -60
  267. package/tests/jest/plugins/url/UrlPlugin.test.js +198 -0
  268. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +57 -18
  269. package/tests/jest/setup.js +3 -0
  270. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  271. package/tests/jest/util/docCookies.test.js +24 -0
  272. package/tests/{util → jest/util}/strings.test.js +1 -1
  273. package/tests/{utils.js → jest/utils.js} +38 -0
  274. package/webpack.config.js +16 -10
  275. package/.babelrc +0 -12
  276. package/.dependabot/config.yml +0 -6
  277. package/.testcaferc.json +0 -5
  278. package/BookReader/bookreader-component-bundle.js +0 -14330
  279. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  280. package/BookReader/bookreader-component-bundle.js.map +0 -1
  281. package/BookReader/icons/sort-ascending.svg +0 -1
  282. package/BookReader/icons/sort-descending.svg +0 -1
  283. package/BookReader/jquery-1.10.1.js +0 -108
  284. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  285. package/BookReader/plugins/plugin.menu_toggle.js +0 -369
  286. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  287. package/BookReader/plugins/plugin.mobile_nav.js +0 -335
  288. package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
  289. package/BookReaderDemo/BookReaderJSAutoplay.js +0 -56
  290. package/BookReaderDemo/IIIFBookReader.js +0 -207
  291. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  292. package/BookReaderDemo/demo-autoplay.html +0 -38
  293. package/BookReaderDemo/demo-iiif.js +0 -26
  294. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  295. package/karma.conf.js +0 -23
  296. package/src/BookNavigator/BookModel.js +0 -14
  297. package/src/BookNavigator/BookNavigator.js +0 -452
  298. package/src/BookNavigator/assets/book-loader.js +0 -27
  299. package/src/BookNavigator/assets/icon_sort_ascending.js +0 -5
  300. package/src/BookNavigator/assets/icon_sort_descending.js +0 -5
  301. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  302. package/src/BookNavigator/search/a-search-result.js +0 -55
  303. package/src/BookNavigator/volumes/volumes-provider.js +0 -108
  304. package/src/BookNavigator/volumes/volumes.js +0 -162
  305. package/src/BookReader/DebugConsole.js +0 -54
  306. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  307. package/src/ItemNavigator/ItemNavigator.js +0 -372
  308. package/src/ItemNavigator/providers/sharing.js +0 -29
  309. package/src/assets/icons/sort-ascending.svg +0 -1
  310. package/src/assets/icons/sort-descending.svg +0 -1
  311. package/src/css/_MobileNav.scss +0 -194
  312. package/src/dragscrollable-br.js +0 -261
  313. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  314. package/src/plugins/plugin.mobile_nav.js +0 -287
  315. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  316. package/tests/BookReader/DebugConsole.test.js +0 -25
  317. package/tests/BookReader/Mode1Up.test.js +0 -164
  318. package/tests/BookReader/Mode2Up.test.js +0 -247
  319. package/tests/BookReader/PageContainer.test.js +0 -115
  320. package/tests/BookReader/utils.test.js +0 -109
  321. package/tests/e2e/helpers/desktopSearch.js +0 -72
  322. package/tests/e2e/helpers/mobileSearch.js +0 -85
  323. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  324. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  325. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  326. package/tests/karma/BookNavigator/volumes.test.js +0 -133
  327. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  328. package/tests/plugins/plugin.archive_analytics.test.js +0 -23
  329. package/tests/plugins/plugin.autoplay.test.js +0 -52
  330. package/tests/plugins/plugin.chapters.test.js +0 -130
  331. package/tests/plugins/plugin.mobile_nav.test.js +0 -66
  332. package/tests/plugins/plugin.text_selection.test.js +0 -203
  333. package/tests/util/docCookies.test.js +0 -15
@@ -0,0 +1,777 @@
1
+ // @ts-check
2
+ import { customElement, property, query } from 'lit/decorators.js';
3
+ import {LitElement, html} from 'lit';
4
+ import { styleMap } from 'lit/directives/style-map.js';
5
+ import { ModeSmoothZoom } from './ModeSmoothZoom';
6
+ import { arrChanged, promisifyEvent } from './utils';
7
+ import { HTMLDimensionsCacher } from "./utils/HTMLDimensionsCacher";
8
+ import { PageModel } from './BookModel';
9
+ import { ModeCoordinateSpace } from './ModeCoordinateSpace';
10
+ /** @typedef {import('./BookModel').BookModel} BookModel */
11
+ /** @typedef {import('./BookModel').PageIndex} PageIndex */
12
+ /** @typedef {import('./ModeSmoothZoom').SmoothZoomable} SmoothZoomable */
13
+ /** @typedef {import('./PageContainer').PageContainer} PageContainer */
14
+ /** @typedef {import('../BookReader').default} BookReader */
15
+
16
+ // I _have_ to make this globally public, otherwise it won't let me call
17
+ // its constructor :/
18
+ /** @implements {SmoothZoomable} */
19
+ @customElement('br-mode-2up')
20
+ export class Mode2UpLit extends LitElement {
21
+ /****************************************/
22
+ /************** PROPERTIES **************/
23
+ /****************************************/
24
+
25
+ /** @type {BookReader} */
26
+ br;
27
+
28
+ /************** BOOK-RELATED PROPERTIES **************/
29
+
30
+ /** @type {BookModel} */
31
+ @property({ type: Object })
32
+ book;
33
+
34
+ /************** SCALE-RELATED PROPERTIES **************/
35
+
36
+ /** @type {ModeCoordinateSpace} Manage conversion between coordinates */
37
+ coordSpace = new ModeCoordinateSpace(this);
38
+
39
+ @property({ type: Number })
40
+ scale = 1;
41
+
42
+ initialScale = 1;
43
+
44
+ /** @type {import('./options').AutoFitValues} */
45
+ @property({ type: String })
46
+ autoFit = 'auto';
47
+
48
+ /************** VIRTUAL-FLIPPING PROPERTIES **************/
49
+
50
+ /** ms for flip animation */
51
+ flipSpeed = 400;
52
+
53
+ @query('.br-mode-2up__leafs--flipping') $flippingEdges;
54
+
55
+ /** @type {PageModel[]} */
56
+ @property({ type: Array, hasChanged: arrChanged })
57
+ visiblePages = [];
58
+
59
+ /** @type {PageModel | null} */
60
+ get pageLeft() {
61
+ return this.visiblePages.find(p => p.pageSide == 'L');
62
+ }
63
+
64
+ /** @type {PageModel | null} */
65
+ get pageRight() {
66
+ return this.visiblePages.find(p => p.pageSide == 'R');
67
+ }
68
+
69
+ /** @type {PageModel[]} */
70
+ @property({ type: Array })
71
+ renderedPages = [];
72
+
73
+ /** @type {Record<PageIndex, PageContainer>} position in inches */
74
+ pageContainerCache = {};
75
+
76
+ /** @type {{ direction: 'left' | 'right', pagesFlipping: [PageIndex, PageIndex], pagesFlippingCount: number }} */
77
+ activeFlip = null;
78
+
79
+ /** @private cache this value */
80
+ _leftCoverWidth = 0;
81
+
82
+ /************** DOM-RELATED PROPERTIES **************/
83
+
84
+ /** @type {HTMLElement} */
85
+ get $container() { return this; }
86
+
87
+ /** @type {HTMLElement} */
88
+ get $visibleWorld() { return this.$book; }
89
+
90
+ /** @type {HTMLElement} */
91
+ @query('.br-mode-2up__book')
92
+ $book;
93
+
94
+ get positions() {
95
+ return this.computePositions(this.pageLeft, this.pageRight);
96
+ }
97
+
98
+ /** @param {PageModel} page */
99
+ computePageHeight(page) {
100
+ return this.book.getMedianPageSizeInches().height;
101
+ }
102
+
103
+ /** @param {PageModel} page */
104
+ computePageWidth(page) {
105
+ return page.widthInches * this.computePageHeight(page) / page.heightInches;
106
+ }
107
+
108
+ /**
109
+ * @param {PageModel | null} pageLeft
110
+ * @param {PageModel | null} pageRight
111
+ */
112
+ computePositions(pageLeft, pageRight) {
113
+ const computePageWidth = this.computePageWidth.bind(this);
114
+ const numLeafs = this.book.getNumLeafs();
115
+ const movingPagesWidth = this.activeFlip ? Math.ceil(this.activeFlip.pagesFlippingCount / 2) * this.PAGE_THICKNESS_IN : 0;
116
+ const leftPagesCount = this.book.pageProgression == 'lr' ? (pageLeft?.index ?? 0) : (!pageLeft ? 0 : numLeafs - pageLeft.index);
117
+
118
+ // Everything is relative to the gutter
119
+ const gutter = this._leftCoverWidth + leftPagesCount * this.PAGE_THICKNESS_IN;
120
+
121
+ const pageLeftEnd = gutter;
122
+ const pageLeftWidth = !pageLeft ? computePageWidth(pageRight.right) : computePageWidth(pageLeft);
123
+ const pageLeftStart = gutter - pageLeftWidth;
124
+
125
+ const leafEdgesLeftEnd = pageLeftStart; // leafEdgesLeftStart + leafEdgesLeftMainWidth + leafEdgesLeftMovingWidth;
126
+ const leafEdgesLeftMovingWidth = this.activeFlip?.direction != 'left' ? 0 : movingPagesWidth;
127
+ const leafEdgesLeftMainWidth = Math.ceil(leftPagesCount / 2) * this.PAGE_THICKNESS_IN - leafEdgesLeftMovingWidth;
128
+ const leafEdgesLeftFullWidth = leafEdgesLeftMovingWidth + leafEdgesLeftMainWidth;
129
+ const leafEdgesLeftMovingStart = leafEdgesLeftEnd - leafEdgesLeftMovingWidth;
130
+ const leafEdgesLeftStart = leafEdgesLeftMovingStart - leafEdgesLeftMainWidth;
131
+
132
+ const pageRightStart = gutter;
133
+ const pageRightWidth = !pageRight ? 0 : computePageWidth(pageRight);
134
+ const pageRightEnd = pageRightStart + pageRightWidth;
135
+
136
+ const rightPagesCount = this.book.pageProgression == 'lr' ? (!pageRight ? 0 : numLeafs - pageRight.index) : (pageRight?.index ?? 0);
137
+ const leafEdgesRightStart = pageRightEnd;
138
+ const leafEdgesRightMovingWidth = this.activeFlip?.direction != 'right' ? 0 : movingPagesWidth;
139
+ const leafEdgesRightMainStart = leafEdgesRightStart + leafEdgesRightMovingWidth;
140
+ const leafEdgesRightMainWidth = Math.ceil(rightPagesCount / 2) * this.PAGE_THICKNESS_IN - leafEdgesRightMovingWidth;
141
+ const leafEdgesRightEnd = leafEdgesRightStart + leafEdgesRightMainWidth + leafEdgesRightMovingWidth;
142
+ const leafEdgesRightFullWidth = leafEdgesRightMovingWidth + leafEdgesRightMainWidth;
143
+
144
+ const spreadWidth = pageRightEnd - pageLeftStart;
145
+ const bookWidth = leafEdgesRightEnd - leafEdgesLeftStart;
146
+ return {
147
+ leafEdgesLeftStart,
148
+ leafEdgesLeftMainWidth,
149
+ leafEdgesLeftMovingStart,
150
+ leafEdgesLeftMovingWidth,
151
+ leafEdgesLeftEnd,
152
+ leafEdgesLeftFullWidth,
153
+
154
+ pageLeftStart,
155
+ pageLeftWidth,
156
+ pageLeftEnd,
157
+
158
+ gutter,
159
+
160
+ pageRightStart,
161
+ pageRightWidth,
162
+ pageRightEnd,
163
+
164
+ leafEdgesRightStart,
165
+ leafEdgesRightMovingWidth,
166
+ leafEdgesRightMainStart,
167
+ leafEdgesRightMainWidth,
168
+ leafEdgesRightEnd,
169
+ leafEdgesRightFullWidth,
170
+
171
+ spreadWidth,
172
+ bookWidth,
173
+ };
174
+ }
175
+
176
+ /** @type {HTMLDimensionsCacher} Cache things like clientWidth to reduce repaints */
177
+ htmlDimensionsCacher = new HTMLDimensionsCacher(this);
178
+
179
+ smoothZoomer = new ModeSmoothZoom(this);
180
+
181
+ /************** CONSTANT PROPERTIES **************/
182
+
183
+ /** How much to zoom when zoom button pressed */
184
+ ZOOM_FACTOR = 1.1;
185
+
186
+ /** How thick a page is in the real world, as an estimate for the leafs */
187
+ PAGE_THICKNESS_IN = 0.002;
188
+
189
+ /****************************************/
190
+ /************** PUBLIC API **************/
191
+ /****************************************/
192
+
193
+ /************** MAIN PUBLIC METHODS **************/
194
+
195
+ /**
196
+ * @param {PageIndex} index
197
+ * TODO Remove smooth option from everywhere.
198
+ */
199
+ async jumpToIndex(index, { smooth = true } = {}) {
200
+ await this.flipAnimation(index, { animate: smooth });
201
+ }
202
+
203
+ zoomIn() {
204
+ this.scale *= this.ZOOM_FACTOR;
205
+ }
206
+
207
+ zoomOut() {
208
+ this.scale *= 1 / this.ZOOM_FACTOR;
209
+ }
210
+
211
+ /********************************************/
212
+ /************** INTERNAL STUFF **************/
213
+ /********************************************/
214
+
215
+ /************** LIFE CYCLE **************/
216
+
217
+ /**
218
+ * @param {BookModel} book
219
+ * @param {BookReader} br
220
+ */
221
+ constructor(book, br) {
222
+ super();
223
+ this.book = book;
224
+
225
+ /** @type {BookReader} */
226
+ this.br = br;
227
+ }
228
+
229
+ /** @override */
230
+ firstUpdated(changedProps) {
231
+ super.firstUpdated(changedProps);
232
+ this.htmlDimensionsCacher.updateClientSizes();
233
+ this.smoothZoomer.attach();
234
+ }
235
+
236
+ /** @override */
237
+ connectedCallback() {
238
+ super.connectedCallback();
239
+ this.htmlDimensionsCacher.attachResizeListener();
240
+ this.smoothZoomer.attach();
241
+ }
242
+
243
+ /** @override */
244
+ disconnectedCallback() {
245
+ this.htmlDimensionsCacher.detachResizeListener();
246
+ this.smoothZoomer.detach();
247
+ super.disconnectedCallback();
248
+ }
249
+
250
+ /** @override */
251
+ updated(changedProps) {
252
+ // this.X is the new value
253
+ // changedProps.get('X') is the old value
254
+ if (changedProps.has('book')) {
255
+ this._leftCoverWidth = this.computePageWidth(this.book.getPage(this.book.pageProgression == 'lr' ? 0 : -1));
256
+ }
257
+ if (changedProps.has('visiblePages')) {
258
+ this.renderedPages = this.computeRenderedPages();
259
+ this.br.displayedIndices = this.visiblePages.map(p => p.index);
260
+ this.br.updateFirstIndex(this.br.displayedIndices[0]);
261
+ this.br._components.navbar.updateNavIndexThrottled();
262
+ }
263
+ if (changedProps.has('autoFit')) {
264
+ if (this.autoFit != 'none') {
265
+ this.resizeViaAutofit();
266
+ }
267
+ }
268
+ if (changedProps.has('scale')) {
269
+ const oldVal = changedProps.get('scale');
270
+ this.recenter();
271
+ this.smoothZoomer.updateViewportOnZoom(this.scale, oldVal);
272
+ }
273
+ }
274
+
275
+ /************** LIT CONFIGS **************/
276
+
277
+ /** @override */
278
+ createRenderRoot() {
279
+ // Disable shadow DOM; that would require a huge rejiggering of CSS
280
+ return this;
281
+ }
282
+
283
+ /************** RENDERING **************/
284
+
285
+ /** @override */
286
+ render() {
287
+ return html`
288
+ <div class="br-mode-2up__book" @mouseup=${this.handlePageClick}>
289
+ ${this.renderLeafEdges('left')}
290
+ ${this.renderedPages.map(p => this.renderPage(p))}
291
+ ${this.renderLeafEdges('right')}
292
+ </div>`;
293
+ }
294
+
295
+ /** @param {PageModel} page */
296
+ createPageContainer = (page) => {
297
+ return this.pageContainerCache[page.index] || (
298
+ this.pageContainerCache[page.index] = (
299
+ // @ts-ignore I know it's protected, TS! But Mode2Up and BookReader are friends.
300
+ this.br._createPageContainer(page.index)
301
+ )
302
+ );
303
+ }
304
+
305
+ /**
306
+ * @param {PageIndex} startIndex
307
+ */
308
+ initFirstRender(startIndex) {
309
+ const page = this.book.getPage(startIndex);
310
+ const spread = page.spread;
311
+ this.visiblePages = (
312
+ this.book.pageProgression == 'lr' ? [spread.left, spread.right] : [spread.right, spread.left]
313
+ ).filter(p => p);
314
+ this.htmlDimensionsCacher.updateClientSizes();
315
+ this.resizeViaAutofit(page);
316
+ this.initialScale = this.scale;
317
+ }
318
+
319
+ /** @param {PageModel} page */
320
+ renderPage = (page) => {
321
+ const wToR = this.coordSpace.worldUnitsToRenderedPixels;
322
+ const wToV = this.coordSpace.worldUnitsToVisiblePixels;
323
+
324
+ const width = wToR(this.computePageWidth(page));
325
+ const height = wToR(this.computePageHeight(page));
326
+ const isVisible = this.visiblePages.map(p => p.index).includes(page.index);
327
+ const positions = this.computePositions(page.spread.left, page.spread.right);
328
+
329
+ const pageContainerEl = this.createPageContainer(page)
330
+ .update({
331
+ dimensions: {
332
+ width,
333
+ height,
334
+ top: 0,
335
+ left: wToR(page.pageSide == 'L' ? positions.pageLeftStart : positions.pageLeftEnd),
336
+ },
337
+ reduce: page.width / wToV(this.computePageWidth(page)),
338
+ }).$container[0];
339
+
340
+ pageContainerEl.classList.toggle('BRpage-visible', isVisible);
341
+ return pageContainerEl;
342
+ }
343
+
344
+ /**
345
+ * @param {'left' | 'right'} side
346
+ * Renders the current leaf edges, as well as any "moving" leaf edges,
347
+ * i.e. leaf edges that are currently being flipped. Uses a custom element
348
+ * to render br-leaf-edges.
349
+ **/
350
+ renderLeafEdges = (side) => {
351
+ if (!this.visiblePages.length) return html``;
352
+ const fullWidthIn = side == 'left' ? this.positions.leafEdgesLeftFullWidth : this.positions.leafEdgesRightFullWidth;
353
+ if (!fullWidthIn) return html``;
354
+
355
+ const wToR = this.coordSpace.worldUnitsToRenderedPixels;
356
+ const height = wToR(this.computePageHeight(this.visiblePages[0]));
357
+ const hasMovingPages = this.activeFlip?.direction == side;
358
+
359
+ const leftmostPage = this.book.getPage(this.book.pageProgression == 'lr' ? 0 : this.book.getNumLeafs() - 1);
360
+ const rightmostPage = this.book.getPage(this.book.pageProgression == 'lr' ? this.book.getNumLeafs() - 1 : 0);
361
+ const numPagesFlipping = hasMovingPages ? this.activeFlip.pagesFlippingCount : 0;
362
+ const range = side == 'left' ?
363
+ [leftmostPage.index, this.pageLeft.goLeft(numPagesFlipping).index] :
364
+ [this.pageRight.goRight(numPagesFlipping).index, rightmostPage.index];
365
+
366
+ const mainEdgesStyle = {
367
+ width: `${wToR(side == 'left' ? this.positions.leafEdgesLeftMainWidth : this.positions.leafEdgesRightMainWidth)}px`,
368
+ height: `${height}px`,
369
+ left: `${wToR(side == 'left' ? this.positions.leafEdgesLeftStart : this.positions.leafEdgesRightMainStart)}px`,
370
+ };
371
+ const mainEdges = html`
372
+ <br-leaf-edges
373
+ leftIndex=${range[0]}
374
+ rightIndex=${range[1]}
375
+ .book=${this.book}
376
+ .pageClickHandler=${(index) => this.br.jumpToIndex(index)}
377
+ side=${side}
378
+ class="br-mode-2up__leafs br-mode-2up__leafs--${side}"
379
+ style=${styleMap(mainEdgesStyle)}
380
+ ></br-leaf-edges>
381
+ `;
382
+
383
+ if (hasMovingPages) {
384
+ const width = wToR(side == 'left' ? this.positions.leafEdgesLeftMovingWidth : this.positions.leafEdgesRightMovingWidth);
385
+ const style = {
386
+ width: `${width}px`,
387
+ height: `${height}px`,
388
+ left: `${wToR(side == 'left' ? this.positions.leafEdgesLeftMovingStart : this.positions.leafEdgesRightStart)}px`,
389
+ pointerEvents: 'none',
390
+ transformOrigin: `${wToR(side == 'left' ? this.positions.pageLeftWidth : -this.positions.pageRightWidth) + width / 2}px 0`,
391
+ };
392
+
393
+ const movingEdges = html`
394
+ <br-leaf-edges
395
+ leftIndex=${this.activeFlip.pagesFlipping[0]}
396
+ rightIndex=${this.activeFlip.pagesFlipping[1]}
397
+ .book=${this.book}
398
+ side=${side}
399
+ class="br-mode-2up__leafs br-mode-2up__leafs--${side} br-mode-2up__leafs--flipping"
400
+ style=${styleMap(style)}
401
+ ></br-leaf-edges>
402
+ `;
403
+
404
+ return side == 'left' ? html`${mainEdges}${movingEdges}` : html`${movingEdges}${mainEdges}`;
405
+ } else {
406
+ return mainEdges;
407
+ }
408
+ }
409
+
410
+ resizeViaAutofit(page = this.visiblePages[0]) {
411
+ this.scale = this.computeScale(page, this.autoFit);
412
+ }
413
+
414
+ recenter(page = this.visiblePages[0]) {
415
+ const translate = this.computeTranslate(page, this.scale);
416
+ this.$book.style.transform = `translateX(${translate.x}px) translateY(${translate.y}px) scale(${this.scale})`;
417
+ }
418
+
419
+ /**
420
+ * @returns {PageModel[]}
421
+ */
422
+ computeRenderedPages() {
423
+ // Also render 2 pages before/after
424
+ // @ts-ignore TS doesn't understand the filtering out of null values
425
+ return [
426
+ this.visiblePages[0]?.prev?.prev,
427
+ this.visiblePages[0]?.prev,
428
+ ...this.visiblePages,
429
+ this.visiblePages[this.visiblePages.length - 1]?.next,
430
+ this.visiblePages[this.visiblePages.length - 1]?.next?.next,
431
+ ]
432
+ .filter(p => p)
433
+ // Never render more than 10 pages! Usually means something is wrong
434
+ .slice(0, 10);
435
+ }
436
+
437
+ /**
438
+ * @param {PageModel} page
439
+ * @param {import('./options').AutoFitValues} autoFit
440
+ */
441
+ computeScale(page, autoFit) {
442
+ if (!page) return 1;
443
+ const spread = page.spread;
444
+ const bookWidth = this.computePositions(spread.left, spread.right).bookWidth;
445
+ const bookHeight = this.computePageHeight(spread.left || spread.right);
446
+ const BOOK_PADDING_PX = 10;
447
+ const curScale = this.scale;
448
+ this.scale = 1; // Need this temporarily
449
+ const widthScale = this.coordSpace.renderedPixelsToWorldUnits(this.htmlDimensionsCacher.clientWidth - 2 * BOOK_PADDING_PX) / bookWidth;
450
+ const heightScale = this.coordSpace.renderedPixelsToWorldUnits(this.htmlDimensionsCacher.clientHeight - 2 * BOOK_PADDING_PX) / bookHeight;
451
+ this.scale = curScale;
452
+ const realScale = 1;
453
+
454
+ let scale = realScale;
455
+ if (autoFit == 'width') {
456
+ scale = widthScale;
457
+ } else if (autoFit == 'height') {
458
+ scale = heightScale;
459
+ } else if (autoFit == 'auto') {
460
+ scale = Math.min(widthScale, heightScale);
461
+ } else if (autoFit == 'none') {
462
+ scale = this.scale;
463
+ } else {
464
+ // Should be impossible
465
+ throw new Error(`Invalid autoFit value: ${autoFit}`);
466
+ }
467
+
468
+ return scale;
469
+ }
470
+
471
+ /**
472
+ * @param {PageModel} page
473
+ * @param {number} scale
474
+ * @returns {{x: number, y: number}}
475
+ */
476
+ computeTranslate(page, scale = this.scale) {
477
+ if (!page) return { x: 0, y: 0 };
478
+ const spread = page.spread;
479
+ // Default to real size if it fits, otherwise default to full height
480
+ const positions = this.computePositions(spread.left, spread.right);
481
+ const bookWidth = positions.bookWidth;
482
+ const bookHeight = this.computePageHeight(spread.left || spread.right);
483
+ const visibleBookWidth = this.coordSpace.worldUnitsToRenderedPixels(bookWidth) * scale;
484
+ const visibleBookHeight = this.coordSpace.worldUnitsToRenderedPixels(bookHeight) * scale;
485
+ const leftOffset = this.coordSpace.worldUnitsToRenderedPixels(-positions.leafEdgesLeftStart) * scale;
486
+ const translateX = (this.htmlDimensionsCacher.clientWidth - visibleBookWidth) / 2 + leftOffset;
487
+ const translateY = (this.htmlDimensionsCacher.clientHeight - visibleBookHeight) / 2;
488
+ return { x: Math.max(leftOffset, translateX), y: Math.max(0, translateY) };
489
+ }
490
+
491
+ /************** VIRTUAL FLIPPING LOGIC **************/
492
+
493
+ /**
494
+ * @param {'left' | 'right' | 'next' | 'prev' | PageIndex | PageModel | {left: PageModel | null, right: PageModel | null}} nextSpread
495
+ */
496
+ async flipAnimation(nextSpread, { animate = true, flipSpeed = this.flipSpeed } = {}) {
497
+ const curSpread = (this.pageLeft || this.pageRight)?.spread;
498
+ if (!curSpread) {
499
+ // Nothings been actually rendered yet! Will be corrected during initFirstRender
500
+ return;
501
+ }
502
+
503
+ flipSpeed = flipSpeed || this.flipSpeed; // Handle null
504
+ nextSpread = this.parseNextSpread(nextSpread);
505
+ if (this.activeFlip || !nextSpread) return;
506
+
507
+ const progression = this.book.pageProgression;
508
+ const curLeftIndex = curSpread.left?.index ?? (progression == 'lr' ? -1 : this.book.getNumLeafs());
509
+ const nextLeftIndex = nextSpread.left?.index ?? (progression == 'lr' ? -1 : this.book.getNumLeafs());
510
+ if (curLeftIndex == nextLeftIndex) return;
511
+
512
+ const renderedIndices = this.renderedPages.map(p => p.index);
513
+ /** @type {PageContainer[]} */
514
+ const nextPageContainers = [];
515
+ for (const page of [nextSpread.left, nextSpread.right]) {
516
+ if (!page) continue;
517
+ nextPageContainers.push(this.createPageContainer(page));
518
+ if (!renderedIndices.includes(page.index)) {
519
+ this.renderedPages.push(page);
520
+ }
521
+ }
522
+
523
+ const curTranslate = this.computeTranslate(curSpread.left || curSpread.right, this.scale);
524
+ const idealNextTranslate = this.computeTranslate(nextSpread.left || nextSpread.right, this.scale);
525
+ const translateDiff = Math.sqrt((idealNextTranslate.x - curTranslate.x) ** 2 + (idealNextTranslate.y - curTranslate.y) ** 2);
526
+ let nextTranslate = `translate(${idealNextTranslate.x}px, ${idealNextTranslate.y}px)`;
527
+ if (translateDiff < 50) {
528
+ const activeTranslate = this.$book.style.transform.match(/translate\([^)]+\)/)?.[0];
529
+ if (activeTranslate) {
530
+ nextTranslate = activeTranslate;
531
+ }
532
+ }
533
+ const newTransform = `${nextTranslate} scale(${this.scale})`;
534
+
535
+ if (animate && 'animate' in Element.prototype) {
536
+ // This table is used to determine the direction of the flip animation:
537
+ // | < | >
538
+ // lr | L | R
539
+ // rl | R | L
540
+ const direction = progression == 'lr' ? (nextLeftIndex > curLeftIndex ? 'right' : 'left') : (nextLeftIndex > curLeftIndex ? 'left' : 'right');
541
+
542
+ this.activeFlip = {
543
+ direction,
544
+ pagesFlipping: [curLeftIndex, nextLeftIndex],
545
+ pagesFlippingCount: Math.abs(nextLeftIndex - curLeftIndex),
546
+ };
547
+
548
+ this.classList.add(`br-mode-2up--flipping-${direction}`);
549
+ this.classList.add(`BRpageFlipping`);
550
+
551
+ // Wait for lit update cycle to finish so that entering pages are rendered
552
+ this.requestUpdate();
553
+ await this.updateComplete;
554
+
555
+ this.visiblePages
556
+ .map(p => this.pageContainerCache[p.index].$container)
557
+ .forEach($c => $c.addClass('BRpage-exiting'));
558
+
559
+ nextPageContainers.forEach(c => c.$container.addClass('BRpage-entering'));
560
+
561
+ /** @type {KeyframeAnimationOptions} */
562
+ const animationStyle = {
563
+ duration: flipSpeed + this.activeFlip.pagesFlippingCount,
564
+ easing: 'ease-in',
565
+ fill: 'none',
566
+ };
567
+
568
+ const bookCenteringAnimation = this.$book.animate([
569
+ { transform: newTransform },
570
+ ], animationStyle);
571
+
572
+ const edgeTranslationAnimation = this.$flippingEdges.animate([
573
+ { transform: `rotateY(0deg)` },
574
+ {
575
+ transform: direction == 'left' ? `rotateY(-180deg)` : `rotateY(180deg)`,
576
+ },
577
+ ], animationStyle);
578
+
579
+ const exitingPageAnimation = direction == 'left' ?
580
+ this.querySelector('.BRpage-exiting[data-side=L]').animate([
581
+ { transform: `rotateY(0deg)` },
582
+ { transform: `rotateY(180deg)` },
583
+ ], animationStyle) : this.querySelector('.BRpage-exiting[data-side=R]').animate([
584
+ { transform: `rotateY(0deg)` },
585
+ { transform: `rotateY(-180deg)` },
586
+ ], animationStyle);
587
+
588
+ const enteringPageAnimation = direction == 'left' ?
589
+ this.querySelector('.BRpage-entering[data-side=R]').animate([
590
+ { transform: `rotateY(-180deg)` },
591
+ { transform: `rotateY(0deg)` },
592
+ ], animationStyle) : this.querySelector('.BRpage-entering[data-side=L]').animate([
593
+ { transform: `rotateY(180deg)` },
594
+ { transform: `rotateY(0deg)` },
595
+ ], animationStyle);
596
+
597
+ bookCenteringAnimation.play();
598
+ edgeTranslationAnimation.play();
599
+ exitingPageAnimation.play();
600
+ enteringPageAnimation.play();
601
+
602
+ nextPageContainers.forEach(c => c.$container.addClass('BRpage-visible'));
603
+
604
+ await Promise.race([
605
+ promisifyEvent(bookCenteringAnimation, 'finish'),
606
+ promisifyEvent(edgeTranslationAnimation, 'finish'),
607
+ promisifyEvent(exitingPageAnimation, 'finish'),
608
+ promisifyEvent(enteringPageAnimation, 'finish'),
609
+ ]);
610
+
611
+ this.classList.remove(`br-mode-2up--flipping-${direction}`);
612
+ this.classList.remove(`BRpageFlipping`);
613
+
614
+ this.visiblePages
615
+ .map(p => this.pageContainerCache[p.index].$container)
616
+ .forEach($c => $c.removeClass('BRpage-exiting BRpage-visible'));
617
+ nextPageContainers.forEach(c => c.$container.removeClass('BRpage-entering'));
618
+ this.activeFlip = null;
619
+ }
620
+
621
+ this.$book.style.transform = newTransform;
622
+ this.visiblePages = (
623
+ progression == 'lr' ? [nextSpread.left, nextSpread.right] : [nextSpread.right, nextSpread.left]
624
+ ).filter(x => x);
625
+ }
626
+
627
+ /**
628
+ * @param {'left' | 'right' | 'next' | 'prev' | PageIndex | PageModel | {left: PageModel | null, right: PageModel | null}} nextSpread
629
+ * @returns {{left: PageModel | null, right: PageModel | null}}
630
+ */
631
+ parseNextSpread(nextSpread) {
632
+ if (nextSpread == 'next') {
633
+ nextSpread = this.book.pageProgression == 'lr' ? 'right' : 'left';
634
+ } else if (nextSpread == 'prev') {
635
+ nextSpread = this.book.pageProgression == 'lr' ? 'left' : 'right';
636
+ }
637
+
638
+ const curSpread = (this.pageLeft || this.pageRight).spread;
639
+
640
+ if (nextSpread == 'left') {
641
+ nextSpread = curSpread.left?.findLeft({ combineConsecutiveUnviewables: true })?.spread;
642
+ } else if (nextSpread == 'right') {
643
+ nextSpread = curSpread.right?.findRight({ combineConsecutiveUnviewables: true })?.spread;
644
+ }
645
+
646
+ if (typeof(nextSpread) == 'number') {
647
+ nextSpread = this.book.getPage(nextSpread).spread;
648
+ }
649
+
650
+ if (nextSpread instanceof PageModel) {
651
+ nextSpread = nextSpread.spread;
652
+ }
653
+
654
+ return nextSpread;
655
+ }
656
+
657
+ /************** INPUT HANDLERS **************/
658
+
659
+ /**
660
+ * @param {MouseEvent} ev
661
+ */
662
+ handlePageClick = (ev) => {
663
+ // right click
664
+ if (ev.which == 3 && this.br.protected) {
665
+ return false;
666
+ }
667
+
668
+ if (ev.which != 1) return;
669
+
670
+ const $page = $(ev.target).closest('.BRpagecontainer');
671
+ if (!$page.length) return;
672
+ if ($page.data('side') == 'L') {
673
+ this.flipAnimation('left');
674
+ } else if ($page.data('side') == 'R') {
675
+ this.flipAnimation('right');
676
+ }
677
+ }
678
+ }
679
+
680
+ @customElement('br-leaf-edges')
681
+ export class LeafEdges extends LitElement {
682
+ @property({ type: Number }) leftIndex = 0;
683
+ @property({ type: Number }) rightIndex = 0;
684
+ /** @type {'left' | 'right'} */
685
+ @property({ type: String }) side = 'left';
686
+
687
+ /** @type {BookModel} */
688
+ @property({attribute: false})
689
+ book = null;
690
+
691
+ /** @type {(index: PageIndex) => void} */
692
+ @property({attribute: false, type: Function})
693
+ pageClickHandler = null;
694
+
695
+ @query('.br-leaf-edges__bar') $hoverBar;
696
+ @query('.br-leaf-edges__label') $hoverLabel;
697
+
698
+ get pageWidthPercent() {
699
+ return 100 * 1 / (this.rightIndex - this.leftIndex + 1);
700
+ }
701
+
702
+ render() {
703
+ return html`
704
+ <div
705
+ class="br-leaf-edges__bar"
706
+ style="${styleMap({width: `${this.pageWidthPercent}%`})}"
707
+ ></div>
708
+ <div class="br-leaf-edges__label">Page</div>`;
709
+ }
710
+
711
+ connectedCallback() {
712
+ super.connectedCallback();
713
+ this.addEventListener('mouseenter', this.onMouseEnter);
714
+ this.addEventListener('mouseleave', this.onMouseLeave);
715
+ this.addEventListener('click', this.onClick);
716
+ }
717
+ disconnectedCallback() {
718
+ super.disconnectedCallback();
719
+ this.addEventListener('mouseenter', this.onMouseEnter);
720
+ this.removeEventListener('mousemove', this.onMouseMove);
721
+ this.removeEventListener('mouseleave', this.onMouseLeave);
722
+ }
723
+
724
+ /** @override */
725
+ createRenderRoot() {
726
+ // Disable shadow DOM; that would require a huge rejiggering of CSS
727
+ return this;
728
+ }
729
+
730
+ /**
731
+ * @param {MouseEvent} e
732
+ */
733
+ onMouseEnter = (e) => {
734
+ this.addEventListener('mousemove', this.onMouseMove);
735
+ this.$hoverBar.style.display = 'block';
736
+ this.$hoverLabel.style.display = 'block';
737
+ }
738
+
739
+ /**
740
+ * @param {MouseEvent} e
741
+ */
742
+ onMouseMove = (e) => {
743
+ this.$hoverBar.style.left = `${e.offsetX}px`;
744
+ if (this.side == 'right') {
745
+ this.$hoverLabel.style.left = `${e.offsetX}px`;
746
+ } else {
747
+ this.$hoverLabel.style.right = `${this.offsetWidth - e.offsetX}px`;
748
+ }
749
+ this.$hoverLabel.style.top = `${e.offsetY}px`;
750
+ const index = this.mouseEventToPageIndex(e);
751
+ this.$hoverLabel.textContent = this.book.getPageName(index);
752
+ }
753
+
754
+ /**
755
+ * @param {MouseEvent} e
756
+ */
757
+ onMouseLeave = (e) => {
758
+ this.removeEventListener('mousemove', this.onMouseMove);
759
+ this.$hoverBar.style.display = 'none';
760
+ this.$hoverLabel.style.display = 'none';
761
+ }
762
+
763
+ /**
764
+ * @param {MouseEvent} e
765
+ */
766
+ onClick = (e) => {
767
+ this.pageClickHandler(this.mouseEventToPageIndex(e));
768
+ }
769
+
770
+ /**
771
+ * @param {MouseEvent} e
772
+ * @returns {PageIndex}
773
+ */
774
+ mouseEventToPageIndex(e) {
775
+ return Math.floor(this.leftIndex + (e.offsetX / this.offsetWidth) * (this.rightIndex - this.leftIndex + 1));
776
+ }
777
+ }