@internetarchive/bookreader 5.0.0-21 → 5.0.0-24-sortingstate
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/.nvmrc +1 -0
- package/BookReader/BookReader.css +0 -55
- package/BookReader/BookReader.js +32148 -2
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/bookreader-component-bundle.js +10916 -941
- package/BookReader/bookreader-component-bundle.js.map +1 -1
- package/BookReader/icons/1up.svg +12 -1
- package/BookReader/icons/2up.svg +15 -1
- package/BookReader/icons/advance.svg +26 -3
- package/BookReader/icons/chevron-right.svg +1 -1
- package/BookReader/icons/close-circle-dark.svg +1 -1
- package/BookReader/icons/close-circle.svg +1 -1
- package/BookReader/icons/fullscreen.svg +17 -1
- package/BookReader/icons/fullscreen_exit.svg +17 -1
- package/BookReader/icons/hamburger.svg +15 -1
- package/BookReader/icons/left-arrow.svg +12 -1
- package/BookReader/icons/magnify-minus.svg +16 -1
- package/BookReader/icons/magnify-plus.svg +17 -1
- package/BookReader/icons/magnify.svg +15 -1
- package/BookReader/icons/pause.svg +23 -1
- package/BookReader/icons/play.svg +22 -1
- package/BookReader/icons/playback-speed.svg +34 -1
- package/BookReader/icons/read-aloud.svg +22 -1
- package/BookReader/icons/review.svg +22 -3
- package/BookReader/icons/thumbnails.svg +17 -1
- package/BookReader/icons/voice.svg +1 -1
- package/BookReader/icons/volume-full.svg +22 -1
- package/BookReader/images/BRicons.svg +94 -5
- package/BookReader/images/books_graphic.svg +177 -1
- package/BookReader/images/icon_book.svg +12 -1
- package/BookReader/images/icon_bookmark.svg +12 -1
- package/BookReader/images/icon_gear.svg +14 -1
- package/BookReader/images/icon_hamburger.svg +20 -1
- package/BookReader/images/icon_home.svg +21 -1
- package/BookReader/images/icon_info.svg +11 -1
- package/BookReader/images/icon_one_page.svg +8 -1
- package/BookReader/images/icon_pause.svg +1 -1
- package/BookReader/images/icon_play.svg +1 -1
- package/BookReader/images/icon_playback-rate.svg +15 -1
- package/BookReader/images/icon_search_button.svg +8 -1
- package/BookReader/images/icon_share.svg +9 -1
- package/BookReader/images/icon_skip-ahead.svg +6 -1
- package/BookReader/images/icon_skip-back.svg +13 -2
- package/BookReader/images/icon_speaker.svg +18 -1
- package/BookReader/images/icon_speaker_open.svg +10 -1
- package/BookReader/images/icon_thumbnails.svg +12 -1
- package/BookReader/images/icon_toc.svg +5 -1
- package/BookReader/images/icon_two_pages.svg +9 -1
- package/BookReader/images/marker_chap-off.svg +11 -1
- package/BookReader/images/marker_chap-on.svg +11 -1
- package/BookReader/images/marker_srch-on.svg +11 -1
- package/BookReader/jquery-1.10.1.js +108 -2
- package/BookReader/plugins/plugin.archive_analytics.js +170 -1
- package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
- package/BookReader/plugins/plugin.autoplay.js +163 -1
- package/BookReader/plugins/plugin.autoplay.js.map +1 -1
- package/BookReader/plugins/plugin.chapters.js +333 -1
- package/BookReader/plugins/plugin.chapters.js.map +1 -1
- package/BookReader/plugins/plugin.iframe.js +72 -1
- package/BookReader/plugins/plugin.iframe.js.map +1 -1
- package/BookReader/plugins/plugin.mobile_nav.js +332 -1
- package/BookReader/plugins/plugin.mobile_nav.js.map +1 -1
- package/BookReader/plugins/plugin.resume.js +241 -1
- package/BookReader/plugins/plugin.resume.js.map +1 -1
- package/BookReader/plugins/plugin.search.js +1263 -1
- package/BookReader/plugins/plugin.search.js.map +1 -1
- package/BookReader/plugins/plugin.text_selection.js +839 -1
- package/BookReader/plugins/plugin.text_selection.js.map +1 -1
- package/BookReader/plugins/plugin.tts.js +9114 -2
- package/BookReader/plugins/plugin.tts.js.map +1 -1
- package/BookReader/plugins/plugin.url.js +750 -1
- package/BookReader/plugins/plugin.url.js.map +1 -1
- package/BookReader/plugins/plugin.vendor-fullscreen.js +326 -1
- package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
- package/BookReader/webcomponents-bundle.js +411 -2
- package/BookReader/webcomponents-bundle.js.map +1 -1
- package/CHANGELOG.md +12 -0
- package/package.json +2 -6
- package/src/BookNavigator/bookmarks/bookmarks-provider.js +1 -0
- package/src/BookNavigator/bookmarks/ia-bookmarks.js +3 -0
- package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +1 -1
- package/src/BookNavigator/volumes/volumes-provider.js +39 -9
- package/src/BookReader/Mode1Up.js +10 -2
- package/src/BookReader/ModeThumb.js +13 -6
- package/src/BookReader/Navbar/Navbar.js +0 -29
- package/src/BookReader/options.js +4 -0
- package/src/BookReader.js +14 -34
- package/src/ItemNavigator/ItemNavigator.js +1 -0
- package/src/css/_BRnav.scss +0 -23
- package/src/plugins/plugin.url.js +209 -2
- package/tests/{BookReader → jest/BookReader}/BookModel.test.js +0 -0
- package/tests/{BookReader → jest/BookReader}/BookReaderPublicFunctions.test.js +0 -0
- package/tests/{BookReader → jest/BookReader}/DebugConsole.test.js +0 -0
- package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +0 -0
- package/tests/{BookReader → jest/BookReader}/Mode1UpLit.test.js +0 -0
- package/tests/{BookReader → jest/BookReader}/Mode2Up.test.js +0 -0
- package/tests/{BookReader → jest/BookReader}/ModeSmoothZoom.test.js +0 -0
- package/tests/jest/BookReader/ModeThumb.test.js +71 -0
- package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +0 -0
- package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +0 -0
- package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +0 -0
- package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +0 -0
- package/tests/{BookReader → jest/BookReader}/utils/HTMLDimensionsCacher.test.js +0 -0
- package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +0 -0
- package/tests/{BookReader → jest/BookReader}/utils.test.js +0 -0
- package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +8 -0
- package/tests/{BookReader.test.js → jest/BookReader.test.js} +16 -0
- package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +0 -0
- package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +0 -0
- package/tests/{plugins → jest/plugins}/plugin.chapters.test.js +0 -0
- package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +0 -0
- package/tests/{plugins → jest/plugins}/plugin.mobile_nav.test.js +0 -0
- package/tests/{plugins → jest/plugins}/plugin.resume.test.js +0 -0
- package/tests/{plugins → jest/plugins}/plugin.text_selection.test.js +0 -0
- package/tests/jest/plugins/plugin.url.test.js +306 -0
- package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +0 -0
- package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +0 -0
- package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +0 -0
- package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +0 -0
- package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +0 -0
- package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +0 -0
- package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +0 -0
- package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +0 -0
- package/tests/{plugins → jest/plugins}/tts/utils.test.js +0 -0
- package/tests/{util → jest/util}/browserSniffing.test.js +0 -0
- package/tests/{util → jest/util}/docCookies.test.js +0 -0
- package/tests/{util → jest/util}/strings.test.js +0 -0
- package/tests/{utils.js → jest/utils.js} +0 -0
- package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +6 -6
- package/tests/plugins/plugin.url.test.js +0 -147
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
# 5.0.0-24
|
2
|
+
Fix: book-nav side panel zoom out @mc2
|
3
|
+
Dev: refactor zoom code @mc2
|
4
|
+
|
5
|
+
# 5.0.0-23
|
6
|
+
Fix: Darken scrollbars in Safari @pezvi
|
7
|
+
Fix: Bookmarks service calls when reader is logged in @mc2
|
8
|
+
Dev: Move jest tests into separate directory @cdrini
|
9
|
+
|
10
|
+
# 5.0.0-22
|
11
|
+
- Dev: remove deprecated embed nav view, use standard default @iisa
|
12
|
+
|
1
13
|
# 5.0.0-21
|
2
14
|
- Dev: Toggle view=theater in fullscreen @mc2
|
3
15
|
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@internetarchive/bookreader",
|
3
|
-
"version": "5.0.0-
|
3
|
+
"version": "5.0.0-24-sortingstate",
|
4
4
|
"description": "The Internet Archive BookReader.",
|
5
5
|
"repository": {
|
6
6
|
"type": "git",
|
@@ -94,11 +94,7 @@
|
|
94
94
|
],
|
95
95
|
"roots": [
|
96
96
|
"<rootDir>/src/",
|
97
|
-
"<rootDir>/tests/"
|
98
|
-
],
|
99
|
-
"testPathIgnorePatterns": [
|
100
|
-
"<rootDir>/tests/e2e/",
|
101
|
-
"<rootDir>/tests/karma/"
|
97
|
+
"<rootDir>/tests/jest/"
|
102
98
|
],
|
103
99
|
"coverageDirectory": "<rootDir>/coverage-jest"
|
104
100
|
},
|
@@ -15,6 +15,7 @@ export default class BookmarksProvider {
|
|
15
15
|
this.component = document.createElement('ia-bookmarks');
|
16
16
|
this.component.bookreader = bookreader;
|
17
17
|
this.component.options = boundOptions;
|
18
|
+
this.component.displayMode = this.component.options.displayMode;
|
18
19
|
|
19
20
|
this.bindEvents();
|
20
21
|
|
@@ -121,6 +121,9 @@ class IABookmarks extends LitElement {
|
|
121
121
|
|
122
122
|
setup() {
|
123
123
|
this.api.identifier = this.bookreader.bookId;
|
124
|
+
if (this.displayMode === 'login') {
|
125
|
+
return;
|
126
|
+
}
|
124
127
|
this.fetchBookmarks()
|
125
128
|
.then(() => this.initializeBookmarks())
|
126
129
|
.catch((err) => this.displayMode = 'login');
|
@@ -7,8 +7,16 @@ 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
|
+
};
|
10
15
|
export default class VolumesProvider {
|
11
16
|
|
17
|
+
/**
|
18
|
+
* @param {import('../../BookReader').default} bookreader
|
19
|
+
*/
|
12
20
|
constructor(baseHost, bookreader, optionChange) {
|
13
21
|
this.optionChange = optionChange;
|
14
22
|
this.component = document.createElement("viewable-files");
|
@@ -17,6 +25,9 @@ export default class VolumesProvider {
|
|
17
25
|
this.viewableFiles = Object.keys(files).map(item => files[item]);
|
18
26
|
this.volumeCount = Object.keys(files).length;
|
19
27
|
|
28
|
+
/** @type {import('../../BookReader').default} */
|
29
|
+
this.bookreader = bookreader;
|
30
|
+
|
20
31
|
this.component.subPrefix = bookreader.options.subPrefix || "";
|
21
32
|
this.component.hostUrl = baseHost;
|
22
33
|
this.component.viewableFiles = this.viewableFiles;
|
@@ -25,20 +36,27 @@ export default class VolumesProvider {
|
|
25
36
|
this.label = `Viewable files (${this.volumeCount})`;
|
26
37
|
this.icon = html`${volumesIcon}`;
|
27
38
|
|
28
|
-
|
29
|
-
this.
|
39
|
+
// get sort state from query param
|
40
|
+
this.bookreader.urlPlugin.pullFromAddressBar(location.pathname + location.search);
|
41
|
+
const urlSortValue = this.bookreader.urlPlugin.getUrlParam('sort');
|
42
|
+
if (urlSortValue === sortType.title_asc || urlSortValue === sortType.title_desc) {
|
43
|
+
this.sortOrderBy = urlSortValue;
|
44
|
+
} else {
|
45
|
+
this.sortOrderBy = sortType.default;
|
46
|
+
}
|
47
|
+
this.sortVolumes(this.sortOrderBy);
|
30
48
|
}
|
31
49
|
|
32
50
|
get sortButton() {
|
33
51
|
const sortIcons = {
|
34
|
-
|
52
|
+
default: html`
|
35
53
|
<button class="sort-by neutral-icon" aria-label="Sort volumes in initial order" @click=${() => this.sortVolumes("title_asc")}>${sortNeutralIcon}</button>
|
36
54
|
`,
|
37
55
|
title_asc: html`
|
38
56
|
<button class="sort-by asc-icon" aria-label="Sort volumes in ascending order" @click=${() => this.sortVolumes("title_desc")}>${sortAscIcon}</button>
|
39
57
|
`,
|
40
58
|
title_desc: html`
|
41
|
-
<button class="sort-by desc-icon" aria-label="Sort volumes in descending order" @click=${() => this.sortVolumes("
|
59
|
+
<button class="sort-by desc-icon" aria-label="Sort volumes in descending order" @click=${() => this.sortVolumes("default")}>${sortDescIcon}</button>
|
42
60
|
`,
|
43
61
|
};
|
44
62
|
|
@@ -46,28 +64,40 @@ export default class VolumesProvider {
|
|
46
64
|
}
|
47
65
|
|
48
66
|
/**
|
49
|
-
* @param {'
|
67
|
+
* @param {'default' | 'title_asc' | 'title_desc'} sortByType
|
50
68
|
*/
|
51
69
|
sortVolumes(sortByType) {
|
52
70
|
let sortedFiles = [];
|
53
71
|
|
54
72
|
const files = this.viewableFiles;
|
55
73
|
sortedFiles = files.sort((a, b) => {
|
56
|
-
if (sortByType ===
|
57
|
-
else if (sortByType ===
|
58
|
-
else return b.
|
74
|
+
if (sortByType === sortType.title_asc) return a.title.localeCompare(b.title);
|
75
|
+
else if (sortByType === sortType.title_desc) return b.title.localeCompare(a.title);
|
76
|
+
else return a.orig_sort - b.orig_sort;
|
59
77
|
});
|
60
78
|
|
61
79
|
this.sortOrderBy = sortByType;
|
62
80
|
this.component.viewableFiles = [...sortedFiles];
|
63
81
|
this.actionButton = this.sortButton;
|
82
|
+
|
83
|
+
if (this.sortOrderBy !== sortType.default) {
|
84
|
+
this.bookreader.urlPlugin.setUrlParam('sort', sortByType);
|
85
|
+
} else {
|
86
|
+
this.bookreader.urlPlugin.removeUrlParam('sort');
|
87
|
+
}
|
88
|
+
|
89
|
+
const urlSchema = this.bookreader.urlPlugin.urlSchema;
|
90
|
+
const urlState = this.bookreader.urlPlugin.urlState;
|
91
|
+
this.bookreader.urlPlugin.urlStateToUrlString(urlSchema, urlState);
|
92
|
+
this.bookreader.urlPlugin.pushToAddressBar();
|
93
|
+
|
64
94
|
this.optionChange(this.bookreader);
|
65
95
|
|
66
96
|
this.multipleFilesClicked(sortByType);
|
67
97
|
}
|
68
98
|
|
69
99
|
/**
|
70
|
-
* @param {'
|
100
|
+
* @param {'default' | 'title_asc' | 'title_desc'} orderBy
|
71
101
|
*/
|
72
102
|
multipleFilesClicked(orderBy) {
|
73
103
|
if (!window.archive_analytics) {
|
@@ -79,8 +79,16 @@ export class Mode1Up {
|
|
79
79
|
* @param {'in' | 'out'} direction
|
80
80
|
*/
|
81
81
|
zoom(direction) {
|
82
|
-
|
83
|
-
|
82
|
+
switch (direction) {
|
83
|
+
case 'in':
|
84
|
+
this.mode1UpLit.zoomIn();
|
85
|
+
break;
|
86
|
+
case 'out':
|
87
|
+
this.mode1UpLit.zoomOut();
|
88
|
+
break;
|
89
|
+
default:
|
90
|
+
console.error(`Unsupported direction: ${direction}`);
|
91
|
+
}
|
84
92
|
}
|
85
93
|
|
86
94
|
/**
|
@@ -235,20 +235,27 @@ export class ModeThumb {
|
|
235
235
|
}
|
236
236
|
|
237
237
|
/**
|
238
|
-
* @param {
|
238
|
+
* @param {'in' | 'out'} direction
|
239
239
|
*/
|
240
240
|
zoom(direction) {
|
241
241
|
const oldColumns = this.br.thumbColumns;
|
242
242
|
switch (direction) {
|
243
|
-
case
|
244
|
-
this.br.thumbColumns += 1;
|
245
|
-
break;
|
246
|
-
case 1:
|
243
|
+
case 'in':
|
247
244
|
this.br.thumbColumns -= 1;
|
248
245
|
break;
|
246
|
+
case 'out':
|
247
|
+
this.br.thumbColumns += 1;
|
248
|
+
break;
|
249
|
+
default:
|
250
|
+
console.error(`Unsupported direction: ${direction}`);
|
249
251
|
}
|
250
252
|
|
251
|
-
|
253
|
+
// Limit zoom in/out columns
|
254
|
+
this.br.thumbColumns = clamp(
|
255
|
+
this.br.thumbColumns,
|
256
|
+
this.br.options.thumbMinZoomColumns,
|
257
|
+
this.br.options.thumbMaxZoomColumns
|
258
|
+
);
|
252
259
|
|
253
260
|
if (this.br.thumbColumns != oldColumns) {
|
254
261
|
this.br.displayedRows = []; /* force a gallery redraw */
|
@@ -240,35 +240,6 @@ export class Navbar {
|
|
240
240
|
return this.$nav;
|
241
241
|
}
|
242
242
|
|
243
|
-
/**
|
244
|
-
* Initialize the navigation bar when embedded
|
245
|
-
*/
|
246
|
-
initEmbed() {
|
247
|
-
const { br } = this;
|
248
|
-
// IA-specific
|
249
|
-
const thisLink = (window.location + '')
|
250
|
-
.replace('?ui=embed','')
|
251
|
-
.replace('/stream/', '/details/')
|
252
|
-
.replace('#', '/');
|
253
|
-
const logoHtml = br.showLogo ? `<a class="logo" href="${br.logoURL}" target="_blank"></a>` : '';
|
254
|
-
|
255
|
-
br.refs.$BRfooter = this.$root = $('<div class="BRfooter"></div>');
|
256
|
-
br.refs.$BRnav = this.$nav = $(
|
257
|
-
`<div class="BRnav BRnavEmbed">
|
258
|
-
${logoHtml}
|
259
|
-
<span class="BRembedreturn">
|
260
|
-
<a href="${thisLink}" target="_blank">${br.bookTitle}</a>
|
261
|
-
</span>
|
262
|
-
<span class="BRtoolbarbuttons">
|
263
|
-
<button class="BRicon book_left"></button>
|
264
|
-
<button class="BRicon book_right"></button>
|
265
|
-
<button class="BRicon full"></button>
|
266
|
-
</span>
|
267
|
-
</div>`);
|
268
|
-
this.$root.append(this.$nav);
|
269
|
-
br.refs.$br.append(this.$root);
|
270
|
-
}
|
271
|
-
|
272
243
|
/**
|
273
244
|
* Returns the textual representation of the current page for the navbar
|
274
245
|
* @param {number} index
|
@@ -25,6 +25,10 @@ export const DEFAULT_OPTIONS = {
|
|
25
25
|
thumbMaxLoading: 4,
|
26
26
|
/** spacing between thumbnails */
|
27
27
|
thumbPadding: 10,
|
28
|
+
/** min zoom in columns */
|
29
|
+
thumbMinZoomColumns: 2,
|
30
|
+
/** max zoom out columns */
|
31
|
+
thumbMaxZoomColumns: 8,
|
28
32
|
|
29
33
|
/** @type {number | 'fast' | 'slow'} speed for flip animation */
|
30
34
|
flipSpeed: 'fast',
|
package/src/BookReader.js
CHANGED
@@ -63,6 +63,10 @@ if (location.toString().indexOf('_debugShowConsole=true') != -1) {
|
|
63
63
|
*/
|
64
64
|
export default function BookReader(overrides = {}) {
|
65
65
|
const options = jQuery.extend(true, {}, BookReader.defaultOptions, overrides, BookReader.optionOverrides);
|
66
|
+
|
67
|
+
/** @type {import('./plugins/plugin.url').UrlPlugin | null} */
|
68
|
+
this.urlPlugin = null;
|
69
|
+
|
66
70
|
this.setup(options);
|
67
71
|
}
|
68
72
|
|
@@ -531,15 +535,12 @@ BookReader.prototype.init = function() {
|
|
531
535
|
// Explicitly ensure this.mode exists for initNavbar() below
|
532
536
|
this.mode = params.mode;
|
533
537
|
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
if (this.options.showNavbar) {
|
541
|
-
this.initNavbar();
|
542
|
-
}
|
538
|
+
// Display Navigation
|
539
|
+
if (this.options.showToolbar) {
|
540
|
+
this.initToolbar(this.mode, this.ui); // Build inside of toolbar div
|
541
|
+
}
|
542
|
+
if (this.options.showNavbar) { // default navigation
|
543
|
+
this.initNavbar();
|
543
544
|
}
|
544
545
|
|
545
546
|
// Switch navbar controls on mobile/desktop
|
@@ -832,29 +833,11 @@ BookReader.prototype.drawLeafsThrottled = utils.throttle(
|
|
832
833
|
* @param {number} direction Pass 1 to zoom in, anything else to zoom out
|
833
834
|
*/
|
834
835
|
BookReader.prototype.zoom = function(direction) {
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
this.zoom1up('in');
|
840
|
-
} else {
|
841
|
-
this.zoom1up('out');
|
842
|
-
}
|
843
|
-
break;
|
844
|
-
case this.constMode2up:
|
845
|
-
if (direction == 1) {
|
846
|
-
// XXX other cases
|
847
|
-
this.zoom2up('in');
|
848
|
-
} else {
|
849
|
-
this.zoom2up('out');
|
850
|
-
}
|
851
|
-
break;
|
852
|
-
case this.constModeThumb:
|
853
|
-
// XXX update zoomThumb for named directions
|
854
|
-
this.zoomThumb(direction);
|
855
|
-
break;
|
836
|
+
if (direction == 1) {
|
837
|
+
this.activeMode.zoom('in');
|
838
|
+
} else {
|
839
|
+
this.activeMode.zoom('out');
|
856
840
|
}
|
857
|
-
|
858
841
|
this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
|
859
842
|
return;
|
860
843
|
};
|
@@ -1583,9 +1566,6 @@ BookReader.prototype.updateViewModeButton = Navbar.prototype.updateViewModeButto
|
|
1583
1566
|
exposeOverrideableMethod(Navbar, '_components.navbar', 'updateViewModeButton');
|
1584
1567
|
BookReader.prototype.getNavPageNumString = Navbar.prototype.getNavPageNumString;
|
1585
1568
|
exposeOverrideableMethod(Navbar, '_components.navbar', 'getNavPageNumString');
|
1586
|
-
/** @deprecated */
|
1587
|
-
BookReader.prototype.initEmbedNavbar = Navbar.prototype.initEmbed;
|
1588
|
-
exposeOverrideableMethod(Navbar, '_components.navbar', 'initEmbed', 'initEmbedNavbar');
|
1589
1569
|
/** @deprecated unused */
|
1590
1570
|
BookReader.prototype.getNavPageNumHtml = getNavPageNumHtml;
|
1591
1571
|
/** @deprecated unused outside this file */
|
package/src/css/_BRnav.scss
CHANGED
@@ -127,10 +127,6 @@
|
|
127
127
|
// Default
|
128
128
|
@include brNavDark;
|
129
129
|
|
130
|
-
&.BRnavEmbed {
|
131
|
-
@include brNavLight;
|
132
|
-
}
|
133
|
-
|
134
130
|
/* Full mobile styles */
|
135
131
|
@media (max-width: $brBreakPointMobile) {
|
136
132
|
.BRbodyMobileNavEnabled &,
|
@@ -223,25 +219,6 @@
|
|
223
219
|
flex-direction: row;
|
224
220
|
border-top: 1px solid $controlsBorder;
|
225
221
|
|
226
|
-
&.BRnavEmbed {
|
227
|
-
padding-top: 0;
|
228
|
-
height: auto;
|
229
|
-
align-items: center;
|
230
|
-
|
231
|
-
.BRembedreturn {
|
232
|
-
flex: 1 auto;
|
233
|
-
overflow-x: hidden;
|
234
|
-
}
|
235
|
-
.logo {
|
236
|
-
display: inline-block;
|
237
|
-
width: 25px;
|
238
|
-
height: 25px;
|
239
|
-
margin: 0 10px;
|
240
|
-
background: transparent url(images/icon_home.svg) no-repeat center center;
|
241
|
-
background-size: contain;
|
242
|
-
}
|
243
|
-
}
|
244
|
-
|
245
222
|
/* Theming */
|
246
223
|
|
247
224
|
// Default
|
@@ -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(50);
|
53
|
+
document.title = this.shortTitle(this.bookTitle, 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,3 +196,210 @@ 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
|
+
this.urlSchema = [
|
204
|
+
{ name: 'page', position: 'path', default: 'n0' },
|
205
|
+
{ name: 'mode', position: 'path', default: '2up' },
|
206
|
+
{ name: 'search', position: 'path', deprecated_for: 'q' },
|
207
|
+
{ name: 'q', position: 'query_param' },
|
208
|
+
{ name: 'sort', position: 'query_param' },
|
209
|
+
{ name: 'view', position: 'query_param' },
|
210
|
+
{ name: 'admin', position: 'query_param' },
|
211
|
+
];
|
212
|
+
|
213
|
+
this.urlState = {};
|
214
|
+
this.urlMode = 'hash';
|
215
|
+
this.urlHistoryBasePath = '/';
|
216
|
+
this.urlLocationPollId = null;
|
217
|
+
this.oldLocationHash = null;
|
218
|
+
this.oldUserHash = null;
|
219
|
+
}
|
220
|
+
|
221
|
+
/**
|
222
|
+
* Parse JSON object URL state to string format
|
223
|
+
* Arrange path names in an order that it is positioned on the urlSchema
|
224
|
+
* @param {object} urlState
|
225
|
+
* @returns {string}
|
226
|
+
*/
|
227
|
+
urlStateToUrlString(urlSchema, urlState) {
|
228
|
+
const searchParams = new URLSearchParams();
|
229
|
+
const pathParams = {};
|
230
|
+
|
231
|
+
Object.keys(urlState).forEach(key => {
|
232
|
+
let schema = urlSchema.find(schema => schema.name === key);
|
233
|
+
if (schema?.deprecated_for) {
|
234
|
+
schema = urlSchema.find(schemaKey => schemaKey.name === schema.deprecated_for);
|
235
|
+
}
|
236
|
+
if (schema?.position == 'path') {
|
237
|
+
pathParams[schema?.name] = urlState[key];
|
238
|
+
} else {
|
239
|
+
searchParams.append(schema?.name || key, urlState[key]);
|
240
|
+
}
|
241
|
+
});
|
242
|
+
|
243
|
+
const strPathParams = urlSchema
|
244
|
+
.filter(s => s.position == 'path')
|
245
|
+
.map(schema => pathParams[schema.name] ? `${schema.name}/${pathParams[schema.name]}` : '')
|
246
|
+
.join('/');
|
247
|
+
|
248
|
+
const strStrippedTrailingSlash = `${strPathParams.replace(/\/$/, '')}`;
|
249
|
+
const concatenatedPath = `/${strStrippedTrailingSlash}?${searchParams.toString()}`;
|
250
|
+
return searchParams.toString() ? concatenatedPath : `/${strStrippedTrailingSlash}`;
|
251
|
+
}
|
252
|
+
|
253
|
+
/**
|
254
|
+
* Parse string URL and add it in the current urlState
|
255
|
+
* Example:
|
256
|
+
* /page/n7/mode/2up => {page: 'n7', mode: '2up'}
|
257
|
+
* /page/n7/mode/2up/search/hello => {page: 'n7', mode: '2up', q: 'hello'}
|
258
|
+
* @param {array} urlSchema
|
259
|
+
* @param {string} str
|
260
|
+
* @returns {object}
|
261
|
+
*/
|
262
|
+
urlStringToUrlState(urlSchema, str) {
|
263
|
+
const urlState = {};
|
264
|
+
|
265
|
+
// Fetch searchParams from given {str}
|
266
|
+
// Note: whole URL path is needed for URLSearchParams
|
267
|
+
const urlPath = new URL(str, '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
|
+
urlSchema
|
278
|
+
.filter(schema => schema.position == 'path')
|
279
|
+
.forEach(schema => {
|
280
|
+
if (!urlStrSplitSlashObj[schema.name] && schema.default) {
|
281
|
+
return urlState[schema.name] = schema.default;
|
282
|
+
}
|
283
|
+
const hasPropertyKey = doesKeyExists(urlStrSplitSlashObj, schema.name);
|
284
|
+
const hasDeprecatedKey = doesKeyExists(schema, 'deprecated_for') && hasPropertyKey;
|
285
|
+
|
286
|
+
if (hasDeprecatedKey)
|
287
|
+
return urlState[schema.deprecated_for] = urlStrSplitSlashObj[schema.name];
|
288
|
+
|
289
|
+
if (hasPropertyKey)
|
290
|
+
return urlState[schema.name] = urlStrSplitSlashObj[schema.name];
|
291
|
+
});
|
292
|
+
|
293
|
+
// Add searchParams to urlState
|
294
|
+
// Check if Object value is a Boolean and convert value to Boolean
|
295
|
+
// Otherwise, return Object value
|
296
|
+
const isBoolean = value => value === 'true' || (value === 'false' ? false : value);
|
297
|
+
Object.entries(urlSearchParamsObj).forEach(([key, value]) => {
|
298
|
+
urlState[key] = isBoolean(value);
|
299
|
+
});
|
300
|
+
|
301
|
+
return urlState;
|
302
|
+
}
|
303
|
+
|
304
|
+
/**
|
305
|
+
* Add or update key-value to the urlState
|
306
|
+
* @param {string} key
|
307
|
+
* @param {string} val
|
308
|
+
*/
|
309
|
+
setUrlParam(key, value) {
|
310
|
+
this.urlState[key] = value;
|
311
|
+
|
312
|
+
this.pushToAddressBar();
|
313
|
+
}
|
314
|
+
|
315
|
+
/**
|
316
|
+
* Delete key-value to the urlState
|
317
|
+
* @param {string} key
|
318
|
+
*/
|
319
|
+
removeUrlParam(key) {
|
320
|
+
delete this.urlState[key];
|
321
|
+
|
322
|
+
this.pushToAddressBar();
|
323
|
+
}
|
324
|
+
|
325
|
+
/**
|
326
|
+
* Get key-value from the urlState
|
327
|
+
* @param {string} key
|
328
|
+
* @return {string}
|
329
|
+
*/
|
330
|
+
getUrlParam(key) {
|
331
|
+
return this.urlState[key];
|
332
|
+
}
|
333
|
+
|
334
|
+
/**
|
335
|
+
* Push URL params to addressbar
|
336
|
+
*/
|
337
|
+
pushToAddressBar() {
|
338
|
+
const urlStrPath = this.urlStateToUrlString(this.urlSchema, this.urlState);
|
339
|
+
if (this.urlMode == 'history') {
|
340
|
+
if (window.history && window.history.replaceState) {
|
341
|
+
const newUrlPath = `${this.urlHistoryBasePath}${urlStrPath}`;
|
342
|
+
window.history.replaceState({}, null, newUrlPath);
|
343
|
+
}
|
344
|
+
} else {
|
345
|
+
window.location.replace('#' + urlStrPath);
|
346
|
+
}
|
347
|
+
this.oldLocationHash = urlStrPath;
|
348
|
+
}
|
349
|
+
|
350
|
+
/**
|
351
|
+
* Get the url and check if it has changed
|
352
|
+
* If it was changeed, update the urlState
|
353
|
+
*/
|
354
|
+
listenForHashChanges() {
|
355
|
+
this.oldLocationHash = window.location.hash.substr(1);
|
356
|
+
if (this.urlLocationPollId) {
|
357
|
+
clearInterval(this.urlLocationPollId);
|
358
|
+
this.urlLocationPollId = null;
|
359
|
+
}
|
360
|
+
|
361
|
+
// check if the URL changes
|
362
|
+
const updateHash = () => {
|
363
|
+
const newFragment = window.location.hash.substr(1);
|
364
|
+
const hasFragmentChange = newFragment != this.oldLocationHash;
|
365
|
+
|
366
|
+
if (!hasFragmentChange) { return; }
|
367
|
+
|
368
|
+
this.urlState = this.urlStringToUrlState(newFragment);
|
369
|
+
};
|
370
|
+
this.urlLocationPollId = setInterval(updateHash, 500);
|
371
|
+
}
|
372
|
+
|
373
|
+
/**
|
374
|
+
* Will read either the hash or URL and return the bookreader fragment
|
375
|
+
* @param {string} location
|
376
|
+
* @return {string}
|
377
|
+
*/
|
378
|
+
pullFromAddressBar (location) {
|
379
|
+
const path = this.urlMode === 'history'
|
380
|
+
? location.substr(this.urlHistoryBasePath.length)
|
381
|
+
: location.substr(1);
|
382
|
+
this.urlState = this.urlStringToUrlState(this.urlSchema, path);
|
383
|
+
}
|
384
|
+
}
|
385
|
+
export class BookreaderUrlPlugin extends BookReader {
|
386
|
+
|
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
|
+
|
404
|
+
window.BookReader = BookreaderUrlPlugin;
|
405
|
+
export default BookreaderUrlPlugin;
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|