@internetarchive/bookreader 5.0.0-57 → 5.0.0-59

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 (53) hide show
  1. package/BookReader/BookReader.css +110 -39
  2. package/BookReader/BookReader.js +1 -1
  3. package/BookReader/BookReader.js.LICENSE.txt +0 -20
  4. package/BookReader/BookReader.js.map +1 -1
  5. package/BookReader/ia-bookreader-bundle.js +1 -1
  6. package/BookReader/ia-bookreader-bundle.js.map +1 -1
  7. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  8. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  9. package/BookReader/plugins/plugin.autoplay.js +1 -1
  10. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  11. package/BookReader/plugins/plugin.resume.js +1 -1
  12. package/BookReader/plugins/plugin.resume.js.map +1 -1
  13. package/BookReader/plugins/plugin.tts.js +1 -1
  14. package/BookReader/plugins/plugin.tts.js.map +1 -1
  15. package/BookReader/plugins/plugin.url.js +1 -1
  16. package/BookReader/plugins/plugin.url.js.map +1 -1
  17. package/BookReaderDemo/BookReaderJSAutoplay.js +4 -1
  18. package/BookReaderDemo/BookReaderJSSimple.js +1 -0
  19. package/BookReaderDemo/IADemoBr.js +1 -2
  20. package/CHANGELOG.md +8 -0
  21. package/babel.config.js +5 -2
  22. package/package.json +10 -9
  23. package/src/BookReader/BookModel.js +59 -1
  24. package/src/BookReader/Mode1Up.js +5 -0
  25. package/src/BookReader/Mode1UpLit.js +19 -73
  26. package/src/BookReader/Mode2Up.js +72 -1332
  27. package/src/BookReader/Mode2UpLit.js +774 -0
  28. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  29. package/src/BookReader/ModeSmoothZoom.js +32 -0
  30. package/src/BookReader/options.js +8 -2
  31. package/src/BookReader/utils.js +16 -0
  32. package/src/BookReader.js +24 -217
  33. package/src/css/_BRBookmarks.scss +1 -1
  34. package/src/css/_BRmain.scss +14 -0
  35. package/src/css/_BRpages.scss +113 -41
  36. package/src/plugins/plugin.autoplay.js +1 -6
  37. package/src/plugins/tts/WebTTSEngine.js +2 -2
  38. package/src/plugins/tts/plugin.tts.js +3 -17
  39. package/src/plugins/tts/utils.js +0 -16
  40. package/tests/e2e/helpers/base.js +20 -20
  41. package/tests/e2e/helpers/rightToLeft.js +4 -10
  42. package/tests/e2e/viewmode.test.js +10 -8
  43. package/tests/jest/BookReader/BookModel.test.js +25 -0
  44. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +28 -11
  45. package/tests/jest/BookReader/Mode1UpLit.test.js +0 -19
  46. package/tests/jest/BookReader/Mode2Up.test.js +55 -225
  47. package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
  48. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
  49. package/tests/jest/BookReader/ModeSmoothZoom.test.js +26 -0
  50. package/tests/jest/BookReader/Navbar/Navbar.test.js +3 -3
  51. package/tests/jest/BookReader/utils.test.js +32 -1
  52. package/tests/jest/plugins/tts/utils.test.js +0 -34
  53. package/tests/jest/setup.js +3 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/bookreader",
3
- "version": "5.0.0-57",
3
+ "version": "5.0.0-59",
4
4
  "description": "The Internet Archive BookReader.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,12 +42,12 @@
42
42
  },
