@internetarchive/bookreader 5.0.0-5 → 5.0.0-50-a1

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 (260) hide show
  1. package/.eslintrc.js +17 -15
  2. package/.github/workflows/node.js.yml +77 -6
  3. package/.github/workflows/npm-publish.yml +6 -20
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +131 -339
  6. package/BookReader/BookReader.js +1 -1
  7. package/BookReader/BookReader.js.LICENSE.txt +24 -0
  8. package/BookReader/BookReader.js.map +1 -1
  9. package/BookReader/ia-bookreader-bundle.js +1493 -0
  10. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +17 -0
  11. package/BookReader/ia-bookreader-bundle.js.map +1 -0
  12. package/BookReader/icons/close-circle-dark.svg +1 -0
  13. package/BookReader/icons/magnify-minus.svg +1 -1
  14. package/BookReader/icons/magnify-plus.svg +1 -1
  15. package/BookReader/icons/pause.svg +1 -1
  16. package/BookReader/icons/playback-speed.svg +1 -1
  17. package/BookReader/icons/read-aloud.svg +1 -1
  18. package/BookReader/icons/voice.svg +1 -0
  19. package/BookReader/images/BRicons.svg +2 -2
  20. package/BookReader/images/books_graphic.svg +1 -1
  21. package/BookReader/images/icon_book.svg +1 -1
  22. package/BookReader/images/icon_gear.svg +1 -1
  23. package/BookReader/images/icon_info.svg +1 -1
  24. package/BookReader/images/icon_playback-rate.svg +1 -1
  25. package/BookReader/images/icon_search_button.svg +1 -1
  26. package/BookReader/images/icon_share.svg +1 -1
  27. package/BookReader/images/icon_speaker.svg +1 -1
  28. package/BookReader/images/icon_speaker_open.svg +1 -1
  29. package/BookReader/images/marker_chap-off.svg +1 -1
  30. package/BookReader/images/marker_chap-on.svg +1 -1
  31. package/BookReader/images/marker_srch-on.svg +1 -1
  32. package/BookReader/jquery-3.js +2 -0
  33. package/BookReader/jquery-3.js.LICENSE.txt +24 -0
  34. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  35. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  36. package/BookReader/plugins/plugin.autoplay.js +1 -1
  37. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  38. package/BookReader/plugins/plugin.chapters.js +1 -1
  39. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  40. package/BookReader/plugins/plugin.iframe.js +1 -1
  41. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  42. package/BookReader/plugins/plugin.mobile_nav.js +1 -1
  43. package/BookReader/plugins/plugin.mobile_nav.js.map +1 -1
  44. package/BookReader/plugins/plugin.resume.js +1 -1
  45. package/BookReader/plugins/plugin.resume.js.map +1 -1
  46. package/BookReader/plugins/plugin.search.js +1 -1
  47. package/BookReader/plugins/plugin.search.js.map +1 -1
  48. package/BookReader/plugins/plugin.text_selection.js +1 -1
  49. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  50. package/BookReader/plugins/plugin.tts.js +1 -1
  51. package/BookReader/plugins/plugin.tts.js.map +1 -1
  52. package/BookReader/plugins/plugin.url.js +1 -1
  53. package/BookReader/plugins/plugin.url.js.map +1 -1
  54. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
  55. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  56. package/BookReader/webcomponents-bundle.js +3 -0
  57. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  58. package/BookReader/webcomponents-bundle.js.map +1 -0
  59. package/BookReaderDemo/BookReaderDemo.css +14 -1
  60. package/BookReaderDemo/IADemoBr.js +148 -0
  61. package/BookReaderDemo/demo-advanced.html +2 -2
  62. package/BookReaderDemo/demo-autoplay.html +2 -1
  63. package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
  64. package/BookReaderDemo/demo-fullscreen-mobile.html +2 -1
  65. package/BookReaderDemo/demo-fullscreen.html +2 -1
  66. package/BookReaderDemo/demo-iiif.html +2 -1
  67. package/BookReaderDemo/demo-internetarchive.html +84 -17
  68. package/BookReaderDemo/demo-multiple.html +2 -1
  69. package/BookReaderDemo/demo-preview-pages.html +2 -1
  70. package/BookReaderDemo/demo-simple.html +2 -1
  71. package/BookReaderDemo/demo-vendor-fullscreen.html +2 -1
  72. package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
  73. package/BookReaderDemo/immersion-1up.html +2 -1
  74. package/BookReaderDemo/immersion-mode.html +2 -1
  75. package/BookReaderDemo/toggle_controls.html +2 -1
  76. package/BookReaderDemo/view_mode.html +2 -1
  77. package/BookReaderDemo/viewmode-cycle.html +2 -3
  78. package/CHANGELOG.md +202 -0
  79. package/README.md +14 -1
  80. package/babel.config.js +18 -0
  81. package/codecov.yml +6 -0
  82. package/index.html +3 -0
  83. package/jsconfig.json +19 -0
  84. package/package.json +66 -56
  85. package/renovate.json +52 -0
  86. package/scripts/preversion.js +4 -1
  87. package/src/BookNavigator/assets/bookmark-colors.js +1 -1
  88. package/src/BookNavigator/assets/button-base.js +9 -2
  89. package/src/BookNavigator/assets/ia-logo.js +17 -0
  90. package/src/BookNavigator/assets/icon_checkmark.js +1 -1
  91. package/src/BookNavigator/assets/icon_close.js +1 -1
  92. package/src/BookNavigator/assets/icon_sort_asc.js +5 -0
  93. package/src/BookNavigator/assets/icon_sort_desc.js +5 -0
  94. package/src/BookNavigator/assets/icon_sort_neutral.js +5 -0
  95. package/src/BookNavigator/assets/icon_volumes.js +11 -0
  96. package/src/BookNavigator/book-navigator.js +583 -0
  97. package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
  98. package/src/BookNavigator/bookmarks/bookmark-edit.js +3 -4
  99. package/src/BookNavigator/bookmarks/bookmarks-list.js +2 -3
  100. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +3 -8
  101. package/src/BookNavigator/bookmarks/bookmarks-provider.js +21 -8
  102. package/src/BookNavigator/bookmarks/ia-bookmarks.js +102 -66
  103. package/src/BookNavigator/delete-modal-actions.js +1 -1
  104. package/src/BookNavigator/downloads/downloads-provider.js +36 -21
  105. package/src/BookNavigator/downloads/downloads.js +41 -25
  106. package/src/BookNavigator/search/a-search-result.js +18 -13
  107. package/src/BookNavigator/search/search-provider.js +80 -28
  108. package/src/BookNavigator/search/search-results.js +10 -18
  109. package/src/BookNavigator/sharing.js +27 -0
  110. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +11 -10
  111. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +3 -3
  112. package/src/BookNavigator/volumes/volumes-provider.js +114 -0
  113. package/src/BookNavigator/volumes/volumes.js +188 -0
  114. package/src/BookReader/BookModel.js +0 -29
  115. package/src/BookReader/DebugConsole.js +3 -3
  116. package/src/BookReader/DragScrollable.js +233 -0
  117. package/src/BookReader/Mode1Up.js +51 -351
  118. package/src/BookReader/Mode1UpLit.js +441 -0
  119. package/src/BookReader/Mode2Up.js +120 -105
  120. package/src/BookReader/ModeSmoothZoom.js +179 -0
  121. package/src/BookReader/ModeThumb.js +17 -11
  122. package/src/BookReader/Navbar/Navbar.js +10 -36
  123. package/src/BookReader/PageContainer.js +69 -6
  124. package/src/BookReader/ReduceSet.js +1 -1
  125. package/src/BookReader/Toolbar/Toolbar.js +10 -37
  126. package/src/BookReader/options.js +10 -0
  127. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  128. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  129. package/src/BookReader/utils.js +92 -13
  130. package/src/BookReader.js +431 -620
  131. package/src/assets/icons/close-circle-dark.svg +1 -0
  132. package/src/assets/icons/magnify-minus.svg +3 -7
  133. package/src/assets/icons/magnify-plus.svg +3 -7
  134. package/src/assets/icons/voice.svg +1 -0
  135. package/src/css/BookReader.scss +0 -12
  136. package/src/css/_BRComponent.scss +1 -1
  137. package/src/css/_BRmain.scss +19 -24
  138. package/src/css/_BRnav.scss +4 -26
  139. package/src/css/_BRpages.scss +35 -0
  140. package/src/css/_BRsearch.scss +25 -216
  141. package/src/css/_TextSelection.scss +14 -17
  142. package/src/css/_colorbox.scss +2 -2
  143. package/src/css/_controls.scss +17 -5
  144. package/src/css/_icons.scss +6 -0
  145. package/src/ia-bookreader/ia-bookreader.js +224 -0
  146. package/src/plugins/plugin.autoplay.js +4 -4
  147. package/src/plugins/plugin.chapters.js +28 -35
  148. package/src/plugins/plugin.mobile_nav.js +11 -10
  149. package/src/plugins/plugin.resume.js +3 -3
  150. package/src/plugins/plugin.text_selection.js +26 -39
  151. package/src/plugins/plugin.vendor-fullscreen.js +4 -4
  152. package/src/plugins/search/plugin.search.js +174 -116
  153. package/src/plugins/search/view.js +63 -179
  154. package/src/plugins/tts/AbstractTTSEngine.js +46 -37
  155. package/src/plugins/tts/FestivalTTSEngine.js +13 -14
  156. package/src/plugins/tts/PageChunk.js +15 -21
  157. package/src/plugins/tts/PageChunkIterator.js +8 -12
  158. package/src/plugins/tts/WebTTSEngine.js +66 -69
  159. package/src/plugins/tts/plugin.tts.js +92 -109
  160. package/src/plugins/tts/utils.js +0 -9
  161. package/src/plugins/url/UrlPlugin.js +184 -0
  162. package/src/plugins/{plugin.url.js → url/plugin.url.js} +28 -6
  163. package/src/util/manifestGenerator.js +0 -0
  164. package/tests/e2e/README.md +37 -0
  165. package/tests/e2e/autoplay.test.js +2 -2
  166. package/tests/e2e/base.test.js +7 -7
  167. package/tests/e2e/helpers/base.js +9 -3
  168. package/tests/e2e/helpers/debug.js +1 -1
  169. package/tests/e2e/helpers/desktopSearch.js +14 -13
  170. package/tests/e2e/helpers/mobileSearch.js +3 -3
  171. package/tests/e2e/helpers/params.js +17 -0
  172. package/tests/e2e/models/Navigation.js +13 -4
  173. package/tests/e2e/rightToLeft.test.js +4 -5
  174. package/tests/e2e/viewmode.test.js +38 -33
  175. package/tests/jest/BookNavigator/book-navigator.test.js +634 -0
  176. package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
  177. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
  178. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
  179. package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
  180. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
  181. package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
  182. package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
  183. package/tests/{karma/BookNavigator → jest/BookNavigator/search}/search-results.test.js +102 -58
  184. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
  185. package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
  186. package/tests/jest/BookNavigator/volumes/volumes-provider.test.js +184 -0
  187. package/tests/jest/BookNavigator/volumes/volumes.test.js +97 -0
  188. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +34 -14
  189. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +176 -0
  190. package/tests/{BookReader → jest/BookReader}/DebugConsole.test.js +1 -1
  191. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  192. package/tests/jest/BookReader/Mode1UpLit.test.js +92 -0
  193. package/tests/{BookReader → jest/BookReader}/Mode2Up.test.js +36 -15
  194. package/tests/jest/BookReader/ModeSmoothZoom.test.js +149 -0
  195. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  196. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +7 -7
  197. package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +88 -6
  198. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  199. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
  200. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  201. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  202. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  203. package/tests/jest/BookReader/utils.test.js +186 -0
  204. package/tests/jest/BookReader.keyboard.test.js +190 -0
  205. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
  206. package/tests/{BookReader.test.js → jest/BookReader.test.js} +18 -37
  207. package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
  208. package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +4 -4
  209. package/tests/{plugins → jest/plugins}/plugin.chapters.test.js +10 -11
  210. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
  211. package/tests/{plugins → jest/plugins}/plugin.mobile_nav.test.js +5 -5
  212. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
  213. package/tests/{plugins → jest/plugins}/plugin.text_selection.test.js +39 -47
  214. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  215. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +63 -47
  216. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +35 -6
  217. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +9 -9
  218. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  219. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  220. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  221. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +1 -1
  222. package/tests/{plugins → jest/plugins}/tts/utils.test.js +3 -28
  223. package/tests/jest/plugins/url/UrlPlugin.test.js +190 -0
  224. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +33 -14
  225. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  226. package/tests/{util → jest/util}/docCookies.test.js +1 -1
  227. package/tests/{util → jest/util}/strings.test.js +1 -1
  228. package/tests/{utils.js → jest/utils.js} +38 -0
  229. package/webpack.config.js +11 -5
  230. package/.babelrc +0 -12
  231. package/.dependabot/config.yml +0 -6
  232. package/.testcaferc.json +0 -5
  233. package/BookReader/bookreader-component-bundle.js +0 -1450
  234. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  235. package/BookReader/bookreader-component-bundle.js.map +0 -1
  236. package/BookReader/jquery-1.10.1.js +0 -2
  237. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  238. package/BookReader/plugins/plugin.menu_toggle.js +0 -2
  239. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  240. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  241. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  242. package/karma.conf.js +0 -23
  243. package/src/BookNavigator/BookModel.js +0 -14
  244. package/src/BookNavigator/BookNavigator.js +0 -438
  245. package/src/BookNavigator/assets/book-loader.js +0 -27
  246. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  247. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  248. package/src/ItemNavigator/ItemNavigator.js +0 -372
  249. package/src/ItemNavigator/providers/sharing.js +0 -29
  250. package/src/Layers/sharing/sharing-provider.js +0 -22
  251. package/src/dragscrollable-br.js +0 -261
  252. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  253. package/src/plugins/plugin.bookmarks.js +0 -50
  254. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  255. package/tests/BookReader/Mode1Up.test.js +0 -164
  256. package/tests/BookReader/utils.test.js +0 -109
  257. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  258. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  259. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  260. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
