@internetarchive/bookreader 5.0.0-45 → 5.0.0-46

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 });