@internetarchive/bookreader 5.0.0-70-a2 → 5.0.0-71
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.js +1 -1
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +11 -8
- 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/BookReader/plugins/plugin.url.js +1 -1
- package/BookReader/plugins/plugin.url.js.map +1 -1
- package/BookReaderDemo/demo-internetarchive.html +1 -1
- package/CHANGELOG.md +8 -0
- package/package.json +5 -5
- package/src/BookNavigator/book-navigator.js +2 -2
- package/src/BookNavigator/sharing.js +4 -4
- package/src/BookNavigator/{volumes/volumes-provider.js → viewable-files.js} +7 -7
- package/src/BookReader.js +4 -5
- package/src/ia-bookreader/ia-bookreader.js +1 -1
- package/src/plugins/tts/AbstractTTSEngine.js +22 -3
- package/src/plugins/url/UrlPlugin.js +5 -7
- package/tests/jest/BookNavigator/book-navigator.test.js +2 -2
- package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +1 -1
- package/tests/jest/BookNavigator/{volumes/volumes-provider.test.js → viewable-files/viewable-files-provider.test.js} +2 -2
- package/tests/jest/plugins/tts/AbstractTTSEngine.test.js +20 -0
- package/tests/jest/plugins/url/UrlPlugin.test.js +8 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { html } from 'lit';
|
|
2
2
|
|
|
3
|
-
import { viewableFilesIcon } from '@internetarchive/ia-item-navigator';
|
|
4
|
-
import '@internetarchive/ia-item-navigator';
|
|
3
|
+
import { viewableFilesIcon } from '@internetarchive/ia-item-navigator/dist/src/menus/viewable-files';
|
|
4
|
+
import '@internetarchive/ia-item-navigator/dist/src/menus/viewable-files';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* * @typedef { 'title_asc' | 'title_desc' | 'default'} SortTypesT
|
|
@@ -11,12 +11,12 @@ const sortTypes = {
|
|
|
11
11
|
title_desc: 'title_desc',
|
|
12
12
|
default: 'default'
|
|
13
13
|
};
|
|
14
|
-
export default class
|
|
14
|
+
export default class ViewableFilesProvider {
|
|
15
15
|
/**
|
|
16
|
-
* @param {import('
|
|
16
|
+
* @param {import('../BookReader').default} bookreader
|
|
17
17
|
*/
|
|
18
18
|
constructor({ baseHost, bookreader, onProviderChange }) {
|
|
19
|
-
/** @type {import('
|
|
19
|
+
/** @type {import('../BookReader').default} */
|
|
20
20
|
this.bookreader = bookreader;
|
|
21
21
|
this.onProviderChange = onProviderChange;
|
|
22
22
|
this.baseHost = baseHost;
|
|
@@ -30,13 +30,13 @@ export default class VolumesProvider {
|
|
|
30
30
|
this.icon = html`${viewableFilesIcon}`;
|
|
31
31
|
this.sortOrderBy = sortTypes.default;
|
|
32
32
|
|
|
33
|
-
this.component = document.createElement("iaux-viewable-files");
|
|
33
|
+
this.component = document.createElement("iaux-in-viewable-files-panel");
|
|
34
34
|
this.component.addSortToUrl = true;
|
|
35
35
|
this.component.subPrefix = bookreader.options.subPrefix || "";
|
|
36
36
|
this.component.baseHost = baseHost;
|
|
37
37
|
this.component.fileList = [...this.viewableFiles];
|
|
38
38
|
|
|
39
|
-
this.sortFilesComponent = document.createElement("iaux-sort-
|
|
39
|
+
this.sortFilesComponent = document.createElement("iaux-in-sort-files-button");
|
|
40
40
|
this.sortFilesComponent.fileListRaw = this.viewableFiles;
|
|
41
41
|
this.sortFilesComponent.addEventListener('fileListSorted', (e) => this.handleFileListSorted(e));
|
|
42
42
|
this.actionButton = this.sortFilesComponent;
|
package/src/BookReader.js
CHANGED
|
@@ -1788,7 +1788,7 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
|
|
|
1788
1788
|
// Index and page
|
|
1789
1789
|
if ('undefined' != typeof(urlHash['page'])) {
|
|
1790
1790
|
// page was set -- may not be int
|
|
1791
|
-
params.page = urlHash['page'];
|
|
1791
|
+
params.page = decodeURIComponent(urlHash['page']);
|
|
1792
1792
|
}
|
|
1793
1793
|
|
|
1794
1794
|
// $$$ process /region
|
|
@@ -1820,11 +1820,10 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
|
|
|
1820
1820
|
* @return {string}
|
|
1821
1821
|
*/
|
|
1822
1822
|
BookReader.prototype.fragmentFromParams = function(params, urlMode = 'hash') {
|
|
1823
|
-
const separator = '/';
|
|
1824
1823
|
const fragments = [];
|
|
1825
1824
|
|
|
1826
1825
|
if ('undefined' != typeof(params.page)) {
|
|
1827
|
-
fragments.push('page', params.page);
|
|
1826
|
+
fragments.push('page', encodeURIComponent(params.page));
|
|
1828
1827
|
} else {
|
|
1829
1828
|
if ('undefined' != typeof(params.index)) {
|
|
1830
1829
|
// Don't have page numbering but we do have the index
|
|
@@ -1850,10 +1849,10 @@ BookReader.prototype.fragmentFromParams = function(params, urlMode = 'hash') {
|
|
|
1850
1849
|
|
|
1851
1850
|
// search
|
|
1852
1851
|
if (params.search && urlMode === 'hash') {
|
|
1853
|
-
fragments.push('search', params.search);
|
|
1852
|
+
fragments.push('search', utils.encodeURIComponentPlus(params.search));
|
|
1854
1853
|
}
|
|
1855
1854
|
|
|
1856
|
-
return
|
|
1855
|
+
return fragments.join('/');
|
|
1857
1856
|
};
|
|
1858
1857
|
|
|
1859
1858
|
/**
|
|
@@ -30,7 +30,7 @@ export class IaBookReader extends LitElement {
|
|
|
30
30
|
super();
|
|
31
31
|
this.item = undefined;
|
|
32
32
|
this.bookreader = undefined;
|
|
33
|
-
this.baseHost = '
|
|
33
|
+
this.baseHost = 'archive.org';
|
|
34
34
|
this.fullscreen = false;
|
|
35
35
|
this.signedIn = false;
|
|
36
36
|
/** @type {ModalManager} */
|
|
@@ -142,6 +142,10 @@ export default class AbstractTTSEngine {
|
|
|
142
142
|
// MS Edge fires voices changed randomly very often
|
|
143
143
|
this.events.off('voiceschanged', this.updateBestVoice);
|
|
144
144
|
this.voice = this.getVoices().find(voice => voice.voiceURI === voiceURI);
|
|
145
|
+
// if the current book has a language set, store the selected voice with the book language as a suffix
|
|
146
|
+
if (this.opts.bookLanguage) {
|
|
147
|
+
localStorage.setItem(`BRtts-voice-${this.opts.bookLanguage}`, this.voice.voiceURI);
|
|
148
|
+
}
|
|
145
149
|
if (this.activeSound) this.activeSound.setVoice(this.voice);
|
|
146
150
|
}
|
|
147
151
|
|
|
@@ -221,10 +225,12 @@ export default class AbstractTTSEngine {
|
|
|
221
225
|
// user languages that match the book language
|
|
222
226
|
const matchingUserLangs = userLanguages.filter(lang => lang.startsWith(bookLanguage));
|
|
223
227
|
|
|
224
|
-
//
|
|
225
|
-
return AbstractTTSEngine.
|
|
228
|
+
// First try to find the last chosen voice from localStorage for the current book language
|
|
229
|
+
return AbstractTTSEngine.getMatchingStoredVoice(bookLangVoices, bookLanguage)
|
|
230
|
+
// Try to find voices that intersect these two sets
|
|
231
|
+
|| AbstractTTSEngine.getMatchingVoice(matchingUserLangs, bookLangVoices)
|
|
226
232
|
// no user languages match the books; let's return the best voice for the book language
|
|
227
|
-
(bookLangVoices.find(v => v.default) || bookLangVoices[0])
|
|
233
|
+
|| (bookLangVoices.find(v => v.default) || bookLangVoices[0])
|
|
228
234
|
// No voices match the book language? let's find a voice in the user's language
|
|
229
235
|
// and ignore book lang
|
|
230
236
|
|| AbstractTTSEngine.getMatchingVoice(userLanguages, voices)
|
|
@@ -232,6 +238,19 @@ export default class AbstractTTSEngine {
|
|
|
232
238
|
|| (voices.find(v => v.default) || voices[0]);
|
|
233
239
|
}
|
|
234
240
|
|
|
241
|
+
/**
|
|
242
|
+
* @private
|
|
243
|
+
* Get the voice last selected by the user for the book language from localStorage.
|
|
244
|
+
* Returns undefined if no voice is stored or found.
|
|
245
|
+
* @param {SpeechSynthesisVoice[]} voices browser voices to choose from
|
|
246
|
+
* @param {ISO6391} bookLanguage book language to look for
|
|
247
|
+
* @return {SpeechSynthesisVoice | undefined}
|
|
248
|
+
*/
|
|
249
|
+
static getMatchingStoredVoice(voices, bookLanguage) {
|
|
250
|
+
const storedVoice = localStorage.getItem(`BRtts-voice-${bookLanguage}`);
|
|
251
|
+
return (storedVoice ? voices.find(v => v.voiceURI === storedVoice) : undefined);
|
|
252
|
+
}
|
|
253
|
+
|
|
235
254
|
/**
|
|
236
255
|
* @private
|
|
237
256
|
* Get the best voice that matches one of the BCP47 languages (order by preference)
|
|
@@ -45,7 +45,7 @@ export class UrlPlugin {
|
|
|
45
45
|
|
|
46
46
|
const strPathParams = this.urlSchema
|
|
47
47
|
.filter(s => s.position == 'path')
|
|
48
|
-
.map(schema => pathParams[schema.name] ? `${schema.name}/${pathParams[schema.name]}` : '')
|
|
48
|
+
.map(schema => pathParams[schema.name] ? `${schema.name}/${encodeURIComponent(pathParams[schema.name])}` : '')
|
|
49
49
|
.join('/');
|
|
50
50
|
|
|
51
51
|
// replace consecutive slashes with a single slash + remove trailing slashes
|
|
@@ -83,15 +83,13 @@ export class UrlPlugin {
|
|
|
83
83
|
const hasPropertyKey = doesKeyExists(urlStrSplitSlashObj, schema.name);
|
|
84
84
|
const hasDeprecatedKey = doesKeyExists(schema, 'deprecated_for') && hasPropertyKey;
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
// Not in the URL
|
|
87
|
+
if (!hasPropertyKey && !hasDeprecatedKey) {
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
91
|
+
const urlStateParam = hasDeprecatedKey ? schema.deprecated_for : schema.name;
|
|
92
|
+
urlState[urlStateParam] = decodeURIComponent(urlStrSplitSlashObj[schema.name]);
|
|
95
93
|
});
|
|
96
94
|
|
|
97
95
|
// Add searchParams to urlState
|
|
@@ -9,7 +9,7 @@ import DownloadsProvider from '@/src/BookNavigator/downloads/downloads-provider.
|
|
|
9
9
|
import SearchProvider from '@/src/BookNavigator/search/search-provider.js';
|
|
10
10
|
import SharingProvider from '@/src/BookNavigator/sharing.js';
|
|
11
11
|
import VisualAdjustmentsProvider from '@/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js';
|
|
12
|
-
import
|
|
12
|
+
import ViewableFilesProvider from '@/src/BookNavigator/viewable-files.js';
|
|
13
13
|
import { ModalManager } from '@internetarchive/modal-manager';
|
|
14
14
|
import { SharedResizeObserver } from '@internetarchive/shared-resize-observer';
|
|
15
15
|
import '@/src/BookNavigator/book-navigator.js';
|
|
@@ -214,7 +214,7 @@ describe('<book-navigator>', () => {
|
|
|
214
214
|
await el.elementUpdated;
|
|
215
215
|
|
|
216
216
|
expect(el.menuProviders.volumes).toBeDefined();
|
|
217
|
-
expect(el.menuProviders.volumes).toBeInstanceOf(
|
|
217
|
+
expect(el.menuProviders.volumes).toBeInstanceOf(ViewableFilesProvider);
|
|
218
218
|
|
|
219
219
|
// also adds a menu shortcut
|
|
220
220
|
expect(el.menuShortcuts.find(m => m.id === 'volumes')).toBeDefined();
|
|
@@ -30,7 +30,7 @@ describe('Sharing Provider', () => {
|
|
|
30
30
|
expect(provider.id).toEqual('share');
|
|
31
31
|
expect(provider.icon).toBeDefined();
|
|
32
32
|
expect(provider.label).toEqual('Share this book');
|
|
33
|
-
expect(fixtureSync(provider.component).tagName).toContain('IAUX-
|
|
33
|
+
expect(fixtureSync(provider.component).tagName).toContain('IAUX-IN-SHARE-PANEL');
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
describe('Handles being a sub file/volume', () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { fixtureCleanup, fixtureSync } from '@open-wc/testing-helpers';
|
|
2
2
|
import sinon from 'sinon';
|
|
3
|
-
import
|
|
3
|
+
import ViewableFilesProvider from '@/src/BookNavigator/viewable-files';
|
|
4
4
|
|
|
5
5
|
const brOptions = {
|
|
6
6
|
"options": {
|
|
@@ -44,7 +44,7 @@ describe('Volumes Provider', () => {
|
|
|
44
44
|
const onProviderChange = sinon.fake();
|
|
45
45
|
|
|
46
46
|
const baseHost = "https://archive.org";
|
|
47
|
-
const provider = new
|
|
47
|
+
const provider = new ViewableFilesProvider({
|
|
48
48
|
baseHost,
|
|
49
49
|
bookreader: brOptions,
|
|
50
50
|
onProviderChange
|
|
@@ -111,6 +111,26 @@ for (const dummyVoice of [dummyVoiceHyphens, dummyVoiceUnderscores]) {
|
|
|
111
111
|
|
|
112
112
|
expect(getBestBookVoice(voices, 'en', ['en-CA', 'en'])).toBe(voices[0]);
|
|
113
113
|
});
|
|
114
|
+
|
|
115
|
+
test('choose stored language from localStorage', () => {
|
|
116
|
+
const voices = [
|
|
117
|
+
dummyVoice({lang: "en-US", voiceURI: "English US", default: true}),
|
|
118
|
+
dummyVoice({lang: "en-GB", voiceURI: "English GB"}),
|
|
119
|
+
dummyVoice({lang: "en-CA", voiceURI: "English CA"}),
|
|
120
|
+
];
|
|
121
|
+
class DummyEngine extends AbstractTTSEngine {
|
|
122
|
+
getVoices() { return voices; }
|
|
123
|
+
}
|
|
124
|
+
const ttsEngine = new DummyEngine({...DUMMY_TTS_ENGINE_OPTS, bookLanguage: 'en'});
|
|
125
|
+
// simulates setting default voice on tts startup
|
|
126
|
+
ttsEngine.updateBestVoice();
|
|
127
|
+
// simulates user choosing a voice that matches the bookLanguage
|
|
128
|
+
// voice will be stored in localStorage
|
|
129
|
+
ttsEngine.setVoice(voices[2].voiceURI);
|
|
130
|
+
|
|
131
|
+
// expecting the voice to be selected by getMatchingStoredVoice and returned as best voice
|
|
132
|
+
expect(getBestBookVoice(voices, 'en', [])).toBe(voices[2]);
|
|
133
|
+
});
|
|
114
134
|
});
|
|
115
135
|
}
|
|
116
136
|
|
|
@@ -19,6 +19,10 @@ describe('UrlPlugin tests', () => {
|
|
|
19
19
|
expect(urlPlugin.urlStateToUrlString(urlStateWithQueries)).toBe(expectedUrlFromStateWithQueries);
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
+
test('encodes page number', () => {
|
|
23
|
+
expect(urlPlugin.urlStateToUrlString({ page: '12/46' })).toBe(`page/12%2F46`);
|
|
24
|
+
});
|
|
25
|
+
|
|
22
26
|
test('urlStateToUrlString with unknown states in schema', () => {
|
|
23
27
|
const urlState = { page: 'n7', mode: '1up' };
|
|
24
28
|
const urlStateWithQueries = { page: 'n7', mode: '1up', q: 'hello', viewer: 'theater', sortBy: 'title_asc' };
|
|
@@ -47,6 +51,10 @@ describe('UrlPlugin tests', () => {
|
|
|
47
51
|
expect(urlPlugin.urlStringToUrlState(url1)).toEqual({page: 'n7', mode: '1up'});
|
|
48
52
|
});
|
|
49
53
|
|
|
54
|
+
test('decodes page number', () => {
|
|
55
|
+
expect(urlPlugin.urlStringToUrlState('/page/12%2F46')).toStrictEqual({ page: '12/46' });
|
|
56
|
+
});
|
|
57
|
+
|
|
50
58
|
test('urlStringToUrlState with deprecated_for', () => {
|
|
51
59
|
const url = '/page/n7/mode/2up/search/hello';
|
|
52
60
|
|