@internetarchive/bookreader 5.0.0-88-alpha.11 → 5.0.0-89
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.
- package/BookReader/BookReader.css +17 -3
- package/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +87 -108
- package/BookReader/ia-bookreader-bundle.js.map +1 -1
- package/BookReader/plugins/plugin.archive_analytics.js +1 -1
- package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
- package/BookReader/plugins/plugin.autoplay.js +1 -1
- package/BookReader/plugins/plugin.autoplay.js.map +1 -1
- package/BookReader/plugins/plugin.iiif.js +1 -1
- package/BookReader/plugins/plugin.iiif.js.map +1 -1
- package/BookReader/plugins/plugin.resume.js +1 -1
- package/BookReader/plugins/plugin.resume.js.map +1 -1
- package/BookReader/plugins/plugin.search.js +1 -1
- package/BookReader/plugins/plugin.search.js.map +1 -1
- package/BookReader/plugins/plugin.text_selection.js +1 -1
- package/BookReader/plugins/plugin.text_selection.js.map +1 -1
- package/BookReader/plugins/plugin.tts.js +1 -1
- package/BookReader/plugins/plugin.tts.js.map +1 -1
- package/BookReader/plugins/plugin.url.js +1 -1
- package/BookReader/plugins/plugin.url.js.map +1 -1
- package/CHANGELOG.md +14 -0
- package/babel.config.js +12 -30
- package/codecov.yml +1 -1
- package/jsconfig.json +1 -3
- package/package.json +14 -16
- package/src/BookNavigator/search/search-results.js +1 -1
- package/src/BookReader/ImageCache.js +48 -15
- package/src/BookReader/Mode1UpLit.js +56 -86
- package/src/BookReader/Mode2UpLit.js +5 -5
- package/src/BookReader/Navbar/Navbar.js +53 -11
- package/src/BookReader/PageContainer.js +41 -22
- package/src/BookReader/options.js +27 -3
- package/src/BookReader/utils.js +10 -0
- package/src/BookReader.js +120 -21
- package/src/BookReaderPlugin.js +44 -0
- package/src/css/_BRnav.scss +0 -3
- package/src/css/_BRpages.scss +21 -2
- package/src/css/_controls.scss +4 -0
- package/src/plugins/plugin.archive_analytics.js +84 -78
- package/src/plugins/plugin.autoplay.js +98 -102
- package/src/plugins/plugin.chapters.js +17 -22
- package/src/plugins/plugin.iiif.js +16 -30
- package/src/plugins/plugin.resume.js +54 -51
- package/src/plugins/plugin.text_selection.js +68 -76
- package/src/plugins/tts/AbstractTTSEngine.js +2 -4
- package/src/plugins/tts/PageChunk.js +5 -9
- package/src/plugins/tts/PageChunkIterator.js +3 -5
- package/src/plugins/tts/plugin.tts.js +309 -329
- package/src/plugins/url/plugin.url.js +1 -1
- package/src/util/strings.js +1 -0
- package/tests/e2e/autoplay.test.js +8 -5
- package/tests/e2e/helpers/base.js +2 -2
- package/tests/e2e/helpers/mockSearch.js +6 -9
- package/tests/jest/BookReader/Navbar/Navbar.test.js +16 -3
- package/tests/jest/BookReader/PageContainer.test.js +96 -55
- package/tests/jest/BookReader/utils.test.js +21 -0
- package/tests/jest/BookReader.test.js +13 -12
- package/tests/jest/plugins/plugin.archive_analytics.test.js +8 -11
- package/tests/jest/plugins/plugin.autoplay.test.js +9 -22
- package/tests/jest/plugins/plugin.resume.test.js +19 -32
- package/tests/jest/plugins/plugin.text_selection.test.js +23 -24
- package/dist/esm/BookNavigator/assets/bookmark-colors.js +0 -4
- package/dist/esm/BookNavigator/assets/button-base.js +0 -4
- package/dist/esm/BookNavigator/assets/ia-logo.js +0 -4
- package/dist/esm/BookNavigator/assets/icon_checkmark.js +0 -8
- package/dist/esm/BookNavigator/assets/icon_close.js +0 -4
- package/dist/esm/BookNavigator/book-navigator.js +0 -612
- package/dist/esm/BookNavigator/bookmarks/bookmark-button.js +0 -35
- package/dist/esm/BookNavigator/bookmarks/bookmark-edit.js +0 -78
- package/dist/esm/BookNavigator/bookmarks/bookmarks-list.js +0 -160
- package/dist/esm/BookNavigator/bookmarks/bookmarks-loginCTA.js +0 -24
- package/dist/esm/BookNavigator/bookmarks/bookmarks-provider.js +0 -55
- package/dist/esm/BookNavigator/bookmarks/ia-bookmarks.js +0 -521
- package/dist/esm/BookNavigator/delete-modal-actions.js +0 -29
- package/dist/esm/BookNavigator/downloads/downloads-provider.js +0 -84
- package/dist/esm/BookNavigator/downloads/downloads.js +0 -69
- package/dist/esm/BookNavigator/search/search-provider.js +0 -238
- package/dist/esm/BookNavigator/search/search-results.js +0 -161
- package/dist/esm/BookNavigator/sharing.js +0 -26
- package/dist/esm/BookNavigator/viewable-files.js +0 -94
- package/dist/esm/BookNavigator/visual-adjustments/visual-adjustments-provider.js +0 -83
- package/dist/esm/BookNavigator/visual-adjustments/visual-adjustments.js +0 -131
- package/dist/esm/BookReader/BookModel.js +0 -575
- package/dist/esm/BookReader/DragScrollable.js +0 -224
- package/dist/esm/BookReader/ImageCache.js +0 -122
- package/dist/esm/BookReader/Mode1Up.js +0 -114
- package/dist/esm/BookReader/Mode1UpLit.js +0 -579
- package/dist/esm/BookReader/Mode2Up.js +0 -106
- package/dist/esm/BookReader/Mode2UpLit.js +0 -1020
- package/dist/esm/BookReader/ModeCoordinateSpace.js +0 -28
- package/dist/esm/BookReader/ModeSmoothZoom.js +0 -318
- package/dist/esm/BookReader/ModeThumb.js +0 -366
- package/dist/esm/BookReader/Navbar/Navbar.js +0 -253
- package/dist/esm/BookReader/PageContainer.js +0 -165
- package/dist/esm/BookReader/ReduceSet.js +0 -27
- package/dist/esm/BookReader/Toolbar/Toolbar.js +0 -242
- package/dist/esm/BookReader/events.js +0 -20
- package/dist/esm/BookReader/options.js +0 -331
- package/dist/esm/BookReader/utils/HTMLDimensionsCacher.js +0 -48
- package/dist/esm/BookReader/utils/ScrollClassAdder.js +0 -31
- package/dist/esm/BookReader/utils/SelectionObserver.js +0 -42
- package/dist/esm/BookReader/utils/classes.js +0 -37
- package/dist/esm/BookReader/utils.js +0 -315
- package/dist/esm/BookReader.js +0 -1828
- package/dist/esm/assets/icons/1up.svg +0 -12
- package/dist/esm/assets/icons/2up.svg +0 -15
- package/dist/esm/assets/icons/advance.svg +0 -26
- package/dist/esm/assets/icons/chevron-right.svg +0 -1
- package/dist/esm/assets/icons/close-circle-dark.svg +0 -1
- package/dist/esm/assets/icons/close-circle.svg +0 -1
- package/dist/esm/assets/icons/fullscreen.svg +0 -17
- package/dist/esm/assets/icons/fullscreen_exit.svg +0 -17
- package/dist/esm/assets/icons/hamburger.svg +0 -15
- package/dist/esm/assets/icons/left-arrow.svg +0 -12
- package/dist/esm/assets/icons/magnify-minus.svg +0 -12
- package/dist/esm/assets/icons/magnify-plus.svg +0 -13
- package/dist/esm/assets/icons/magnify.svg +0 -15
- package/dist/esm/assets/icons/pause.svg +0 -23
- package/dist/esm/assets/icons/play.svg +0 -22
- package/dist/esm/assets/icons/playback-speed.svg +0 -34
- package/dist/esm/assets/icons/read-aloud.svg +0 -22
- package/dist/esm/assets/icons/review.svg +0 -22
- package/dist/esm/assets/icons/thumbnails.svg +0 -17
- package/dist/esm/assets/icons/voice.svg +0 -1
- package/dist/esm/assets/icons/volume-full.svg +0 -22
- package/dist/esm/assets/images/BRicons.png +0 -0
- package/dist/esm/assets/images/BRicons.svg +0 -94
- package/dist/esm/assets/images/BRicons_ia.png +0 -0
- package/dist/esm/assets/images/back_pages.png +0 -0
- package/dist/esm/assets/images/book_bottom_icon.png +0 -0
- package/dist/esm/assets/images/book_down_icon.png +0 -0
- package/dist/esm/assets/images/book_left_icon.png +0 -0
- package/dist/esm/assets/images/book_leftmost_icon.png +0 -0
- package/dist/esm/assets/images/book_right_icon.png +0 -0
- package/dist/esm/assets/images/book_rightmost_icon.png +0 -0
- package/dist/esm/assets/images/book_top_icon.png +0 -0
- package/dist/esm/assets/images/book_up_icon.png +0 -0
- package/dist/esm/assets/images/books_graphic.svg +0 -177
- package/dist/esm/assets/images/booksplit.png +0 -0
- package/dist/esm/assets/images/control_pause_icon.png +0 -0
- package/dist/esm/assets/images/control_play_icon.png +0 -0
- package/dist/esm/assets/images/embed_icon.png +0 -0
- package/dist/esm/assets/images/icon-home-ia.png +0 -0
- package/dist/esm/assets/images/icon_OL-logo-xs.png +0 -0
- package/dist/esm/assets/images/icon_alert-xs.png +0 -0
- package/dist/esm/assets/images/icon_book.svg +0 -12
- package/dist/esm/assets/images/icon_bookmark.svg +0 -12
- package/dist/esm/assets/images/icon_close-pop.png +0 -0
- package/dist/esm/assets/images/icon_download.png +0 -0
- package/dist/esm/assets/images/icon_gear.svg +0 -14
- package/dist/esm/assets/images/icon_hamburger.svg +0 -20
- package/dist/esm/assets/images/icon_home.png +0 -0
- package/dist/esm/assets/images/icon_home.svg +0 -21
- package/dist/esm/assets/images/icon_home_ia.png +0 -0
- package/dist/esm/assets/images/icon_indicator.png +0 -0
- package/dist/esm/assets/images/icon_info.svg +0 -11
- package/dist/esm/assets/images/icon_one_page.svg +0 -8
- package/dist/esm/assets/images/icon_pause.svg +0 -1
- package/dist/esm/assets/images/icon_play.svg +0 -1
- package/dist/esm/assets/images/icon_playback-rate.svg +0 -15
- package/dist/esm/assets/images/icon_return.png +0 -0
- package/dist/esm/assets/images/icon_search_button.svg +0 -8
- package/dist/esm/assets/images/icon_share.svg +0 -9
- package/dist/esm/assets/images/icon_skip-ahead.svg +0 -6
- package/dist/esm/assets/images/icon_skip-back.svg +0 -13
- package/dist/esm/assets/images/icon_speaker.svg +0 -18
- package/dist/esm/assets/images/icon_speaker_open.svg +0 -10
- package/dist/esm/assets/images/icon_thumbnails.svg +0 -12
- package/dist/esm/assets/images/icon_toc.svg +0 -5
- package/dist/esm/assets/images/icon_two_pages.svg +0 -9
- package/dist/esm/assets/images/icon_zoomer.png +0 -0
- package/dist/esm/assets/images/loading.gif +0 -0
- package/dist/esm/assets/images/logo_icon.png +0 -0
- package/dist/esm/assets/images/marker_chap-off.png +0 -0
- package/dist/esm/assets/images/marker_chap-off.svg +0 -11
- package/dist/esm/assets/images/marker_chap-off_ia.png +0 -0
- package/dist/esm/assets/images/marker_chap-on.png +0 -0
- package/dist/esm/assets/images/marker_chap-on.svg +0 -11
- package/dist/esm/assets/images/marker_srch-on.svg +0 -11
- package/dist/esm/assets/images/marker_srchchap-off.png +0 -0
- package/dist/esm/assets/images/marker_srchchap-on.png +0 -0
- package/dist/esm/assets/images/nav_control-dn.png +0 -0
- package/dist/esm/assets/images/nav_control-dn_ia.png +0 -0
- package/dist/esm/assets/images/nav_control-up.png +0 -0
- package/dist/esm/assets/images/nav_control-up_ia.png +0 -0
- package/dist/esm/assets/images/nav_control.png +0 -0
- package/dist/esm/assets/images/one_page_mode_icon.png +0 -0
- package/dist/esm/assets/images/paper-badge.png +0 -0
- package/dist/esm/assets/images/print_icon.png +0 -0
- package/dist/esm/assets/images/progressbar.gif +0 -0
- package/dist/esm/assets/images/right_edges.png +0 -0
- package/dist/esm/assets/images/slider.png +0 -0
- package/dist/esm/assets/images/slider_ia.png +0 -0
- package/dist/esm/assets/images/thumbnail_mode_icon.png +0 -0
- package/dist/esm/assets/images/transparent.png +0 -0
- package/dist/esm/assets/images/two_page_mode_icon.png +0 -0
- package/dist/esm/assets/images/unviewable_page.png +0 -0
- package/dist/esm/assets/images/zoom_in_icon.png +0 -0
- package/dist/esm/assets/images/zoom_out_icon.png +0 -0
- package/dist/esm/css/BookReader.scss +0 -85
- package/dist/esm/css/_BRBookmarks.scss +0 -29
- package/dist/esm/css/_BRComponent.scss +0 -13
- package/dist/esm/css/_BRfloat.scss +0 -197
- package/dist/esm/css/_BRicon.scss +0 -54
- package/dist/esm/css/_BRmain.scss +0 -262
- package/dist/esm/css/_BRnav.scss +0 -354
- package/dist/esm/css/_BRpages.scss +0 -213
- package/dist/esm/css/_BRsearch.scss +0 -268
- package/dist/esm/css/_BRtoolbar.scss +0 -84
- package/dist/esm/css/_BRvendor.scss +0 -5
- package/dist/esm/css/_TextSelection.scss +0 -108
- package/dist/esm/css/_colorbox.scss +0 -52
- package/dist/esm/css/_controls.scss +0 -257
- package/dist/esm/css/_icons.scss +0 -121
- package/dist/esm/ia-bookreader/ia-bookreader.js +0 -141
- package/dist/esm/jquery-wrapper.js +0 -3
- package/dist/esm/plugins/plugin.archive_analytics.js +0 -72
- package/dist/esm/plugins/plugin.autoplay.js +0 -119
- package/dist/esm/plugins/plugin.chapters.js +0 -288
- package/dist/esm/plugins/plugin.iframe.js +0 -44
- package/dist/esm/plugins/plugin.iiif.js +0 -146
- package/dist/esm/plugins/plugin.resume.js +0 -66
- package/dist/esm/plugins/plugin.text_selection.js +0 -621
- package/dist/esm/plugins/plugin.vendor-fullscreen.js +0 -227
- package/dist/esm/plugins/search/plugin.search.js +0 -499
- package/dist/esm/plugins/search/utils.js +0 -42
- package/dist/esm/plugins/search/view.js +0 -360
- package/dist/esm/plugins/tts/AbstractTTSEngine.js +0 -282
- package/dist/esm/plugins/tts/FestivalTTSEngine.js +0 -192
- package/dist/esm/plugins/tts/PageChunk.js +0 -105
- package/dist/esm/plugins/tts/PageChunkIterator.js +0 -155
- package/dist/esm/plugins/tts/WebTTSEngine.js +0 -364
- package/dist/esm/plugins/tts/plugin.tts.js +0 -315
- package/dist/esm/plugins/tts/tooltip_dict.js +0 -14
- package/dist/esm/plugins/tts/utils.js +0 -79
- package/dist/esm/plugins/url/UrlPlugin.js +0 -197
- package/dist/esm/plugins/url/plugin.url.js +0 -212
- package/dist/esm/util/browserSniffing.js +0 -56
- package/dist/esm/util/debouncer.js +0 -25
- package/dist/esm/util/docCookies.js +0 -75
- package/dist/esm/util/strings.js +0 -34
- package/index.js +0 -2
@@ -2,6 +2,8 @@
|
|
2
2
|
/** @typedef {import('./BookModel.js').PageModel} PageModel */
|
3
3
|
/** @typedef {import('./ImageCache.js').ImageCache} ImageCache */
|
4
4
|
|
5
|
+
import { sleep } from './utils.js';
|
6
|
+
|
5
7
|
|
6
8
|
export class PageContainer {
|
7
9
|
/**
|
@@ -9,12 +11,10 @@ export class PageContainer {
|
|
9
11
|
* @param {object} opts
|
10
12
|
* @param {boolean} opts.isProtected Whether we're in a protected book
|
11
13
|
* @param {ImageCache} opts.imageCache
|
12
|
-
* @param {string} opts.loadingImage
|
13
14
|
*/
|
14
|
-
constructor(page, {isProtected, imageCache
|
15
|
+
constructor(page, {isProtected, imageCache}) {
|
15
16
|
this.page = page;
|
16
17
|
this.imageCache = imageCache;
|
17
|
-
this.loadingImage = loadingImage;
|
18
18
|
this.$container = $('<div />', {
|
19
19
|
'class': `BRpagecontainer ${page ? `pagediv${page.index}` : 'BRemptypage'}`,
|
20
20
|
css: { position: 'absolute' },
|
@@ -43,39 +43,58 @@ export class PageContainer {
|
|
43
43
|
return;
|
44
44
|
}
|
45
45
|
|
46
|
-
const
|
47
|
-
const
|
46
|
+
const finalReduce = this.imageCache.getFinalReduce(this.page.index, reduce);
|
47
|
+
const newImageURI = this.page.getURI(finalReduce, 0);
|
48
48
|
|
49
|
-
//
|
50
|
-
const
|
49
|
+
// Note: These must be computed _before_ we call .image()
|
50
|
+
const alreadyLoaded = this.imageCache.imageLoaded(this.page.index, finalReduce);
|
51
|
+
const nextBestLoadedReduce = this.imageCache.getBestLoadedReduce(this.page.index, reduce);
|
51
52
|
|
52
53
|
// Avoid removing/re-adding the image if it's already there
|
53
54
|
// This can be called quite a bit, so we need to be fast
|
54
|
-
if (this.$img?.
|
55
|
+
if (this.$img?.data('src') == newImageURI) {
|
55
56
|
return this;
|
56
57
|
}
|
57
58
|
|
58
|
-
this.$img
|
59
|
-
this.$img =
|
59
|
+
let $oldImg = this.$img;
|
60
|
+
this.$img = this.imageCache.image(this.page.index, finalReduce);
|
61
|
+
if ($oldImg) {
|
62
|
+
this.$img.insertAfter($oldImg);
|
63
|
+
} else {
|
64
|
+
this.$img.prependTo(this.$container);
|
65
|
+
}
|
60
66
|
|
61
|
-
const backgroundLayers = [];
|
62
67
|
if (!alreadyLoaded) {
|
63
68
|
this.$container.addClass('BRpageloading');
|
64
|
-
backgroundLayers.push(`url("${this.loadingImage}") center/20px no-repeat`);
|
65
|
-
}
|
66
|
-
if (nextBestLoadedReduce) {
|
67
|
-
backgroundLayers.push(`url("${this.page.getURI(nextBestLoadedReduce, 0)}") center/100% 100% no-repeat`);
|
68
69
|
}
|
69
70
|
|
70
|
-
if (!alreadyLoaded) {
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
71
|
+
if (!alreadyLoaded && nextBestLoadedReduce) {
|
72
|
+
// If we have a slightly lower quality image loaded, use that as the background
|
73
|
+
// while the higher res one loads
|
74
|
+
const nextBestUri = this.page.getURI(nextBestLoadedReduce, 0);
|
75
|
+
if ($oldImg) {
|
76
|
+
if ($oldImg.data('src') == nextBestUri) {
|
77
|
+
// Do nothing! It's already showing the right thing
|
78
|
+
} else {
|
79
|
+
// We have a different src, need to update the src
|
80
|
+
this.imageCache.image(this.page.index, nextBestLoadedReduce, $oldImg[0]);
|
81
|
+
}
|
82
|
+
} else {
|
83
|
+
// We don't have an old <img>, so we need to create a new one
|
84
|
+
$oldImg = this.imageCache.image(this.page.index, nextBestLoadedReduce);
|
85
|
+
$oldImg.prependTo(this.$container);
|
86
|
+
}
|
77
87
|
}
|
78
88
|
|
89
|
+
this.$img
|
90
|
+
.one('load', async (ev) => {
|
91
|
+
this.$container.removeClass('BRpageloading');
|
92
|
+
// `load` can fire a little early, so wait a spell before removing the old image
|
93
|
+
// to avoid flicker
|
94
|
+
await sleep(100);
|
95
|
+
$oldImg?.remove();
|
96
|
+
});
|
97
|
+
|
79
98
|
return this;
|
80
99
|
}
|
81
100
|
}
|
@@ -1,3 +1,4 @@
|
|
1
|
+
// @ts-check
|
1
2
|
/** @typedef {import('./BookModel.js').PageNumString} PageNumString */
|
2
3
|
/** @typedef {import('./BookModel.js').LeafNum} LeafNum */
|
3
4
|
|
@@ -140,8 +141,18 @@ export const DEFAULT_OPTIONS = {
|
|
140
141
|
* but going forward we'll keep them here.
|
141
142
|
**/
|
142
143
|
plugins: {
|
143
|
-
/** @type {import('../plugins/plugin.
|
144
|
+
/** @type {import('../plugins/plugin.archive_analytics.js').ArchiveAnalyticsPlugin['options']}*/
|
145
|
+
archiveAnalytics: null,
|
146
|
+
/** @type {import('../plugins/plugin.autoplay.js').AutoplayPlugin['options']}*/
|
147
|
+
autoplay: null,
|
148
|
+
/** @type {import('../plugins/plugin.iiif.js').IiifPlugin['options']} */
|
149
|
+
iiif: null,
|
150
|
+
/** @type {import('../plugins/plugin.resume.js').ResumePlugin['options']} */
|
151
|
+
resume: null,
|
152
|
+
/** @type {import('../plugins/plugin.text_selection.js').TextSelectionPlugin['options']} */
|
144
153
|
textSelection: null,
|
154
|
+
/** @type {import('../plugins/tts/plugin.tts.js').TtsPlugin['options']} */
|
155
|
+
tts: null,
|
145
156
|
},
|
146
157
|
|
147
158
|
/**
|
@@ -183,16 +194,29 @@ export const DEFAULT_OPTIONS = {
|
|
183
194
|
/** @type {import('../plugins/plugin.chapters.js').TocEntry[]} */
|
184
195
|
table_of_contents: null,
|
185
196
|
|
186
|
-
/**
|
197
|
+
/**
|
198
|
+
* Advanced methods for page rendering.
|
199
|
+
* All option functions have their `this` object set to the BookReader instance.
|
200
|
+
**/
|
201
|
+
|
187
202
|
/** @type {() => number} */
|
188
203
|
getNumLeafs: null,
|
189
204
|
/** @type {(index: number) => number} */
|
190
205
|
getPageWidth: null,
|
191
206
|
/** @type {(index: number) => number} */
|
192
207
|
getPageHeight: null,
|
193
|
-
/** @type {(index: number, reduce: number, rotate: number) =>
|
208
|
+
/** @type {(index: number, reduce: number, rotate: number) => string} */
|
194
209
|
getPageURI: null,
|
195
210
|
|
211
|
+
/**
|
212
|
+
* @type {(img: HTMLImageElement, uri: string) => Promise<void>}
|
213
|
+
* Render the page URI into the image element. Perform any necessary preloading,
|
214
|
+
* authentication, etc.
|
215
|
+
*/
|
216
|
+
renderPageURI(img, uri) {
|
217
|
+
img.src = uri;
|
218
|
+
},
|
219
|
+
|
196
220
|
/**
|
197
221
|
* @type {(index: number) => 'L' | 'R'}
|
198
222
|
* Return which side, left or right, that a given page should be displayed on
|
package/src/BookReader/utils.js
CHANGED
@@ -288,3 +288,13 @@ export function promisifyEvent(target, eventType) {
|
|
288
288
|
export function escapeRegExp(string) {
|
289
289
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
|
290
290
|
}
|
291
|
+
|
292
|
+
/**
|
293
|
+
* @param {number | 'fast' | 'slow' | string} speed
|
294
|
+
* Parsing of the jquery animation speed; see https://api.jquery.com/animate/
|
295
|
+
*/
|
296
|
+
export function parseAnimationSpeed(speed) {
|
297
|
+
if (speed === 'slow') return 600;
|
298
|
+
if (speed === 'fast') return 200;
|
299
|
+
return parseInt(speed, 10);
|
300
|
+
}
|
package/src/BookReader.js
CHANGED
@@ -23,9 +23,8 @@ This file is part of BookReader.
|
|
23
23
|
import 'jquery-ui/ui/widget.js';
|
24
24
|
import 'jquery-ui/ui/widgets/mouse.js';
|
25
25
|
import 'jquery-ui-touch-punch';
|
26
|
-
import jQuery from 'jquery';
|
27
26
|
|
28
|
-
|
27
|
+
import PACKAGE_JSON from '../package.json';
|
29
28
|
import * as utils from './BookReader/utils.js';
|
30
29
|
import { exposeOverrideable } from './BookReader/utils/classes.js';
|
31
30
|
import { Navbar } from './BookReader/Navbar/Navbar.js';
|
@@ -54,7 +53,7 @@ export default function BookReader(overrides = {}) {
|
|
54
53
|
this.setup(options);
|
55
54
|
}
|
56
55
|
|
57
|
-
BookReader.version =
|
56
|
+
BookReader.version = PACKAGE_JSON.version;
|
58
57
|
|
59
58
|
// Mode constants
|
60
59
|
/** 1 page view */
|
@@ -63,6 +62,33 @@ BookReader.constMode1up = 1;
|
|
63
62
|
BookReader.constMode2up = 2;
|
64
63
|
/** thumbnails view */
|
65
64
|
BookReader.constModeThumb = 3;
|
65
|
+
|
66
|
+
// Although this can actualy have any BookReaderPlugin subclass as value, we
|
67
|
+
// hardcode the known plugins here for type checking
|
68
|
+
BookReader.PLUGINS = {
|
69
|
+
/** @type {typeof import('./plugins/plugin.archive_analytics.js').ArchiveAnalyticsPlugin | null}*/
|
70
|
+
archiveAnalytics: null,
|
71
|
+
/** @type {typeof import('./plugins/plugin.autoplay.js').AutoplayPlugin | null}*/
|
72
|
+
autoplay: null,
|
73
|
+
/** @type {typeof import('./plugins/plugin.resume.js').ResumePlugin | null}*/
|
74
|
+
resume: null,
|
75
|
+
/** @type {typeof import('./plugins/plugin.text_selection.js').TextSelectionPlugin | null}*/
|
76
|
+
textSelection: null,
|
77
|
+
/** @type {typeof import('./plugins/tts/plugin.tts.js').TtsPlugin | null}*/
|
78
|
+
tts: null,
|
79
|
+
};
|
80
|
+
|
81
|
+
/**
|
82
|
+
* @param {string} pluginName
|
83
|
+
* @param {typeof import('./BookReaderPlugin.js').BookReaderPlugin} plugin
|
84
|
+
*/
|
85
|
+
BookReader.registerPlugin = function(pluginName, plugin) {
|
86
|
+
if (BookReader.PLUGINS[pluginName]) {
|
87
|
+
console.warn(`Plugin ${pluginName} already registered. Overwriting.`);
|
88
|
+
}
|
89
|
+
BookReader.PLUGINS[pluginName] = plugin;
|
90
|
+
};
|
91
|
+
|
66
92
|
/** image cache */
|
67
93
|
BookReader.imageCache = null;
|
68
94
|
|
@@ -90,6 +116,38 @@ BookReader.prototype.setup = function(options) {
|
|
90
116
|
// Store the options used to setup bookreader
|
91
117
|
this.options = options;
|
92
118
|
|
119
|
+
// Construct the usual plugins first to get type hints
|
120
|
+
this._plugins = {
|
121
|
+
archiveAnalytics: BookReader.PLUGINS.archiveAnalytics ? new BookReader.PLUGINS.archiveAnalytics(this) : null,
|
122
|
+
autoplay: BookReader.PLUGINS.autoplay ? new BookReader.PLUGINS.autoplay(this) : null,
|
123
|
+
resume: BookReader.PLUGINS.resume ? new BookReader.PLUGINS.resume(this) : null,
|
124
|
+
textSelection: BookReader.PLUGINS.textSelection ? new BookReader.PLUGINS.textSelection(this) : null,
|
125
|
+
tts: BookReader.PLUGINS.tts ? new BookReader.PLUGINS.tts(this) : null,
|
126
|
+
};
|
127
|
+
|
128
|
+
// Delete anything that's null
|
129
|
+
for (const [pluginName, plugin] of Object.entries(this._plugins)) {
|
130
|
+
if (!plugin) delete this._plugins[pluginName];
|
131
|
+
}
|
132
|
+
|
133
|
+
// Now construct the rest of the plugins
|
134
|
+
for (const [pluginName, PluginClass] of Object.entries(BookReader.PLUGINS)) {
|
135
|
+
if (this._plugins[pluginName] || !PluginClass) continue;
|
136
|
+
this._plugins[pluginName] = new PluginClass(this);
|
137
|
+
}
|
138
|
+
|
139
|
+
// And call setup on them
|
140
|
+
for (const [pluginName, plugin] of Object.entries(this._plugins)) {
|
141
|
+
try {
|
142
|
+
plugin.setup(this.options.plugins?.[pluginName] ?? {});
|
143
|
+
// Write the options back; this way the plugin is the source of truth,
|
144
|
+
// and BR just contains a reference to it.
|
145
|
+
this.options.plugins[pluginName] = plugin.options;
|
146
|
+
} catch (e) {
|
147
|
+
console.error(`Error setting up plugin ${pluginName}`, e);
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
93
151
|
/** @type {number} @deprecated some past iterations set this */
|
94
152
|
this.numLeafs = undefined;
|
95
153
|
|
@@ -148,11 +206,7 @@ BookReader.prototype.setup = function(options) {
|
|
148
206
|
this.displayedIndices = [];
|
149
207
|
|
150
208
|
this.animating = false;
|
151
|
-
this.flipSpeed =
|
152
|
-
'fast': 200,
|
153
|
-
'slow': 600,
|
154
|
-
}[options.flipSpeed] || 400;
|
155
|
-
this.flipDelay = options.flipDelay;
|
209
|
+
this.flipSpeed = utils.parseAnimationSpeed(options.flipSpeed) || 400;
|
156
210
|
|
157
211
|
/**
|
158
212
|
* Represents the first displayed index
|
@@ -242,6 +296,7 @@ BookReader.prototype.setup = function(options) {
|
|
242
296
|
this.imageCache = new ImageCache(this.book, {
|
243
297
|
useSrcSet: this.options.useSrcSet,
|
244
298
|
reduceSet: this.reduceSet,
|
299
|
+
renderPageURI: options.renderPageURI.bind(this),
|
245
300
|
});
|
246
301
|
|
247
302
|
/**
|
@@ -350,9 +405,9 @@ BookReader.prototype.initParams = function() {
|
|
350
405
|
}
|
351
406
|
|
352
407
|
// Check for Resume plugin
|
353
|
-
if (this.options.
|
408
|
+
if (this._plugins.resume?.options.enabled) {
|
354
409
|
// Check cookies
|
355
|
-
const val = this.getResumeValue();
|
410
|
+
const val = this._plugins.resume.getResumeValue();
|
356
411
|
if (val !== null) {
|
357
412
|
// If page index different from default
|
358
413
|
if (params.index !== val) {
|
@@ -539,7 +594,12 @@ BookReader.prototype.init = function() {
|
|
539
594
|
this.initToolbar(this.mode, this.ui); // Build inside of toolbar div
|
540
595
|
}
|
541
596
|
if (this.options.showNavbar) { // default navigation
|
542
|
-
this.initNavbar();
|
597
|
+
const $navBar = this.initNavbar();
|
598
|
+
|
599
|
+
// extend navbar with plugins
|
600
|
+
for (const plugin of Object.values(this._plugins)) {
|
601
|
+
plugin.extendNavBar($navBar);
|
602
|
+
}
|
543
603
|
}
|
544
604
|
|
545
605
|
// Switch navbar controls on mobile/desktop
|
@@ -586,6 +646,16 @@ BookReader.prototype.init = function() {
|
|
586
646
|
this.enterFullscreen(true);
|
587
647
|
}
|
588
648
|
|
649
|
+
// Init plugins
|
650
|
+
for (const [pluginName, plugin] of Object.entries(this._plugins)) {
|
651
|
+
try {
|
652
|
+
plugin.init();
|
653
|
+
}
|
654
|
+
catch (e) {
|
655
|
+
console.error(`Error initializing plugin ${pluginName}`, e);
|
656
|
+
}
|
657
|
+
}
|
658
|
+
|
589
659
|
this.init.initComplete = true;
|
590
660
|
this.trigger(BookReader.eventNames.PostInit);
|
591
661
|
|
@@ -776,11 +846,17 @@ BookReader.prototype.drawLeafs = function() {
|
|
776
846
|
* @param {PageIndex} index
|
777
847
|
*/
|
778
848
|
BookReader.prototype._createPageContainer = function(index) {
|
779
|
-
|
849
|
+
const pageContainer = new PageContainer(this.book.getPage(index, false), {
|
780
850
|
isProtected: this.protected,
|
781
851
|
imageCache: this.imageCache,
|
782
|
-
loadingImage: this.imagesBaseURL + 'loading.gif',
|
783
852
|
});
|
853
|
+
|
854
|
+
// Call plugin handlers
|
855
|
+
for (const plugin of Object.values(this._plugins)) {
|
856
|
+
plugin._configurePageContainer(pageContainer);
|
857
|
+
}
|
858
|
+
|
859
|
+
return pageContainer;
|
784
860
|
};
|
785
861
|
|
786
862
|
BookReader.prototype.bindGestures = function(jElement) {
|
@@ -829,7 +905,7 @@ BookReader.prototype.zoom = function(direction) {
|
|
829
905
|
} else {
|
830
906
|
this.activeMode.zoom('out');
|
831
907
|
}
|
832
|
-
this.
|
908
|
+
this._plugins.textSelection?.stopPageFlip(this.refs.$brContainer);
|
833
909
|
return;
|
834
910
|
};
|
835
911
|
|
@@ -1060,7 +1136,7 @@ BookReader.prototype.switchMode = function(
|
|
1060
1136
|
const eventName = mode + 'PageViewSelected';
|
1061
1137
|
this.trigger(BookReader.eventNames[eventName]);
|
1062
1138
|
|
1063
|
-
this.
|
1139
|
+
this._plugins.textSelection?.stopPageFlip(this.refs.$brContainer);
|
1064
1140
|
};
|
1065
1141
|
|
1066
1142
|
BookReader.prototype.updateBrClasses = function() {
|
@@ -1132,7 +1208,7 @@ BookReader.prototype.enterFullscreen = async function(bindKeyboardControls = tru
|
|
1132
1208
|
}
|
1133
1209
|
this.jumpToIndex(currentIndex);
|
1134
1210
|
|
1135
|
-
this.
|
1211
|
+
this._plugins.textSelection?.stopPageFlip(this.refs.$brContainer);
|
1136
1212
|
// Add "?view=theater"
|
1137
1213
|
this.trigger(BookReader.eventNames.fragmentChange);
|
1138
1214
|
// trigger event here, so that animations,
|
@@ -1178,7 +1254,7 @@ BookReader.prototype.exitFullScreen = async function () {
|
|
1178
1254
|
await this.activeMode.mode1UpLit.updateComplete;
|
1179
1255
|
}
|
1180
1256
|
|
1181
|
-
this.
|
1257
|
+
this._plugins.textSelection?.stopPageFlip(this.refs.$brContainer);
|
1182
1258
|
// Remove "?view=theater"
|
1183
1259
|
this.trigger(BookReader.eventNames.fragmentChange);
|
1184
1260
|
this.refs.$br.removeClass('BRfullscreenAnimation');
|
@@ -1277,10 +1353,19 @@ BookReader.prototype.leftmost = function() {
|
|
1277
1353
|
}
|
1278
1354
|
};
|
1279
1355
|
|
1280
|
-
|
1356
|
+
/**
|
1357
|
+
* @param {object} options
|
1358
|
+
* @param {boolean} [options.triggerStop = true]
|
1359
|
+
* @param {number | 'fast' | 'slow'} [options.flipSpeed]
|
1360
|
+
*/
|
1361
|
+
BookReader.prototype.next = function({
|
1362
|
+
triggerStop = true,
|
1363
|
+
flipSpeed = null,
|
1364
|
+
} = {}) {
|
1281
1365
|
if (this.constMode2up == this.mode) {
|
1282
1366
|
if (triggerStop) this.trigger(BookReader.eventNames.stop);
|
1283
|
-
|
1367
|
+
flipSpeed = utils.parseAnimationSpeed(flipSpeed) || this.flipSpeed;
|
1368
|
+
this._modes.mode2Up.mode2UpLit.flipAnimation('next', {flipSpeed});
|
1284
1369
|
} else {
|
1285
1370
|
if (this.firstIndex < this.book.getNumLeafs() - 1) {
|
1286
1371
|
this.jumpToIndex(this.firstIndex + 1);
|
@@ -1288,13 +1373,22 @@ BookReader.prototype.next = function({triggerStop = true} = {}) {
|
|
1288
1373
|
}
|
1289
1374
|
};
|
1290
1375
|
|
1291
|
-
|
1376
|
+
/**
|
1377
|
+
* @param {object} options
|
1378
|
+
* @param {boolean} [options.triggerStop = true]
|
1379
|
+
* @param {number | 'fast' | 'slow'} [options.flipSpeed]
|
1380
|
+
*/
|
1381
|
+
BookReader.prototype.prev = function({
|
1382
|
+
triggerStop = true,
|
1383
|
+
flipSpeed = null,
|
1384
|
+
} = {}) {
|
1292
1385
|
const isOnFrontPage = this.firstIndex < 1;
|
1293
1386
|
if (isOnFrontPage) return;
|
1294
1387
|
|
1295
1388
|
if (this.constMode2up == this.mode) {
|
1296
1389
|
if (triggerStop) this.trigger(BookReader.eventNames.stop);
|
1297
|
-
|
1390
|
+
flipSpeed = utils.parseAnimationSpeed(flipSpeed) || this.flipSpeed;
|
1391
|
+
this._modes.mode2Up.mode2UpLit.flipAnimation('prev', {flipSpeed});
|
1298
1392
|
} else {
|
1299
1393
|
if (this.firstIndex >= 1) {
|
1300
1394
|
this.jumpToIndex(this.firstIndex - 1);
|
@@ -1479,6 +1573,11 @@ BookReader.prototype.bindNavigationHandlers = function() {
|
|
1479
1573
|
self.$('.BRnavCntl').animate({opacity:.75},250);
|
1480
1574
|
}
|
1481
1575
|
});
|
1576
|
+
|
1577
|
+
// Call _bindNavigationHandlers on the plugins
|
1578
|
+
for (const plugin of Object.values(this._plugins)) {
|
1579
|
+
plugin._bindNavigationHandlers();
|
1580
|
+
}
|
1482
1581
|
};
|
1483
1582
|
|
1484
1583
|
/**************************/
|
@@ -0,0 +1,44 @@
|
|
1
|
+
// @ts-check
|
2
|
+
/** @typedef {import("./BookReader.js").default} BookReader */
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @template TOptions
|
6
|
+
*/
|
7
|
+
export class BookReaderPlugin {
|
8
|
+
/**
|
9
|
+
* @param {BookReader} br
|
10
|
+
*/
|
11
|
+
constructor(br) {
|
12
|
+
/** @type {BookReader} */
|
13
|
+
this.br = br;
|
14
|
+
/** @type {TOptions} */
|
15
|
+
this.options;
|
16
|
+
}
|
17
|
+
|
18
|
+
/**
|
19
|
+
* @abstract
|
20
|
+
* @param {TOptions} options
|
21
|
+
**/
|
22
|
+
setup(options) {
|
23
|
+
this.options = Object.assign({}, this.options, options);
|
24
|
+
}
|
25
|
+
|
26
|
+
/** @abstract */
|
27
|
+
init() {}
|
28
|
+
|
29
|
+
/**
|
30
|
+
* @abstract
|
31
|
+
* @protected
|
32
|
+
* @param {import ("./BookReader/PageContainer.js").PageContainer} pageContainer
|
33
|
+
*/
|
34
|
+
_configurePageContainer(pageContainer) {
|
35
|
+
}
|
36
|
+
|
37
|
+
/** @abstract @protected */
|
38
|
+
_bindNavigationHandlers() {}
|
39
|
+
|
40
|
+
/**
|
41
|
+
* @param {JQuery<HTMLElement>} $navBar
|
42
|
+
*/
|
43
|
+
extendNavBar($navBar) {}
|
44
|
+
}
|
package/src/css/_BRnav.scss
CHANGED
package/src/css/_BRpages.scss
CHANGED
@@ -60,9 +60,28 @@
|
|
60
60
|
left: 0;
|
61
61
|
z-index: 1;
|
62
62
|
}
|
63
|
-
&.BRpageloading
|
63
|
+
&.BRpageloading {
|
64
64
|
// Don't show the alt text while loading
|
65
|
-
|
65
|
+
img {
|
66
|
+
color: transparent;
|
67
|
+
}
|
68
|
+
|
69
|
+
// src can be set async, so hide the image if it's not set
|
70
|
+
img:not([src]) {
|
71
|
+
display: none;
|
72
|
+
}
|
73
|
+
|
74
|
+
&::after {
|
75
|
+
display: block;
|
76
|
+
content: "";
|
77
|
+
width: 20px;
|
78
|
+
height: 20px;
|
79
|
+
position: absolute;
|
80
|
+
left: 50%;
|
81
|
+
top: 50%;
|
82
|
+
transform: translate(-50%, -50%);
|
83
|
+
background: url("images/loading.gif") center/20px no-repeat;
|
84
|
+
}
|
66
85
|
}
|
67
86
|
&.BRemptypage {
|
68
87
|
background: transparent;
|