@internetarchive/bookreader 5.0.0-45 → 5.0.0-46

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.
@@ -126,6 +126,13 @@
126
126
  const placeholder = document.querySelector('#placeholder');
127
127
  placeholder.innerHTML = 'Dependencies are complete, bookreader has loaded';
128
128
  });
129
+
130
+ // analytics stub
131
+ window.archive_analytics = {
132
+ send_event_no_sampling: (category, action, label) => console.log('~~~ NO SAMPLE EVENT CALLED: ', { category, action, label }),
133
+ send_event: (category, action, label) => console.log('~~~ send_event SAMPLE EVENT CALLED: ', { category, action, label }),
134
+ send_ping: (category, action, label) => console.log('~~~ send_ping SAMPLE EVENT CALLED: ', { category, action, label }),
135
+ }
129
136
  </script>
130
137
 
131
138
  <!-- IA fetch demo -->
package/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ # 5.0.0-46
2
+ Fix: Leaf positioning during RTL fliip animation @latonv
3
+ Dev: dependency updates @renovate
4
+ Fix: Disable contextmenu for restricted books @iisa
5
+
1
6
  # 5.0.0-45
2
7
  # 5.0.0-44
3
8
  Fix: dynamic `q=<term>` url parameter @iisa
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/bookreader",
3
- "version": "5.0.0-45",
3
+ "version": "5.0.0-46",
4
4
  "description": "The Internet Archive BookReader.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -46,7 +46,7 @@
46
46
  "@babel/plugin-proposal-class-properties": "7.16.7",
47
47
  "@babel/plugin-proposal-decorators": "7.17.9",
48
48
  "@babel/preset-env": "7.16.11",
49
- "@open-wc/testing-helpers": "^2.1.2",
49
+ "@open-wc/testing-helpers": "^2.1.3",
50
50
  "@types/jest": "^27.5.1",
51
51
  "@webcomponents/webcomponentsjs": "^2.6.0",
52
52
  "babel-loader": "8.2.5",
@@ -443,6 +443,29 @@ export class BookNavigator extends LitElement {
443
443
  this.downloadableTypes = downloadURLs;
444
444
  this.bookIsRestricted = isRestricted;
445
445
  });
446
+ window.addEventListener('contextmenu', (e) => this.manageContextMenuVisibility(e), { capture: true });
447
+ }
448
+
449
+ /** Display an element's context menu */
450
+ manageContextMenuVisibility(e) {
451
+ if (window.archive_analytics) {
452
+ window.archive_analytics?.send_event_no_sampling(
453
+ 'BookReader',
454
+ `contextmenu-${this.bookIsRestricted ? 'restricted' : 'unrestricted'}`,
455
+ e.target.classList.value
456
+ );
457
+ }
458
+ if (!this.bookIsRestricted) {
459
+ return;
460
+ }
461
+
462
+ const imagePane = e.target.classList.value.match(/BRscreen|BRpageimage/g);
463
+ if (!imagePane) {
464
+ return;
465
+ }
466
+
467
+ e.preventDefault();
468
+ return false;
446
469
  }
447
470
 
