@internetarchive/bookreader 5.0.0-46-alpha2 → 5.0.0-46-no-right-click

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. package/BookReader/BookReader.js +1 -1
  2. package/BookReader/ia-bookreader-bundle.js +31 -31
  3. package/BookReader/ia-bookreader-bundle.js.map +1 -1
  4. package/BookReader/jquery-1.10.1.js +2 -0
  5. package/BookReader/jquery-1.10.1.js.LICENSE.txt +24 -0
  6. package/BookReader/plugins/plugin.search.js +1 -1
  7. package/BookReader/plugins/plugin.search.js.map +1 -1
  8. package/BookReaderDemo/IADemoBr.js +13 -17
  9. package/BookReaderDemo/demo-advanced.html +1 -1
  10. package/BookReaderDemo/demo-autoplay.html +1 -1
  11. package/BookReaderDemo/demo-embed-iframe-src.html +1 -1
  12. package/BookReaderDemo/demo-fullscreen-mobile.html +1 -1
  13. package/BookReaderDemo/demo-fullscreen.html +1 -1
  14. package/BookReaderDemo/demo-iiif.html +1 -1
  15. package/BookReaderDemo/demo-internetarchive.html +1 -3
  16. package/BookReaderDemo/demo-multiple.html +1 -1
  17. package/BookReaderDemo/demo-preview-pages.html +1 -1
  18. package/BookReaderDemo/demo-simple.html +1 -1
  19. package/BookReaderDemo/demo-vendor-fullscreen.html +1 -1
  20. package/BookReaderDemo/immersion-1up.html +1 -1
  21. package/BookReaderDemo/immersion-mode.html +1 -1
  22. package/BookReaderDemo/toggle_controls.html +1 -1
  23. package/BookReaderDemo/view_mode.html +1 -1
  24. package/BookReaderDemo/viewmode-cycle.html +1 -1
  25. package/CHANGELOG.md +0 -12
  26. package/package.json +7 -7
  27. package/src/BookNavigator/bookmarks/ia-bookmarks.js +1 -15
  28. package/src/BookNavigator/search/a-search-result.js +4 -12
  29. package/src/plugins/search/view.js +5 -10
  30. package/src/util/manifestGenerator.js +0 -0
  31. package/tests/jest/BookNavigator/book-navigator.test.js +51 -75
  32. package/tests/jest/BookNavigator/search/search-results.test.js +1 -33
  33. package/tests/jest/plugins/search/plugin.search.view.test.js +0 -29
  34. package/webpack.config.js +1 -1
  35. package/BookReader/jquery-3.js +0 -2
  36. package/BookReader/jquery-3.js.LICENSE.txt +0 -24
@@ -1,5 +1,3 @@
1
- import { escapeHTML } from "../../BookReader/utils.js";
2
-
3
1
  class SearchView {
4
2
  /**
5
3
  * @param {object} params
@@ -15,7 +13,7 @@ class SearchView {
15
13
  // Search results are returned as a text blob with the hits wrapped in
16
14
  // triple mustaches. Hits occasionally include text beyond the search
17
15
  // term, so everything within the staches is captured and wrapped.
18
- this.matcher = new RegExp('{{{(.+?)}}}', 'gs');
16
+ this.matcher = new RegExp('{{{(.+?)}}}', 'g');
19
17
  this.matches = [];
20
18
  this.cacheDOMElements();
21
19
  this.bindEvents();
@@ -236,18 +234,15 @@ class SearchView {
236
234
 
237
235
  const percentThrough = this.br.constructor.util.cssPercentage(pageIndex, this.br.getNumLeafs() - 1);
238
236
 
239
- const escapedQueryString = escapeHTML(queryString);
240
- const queryStringWithB = escapedQueryString.replace(this.matcher, '<b>$1</b>');
237
+ const queryStringWithB = queryString.replace(this.matcher, '<b>$1</b>');
241
238
 
242
239
  let queryStringWithBTruncated = '';
243
240
 
244
241
  if (queryString.length > 100) {
245
- queryStringWithBTruncated = queryString.replace(/^(.{100}[^\s]*).*/, "$1");
246
-
247
- // If truncating, we must escape *after* truncation occurs (but before wrapping in <b>)
248
- queryStringWithBTruncated = escapeHTML(queryStringWithBTruncated)
242
+ queryStringWithBTruncated = queryString
243
+ .replace(/^(.{100}[^\s]*).*/, "$1")
249
244
  .replace(this.matcher, '<b>$1</b>')