43
43
  "devDependencies": {
44
44
  "@babel/core": "7.17.9",
45
- "@babel/eslint-parser": "7.17.0",
45
+ "@babel/eslint-parser": "7.21.3",
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.4",
50
- "@types/jest": "^29.4.0",
49
+ "@open-wc/testing-helpers": "^2.2.0",
50
+ "@types/jest": "^29.5.0",
51
51
  "@webcomponents/webcomponentsjs": "^2.6.0",
52
52
  "babel-loader": "8.2.5",
53
53
  "codecov": "^3.8.3",
@@ -60,7 +60,7 @@
60
60
  "hammerjs": "^2.0.8",
61
61
  "http-server": "14.1.1",
62
62
  "iso-language-codes": "1.1.0",
63
- "jest": "^29.4.3",
63
+ "jest": "29.5.0",
64
64
  "jest-environment-jsdom": "^29.4.3",
65
65
  "jquery": "3.6.1",
66
66
  "jquery-colorbox": "1.6.4",
@@ -72,10 +72,10 @@
72
72
  "node-fetch": "3.2.10",
73
73
  "regenerator-runtime": "0.13.9",
74
74
  "sass": "1.52.1",
75
- "sinon": "^15.0.1",
75
+ "sinon": "15.0.3",
76
76
  "soundmanager2": "2.97.20170602",
77
77
  "svgo": "2.8.0",
78
- "testcafe": "^2.0.1",
78
+ "testcafe": "2.4.0",
79
79
  "testcafe-browser-provider-browserstack": "^1.13.2-alpha.1",
80
80
  "webpack": "5.51.1",
81
81
  "webpack-cli": "4.9.2"
@@ -89,7 +89,8 @@
89
89
  "^@/(.*)$": "<rootDir>/$1"
90
90
  },
91
91
  "setupFiles": [
92
- "./src/jquery-wrapper.js"
92
+ "./src/jquery-wrapper.js",
93
+ "./tests/jest/setup.js"
93
94
  ],
