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