250
- + '...';
245
+ + '...';
251
246
  }
252
247
 
253
248
  // draw marker
File without changes
@@ -13,8 +13,9 @@ import VolumesProvider from '@/src/BookNavigator/volumes/volumes-provider.js';
13
13
  import { ModalManager } from '@internetarchive/modal-manager';
14
14
  import { SharedResizeObserver } from '@internetarchive/shared-resize-observer';
15
15
  import '@/src/BookNavigator/book-navigator.js';
16
+ import { sleep } from '@/src/BookReader/utils';
16
17
 
17
- const promise0 = () => new Promise(res => setTimeout(res, 0));
18
+ const promise0 = () => new Promise((res) => setTimeout(res, 0));
18
19
 
19
20
  const container = (sharedObserver = null) => {
20
21
  const itemStub = {
@@ -22,7 +23,7 @@ const container = (sharedObserver = null) => {
22
23
  identifier: 'foo',
23
24
  creator: 'bar',
24
25
  title: 'baz',
25
- }
26
+ },
26
27
  };
27
28
  const modalMgr = new ModalManager();
28
29
  return html`
@@ -32,18 +33,18 @@ const container = (sharedObserver = null) => {
32
33
  .sharedObserver=${sharedObserver || new SharedResizeObserver()}
33
34
  .modal=${modalMgr}
34
35
  >
35
- <div slot="main">
36
- <div id="BookReader"></div>
37
- <p class="visible-in-reader">now showing</p>
36
+ <div slot='main'>
37
+ <div id='BookReader'></div>
38
+ <p class='visible-in-reader'>now showing</p>
38
39
  <\div>
39
40
  </book-navigator>
40
41
  `;
41
42
  };
42
43
 
43
44
  window.ResizeObserver = class ResizeObserver {
44
- observe = sinon.fake()
45
- unobserve = sinon.fake()
46
- disconnect = sinon.fake()
45
+ observe = sinon.fake();
46
+ unobserve = sinon.fake();
47
+ disconnect = sinon.fake();
47
48
  };
48
49
 
