@internetarchive/bookreader 5.0.0-38 → 5.0.0-39
Sign up to get free protection for your applications and to get access to all the features.
- package/BookReader/BookReader.css +8 -0
- package/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +99 -75
- package/BookReader/ia-bookreader-bundle.js.map +1 -1
- package/BookReader/icons/magnify-minus.svg +1 -1
- package/BookReader/icons/magnify-plus.svg +1 -1
- package/BookReader/plugins/plugin.autoplay.js +1 -1
- package/BookReader/plugins/plugin.autoplay.js.map +1 -1
- package/BookReader/plugins/plugin.chapters.js +1 -1
- package/BookReader/plugins/plugin.chapters.js.map +1 -1
- package/BookReader/plugins/plugin.mobile_nav.js +1 -1
- package/BookReader/plugins/plugin.mobile_nav.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 +5 -0
- package/README.md +13 -0
- package/package.json +14 -14
- package/renovate.json +1 -1
- package/src/BookReader/Mode1UpLit.js +7 -1
- package/src/BookReader/Mode2Up.js +11 -0
- package/src/BookReader/ModeSmoothZoom.js +2 -0
- package/src/BookReader/PageContainer.js +10 -4
- package/src/BookReader/utils/ScrollClassAdder.js +31 -0
- package/src/assets/icons/magnify-minus.svg +3 -7
- package/src/assets/icons/magnify-plus.svg +3 -7
- package/src/css/_TextSelection.scss +13 -0
- package/tests/jest/BookReader/PageContainer.test.js +5 -4
- package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
- package/.husky/_/husky.sh +0 -30
- package/stat/BookNavigator/BookModel.js +0 -14
- package/stat/BookNavigator/BookNavigator.js +0 -524
- package/stat/BookNavigator/assets/bookmark-colors.js +0 -15
- package/stat/BookNavigator/assets/button-base.js +0 -61
- package/stat/BookNavigator/assets/ia-logo.js +0 -17
- package/stat/BookNavigator/assets/icon_checkmark.js +0 -6
- package/stat/BookNavigator/assets/icon_close.js +0 -3
- package/stat/BookNavigator/assets/icon_sort_asc.js +0 -5
- package/stat/BookNavigator/assets/icon_sort_desc.js +0 -5
- package/stat/BookNavigator/assets/icon_sort_neutral.js +0 -5
- package/stat/BookNavigator/assets/icon_volumes.js +0 -11
- package/stat/BookNavigator/bookmarks/bookmark-button.js +0 -64
- package/stat/BookNavigator/bookmarks/bookmark-edit.js +0 -215
- package/stat/BookNavigator/bookmarks/bookmarks-list.js +0 -285
- package/stat/BookNavigator/bookmarks/bookmarks-loginCTA.js +0 -28
- package/stat/BookNavigator/bookmarks/bookmarks-provider.js +0 -56
- package/stat/BookNavigator/bookmarks/ia-bookmarks.js +0 -523
- package/stat/BookNavigator/br-fullscreen-mgr.js +0 -82
- package/stat/BookNavigator/delete-modal-actions.js +0 -49
- package/stat/BookNavigator/downloads/downloads-provider.js +0 -72
- package/stat/BookNavigator/downloads/downloads.js +0 -139
- package/stat/BookNavigator/provider-config.js +0 -0
- package/stat/BookNavigator/search/a-search-result.js +0 -55
- package/stat/BookNavigator/search/search-provider.js +0 -180
- package/stat/BookNavigator/search/search-results.js +0 -360
- package/stat/BookNavigator/sharing.js +0 -31
- package/stat/BookNavigator/visual-adjustments/visual-adjustments-provider.js +0 -94
- package/stat/BookNavigator/visual-adjustments/visual-adjustments.js +0 -280
- package/stat/BookNavigator/volumes/volumes-provider.js +0 -83
- package/stat/BookNavigator/volumes/volumes.js +0 -178
- package/stat/BookReader/BookModel.js +0 -518
- package/stat/BookReader/DebugConsole.js +0 -54
- package/stat/BookReader/DragScrollable.js +0 -233
- package/stat/BookReader/ImageCache.js +0 -116
- package/stat/BookReader/Mode1Up.js +0 -102
- package/stat/BookReader/Mode1UpLit.js +0 -434
- package/stat/BookReader/Mode2Up.js +0 -1372
- package/stat/BookReader/ModeSmoothZoom.js +0 -177
- package/stat/BookReader/ModeThumb.js +0 -344
- package/stat/BookReader/Navbar/Navbar.js +0 -310
- package/stat/BookReader/PageContainer.js +0 -120
- package/stat/BookReader/ReduceSet.js +0 -26
- package/stat/BookReader/Toolbar/Toolbar.js +0 -384
- package/stat/BookReader/events.js +0 -20
- package/stat/BookReader/options.js +0 -324
- package/stat/BookReader/utils/HTMLDimensionsCacher.js +0 -44
- package/stat/BookReader/utils/classes.js +0 -36
- package/stat/BookReader/utils.js +0 -240
- package/stat/BookReader.js +0 -2550
- package/stat/BookReaderComponent/BookReaderComponent.js +0 -117
- package/stat/assets/icons/1up.svg +0 -12
- package/stat/assets/icons/2up.svg +0 -15
- package/stat/assets/icons/advance.svg +0 -26
- package/stat/assets/icons/chevron-right.svg +0 -1
- package/stat/assets/icons/close-circle-dark.svg +0 -1
- package/stat/assets/icons/close-circle.svg +0 -1
- package/stat/assets/icons/fullscreen.svg +0 -17
- package/stat/assets/icons/fullscreen_exit.svg +0 -17
- package/stat/assets/icons/hamburger.svg +0 -15
- package/stat/assets/icons/left-arrow.svg +0 -12
- package/stat/assets/icons/magnify-minus.svg +0 -16
- package/stat/assets/icons/magnify-plus.svg +0 -17
- package/stat/assets/icons/magnify.svg +0 -15
- package/stat/assets/icons/pause.svg +0 -23
- package/stat/assets/icons/play.svg +0 -22
- package/stat/assets/icons/playback-speed.svg +0 -34
- package/stat/assets/icons/read-aloud.svg +0 -22
- package/stat/assets/icons/review.svg +0 -22
- package/stat/assets/icons/thumbnails.svg +0 -17
- package/stat/assets/icons/voice.svg +0 -1
- package/stat/assets/icons/volume-full.svg +0 -22
- package/stat/assets/images/BRicons.png +0 -0
- package/stat/assets/images/BRicons.svg +0 -94
- 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 +0 -177
- 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 +0 -12
- package/stat/assets/images/icon_bookmark.svg +0 -12
- 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 +0 -14
- package/stat/assets/images/icon_hamburger.svg +0 -20
- package/stat/assets/images/icon_home.png +0 -0
- package/stat/assets/images/icon_home.svg +0 -21
- 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 +0 -11
- package/stat/assets/images/icon_one_page.svg +0 -8
- package/stat/assets/images/icon_pause.svg +0 -1
- package/stat/assets/images/icon_play.svg +0 -1
- package/stat/assets/images/icon_playback-rate.svg +0 -15
- package/stat/assets/images/icon_return.png +0 -0
- package/stat/assets/images/icon_search_button.svg +0 -8
- package/stat/assets/images/icon_share.svg +0 -9
- package/stat/assets/images/icon_skip-ahead.svg +0 -6
- package/stat/assets/images/icon_skip-back.svg +0 -13
- package/stat/assets/images/icon_speaker.svg +0 -18
- package/stat/assets/images/icon_speaker_open.svg +0 -10
- package/stat/assets/images/icon_thumbnails.svg +0 -12
- package/stat/assets/images/icon_toc.svg +0 -5
- package/stat/assets/images/icon_two_pages.svg +0 -9
- 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 +0 -11
- 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 +0 -11
- package/stat/assets/images/marker_srch-on.svg +0 -11
- 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 +0 -89
- package/stat/css/_BRBookmarks.scss +0 -29
- package/stat/css/_BRComponent.scss +0 -13
- package/stat/css/_BRfloat.scss +0 -197
- package/stat/css/_BRicon.scss +0 -48
- package/stat/css/_BRmain.scss +0 -251
- package/stat/css/_BRnav.scss +0 -359
- package/stat/css/_BRpages.scss +0 -139
- package/stat/css/_BRsearch.scss +0 -226
- package/stat/css/_BRtoolbar.scss +0 -84
- package/stat/css/_BRvendor.scss +0 -5
- package/stat/css/_MobileNav.scss +0 -194
- package/stat/css/_TextSelection.scss +0 -32
- package/stat/css/_colorbox.scss +0 -52
- package/stat/css/_controls.scss +0 -253
- package/stat/css/_icons.scss +0 -121
- package/stat/jquery-wrapper.js +0 -4
- package/stat/plugins/plugin.archive_analytics.js +0 -86
- package/stat/plugins/plugin.autoplay.js +0 -129
- package/stat/plugins/plugin.chapters.js +0 -248
- package/stat/plugins/plugin.iframe.js +0 -48
- package/stat/plugins/plugin.mobile_nav.js +0 -288
- package/stat/plugins/plugin.resume.js +0 -68
- package/stat/plugins/plugin.text_selection.js +0 -291
- package/stat/plugins/plugin.url.js +0 -198
- package/stat/plugins/plugin.vendor-fullscreen.js +0 -247
- package/stat/plugins/search/plugin.search.js +0 -439
- package/stat/plugins/search/view.js +0 -439
- package/stat/plugins/tts/AbstractTTSEngine.js +0 -249
- package/stat/plugins/tts/FestivalTTSEngine.js +0 -169
- package/stat/plugins/tts/PageChunk.js +0 -107
- package/stat/plugins/tts/PageChunkIterator.js +0 -163
- package/stat/plugins/tts/WebTTSEngine.js +0 -357
- package/stat/plugins/tts/plugin.tts.js +0 -357
- package/stat/plugins/tts/tooltip_dict.js +0 -15
- package/stat/plugins/tts/utils.js +0 -91
- package/stat/util/browserSniffing.js +0 -30
- package/stat/util/debouncer.js +0 -26
- package/stat/util/docCookies.js +0 -67
- package/stat/util/strings.js +0 -34
@@ -1,169 +0,0 @@
|
|
1
|
-
import AbstractTTSEngine from './AbstractTTSEngine.js';
|
2
|
-
import { sleep } from './utils.js';
|
3
|
-
/* global soundManager */
|
4
|
-
import 'soundmanager2';
|
5
|
-
import 'jquery.browser';
|
6
|
-
|
7
|
-
/** @typedef {import("./AbstractTTSEngine.js").TTSEngineOptions} TTSEngineOptions */
|
8
|
-
/** @typedef {import("./AbstractTTSEngine.js").AbstractTTSSound} AbstractTTSSound */
|
9
|
-
|
10
|
-
/**
|
11
|
-
* @extends AbstractTTSEngine
|
12
|
-
* TTS using Festival endpoint
|
13
|
-
**/
|
14
|
-
export default class FestivalTTSEngine extends AbstractTTSEngine {
|
15
|
-
/** @override */
|
16
|
-
static isSupported() {
|
17
|
-
return typeof(soundManager) !== 'undefined' && soundManager.supported();
|
18
|
-
}
|
19
|
-
|
20
|
-
/** @param {TTSEngineOptions} options */
|
21
|
-
constructor(options) {
|
22
|
-
super(options);
|
23
|
-
// $.browsers is sometimes undefined on some Android browsers :/
|
24
|
-
// Likely related to when $.browser was moved to npm
|
25
|
-
/** @type {'mp3' | 'ogg'} format of audio to get */
|
26
|
-
this.audioFormat = $.browser?.mozilla ? 'ogg' : 'mp3';
|
27
|
-
}
|
28
|
-
|
29
|
-
/** @override */
|
30
|
-
getVoices() {
|
31
|
-
return [
|
32
|
-
{ default: true, lang: "en-US", localService: false, name: "Festival - English (US)", voiceURI: null }
|
33
|
-
];
|
34
|
-
}
|
35
|
-
|
36
|
-
/** @override */
|
37
|
-
init() {
|
38
|
-
// setup sound manager
|
39
|
-
soundManager.setup({
|
40
|
-
debugMode: false,
|
41
|
-
// Note, there's a bug in Chrome regarding range requests.
|
42
|
-
// Flash is used as a workaround.
|
43
|
-
// See https://bugs.chromium.org/p/chromium/issues/detail?id=505707
|
44
|
-
preferFlash: true,
|
45
|
-
url: '/bookreader/BookReader/soundmanager/swf',
|
46
|
-
useHTML5Audio: true,
|
47
|
-
//flash 8 version of swf is buggy when calling play() on a sound that is still loading
|
48
|
-
flashVersion: 9
|
49
|
-
});
|
50
|
-
}
|
51
|
-
|
52
|
-
/**
|
53
|
-
* @override
|
54
|
-
* @param {number} leafIndex
|
55
|
-
* @param {number} numLeafs total number of leafs in the current book
|
56
|
-
*/
|
57
|
-
start(leafIndex, numLeafs) {
|
58
|
-
let promise = null;
|
59
|
-
|
60
|
-
// Hack for iOS
|
61
|
-
if (navigator.userAgent.match(/mobile/i)) {
|
62
|
-
promise = this.iOSCaptureUserIntentHack();
|
63
|
-
}
|
64
|
-
|
65
|
-
promise = promise || Promise.resolve();
|
66
|
-
promise.then(() => super.start(leafIndex, numLeafs));
|
67
|
-
}
|
68
|
-
|
69
|
-
/** @override */
|
70
|
-
createSound(chunk) {
|
71
|
-
return new FestivalTTSSound(this.getSoundUrl(chunk.text));
|
72
|
-
}
|
73
|
-
|
74
|
-
/**
|
75
|
-
* @private
|
76
|
-
* Get URL for audio that says this text
|
77
|
-
* @param {String} dataString the thing to say
|
78
|
-
* @return {String} url
|
79
|
-
*/
|
80
|
-
getSoundUrl(dataString) {
|
81
|
-
return 'https://' + this.opts.server + '/BookReader/BookReaderGetTTS.php?string='
|
82
|
-
+ encodeURIComponent(dataString)
|
83
|
-
+ '&format=.' + this.audioFormat;
|
84
|
-
}
|
85
|
-
|
86
|
-
/**
|
87
|
-
* @private
|
88
|
-
* Security restrictions require playback to be triggered
|
89
|
-
* by a user click/touch. This intention gets lost in the async calls
|
90
|
-
* on iOS, but, for some reason, if we start the audio here, it works.
|
91
|
-
* See https://stackoverflow.com/questions/12206631/html5-audio-cant-play-through-javascript-unless-triggered-manually-once
|
92
|
-
* @return {PromiseLike}
|
93
|
-
*/
|
94
|
-
iOSCaptureUserIntentHack() {
|
95
|
-
const sound = soundManager.createSound({ url: SILENCE_1MS[this.audioFormat] });
|
96
|
-
return new Promise(res => sound.play({onfinish: res}))
|
97
|
-
.then(() => sound.destruct());
|
98
|
-
}
|
99
|
-
}
|
100
|
-
|
101
|
-
/** @extends AbstractTTSSound */
|
102
|
-
class FestivalTTSSound {
|
103
|
-
/** @param {string} soundUrl **/
|
104
|
-
constructor(soundUrl) {
|
105
|
-
this.soundUrl = soundUrl;
|
106
|
-
/** @type {SMSound} */
|
107
|
-
this.sound = null;
|
108
|
-
this.rate = 1;
|
109
|
-
/** @type {function} calling this resolves the "play" promise */
|
110
|
-
this._finishResolver = null;
|
111
|
-
}
|
112
|
-
|
113
|
-
get loaded() {
|
114
|
-
return this.sound && this.sound.loaded;
|
115
|
-
}
|
116
|
-
|
117
|
-
load(onload) {
|
118
|
-
this.sound = soundManager.createSound({
|
119
|
-
url: this.soundUrl,
|
120
|
-
// API recommended, but only fires once play started on safari
|
121
|
-
onload: () => {
|
122
|
-
if (this.rate != 1) this.sound.setPlaybackRate(this.rate);
|
123
|
-
onload();
|
124
|
-
},
|
125
|
-
onresume: () => {
|
126
|
-
sleep(25).then(() => {
|
127
|
-
if (this.rate != 1) this.sound.setPlaybackRate(this.rate);
|
128
|
-
});
|
129
|
-
}
|
130
|
-
});
|
131
|
-
return this.sound.load();
|
132
|
-
}
|
133
|
-
|
134
|
-
play() {
|
135
|
-
return new Promise(res => {
|
136
|
-
this._finishResolver = res;
|
137
|
-
this.sound.play({ onfinish: res });
|
138
|
-
})
|
139
|
-
.then(() => this.sound.destruct());
|
140
|
-
}
|
141
|
-
|
142
|
-
/** @override */
|
143
|
-
stop() {
|
144
|
-
this.sound.stop();
|
145
|
-
return Promise.resolve();
|
146
|
-
}
|
147
|
-
|
148
|
-
/** @override */
|
149
|
-
pause() { this.sound.pause(); }
|
150
|
-
/** @override */
|
151
|
-
resume() { this.sound.resume(); }
|
152
|
-
/** @override */
|
153
|
-
setPlaybackRate(rate) {
|
154
|
-
this.rate = rate;
|
155
|
-
this.sound.setPlaybackRate(rate);
|
156
|
-
}
|
157
|
-
|
158
|
-
/** @override */
|
159
|
-
finish() {
|
160
|
-
this.sound.stop();
|
161
|
-
this._finishResolver();
|
162
|
-
}
|
163
|
-
}
|
164
|
-
|
165
|
-
/** Needed to capture the audio context for iOS hack. Generated using Audacity. */
|
166
|
-
const SILENCE_1MS = {
|
167
|
-
mp3: 'data:audio/mp3;base64,//uQxAAAAAAAAAAAAAAAAAAAAAAAWGluZwAAAA8AAAACAAACcQCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA//////////////////////////////////////////////////////////////////8AAAAeTEFNRTMuOTlyBJwAAAAAAAAAADUgJAaUQQABrgAAAnHIf8sZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//sQxAADwlwBKGAAACAAAD/AAAAEAAAAH///////////////+UBAMExBTUUzLjk5LjOqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqr/+xDEIAPAAAGkAAAAIAAANIAAAASqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg==',
|
168
|
-
ogg: 'data:audio/ogg;base64,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',
|
169
|
-
};
|
@@ -1,107 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Class to manage a 'chunk' (approximately a paragraph) of text on a page.
|
3
|
-
*/
|
4
|
-
export default class PageChunk {
|
5
|
-
/**
|
6
|
-
* @param {number} leafIndex
|
7
|
-
* @param {number} chunkIndex
|
8
|
-
* @param {string} text
|
9
|
-
* @param {DJVURect[]} lineRects
|
10
|
-
*/
|
11
|
-
constructor(leafIndex, chunkIndex, text, lineRects) {
|
12
|
-
this.leafIndex = leafIndex;
|
13
|
-
this.chunkIndex = chunkIndex;
|
14
|
-
this.text = text;
|
15
|
-
this.lineRects = lineRects;
|
16
|
-
}
|
17
|
-
|
18
|
-
/**
|
19
|
-
* @param {string} server
|
20
|
-
* @param {string} bookPath
|
21
|
-
* @param {number} leafIndex
|
22
|
-
* @return {Promise<PageChunk[]>}
|
23
|
-
*/
|
24
|
-
static fetch(server, bookPath, leafIndex) {
|
25
|
-
// jquery's ajax "PromiseLike" implementation is inconsistent with
|
26
|
-
// modern Promises, so convert it to a full promise (it doesn't forward
|
27
|
-
// a returned promise to the next handler in the chain, which kind of
|
28
|
-
// defeats the entire point of using promises to avoid "callback hell")
|
29
|
-
return new Promise((res, rej) => {
|
30
|
-
$.ajax({
|
31
|
-
type: 'GET',
|
32
|
-
url: `https://${server}/BookReader/BookReaderGetTextWrapper.php`,
|
33
|
-
dataType:'jsonp',
|
34
|
-
cache: true,
|
35
|
-
data: {
|
36
|
-
path: `${bookPath}_djvu.xml`,
|
37
|
-
page: leafIndex
|
38
|
-
},
|
39
|
-
error: rej,
|
40
|
-
})
|
41
|
-
.then(chunks => {
|
42
|
-
res(PageChunk._fromTextWrapperResponse(leafIndex, chunks));
|
43
|
-
});
|
44
|
-
});
|
45
|
-
}
|
46
|
-
|
47
|
-
/**
|
48
|
-
* Convert the response from BookReaderGetTextWrapper.php into a {@link PageChunk} instance
|
49
|
-
* @param {number} leafIndex
|
50
|
-
* @param {Array<[String, ...DJVURect[]]>} chunksResponse
|
51
|
-
* @return {PageChunk[]}
|
52
|
-
*/
|
53
|
-
static _fromTextWrapperResponse(leafIndex, chunksResponse) {
|
54
|
-
return chunksResponse.map((c, i) => {
|
55
|
-
const correctedLineRects = PageChunk._fixChunkRects(c.slice(1));
|
56
|
-
const correctedText = PageChunk._removeDanglingHyphens(c[0]);
|
57
|
-
return new PageChunk(leafIndex, i, correctedText, correctedLineRects);
|
58
|
-
});
|
59
|
-
}
|
60
|
-
|
61
|
-
/**
|
62
|
-
* @private
|
63
|
-
* Sometimes the first rectangle will be ridiculously wide/tall. Find those and fix them
|
64
|
-
* *NOTE*: Modifies the original array and returns it.
|
65
|
-
* *NOTE*: This should probably be fixed on the petabox side, and then removed here
|
66
|
-
* Has 2 problems:
|
67
|
-
* - If the rect is the last rect on the page (and hence the only rect in the array),
|
68
|
-
* the rect's size isn't fixed
|
69
|
-
* - Because this relies on the second rect, there's a chance it won't be the right
|
70
|
-
* width
|
71
|
-
* @param {DJVURect[]} rects
|
72
|
-
* @return {DJVURect[]}
|
73
|
-
*/
|
74
|
-
static _fixChunkRects(rects) {
|
75
|
-
if (rects.length < 2) return rects;
|
76
|
-
|
77
|
-
const [firstRect, secondRect] = rects;
|
78
|
-
const [left, bottom, right] = firstRect;
|
79
|
-
const width = right - left;
|
80
|
-
const secondHeight = secondRect[1] - secondRect[3];
|
81
|
-
const secondWidth = secondRect[2] - secondRect[0];
|
82
|
-
const secondRight = secondRect[2];
|
83
|
-
|
84
|
-
if (width > secondWidth * 30) {
|
85
|
-
// Set the end to be the same
|
86
|
-
firstRect[2] = secondRight;
|
87
|
-
// And the top to be the same height
|
88
|
-
firstRect[3] = bottom - secondHeight;
|
89
|
-
}
|
90
|
-
|
91
|
-
return rects;
|
92
|
-
}
|
93
|
-
|
94
|
-
/**
|
95
|
-
* Remove "dangling" hyphens from read aloud text to avoid TTS stuttering
|
96
|
-
* @param {string} text
|
97
|
-
* @return {string}
|
98
|
-
*/
|
99
|
-
static _removeDanglingHyphens(text) {
|
100
|
-
return text.replace(/-\s+/g, '');
|
101
|
-
}
|
102
|
-
}
|
103
|
-
|
104
|
-
/**
|
105
|
-
* @typedef {[number, number, number, number]} DJVURect
|
106
|
-
* coords are in l,b,r,t order
|
107
|
-
*/
|
@@ -1,163 +0,0 @@
|
|
1
|
-
import PageChunk from './PageChunk.js';
|
2
|
-
|
3
|
-
/**
|
4
|
-
* Class that iterates over the page chunks of a book; caching/buffering
|
5
|
-
* as much as possible to try to ensure a smooth experience.
|
6
|
-
*/
|
7
|
-
export default class PageChunkIterator {
|
8
|
-
/**
|
9
|
-
* @param {number} pageCount total number of pages
|
10
|
-
* @param {number} start page to start on
|
11
|
-
* @param {PageChunkIteratorOptions} opts
|
12
|
-
*/
|
13
|
-
constructor(pageCount, start, opts) {
|
14
|
-
this.pageCount = pageCount;
|
15
|
-
this.opts = Object.assign({}, DEFAULT_OPTS, opts);
|
16
|
-
/** Position in the chunk sequence */
|
17
|
-
this._cursor = { page: start, chunk: 0 };
|
18
|
-
/** @type {Object<number, PageChunk[]>} leaf index -> chunks*/
|
19
|
-
this._bufferedPages = {};
|
20
|
-
/** @type {Object<number, PromiseLike<PageChunk[]>} leaf index -> chunks*/
|
21
|
-
this._bufferingPages = {};
|
22
|
-
/**
|
23
|
-
* @type {Promise} promise that manages cursor modifications so that they
|
24
|
-
* happen in order triggered as opposed to order the server responds
|
25
|
-
**/
|
26
|
-
this._cursorLock = Promise.resolve();
|
27
|
-
}
|
28
|
-
|
29
|
-
/**
|
30
|
-
* Get the next chunk
|
31
|
-
* @return {PromiseLike<"__PageChunkIterator.AT_END__" | PageChunk>}
|
32
|
-
*/
|
33
|
-
next() {
|
34
|
-
return this._cursorLock = this._cursorLock
|
35
|
-
.then(() => this._nextUncontrolled());
|
36
|
-
}
|
37
|
-
|
38
|
-
/**
|
39
|
-
* Sends the cursor back 1
|
40
|
-
* @return {Promise}
|
41
|
-
**/
|
42
|
-
decrement() {
|
43
|
-
return this._cursorLock = this._cursorLock
|
44
|
-
.then(() => this._decrementUncontrolled());
|
45
|
-
}
|
46
|
-
|
47
|
-
/**
|
48
|
-
* Gets without ensuring synchronization. Since this iterator has a lot of async
|
49
|
-
* code, calling e.g. "next" twice (before the first call to next has finished)
|
50
|
-
* would cause the system to be in a weird state. To avoid that, we make sure calls
|
51
|
-
* to next and decrement (functions that modify the cursor) are synchronized,
|
52
|
-
* so that regardless how long it takes for one to respond, they'll always be executed
|
53
|
-
* in the correct order.
|
54
|
-
* @return {PromiseLike<"__PageChunkIterator.AT_END__" | PageChunk>}
|
55
|
-
*/
|
56
|
-
_nextUncontrolled() {
|
57
|
-
if (this._cursor.page == this.pageCount) {
|
58
|
-
return Promise.resolve(PageChunkIterator.AT_END);
|
59
|
-
}
|
60
|
-
|
61
|
-
this._recenterBuffer(this._cursor.page);
|
62
|
-
|
63
|
-
return this._fetchPageChunks(this._cursor.page)
|
64
|
-
.then(chunks => {
|
65
|
-
if (this._cursor.chunk == chunks.length) {
|
66
|
-
this._cursor.page++;
|
67
|
-
this._cursor.chunk = 0;
|
68
|
-
return this._nextUncontrolled();
|
69
|
-
}
|
70
|
-
return chunks[this._cursor.chunk++];
|
71
|
-
});
|
72
|
-
}
|
73
|
-
|
74
|
-
/**
|
75
|
-
* Decrements without ensuring synchronization. (See {@link PageChunkIterator._nextUncontrolled});
|
76
|
-
* @return {Promise}
|
77
|
-
*/
|
78
|
-
_decrementUncontrolled() {
|
79
|
-
let cursorChangePromise = Promise.resolve();
|
80
|
-
|
81
|
-
if (this._cursor.chunk > 0) {
|
82
|
-
this._cursor.chunk--;
|
83
|
-
} else if (this._cursor.page > 0) {
|
84
|
-
this._cursor.page--;
|
85
|
-
// Go back possibly multiple pages, because pages can be blank
|
86
|
-
cursorChangePromise = this._fetchPageChunks(this._cursor.page)
|
87
|
-
.then(prevPageChunks => {
|
88
|
-
if (prevPageChunks.length == 0) return this._decrementUncontrolled();
|
89
|
-
else this._cursor.chunk = prevPageChunks.length - 1;
|
90
|
-
});
|
91
|
-
}
|
92
|
-
|
93
|
-
return cursorChangePromise
|
94
|
-
.then(() => this._fetchPageChunks(this._cursor.page));
|
95
|
-
}
|
96
|
-
|
97
|
-
/**
|
98
|
-
* Recenter the buffer around the provided page index
|
99
|
-
* @param {number} index
|
100
|
-
*/
|
101
|
-
_recenterBuffer(index) {
|
102
|
-
const start = Math.max(0, index - this.opts.pageBufferSize);
|
103
|
-
const end = Math.min(this.pageCount, index + this.opts.pageBufferSize + 1);
|
104
|
-
for (let i = start; i < end; i++) {
|
105
|
-
this._fetchPageChunks(i);
|
106
|
-
}
|
107
|
-
|
108
|
-
this._removePageFromBuffer(start - 1);
|
109
|
-
this._removePageFromBuffer(end + 1);
|
110
|
-
}
|
111
|
-
|
112
|
-
/**
|
113
|
-
* @param {number} index
|
114
|
-
*/
|
115
|
-
_removePageFromBuffer(index) {
|
116
|
-
delete this._bufferingPages[index];
|
117
|
-
delete this._bufferedPages[index];
|
118
|
-
}
|
119
|
-
|
120
|
-
/**
|
121
|
-
* Fetches the chunks on a page; checks the buffer, so it won't make unnecessary
|
122
|
-
* requests if it's called multiple times for the same index.
|
123
|
-
* @param {number} index
|
124
|
-
* @return {Promise<PageChunk[]>}
|
125
|
-
*/
|
126
|
-
_fetchPageChunks(index) {
|
127
|
-
if (index in this._bufferingPages) return this._bufferingPages[index];
|
128
|
-
if (index in this._bufferedPages) return Promise.resolve(this._bufferedPages[index]);
|
129
|
-
|
130
|
-
this._bufferingPages[index] = this._fetchPageChunksDirect(index)
|
131
|
-
.then(chunks => {
|
132
|
-
delete this._bufferingPages[index];
|
133
|
-
this._bufferedPages[index] = chunks;
|
134
|
-
return chunks;
|
135
|
-
});
|
136
|
-
|
137
|
-
return this._bufferingPages[index];
|
138
|
-
}
|
139
|
-
|
140
|
-
/**
|
141
|
-
* Fetches a page without checking buffer
|
142
|
-
* @param {number} index
|
143
|
-
*/
|
144
|
-
_fetchPageChunksDirect(index) {
|
145
|
-
return PageChunk.fetch(this.opts.server, this.opts.bookPath, index);
|
146
|
-
}
|
147
|
-
}
|
148
|
-
|
149
|
-
PageChunkIterator.AT_END = "__PageChunkIterator.AT_END__";
|
150
|
-
|
151
|
-
/** @type {PageChunkIteratorOptions} */
|
152
|
-
const DEFAULT_OPTS = {
|
153
|
-
server: null,
|
154
|
-
bookPath: null,
|
155
|
-
pageBufferSize: 2,
|
156
|
-
};
|
157
|
-
|
158
|
-
/**
|
159
|
-
* @typedef {Object} PageChunkIteratorOptions
|
160
|
-
* @property {string} server
|
161
|
-
* @property {string} bookPath
|
162
|
-
* @property {number} [pageBufferSize] number of pages to buffer before/after the current page
|
163
|
-
*/
|