94
95
  "roots": [
95
96
  "<rootDir>/src/",
@@ -118,7 +119,7 @@
118
119
  "test:e2e": "npm run build && npx testcafe",
119
120
  "test:e2e:dev": "npx testcafe --live --dev",
120
121
  "DOCS:update:test-deps": "If CI succeeds, these should be good to update",
121
- "update:test-deps": "npm i @babel/eslint-parser@latest @open-wc/testing-helpers@latest @types/jest@latest codecov@latest eslint@latest eslint-plugin-testcafe@latest jest@latest sinon@latest testcafe@latest",
122
+ "update:test-deps": "npm i @babel/eslint-parser@latest @open-wc/testing-helpers@latest @types/jest@latest codecov@latest eslint@7 eslint-plugin-testcafe@latest jest@latest sinon@latest testcafe@latest",
122
123
  "DOCS:update:build-deps": "These can cause strange changes, so do an npm run build + check file size (git diff --stat), and check the site is as expected",
123
124
  "update:build-deps": "npm i @babel/core@latest @babel/preset-env@latest babel-loader@latest core-js@latest regenerator-runtime@latest sass@latest svgo@latest webpack@latest webpack-cli@latest",
124
125
  "codecov": "npx codecov"
@@ -23,6 +23,8 @@ export class BookModel {
23
23
  this.br = br;
24
24
  this.reduceSet = br.reduceSet;
25
25
  this.ppi = br.options?.ppi ?? DEFAULT_OPTIONS.ppi;
26
+ /** @type {'lr' | 'rl'} Page progression */
27
+ this.pageProgression = br.options?.pageProgression ?? DEFAULT_OPTIONS.pageProgression;
26
28
 
27
29
  /** @type {{width: number, height: number}} memoize storage */
28
30
  this._medianPageSize = null;
@@ -197,7 +199,7 @@ export class BookModel {
197
199
  * @return {[PageIndex, PageIndex]} eg [0, 1]
198
200
  */
199
201
  getSpreadIndices(pindex) {
200
- if (this.br.pageProgression == 'rl') {
202
+ if (this.pageProgression == 'rl') {
201
203
  return this.getPageSide(pindex) == 'R' ? [pindex + 1, pindex] : [pindex, pindex - 1];
202
204
  } else {
203
205
  return this.getPageSide(pindex) == 'L' ? [pindex, pindex + 1] : [pindex - 1, pindex];
@@ -408,6 +410,42 @@ export class PageModel {
408
410
  return this.findNext();
409
411
  }
410
412
 
413
+ /** @type {PageModel | null} */
414
+ get left() {
415
+ return this.book.pageProgression === 'lr' ? this.prev : this.next;
416
+ }
417
+
418
+ /** @type {PageModel | null} */
419
+ get right() {
420
+ return this.book.pageProgression === 'lr' ? this.next : this.prev;
421
+ }
422
+
423
+ /**
424
+ * @type {{left: PageModel | null, right: PageModel | null}}
425
+ */
426
+ get spread() {
427
+ return {
428
+ left: this.pageSide === 'L' ? this : this.left,
429
+ right: this.pageSide === 'R' ? this : this.right,
430
+ };
431
+ }
432
+
433
+ /**
434
+ * @param {number} pages
435
+ */
436
+ goLeft(pages) {
437
+ const newIndex = this.book.pageProgression === 'lr' ? this.index - pages : this.index + pages;
438
+ return this.book.getPage(newIndex);
439
+ }
440
+
441
+ /**
442
+ * @param {number} pages
443
+ */
444
+ goRight(pages) {
445
+ const newIndex = this.book.pageProgression === 'lr' ? this.index + pages : this.index - pages;
446
+ return this.book.getPage(newIndex);
447
+ }
448
+
411
449
  /**
412
450
  * @param {number} reduce
413
451
  * @param {number} rotate
@@ -469,6 +507,26 @@ export class PageModel {
469
507
  return new PageModel(this.book, this.index - 1);
470
508
  }
471
509
  }
510
+
511
+ /**
512
+ * @param {object} [arg0]
513
+ * @param {boolean} [arg0.combineConsecutiveUnviewables] Whether to only yield the first page
514
+ * of a series of unviewable pages instead of each page
515
+ * @return {PageModel|void}
516
+ */
517
+ findLeft({ combineConsecutiveUnviewables = false } = {}) {
518
+ return this.book.pageProgression === 'lr' ? this.findPrev({ combineConsecutiveUnviewables }) : this.findNext({ combineConsecutiveUnviewables });
519
+ }
520
+
521
+ /**
522
+ * @param {object} [arg0]
523
+ * @param {boolean} [arg0.combineConsecutiveUnviewables] Whether to only yield the first page
524
+ * of a series of unviewable pages instead of each page
525
+ * @return {PageModel|void}
526
+ */
527
+ findRight({ combineConsecutiveUnviewables = false } = {}) {
528
+ return this.book.pageProgression === 'lr' ? this.findNext({ combineConsecutiveUnviewables }) : this.findPrev({ combineConsecutiveUnviewables });
529
+ }
472
530
  }
473
531
 
474
532
  // There are a few main ways we can reference a specific page in a book:
@@ -58,6 +58,11 @@ export class Mode1Up {
58
58
  });
59
59
  }
60
60
  this.mode1UpLit.jumpToIndex(startLeaf);
61
+ setTimeout(() => {
62
+ // Must explicitly call updateVisibleRegion, since no
63
+ // scroll event seems to fire.
64
+ this.mode1UpLit.updateVisibleRegion();
65
+ });
61
66
  });
62
67
  this.br.updateBrClasses();
63
68
  }
@@ -3,9 +3,10 @@ import { customElement, property, query } from 'lit/decorators.js';
3
3
  import {LitElement, html} from 'lit';
4
4
  import { styleMap } from 'lit/directives/style-map.js';
5
5
  import { ModeSmoothZoom } from './ModeSmoothZoom';
6
- import { arrChanged, calcScreenDPI, genToArray, sum, throttle } from './utils';
6
+ import { arrChanged, genToArray, sum, throttle } from './utils';
7
7
  import { HTMLDimensionsCacher } from "./utils/HTMLDimensionsCacher";
8
8
  import { ScrollClassAdder } from './utils/ScrollClassAdder';
9
+ import { ModeCoordinateSpace } from './ModeCoordinateSpace';
9
10
  /** @typedef {import('./BookModel').BookModel} BookModel */
10
11
  /** @typedef {import('./BookModel').PageIndex} PageIndex */
11
12
  /** @typedef {import('./BookModel').PageModel} PageModel */
