@internetarchive/bookreader 5.0.0-90 → 5.0.0-92
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 +2 -2
- package/BookReader/ia-bookreader-bundle.js.map +1 -1
- 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 +2 -2
- package/BookReader/plugins/plugin.chapters.js.map +1 -1
- package/BookReader/plugins/plugin.iiif.js +1 -1
- package/BookReader/plugins/plugin.iiif.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/BookReaderDemo/IADemoBr.js +29 -1
- package/BookReaderDemo/ia-multiple-volumes-manifest.js +0 -1
- package/CHANGELOG.md +28 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/BookNavigator/book-navigator.js +5 -2
- package/src/BookNavigator/search/search-provider.js +13 -7
- package/src/BookNavigator/sharing.js +1 -1
- package/src/BookReader/BookModel.js +5 -4
- package/src/BookReader/Toolbar/Toolbar.js +5 -0
- package/src/BookReader/options.js +10 -6
- package/src/BookReader.js +49 -23
- package/src/BookReaderPlugin.js +8 -0
- package/src/plugins/plugin.chapters.js +220 -157
- package/src/plugins/plugin.text_selection.js +19 -1
- package/src/plugins/search/plugin.search.js +330 -376
- package/src/plugins/search/view.js +13 -9
- package/src/plugins/tts/WebTTSEngine.js +67 -41
- package/src/plugins/tts/plugin.tts.js +1 -3
- package/src/plugins/tts/utils.js +13 -0
- package/src/util/browserSniffing.js +11 -1
- package/tests/e2e/helpers/mockSearch.js +1 -1
- package/tests/jest/BookNavigator/book-navigator.test.js +8 -3
- package/tests/jest/BookNavigator/search/search-provider.test.js +16 -4
- package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +1 -1
- package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +70 -0
- package/tests/jest/BookReader.test.js +26 -1
- package/tests/jest/plugins/plugin.chapters.test.js +56 -58
- package/tests/jest/plugins/search/plugin.search.test.js +17 -42
- package/tests/jest/plugins/search/plugin.search.view.test.js +10 -18
- package/tests/jest/plugins/tts/WebTTSEngine.test.js +18 -12
- package/tests/jest/plugins/url/plugin.url.test.js +1 -1
- package/tests/jest/util/browserSniffing.test.js +9 -3
- package/tests/jest/utils.js +4 -1
@@ -45,6 +45,23 @@ BookReader.optionOverrides.imagesBaseURL = '/BookReader/images/';
|
|
45
45
|
const initializeBookReader = (brManifest) => {
|
46
46
|
console.log('initializeBookReader', brManifest);
|
47
47
|
|
48
|
+
const {bookPath, subPrefix} = brManifest.data.brOptions;
|
49
|
+
let path = bookPath;
|
50
|
+
const subPrefixWithSlash = `/${subPrefix}`;
|
51
|
+
if (bookPath.length - bookPath.lastIndexOf(subPrefixWithSlash) == subPrefixWithSlash.length) {
|
52
|
+
path = bookPath.substr(0, bookPath.length - subPrefixWithSlash.length);
|
53
|
+
}
|
54
|
+
|
55
|
+
const searchInsideUrl = '//{{server}}/fulltext/inside.php?' + [
|
56
|
+
'item_id={{bookId|urlencode}}',
|
57
|
+
'doc={{subPrefix|urlencode}}',
|
58
|
+
'q={{query|urlencode}}',
|
59
|
+
// This endpoint doesn't expect the path to be url encoded
|
60
|
+
`path=${encodeURIComponent(path).replace(/%2F/g, '/')}`,
|
61
|
+
'pre_tag={{preTag|urlencode}}',
|
62
|
+
'post_tag={{postTag|urlencode}}',
|
63
|
+
].join('&');
|
64
|
+
|
48
65
|
const options = {
|
49
66
|
el: '#BookReader',
|
50
67
|
/* Url plugin - IA uses History mode for URL */
|
@@ -59,7 +76,6 @@ const initializeBookReader = (brManifest) => {
|
|
59
76
|
enableBookTitleLink: false,
|
60
77
|
bookUrlText: null,
|
61
78
|
startFullscreen: openFullImmersionTheater,
|
62
|
-
initialSearchTerm: searchTerm ? searchTerm : '',
|
63
79
|
// leaving this option commented out bc we change given user agent on archive.org
|
64
80
|
// onePage: { autofit: <?=json_encode($this->ios ? 'width' : 'auto')?> },
|
65
81
|
showToolbar: getFromUrl('options.showToolbar', 'false') === 'true',
|
@@ -70,6 +86,18 @@ const initializeBookReader = (brManifest) => {
|
|
70
86
|
/* End multiple volumes */
|
71
87
|
enableBookmarks: true, // turn this on
|
72
88
|
enableFSLogoShortcut: true,
|
89
|
+
|
90
|
+
// TMP: To be replaced once BookReaderJSIA is updated to provide
|
91
|
+
// these in the right spot.
|
92
|
+
plugins: {
|
93
|
+
search: {
|
94
|
+
enabled: true,
|
95
|
+
initialSearchTerm: searchTerm ? searchTerm : '',
|
96
|
+
searchInsideUrl,
|
97
|
+
preTag: brManifest.data.brOptions.searchInsidePreTag,
|
98
|
+
postTag: brManifest.data.brOptions.searchInsidePostTag,
|
99
|
+
},
|
100
|
+
},
|
73
101
|
};
|
74
102
|
|
75
103
|
// we want to show item as embedded when ?ui=embed is in URI
|
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,31 @@
|
|
1
|
+
# 5.0.0-92
|
2
|
+
- Refactor: Migrate search plugin to BookReaderPlugin system @cdrini
|
3
|
+
- Breaking changes:
|
4
|
+
- Search options are now nested under the plugin (eg `searchInsideUrl` → `options.plugins.search.searchInsideUrl`), and mostly no longer include the `search` prefix; specifically:
|
5
|
+
- `searchInsidePreTag`, `searchInsidePostTag` → `options.plugins.search.preTag` and `postTag`
|
6
|
+
- `searchInsideUrl` is now a `StringWithVars` and supports including `vars` from the root options, e.g. `searchInsideUrl: "https://{{server}}/search_endpoint?q={{query|urlencode}}"` . It also has access to the `preTag` and `postTag` from the options. Note this allows the search inside URL to now be fully customizable from the options, and is no longer hard-coded internally to Internet Archive specific logic.
|
7
|
+
- `initialSearchTerm` → `options.plugins.search.initialSearchTerm`
|
8
|
+
- `searchInsideProtocol` → deprecated in favour of variables in `searchInsideUrl`
|
9
|
+
- Internal search related methods/properties are now nested in the `SearchPlugin` and no longer accessible from the root bookreader object. Notable exception: `br.search` is still made available for convenience.
|
10
|
+
- Moved: `searchResults`, `searchXHR`, `searchView`, `cancelSearchRequest`, `BRSearchCallback`, `updateSearchHilites`, `removeSearchResults`, `removeSearchHilites`, `searchHighlightVisible`
|
11
|
+
|
12
|
+
|
13
|
+
# 5.0.0-91
|
14
|
+
- Refactor: Migrate ChaptersPlugin to BookReaderPlugin system @cdrini
|
15
|
+
- Breaking changes:
|
16
|
+
- Options moved:
|
17
|
+
- `options.olHost` → `options.plugins.chapters.olHost`
|
18
|
+
- `options.enableChaptersPlugin` → `options.plugins.chapters.enabled`
|
19
|
+
- Note `table_of_contents` _did not_ move; that is still root-level
|
20
|
+
- All chapters state/methods moved from `BookReader` to `BookReader.plugins.chapters`. Full list:
|
21
|
+
- eg `BookReader._chapters*` has been moved to `BookReader.plugins.chapters._*`
|
22
|
+
- See https://github.com/internetarchive/bookreader/pull/1381
|
23
|
+
- Feature: TextSelectionPlugin now supports `protected` option @cdrini
|
24
|
+
- Feature: New option for ChaptersPlugin for explicit `openLibraryId` @cdrini
|
25
|
+
- Fix: No longer error when book ends on unviewable page @cdrini
|
26
|
+
- Fix: ReadAloud no longer jumps to top of page every time it scrolls @cdrini
|
27
|
+
- Fix: Fix various ReadAloud issues -- ReadAloud stuttering, not stopping, getting stuck, etc. @cdrini
|
28
|
+
|
1
29
|
# 5.0.0-90
|
2
30
|
- Fix: Festival TTS not working @cdrini
|
3
31
|
|
package/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
 [](https://codecov.io/gh/internetarchive/bookreader)
|
4
4
|
|
5
|
-
**Disclaimer: BookReader v5 is currently in beta. It
|
5
|
+
**Disclaimer: BookReader v5 is currently in beta. It is stable enough for production use and is actively deployed on archive.org. Future updates while in v5 beta may introduce breaking changes to public BookReader APIs, although these will be noted in the CHANGELOG.**
|
6
6
|
|
7
7
|
|
8
8
|
<p align="center">
|
package/package.json
CHANGED
@@ -10,6 +10,7 @@ import BookmarksProvider from './bookmarks/bookmarks-provider.js';
|
|
10
10
|
import SharingProvider from './sharing.js';
|
11
11
|
import ViewableFilesProvider from './viewable-files.js';
|
12
12
|
import iaLogo from './assets/ia-logo.js';
|
13
|
+
/** @typedef {import('@/src/BookReader.js').default} BookReader */
|
13
14
|
|
14
15
|
const events = {
|
15
16
|
menuUpdated: 'menuUpdated',
|
@@ -183,7 +184,9 @@ export class BookNavigator extends LitElement {
|
|
183
184
|
providers.downloads = new DownloadProvider(this.baseProviderConfig);
|
184
185
|
}
|
185
186
|
|
186
|
-
|
187
|
+
// Note plugins will never be null-ish in runtime, but some of the unit tests
|
188
|
+
// stub BR with a nullish value there.
|
189
|
+
if (this.bookreader.options.plugins?.search?.enabled) {
|
187
190
|
providers.search = new SearchProvider({
|
188
191
|
...this.baseProviderConfig,
|
189
192
|
/**
|
@@ -193,7 +196,7 @@ export class BookNavigator extends LitElement {
|
|
193
196
|
*/
|
194
197
|
onProviderChange: (brInstance = null, searchUpdates = {}) => {
|
195
198
|
if (brInstance) {
|
196
|
-
|
199
|
+
/** @type {BookReader} refresh br instance reference */
|
197
200
|
this.bookreader = brInstance;
|
198
201
|
}
|
199
202
|
|
@@ -1,7 +1,10 @@
|
|
1
|
+
// @ts-check
|
1
2
|
import { html, nothing } from 'lit';
|
2
3
|
import '@internetarchive/icon-search/icon-search';
|
3
4
|
import './search-results';
|
4
5
|
/** @typedef {import('@/src/plugins/search/plugin.search.js').SearchInsideMatch} SearchInsideMatch */
|
6
|
+
/** @typedef {import('@/src/plugins/search/plugin.search.js').SearchInsideResults} SearchInsideResults */
|
7
|
+
/** @typedef {import('@/src/BookReader.js').default} BookReader */
|
5
8
|
|
6
9
|
let searchState = {
|
7
10
|
query: '',
|
@@ -99,11 +102,14 @@ export default class SearchProvider {
|
|
99
102
|
this.bookreader.search(searchState.query);
|
100
103
|
}
|
101
104
|
|
105
|
+
/**
|
106
|
+
* @param {CustomEvent<{props: {instance: BookReader, results: SearchInsideResults}}>} event
|
107
|
+
*/
|
102
108
|
onSearchRequestError(event, errorType = 'default') {
|
103
|
-
const { detail: { props
|
104
|
-
const { instance
|
109
|
+
const { detail: { props } } = event;
|
110
|
+
const { instance, results } = props;
|
105
111
|
if (instance) {
|
106
|
-
|
112
|
+
/** @type {BookReader} keep bookreader instance reference up-to-date */
|
107
113
|
this.bookreader = instance;
|
108
114
|
}
|
109
115
|
const errorMessages = {
|
@@ -114,7 +120,7 @@ export default class SearchProvider {
|
|
114
120
|
};
|
115
121
|
|
116
122
|
const messageToShow = errorMessages[errorType] ?? errorMessages.default;
|
117
|
-
searchState.query =
|
123
|
+
searchState.query = results?.q || '';
|
118
124
|
searchState.results = [];
|
119
125
|
searchState.resultsCount = 0;
|
120
126
|
searchState.queryInProgress = false;
|
@@ -137,7 +143,7 @@ export default class SearchProvider {
|
|
137
143
|
}
|
138
144
|
|
139
145
|
searchCanceledInMenu() {
|
140
|
-
this.bookreader
|
146
|
+
this.bookreader._plugins.search.cancelSearchRequest();
|
141
147
|
}
|
142
148
|
|
143
149
|
onSearchResultsCleared() {
|
@@ -149,7 +155,7 @@ export default class SearchProvider {
|
|
149
155
|
errorMessage: '',
|
150
156
|
};
|
151
157
|
this.updateMenu({ openMenu: false });
|
152
|
-
this.bookreader
|
158
|
+
this.bookreader._plugins.search.searchView.clearSearchFieldAndResults(false);
|
153
159
|
if (this.bookreader.urlPlugin) {
|
154
160
|
this.updateSearchInUrl();
|
155
161
|
}
|
@@ -198,6 +204,6 @@ export default class SearchProvider {
|
|
198
204
|
* @param {{ detail: {match: SearchInsideMatch} }} param0
|
199
205
|
*/
|
200
206
|
onSearchResultsClicked({ detail }) {
|
201
|
-
this.bookreader.
|
207
|
+
this.bookreader._plugins.search.jumpToMatch(detail.match.matchIndex);
|
202
208
|
}
|
203
209
|
}
|
@@ -10,7 +10,7 @@ export default class SharingProvider {
|
|
10
10
|
}) {
|
11
11
|
const { identifier, creator, title } = item?.metadata;
|
12
12
|
const creatorToUse = Array.isArray(creator) ? creator[0] : creator;
|
13
|
-
const subPrefix = bookreader.
|
13
|
+
const subPrefix = bookreader.subPrefix || '';
|
14
14
|
const label = `Share this book`;
|
15
15
|
this.icon = html`${iauxShareIcon}`;
|
16
16
|
this.label = label;
|
@@ -274,6 +274,7 @@ export class BookModel {
|
|
274
274
|
* @param {number} [arg0.end] exclusive
|
275
275
|
* @param {boolean} [arg0.combineConsecutiveUnviewables] Yield only first unviewable
|
276
276
|
* of a chunk of unviewable pages instead of each page
|
277
|
+
* @return {Generator<PageModel>}
|
277
278
|
*/
|
278
279
|
* pagesIterator({ start = 0, end = Infinity, combineConsecutiveUnviewables = false } = {}) {
|
279
280
|
start = Math.max(0, start);
|
@@ -483,7 +484,7 @@ export class PageModel {
|
|
483
484
|
* @param {object} [arg0]
|
484
485
|
* @param {boolean} [arg0.combineConsecutiveUnviewables] Whether to only yield the first page
|
485
486
|
* of a series of unviewable pages instead of each page
|
486
|
-
* @return {PageModel|
|
487
|
+
* @return {PageModel|undefined}
|
487
488
|
*/
|
488
489
|
findNext({ combineConsecutiveUnviewables = false } = {}) {
|
489
490
|
return this.book
|
@@ -495,7 +496,7 @@ export class PageModel {
|
|
495
496
|
* @param {object} [arg0]
|
496
497
|
* @param {boolean} [arg0.combineConsecutiveUnviewables] Whether to only yield the first page
|
497
498
|
* of a series of unviewable pages instead of each page
|
498
|
-
* @return {PageModel|
|
499
|
+
* @return {PageModel|undefined}
|
499
500
|
*/
|
500
501
|
findPrev({ combineConsecutiveUnviewables = false } = {}) {
|
501
502
|
if (this.index == 0) return undefined;
|
@@ -518,7 +519,7 @@ export class PageModel {
|
|
518
519
|
* @param {object} [arg0]
|
519
520
|
* @param {boolean} [arg0.combineConsecutiveUnviewables] Whether to only yield the first page
|
520
521
|
* of a series of unviewable pages instead of each page
|
521
|
-
* @return {PageModel|
|
522
|
+
* @return {PageModel|undefined}
|
522
523
|
*/
|
523
524
|
findLeft({ combineConsecutiveUnviewables = false } = {}) {
|
524
525
|
return this.book.pageProgression === 'lr' ? this.findPrev({ combineConsecutiveUnviewables }) : this.findNext({ combineConsecutiveUnviewables });
|
@@ -528,7 +529,7 @@ export class PageModel {
|
|
528
529
|
* @param {object} [arg0]
|
529
530
|
* @param {boolean} [arg0.combineConsecutiveUnviewables] Whether to only yield the first page
|
530
531
|
* of a series of unviewable pages instead of each page
|
531
|
-
* @return {PageModel|
|
532
|
+
* @return {PageModel|undefined}
|
532
533
|
*/
|
533
534
|
findRight({ combineConsecutiveUnviewables = false } = {}) {
|
534
535
|
return this.book.pageProgression === 'lr' ? this.findNext({ combineConsecutiveUnviewables }) : this.findPrev({ combineConsecutiveUnviewables });
|
@@ -59,6 +59,11 @@ export class Toolbar {
|
|
59
59
|
$titleSectionEl.append(br.bookUrlText || br.bookTitle);
|
60
60
|
}
|
61
61
|
|
62
|
+
// Call _bindNavigationHandlers on the plugins
|
63
|
+
for (const plugin of Object.values(br._plugins)) {
|
64
|
+
plugin._configureToolbar(br.refs.$BRtoolbar);
|
65
|
+
}
|
66
|
+
|
62
67
|
// const $hamburger = br.refs.$BRtoolbar.find('BRtoolbarHamburger');
|
63
68
|
return br.refs.$BRtoolbar;
|
64
69
|
}
|
@@ -141,17 +141,21 @@ export const DEFAULT_OPTIONS = {
|
|
141
141
|
* but going forward we'll keep them here.
|
142
142
|
**/
|
143
143
|
plugins: {
|
144
|
-
/** @type {import('../plugins/plugin.archive_analytics.js').ArchiveAnalyticsPlugin['options']}*/
|
144
|
+
/** @type {Partial<import('../plugins/plugin.archive_analytics.js').ArchiveAnalyticsPlugin['options'>]}*/
|
145
145
|
archiveAnalytics: null,
|
146
|
-
/** @type {import('../plugins/plugin.autoplay.js').AutoplayPlugin['options']}*/
|
146
|
+
/** @type {Partial<import('../plugins/plugin.autoplay.js').AutoplayPlugin['options'>]}*/
|
147
147
|
autoplay: null,
|
148
|
-
/** @type {import('../plugins/plugin.
|
148
|
+
/** @type {Partial<import('../plugins/plugin.chapters.js').ChaptersPlugin['options']>} */
|
149
|
+
chapters: null,
|
150
|
+
/** @type {Partial<import('../plugins/plugin.iiif.js').IiifPlugin['options']>} */
|
149
151
|
iiif: null,
|
150
|
-
/** @type {import('../plugins/plugin.resume.js').ResumePlugin['options']} */
|
152
|
+
/** @type {Partial<import('../plugins/plugin.resume.js').ResumePlugin['options']>} */
|
151
153
|
resume: null,
|
152
|
-
/** @type {import('../plugins/plugin.
|
154
|
+
/** @type {Partial<import('../plugins/search/plugin.search.js').SearchPlugin['options']>} */
|
155
|
+
search: null,
|
156
|
+
/** @type {Partial<import('../plugins/plugin.text_selection.js').TextSelectionPlugin['options']>} */
|
153
157
|
textSelection: null,
|
154
|
-
/** @type {import('../plugins/tts/plugin.tts.js').TtsPlugin['options']} */
|
158
|
+
/** @type {Partial<import('../plugins/tts/plugin.tts.js').TtsPlugin['options']>} */
|
155
159
|
tts: null,
|
156
160
|
},
|
157
161
|
|
package/src/BookReader.js
CHANGED
@@ -44,7 +44,7 @@ import { NAMED_REDUCE_SETS } from './BookReader/ReduceSet';
|
|
44
44
|
|
45
45
|
/**
|
46
46
|
* BookReader
|
47
|
-
* @param {BookReaderOptions}
|
47
|
+
* @param {Partial<BookReaderOptions>} overrides
|
48
48
|
* TODO document all options properties
|
49
49
|
* @constructor
|
50
50
|
*/
|
@@ -70,8 +70,12 @@ BookReader.PLUGINS = {
|
|
70
70
|
archiveAnalytics: null,
|
71
71
|
/** @type {typeof import('./plugins/plugin.autoplay.js').AutoplayPlugin | null}*/
|
72
72
|
autoplay: null,
|
73
|
+
/** @type {typeof import('./plugins/plugin.chapters.js').ChaptersPlugin | null}*/
|
74
|
+
chapters: null,
|
73
75
|
/** @type {typeof import('./plugins/plugin.resume.js').ResumePlugin | null}*/
|
74
76
|
resume: null,
|
77
|
+
/** @type {typeof import('./plugins/search/plugin.search.js').SearchPlugin | null}*/
|
78
|
+
search: null,
|
75
79
|
/** @type {typeof import('./plugins/plugin.text_selection.js').TextSelectionPlugin | null}*/
|
76
80
|
textSelection: null,
|
77
81
|
/** @type {typeof import('./plugins/tts/plugin.tts.js').TtsPlugin | null}*/
|
@@ -80,7 +84,7 @@ BookReader.PLUGINS = {
|
|
80
84
|
|
81
85
|
/**
|
82
86
|
* @param {string} pluginName
|
83
|
-
* @param {
|
87
|
+
* @param {new (...args: any[]) => import('./BookReaderPlugin.js').BookReaderPlugin} plugin
|
84
88
|
*/
|
85
89
|
BookReader.registerPlugin = function(pluginName, plugin) {
|
86
90
|
if (BookReader.PLUGINS[pluginName]) {
|
@@ -116,10 +120,25 @@ BookReader.prototype.setup = function(options) {
|
|
116
120
|
// Store the options used to setup bookreader
|
117
121
|
this.options = options;
|
118
122
|
|
123
|
+
/** @type {import('@/src/BookNavigator/book-navigator.js').BookNavigator} */
|
124
|
+
this.shell;
|
125
|
+
|
126
|
+
// Base server used by some api calls
|
127
|
+
/** @deprecated */
|
128
|
+
this.bookId = options.bookId;
|
129
|
+
/** @deprecated */
|
130
|
+
this.server = options.server;
|
131
|
+
/** @deprecated */
|
132
|
+
this.subPrefix = options.subPrefix;
|
133
|
+
/** @deprecated */
|
134
|
+
this.bookPath = options.bookPath;
|
135
|
+
|
119
136
|
// Construct the usual plugins first to get type hints
|
120
137
|
this._plugins = {
|
121
138
|
archiveAnalytics: BookReader.PLUGINS.archiveAnalytics ? new BookReader.PLUGINS.archiveAnalytics(this) : null,
|
122
139
|
autoplay: BookReader.PLUGINS.autoplay ? new BookReader.PLUGINS.autoplay(this) : null,
|
140
|
+
chapters: BookReader.PLUGINS.chapters ? new BookReader.PLUGINS.chapters(this) : null,
|
141
|
+
search: BookReader.PLUGINS.search ? new BookReader.PLUGINS.search(this) : null,
|
123
142
|
resume: BookReader.PLUGINS.resume ? new BookReader.PLUGINS.resume(this) : null,
|
124
143
|
textSelection: BookReader.PLUGINS.textSelection ? new BookReader.PLUGINS.textSelection(this) : null,
|
125
144
|
tts: BookReader.PLUGINS.tts ? new BookReader.PLUGINS.tts(this) : null,
|
@@ -148,12 +167,14 @@ BookReader.prototype.setup = function(options) {
|
|
148
167
|
}
|
149
168
|
}
|
150
169
|
|
170
|
+
if (this._plugins.search?.options.enabled) {
|
171
|
+
// Expose the search method for convenience / backward compat
|
172
|
+
this.search = this._plugins.search.search.bind(this._plugins.search);
|
173
|
+
}
|
174
|
+
|
151
175
|
/** @type {number} @deprecated some past iterations set this */
|
152
176
|
this.numLeafs = undefined;
|
153
177
|
|
154
|
-
/** Overridden by plugin.search.js */
|
155
|
-
this.enableSearch = false;
|
156
|
-
|
157
178
|
/**
|
158
179
|
* Store viewModeOrder states
|
159
180
|
* @var {boolean}
|
@@ -441,23 +462,24 @@ BookReader.prototype.initParams = function() {
|
|
441
462
|
}
|
442
463
|
|
443
464
|
// Check for Search plugin
|
444
|
-
if (this.options.
|
465
|
+
if (this._plugins.search?.options.enabled) {
|
466
|
+
const sp = this._plugins.search;
|
445
467
|
// Go to first result only if no default or URL page
|
446
|
-
|
468
|
+
sp.options.goToFirstResult = !params.pageFound;
|
447
469
|
|
448
470
|
// If initialSearchTerm not set
|
449
|
-
if (!
|
471
|
+
if (!sp.initialSearchTerm) {
|
450
472
|
// Look for any term in URL
|
451
473
|
if (params.search) {
|
452
474
|
// Old style: /search/[term]
|
453
|
-
|
454
|
-
|
475
|
+
sp.options.initialSearchTerm = params.search;
|
476
|
+
sp.searchTerm = params.search;
|
455
477
|
} else {
|
456
478
|
// If we have a query string: q=[term]
|
457
479
|
const searchParams = new URLSearchParams(this.readQueryString());
|
458
480
|
const searchTerm = searchParams.get('q');
|
459
481
|
if (searchTerm) {
|
460
|
-
|
482
|
+
sp.options.initialSearchTerm = utils.decodeURIComponentPlus(searchTerm);
|
461
483
|
}
|
462
484
|
}
|
463
485
|
}
|
@@ -638,7 +660,7 @@ BookReader.prototype.init = function() {
|
|
638
660
|
}
|
639
661
|
|
640
662
|
// If not searching, set to allow on-going fragment changes
|
641
|
-
if (!this.options.initialSearchTerm) {
|
663
|
+
if (!this._plugins.search?.options.initialSearchTerm) {
|
642
664
|
this.suppressFragmentChange = false;
|
643
665
|
}
|
644
666
|
|
@@ -1052,16 +1074,20 @@ BookReader.prototype._isIndexDisplayed = function(index) {
|
|
1052
1074
|
BookReader.prototype.jumpToIndex = function(index, pageX, pageY, noAnimate) {
|
1053
1075
|
// Don't jump into specific unviewable page
|
1054
1076
|
const page = this.book.getPage(index);
|
1077
|
+
|
1055
1078
|
if (!page.isViewable && page.unviewablesStart != page.index) {
|
1056
1079
|
// If already in unviewable range, jump to end of that range
|
1057
1080
|
const alreadyInPreview = this._isIndexDisplayed(page.unviewablesStart);
|
1058
|
-
const newIndex = alreadyInPreview ? page.findNext({ combineConsecutiveUnviewables: true })
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1081
|
+
const newIndex = alreadyInPreview ? page.findNext({ combineConsecutiveUnviewables: true })?.index : page.unviewablesStart;
|
1082
|
+
// Rare, but a book can end on an unviewable page, so this could be undefined
|
1083
|
+
if (typeof newIndex !== 'undefined') {
|
1084
|
+
this.jumpToIndex(newIndex, pageX, pageY, noAnimate);
|
1085
|
+
}
|
1086
|
+
} else {
|
1087
|
+
this.trigger(BookReader.eventNames.stop);
|
1063
1088
|
|
1064
|
-
|
1089
|
+
this.activeMode.jumpToIndex(index, pageX, pageY, noAnimate);
|
1090
|
+
}
|
1065
1091
|
};
|
1066
1092
|
|
1067
1093
|
/**
|
@@ -1298,7 +1324,7 @@ BookReader.prototype.updateFirstIndex = function(
|
|
1298
1324
|
// If there's an initial search we stop suppressing global URL changes
|
1299
1325
|
// when local suppression ends
|
1300
1326
|
// This seems to correctly handle multiple calls during mode/1up
|
1301
|
-
if (this.options.initialSearchTerm && !suppressFragmentChange) {
|
1327
|
+
if (this._plugins.search.options.initialSearchTerm && !suppressFragmentChange) {
|
1302
1328
|
this.suppressFragmentChange = false;
|
1303
1329
|
}
|
1304
1330
|
|
@@ -1627,8 +1653,8 @@ BookReader.prototype.updateFromParams = function(params) {
|
|
1627
1653
|
// process /search
|
1628
1654
|
// @deprecated for urlMode 'history'
|
1629
1655
|
// Continues to work for urlMode 'hash'
|
1630
|
-
if (this.
|
1631
|
-
if (this.searchTerm !== params.search) {
|
1656
|
+
if (this._plugins.search?.enabled && 'undefined' != typeof(params.search)) {
|
1657
|
+
if (this._plugins.search.searchTerm !== params.search) {
|
1632
1658
|
this.$('.BRsearchInput').val(params.search);
|
1633
1659
|
}
|
1634
1660
|
}
|
@@ -1832,8 +1858,8 @@ BookReader.prototype.paramsFromCurrent = function() {
|
|
1832
1858
|
params.view = fullscreenView;
|
1833
1859
|
}
|
1834
1860
|
// Search
|
1835
|
-
if (this.
|
1836
|
-
params.search = this.searchTerm;
|
1861
|
+
if (this._plugins.search?.enabled) {
|
1862
|
+
params.search = this._plugins.search.searchTerm;
|
1837
1863
|
}
|
1838
1864
|
|
1839
1865
|
return params;
|
package/src/BookReaderPlugin.js
CHANGED
@@ -34,6 +34,14 @@ export class BookReaderPlugin {
|
|
34
34
|
_configurePageContainer(pageContainer) {
|
35
35
|
}
|
36
36
|
|
37
|
+
/**
|
38
|
+
* @abstract
|
39
|
+
* @protected
|
40
|
+
* @param {JQuery<HTMLElement>} $toolbarElement
|
41
|
+
*/
|
42
|
+
_configureToolbar($toolbarElement) {
|
43
|
+
}
|
44
|
+
|
37
45
|
/** @abstract @protected */
|
38
46
|
_bindNavigationHandlers() {}
|
39
47
|
|