@internetarchive/bookreader 5.0.0-93 → 5.0.0-95

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 (224) hide show
  1. package/.github/workflows/npm-publish.yml +2 -12
  2. package/BookReaderDemo/IADemoBr.js +1 -24
  3. package/BookReaderDemo/demo-internetarchive.html +1 -0
  4. package/CHANGELOG.md +19 -1
  5. package/README.md +0 -2
  6. package/package.json +8 -4
  7. package/scripts/postversion.js +3 -2
  8. package/scripts/preversion.js +3 -1
  9. package/scripts/version.js +4 -6
  10. package/src/BookNavigator/book-navigator.js +38 -12
  11. package/src/BookNavigator/downloads/downloads-provider.js +2 -2
  12. package/src/BookNavigator/search/search-provider.js +5 -5
  13. package/src/BookNavigator/search/search-results.js +1 -1
  14. package/src/BookNavigator/sharing.js +2 -2
  15. package/src/BookNavigator/viewable-files.js +2 -2
  16. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +3 -3
  17. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +2 -2
  18. package/src/BookReader.js +57 -31
  19. package/src/assets/images/hypothesis.ico +0 -0
  20. package/src/css/_TextSelection.scss +3 -1
  21. package/src/plugins/plugin.autoplay.js +3 -3
  22. package/src/plugins/plugin.chapters.js +2 -2
  23. package/src/plugins/plugin.experiments.js +294 -0
  24. package/src/plugins/plugin.iiif.js +1 -1
  25. package/src/plugins/plugin.text_selection.js +112 -1
  26. package/src/plugins/search/view.js +5 -5
  27. package/src/plugins/tts/plugin.tts.js +3 -3
  28. package/src/plugins/url/plugin.url.js +2 -2
  29. package/tests/e2e/autoplay.test.js +1 -1
  30. package/tests/e2e/base.test.js +4 -4
  31. package/tests/e2e/helpers/base.js +2 -2
  32. package/tests/e2e/models/BookReader.js +1 -1
  33. package/tests/e2e/rightToLeft.test.js +4 -4
  34. package/tests/e2e/viewmode.test.js +2 -2
  35. package/tests/jest/BookNavigator/book-navigator.test.js +0 -13
  36. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +1 -1
  37. package/tests/jest/BookNavigator/downloads/downloads.test.js +1 -1
  38. package/tests/jest/BookNavigator/search/search-provider.test.js +5 -5
  39. package/tests/jest/BookReader.test.js +10 -10
  40. package/tests/jest/plugins/plugin.autoplay.test.js +6 -6
  41. package/tests/jest/plugins/plugin.chapters.test.js +2 -2
  42. package/tests/jest/plugins/plugin.resume.test.js +13 -13
  43. package/tests/jest/plugins/plugin.text_selection.test.js +155 -24
  44. package/tests/jest/plugins/search/plugin.search.test.js +7 -7
  45. package/tests/jest/plugins/search/plugin.search.view.test.js +8 -8
  46. package/tests/jest/plugins/search/utils.js +1 -1
  47. package/tests/jest/plugins/tts/PageChunkIterator.test.js +2 -2
  48. package/tests/jest/plugins/url/UrlPlugin.test.js +1 -1
  49. package/webpack.config.js +8 -3
  50. package/BookReader/BookReader.css +0 -2250
  51. package/BookReader/BookReader.js +0 -3
  52. package/BookReader/BookReader.js.LICENSE.txt +0 -72
  53. package/BookReader/BookReader.js.map +0 -1
  54. package/BookReader/ia-bookreader-bundle.js +0 -1782
  55. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +0 -7
  56. package/BookReader/ia-bookreader-bundle.js.map +0 -1
  57. package/BookReader/icons/1up.svg +0 -1
  58. package/BookReader/icons/2up.svg +0 -1
  59. package/BookReader/icons/advance.svg +0 -3
  60. package/BookReader/icons/chevron-right.svg +0 -1
  61. package/BookReader/icons/close-circle-dark.svg +0 -1
  62. package/BookReader/icons/close-circle.svg +0 -1
  63. package/BookReader/icons/fullscreen.svg +0 -1
  64. package/BookReader/icons/fullscreen_exit.svg +0 -1
  65. package/BookReader/icons/hamburger.svg +0 -1
  66. package/BookReader/icons/left-arrow.svg +0 -1
  67. package/BookReader/icons/magnify-minus.svg +0 -1
  68. package/BookReader/icons/magnify-plus.svg +0 -1
  69. package/BookReader/icons/magnify.svg +0 -1
  70. package/BookReader/icons/pause.svg +0 -1
  71. package/BookReader/icons/play.svg +0 -1
  72. package/BookReader/icons/playback-speed.svg +0 -1
  73. package/BookReader/icons/read-aloud.svg +0 -1
  74. package/BookReader/icons/review.svg +0 -3
  75. package/BookReader/icons/thumbnails.svg +0 -1
  76. package/BookReader/icons/voice.svg +0 -1
  77. package/BookReader/icons/volume-full.svg +0 -1
  78. package/BookReader/images/BRicons.png +0 -0
  79. package/BookReader/images/BRicons.svg +0 -5
  80. package/BookReader/images/BRicons_ia.png +0 -0
  81. package/BookReader/images/back_pages.png +0 -0
  82. package/BookReader/images/book_bottom_icon.png +0 -0
  83. package/BookReader/images/book_down_icon.png +0 -0
  84. package/BookReader/images/book_left_icon.png +0 -0
  85. package/BookReader/images/book_leftmost_icon.png +0 -0
  86. package/BookReader/images/book_right_icon.png +0 -0
  87. package/BookReader/images/book_rightmost_icon.png +0 -0
  88. package/BookReader/images/book_top_icon.png +0 -0
  89. package/BookReader/images/book_up_icon.png +0 -0
  90. package/BookReader/images/books_graphic.svg +0 -1
  91. package/BookReader/images/booksplit.png +0 -0
  92. package/BookReader/images/control_pause_icon.png +0 -0
  93. package/BookReader/images/control_play_icon.png +0 -0
  94. package/BookReader/images/embed_icon.png +0 -0
  95. package/BookReader/images/icon-home-ia.png +0 -0
  96. package/BookReader/images/icon_OL-logo-xs.png +0 -0
  97. package/BookReader/images/icon_alert-xs.png +0 -0
  98. package/BookReader/images/icon_book.svg +0 -1
  99. package/BookReader/images/icon_bookmark.svg +0 -1
  100. package/BookReader/images/icon_close-pop.png +0 -0
  101. package/BookReader/images/icon_download.png +0 -0
  102. package/BookReader/images/icon_gear.svg +0 -1
  103. package/BookReader/images/icon_hamburger.svg +0 -1
  104. package/BookReader/images/icon_home.png +0 -0
  105. package/BookReader/images/icon_home.svg +0 -1
  106. package/BookReader/images/icon_home_ia.png +0 -0
  107. package/BookReader/images/icon_indicator.png +0 -0
  108. package/BookReader/images/icon_info.svg +0 -1
  109. package/BookReader/images/icon_one_page.svg +0 -1
  110. package/BookReader/images/icon_pause.svg +0 -1
  111. package/BookReader/images/icon_play.svg +0 -1
  112. package/BookReader/images/icon_playback-rate.svg +0 -1
  113. package/BookReader/images/icon_return.png +0 -0
  114. package/BookReader/images/icon_search_button.svg +0 -1
  115. package/BookReader/images/icon_share.svg +0 -1
  116. package/BookReader/images/icon_skip-ahead.svg +0 -1
  117. package/BookReader/images/icon_skip-back.svg +0 -2
  118. package/BookReader/images/icon_speaker.svg +0 -1
  119. package/BookReader/images/icon_speaker_open.svg +0 -1
  120. package/BookReader/images/icon_thumbnails.svg +0 -1
  121. package/BookReader/images/icon_toc.svg +0 -1
  122. package/BookReader/images/icon_two_pages.svg +0 -1
  123. package/BookReader/images/icon_zoomer.png +0 -0
  124. package/BookReader/images/loading.gif +0 -0
  125. package/BookReader/images/logo_icon.png +0 -0
  126. package/BookReader/images/marker_chap-off.png +0 -0
  127. package/BookReader/images/marker_chap-off.svg +0 -1
  128. package/BookReader/images/marker_chap-off_ia.png +0 -0
  129. package/BookReader/images/marker_chap-on.png +0 -0
  130. package/BookReader/images/marker_chap-on.svg +0 -1
  131. package/BookReader/images/marker_srch-on.svg +0 -1
  132. package/BookReader/images/marker_srchchap-off.png +0 -0
  133. package/BookReader/images/marker_srchchap-on.png +0 -0
  134. package/BookReader/images/nav_control-dn.png +0 -0
  135. package/BookReader/images/nav_control-dn_ia.png +0 -0
  136. package/BookReader/images/nav_control-up.png +0 -0
  137. package/BookReader/images/nav_control-up_ia.png +0 -0
  138. package/BookReader/images/nav_control.png +0 -0
  139. package/BookReader/images/one_page_mode_icon.png +0 -0
  140. package/BookReader/images/paper-badge.png +0 -0
  141. package/BookReader/images/print_icon.png +0 -0
  142. package/BookReader/images/progressbar.gif +0 -0
  143. package/BookReader/images/right_edges.png +0 -0
  144. package/BookReader/images/slider.png +0 -0
  145. package/BookReader/images/slider_ia.png +0 -0
  146. package/BookReader/images/thumbnail_mode_icon.png +0 -0
  147. package/BookReader/images/transparent.png +0 -0
  148. package/BookReader/images/two_page_mode_icon.png +0 -0
  149. package/BookReader/images/unviewable_page.png +0 -0
  150. package/BookReader/images/zoom_in_icon.png +0 -0
  151. package/BookReader/images/zoom_out_icon.png +0 -0
  152. package/BookReader/jquery-3.js +0 -2
  153. package/BookReader/jquery-3.js.LICENSE.txt +0 -24
  154. package/BookReader/plugins/plugin.archive_analytics.js +0 -2
  155. package/BookReader/plugins/plugin.archive_analytics.js.map +0 -1
  156. package/BookReader/plugins/plugin.autoplay.js +0 -2
  157. package/BookReader/plugins/plugin.autoplay.js.map +0 -1
  158. package/BookReader/plugins/plugin.chapters.js +0 -26
  159. package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +0 -1
  160. package/BookReader/plugins/plugin.chapters.js.map +0 -1
  161. package/BookReader/plugins/plugin.iframe.js +0 -2
  162. package/BookReader/plugins/plugin.iframe.js.map +0 -1
  163. package/BookReader/plugins/plugin.iiif.js +0 -2
  164. package/BookReader/plugins/plugin.iiif.js.map +0 -1
  165. package/BookReader/plugins/plugin.resume.js +0 -2
  166. package/BookReader/plugins/plugin.resume.js.map +0 -1
  167. package/BookReader/plugins/plugin.search.js +0 -3
  168. package/BookReader/plugins/plugin.search.js.LICENSE.txt +0 -1
  169. package/BookReader/plugins/plugin.search.js.map +0 -1
  170. package/BookReader/plugins/plugin.text_selection.js +0 -3
  171. package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +0 -1
  172. package/BookReader/plugins/plugin.text_selection.js.map +0 -1
  173. package/BookReader/plugins/plugin.tts.js +0 -3
  174. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +0 -29
  175. package/BookReader/plugins/plugin.tts.js.map +0 -1
  176. package/BookReader/plugins/plugin.url.js +0 -2
  177. package/BookReader/plugins/plugin.url.js.map +0 -1
  178. package/BookReader/plugins/plugin.vendor-fullscreen.js +0 -2
  179. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +0 -1
  180. package/BookReader/webcomponents-bundle.js +0 -3
  181. package/BookReader/webcomponents-bundle.js.LICENSE.txt +0 -9
  182. package/BookReader/webcomponents-bundle.js.map +0 -1
  183. package/src/BookReader/BookModel.js +0 -554
  184. package/src/BookReader/DragScrollable.js +0 -233
  185. package/src/BookReader/ImageCache.js +0 -149
  186. package/src/BookReader/Mode1Up.js +0 -108
  187. package/src/BookReader/Mode1UpLit.js +0 -388
  188. package/src/BookReader/Mode2Up.js +0 -105
  189. package/src/BookReader/Mode2UpLit.js +0 -777
  190. package/src/BookReader/ModeCoordinateSpace.js +0 -29
  191. package/src/BookReader/ModeSmoothZoom.js +0 -312
  192. package/src/BookReader/ModeThumb.js +0 -342
  193. package/src/BookReader/Navbar/Navbar.js +0 -355
  194. package/src/BookReader/PageContainer.js +0 -169
  195. package/src/BookReader/ReduceSet.js +0 -26
  196. package/src/BookReader/Toolbar/Toolbar.js +0 -362
  197. package/src/BookReader/events.js +0 -19
  198. package/src/BookReader/options.js +0 -382
  199. package/src/BookReader/utils/HTMLDimensionsCacher.js +0 -44
  200. package/src/BookReader/utils/ScrollClassAdder.js +0 -31
  201. package/src/BookReader/utils/SelectionObserver.js +0 -45
  202. package/src/BookReader/utils/classes.js +0 -36
  203. package/src/BookReader/utils.js +0 -300
  204. package/tests/jest/BookReader/BookModel.test.js +0 -372
  205. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +0 -263
  206. package/tests/jest/BookReader/ImageCache.test.js +0 -150
  207. package/tests/jest/BookReader/Mode1UpLit.test.js +0 -73
  208. package/tests/jest/BookReader/Mode2Up.test.js +0 -98
  209. package/tests/jest/BookReader/Mode2UpLit.test.js +0 -190
  210. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +0 -16
  211. package/tests/jest/BookReader/ModeSmoothZoom.test.js +0 -218
  212. package/tests/jest/BookReader/ModeThumb.test.js +0 -71
  213. package/tests/jest/BookReader/Navbar/Navbar.test.js +0 -182
  214. package/tests/jest/BookReader/PageContainer.test.js +0 -238
  215. package/tests/jest/BookReader/ReduceSet.test.js +0 -38
  216. package/tests/jest/BookReader/Toolbar/Toolbar.test.js +0 -26
  217. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +0 -59
  218. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +0 -49
  219. package/tests/jest/BookReader/utils/SelectionObserver.test.js +0 -57
  220. package/tests/jest/BookReader/utils/classes.test.js +0 -88
  221. package/tests/jest/BookReader/utils.test.js +0 -250
  222. /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
  223. /package/{.testcaferc.js → .testcaferc.cjs} +0 -0
  224. /package/{babel.config.js → babel.config.cjs} +0 -0
