@internetarchive/bookreader 5.0.0-5 → 5.0.0-50-a1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +17 -15
- package/.github/workflows/node.js.yml +77 -6
- package/.github/workflows/npm-publish.yml +6 -20
- package/.testcaferc.js +10 -0
- package/BookReader/BookReader.css +131 -339
- package/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.LICENSE.txt +24 -0
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +1493 -0
- package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +17 -0
- package/BookReader/ia-bookreader-bundle.js.map +1 -0
- package/BookReader/icons/close-circle-dark.svg +1 -0
- package/BookReader/icons/magnify-minus.svg +1 -1
- package/BookReader/icons/magnify-plus.svg +1 -1
- package/BookReader/icons/pause.svg +1 -1
- package/BookReader/icons/playback-speed.svg +1 -1
- package/BookReader/icons/read-aloud.svg +1 -1
- package/BookReader/icons/voice.svg +1 -0
- package/BookReader/images/BRicons.svg +2 -2
- package/BookReader/images/books_graphic.svg +1 -1
- package/BookReader/images/icon_book.svg +1 -1
- package/BookReader/images/icon_gear.svg +1 -1
- package/BookReader/images/icon_info.svg +1 -1
- package/BookReader/images/icon_playback-rate.svg +1 -1
- package/BookReader/images/icon_search_button.svg +1 -1
- package/BookReader/images/icon_share.svg +1 -1
- package/BookReader/images/icon_speaker.svg +1 -1
- package/BookReader/images/icon_speaker_open.svg +1 -1
- package/BookReader/images/marker_chap-off.svg +1 -1
- package/BookReader/images/marker_chap-on.svg +1 -1
- package/BookReader/images/marker_srch-on.svg +1 -1
- package/BookReader/jquery-3.js +2 -0
- package/BookReader/jquery-3.js.LICENSE.txt +24 -0
- 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.chapters.js +1 -1
- package/BookReader/plugins/plugin.chapters.js.map +1 -1
- package/BookReader/plugins/plugin.iframe.js +1 -1
- package/BookReader/plugins/plugin.iframe.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/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
- package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
- package/BookReader/webcomponents-bundle.js +3 -0
- package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
- package/BookReader/webcomponents-bundle.js.map +1 -0
- package/BookReaderDemo/BookReaderDemo.css +14 -1
- package/BookReaderDemo/IADemoBr.js +148 -0
- package/BookReaderDemo/demo-advanced.html +2 -2
- package/BookReaderDemo/demo-autoplay.html +2 -1
- package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
- package/BookReaderDemo/demo-fullscreen-mobile.html +2 -1
- package/BookReaderDemo/demo-fullscreen.html +2 -1
- package/BookReaderDemo/demo-iiif.html +2 -1
- package/BookReaderDemo/demo-internetarchive.html +84 -17
- package/BookReaderDemo/demo-multiple.html +2 -1
- package/BookReaderDemo/demo-preview-pages.html +2 -1
- package/BookReaderDemo/demo-simple.html +2 -1
- package/BookReaderDemo/demo-vendor-fullscreen.html +2 -1
- package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
- package/BookReaderDemo/immersion-1up.html +2 -1
- package/BookReaderDemo/immersion-mode.html +2 -1
- package/BookReaderDemo/toggle_controls.html +2 -1
- package/BookReaderDemo/view_mode.html +2 -1
- package/BookReaderDemo/viewmode-cycle.html +2 -3
- package/CHANGELOG.md +202 -0
- package/README.md +14 -1
- package/babel.config.js +18 -0
- package/codecov.yml +6 -0
- package/index.html +3 -0
- package/jsconfig.json +19 -0
- package/package.json +66 -56
- package/renovate.json +52 -0
- package/scripts/preversion.js +4 -1
- package/src/BookNavigator/assets/bookmark-colors.js +1 -1
- package/src/BookNavigator/assets/button-base.js +9 -2
- package/src/BookNavigator/assets/ia-logo.js +17 -0
- package/src/BookNavigator/assets/icon_checkmark.js +1 -1
- package/src/BookNavigator/assets/icon_close.js +1 -1
- package/src/BookNavigator/assets/icon_sort_asc.js +5 -0
- package/src/BookNavigator/assets/icon_sort_desc.js +5 -0
- package/src/BookNavigator/assets/icon_sort_neutral.js +5 -0
- package/src/BookNavigator/assets/icon_volumes.js +11 -0
- package/src/BookNavigator/book-navigator.js +583 -0
- package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
- package/src/BookNavigator/bookmarks/bookmark-edit.js +3 -4
- package/src/BookNavigator/bookmarks/bookmarks-list.js +2 -3
- package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +3 -8
- package/src/BookNavigator/bookmarks/bookmarks-provider.js +21 -8
- package/src/BookNavigator/bookmarks/ia-bookmarks.js +102 -66
- package/src/BookNavigator/delete-modal-actions.js +1 -1
- package/src/BookNavigator/downloads/downloads-provider.js +36 -21
- package/src/BookNavigator/downloads/downloads.js +41 -25
- package/src/BookNavigator/search/a-search-result.js +18 -13
- package/src/BookNavigator/search/search-provider.js +80 -28
- package/src/BookNavigator/search/search-results.js +10 -18
- package/src/BookNavigator/sharing.js +27 -0
- package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +11 -10
- package/src/BookNavigator/visual-adjustments/visual-adjustments.js +3 -3
- package/src/BookNavigator/volumes/volumes-provider.js +114 -0
- package/src/BookNavigator/volumes/volumes.js +188 -0
- package/src/BookReader/BookModel.js +0 -29
- package/src/BookReader/DebugConsole.js +3 -3
- package/src/BookReader/DragScrollable.js +233 -0
- package/src/BookReader/Mode1Up.js +51 -351
- package/src/BookReader/Mode1UpLit.js +441 -0
- package/src/BookReader/Mode2Up.js +120 -105
- package/src/BookReader/ModeSmoothZoom.js +179 -0
- package/src/BookReader/ModeThumb.js +17 -11
- package/src/BookReader/Navbar/Navbar.js +10 -36
- package/src/BookReader/PageContainer.js +69 -6
- package/src/BookReader/ReduceSet.js +1 -1
- package/src/BookReader/Toolbar/Toolbar.js +10 -37
- package/src/BookReader/options.js +10 -0
- package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
- package/src/BookReader/utils/ScrollClassAdder.js +31 -0
- package/src/BookReader/utils.js +92 -13
- package/src/BookReader.js +431 -620
- package/src/assets/icons/close-circle-dark.svg +1 -0
- package/src/assets/icons/magnify-minus.svg +3 -7
- package/src/assets/icons/magnify-plus.svg +3 -7
- package/src/assets/icons/voice.svg +1 -0
- package/src/css/BookReader.scss +0 -12
- package/src/css/_BRComponent.scss +1 -1
- package/src/css/_BRmain.scss +19 -24
- package/src/css/_BRnav.scss +4 -26
- package/src/css/_BRpages.scss +35 -0
- package/src/css/_BRsearch.scss +25 -216
- package/src/css/_TextSelection.scss +14 -17
- package/src/css/_colorbox.scss +2 -2
- package/src/css/_controls.scss +17 -5
- package/src/css/_icons.scss +6 -0
- package/src/ia-bookreader/ia-bookreader.js +224 -0
- package/src/plugins/plugin.autoplay.js +4 -4
- package/src/plugins/plugin.chapters.js +28 -35
- package/src/plugins/plugin.mobile_nav.js +11 -10
- package/src/plugins/plugin.resume.js +3 -3
- package/src/plugins/plugin.text_selection.js +26 -39
- package/src/plugins/plugin.vendor-fullscreen.js +4 -4
- package/src/plugins/search/plugin.search.js +174 -116
- package/src/plugins/search/view.js +63 -179
- package/src/plugins/tts/AbstractTTSEngine.js +46 -37
- package/src/plugins/tts/FestivalTTSEngine.js +13 -14
- package/src/plugins/tts/PageChunk.js +15 -21
- package/src/plugins/tts/PageChunkIterator.js +8 -12
- package/src/plugins/tts/WebTTSEngine.js +66 -69
- package/src/plugins/tts/plugin.tts.js +92 -109
- package/src/plugins/tts/utils.js +0 -9
- package/src/plugins/url/UrlPlugin.js +184 -0
- package/src/plugins/{plugin.url.js → url/plugin.url.js} +28 -6
- package/src/util/manifestGenerator.js +0 -0
- package/tests/e2e/README.md +37 -0
- package/tests/e2e/autoplay.test.js +2 -2
- package/tests/e2e/base.test.js +7 -7
- package/tests/e2e/helpers/base.js +9 -3
- package/tests/e2e/helpers/debug.js +1 -1
- package/tests/e2e/helpers/desktopSearch.js +14 -13
- package/tests/e2e/helpers/mobileSearch.js +3 -3
- package/tests/e2e/helpers/params.js +17 -0
- package/tests/e2e/models/Navigation.js +13 -4
- package/tests/e2e/rightToLeft.test.js +4 -5
- package/tests/e2e/viewmode.test.js +38 -33
- package/tests/jest/BookNavigator/book-navigator.test.js +634 -0
- package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
- package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
- package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
- package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
- package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
- package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
- package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
- package/tests/{karma/BookNavigator → jest/BookNavigator/search}/search-results.test.js +102 -58
- package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
- package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
- package/tests/jest/BookNavigator/volumes/volumes-provider.test.js +184 -0
- package/tests/jest/BookNavigator/volumes/volumes.test.js +97 -0
- package/tests/{BookReader → jest/BookReader}/BookModel.test.js +34 -14
- package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +176 -0
- package/tests/{BookReader → jest/BookReader}/DebugConsole.test.js +1 -1
- package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
- package/tests/jest/BookReader/Mode1UpLit.test.js +92 -0
- package/tests/{BookReader → jest/BookReader}/Mode2Up.test.js +36 -15
- package/tests/jest/BookReader/ModeSmoothZoom.test.js +149 -0
- package/tests/jest/BookReader/ModeThumb.test.js +71 -0
- package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +7 -7
- package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +88 -6
- package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
- package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
- package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
- package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
- package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
- package/tests/jest/BookReader/utils.test.js +186 -0
- package/tests/jest/BookReader.keyboard.test.js +190 -0
- package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
- package/tests/{BookReader.test.js → jest/BookReader.test.js} +18 -37
- package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
- package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +4 -4
- package/tests/{plugins → jest/plugins}/plugin.chapters.test.js +10 -11
- package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
- package/tests/{plugins → jest/plugins}/plugin.mobile_nav.test.js +5 -5
- package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
- package/tests/{plugins → jest/plugins}/plugin.text_selection.test.js +39 -47
- package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
- package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +63 -47
- package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +35 -6
- package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +9 -9
- package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
- package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
- package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
- package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +1 -1
- package/tests/{plugins → jest/plugins}/tts/utils.test.js +3 -28
- package/tests/jest/plugins/url/UrlPlugin.test.js +190 -0
- package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +33 -14
- package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
- package/tests/{util → jest/util}/docCookies.test.js +1 -1
- package/tests/{util → jest/util}/strings.test.js +1 -1
- package/tests/{utils.js → jest/utils.js} +38 -0
- package/webpack.config.js +11 -5
- package/.babelrc +0 -12
- package/.dependabot/config.yml +0 -6
- package/.testcaferc.json +0 -5
- package/BookReader/bookreader-component-bundle.js +0 -1450
- package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
- package/BookReader/bookreader-component-bundle.js.map +0 -1
- package/BookReader/jquery-1.10.1.js +0 -2
- package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
- package/BookReader/plugins/plugin.menu_toggle.js +0 -2
- package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
- package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
- package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
- package/karma.conf.js +0 -23
- package/src/BookNavigator/BookModel.js +0 -14
- package/src/BookNavigator/BookNavigator.js +0 -438
- package/src/BookNavigator/assets/book-loader.js +0 -27
- package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
- package/src/BookReaderComponent/BookReaderComponent.js +0 -112
- package/src/ItemNavigator/ItemNavigator.js +0 -372
- package/src/ItemNavigator/providers/sharing.js +0 -29
- package/src/Layers/sharing/sharing-provider.js +0 -22
- package/src/dragscrollable-br.js +0 -261
- package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
- package/src/plugins/plugin.bookmarks.js +0 -50
- package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
- package/tests/BookReader/Mode1Up.test.js +0 -164
- package/tests/BookReader/utils.test.js +0 -109
- package/tests/e2e/ia-production/ia-prod-base.js +0 -17
- package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
- package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
- package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
@@ -1,6 +1,7 @@
|
|
1
1
|
/* global br */
|
2
2
|
import { isChrome, isFirefox } from '../../util/browserSniffing.js';
|
3
|
-
import {
|
3
|
+
import { promisifyEvent, isAndroid } from './utils.js';
|
4
|
+
import { sleep } from '../../BookReader/utils.js';
|
4
5
|
import AbstractTTSEngine from './AbstractTTSEngine.js';
|
5
6
|
/** @typedef {import("./AbstractTTSEngine.js").PageChunk} PageChunk */
|
6
7
|
/** @typedef {import("./AbstractTTSEngine.js").AbstractTTSSound} AbstractTTSSound */
|
@@ -167,7 +168,7 @@ export class WebTTSSound {
|
|
167
168
|
* left off.
|
168
169
|
* @return {Promise<void>}
|
169
170
|
*/
|
170
|
-
reload() {
|
171
|
+
async reload() {
|
171
172
|
// We'll restore the pause state, so copy it here
|
172
173
|
const wasPaused = this.paused;
|
173
174
|
// Use recent event to determine where we'll restart from
|
@@ -179,14 +180,12 @@ export class WebTTSSound {
|
|
179
180
|
}
|
180
181
|
|
181
182
|
// We can't modify the utterance object, so we have to make a new one
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
if (!wasPaused) this.play();
|
189
|
-
});
|
183
|
+
await this.stop();
|
184
|
+
this.load();
|
185
|
+
// Instead of playing and immediately pausing, we don't start playing. Note
|
186
|
+
// this is a requirement because pause doesn't work consistently across
|
187
|
+
// browsers.
|
188
|
+
if (!wasPaused) this.play();
|
190
189
|
}
|
191
190
|
|
192
191
|
play() {
|
@@ -222,15 +221,16 @@ export class WebTTSSound {
|
|
222
221
|
return endPromise;
|
223
222
|
}
|
224
223
|
|
225
|
-
finish() {
|
226
|
-
this.stop()
|
224
|
+
async finish() {
|
225
|
+
await this.stop();
|
226
|
+
this.utterance.dispatchEvent(new Event('finish'));
|
227
227
|
}
|
228
228
|
|
229
229
|
/**
|
230
230
|
* @override
|
231
231
|
* Will fire a pause event unless already paused
|
232
232
|
**/
|
233
|
-
pause() {
|
233
|
+
async pause() {
|
234
234
|
if (this.paused) return;
|
235
235
|
|
236
236
|
const pausePromise = promisifyEvent(this.utterance, 'pause');
|
@@ -246,19 +246,18 @@ export class WebTTSSound {
|
|
246
246
|
if (pauseMightNotFire) {
|
247
247
|
// wait for it just in case
|
248
248
|
const timeoutPromise = sleep(100).then(() => 'timeout');
|
249
|
-
Promise.race([pausePromise, timeoutPromise])
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
});
|
249
|
+
const result = await Promise.race([pausePromise, timeoutPromise]);
|
250
|
+
// We got our pause event; nothing to do!
|
251
|
+
if (result != 'timeout') return;
|
252
|
+
|
253
|
+
this.utterance.dispatchEvent(new CustomEvent('pause', this._lastEvents.start));
|
254
|
+
|
255
|
+
// if pause might not work, then we'll stop entirely and restart later
|
256
|
+
if (pauseMightNotWork) this.stop();
|
258
257
|
}
|
259
258
|
}
|
260
259
|
|
261
|
-
resume() {
|
260
|
+
async resume() {
|
262
261
|
if (!this.started) {
|
263
262
|
this.play();
|
264
263
|
return;
|
@@ -278,16 +277,15 @@ export class WebTTSSound {
|
|
278
277
|
speechSynthesis.resume();
|
279
278
|
|
280
279
|
if (resumeMightNotFire) {
|
281
|
-
Promise.race([resumePromise, sleep(100).then(() => 'timeout')])
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
});
|
280
|
+
const result = await Promise.race([resumePromise, sleep(100).then(() => 'timeout')]);
|
281
|
+
|
282
|
+
if (result != 'timeout') return;
|
283
|
+
|
284
|
+
this.utterance.dispatchEvent(new CustomEvent('resume', {}));
|
285
|
+
if (resumeMightNotWork) {
|
286
|
+
await this.reload();
|
287
|
+
this.play();
|
288
|
+
}
|
291
289
|
}
|
292
290
|
}
|
293
291
|
|
@@ -296,6 +294,11 @@ export class WebTTSSound {
|
|
296
294
|
this.reload();
|
297
295
|
}
|
298
296
|
|
297
|
+
/** @param {SpeechSynthesisVoice} voice */
|
298
|
+
setVoice(voice) {
|
299
|
+
this.voice = voice;
|
300
|
+
this.reload();
|
301
|
+
}
|
299
302
|
/**
|
300
303
|
* @private
|
301
304
|
* Chrome has a bug where it only plays 15 seconds of TTS and then
|
@@ -303,45 +306,39 @@ export class WebTTSSound {
|
|
303
306
|
* We avoid this (as described here: https://bugs.chromium.org/p/chromium/issues/detail?id=679437#c15 )
|
304
307
|
* by pausing after 14 seconds and ~instantly resuming.
|
305
308
|
*/
|
306
|
-
_chromePausingBugFix() {
|
309
|
+
async _chromePausingBugFix() {
|
307
310
|
const timeoutPromise = sleep(14000).then(() => 'timeout');
|
308
311
|
const pausePromise = promisifyEvent(this.utterance, 'pause').then(() => 'paused');
|
309
312
|
const endPromise = promisifyEvent(this.utterance, 'end').then(() => 'ended');
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
speechSynthesis.resume();
|
340
|
-
this._chromePausingBugFix();
|
341
|
-
});
|
342
|
-
break;
|
343
|
-
}
|
344
|
-
});
|
313
|
+
const result = await Promise.race([timeoutPromise, pausePromise, endPromise]);
|
314
|
+
if (location.toString().indexOf('_debugReadAloud=true') != -1) {
|
315
|
+
console.log(`CHROME-PAUSE-HACK: ${result}`);
|
316
|
+
}
|
317
|
+
switch (result) {
|
318
|
+
case 'ended':
|
319
|
+
// audio was stopped/finished; nothing to do
|
320
|
+
break;
|
321
|
+
case 'paused':
|
322
|
+
// audio was paused; wait for resume
|
323
|
+
// Chrome won't let you resume the audio if 14s have passed 🤷
|
324
|
+
// We could do the same as before (but resume+pause instead of pause+resume),
|
325
|
+
// but that means we'd _constantly_ be running in the background. So in that
|
326
|
+
// case, let's just restart the chunk
|
327
|
+
await Promise.race([
|
328
|
+
promisifyEvent(this.utterance, 'resume'),
|
329
|
+
sleep(14000).then(() => 'timeout'),
|
330
|
+
]);
|
331
|
+
result == 'timeout' ? this.reload() : this._chromePausingBugFix();
|
332
|
+
break;
|
333
|
+
case 'timeout':
|
334
|
+
// We hit Chrome's secret cut off time. Pause/resume
|
335
|
+
// to be able to keep TTS-ing
|
336
|
+
speechSynthesis.pause();
|
337
|
+
await sleep(25);
|
338
|
+
speechSynthesis.resume();
|
339
|
+
this._chromePausingBugFix();
|
340
|
+
break;
|
341
|
+
}
|
345
342
|
}
|
346
343
|
}
|
347
344
|
|
@@ -6,8 +6,9 @@ import FestivalTTSEngine from './FestivalTTSEngine.js';
|
|
6
6
|
import WebTTSEngine from './WebTTSEngine.js';
|
7
7
|
import { toISO6391, approximateWordCount } from './utils.js';
|
8
8
|
import { en as tooltips } from './tooltip_dict.js';
|
9
|
-
|
10
|
-
/** @typedef {import(
|
9
|
+
import { renderBoxesInPageContainerLayer } from '../../BookReader/PageContainer.js';
|
10
|
+
/** @typedef {import('./PageChunk.js').default} PageChunk */
|
11
|
+
/** @typedef {import("./AbstractTTSEngine.js").default} AbstractTTSEngine */
|
11
12
|
|
12
13
|
// Default options for TTS
|
13
14
|
jQuery.extend(BookReader.defaultOptions, {
|
@@ -22,7 +23,9 @@ BookReader.prototype.setup = (function (super_) {
|
|
22
23
|
super_.call(this, options);
|
23
24
|
|
24
25
|
if (this.options.enableTtsPlugin) {
|
25
|
-
|
26
|
+
/** @type { {[pageIndex: number]: Array<{ l: number, r: number, t: number, b: number }>} } */
|
27
|
+
this._ttsBoxesByIndex = {};
|
28
|
+
|
26
29
|
let TTSEngine = WebTTSEngine.isSupported() ? WebTTSEngine :
|
27
30
|
FestivalTTSEngine.isSupported() ? FestivalTTSEngine :
|
28
31
|
null;
|
@@ -54,17 +57,6 @@ BookReader.prototype.init = (function(super_) {
|
|
54
57
|
if (this.options.enableTtsPlugin) {
|
55
58
|
// Bind to events
|
56
59
|
|
57
|
-
// TODO move this to BookReader.js or something
|
58
|
-
this.bind(BookReader.eventNames.fragmentChange, () => {
|
59
|
-
if (this.mode == this.constMode2up) {
|
60
|
-
// clear highlights if they're no longer valid for this page
|
61
|
-
const visibleIndices = [this.twoPage.currentIndexL, this.twoPage.currentIndexR];
|
62
|
-
const visibleSelector = visibleIndices.map(i => `.BRReadAloudHilite.Leaf-${i}`).join(', ');
|
63
|
-
$(this.ttsHilites).filter(visibleSelector).show();
|
64
|
-
$(this.ttsHilites).not(visibleSelector).hide();
|
65
|
-
}
|
66
|
-
});
|
67
|
-
|
68
60
|
this.bind(BookReader.eventNames.PostInit, () => {
|
69
61
|
this.$('.BRicon.read').click(() => {
|
70
62
|
this.ttsToggle();
|
@@ -88,6 +80,17 @@ BookReader.prototype.init = (function(super_) {
|
|
88
80
|
};
|
89
81
|
})(BookReader.prototype.init);
|
90
82
|
|
83
|
+
/** @override */
|
84
|
+
BookReader.prototype._createPageContainer = (function (super_) {
|
85
|
+
return function (index) {
|
86
|
+
const pageContainer = super_.call(this, index);
|
87
|
+
if (this.options.enableTtsPlugin && pageContainer.page && index in this._ttsBoxesByIndex) {
|
88
|
+
const pageIndex = pageContainer.page.index;
|
89
|
+
renderBoxesInPageContainerLayer('ttsHiliteLayer', this._ttsBoxesByIndex[pageIndex], pageContainer.page, pageContainer.$container[0]);
|
90
|
+
}
|
91
|
+
return pageContainer;
|
92
|
+
};
|
93
|
+
})(BookReader.prototype._createPageContainer);
|
91
94
|
|
92
95
|
// Extend buildMobileDrawerElement
|
93
96
|
BookReader.prototype.buildMobileDrawerElement = (function (super_) {
|
@@ -146,15 +149,49 @@ BookReader.prototype.initNavbar = (function (super_) {
|
|
146
149
|
<div class="icon icon-advance"></div>
|
147
150
|
</button>
|
148
151
|
</li>
|
152
|
+
<li>
|
153
|
+
<select class="playback-voices" name="playback-voice" style="display: none" title="Change read aloud voices">
|
154
|
+
</select>
|
155
|
+
</li>
|
149
156
|
</ul>
|
150
157
|
`);
|
158
|
+
|
151
159
|
$el.find('.BRcontrols').prepend(this.refs.$BRReadAloudToolbar);
|
160
|
+
|
161
|
+
const renderVoiceOption = (voices) => {
|
162
|
+
return voices.map(voice =>
|
163
|
+
`<option value="${voice.voiceURI}">${voice.lang} - ${voice.name}</option>`).join('');
|
164
|
+
};
|
165
|
+
|
166
|
+
const voiceSortOrder = (a,b) => `${a.lang} - ${a.name}`.localeCompare(`${b.lang} - ${b.name}`);
|
167
|
+
|
168
|
+
const renderVoicesMenu = (voicesMenu) => {
|
169
|
+
voicesMenu.empty();
|
170
|
+
const bookLanguage = this.ttsEngine.opts.bookLanguage;
|
171
|
+
const bookLanguages = this.ttsEngine.getVoices().filter(v => v.lang.startsWith(bookLanguage)).sort(voiceSortOrder);
|
172
|
+
const otherLanguages = this.ttsEngine.getVoices().filter(v => !v.lang.startsWith(bookLanguage)).sort(voiceSortOrder);
|
173
|
+
|
174
|
+
if (this.ttsEngine.getVoices().length > 1) {
|
175
|
+
voicesMenu.append($(`<optgroup label="Book Language (${bookLanguage})"> ${renderVoiceOption(bookLanguages)} </optgroup>`));
|
176
|
+
voicesMenu.append($(`<optgroup label="Other Languages"> ${renderVoiceOption(otherLanguages)} </optgroup>`));
|
177
|
+
|
178
|
+
voicesMenu.val(this.ttsEngine.voice.voiceURI);
|
179
|
+
voicesMenu.show();
|
180
|
+
} else {
|
181
|
+
voicesMenu.hide();
|
182
|
+
}
|
183
|
+
};
|
184
|
+
|
185
|
+
const voicesMenu = this.refs.$BRReadAloudToolbar.find('[name=playback-voice]');
|
186
|
+
renderVoicesMenu(voicesMenu);
|
187
|
+
voicesMenu.on("change", ev => this.ttsEngine.setVoice(voicesMenu.val()));
|
152
188
|
this.ttsEngine.events.on('pause resume start', () => this.ttsUpdateState());
|
153
|
-
this.
|
154
|
-
this.refs.$BRReadAloudToolbar.find('[name=
|
155
|
-
this.refs.$BRReadAloudToolbar.find('[name=
|
189
|
+
this.ttsEngine.events.on('voiceschanged', () => renderVoicesMenu(voicesMenu));
|
190
|
+
this.refs.$BRReadAloudToolbar.find('[name=play]').on("click", this.ttsPlayPause.bind(this));
|
191
|
+
this.refs.$BRReadAloudToolbar.find('[name=advance]').on("click", this.ttsJumpForward.bind(this));
|
192
|
+
this.refs.$BRReadAloudToolbar.find('[name=review]').on("click", this.ttsJumpBackward.bind(this));
|
156
193
|
const $rateSelector = this.refs.$BRReadAloudToolbar.find('select[name="playback-speed"]');
|
157
|
-
$rateSelector.change
|
194
|
+
$rateSelector.on("change", ev => this.ttsEngine.setPlaybackRate(parseFloat($rateSelector.val())));
|
158
195
|
$(`<li>
|
159
196
|
<button class="BRicon read js-tooltip" title="${tooltips.readAloud}">
|
160
197
|
<div class="icon icon-read-aloud"></div>
|
@@ -187,7 +224,7 @@ BookReader.prototype.ttsStart = function (startTTSEngine = true) {
|
|
187
224
|
this.$('.BRicon.read').addClass('unread active');
|
188
225
|
this.ttsSendAnalyticsEvent('Start');
|
189
226
|
if (startTTSEngine)
|
190
|
-
this.ttsEngine.start(this.currentIndex(), this.getNumLeafs());
|
227
|
+
this.ttsEngine.start(this.currentIndex(), this.book.getNumLeafs());
|
191
228
|
};
|
192
229
|
|
193
230
|
BookReader.prototype.ttsJumpForward = function () {
|
@@ -233,12 +270,10 @@ BookReader.prototype.ttsStop = function () {
|
|
233
270
|
* @param {PageChunk} chunk
|
234
271
|
* @return {PromiseLike<void>} returns once the flip is done
|
235
272
|
*/
|
236
|
-
BookReader.prototype.ttsBeforeChunkPlay = function(chunk) {
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
this.ttsScrollToChunk(chunk);
|
241
|
-
});
|
273
|
+
BookReader.prototype.ttsBeforeChunkPlay = async function(chunk) {
|
274
|
+
await this.ttsMaybeFlipToIndex(chunk.leafIndex);
|
275
|
+
this.ttsHighlightChunk(chunk);
|
276
|
+
this.ttsScrollToChunk(chunk);
|
242
277
|
};
|
243
278
|
|
244
279
|
/**
|
@@ -267,113 +302,61 @@ BookReader.prototype.ttsMaybeFlipToIndex = function (leafIndex) {
|
|
267
302
|
resolve();
|
268
303
|
} else {
|
269
304
|
this.animationFinishedCallback = resolve;
|
270
|
-
|
271
|
-
if (mustGoNext) this.next();
|
272
|
-
else this.prev();
|
273
|
-
promise.then(this.ttsMaybeFlipToIndex.bind(this, leafIndex));
|
305
|
+
this.jumpToIndex(leafIndex);
|
274
306
|
}
|
275
307
|
}
|
276
308
|
|
277
309
|
return promise;
|
278
|
-
}
|
279
|
-
|
280
|
-
/**
|
281
|
-
* @param {PageChunk} chunk
|
282
|
-
*/
|
283
|
-
BookReader.prototype.ttsHighlightChunk = function(chunk) {
|
284
|
-
this.ttsRemoveHilites();
|
285
|
-
|
286
|
-
if (this.constMode2up == this.mode) {
|
287
|
-
this.ttsHilite2UP(chunk);
|
288
|
-
} else {
|
289
|
-
this.ttsHilite1UP(chunk);
|
290
|
-
}
|
291
310
|
};
|
292
311
|
|
293
312
|
/**
|
294
313
|
* @param {PageChunk} chunk
|
295
314
|
*/
|
296
|
-
BookReader.prototype.
|
297
|
-
|
298
|
-
|
299
|
-
let leafTop = 0;
|
300
|
-
let h;
|
301
|
-
let i;
|
302
|
-
for (i = 0; i < chunk.leafIndex; i++) {
|
303
|
-
h = parseInt(this._getPageHeight(i) / this.reduce);
|
304
|
-
leafTop += h + this.padding;
|
305
|
-
}
|
306
|
-
|
307
|
-
const chunkTop = chunk.lineRects[0][3]; //coords are in l,b,r,t order
|
308
|
-
const chunkBot = chunk.lineRects[chunk.lineRects.length - 1][1];
|
309
|
-
|
310
|
-
const topOfFirstChunk = leafTop + chunkTop / this.reduce;
|
311
|
-
const botOfLastChunk = leafTop + chunkBot / this.reduce;
|
312
|
-
|
313
|
-
if (window?.soundManager?.debugMode) console.log('leafTop = ' + leafTop + ' topOfFirstChunk = ' + topOfFirstChunk + ' botOfLastChunk = ' + botOfLastChunk);
|
315
|
+
BookReader.prototype.ttsHighlightChunk = function(chunk) {
|
316
|
+
// The poorly-named variable leafIndex
|
317
|
+
const pageIndex = chunk.leafIndex;
|
314
318
|
|
315
|
-
|
316
|
-
const containerBot = containerTop + this.refs.$brContainer.height();
|
317
|
-
if (window?.soundManager?.debugMode) console.log('containerTop = ' + containerTop + ' containerBot = ' + containerBot);
|
319
|
+
this.ttsRemoveHilites();
|
318
320
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
};
|
321
|
+
// group by index; currently only possible to have chunks on one page :/
|
322
|
+
this._ttsBoxesByIndex = {
|
323
|
+
[pageIndex]: chunk.lineRects.map(([l, b, r, t]) => ({l, r, b, t}))
|
324
|
+
};
|
323
325
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
const l = chunk.lineRects[i][0];
|
331
|
-
const b = chunk.lineRects[i][1];
|
332
|
-
const r = chunk.lineRects[i][2];
|
333
|
-
const t = chunk.lineRects[i][3];
|
334
|
-
|
335
|
-
const div = document.createElement('div');
|
336
|
-
this.ttsHilites.push(div);
|
337
|
-
$(div).prop('className', 'BookReaderSearchHilite').appendTo(
|
338
|
-
this.$('.pagediv' + chunk.leafIndex)
|
339
|
-
);
|
340
|
-
|
341
|
-
$(div).css({
|
342
|
-
width: (r - l) / this.reduce + 'px',
|
343
|
-
height: (b - t) / this.reduce + 'px',
|
344
|
-
left: l / this.reduce + 'px',
|
345
|
-
top: t / this.reduce + 'px'
|
346
|
-
});
|
326
|
+
// update any already created pages
|
327
|
+
for (const [pageIndexString, boxes] of Object.entries(this._ttsBoxesByIndex)) {
|
328
|
+
const pageIndex = parseFloat(pageIndexString);
|
329
|
+
const page = this.book.getPage(pageIndex);
|
330
|
+
const pageContainers = this.getActivePageContainerElementsForIndex(pageIndex);
|
331
|
+
pageContainers.forEach(container => renderBoxesInPageContainerLayer('ttsHiliteLayer', boxes, page, container));
|
347
332
|
}
|
348
|
-
|
349
333
|
};
|
350
334
|
|
351
335
|
/**
|
352
336
|
* @param {PageChunk} chunk
|
353
337
|
*/
|
354
|
-
BookReader.prototype.
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
}
|
338
|
+
BookReader.prototype.ttsScrollToChunk = function(chunk) {
|
339
|
+
// It behaves weird if used in thumb mode
|
340
|
+
if (this.constModeThumb == this.mode) return;
|
341
|
+
|
342
|
+
$(`.pagediv${chunk.leafIndex} .ttsHiliteLayer rect`).last()?.[0]?.scrollIntoView({
|
343
|
+
// Only vertically center the highlight if we're in 1up or in full screen. In
|
344
|
+
// 2up, if we're not fullscreen, the whole body gets scrolled around to try to
|
345
|
+
// center the highlight 🙄 See:
|
346
|
+
// https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move/11041376
|
347
|
+
// Note: nearest doesn't quite work great, because the ReadAloud toolbar is now
|
348
|
+
// full-width, and covers up the last line of the highlight.
|
349
|
+
block: this.constMode1up == this.mode || this.isFullscreenActive ? 'center' : 'nearest',
|
350
|
+
inline: 'center',
|
351
|
+
behavior: 'smooth',
|
352
|
+
});
|
370
353
|
};
|
371
354
|
|
372
355
|
// ttsRemoveHilites()
|
373
356
|
//______________________________________________________________________________
|
374
357
|
BookReader.prototype.ttsRemoveHilites = function () {
|
375
|
-
$(this.
|
376
|
-
this.
|
358
|
+
$(this.getActivePageContainerElements()).find('.ttsHiliteLayer').remove();
|
359
|
+
this._ttsBoxesByIndex = {};
|
377
360
|
};
|
378
361
|
|
379
362
|
/**
|
package/src/plugins/tts/utils.js
CHANGED
@@ -26,15 +26,6 @@ export function approximateWordCount(text) {
|
|
26
26
|
return m ? m.length : 0;
|
27
27
|
}
|
28
28
|
|
29
|
-
/**
|
30
|
-
* Waits the provided number of ms and then resolves with a promise
|
31
|
-
* @param {number} ms
|
32
|
-
* @return {Promise}
|
33
|
-
*/
|
34
|
-
export function sleep(ms) {
|
35
|
-
return new Promise(res => setTimeout(res, ms));
|
36
|
-
}
|
37
|
-
|
38
29
|
/**
|
39
30
|
* Checks whether the current browser is on android
|
40
31
|
* @param {string} [userAgent]
|