@internetarchive/bookreader 5.0.0-35 → 5.0.0-36
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 +1 -11
- package/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +25 -25
- package/BookReader/ia-bookreader-bundle.js.map +1 -1
- package/BookReader/plugins/plugin.tts.js +1 -1
- package/BookReader/plugins/plugin.tts.js.map +1 -1
- package/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/BookNavigator/book-navigator.js +7 -1
- package/src/BookNavigator/bookmarks/ia-bookmarks.js +26 -27
- package/src/BookNavigator/search/search-provider.js +2 -2
- package/src/BookReader.js +55 -55
- package/src/ia-bookreader/ia-bookreader.js +4 -1
- package/src/plugins/tts/AbstractTTSEngine.js +31 -34
- package/src/plugins/tts/PageChunkIterator.js +8 -12
- package/src/plugins/tts/WebTTSEngine.js +41 -46
- package/src/plugins/tts/plugin.tts.js +16 -10
- package/tests/e2e/base.test.js +7 -4
- package/tests/e2e/helpers/params.js +1 -1
- package/tests/e2e/viewmode.test.js +30 -30
|
@@ -258,7 +258,7 @@ export class WebTTSSound {
|
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
resume() {
|
|
261
|
+
async resume() {
|
|
262
262
|
if (!this.started) {
|
|
263
263
|
this.play();
|
|
264
264
|
return;
|
|
@@ -278,16 +278,15 @@ export class WebTTSSound {
|
|
|
278
278
|
speechSynthesis.resume();
|
|
279
279
|
|
|
280
280
|
if (resumeMightNotFire) {
|
|
281
|
-
Promise.race([resumePromise, sleep(100).then(() => 'timeout')])
|
|
282
|
-
.then(result => {
|
|
283
|
-
if (result != 'timeout') return;
|
|
281
|
+
const result = await Promise.race([resumePromise, sleep(100).then(() => 'timeout')]);
|
|
284
282
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
283
|
+
if (result != 'timeout') return;
|
|
284
|
+
|
|
285
|
+
this.utterance.dispatchEvent(new CustomEvent('resume', {}));
|
|
286
|
+
if (resumeMightNotWork) {
|
|
287
|
+
await this.reload();
|
|
288
|
+
this.play();
|
|
289
|
+
}
|
|
291
290
|
}
|
|
292
291
|
}
|
|
293
292
|
|
|
@@ -308,45 +307,41 @@ export class WebTTSSound {
|
|
|
308
307
|
* We avoid this (as described here: https://bugs.chromium.org/p/chromium/issues/detail?id=679437#c15 )
|
|
309
308
|
* by pausing after 14 seconds and ~instantly resuming.
|
|
310
309
|
*/
|
|
311
|
-
_chromePausingBugFix() {
|
|
310
|
+
async _chromePausingBugFix() {
|
|
312
311
|
const timeoutPromise = sleep(14000).then(() => 'timeout');
|
|
313
312
|
const pausePromise = promisifyEvent(this.utterance, 'pause').then(() => 'paused');
|
|
314
313
|
const endPromise = promisifyEvent(this.utterance, 'end').then(() => 'ended');
|
|
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
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
});
|
|
347
|
-
break;
|
|
348
|
-
}
|
|
349
|
-
});
|
|
314
|
+
const result = await Promise.race([timeoutPromise, pausePromise, endPromise]);
|
|
315
|
+
if (location.toString().indexOf('_debugReadAloud=true') != -1) {
|
|
316
|
+
console.log(`CHROME-PAUSE-HACK: ${result}`);
|
|
317
|
+
}
|
|
318
|
+
switch (result) {
|
|
319
|
+
case 'ended':
|
|
320
|
+
// audio was stopped/finished; nothing to do
|
|
321
|
+
break;
|
|
322
|
+
case 'paused':
|
|
323
|
+
// audio was paused; wait for resume
|
|
324
|
+
// Chrome won't let you resume the audio if 14s have passed 🤷
|
|
325
|
+
// We could do the same as before (but resume+pause instead of pause+resume),
|
|
326
|
+
// but that means we'd _constantly_ be running in the background. So in that
|
|
327
|
+
// case, let's just restart the chunk
|
|
328
|
+
Promise.race([
|
|
329
|
+
promisifyEvent(this.utterance, 'resume'),
|
|
330
|
+
sleep(14000).then(() => 'timeout'),
|
|
331
|
+
])
|
|
332
|
+
.then(result => {
|
|
333
|
+
result == 'timeout' ? this.reload() : this._chromePausingBugFix();
|
|
334
|
+
});
|
|
335
|
+
break;
|
|
336
|
+
case 'timeout':
|
|
337
|
+
// We hit Chrome's secret cut off time. Pause/resume
|
|
338
|
+
// to be able to keep TTS-ing
|
|
339
|
+
speechSynthesis.pause();
|
|
340
|
+
await sleep(25);
|
|
341
|
+
speechSynthesis.resume();
|
|
342
|
+
this._chromePausingBugFix();
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
350
345
|
}
|
|
351
346
|
}
|
|
352
347
|
|
|
@@ -261,9 +261,7 @@ BookReader.prototype.ttsStop = function () {
|
|
|
261
261
|
BookReader.prototype.ttsBeforeChunkPlay = async function(chunk) {
|
|
262
262
|
await this.ttsMaybeFlipToIndex(chunk.leafIndex);
|
|
263
263
|
this.ttsHighlightChunk(chunk);
|
|
264
|
-
|
|
265
|
-
// the active page :/ Disabling cause the extra scroll just adds an odd jitter.
|
|
266
|
-
// this.ttsScrollToChunk(chunk);
|
|
264
|
+
this.ttsScrollToChunk(chunk);
|
|
267
265
|
};
|
|
268
266
|
|
|
269
267
|
/**
|
|
@@ -292,10 +290,7 @@ BookReader.prototype.ttsMaybeFlipToIndex = function (leafIndex) {
|
|
|
292
290
|
resolve();
|
|
293
291
|
} else {
|
|
294
292
|
this.animationFinishedCallback = resolve;
|
|
295
|
-
|
|
296
|
-
if (mustGoNext) this.next();
|
|
297
|
-
else this.prev();
|
|
298
|
-
promise.then(this.ttsMaybeFlipToIndex.bind(this, leafIndex));
|
|
293
|
+
this.jumpToIndex(leafIndex);
|
|
299
294
|
}
|
|
300
295
|
}
|
|
301
296
|
|
|
@@ -329,9 +324,20 @@ BookReader.prototype.ttsHighlightChunk = function(chunk) {
|
|
|
329
324
|
* @param {PageChunk} chunk
|
|
330
325
|
*/
|
|
331
326
|
BookReader.prototype.ttsScrollToChunk = function(chunk) {
|
|
332
|
-
if
|
|
333
|
-
|
|
334
|
-
|
|
327
|
+
// It behaves weird if used in thumb mode
|
|
328
|
+
if (this.constModeThumb == this.mode) return;
|
|
329
|
+
|
|
330
|
+
$(`.pagediv${chunk.leafIndex} .ttsHiliteLayer rect`).last()?.[0]?.scrollIntoView({
|
|
331
|
+
// Only vertically center the highlight if we're in 1up or in full screen. In
|
|
332
|
+
// 2up, if we're not fullscreen, the whole body gets scrolled around to try to
|
|
333
|
+
// center the highlight 🙄 See:
|
|
334
|
+
// https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move/11041376
|
|
335
|
+
// Note: nearest doesn't quite work great, because the ReadAloud toolbar is now
|
|
336
|
+
// full-width, and covers up the last line of the highlight.
|
|
337
|
+
block: this.constMode1up == this.mode || this.isFullscreenActive ? 'center' : 'nearest',
|
|
338
|
+
inline: 'center',
|
|
339
|
+
behavior: 'smooth',
|
|
340
|
+
});
|
|
335
341
|
};
|
|
336
342
|
|
|
337
343
|
// ttsRemoveHilites()
|
package/tests/e2e/base.test.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { runBaseTests } from './helpers/base';
|
|
2
2
|
import BookReader from './models/BookReader';
|
|
3
|
-
import { runDesktopSearchTests } from './helpers/desktopSearch';
|
|
3
|
+
// import { runDesktopSearchTests } from './helpers/desktopSearch';
|
|
4
4
|
// import { runMobileSearchTests } from './helpers/mobileSearch';
|
|
5
5
|
import params from './helpers/params';
|
|
6
6
|
|
|
@@ -21,10 +21,13 @@ ocaids.forEach(ocaid => {
|
|
|
21
21
|
fixture `Base Tests for: ${ocaid}`.page `${url}`;
|
|
22
22
|
runBaseTests(new BookReader());
|
|
23
23
|
|
|
24
|
-
fixture `Desktop Search Tests for: ${ocaid}`
|
|
25
|
-
.page `${url}`;
|
|
26
|
-
runDesktopSearchTests(new BookReader());
|
|
27
24
|
|
|
25
|
+
// Todo: Re-enable when testing side panel
|
|
26
|
+
// fixture `Desktop Search Tests for: ${ocaid}`
|
|
27
|
+
// .page `${url}`;
|
|
28
|
+
// runDesktopSearchTests(new BookReader());
|
|
29
|
+
|
|
30
|
+
// Todo: deprecated, will remove once mmenu is removed.
|
|
28
31
|
// fixture `Mobile Search Tests for: ${ocaid}`
|
|
29
32
|
// .page `${url}`
|
|
30
33
|
// runMobileSearchTests(new BookReader());
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
class TestParams {
|
|
3
3
|
baseUrl = process.env.BASE_URL?.replace(/\/+$/, '') ?? 'http://127.0.0.1:8000'
|
|
4
|
-
ocaids = process.env.OCAIDS?.split(',') ??
|
|
4
|
+
ocaids = process.env.OCAIDS?.split(',') ?? null;
|
|
5
5
|
/** Whether the url we're testing is a prod (or near prod) IA url, or a demos url */
|
|
6
6
|
isIA = new URL(this.baseUrl).hostname.endsWith('archive.org');
|
|
7
7
|
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Selector } from 'testcafe';
|
|
2
|
+
import BookReader from './models/BookReader';
|
|
3
|
+
import params from './helpers/params';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
fixture `Viewmode carousel`.page `${params.baseUrl}/BookReaderDemo/demo-internetarchive.html?ocaid=goody`;
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
test('Clicking `view mode` cycles through view modes', async t => {
|
|
8
|
+
const { nav } = (new BookReader());
|
|
9
9
|
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
//
|
|
13
|
-
|
|
10
|
+
// viewmode button only appear on mobile devices
|
|
11
|
+
await t.resizeWindow(400, 800);
|
|
12
|
+
// Flip forward one
|
|
13
|
+
await t.pressKey('right');
|
|
14
14
|
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
// 2up to thumb
|
|
16
|
+
await t.click(nav.desktop.viewmode);
|
|
17
|
+
const thumbnailContainer = Selector('.BRmodeThumb');
|
|
18
|
+
await t.expect(thumbnailContainer.visible).ok();
|
|
19
|
+
const thumbImages = thumbnailContainer.find('.BRpageview img');
|
|
20
|
+
await t.expect(thumbImages.count).gt(0);
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
//
|
|
28
|
-
|
|
22
|
+
// thumb to 1up
|
|
23
|
+
await t.click(nav.desktop.viewmode);
|
|
24
|
+
const onePageViewContainer = Selector('br-mode-1up');
|
|
25
|
+
await t.expect(onePageViewContainer.visible).ok();
|
|
26
|
+
const onePageImages = onePageViewContainer.find('.BRmode1up .BRpagecontainer');
|
|
27
|
+
// we usually pre-fetch the page in question & 1 before/after it
|
|
28
|
+
await t.expect(onePageImages.count).gte(3);
|
|
29
29
|
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
// 1up to 2up
|
|
31
|
+
await t.click(nav.desktop.viewmode);
|
|
32
|
+
const twoPageContainer = Selector('.BRtwopageview');
|
|
33
|
+
await t.expect(twoPageContainer.visible).ok();
|
|
34
|
+
const twoPageImages = twoPageContainer.find('img.BRpageimage');
|
|
35
|
+
await t.expect(twoPageImages.count).gte(2);
|
|
36
|
+
});
|