@internetarchive/bookreader 5.0.0-57 → 5.0.0-59

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