@internetarchive/bookreader 5.0.0-91 → 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 +12 -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/Toolbar/Toolbar.js +5 -0
- package/src/BookReader/options.js +9 -7
- package/src/BookReader.js +31 -15
- package/src/BookReaderPlugin.js +8 -0
- package/src/plugins/plugin.text_selection.js +3 -1
- package/src/plugins/search/plugin.search.js +330 -376
- package/src/plugins/search/view.js +13 -9
- 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.test.js +26 -1
- 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/url/plugin.url.test.js +1 -1
@@ -1,13 +1,17 @@
|
|
1
|
+
// @ts-check
|
2
|
+
/** @typedef {import('@/src/BookReader.js').default} BookReader */
|
3
|
+
|
1
4
|
class SearchView {
|
2
5
|
/**
|
3
6
|
* @param {object} params
|
4
|
-
* @param {
|
5
|
-
* @param {function} params.
|
7
|
+
* @param {BookReader} params.br The BookReader instance
|
8
|
+
* @param {function} params.searchCancelledCallback callback when a user wants to cancel search
|
6
9
|
*
|
7
10
|
* @event BookReader:SearchResultsCleared - when the search results nav gets cleared
|
8
11
|
* @event BookReader:ToggleSearchMenu - when search results menu should toggle
|
9
12
|
*/
|
10
13
|
constructor({ br, searchCancelledCallback = () => {} }) {
|
14
|
+
/** @type {BookReader} */
|
11
15
|
this.br = br;
|
12
16
|
this.matches = [];
|
13
17
|
this.cacheDOMElements();
|
@@ -40,7 +44,7 @@ class SearchView {
|
|
40
44
|
}
|
41
45
|
|
42
46
|
clearSearchFieldAndResults(dispatchEventWhenComplete = true) {
|
43
|
-
this.br.removeSearchResults();
|
47
|
+
this.br._plugins.search.removeSearchResults();
|
44
48
|
this.removeResultPins();
|
45
49
|
this.emptyMatches();
|
46
50
|
this.setQuery('');
|
@@ -269,15 +273,15 @@ class SearchView {
|
|
269
273
|
$(event.target).addClass('front');
|
270
274
|
})
|
271
275
|
.on("mouseleave", (event) => $(event.target).removeClass('front'))
|
272
|
-
.on("click", () => { this.br.
|
276
|
+
.on("click", () => { this.br._plugins.search.jumpToMatch(match.matchIndex); });
|
273
277
|
});
|
274
278
|
}
|
275
279
|
|
276
280
|
/**
|
277
|
-
* @param {boolean}
|
281
|
+
* @param {boolean} show
|
278
282
|
*/
|
279
|
-
toggleSearchPending(
|
280
|
-
if (
|
283
|
+
toggleSearchPending(show = false) {
|
284
|
+
if (show) {
|
281
285
|
this.br.showProgressPopup("Search results will appear below...", () => this.progressPopupClosed());
|
282
286
|
}
|
283
287
|
else {
|
@@ -375,11 +379,11 @@ class SearchView {
|
|
375
379
|
|
376
380
|
handleSearchStarted() {
|
377
381
|
this.emptyMatches();
|
378
|
-
this.br.removeSearchHilites();
|
382
|
+
this.br._plugins.search.removeSearchHilites();
|
379
383
|
this.removeResultPins();
|
380
384
|
this.toggleSearchPending(true);
|
381
385
|
this.teardownSearchNavigation();
|
382
|
-
this.setQuery(this.br.searchTerm);
|
386
|
+
this.setQuery(this.br._plugins.search.searchTerm);
|
383
387
|
}
|
384
388
|
|
385
389
|
/**
|
@@ -2,7 +2,7 @@ export const TEST_TEXT_FOUND = 'theory';
|
|
2
2
|
export const TEST_TEXT_NOT_FOUND = 'HopefullyNotFoundLongWord';
|
3
3
|
export const PAGE_FIRST_RESULT = 30;
|
4
4
|
|
5
|
-
export const SEARCH_INSIDE_URL_RE =
|
5
|
+
export const SEARCH_INSIDE_URL_RE = /\/fulltext\/inside\.php/;
|
6
6
|
|
7
7
|
/** Mock response for a matching search term. */
|
8
8
|
export function mockResponseFound(req, res) {
|
@@ -164,14 +164,20 @@ describe('<book-navigator>', () => {
|
|
164
164
|
expect(el.menuProviders.visualAdjustments).toBeInstanceOf(VisualAdjustmentsProvider);
|
165
165
|
});
|
166
166
|
describe('Loading Sub Menus By Plugin Flags', () => {
|
167
|
-
test('Search: uses `
|
167
|
+
test('Search: uses `enabled` flag', async() => {
|
168
168
|
const el = fixtureSync(container());
|
169
169
|
const $brContainer = document.createElement('div');
|
170
170
|
const brStub = {
|
171
171
|
resize: sinon.fake(),
|
172
172
|
currentIndex: sinon.fake(),
|
173
173
|
jumpToIndex: sinon.fake(),
|
174
|
-
options: {
|
174
|
+
options: {
|
175
|
+
plugins: {
|
176
|
+
search: {
|
177
|
+
enabled: true,
|
178
|
+
},
|
179
|
+
},
|
180
|
+
},
|
175
181
|
refs: {
|
176
182
|
$brContainer,
|
177
183
|
},
|
@@ -346,7 +352,6 @@ describe('<book-navigator>', () => {
|
|
346
352
|
|
347
353
|
let sidePanelConfig = {};
|
348
354
|
el.addEventListener('updateSideMenu', (e) => {
|
349
|
-
console.log();
|
350
355
|
sidePanelConfig = e.detail;
|
351
356
|
});
|
352
357
|
const toggleSearchMenuEvent = new Event('BookReader:ToggleSearchMenu');
|
@@ -88,7 +88,11 @@ describe('Search Provider', () => {
|
|
88
88
|
onProviderChange: sinon.fake(),
|
89
89
|
bookreader: {
|
90
90
|
leafNumToIndex: sinon.fake(),
|
91
|
-
|
91
|
+
_plugins: {
|
92
|
+
search: {
|
93
|
+
jumpToMatch: sinon.fake(),
|
94
|
+
},
|
95
|
+
},
|
92
96
|
},
|
93
97
|
});
|
94
98
|
|
@@ -100,7 +104,7 @@ describe('Search Provider', () => {
|
|
100
104
|
{ detail: searchResultStub }),
|
101
105
|
);
|
102
106
|
|
103
|
-
expect(provider.bookreader.
|
107
|
+
expect(provider.bookreader._plugins.search.jumpToMatch.callCount).toEqual(1);
|
104
108
|
});
|
105
109
|
test('update url when search is cancelled or input cleared', async() => {
|
106
110
|
const urlPluginMock = {
|
@@ -111,7 +115,11 @@ describe('Search Provider', () => {
|
|
111
115
|
onProviderChange: sinon.fake(),
|
112
116
|
bookreader: {
|
113
117
|
leafNumToIndex: sinon.fake(),
|
114
|
-
|
118
|
+
_plugins: {
|
119
|
+
search: {
|
120
|
+
jumpToMatch: sinon.fake(),
|
121
|
+
},
|
122
|
+
},
|
115
123
|
urlPlugin: urlPluginMock,
|
116
124
|
},
|
117
125
|
});
|
@@ -145,7 +153,11 @@ describe('Search Provider', () => {
|
|
145
153
|
onProviderChange: sinon.fake(),
|
146
154
|
bookreader: {
|
147
155
|
leafNumToIndex: sinon.fake(),
|
148
|
-
|
156
|
+
_plugins: {
|
157
|
+
search: {
|
158
|
+
jumpToMatch: sinon.fake(),
|
159
|
+
},
|
160
|
+
},
|
149
161
|
urlPlugin: urlPluginMock,
|
150
162
|
search: sinon.fake(),
|
151
163
|
},
|
@@ -7,7 +7,12 @@ import '@/src/plugins/url/plugin.url.js';
|
|
7
7
|
let br;
|
8
8
|
beforeAll(() => {
|
9
9
|
document.body.innerHTML = '<div id="BookReader">';
|
10
|
-
br = new BookReader(
|
10
|
+
br = new BookReader({
|
11
|
+
server: '',
|
12
|
+
bookId: '',
|
13
|
+
subPrefix: '',
|
14
|
+
bookPath: '',
|
15
|
+
});
|
11
16
|
});
|
12
17
|
|
13
18
|
afterEach(() => {
|
@@ -91,6 +96,26 @@ test('calls switchMode with init option when init called', () => {
|
|
91
96
|
.toHaveProperty('init', true);
|
92
97
|
});
|
93
98
|
|
99
|
+
test('has added BR property: server', () => {
|
100
|
+
expect(br).toHaveProperty('server');
|
101
|
+
expect(br.server).toBeDefined();
|
102
|
+
});
|
103
|
+
|
104
|
+
test('has added BR property: bookId', () => {
|
105
|
+
expect(br).toHaveProperty('bookId');
|
106
|
+
expect(br.bookId).toBeDefined();
|
107
|
+
});
|
108
|
+
|
109
|
+
test('has added BR property: subPrefix', () => {
|
110
|
+
expect(br).toHaveProperty('subPrefix');
|
111
|
+
expect(br.subPrefix).toBeDefined();
|
112
|
+
});
|
113
|
+
|
114
|
+
test('has added BR property: bookPath', () => {
|
115
|
+
expect(br).toHaveProperty('bookPath');
|
116
|
+
expect(br.bookPath).toBeDefined();
|
117
|
+
});
|
118
|
+
|
94
119
|
test('has suppressFragmentChange true when init with no input', () => {
|
95
120
|
br._plugins.resume.getResumeValue = jest.fn(() => null);
|
96
121
|
br.urlReadFragment = jest.fn(() => '');
|
@@ -5,6 +5,7 @@ import { DUMMY_RESULTS } from './utils.js';
|
|
5
5
|
|
6
6
|
jest.mock('@/src/plugins/search/view.js');
|
7
7
|
|
8
|
+
/** @type {BookReader} */
|
8
9
|
let br;
|
9
10
|
const namespace = 'BookReader:';
|
10
11
|
const triggeredEvents = () => {
|
@@ -23,10 +24,14 @@ beforeEach(() => {
|
|
23
24
|
|
24
25
|
$.fn.trigger = jest.fn();
|
25
26
|
document.body.innerHTML = '<div id="BookReader">';
|
26
|
-
br = new BookReader(
|
27
|
+
br = new BookReader({
|
28
|
+
server: 'foo.bar.com',
|
29
|
+
bookPath: '/13/items/foo/foobar',
|
30
|
+
subPrefix: '/foobar',
|
31
|
+
});
|
27
32
|
br.initToolbar = jest.fn();
|
28
33
|
br.showProgressPopup = jest.fn();
|
29
|
-
br.searchXHR = jest.fn();
|
34
|
+
br._plugins.search.searchXHR = jest.fn();
|
30
35
|
});
|
31
36
|
|
32
37
|
afterEach(() => {
|
@@ -34,54 +39,24 @@ afterEach(() => {
|
|
34
39
|
});
|
35
40
|
|
36
41
|
describe('Plugin: Search', () => {
|
37
|
-
test('has option flag', () => {
|
38
|
-
expect(BookReader.defaultOptions.enableSearch).toEqual(true);
|
39
|
-
});
|
40
|
-
|
41
|
-
test('has added BR property: server', () => {
|
42
|
-
expect(br).toHaveProperty('server');
|
43
|
-
expect(br.server).toBeTruthy();
|
44
|
-
});
|
45
|
-
|
46
|
-
test('has added BR property: bookId', () => {
|
47
|
-
expect(br).toHaveProperty('bookId');
|
48
|
-
expect(br.bookId).toBeFalsy();
|
49
|
-
});
|
50
|
-
|
51
|
-
test('has added BR property: subPrefix', () => {
|
52
|
-
expect(br).toHaveProperty('subPrefix');
|
53
|
-
expect(br.subPrefix).toBeFalsy();
|
54
|
-
});
|
55
|
-
|
56
|
-
test('has added BR property: bookPath', () => {
|
57
|
-
expect(br).toHaveProperty('bookPath');
|
58
|
-
expect(br.bookPath).toBeFalsy();
|
59
|
-
});
|
60
|
-
|
61
|
-
test('has added BR property: searchInsideUrl', () => {
|
62
|
-
expect(br).toHaveProperty('searchInsideUrl');
|
63
|
-
expect(br.searchInsideUrl).toBeTruthy();
|
64
|
-
});
|
65
|
-
|
66
|
-
test('has added BR property: initialSearchTerm', () => {
|
67
|
-
expect(br.options).toHaveProperty('initialSearchTerm');
|
68
|
-
expect(br.options.initialSearchTerm).toBeFalsy();
|
69
|
-
});
|
70
|
-
|
71
42
|
test('On init, it loads the toolbar', () => {
|
72
43
|
br.init();
|
73
44
|
expect(br.initToolbar).toHaveBeenCalled();
|
74
45
|
});
|
75
46
|
|
47
|
+
test('Constructs SearchView', () => {
|
48
|
+
expect(br._plugins.search.searchView).toBeDefined();
|
49
|
+
});
|
50
|
+
|
76
51
|
test('On init, it will run a search if given `options.initialSearchTerm`', () => {
|
77
|
-
br.search = jest.fn();
|
78
|
-
br.options.initialSearchTerm = 'foo';
|
52
|
+
br._plugins.search.search = jest.fn();
|
53
|
+
br.options.plugins.search.initialSearchTerm = 'foo';
|
79
54
|
br.init();
|
80
55
|
|
81
|
-
expect(br.search).toHaveBeenCalled();
|
82
|
-
expect(br.search.mock.calls[0][1])
|
56
|
+
expect(br._plugins.search.search).toHaveBeenCalled();
|
57
|
+
expect(br._plugins.search.search.mock.calls[0][1])
|
83
58
|
.toHaveProperty('goToFirstResult', true);
|
84
|
-
expect(br.search.mock.calls[0][1])
|
59
|
+
expect(br._plugins.search.search.mock.calls[0][1])
|
85
60
|
.toHaveProperty('suppressFragmentChange', false);
|
86
61
|
});
|
87
62
|
|
@@ -100,7 +75,7 @@ describe('Plugin: Search', () => {
|
|
100
75
|
test('SearchStarted event fires and should go to first result', () => {
|
101
76
|
br.init();
|
102
77
|
br.search('foo', { goToFirstResult: true});
|
103
|
-
expect(br.options.goToFirstResult).toBeTruthy();
|
78
|
+
expect(br._plugins.search.options.goToFirstResult).toBeTruthy();
|
104
79
|
});
|
105
80
|
|
106
81
|
test('SearchCallback event fires when AJAX search returns results', async () => {
|
@@ -1,9 +1,10 @@
|
|
1
|
-
|
1
|
+
// @ts-check
|
2
2
|
import BookReader from '@/src/BookReader.js';
|
3
3
|
import '@/src/plugins/search/plugin.search.js';
|
4
4
|
import { marshallSearchResults } from '@/src/plugins/search/utils.js';
|
5
5
|
import '@/src/plugins/search/view.js';
|
6
6
|
|
7
|
+
/** @type {BookReader} */
|
7
8
|
let br;
|
8
9
|
const namespace = 'BookReader:';
|
9
10
|
const results = {
|
@@ -71,31 +72,22 @@ afterEach(() => {
|
|
71
72
|
});
|
72
73
|
|
73
74
|
describe('View: Plugin: Search', () => {
|
74
|
-
test('When search runs, the view gets created.', () => {
|
75
|
-
br.search = jest.fn();
|
76
|
-
br.options.initialSearchTerm = 'foo';
|
77
|
-
br.init();
|
78
|
-
|
79
|
-
expect(br.searchView).toBeDefined();
|
80
|
-
expect(br.searchView.handleSearchCallback).toBeDefined();
|
81
|
-
});
|
82
|
-
|
83
75
|
describe("Search results navigation bar", () => {
|
84
76
|
test('Search Results callback creates the results nav', () => {
|
85
77
|
br.init();
|
86
78
|
const event = new CustomEvent(`${namespace}SearchCallback`);
|
87
79
|
const options = { goToFirstResult: false };
|
88
80
|
|
89
|
-
expect(br.searchView.dom.searchNavigation).toBeUndefined();
|
81
|
+
expect(br._plugins.search.searchView.dom.searchNavigation).toBeUndefined();
|
90
82
|
|
91
|
-
br.searchView.handleSearchCallback(event, { results, options});
|
92
|
-
expect(br.searchView.dom.searchNavigation).toBeDefined();
|
83
|
+
br._plugins.search.searchView.handleSearchCallback(event, { results, options});
|
84
|
+
expect(br._plugins.search.searchView.dom.searchNavigation).toBeDefined();
|
93
85
|
});
|
94
86
|
test('has controls', () => {
|
95
87
|
br.init();
|
96
88
|
const event = new CustomEvent(`${namespace}SearchCallback`);
|
97
89
|
const options = { goToFirstResult: false };
|
98
|
-
br.searchView.handleSearchCallback(event, { results, options});
|
90
|
+
br._plugins.search.searchView.handleSearchCallback(event, { results, options});
|
99
91
|
|
100
92
|
const searchResultsNav = document.querySelector('.BRsearch-navigation');
|
101
93
|
expect(searchResultsNav).toBeDefined();
|
@@ -110,9 +102,9 @@ describe('View: Plugin: Search', () => {
|
|
110
102
|
br.init();
|
111
103
|
const event = new CustomEvent(`${namespace}SearchCallback`);
|
112
104
|
const options = { goToFirstResult: false };
|
113
|
-
br.searchView.handleSearchCallback(event, { results: resultWithScript, options });
|
105
|
+
br._plugins.search.searchView.handleSearchCallback(event, { results: resultWithScript, options });
|
114
106
|
|
115
|
-
expect(br.searchView.dom.searchNavigation.parent().html()).not.toContain('<script>alert(1);</script>');
|
107
|
+
expect(br._plugins.search.searchView.dom.searchNavigation.parent().html()).not.toContain('<script>alert(1);</script>');
|
116
108
|
});
|
117
109
|
|
118
110
|
describe('Click events handlers', () => {
|
@@ -122,7 +114,7 @@ describe('View: Plugin: Search', () => {
|
|
122
114
|
br.trigger = (eventName) => eventNameTriggered = eventName;
|
123
115
|
|
124
116
|
expect(eventNameTriggered).toBeFalsy();
|
125
|
-
br.searchView.toggleSidebar();
|
117
|
+
br._plugins.search.searchView.toggleSidebar();
|
126
118
|
expect(eventNameTriggered).toEqual('ToggleSearchMenu');
|
127
119
|
});
|
128
120
|
it('triggers custom event when closing navbar', () => {
|
@@ -131,7 +123,7 @@ describe('View: Plugin: Search', () => {
|
|
131
123
|
br.trigger = (eventName) => eventNameTriggered = eventName;
|
132
124
|
|
133
125
|
expect(eventNameTriggered).toBeFalsy();
|
134
|
-
br.searchView.clearSearchFieldAndResults();
|
126
|
+
br._plugins.search.searchView.clearSearchFieldAndResults();
|
135
127
|
expect(eventNameTriggered).toEqual('SearchResultsCleared');
|
136
128
|
});
|
137
129
|
});
|
@@ -145,7 +145,7 @@ describe('Plugin: URL controller', () => {
|
|
145
145
|
search: 'foo',
|
146
146
|
}));
|
147
147
|
BookReader.prototype.search = jest.fn();
|
148
|
-
br.options.
|
148
|
+
br.options.plugins.search = { initialSearchTerm: 'foo' };
|
149
149
|
br.options.urlMode = 'history';
|
150
150
|
br.init();
|
151
151
|
br.urlUpdateFragment();
|