@internetarchive/bookreader 5.0.0-71 → 5.0.0-72

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ # 5.0.0-72
2
+ - Fix: Play/pause button out of sync with ReadAloud @sbwhitt
3
+ - Fix: BookReader not loading in sandboxed iframe @cdrini
4
+ - Dev: Fix noisy sentry error firing on any selection @cdrini
5
+ - Dev: Update to Node 20 @cdrini
6
+
1
7
  # 5.0.0-71
2
8
  - Dev: update jest monorepo @renovate
3
9
  - Fix: Share & Mutliple View menu panel refactor @iisa
package/netlify.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [build.environment]
2
2
  # Keep in sync with CI in .github/workflows/node.js.yml
3
- NODE_VERSION = "16"
3
+ NODE_VERSION = "20"
4
4
 
5
5
  [[headers]]
6
6
  # Define which paths this specific [[headers]] block will cover.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/bookreader",
3
- "version": "5.0.0-71",
3
+ "version": "5.0.0-72",
4
4
  "description": "The Internet Archive BookReader.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -46,7 +46,7 @@
46
46
  "@babel/plugin-proposal-decorators": "7.22.7",
47
47
  "@babel/preset-env": "7.22.9",
48
48
  "@open-wc/testing-helpers": "^2.3.0",
49
- "@types/jest": "29.5.5",
49
+ "@types/jest": "29.5.6",
50
50
  "@webcomponents/webcomponentsjs": "^2.6.0",
51
51
  "babel-loader": "9.1.3",
52
52
  "codecov": "^3.8.3",
@@ -67,10 +67,9 @@
67
67
  "jquery-ui-touch-punch": "0.2.3",
68
68
  "jquery.browser": "0.1.0",
69
69
  "live-server": "1.2.2",
70
- "node-fetch": "3.3.2",
71
70
  "regenerator-runtime": "0.13.11",
72
71
  "sass": "1.64.2",
73
- "sinon": "^15.1.0",
72
+ "sinon": "^17.0.0",
74
73
  "soundmanager2": "2.97.20170602",
75
74
  "svgo": "3.0.2",
76
75
  "testcafe": "2.6.2",