@@ -1,554 +0,0 @@
1
- // @ts-check
2
- import { DEFAULT_OPTIONS } from './options.js';
3
- import { clamp } from './utils.js';
4
- /** @typedef {import('./options.js').PageData} PageData */
5
- /** @typedef {import('../BookReader.js').default} BookReader */
6
-
7
- /**
8
- * Contains information about the Book/Document independent of the way it is
9
- * being rendering. Nothing here should reference e.g. the mode, zoom, etc.
10
- * It's just information about the book and its pages (usually as specified
11
- * in the BookReader data option.)
12
- */
13
- export class BookModel {
14
- /**
15
- * @param {BookReader} br
16
- */
17
- constructor(br) {
18
- this.br = br;
19
- this.reduceSet = br.reduceSet;
20
- this.ppi = br.options?.ppi ?? DEFAULT_OPTIONS.ppi;
21
- /** @type {'lr' | 'rl'} Page progression */
22
- this.pageProgression = br.options?.pageProgression ?? DEFAULT_OPTIONS.pageProgression;
23
-
24
- /** @type {{width: number, height: number}} memoize storage */
25
- this._medianPageSize = null;
26
- /** @type {[PageData[], number]} */
27
- this._getDataFlattenedCached = null;
28
- }
29
-
30
- /** Get median width/height of page in inches. Memoized for performance. */
31
- getMedianPageSizeInches() {
32
- if (this._medianPageSize) {
33
- return this._medianPageSize;
34
- }
35
-
36
- const widths = [];
37
- const heights = [];
38
- for (const page of this.pagesIterator()) {
39
- widths.push(page.widthInches);
40
- heights.push(page.heightInches);
41
- }
42
-
43
- widths.sort((a, b) => a - b);
44
- heights.sort((a, b) => a - b);
45
-
46
- this._medianPageSize = {
47
- width: widths[Math.floor(widths.length / 2)],
48
- height: heights[Math.floor(heights.length / 2)],
49
- };
50
- return this._medianPageSize;
51
- }
52
-
53
- /**
54
- * Returns the page width for the given index, or first or last page if out of range
55
- * @deprecated see getPageWidth
56
- * @param {PageIndex} index
57
- */
58
- _getPageWidth(index) {
59
- // Synthesize a page width for pages not actually present in book.
60
- // May or may not be the best approach.
61
- // If index is out of range we return the width of first or last page
62
- index = clamp(index, 0, this.getNumLeafs() - 1);
63
- return this.getPageWidth(index);
64
- }
65
-
66
- /**
67
- * Returns the page height for the given index, or first or last page if out of range
68
- * @deprecated see getPageHeight
69
- * @param {PageIndex} index
70
- */
71
- _getPageHeight(index) {
72
- const clampedIndex = clamp(index, 0, this.getNumLeafs() - 1);
73
- return this.getPageHeight(clampedIndex);
74
- }
75
-
76
- /**
77
- * Returns the *highest* index the given page number, or undefined
78
- * @param {PageNumString} pageNum
79
- * @return {PageIndex|undefined}
80
- */
81
- getPageIndex(pageNum) {
82
- const pageIndices = this.getPageIndices(pageNum);
83
- return pageIndices.length ? pageIndices[pageIndices.length - 1] : undefined;
84
- }
85
-
86
- /**
87
- * Returns an array (possibly empty) of the indices with the given page number
88
- * @param {PageNumString} pageNum
89
- * @return {PageIndex[]}
90
- */
91
- getPageIndices(pageNum) {
92
- const indices = [];
93
-
94
- // Check for special "nXX" page number
95
- if (pageNum.slice(0,1) == 'n') {
96
- try {
97
- const pageIntStr = pageNum.slice(1, pageNum.length);
98
- const pageIndex = parseInt(pageIntStr);
99
- indices.push(pageIndex);
100
- return indices;
101
- } catch (err) {
102
- // Do nothing... will run through page names and see if one matches
103
- }
104
- }
105
-
106
- for (let i = 0; i < this.getNumLeafs(); i++) {
107
- if (this.getPageNum(i) == pageNum) {
108
- indices.push(i);
109
- }
110
- }
111
-
112
- return indices;
113
- }
114
-
115
- /**
116
- * Returns the name of the page as it should be displayed in the user interface
117
- * @param {PageIndex} index
118
- * @return {string}
119
- */
120
- getPageName(index) {
121
- return 'Page ' + this.getPageNum(index);
122
- }
123
-
124
- /**
125
- * @return {number} the total number of leafs (like an array length)
126
- */
127
- getNumLeafs() {
128
- // For deprecated interface support, if numLeafs is set, use that.
129
- if (this.br.numLeafs !== undefined)
130
- return this.br.numLeafs;
131
- return this._getDataFlattened().length;
132
- }
133
-
134
- /**
135
- * @param {PageIndex} index
136
- * @return {Number|undefined}
137
- */
138
- getPageWidth(index) {
139
- return this.getPageProp(index, 'width');
140
- }
141
-
142
- /**
143
- * @param {PageIndex} index
144
- * @return {Number|undefined}
145
- */
146
- getPageHeight(index) {
147
- return this.getPageProp(index, 'height');
148
- }
149
-
150
- /**
151
- * @param {PageIndex} index
152
- * @param {number} reduce - not used in default implementation
153
- * @param {number} rotate - not used in default implementation
154
- * @return {string|undefined}
155
- */
156
- // eslint-disable-next-line no-unused-vars
157
- getPageURI(index, reduce, rotate) {
158
- if (!this.getPageProp(index, 'viewable', true)) {
159
- const uri = this.br.options.unviewablePageURI;
160
- if (uri.startsWith('.')) {
161
- // It's a relative path, so make it relative to the images path
162
- return this.br.options.imagesBaseURL + uri;
163
- } else {
164
- return uri;
165
- }
166
- } else {
167
- return this.getPageProp(index, 'uri');
168
- }
169
- }
170
-
171
- /**
172
- * @param {PageIndex} index
173
- * @return {'L' | 'R'}
174
- */
175
- getPageSide(index) {
176
- return this.getPageProp(index, 'pageSide') || (index % 2 === 0 ? 'R' : 'L');
177
- }
178
-
179
- /**
180
- * @param {PageIndex} index
181
- * @return {PageNumString}
182
- */
183
- getPageNum(index) {
184
- const pageNum = this.getPageProp(index, 'pageNum');
185
- return pageNum === undefined ? `n${index}` : pageNum;
186
- }
187
-
188
- /**
189
- * Generalized property accessor.
190
- * @param {PageIndex} index
191
- * @param {keyof PageData} propName
192
- * @param {*} [fallbackValue] return if undefined
193
- * @return {*|undefined}
194
- */
195
- getPageProp(index, propName, fallbackValue = undefined) {
196
- return this._getDataProp(index, propName, fallbackValue);
197
- }
198
-
199
- /**
200
- * This function returns the left and right indices for the user-visible
201
- * spread that contains the given index.
202
- * @note Can return indices out of range of what's in the book.
203
- * @param {PageIndex} pindex
204
- * @return {[PageIndex, PageIndex]} eg [0, 1]
205
- */
206
- getSpreadIndices(pindex) {
207
- if (this.pageProgression == 'rl') {
208
- return this.getPageSide(pindex) == 'R' ? [pindex + 1, pindex] : [pindex, pindex - 1];
209
- } else {
210
- return this.getPageSide(pindex) == 'L' ? [pindex, pindex + 1] : [pindex - 1, pindex];
211
- }
212
- }
213
-
214
- /**
215
- * Single images in the Internet Archive scandata.xml metadata are (somewhat incorrectly)
216
- * given a "leaf" number. Some of these images from the scanning process should not
217
- * be displayed in the BookReader (for example colour calibration cards). Since some
218
- * of the scanned images will not be displayed in the BookReader (those marked with
219
- * addToAccessFormats false in the scandata.xml) leaf numbers and BookReader page
220
- * indexes are generally not the same. This function returns the BookReader page
221
- * index given a scanned leaf number.
222
- *
223
- * This function is used, for example, to map between search results (that use the
224
- * leaf numbers) and the displayed pages in the BookReader.
225
- * @param {LeafNum} leafNum
226
- * @return {PageIndex}
227
- */
228
- leafNumToIndex(leafNum) {
229
- const index = this._getDataFlattened()
230
- .findIndex(d => d.leafNum == leafNum);
231
- // If no match is found, fall back to the leafNum provide (leafNum == index)
232
- return index > -1 ? index : leafNum;
233
- }
234
-
235
- /**
236
- * Parses the pageString format
237
- * @param {PageString} pageString
238
- * @return {PageIndex|undefined}
239
- */
240
- parsePageString(pageString) {
241
- let pageIndex;
242
- // Check for special "leaf"
243
- const leafMatch = /^leaf(\d+)/.exec(pageString);
244
- if (leafMatch) {
245
- pageIndex = this.leafNumToIndex(parseInt(leafMatch[1], 10));
246
- if (pageIndex === null) {
247
- pageIndex = undefined; // to match return type of getPageIndex
248
- }
249
- } else {
250
- pageIndex = this.getPageIndex(pageString);
251
- }
252
- return pageIndex;
253
- }
254
-
255
- /**
256
- * @param {number} index use negatives to get page relative to end
257
- * @param loop whether to loop (i.e. -1 == last page)
258
- */
259
- getPage(index, loop = true) {
260
- const numLeafs = this.getNumLeafs();
261
- if (!loop && (index < 0 || index >= numLeafs)) {
262
- return undefined;
263
- }
264
- if (index < 0 && index >= -numLeafs) {
265
- index += numLeafs;
266
- }
267
- index = index % numLeafs;
268
- return new PageModel(this, index);
269
- }
270
-
271
- /**
272
- * @param {object} [arg0]
273
- * @param {number} [arg0.start] inclusive
274
- * @param {number} [arg0.end] exclusive
275
- * @param {boolean} [arg0.combineConsecutiveUnviewables] Yield only first unviewable
276
- * of a chunk of unviewable pages instead of each page
277
- * @return {Generator<PageModel>}
278
- */
279
- * pagesIterator({ start = 0, end = Infinity, combineConsecutiveUnviewables = false } = {}) {
280
- start = Math.max(0, start);
281
- end = Math.min(end, this.getNumLeafs());
282
-
283
- for (let i = start; i < end; i++) {
284
- const page = this.getPage(i);
285
- if (combineConsecutiveUnviewables && page.isConsecutiveUnviewable) continue;
286
-
287
- yield page;
288
- }
289
- }
290
-
291
- /**
292
- * Flatten the nested structure (make 1d array), and also add pageSide prop
293
- * @return {PageData[]}
294
- */
295
- _getDataFlattened() {
296
- if (this._getDataFlattenedCached && this._getDataFlattenedCached[1] === this.br.data.length)
297
- return this._getDataFlattenedCached[0];
298
-
299
- let prevPageSide = null;
300
- /** @type {number|null} */
301
- let unviewablesChunkStart = null;
302
- let index = 0;
303
- // @ts-ignore TS doesn't know about flatMap for some reason
304
- const flattened = this.br.data.flatMap(spread => {
305
- return spread.map(page => {
306
- if (!page.pageSide) {
307
- if (prevPageSide === null) {
308
- page.pageSide = spread.length === 2 ? 'L' : 'R';
309
- } else {
310
- page.pageSide = prevPageSide === 'L' ? 'R' : 'L';
311
- }
312
- }
313
- prevPageSide = page.pageSide;
314
-
315
- if (page.viewable === false) {
316
- if (unviewablesChunkStart === null) {
317
- page.unviewablesStart = unviewablesChunkStart = index;
318
- } else {
319
- page.unviewablesStart = unviewablesChunkStart;
320
- }
321
- } else {
322
- unviewablesChunkStart = null;
323
- }
324
-
325
- index++;
326
- return page;
327
- });
328
- });
329
-
330
- // length is used as a cache breaker
331
- this._getDataFlattenedCached = [flattened, this.br.data.length];
332
- return flattened;
333
- }
334
-
335
- /**
336
- * Helper. Return a prop for a given index. Returns `fallbackValue` if index is invalid or
337
- * property not on page.
338
- * @param {PageIndex} index
339
- * @param {keyof PageData} prop
340
- * @param {*} fallbackValue return if property not on the record
341
- * @return {*}
342
- */
343
- _getDataProp(index, prop, fallbackValue = undefined) {
344
- const dataf = this._getDataFlattened();
345
- const invalidIndex = isNaN(index) || index < 0 || index >= dataf.length;
346
- if (invalidIndex || 'undefined' == typeof(dataf[index][prop]))
347
- return fallbackValue;
348
- return dataf[index][prop];
349
- }
350
- }
351
-
352
- /**
353
- * A controlled schema for page data.
354
- */
355
- export class PageModel {
356
- /**
357
- * @param {BookModel} book
358
- * @param {PageIndex} index
359
- */
360
- constructor(book, index) {
361
- // Values less than 10 cause the UI to not work correctly
362
- const pagePPI = book._getDataProp(index, 'ppi', book.ppi);
363
- this.ppi = Math.max(pagePPI < 10 ? book.ppi : pagePPI, 10);
364
- this.book = book;
365
- this.index = index;
366
- this.width = book.getPageWidth(index);
367
- this.widthInches = this.width / this.ppi;
368
- this.height = book.getPageHeight(index);
369
- this.heightInches = this.height / this.ppi;
370
- this.pageSide = book.getPageSide(index);
371
- this.leafNum = book._getDataProp(index, 'leafNum', this.index);
372
-
373
- /** @type {boolean} */
374
- this.isViewable = book._getDataProp(index, 'viewable', true);
375
- /** @type {PageIndex} The first in the series of unviewable pages this is in. */
376
- this.unviewablesStart = book._getDataProp(index, 'unviewablesStart') || null;
377
- /**
378
- * Consecutive unviewable pages are pages in an unviewable "chunk" which are not the first
379
- * of that chunk.
380
- */
381
- this.isConsecutiveUnviewable = !this.isViewable && this.unviewablesStart != this.index;
382
-
383
- this._rawData = this.book._getDataFlattened()[this.index];
384
- }
385
-
386
- /**
387
- * Updates the page to no longer be unviewable. Assumes the
388
- * Page's URI is already set/correct.
389
- */
390
- makeViewable(newViewableState = true) {
391
- if (this.isViewable == newViewableState) return;
392
-
393
- if (newViewableState) {
394
- this._rawData.viewable = true;
395
- delete this._rawData.unviewablesStart;
396
- // Update any subsequent page to now point to the right "start"
397
- for (const page of this.book.pagesIterator({ start: this.index + 1 })) {
398
- if (page.isViewable) break;
399
- page._rawData.unviewablesStart = this.index + 1;
400
- }
401
- } else {
402
- this._rawData.viewable = false;
403
- this._rawData.unviewablesStart = (this.prev && !this.prev.isViewable) ? this.prev.unviewablesStart : this.index;
404
- // Update any subsequent page to now point to the right "start"
405
- for (const page of this.book.pagesIterator({ start: this.index + 1 })) {
406
- if (!page.isViewable) break;
407
- page._rawData.unviewablesStart = this._rawData.unviewablesStart;
408
- }
409
- }
410
- }
411
-
412
- get prev() {
413
- return this.findPrev();
414
- }
415
-
416
- get next() {
417
- return this.findNext();
418
- }
419
-
420
- /** @type {PageModel | null} */
421
- get left() {
422
- return this.book.pageProgression === 'lr' ? this.prev : this.next;
423
- }
424
-
425
- /** @type {PageModel | null} */
426
- get right() {
427
- return this.book.pageProgression === 'lr' ? this.next : this.prev;
428
- }
429
-
430
- /**
431
- * @type {{left: PageModel | null, right: PageModel | null}}
432
- */
433
- get spread() {
434
- return {
435
- left: this.pageSide === 'L' ? this : this.left,
436
- right: this.pageSide === 'R' ? this : this.right,
437
- };
438
- }
439
-
440
- /**
441
- * @param {number} pages
442
- */
443
- goLeft(pages) {
444
- const newIndex = this.book.pageProgression === 'lr' ? this.index - pages : this.index + pages;
445
- return this.book.getPage(newIndex);
446
- }
447
-
448
- /**
449
- * @param {number} pages
450
- */
451
- goRight(pages) {
452
- const newIndex = this.book.pageProgression === 'lr' ? this.index + pages : this.index - pages;
453
- return this.book.getPage(newIndex);
454
- }
455
-
456
- /**
457
- * @param {number} reduce
458
- * @param {number} rotate
459
- */
460
- getURI(reduce, rotate) {
461
- return this.book.getPageURI(this.index, reduce, rotate);
462
- }
463
-
464
- /**
465
- * Returns the srcset with correct URIs or void string if out of range
466
- * @param {number} reduce
467
- * @param {number} [rotate]
468
- */
469
- getURISrcSet(reduce, rotate = 0) {
470
- const { reduceSet } = this.book;
471
- const initialReduce = reduceSet.floor(reduce);
472
- // We don't need to repeat the initial reduce in the srcset
473
- const topReduce = reduceSet.decr(initialReduce);
474
- const reduces = [];
475
- for (let r = topReduce; r >= 1; r = reduceSet.decr(r)) {
476
- reduces.push(r);
477
- }
478
- return reduces
479
- .map(r => `${this.getURI(r, rotate)} ${initialReduce / r}x`)
480
- .join(', ');
481
- }
482
-
483
- /**
484
- * @param {object} [arg0]
485
- * @param {boolean} [arg0.combineConsecutiveUnviewables] Whether to only yield the first page
486
- * of a series of unviewable pages instead of each page
487
- * @return {PageModel|undefined}
488
- */
489
- findNext({ combineConsecutiveUnviewables = false } = {}) {
490
- return this.book
491
- .pagesIterator({ start: this.index + 1, combineConsecutiveUnviewables })
492
- .next().value;
493
- }
494
-
495
- /**
496
- * @param {object} [arg0]
497
- * @param {boolean} [arg0.combineConsecutiveUnviewables] Whether to only yield the first page
498
- * of a series of unviewable pages instead of each page
499
- * @return {PageModel|undefined}
500
- */
501
- findPrev({ combineConsecutiveUnviewables = false } = {}) {
502
- if (this.index == 0) return undefined;
503
-
504
- if (combineConsecutiveUnviewables) {
505
- if (this.isConsecutiveUnviewable) {
506
- return this.book.getPage(this.unviewablesStart);
507
- } else {
508
- // Recursively goes backward through the book
509
- // TODO make a reverse iterator to make it look identical to findNext
510
- const prev = new PageModel(this.book, this.index - 1);
511
- return prev.isViewable ? prev : prev.findPrev({ combineConsecutiveUnviewables });
512
- }
513
- } else {
514
- return new PageModel(this.book, this.index - 1);
515
- }
516
- }
517
-
518
- /**
519
- * @param {object} [arg0]
520
- * @param {boolean} [arg0.combineConsecutiveUnviewables] Whether to only yield the first page
521
- * of a series of unviewable pages instead of each page
522
- * @return {PageModel|undefined}
523
- */
524
- findLeft({ combineConsecutiveUnviewables = false } = {}) {
525
- return this.book.pageProgression === 'lr' ? this.findPrev({ combineConsecutiveUnviewables }) : this.findNext({ combineConsecutiveUnviewables });
526
- }
527
-
528
- /**
529
- * @param {object} [arg0]
530
- * @param {boolean} [arg0.combineConsecutiveUnviewables] Whether to only yield the first page
531
- * of a series of unviewable pages instead of each page
532
- * @return {PageModel|undefined}
533
- */
534
- findRight({ combineConsecutiveUnviewables = false } = {}) {
535
- return this.book.pageProgression === 'lr' ? this.findNext({ combineConsecutiveUnviewables }) : this.findPrev({ combineConsecutiveUnviewables });
536
- }
537
- }
538
-
539
- // There are a few main ways we can reference a specific page in a book:
540
- /**
541
- * @typedef {string} PageNumString
542
- * Possible values: /^n?\d+$/. Example: 'n7', '18'
543
- * Not necessarily unique
544
- */
545
- /**
546
- * @typedef {number} LeafNum
547
- * No clue if 0 or 1 indexed or consecutive; generally from IA book info.
548
- */
549
- /**
550
- * @typedef {string} PageString
551
- * Possible values: /^(leaf)?\d+$/ Example: 'leaf7', '18'
552
- * If leaf-prefixed, then the number is a LeafNum. Otherwise it's a PageNumString
553
- */
554
- /** @typedef {number} PageIndex 0-based index of all the pages */