448
471
  loadSharedObserver() {
@@ -843,6 +843,13 @@ export class Mode2Up {
843
843
  this.pageContainers[this.br.twoPage.currentIndexR].$container.animate({width: '0px'}, speed, 'easeInSine', () => {
844
844
  this.br.$('BRgutter').css({left: `${gutter - this.br.twoPage.bookSpineDivWidth * 0.5}px`});
845
845
  $(this.br.leafEdgeTmp).animate({left: `${gutter - newWidthL - leafEdgeTmpW}px`}, speed, 'easeOutSine');
846
+
847
+ // Ensure the new left leaf is right-positioned before animating its width.
848
+ // Otherwise, it animates in the wrong direction.
849
+ this.pageContainers[newIndexL].$container.css({
850
+ right: `${$twoPageViewEl.prop('clientWidth') - gutter}px`,
851
+ left: ''
852
+ });
846
853
  this.pageContainers[newIndexL].$container.animate({width: `${newWidthL}px`}, speed, 'easeOutSine', () => {
847
854
  this.pageContainers[newIndexR].$container.css('zIndex', 2);
848
855
 
File without changes
@@ -513,4 +513,118 @@ describe('<book-navigator>', () => {
513
513
  expect(el.manageFullScreenBehavior.callCount).toEqual(1);
514
514
  });
515
515
  });
516
+ describe('Handles Restricted Books', () => {
517
+ describe('contextMenu is prevented when book is restricted', () => {
518
+ it('watches on `div.BRscreen`', async () => {
519
+ window.archive_analytics = { send_event_no_sampling: sinon.fake() };
520
+
521
+ const el = fixtureSync(container());
522
+ const brStub = {
523
+ options: { restricted: true },
524
+ };
525
+
526
+ el.bookreader = brStub;
527
+ el.bookIsRestricted = true;
528
+
529
+ const elSpy = sinon.spy(el.manageContextMenuVisibility);
530
+ await el.elementUpdated;
531
+
532
+ expect(window.archive_analytics.send_event_no_sampling.called).toEqual(
533
+ false
534
+ );
535
+ expect(elSpy.called).toEqual(false);
536
+
537
+ const body = document.querySelector('body');
538
+
539
+ const divBRscreen = document.createElement('div');
540
+ divBRscreen.classList.add('BRscreen');
541
+ body.appendChild(divBRscreen);
542
+
543
+ const contextMenuEvent = new Event('contextmenu', { bubbles: true });
544
+
545
+ // Set spy on contextMenuEvent to check if `preventDefault` is called
546
+ const preventDefaultSpy = sinon.spy(contextMenuEvent, 'preventDefault');
547
+ expect(preventDefaultSpy.called).toEqual(false);
548
+
549
+ divBRscreen.dispatchEvent(contextMenuEvent);
550
+
551
+ // analytics fires
552
+ expect(window.archive_analytics.send_event_no_sampling.called).toEqual(
553
+ true
554
+ );
555
+ // we prevent default
556
+ expect(preventDefaultSpy.called).toEqual(true);
557
+ });
558
+ it('watches on `img.BRpageimage`', async () => {
559
+ window.archive_analytics = { send_event_no_sampling: sinon.fake() };
560
+
561
+ const el = fixtureSync(container());
562
+ const brStub = {
563
+ options: { restricted: true },
564
+ };
565
+
566
+ el.bookreader = brStub;
567
+ el.bookIsRestricted = true;
568
+
569
+ await el.elementUpdated;
570
+
571
+ expect(window.archive_analytics.send_event_no_sampling.called).toEqual(
572
+ false
573
+ );
574
+
575
+ const body = document.querySelector('body');
576
+ // const element stub for img.BRpageimage
577
+ const imgBRpageimage = document.createElement('img');
578
+ imgBRpageimage.classList.add('BRpageimage');
579
+ body.appendChild(imgBRpageimage);
580
+ const contextMenuEvent = new Event('contextmenu', { bubbles: true });
581
+
582
+ // Set spy on contextMenuEvent to check if `preventDefault` is called
583
+ const preventDefaultSpy = sinon.spy(contextMenuEvent, 'preventDefault');
584
+ expect(preventDefaultSpy.called).toEqual(false);
585
+
586
+ imgBRpageimage.dispatchEvent(contextMenuEvent);
587
+
588
+ // analytics fires
589
+ expect(window.archive_analytics.send_event_no_sampling.called).toEqual(true);
590
+ // we prevent default
591
+ expect(preventDefaultSpy.called).toEqual(true);
592
+ });
593
+ });
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
+ });
516
630
  });
@@ -47,6 +47,29 @@ describe('zoom', () => {
47
47
  });
48
48
  });
49
49
 
50
+ describe('page flip directions', () => {
51
+ test('animates the left page in the correct direction', () => {
52
+ const br = new BookReader({ data: SAMPLE_DATA });
53
+ br.init();
54
+
55
+ const fake = sinon.fake();
56
+ const fakeAnimWithCB = sinon.fake.yields();
57
+ const fakeAnim = sinon.fake((...args) =>
58
+ typeof args[args.length - 1] === 'function' ? fakeAnimWithCB(...args) : fake
59
+ );
60
+ sinon.replace(jQuery.prototype, 'animate', fakeAnim);
61
+
62
+ const fakeCSS = sinon.spy(jQuery.prototype, 'css');
63
+
64
+ br.next();
65
+
66
+ expect(fakeAnimWithCB.callCount).toBe(2);
67
+ // Find the call to .css() immediately preceding the second animation with a callback (i.e., the left page animation)
68
+ const preSecondAnimCssCallIndex = fakeCSS.getCalls().findIndex(call => call.calledAfter(fakeAnimWithCB.getCall(1))) - 1;
69
+ expect(fakeCSS.getCall(preSecondAnimCssCallIndex).args[0].left).toBe('');
70
+ });
71
+ });
72
+
50
73
  describe('prefetch', () => {
51
74
  test('loads nearby pages', () => {
52
75
  const br = new BookReader({ data: SAMPLE_DATA });