@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.
Files changed (43) hide show
  1. package/BookReader/BookReader.js +1 -1
  2. package/BookReader/BookReader.js.map +1 -1
  3. package/BookReader/ia-bookreader-bundle.js +2 -2
  4. package/BookReader/ia-bookreader-bundle.js.map +1 -1
  5. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  6. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  7. package/BookReader/plugins/plugin.autoplay.js +1 -1
  8. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  9. package/BookReader/plugins/plugin.chapters.js +2 -2
  10. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  11. package/BookReader/plugins/plugin.iiif.js +1 -1
  12. package/BookReader/plugins/plugin.iiif.js.map +1 -1
  13. package/BookReader/plugins/plugin.resume.js +1 -1
  14. package/BookReader/plugins/plugin.resume.js.map +1 -1
  15. package/BookReader/plugins/plugin.search.js +1 -1
  16. package/BookReader/plugins/plugin.search.js.map +1 -1
  17. package/BookReader/plugins/plugin.text_selection.js +1 -1
  18. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  19. package/BookReader/plugins/plugin.tts.js +1 -1
  20. package/BookReader/plugins/plugin.tts.js.map +1 -1
  21. package/BookReaderDemo/IADemoBr.js +29 -1
  22. package/BookReaderDemo/ia-multiple-volumes-manifest.js +0 -1
  23. package/CHANGELOG.md +12 -0
  24. package/README.md +1 -1
  25. package/package.json +1 -1
  26. package/src/BookNavigator/book-navigator.js +5 -2
  27. package/src/BookNavigator/search/search-provider.js +13 -7
  28. package/src/BookNavigator/sharing.js +1 -1
  29. package/src/BookReader/Toolbar/Toolbar.js +5 -0
  30. package/src/BookReader/options.js +9 -7
  31. package/src/BookReader.js +31 -15
  32. package/src/BookReaderPlugin.js +8 -0
  33. package/src/plugins/plugin.text_selection.js +3 -1
  34. package/src/plugins/search/plugin.search.js +330 -376
  35. package/src/plugins/search/view.js +13 -9
  36. package/tests/e2e/helpers/mockSearch.js +1 -1
  37. package/tests/jest/BookNavigator/book-navigator.test.js +8 -3
  38. package/tests/jest/BookNavigator/search/search-provider.test.js +16 -4
  39. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +1 -1
  40. package/tests/jest/BookReader.test.js +26 -1
  41. package/tests/jest/plugins/search/plugin.search.test.js +17 -42
  42. package/tests/jest/plugins/search/plugin.search.view.test.js +10 -18
  43. 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 {object} params.br The BookReader instance
5
- * @param {function} params.cancelSearch callback when a user wants to cancel search
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._searchPluginGoToResult(match.matchIndex); });
276
+ .on("click", () => { this.br._plugins.search.jumpToMatch(match.matchIndex); });
273
277
  });
274
278
  }
275
279
 
276
280
  /**
277
- * @param {boolean} bool
281
+ * @param {boolean} show
278
282
  */
279
- toggleSearchPending(bool) {
280
- if (bool) {
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 = /https:\/\/ia[0-9]+\.us\.archive\.org\/fulltext\/inside\.php\?item_id=.*/;
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 `enableSearch` flag', async() => {
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: { enableSearch: true },
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
- _searchPluginGoToResult: sinon.fake(),
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._searchPluginGoToResult.callCount).toEqual(1);
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
- _searchPluginGoToResult: sinon.fake(),
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
- _searchPluginGoToResult: sinon.fake(),
156
+ _plugins: {
157
+ search: {
158
+ jumpToMatch: sinon.fake(),
159
+ },
160
+ },
149
161
  urlPlugin: urlPluginMock,
150
162
  search: sinon.fake(),
151
163
  },
@@ -39,7 +39,7 @@ describe('Sharing Provider', () => {
39
39
  item,
40
40
  baseHost,
41
41
  bookreader: {
42
- options: { subPrefix },
42
+ subPrefix,
43
43
  },
44
44
  });
45
45
 
@@ -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.initialSearchTerm = 'foo';
148
+ br.options.plugins.search = { initialSearchTerm: 'foo' };
149
149
  br.options.urlMode = 'history';
150
150
  br.init();
151
151
  br.urlUpdateFragment();