@@ -41,17 +42,8 @@ export class Mode1UpLit extends LitElement {
41
42
 
42
43
  /************** SCALE-RELATED PROPERTIES **************/
43
44
 
44
- /** @private */
45
- screenDPI = calcScreenDPI();
46
-
47
- /**
48
- * How much smaller the rendered pages are than the real-world item
49
- *
50
- * Mode1Up doesn't use the br.reduce because it is DPI aware. The reduction factor
51
- * of a given leaf can change (since leaves can have different DPIs), but the real-world
52
- * reduction is constant throughout.
53
- */
54
- realWorldReduce = 1;
45
+ /** @type {ModeCoordinateSpace} Manage conversion between coordinates */
46
+ coordSpace = new ModeCoordinateSpace(this);
55
47
 
56
48
  @property({ type: Number })
57
49
  scale = 1;
@@ -95,7 +87,7 @@ export class Mode1UpLit extends LitElement {
95
87
  worldDimensions = { width: 100, height: 100 };
96
88
 
97
89
  get worldStyle() {
98
- const wToR = this.worldUnitsToRenderedPixels;
90
+ const wToR = this.coordSpace.worldUnitsToRenderedPixels;
99
91
  return {
100
92
  width: wToR(this.worldDimensions.width) + "px",
101
93
  height: wToR(this.worldDimensions.height) + "px",
@@ -139,7 +131,7 @@ export class Mode1UpLit extends LitElement {
139
131
  if (smooth) {
140
132
  this.style.scrollBehavior = 'smooth';
141
133
  }
142
- this.scrollTop = this.worldUnitsToVisiblePixels(this.pageTops[index] - this.SPACING_IN / 2);
134
+ this.scrollTop = this.coordSpace.worldUnitsToVisiblePixels(this.pageTops[index] - this.SPACING_IN / 2);
143
135
  // TODO: Also h center?
144
136
  if (smooth) {
145
137
  setTimeout(() => this.style.scrollBehavior = '', 100);
@@ -203,15 +195,19 @@ export class Mode1UpLit extends LitElement {
203
195
  }
204
196
  if (changedProps.has('visiblePages')) {
205
197
  this.throttledUpdateRenderedPages();
206
- this.br.displayedIndices = this.visiblePages.map(p => p.index);
207
- this.br.updateFirstIndex(this.br.displayedIndices[0]);
208
- this.br._components.navbar.updateNavIndexThrottled();
198
+ if (this.visiblePages.length) {
199
+ // unclear why this is ever really happening
200
+ this.br.displayedIndices = this.visiblePages.map(p => p.index);
201
+ this.br.updateFirstIndex(this.br.displayedIndices[0]);
202
+ this.br._components.navbar.updateNavIndexThrottled();
203
+ }
209
204
  }
210
205
  if (changedProps.has('scale')) {
211
206
  const oldVal = changedProps.get('scale');
212
207
  // Need to set this scale to actually scale the pages
213
208
  this.$visibleWorld.style.transform = `scale(${this.scale})`;
214
- this.updateViewportOnZoom(this.scale, oldVal);
209
+ this.smoothZoomer.updateViewportOnZoom(this.scale, oldVal);
210
+ this.updateVisibleRegion();
215
211
  // Need to set this scale to update the world size, so the scrollbar gets the correct size
216
212
  this.$world.style.transform = `scale(${this.scale})`;
217
213
  }
@@ -241,25 +237,6 @@ export class Mode1UpLit extends LitElement {
241
237
  return this;
242
238
  }
243
239
 
244
- /************** COORDINATE SPACE CONVERTERS **************/
245
- /**
246
- * There are a few different "coordinate spaces" at play in BR:
247
- * (1) World units: i.e. inches. Unless otherwise stated, all computations
248
- * are done in world units.
249
- * (2) Rendered Pixels: i.e. img.width = '300'. Note this does _not_ take
250
- * into account zoom scaling.
251
- * (3) Visible Pixels: Just rendered pixels, but taking into account scaling.
252
- */
253
-
254
- worldUnitsToRenderedPixels = (/** @type {number} */inches) => inches * this.screenDPI / this.realWorldReduce;
255
- renderedPixelsToWorldUnits = (/** @type {number} */px) => px * this.realWorldReduce / this.screenDPI;
256
-
257
- renderedPixelsToVisiblePixels = (/** @type {number} */px) => px * this.scale;
258
- visiblePixelsToRenderedPixels = (/** @type {number} */px) => px / this.scale;
259
-
260
- worldUnitsToVisiblePixels = (/** @type {number} */px) => this.renderedPixelsToVisiblePixels(this.worldUnitsToRenderedPixels(px));
261
- visiblePixelsToWorldUnits = (/** @type {number} */px) => this.renderedPixelsToWorldUnits(this.visiblePixelsToRenderedPixels(px));
262
-
263
240
  /************** RENDERING **************/
264
241
 
265
242
  /** @override */
@@ -283,9 +260,9 @@ export class Mode1UpLit extends LitElement {
283
260
 
284
261
  /** @param {PageModel} page */
285
262
  renderPage = (page) => {
286
- const wToR = this.worldUnitsToRenderedPixels;
287
- const wToV = this.worldUnitsToVisiblePixels;
288
- const containerWidth = this.visiblePixelsToWorldUnits(this.htmlDimensionsCacher.clientWidth);
263
+ const wToR = this.coordSpace.worldUnitsToRenderedPixels;
264
+ const wToV = this.coordSpace.worldUnitsToVisiblePixels;
265
+ const containerWidth = this.coordSpace.visiblePixelsToWorldUnits(this.htmlDimensionsCacher.clientWidth);
289
266
 
290
267
  const width = wToR(page.widthInches);
291
268
  const height = wToR(page.heightInches);
@@ -320,7 +297,7 @@ export class Mode1UpLit extends LitElement {
320
297
  // Note: scrollTop, and clientWidth all are in visible space;
321
298
  // i.e. they are affects by the CSS transforms.
322
299
 
323
- const vToW = this.visiblePixelsToWorldUnits;
300
+ const vToW = this.coordSpace.visiblePixelsToWorldUnits;
324
301
  this.visibleRegion = {
325
302
  top: vToW(scrollTop),
326
303
  height: vToW(clientHeight),
@@ -372,7 +349,7 @@ export class Mode1UpLit extends LitElement {
372
349
  */
373
350
  computeDefaultScale(page) {
374
351
  // Default to real size if it fits, otherwise default to full width
375
- const containerWidthIn = this.visiblePixelsToWorldUnits(this.htmlDimensionsCacher.clientWidth);
352
+ const containerWidthIn = this.coordSpace.visiblePixelsToWorldUnits(this.htmlDimensionsCacher.clientWidth);
376
353
  return Math.min(1, containerWidthIn / (page.widthInches + 2 * this.SPACING_IN)) || 1;
377
354
  }
378
355
 
@@ -396,37 +373,6 @@ export class Mode1UpLit extends LitElement {
396
373
  });
397
374
  }
398
375
 
399
- /************** ZOOMING LOGIC **************/
400
-
401
- /**
402
- * @param {number} newScale
403
- * @param {number} oldScale
404
- */
405
- updateViewportOnZoom(newScale, oldScale) {
406
- const container = this;
407
- const { scrollTop: T, scrollLeft: L } = container;
408
- const W = this.htmlDimensionsCacher.clientWidth;
409
- const H = this.htmlDimensionsCacher.clientHeight;
410
-
411
- // Scale factor change
412
- const F = newScale / oldScale;
413
-
414
- // Where in the viewport the zoom is centered on
415
- const XPOS = this.scaleCenter.x;
416
- const YPOS = this.scaleCenter.y;
417
- const oldCenter = {
418
- x: L + XPOS * W,
419
- y: T + YPOS * H,
420
- };
421
- const newCenter = {
422
- x: F * oldCenter.x,
423
- y: F * oldCenter.y,
424
- };
425
- container.scrollTop = newCenter.y - YPOS * H;
426
- container.scrollLeft = newCenter.x - XPOS * W;
427
- this.updateVisibleRegion();
428
- }
429
-
430
376
  /************** INPUT HANDLERS **************/
431
377
 
432
378
  attachScrollListeners = () => {