@@ -0,0 +1,441 @@
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, calcScreenDPI, genToArray, sum, throttle } from './utils';
7
+ import { HTMLDimensionsCacher } from "./utils/HTMLDimensionsCacher";
8
+ import { ScrollClassAdder } from './utils/ScrollClassAdder';
9
+ /** @typedef {import('./BookModel').BookModel} BookModel */
10
+ /** @typedef {import('./BookModel').PageIndex} PageIndex */
11
+ /** @typedef {import('./BookModel').PageModel} PageModel */
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
+ // it's constructor :/
18
+ /** @implements {SmoothZoomable} */
19
+ @customElement('br-mode-1up')
20
+ export class Mode1UpLit 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
+ /** @type {PageModel[]} */
35
+ @property({ type: Array })
36
+ pages = [];
37
+
38
+ /** @type {Record<PageIndex, number>} in world coordinates (inches) */
39
+ @property({ type: Object })
40
+ pageTops = {};
41
+
42
+ /************** SCALE-RELATED PROPERTIES **************/
43
+
44
+ /** @private */
45
+ screenDPI = calcScreenDPI();
46
+
47
+ /**
48
+ * How much smaller the rendered pages are than the real-world item
49
+ *
50
+ * Mode1Up doesn't use the br.reduce because it is DPI aware. The reduction factor
51
+ * of a given leaf can change (since leaves can have different DPIs), but the real-world
52
+ * reduction is constant throughout.
53
+ */
54
+ realWorldReduce = 1;
55
+
56
+ @property({ type: Number })
57
+ scale = 1;
58
+ /** Position (in unit-less, [0, 1] coordinates) in client to scale around */
59
+ @property({ type: Object })
60
+ scaleCenter = { x: 0.5, y: 0.5 };
61
+
62
+ /************** VIRTUAL-SCROLLING PROPERTIES **************/
63
+
64
+ /** in world coordinates (inches) */
65
+ @property({ type: Object })
66
+ visibleRegion = {
67
+ top: 0,
68
+ left: 0,
69
+ width: 100,
70
+ height: 100,
71
+ };
72
+
73
+ /** @type {PageModel[]} */
74
+ @property({ type: Array, hasChanged: arrChanged })
75
+ visiblePages = [];
76
+
77
+ /** @type {PageModel[]} */
78
+ @property({ type: Array })
79
+ renderedPages = [];
80
+
81
+ /** @type {Record<PageIndex, PageContainer>} position in inches */
82
+ pageContainerCache = {};
83
+
84
+ /************** WORLD-RELATED PROPERTIES **************/
85
+ /**
86
+ * The world is an imaginary giant document that contains all the pages.
87
+ * The "world"'s size is used to determine how long the scroll bar should
88
+ * be, for example.
89
+ */
90
+
91
+ /** @type {HTMLElement} */
92
+ @query('.br-mode-1up__world')
93
+ $world;
94
+
95
+ worldDimensions = { width: 100, height: 100 };
96
+
97
+ get worldStyle() {
98
+ const wToR = this.worldUnitsToRenderedPixels;
99
+ return {
100
+ width: wToR(this.worldDimensions.width) + "px",
101
+ height: wToR(this.worldDimensions.height) + "px",
102
+ };
103
+ }
104
+
105
+ /** @type {HTMLElement} */
106
+ get $container() { return this; }
107
+
108
+ /** @type {HTMLElement} */
109
+ @query('.br-mode-1up__visible-world')
110
+ $visibleWorld;
111
+
112
+ /************** DOM-RELATED PROPERTIES **************/
113
+
114
+ /** @type {HTMLDimensionsCacher} Cache things like clientWidth to reduce repaints */
115
+ htmlDimensionsCacher = new HTMLDimensionsCacher(this);
116
+
117
+ smoothZoomer = new ModeSmoothZoom(this);
118
+
119
+ scrollClassAdder = new ScrollClassAdder(this, 'BRscrolling-active');
120
+
121
+ /************** CONSTANT PROPERTIES **************/
122
+
123
+ /** Vertical space between/around the pages in inches */
124
+ SPACING_IN = 0.2;
125
+
126
+ /** How much to zoom when zoom button pressed */
127
+ ZOOM_FACTOR = 1.1;
128
+
129
+ /****************************************/
130
+ /************** PUBLIC API **************/
131
+ /****************************************/
132
+
133
+ /************** MAIN PUBLIC METHODS **************/
134
+
135
+ /**
136
+ * @param {PageIndex} index
137
+ */
138
+ jumpToIndex(index, { smooth = false } = {}) {
139
+ if (smooth) {
140
+ this.style.scrollBehavior = 'smooth';
141
+ }
142
+ this.scrollTop = this.worldUnitsToVisiblePixels(this.pageTops[index] - this.SPACING_IN / 2);
143
+ // TODO: Also h center?
144
+ if (smooth) {
145
+ setTimeout(() => this.style.scrollBehavior = '', 100);
146
+ }
147
+ }
148
+
149
+ zoomIn() {
150
+ this.scale *= this.ZOOM_FACTOR;
151
+ }
152
+
153
+ zoomOut() {
154
+ this.scale *= 1 / this.ZOOM_FACTOR;
155
+ }
156
+
157
+ /********************************************/
158
+ /************** INTERNAL STUFF **************/
159
+ /********************************************/
160
+
161
+ /************** LIFE CYCLE **************/
162
+
163
+ /**
164
+ * @param {BookModel} book
165
+ * @param {BookReader} br
166
+ */
167
+ constructor(book, br) {
168
+ super();
169
+ this.book = book;
170
+
171
+ /** @type {BookReader} */
172
+ this.br = br;
173
+ }
174
+
175
+ /** @override */
176
+ firstUpdated(changedProps) {
177
+ super.firstUpdated(changedProps);
178
+ this.htmlDimensionsCacher.updateClientSizes();
179
+ this.smoothZoomer.attach();
180
+ }
181
+
182
+ /**
183
+ * @param {PageIndex} startIndex
184
+ */
185
+ initFirstRender(startIndex) {
186
+ const page = this.book.getPage(startIndex);
187
+ this.scale = this.computeDefaultScale(page);
188
+ }
189
+
190
+ /** @override */
191
+ updated(changedProps) {
192
+ // this.X is the new value
193
+ // changedProps.get('X') is the old value
194
+ if (changedProps.has('book')) {
195
+ this.pages = genToArray(this.book.pagesIterator({ combineConsecutiveUnviewables: true }));
196
+ }
197
+ if (changedProps.has('pages')) {
198
+ this.worldDimensions = this.computeWorldDimensions();
199
+ this.pageTops = this.computePageTops(this.pages, this.SPACING_IN);
200
+ }
201
+ if (changedProps.has('visibleRegion')) {
202
+ this.visiblePages = this.computeVisiblePages();
203
+ }
204
+ if (changedProps.has('visiblePages')) {
205
+ this.throttledUpdateRenderedPages();
206
+ this.br.displayedIndices = this.visiblePages.map(p => p.index);
207
+ this.br.updateFirstIndex(this.br.displayedIndices[0]);
208
+ this.br._components.navbar.updateNavIndexThrottled();
209
+ }
210
+ if (changedProps.has('scale')) {
211
+ const oldVal = changedProps.get('scale');
212
+ // Need to set this scale to actually scale the pages
213
+ this.$visibleWorld.style.transform = `scale(${this.scale})`;
214
+ this.updateViewportOnZoom(this.scale, oldVal);
215
+ // Need to set this scale to update the world size, so the scrollbar gets the correct size
216
+ this.$world.style.transform = `scale(${this.scale})`;
217
+ }
218
+ }
219
+
220
+ /** @override */
221
+ connectedCallback() {
222
+ super.connectedCallback();
223
+ this.htmlDimensionsCacher.attachResizeListener();
224
+ this.attachScrollListeners();
225
+ this.smoothZoomer.attach();
226
+ }
227
+
228
+ /** @override */
229
+ disconnectedCallback() {
230
+ this.htmlDimensionsCacher.detachResizeListener();
231
+ this.detachScrollListeners();
232
+ this.smoothZoomer.detach();
233
+ super.disconnectedCallback();
234
+ }
235
+
236
+ /************** LIT CONFIGS **************/
237
+
238
+ /** @override */
239
+ createRenderRoot() {
240
+ // Disable shadow DOM; that would require a huge rejiggering of CSS
241
+ return this;
242
+ }
243
+
244
+ /************** COORDINATE SPACE CONVERTERS **************/
245
+ /**
246
+ * There are a few different "coordinate spaces" at play in BR:
247
+ * (1) World units: i.e. inches. Unless otherwise stated, all computations
248
+ * are done in world units.
249
+ * (2) Rendered Pixels: i.e. img.width = '300'. Note this does _not_ take
250
+ * into account zoom scaling.
251
+ * (3) Visible Pixels: Just rendered pixels, but taking into account scaling.
252
+ */
253
+
254
+ worldUnitsToRenderedPixels = (/** @type {number} */inches) => inches * this.screenDPI / this.realWorldReduce;
255
+ renderedPixelsToWorldUnits = (/** @type {number} */px) => px * this.realWorldReduce / this.screenDPI;
256
+
257
+ renderedPixelsToVisiblePixels = (/** @type {number} */px) => px * this.scale;
258
+ visiblePixelsToRenderedPixels = (/** @type {number} */px) => px / this.scale;
259
+
260
+ worldUnitsToVisiblePixels = (/** @type {number} */px) => this.renderedPixelsToVisiblePixels(this.worldUnitsToRenderedPixels(px));
261
+ visiblePixelsToWorldUnits = (/** @type {number} */px) => this.renderedPixelsToWorldUnits(this.visiblePixelsToRenderedPixels(px));
262
+
263
+ /************** RENDERING **************/
264
+
265
+ /** @override */
266
+ render() {
267
+ return html`
268
+ <div class="br-mode-1up__world" style=${styleMap(this.worldStyle)}></div>
269
+ <div class="br-mode-1up__visible-world">
270
+ ${this.renderedPages.map(p => this.renderPage(p))}
271
+ </div>`;
272
+ }
273
+
274
+ /** @param {PageModel} page */
275
+ createPageContainer = (page) => {
276
+ return this.pageContainerCache[page.index] || (
277
+ this.pageContainerCache[page.index] = (
278
+ // @ts-ignore I know it's protected, TS! But Mode1Up and BookReader are friends.
279
+ this.br._createPageContainer(page.index)
280
+ )
281
+ );
282
+ }
283
+
284
+ /** @param {PageModel} page */
285
+ renderPage = (page) => {
286
+ const wToR = this.worldUnitsToRenderedPixels;
287
+ const wToV = this.worldUnitsToVisiblePixels;
288
+ const containerWidth = this.visiblePixelsToWorldUnits(this.htmlDimensionsCacher.clientWidth);
289
+
290
+ const width = wToR(page.widthInches);
291
+ const height = wToR(page.heightInches);
292
+ const left = Math.max(this.SPACING_IN, (containerWidth - page.widthInches) / 2);
293
+ const top = this.pageTops[page.index];
294
+
295
+ const transform = `translate(${wToR(left)}px, ${wToR(top)}px)`;
296
+ const pageContainerEl = this.createPageContainer(page)
297
+ .update({
298
+ dimensions: {
299
+ width,
300
+ height,
301
+ top: 0,
302
+ left: 0,
303
+ },
304
+ reduce: page.width / wToV(page.widthInches),
305
+ }).$container[0];
306
+
307
+ pageContainerEl.style.transform = transform;
308
+ pageContainerEl.classList.toggle('BRpage-visible', this.visiblePages.includes(page));
309
+ return pageContainerEl;
310
+ }
311
+
312
+ /************** VIRTUAL SCROLLING LOGIC **************/
313
+
314
+ updateVisibleRegion = () => {
315
+ const { scrollTop, scrollLeft } = this;
316
+ // clientHeight excludes scrollbars, which is good.
317
+ const clientWidth = this.htmlDimensionsCacher.clientWidth;
318
+ const clientHeight = this.htmlDimensionsCacher.clientHeight;
319
+
320
+ // Note: scrollTop, and clientWidth all are in visible space;
321
+ // i.e. they are affects by the CSS transforms.
322
+
323
+ const vToW = this.visiblePixelsToWorldUnits;
324
+ this.visibleRegion = {
325
+ top: vToW(scrollTop),
326
+ height: vToW(clientHeight),
327
+ // TODO: These are very likely wrong
328
+ left: vToW(scrollLeft),
329
+ width: vToW(clientWidth),
330
+ };
331
+ }
332
+
333
+ /**
334
+ * @returns {PageModel[]}
335
+ */
336
+ computeRenderedPages() {
337
+ // Also render 1 page before/after
338
+ // @ts-ignore TS doesn't understand the filtering out of null values
339
+ return [
340
+ this.visiblePages[0]?.prev,
341
+ ...this.visiblePages,
342
+ this.visiblePages[this.visiblePages.length - 1]?.next,
343
+ ]
344
+ .filter(p => p)
345
+ // Never render more than 10 pages! Usually means something is wrong
346
+ .slice(0, 10);
347
+ }
348
+
349
+ throttledUpdateRenderedPages = throttle(() => {
350
+ this.renderedPages = this.computeRenderedPages();
351
+ this.requestUpdate();
352
+ }, 100, null)
353
+
354
+ /**
355
+ * @param {PageModel[]} pages
356
+ * @param {number} spacing
357
+ */
358
+ computePageTops(pages, spacing) {
359
+ /** @type {{ [pageIndex: string]: number }} */
360
+ const result = {};
361
+ let top = spacing;
362
+ for (const page of pages) {
363
+ result[page.index] = top;
364
+ top += page.heightInches + spacing;
365
+ }
366
+ return result;
367
+ }
368
+
369
+ /**
370
+ * @param {PageModel} page
371
+ * @returns {number}
372
+ */
373
+ computeDefaultScale(page) {
374
+ // Default to real size if it fits, otherwise default to full width
375
+ const containerWidthIn = this.visiblePixelsToWorldUnits(this.htmlDimensionsCacher.clientWidth);
376
+ return Math.min(1, containerWidthIn / (page.widthInches + 2 * this.SPACING_IN)) || 1;
377
+ }
378
+
379
+ computeWorldDimensions() {
380
+ return {
381
+ width: Math.max(...this.pages.map(p => p.widthInches)) + 2 * this.SPACING_IN,
382
+ height:
383
+ sum(this.pages.map(p => p.heightInches)) +
384
+ (this.pages.length + 1) * this.SPACING_IN,
385
+ };
386
+ }
387
+
388
+ computeVisiblePages() {
389
+ return this.pages.filter(page => {
390
+ const PT = this.pageTops[page.index];
391
+ const PB = PT + page.heightInches;
392
+
393
+ const VT = this.visibleRegion.top;
394
+ const VB = VT + this.visibleRegion.height;
395
+ return PT <= VB && PB >= VT;
396
+ });
397
+ }
398
+
399
+ /************** ZOOMING LOGIC **************/
400
+
401
+ /**
402
+ * @param {number} newScale
403
+ * @param {number} oldScale
404
+ */
405
+ updateViewportOnZoom(newScale, oldScale) {
406
+ const container = this;
407
+ const { scrollTop: T, scrollLeft: L } = container;
408
+ const W = this.htmlDimensionsCacher.clientWidth;
409
+ const H = this.htmlDimensionsCacher.clientHeight;
410
+
411
+ // Scale factor change
412
+ const F = newScale / oldScale;
413
+
414
+ // Where in the viewport the zoom is centered on
415
+ const XPOS = this.scaleCenter.x;
416
+ const YPOS = this.scaleCenter.y;
417
+ const oldCenter = {
418
+ x: L + XPOS * W,
419
+ y: T + YPOS * H,
420
+ };
421
+ const newCenter = {
422
+ x: F * oldCenter.x,
423
+ y: F * oldCenter.y,
424
+ };
425
+ container.scrollTop = newCenter.y - YPOS * H;
426
+ container.scrollLeft = newCenter.x - XPOS * W;
427
+ this.updateVisibleRegion();
428
+ }
429
+
430
+ /************** INPUT HANDLERS **************/
431
+
432
+ attachScrollListeners = () => {
433
+ this.addEventListener("scroll", this.updateVisibleRegion);
434
+ this.scrollClassAdder.attach();
435
+ }
436
+
437
+ detachScrollListeners = () => {
438
+ this.removeEventListener("scroll", this.updateVisibleRegion);
439
+ this.scrollClassAdder.detach();
440
+ }
441
+ }