@internetarchive/bookreader 5.0.0-24-sortingstate-final → 5.0.0-27
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 +4 -0
- package/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/bookreader-component-bundle.js +1 -1
- package/BookReader/bookreader-component-bundle.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.tts.js.map +1 -1
- package/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/src/BookNavigator/bookmarks/ia-bookmarks.js +1 -0
- package/src/BookNavigator/volumes/volumes-provider.js +9 -39
- package/src/BookReader.js +102 -64
- package/src/plugins/plugin.url.js +2 -208
- package/src/plugins/search/plugin.search.js +1 -0
- package/src/plugins/tts/FestivalTTSEngine.js +1 -1
- package/tests/jest/BookReader.keyboard.test.js +190 -0
- package/tests/jest/plugins/plugin.url.test.js +1 -151
- package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +6 -6
- package/.nvmrc +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
# 5.0.0-27
|
|
2
|
+
Dev: eslint fix for $.browser @homewardgamer
|
|
3
|
+
Fix: cache search inside requests @iisa
|
|
4
|
+
# 5.0.0-26
|
|
5
|
+
Fix: read aloud play/pause button @nsharma123
|
|
6
|
+
Dev: strict keyboard shortcuts @mc2
|
|
7
|
+
Dev: update IA demo page @iisa
|
|
8
|
+
|
|
1
9
|
# 5.0.0-24
|
|
2
10
|
Fix: book-nav side panel zoom out @mc2
|
|
3
11
|
Dev: refactor zoom code @mc2
|
package/package.json
CHANGED
|
@@ -7,16 +7,8 @@ import volumesIcon from '../assets/icon_volumes.js';
|
|
|
7
7
|
|
|
8
8
|
import './volumes.js';
|
|
9
9
|
|
|
10
|
-
const sortType = {
|
|
11
|
-
title_asc: 'title_asc',
|
|
12
|
-
title_desc: 'title_desc',
|
|
13
|
-
default: 'default'
|
|
14
|
-
};
|
|
15
10
|
export default class VolumesProvider {
|
|
16
11
|
|
|
17
|
-
/**
|
|
18
|
-
* @param {import('../../BookReader').default} bookreader
|
|
19
|
-
*/
|
|
20
12
|
constructor(baseHost, bookreader, optionChange) {
|
|
21
13
|
this.optionChange = optionChange;
|
|
22
14
|
this.component = document.createElement("viewable-files");
|
|
@@ -25,9 +17,6 @@ export default class VolumesProvider {
|
|
|
25
17
|
this.viewableFiles = Object.keys(files).map(item => files[item]);
|
|
26
18
|
this.volumeCount = Object.keys(files).length;
|
|
27
19
|
|
|
28
|
-
/** @type {import('../../BookReader').default} */
|
|
29
|
-
this.bookreader = bookreader;
|
|
30
|
-
|
|
31
20
|
this.component.subPrefix = bookreader.options.subPrefix || "";
|
|
32
21
|
this.component.hostUrl = baseHost;
|
|
33
22
|
this.component.viewableFiles = this.viewableFiles;
|
|
@@ -36,30 +25,20 @@ export default class VolumesProvider {
|
|
|
36
25
|
this.label = `Viewable files (${this.volumeCount})`;
|
|
37
26
|
this.icon = html`${volumesIcon}`;
|
|
38
27
|
|
|
39
|
-
this.sortOrderBy =
|
|
40
|
-
|
|
41
|
-
// get sort state from query param
|
|
42
|
-
if (this.bookreader.urlPlugin) {
|
|
43
|
-
this.bookreader.urlPlugin.pullFromAddressBar();
|
|
44
|
-
|
|
45
|
-
const urlSortValue = this.bookreader.urlPlugin.getUrlParam('sort');
|
|
46
|
-
if (urlSortValue === sortType.title_asc || urlSortValue === sortType.title_desc) {
|
|
47
|
-
this.sortOrderBy = urlSortValue;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
this.sortVolumes(this.sortOrderBy);
|
|
28
|
+
this.sortOrderBy = "orig_sort";
|
|
29
|
+
this.sortVolumes("orig_sort");
|
|
51
30
|
}
|
|
52
31
|
|
|
53
32
|
get sortButton() {
|
|
54
33
|
const sortIcons = {
|
|
55
|
-
|
|
34
|
+
orig_sort: html`
|
|
56
35
|
<button class="sort-by neutral-icon" aria-label="Sort volumes in initial order" @click=${() => this.sortVolumes("title_asc")}>${sortNeutralIcon}</button>
|
|
57
36
|
`,
|
|
58
37
|
title_asc: html`
|
|
59
38
|
<button class="sort-by asc-icon" aria-label="Sort volumes in ascending order" @click=${() => this.sortVolumes("title_desc")}>${sortAscIcon}</button>
|
|
60
39
|
`,
|
|
61
40
|
title_desc: html`
|
|
62
|
-
<button class="sort-by desc-icon" aria-label="Sort volumes in descending order" @click=${() => this.sortVolumes("
|
|
41
|
+
<button class="sort-by desc-icon" aria-label="Sort volumes in descending order" @click=${() => this.sortVolumes("orig_sort")}>${sortDescIcon}</button>
|
|
63
42
|
`,
|
|
64
43
|
};
|
|
65
44
|
|
|
@@ -67,37 +46,28 @@ export default class VolumesProvider {
|
|
|
67
46
|
}
|
|
68
47
|
|
|
69
48
|
/**
|
|
70
|
-
* @param {'
|
|
49
|
+
* @param {'orig_sort' | 'title_asc' | 'title_desc'} sortByType
|
|
71
50
|
*/
|
|
72
51
|
sortVolumes(sortByType) {
|
|
73
52
|
let sortedFiles = [];
|
|
74
53
|
|
|
75
54
|
const files = this.viewableFiles;
|
|
76
55
|
sortedFiles = files.sort((a, b) => {
|
|
77
|
-
if (sortByType ===
|
|
78
|
-
else if (sortByType ===
|
|
79
|
-
else return a.
|
|
56
|
+
if (sortByType === 'orig_sort') return a.orig_sort - b.orig_sort;
|
|
57
|
+
else if (sortByType === 'title_asc') return a.title.localeCompare(b.title);
|
|
58
|
+
else return b.title.localeCompare(a.title);
|
|
80
59
|
});
|
|
81
60
|
|
|
82
61
|
this.sortOrderBy = sortByType;
|
|
83
62
|
this.component.viewableFiles = [...sortedFiles];
|
|
84
63
|
this.actionButton = this.sortButton;
|
|
85
|
-
|
|
86
|
-
if (this.bookreader.urlPlugin) {
|
|
87
|
-
if (this.sortOrderBy !== sortType.default) {
|
|
88
|
-
this.bookreader.urlPlugin.setUrlParam('sort', sortByType);
|
|
89
|
-
} else {
|
|
90
|
-
this.bookreader.urlPlugin.removeUrlParam('sort');
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
64
|
this.optionChange(this.bookreader);
|
|
95
65
|
|
|
96
66
|
this.multipleFilesClicked(sortByType);
|
|
97
67
|
}
|
|
98
68
|
|
|
99
69
|
/**
|
|
100
|
-
* @param {'
|
|
70
|
+
* @param {'orig_sort' | 'title_asc' | 'title_desc'} orderBy
|
|
101
71
|
*/
|
|
102
72
|
multipleFilesClicked(orderBy) {
|
|
103
73
|
if (!window.archive_analytics) {
|
package/src/BookReader.js
CHANGED
|
@@ -264,6 +264,15 @@ BookReader.prototype.setup = function(options) {
|
|
|
264
264
|
useSrcSet: this.options.useSrcSet,
|
|
265
265
|
reduceSet: this.reduceSet,
|
|
266
266
|
});
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Flag if BookReader has "focus" for keyboard shortcuts
|
|
270
|
+
* Initially true, set to false when:
|
|
271
|
+
* - BookReader scrolled out of view
|
|
272
|
+
* Set to true when:
|
|
273
|
+
* - BookReader scrolled into view
|
|
274
|
+
*/
|
|
275
|
+
this.hasKeyFocus = true;
|
|
267
276
|
};
|
|
268
277
|
|
|
269
278
|
/**
|
|
@@ -662,87 +671,116 @@ BookReader.prototype.resize = function() {
|
|
|
662
671
|
};
|
|
663
672
|
|
|
664
673
|
/**
|
|
665
|
-
* Binds keyboard event listeners
|
|
674
|
+
* Binds keyboard and keyboard focus event listeners
|
|
666
675
|
*/
|
|
667
|
-
BookReader.prototype.setupKeyListeners = function() {
|
|
668
|
-
var self = this;
|
|
676
|
+
BookReader.prototype.setupKeyListeners = function () {
|
|
669
677
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
678
|
+
// Keyboard focus by BookReader in viewport
|
|
679
|
+
//
|
|
680
|
+
// Intersection observer and callback sets BookReader keyboard
|
|
681
|
+
// "focus" flag off when the BookReader is not in the viewport.
|
|
682
|
+
if (window.IntersectionObserver) {
|
|
683
|
+
const observer = new IntersectionObserver((entries) => {
|
|
684
|
+
entries.forEach((entry) => {
|
|
685
|
+
if (entry.intersectionRatio === 0) {
|
|
686
|
+
this.hasKeyFocus = false;
|
|
687
|
+
} else {
|
|
688
|
+
this.hasKeyFocus = true;
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
}, {
|
|
692
|
+
root: null,
|
|
693
|
+
rootMargin: '0px',
|
|
694
|
+
threshold: [0, 0.05, 1],
|
|
695
|
+
});
|
|
696
|
+
observer.observe(this.refs.$br[0]);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Keyboard listeners
|
|
700
|
+
document.addEventListener('keydown', (e) => {
|
|
701
|
+
|
|
702
|
+
// Ignore if BookReader "focus" flag not set
|
|
703
|
+
if (!this.hasKeyFocus) {
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// Ignore if modifiers are active.
|
|
708
|
+
if (e.getModifierState('Control') ||
|
|
709
|
+
e.getModifierState('Alt') ||
|
|
710
|
+
e.getModifierState('Meta') ||
|
|
711
|
+
e.getModifierState('Win') /* hack for IE */) {
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// Ignore in input elements
|
|
716
|
+
if (utils.isInputActive()) {
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// KeyboardEvent code values:
|
|
721
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values
|
|
722
|
+
switch (e.key) {
|
|
723
|
+
|
|
724
|
+
// Page navigation
|
|
725
|
+
case "Home":
|
|
726
|
+
e.preventDefault();
|
|
727
|
+
this.first();
|
|
699
728
|
break;
|
|
700
|
-
case
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
e.preventDefault();
|
|
704
|
-
self.next();
|
|
705
|
-
}
|
|
729
|
+
case "End":
|
|
730
|
+
e.preventDefault();
|
|
731
|
+
this.last();
|
|
706
732
|
break;
|
|
707
|
-
case
|
|
708
|
-
|
|
733
|
+
case "ArrowDown":
|
|
734
|
+
case "PageDown":
|
|
735
|
+
case "Down": // hack for IE and old Gecko
|
|
736
|
+
// In 1up and thumb mode page scrolling handled by browser
|
|
737
|
+
if (this.constMode2up === this.mode) {
|
|
709
738
|
e.preventDefault();
|
|
710
|
-
|
|
739
|
+
this.next();
|
|
711
740
|
}
|
|
712
741
|
break;
|
|
713
|
-
case
|
|
714
|
-
|
|
742
|
+
case "ArrowUp":
|
|
743
|
+
case "PageUp":
|
|
744
|
+
case "Up": // hack for IE and old Gecko
|
|
745
|
+
// In 1up and thumb mode page scrolling handled by browser
|
|
746
|
+
if (this.constMode2up === this.mode) {
|
|
715
747
|
e.preventDefault();
|
|
716
|
-
|
|
748
|
+
this.prev();
|
|
717
749
|
}
|
|
718
750
|
break;
|
|
719
|
-
case
|
|
720
|
-
|
|
751
|
+
case "ArrowLeft":
|
|
752
|
+
case "Left": // hack for IE and old Gecko
|
|
753
|
+
// No y-scrolling in thumb mode
|
|
754
|
+
if (this.constModeThumb != this.mode) {
|
|
721
755
|
e.preventDefault();
|
|
722
|
-
|
|
756
|
+
this.left();
|
|
723
757
|
}
|
|
724
758
|
break;
|
|
725
|
-
case
|
|
726
|
-
|
|
759
|
+
case "ArrowRight":
|
|
760
|
+
case "Right": // hack for IE and old Gecko
|
|
761
|
+
// No y-scrolling in thumb mode
|
|
762
|
+
if (this.constModeThumb != this.mode) {
|
|
727
763
|
e.preventDefault();
|
|
728
|
-
|
|
764
|
+
this.right();
|
|
729
765
|
}
|
|
730
766
|
break;
|
|
731
|
-
|
|
732
|
-
case
|
|
733
|
-
case
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
self.zoom(-1);
|
|
737
|
-
}
|
|
767
|
+
// Zoom
|
|
768
|
+
case '-':
|
|
769
|
+
case 'Subtract':
|
|
770
|
+
e.preventDefault();
|
|
771
|
+
this.zoom(-1);
|
|
738
772
|
break;
|
|
739
|
-
case
|
|
740
|
-
case
|
|
741
|
-
case
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
773
|
+
case '+':
|
|
774
|
+
case '=':
|
|
775
|
+
case 'Add':
|
|
776
|
+
e.preventDefault();
|
|
777
|
+
this.zoom(1);
|
|
778
|
+
break;
|
|
779
|
+
// Fullscreen
|
|
780
|
+
case 'F':
|
|
781
|
+
case 'f':
|
|
782
|
+
e.preventDefault();
|
|
783
|
+
this.toggleFullscreen();
|
|
746
784
|
break;
|
|
747
785
|
}
|
|
748
786
|
});
|
|
@@ -50,7 +50,7 @@ BookReader.prototype.init = (function(super_) {
|
|
|
50
50
|
this.bind(BookReader.eventNames.PostInit, () => {
|
|
51
51
|
const { updateWindowTitle, urlMode } = this.options;
|
|
52
52
|
if (updateWindowTitle) {
|
|
53
|
-
document.title = this.shortTitle(
|
|
53
|
+
document.title = this.shortTitle(50);
|
|
54
54
|
}
|
|
55
55
|
if (urlMode === 'hash') {
|
|
56
56
|
this.urlStartLocationPolling();
|
|
@@ -86,7 +86,7 @@ BookReader.prototype.urlStartLocationPolling = function() {
|
|
|
86
86
|
this.oldLocationHash = this.urlReadFragment();
|
|
87
87
|
|
|
88
88
|
if (this.locationPollId) {
|
|
89
|
-
clearInterval(this.
|
|
89
|
+
clearInterval(this.locationPollID);
|
|
90
90
|
this.locationPollId = null;
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -196,209 +196,3 @@ BookReader.prototype.urlReadFragment = function() {
|
|
|
196
196
|
BookReader.prototype.urlReadHashFragment = function() {
|
|
197
197
|
return window.location.hash.substr(1);
|
|
198
198
|
};
|
|
199
|
-
export class UrlPlugin {
|
|
200
|
-
constructor(options = {}) {
|
|
201
|
-
this.bookReaderOptions = options;
|
|
202
|
-
|
|
203
|
-
// the canonical order of elements is important in the path and query string
|
|
204
|
-
this.urlSchema = [
|
|
205
|
-
{ name: 'page', position: 'path', default: 'n0' },
|
|
206
|
-
{ name: 'mode', position: 'path', default: '2up' },
|
|
207
|
-
{ name: 'search', position: 'path', deprecated_for: 'q' },
|
|
208
|
-
{ name: 'q', position: 'query_param' },
|
|
209
|
-
{ name: 'sort', position: 'query_param' },
|
|
210
|
-
{ name: 'view', position: 'query_param' },
|
|
211
|
-
{ name: 'admin', position: 'query_param' },
|
|
212
|
-
];
|
|
213
|
-
|
|
214
|
-
this.urlState = {};
|
|
215
|
-
this.urlMode = this.bookReaderOptions.urlMode || 'hash';
|
|
216
|
-
this.urlHistoryBasePath = this.bookReaderOptions.urlHistoryBasePath || '/';
|
|
217
|
-
this.urlLocationPollId = null;
|
|
218
|
-
this.oldLocationHash = null;
|
|
219
|
-
this.oldUserHash = null;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Parse JSON object URL state to string format
|
|
224
|
-
* Arrange path names in an order that it is positioned on the urlSchema
|
|
225
|
-
* @param {Object} urlState
|
|
226
|
-
* @returns {string}
|
|
227
|
-
*/
|
|
228
|
-
urlStateToUrlString(urlState) {
|
|
229
|
-
const searchParams = new URLSearchParams();
|
|
230
|
-
const pathParams = {};
|
|
231
|
-
|
|
232
|
-
Object.keys(urlState).forEach(key => {
|
|
233
|
-
let schema = this.urlSchema.find(schema => schema.name === key);
|
|
234
|
-
if (schema?.deprecated_for) {
|
|
235
|
-
schema = this.urlSchema.find(schemaKey => schemaKey.name === schema.deprecated_for);
|
|
236
|
-
}
|
|
237
|
-
if (schema?.position == 'path') {
|
|
238
|
-
pathParams[schema?.name] = urlState[key];
|
|
239
|
-
} else {
|
|
240
|
-
searchParams.append(schema?.name || key, urlState[key]);
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
const strPathParams = this.urlSchema
|
|
245
|
-
.filter(s => s.position == 'path')
|
|
246
|
-
.map(schema => pathParams[schema.name] ? `${schema.name}/${pathParams[schema.name]}` : '')
|
|
247
|
-
.join('/');
|
|
248
|
-
|
|
249
|
-
const strStrippedTrailingSlash = `${strPathParams.replace(/\/$/, '')}`;
|
|
250
|
-
const concatenatedPath = `${strStrippedTrailingSlash}?${searchParams.toString()}`;
|
|
251
|
-
return searchParams.toString() ? concatenatedPath : `${strStrippedTrailingSlash}`;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Parse string URL and add it in the current urlState
|
|
256
|
-
* Example:
|
|
257
|
-
* /page/n7/mode/2up => {page: 'n7', mode: '2up'}
|
|
258
|
-
* /page/n7/mode/2up/search/hello => {page: 'n7', mode: '2up', q: 'hello'}
|
|
259
|
-
* @param {string} urlString
|
|
260
|
-
* @returns {object}
|
|
261
|
-
*/
|
|
262
|
-
urlStringToUrlState(urlString) {
|
|
263
|
-
const urlState = {};
|
|
264
|
-
|
|
265
|
-
// Fetch searchParams from given {str}
|
|
266
|
-
// Note: whole URL path is needed for URL parsing
|
|
267
|
-
const urlPath = new URL(urlString, 'http://example.com');
|
|
268
|
-
const urlSearchParamsObj = Object.fromEntries(urlPath.searchParams.entries());
|
|
269
|
-
const urlStrSplitSlashObj = Object.fromEntries(urlPath.pathname
|
|
270
|
-
.match(/[^\\/]+\/[^\\/]+/g)
|
|
271
|
-
.map(x => x.split('/'))
|
|
272
|
-
);
|
|
273
|
-
const doesKeyExists = (_object, _key) => {
|
|
274
|
-
return Object.keys(_object).some(value => value == _key);
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
// Add path objects to urlState
|
|
278
|
-
this.urlSchema
|
|
279
|
-
.filter(schema => schema.position == 'path')
|
|
280
|
-
.forEach(schema => {
|
|
281
|
-
if (!urlStrSplitSlashObj[schema.name] && schema.default) {
|
|
282
|
-
return urlState[schema.name] = schema.default;
|
|
283
|
-
}
|
|
284
|
-
const hasPropertyKey = doesKeyExists(urlStrSplitSlashObj, schema.name);
|
|
285
|
-
const hasDeprecatedKey = doesKeyExists(schema, 'deprecated_for') && hasPropertyKey;
|
|
286
|
-
|
|
287
|
-
if (hasDeprecatedKey) {
|
|
288
|
-
urlState[schema.deprecated_for] = urlStrSplitSlashObj[schema.name];
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (hasPropertyKey) {
|
|
293
|
-
urlState[schema.name] = urlStrSplitSlashObj[schema.name];
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
// Add searchParams to urlState
|
|
299
|
-
Object.entries(urlSearchParamsObj).forEach(([key, value]) => {
|
|
300
|
-
urlState[key] = value;
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
return urlState;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Add or update key-value to the urlState
|
|
308
|
-
* @param {string} key
|
|
309
|
-
* @param {string} val
|
|
310
|
-
*/
|
|
311
|
-
setUrlParam(key, value) {
|
|
312
|
-
this.urlState[key] = value;
|
|
313
|
-
|
|
314
|
-
this.pushToAddressBar();
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Delete key-value to the urlState
|
|
319
|
-
* @param {string} key
|
|
320
|
-
*/
|
|
321
|
-
removeUrlParam(key) {
|
|
322
|
-
delete this.urlState[key];
|
|
323
|
-
|
|
324
|
-
this.pushToAddressBar();
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Get key-value from the urlState
|
|
329
|
-
* @param {string} key
|
|
330
|
-
* @return {string}
|
|
331
|
-
*/
|
|
332
|
-
getUrlParam(key) {
|
|
333
|
-
return this.urlState[key];
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Push URL params to addressbar
|
|
338
|
-
*/
|
|
339
|
-
pushToAddressBar() {
|
|
340
|
-
const urlStrPath = this.urlStateToUrlString(this.urlState);
|
|
341
|
-
if (this.urlMode == 'history') {
|
|
342
|
-
if (window.history && window.history.replaceState) {
|
|
343
|
-
const newUrlPath = `${this.urlHistoryBasePath}/${urlStrPath}`;
|
|
344
|
-
window.history.replaceState({}, null, newUrlPath);
|
|
345
|
-
}
|
|
346
|
-
} else {
|
|
347
|
-
window.location.replace('#' + urlStrPath);
|
|
348
|
-
}
|
|
349
|
-
this.oldLocationHash = urlStrPath;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Get the url and check if it has changed
|
|
354
|
-
* If it was changeed, update the urlState
|
|
355
|
-
*/
|
|
356
|
-
listenForHashChanges() {
|
|
357
|
-
this.oldLocationHash = window.location.hash.substr(1);
|
|
358
|
-
if (this.urlLocationPollId) {
|
|
359
|
-
clearInterval(this.urlLocationPollId);
|
|
360
|
-
this.urlLocationPollId = null;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// check if the URL changes
|
|
364
|
-
const updateHash = () => {
|
|
365
|
-
const newFragment = window.location.hash.substr(1);
|
|
366
|
-
const hasFragmentChange = newFragment != this.oldLocationHash;
|
|
367
|
-
|
|
368
|
-
if (!hasFragmentChange) { return; }
|
|
369
|
-
|
|
370
|
-
this.urlState = this.urlStringToUrlState(newFragment);
|
|
371
|
-
};
|
|
372
|
-
this.urlLocationPollId = setInterval(updateHash, 500);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Will read either the hash or URL and return the bookreader fragment
|
|
377
|
-
*/
|
|
378
|
-
pullFromAddressBar (location = window.location) {
|
|
379
|
-
const path = this.urlMode === 'history'
|
|
380
|
-
? (location.pathname.substr(this.urlHistoryBasePath.length) + location.search)
|
|
381
|
-
: location.hash.substr(1);
|
|
382
|
-
this.urlState = this.urlStringToUrlState(path);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
export class BookreaderUrlPlugin extends BookReader {
|
|
387
|
-
init() {
|
|
388
|
-
if (this.options.enableUrlPlugin) {
|
|
389
|
-
this.urlPlugin = new UrlPlugin(this.options);
|
|
390
|
-
this.bind(BookReader.eventNames.PostInit, () => {
|
|
391
|
-
const { urlMode } = this.options;
|
|
392
|
-
|
|
393
|
-
if (urlMode === 'hash') {
|
|
394
|
-
this.urlPlugin.listenForHashChanges();
|
|
395
|
-
}
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
super.init();
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
window.BookReader = BookreaderUrlPlugin;
|
|
404
|
-
export default BookreaderUrlPlugin;
|
|
@@ -23,7 +23,7 @@ export default class FestivalTTSEngine extends AbstractTTSEngine {
|
|
|
23
23
|
// $.browsers is sometimes undefined on some Android browsers :/
|
|
24
24
|
// Likely related to when $.browser was moved to npm
|
|
25
25
|
/** @type {'mp3' | 'ogg'} format of audio to get */
|
|
26
|
-
this.audioFormat = $.browser?.mozilla ? 'ogg' : 'mp3';
|
|
26
|
+
this.audioFormat = $.browser?.mozilla ? 'ogg' : 'mp3'; //eslint-disable-line no-jquery/no-browser
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/** @override */
|