49
50
  afterEach(() => {
@@ -52,11 +53,11 @@ afterEach(() => {
52
53
  const body = document.querySelector('body');
53
54
  body.innerHTML = '';
54
55
  sinon.restore();
56
+ window.archive_analytics = null;
55
57
  });
56
58
 
57
-
58
59
  describe('<book-navigator>', () => {
59
- describe("How it loads", () => {
60
+ describe('How it loads', () => {
60
61
  describe('Attaches BookReader listeners before `br.init` is called', () => {
61
62
  test('binds global event listeners', async () => {
62
63
  const el = fixtureSync(container());
@@ -71,7 +72,7 @@ describe('<book-navigator>', () => {
71
72
  jumpToIndex: sinon.fake(),
72
73
  options: { enableMultipleBooks: false }, // for multipleBooks
73
74
  el: '#BookReader',
74
- refs: {}
75
+ refs: {},
75
76
  };
76
77
 
77
78
  const sharedObserver = new SharedResizeObserver();
@@ -85,9 +86,11 @@ describe('<book-navigator>', () => {
85
86
 
86
87
  expect(brStub.resize.callCount).toEqual(0);
87
88
 
88
- window.dispatchEvent(new CustomEvent('BookReader:PostInit', {
89
- detail: { props: brStub }
90
- }));
89
+ window.dispatchEvent(
90
+ new CustomEvent('BookReader:PostInit', {
91
+ detail: { props: brStub },
92
+ })
93
+ );
91
94
  await elementUpdated(el);
92
95
 
93
96
  expect(el.emitLoadingStatusUpdate.callCount).toEqual(1);
@@ -120,13 +123,15 @@ describe('<book-navigator>', () => {
120
123
  const itemImage = fixtureSync(el.itemImage);
121
124
  expect(itemImage).toBeInstanceOf(HTMLImageElement);
122
125
  expect(itemImage.getAttribute('class')).toEqual('cover-img');
123
- expect(itemImage.getAttribute('src')).toEqual('https://https://foo.archive.org/services/img/foo');
126
+ expect(itemImage.getAttribute('src')).toEqual(
127
+ 'https://https://foo.archive.org/services/img/foo'
128
+ );
124
129
  });
125
130
  });
126
131
  describe('Menu/Layer Provider', () => {
127
132
  describe('Connecting with a provider:', () => {
128
133
  // loads Providers with base shared resources
129
- test('We load 3 Sub Menus by default', async() => {
134
+ test('We load 3 Sub Menus by default', async () => {
130
135
  const el = fixtureSync(container());
131
136
  const $brContainer = document.createElement('div');
132
137
  const brStub = {
@@ -135,8 +140,8 @@ describe('<book-navigator>', () => {
135
140
  jumpToIndex: sinon.fake(),
136
141
  options: {},
137
142
  refs: {
138
- $brContainer
139
- }
143
+ $brContainer,
144
+ },
140
145
  };
141
146
  el.bookreader = brStub;
142
147
  await el.elementUpdated;
@@ -151,10 +156,12 @@ describe('<book-navigator>', () => {
151
156
  expect(el.menuProviders.share).toBeInstanceOf(SharingProvider);
152
157
 
153
158
  expect(defaultMenus).toContain('visualAdjustments');
154
- expect(el.menuProviders.visualAdjustments).toBeInstanceOf(VisualAdjustmentsProvider);
159
+ expect(el.menuProviders.visualAdjustments).toBeInstanceOf(
160
+ VisualAdjustmentsProvider
161
+ );
155
162
  });
156
163
  describe('Loading Sub Menus By Plugin Flags', () => {
157
- test('Search: uses `enableSearch` flag', async() => {
164
+ test('Search: uses `enableSearch` flag', async () => {
158
165
  const el = fixtureSync(container());
159
166
  const $brContainer = document.createElement('div');
160
167
  const brStub = {
@@ -163,8 +170,8 @@ describe('<book-navigator>', () => {
163
170
  jumpToIndex: sinon.fake(),
164
171
  options: { enableSearch: true },
165
172
  refs: {
166
- $brContainer
167
- }
173
+ $brContainer,
174
+ },
168
175
  };
169
176
  el.bookreader = brStub;
170
177
  await el.elementUpdated;
@@ -176,9 +183,9 @@ describe('<book-navigator>', () => {
176
183
  expect(el.menuProviders.search).toBeInstanceOf(SearchProvider);
177
184
 
178
185
  // also adds a menu shortcut
179
- expect(el.menuShortcuts.find(m => m.id === 'search')).toBeDefined();
186
+ expect(el.menuShortcuts.find((m) => m.id === 'search')).toBeDefined();
180
187
  });
181
- test('Volumes/Multiple Books: uses `enableMultipleBooks` flag', async() => {
188
+ test('Volumes/Multiple Books: uses `enableMultipleBooks` flag', async () => {
182
189
  const el = fixtureSync(container());
183
190
  const $brContainer = document.createElement('div');
184
191
  const brStub = {
@@ -189,13 +196,13 @@ describe('<book-navigator>', () => {
189
196
  enableMultipleBooks: true,
190
197
  multipleBooksList: {
191
198
  by_subprefix: {
192
- fooSubprefix: 'beep'
193
- }
194
- }
199
+ fooSubprefix: 'beep',
200
+ },
201
+ },
195
202
  },
196
203
  refs: {
197
- $brContainer
198
- }
204
+ $brContainer,
205
+ },
199
206
  };
200
207
  el.bookreader = brStub;
201
208
  await el.elementUpdated;
@@ -207,7 +214,9 @@ describe('<book-navigator>', () => {
207
214
  expect(el.menuProviders.volumes).toBeInstanceOf(VolumesProvider);
208
215
 
209
216
  // also adds a menu shortcut
210
- expect(el.menuShortcuts.find(m => m.id === 'volumes')).toBeDefined();
217
+ expect(
218
+ el.menuShortcuts.find((m) => m.id === 'volumes')
219
+ ).toBeDefined();
211
220
  });
212
221
  });
213
222
  test('keeps track of base shared resources for providers in: `baseProviderConfig`', () => {
@@ -315,7 +324,9 @@ describe('<book-navigator>', () => {
315
324
  console.log();
316
325
  sidePanelConfig = e.detail;
317
326
  });
318
- const toggleSearchMenuEvent = new Event('BookReader:ToggleSearchMenu');
327
+ const toggleSearchMenuEvent = new Event(
328
+ 'BookReader:ToggleSearchMenu'
329
+ );
319
330
  window.dispatchEvent(toggleSearchMenuEvent);
320
331
 
321
332
  await elementUpdated(el);
@@ -327,15 +338,15 @@ describe('<book-navigator>', () => {
327
338
  });
328
339
  });
329
340
 
330
- describe('Resizing',() => {
341
+ describe('Resizing', () => {
331
342
  test('keeps track of `brWidth` and `brHeight`', async () => {
332
343
  const el = fixtureSync(container());
333
344
  const brStub = {
334
345
  resize: sinon.fake(),
335
346
  options: {},
336
347
  refs: {
337
- $brContainer: document.createElement('div')
338
- }
348
+ $brContainer: document.createElement('div'),
349
+ },
339
350
  };
340
351
  el.bookreader = brStub;
341
352
  await elementUpdated(el);
@@ -345,9 +356,9 @@ describe('<book-navigator>', () => {
345
356
  const mockResizeEvent = {
346
357
  contentRect: {
347
358
  height: 500,
348
- width: 900
359
+ width: 900,
349
360
  },
350
- target: el.mainBRContainer
361
+ target: el.mainBRContainer,
351
362
  };
352
363
  el.handleResize(mockResizeEvent);
353
364
 
@@ -364,8 +375,8 @@ describe('<book-navigator>', () => {
364
375
  resize: sinon.fake(),
365
376
  options: {},
366
377
  refs: {
367
- $brContainer: document.createElement('div')
368
- }
378
+ $brContainer: document.createElement('div'),
379
+ },
369
380
  };
370
381
 
371
382
  el.bookreader = brStub;
@@ -470,7 +481,7 @@ describe('<book-navigator>', () => {
470
481
 
471
482
  expect(el.closeFullscreen.callCount).toEqual(1);
472
483
  });
473
- test('removes Fullscreen shortcut when leaving fullscreen', async() => {
484
+ test('removes Fullscreen shortcut when leaving fullscreen', async () => {
474
485
  const el = fixtureSync(container());
475
486
  const brStub = {
476
487
  isFullscreen: () => false,
@@ -489,7 +500,7 @@ describe('<book-navigator>', () => {
489
500
  expect(el.menuShortcuts.length).toEqual(0);
490
501
  expect(el.emitMenuShortcutsUpdated.callCount).toEqual(1);
491
502
  });
492
- test('Event: Listens for `BookReader:FullscreenToggled', async() => {
503
+ test('Event: Listens for `BookReader:FullscreenToggled', async () => {
493
504
  const el = fixtureSync(container());
494
505
  const brStub = {
495
506
  isFullscreen: () => true,
@@ -591,40 +602,5 @@ describe('<book-navigator>', () => {
591
602
  expect(preventDefaultSpy.called).toEqual(true);
592
603
  });
593
604
  });
594
- it('Allows unrestricted books access to context menu', async () => {
595
- window.archive_analytics = { send_event_no_sampling: sinon.fake() };
596
-
597
- const el = fixtureSync(container());
598
- const brStub = {
599
- options: { restricted: false },
600
- };
601
-
602
- el.bookreader = brStub;
603
- el.bookIsRestricted = false;
604
-
605
- await el.elementUpdated;
606
-
607
- expect(window.archive_analytics.send_event_no_sampling.called).toEqual(
608
- false
609
- );
610
-
611
- const body = document.querySelector('body');
612
- // const element stub for img.BRpageimage
613
- const imgBRpageimage = document.createElement('img');
614
- imgBRpageimage.classList.add('not-targeted-element');
615
- body.appendChild(imgBRpageimage);
616
- const contextMenuEvent = new Event('contextmenu', { bubbles: true });
617
-
618
- // Set spy on contextMenuEvent to check if `preventDefault` is called
619
- const preventDefaultSpy = sinon.spy(contextMenuEvent, 'preventDefault');
620
- expect(preventDefaultSpy.called).toEqual(false);
621
-
622
- imgBRpageimage.dispatchEvent(contextMenuEvent);
623
-
624
- // analytics fires
625
- expect(window.archive_analytics.send_event_no_sampling.called).toEqual(true);
626
- // we do not prevent default
627
- expect(preventDefaultSpy.called).toEqual(false);
628
- });
629
605
  });
630
606
  });
@@ -46,25 +46,6 @@ const results = [{
46
46
  }],
47
47
  }];
48
48
 
49
- const resultWithScript = [{
50
- text: `foo bar <script>const msg = 'test' + ' failure'; document.write(msg);</script> {{{${searchQuery}}}} baz`,
51
- cover: '//placehold.it/30x44',
52
- title: 'Book title',
53
- displayPageNumber: 'Page 24',
54
- par: [{
55
- boxes: [{
56
- r: 2672, b: 792, t: 689, page: 24, l: 2424,
57
- }],
58
- b: 1371,
59
- t: 689,
60
- page_width: 3658,
61
- r: 3192,
62
- l: 428,
63
- page_height: 5357,
64
- page: 24,
65
- }],
66
- }];
67
-
68
49
  describe('<ia-book-search-results>', () => {
69
50
  afterEach(() => {
70
51
  sinon.restore();
@@ -107,20 +88,7 @@ describe('<ia-book-search-results>', () => {
107
88
  sinon.replace(IABookSearchResults.prototype, 'createRenderRoot', function createRenderRoot() { return this; });
108
89
  const el = await fixture(container(results));
109
90
 
110
- // Lit inserts HTML comments that inhibit searching for exact innerHTML matches.
111
- // So query the DOM for the match instead.
112
- const match = el.querySelector('book-search-result mark');
113
- expect(match?.textContent).toEqual(searchQuery);
114
- });
115
-
116
- test('renders results that contain sanitized HTML tags', async () => {
117
- sinon.replace(IABookSearchResults.prototype, 'createRenderRoot', function createRenderRoot() { return this; });
118
- // A result whose text contains a <script> tag that will insert 'test failure' into the element if not sanitized
119
- const el = await fixture(container(resultWithScript));
120
-
121
- const match = el.querySelector('book-search-result mark');
122
- expect(match?.textContent).toEqual(searchQuery);
123
- expect(el.innerHTML).not.toContain('test failure');
91
+ expect(el.innerHTML).toContain(`<mark>${searchQuery}</mark>`);
124
92
  });
125
93
 
126
94
  test('renders results that contain an optional cover image', async () => {
@@ -27,27 +27,6 @@ const results = {
27
27
  }]
28
28
  }]
29
29
  };
30
- const resultWithScript = {
31
- ia: "adventuresofoli00dick",
32
- q: "child",
33
- indexed: true,
34
- page_count: 644,
35
- body_length: 666,
36
- leaf0_missing: false,
37
- matches: [{
38
- text: 'foo bar <script>alert(1);</script> {{{keyword}}} baz',
39
- par: [{
40
- boxes: [{r: 1221, b: 2121, t: 2075, page: 37, l: 1107}],
41
- b: 2535,
42
- t: 1942,
43
- page_width: 1790,
44
- r: 1598,
45
- l: 50,
46
- page_height: 2940,
47
- page: 37
48
- }]
49
- }]
50
- };
51
30
  beforeEach(() => {
52
31
  $.ajax = jest.fn().mockImplementation(() => {
53
32
  // return from:
@@ -102,14 +81,6 @@ describe('View: Plugin: Search', () => {
102
81
  expect(searchResultsNav.querySelector('.prev')).toBeDefined();
103
82
  expect(searchResultsNav.querySelector('.next')).toBeDefined();
104
83
  });
105
- test('disallows xss from search results', () => {
106
- br.init();
107
- const event = new CustomEvent(`${namespace}SearchCallback`);
108
- const options = { goToFirstResult: false };
109
- br.searchView.handleSearchCallback(event, { results: resultWithScript, options });
110
-
111
- expect(br.searchView.dom.searchNavigation.parent().html()).not.toContain('<script>alert(1);</script>');
112
- });
113
84
 
114
85
  describe('Click events handlers', () => {
115
86
  it('triggers custom event when toggling side menu', () => {
package/webpack.config.js CHANGED
@@ -86,7 +86,7 @@ module.exports = [
86
86
  ...shared,
87
87
 
88
88
  entry: {
89
- 'jquery-3.js': { import: './src/jquery-wrapper.js' },
89
+ 'jquery-1.10.1.js': { import: './src/jquery-wrapper.js' },
90
90
  },
91
91
  },
92
92
  ];