@@ -81,7 +80,7 @@
81
80
  "jest": {
82
81
  "testEnvironment": "jsdom",
83
82
  "transformIgnorePatterns": [
84
- "node_modules/(?!(lit-html|lit-element|lit|@lit|@internetarchive|@open-wc)/)"
83
+ "node_modules/(?!(sinon|lit-html|lit-element|lit|@lit|@internetarchive|@open-wc)/)"
85
84
  ],
86
85
  "moduleNameMapper": {
87
86
  "^@/(.*)$": "<rootDir>/$1"
@@ -2,10 +2,6 @@ const { version: OLD_VERSION } = require('../package.json');
2
2
  const OLD_RELEASE_URL = `https://api.github.com/repos/internetarchive/bookreader/releases/tags/v${OLD_VERSION}`;
3
3
 
4
4
  async function main() {
5
- // Need this because fetch is ESM-only, and we're on Node 16. Someday we should
6
- // be able to move this up to the top without renaming this file to a .mjs or whatever
7
- const {default: fetch} = await import('node-fetch');
8
-
9
5
  const {created_at} = await fetch(OLD_RELEASE_URL).then(r => r.json());
10
6
  const today = new Date().toISOString().slice(0, -5);
11
7
  const searchUrl = 'https://github.com/internetarchive/bookreader/pulls?' + new URLSearchParams({
@@ -30,8 +30,10 @@ export class SelectionObserver {
30
30
  const sel = window.getSelection();
31
31
 
32
32
  if (!this.selecting && sel.toString()) {
33
+ const target = $(sel.anchorNode).closest(this.selector)[0];
34
+ if (!target) return;
35
+ this.target = target;
33
36
  this.selecting = true;
34
- this.target = $(sel.anchorNode).closest(this.selector)[0];
35
37
  this.handler('started', this.target);
36
38
  }
37
39
 
@@ -1,4 +1,5 @@
1
1
  import PageChunkIterator from './PageChunkIterator.js';
2
+ import { hasLocalStorage } from './utils.js';
2
3
  /** @typedef {import('./utils.js').ISO6391} ISO6391 */
3
4
  /** @typedef {import('./PageChunk.js')} PageChunk */
4
5
 
@@ -80,6 +81,7 @@ export default class AbstractTTSEngine {
80
81
  */
81
82
  start(leafIndex, numLeafs) {
82
83
  this.playing = true;
84
+ this.paused = false;
83
85
  this.opts.onLoadingStart();
84
86
 
85
87
  this._chunkIterator = new PageChunkIterator(numLeafs, leafIndex, {
@@ -95,6 +97,7 @@ export default class AbstractTTSEngine {
95
97
  stop() {
96
98
  if (this.activeSound) this.activeSound.stop();
97
99
  this.playing = false;
100
+ this.paused = true;
98
101
  this._chunkIterator = null;
99
102
  this.activeSound = null;
100
103
  this.events.trigger('stop');
@@ -143,7 +146,7 @@ export default class AbstractTTSEngine {
143
146
  this.events.off('voiceschanged', this.updateBestVoice);
144
147
  this.voice = this.getVoices().find(voice => voice.voiceURI === voiceURI);
145
148
  // if the current book has a language set, store the selected voice with the book language as a suffix
146
- if (this.opts.bookLanguage) {
149
+ if (this.opts.bookLanguage && hasLocalStorage()) {
147
150
  localStorage.setItem(`BRtts-voice-${this.opts.bookLanguage}`, this.voice.voiceURI);
148
151
  }
149
152
  if (this.activeSound) this.activeSound.setVoice(this.voice);
@@ -247,7 +250,7 @@ export default class AbstractTTSEngine {
247
250
  * @return {SpeechSynthesisVoice | undefined}
248
251
  */
249
252
  static getMatchingStoredVoice(voices, bookLanguage) {
250
- const storedVoice = localStorage.getItem(`BRtts-voice-${bookLanguage}`);
253
+ const storedVoice = hasLocalStorage() && localStorage.getItem(`BRtts-voice-${bookLanguage}`);
251
254
  return (storedVoice ? voices.find(v => v.voiceURI === storedVoice) : undefined);
252
255
  }
253
256
 
@@ -251,7 +251,7 @@ BookReader.prototype.ttsPlayPause = function() {
251
251
  this.ttsToggle();
252
252
  } else {
253
253
  this.ttsEngine.togglePlayPause();
254
- this.ttsUpdateState(this.ttsEngine.paused);
254
+ this.ttsUpdateState();
255
255
  }
256
256
  };
257
257
 
@@ -64,3 +64,18 @@ function searchForISO6391(language, columnsToSearch) {
64
64
  }
65
65
  return null;
66
66
  }
67
+
68
+ /**
69
+ * Checks whether the current browser supports localStorage or
70
+ * if the current context has access to it.
71
+ * @return {boolean}
72
+ */
73
+ export function hasLocalStorage() {
74
+ try {
75
+ return !!window.localStorage;
76
+ } catch (e) {
77
+ // Will throw in sandboxed iframe
78
+ // DOMException: Window.localStorage getter: Forbidden in a sandboxed document without the 'allow-same-origin' flag.
79
+ return false;
80
+ }
81
+ }
@@ -40,4 +40,18 @@ describe("SelectionObserver", () => {
40
40
  observer._onSelectionChange();
41
41
  expect(handler.callCount).toBe(2);
42
42
  });
43
+
44
+ test('Only fires when selection started in selector', () => {
45
+ const handler = sinon.spy();
46
+ const observer = new SelectionObserver(".text-layer", handler);
47
+ const target = document.createElement("div");
48
+ target.classList.add("text-layer");
49
+
50
+ // stub window.getSelection
51
+ const getSelectionStub = sinon.stub(window, "getSelection");
52
+ getSelectionStub.returns({ toString: () => "test", anchorNode: document.body });
53
+ observer._onSelectionChange();
54
+ expect(handler.callCount).toBe(0);
55
+ expect(observer.selecting).toBe(false);
56
+ });
43
57
  });