@internetarchive/bookreader 5.0.0-115 → 5.0.0-116

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/bookreader",
3
- "version": "5.0.0-115",
3
+ "version": "5.0.0-116",
4
4
  "description": "The Internet Archive BookReader.",
5
5
  "type": "module",
6
6
  "files": [
@@ -132,14 +132,17 @@
132
132
  "test:e2e": "npm run build && npx testcafe",
133
133
  "test:e2e:dev": "npx testcafe --live --dev",
134
134
  "DOCS:update:test-deps": "If CI succeeds, these should be good to update",
135
- "update:test-deps": "npm i @babel/eslint-parser@latest @open-wc/testing-helpers@latest @types/jest@latest eslint@7 eslint-plugin-testcafe@latest jest@latest jest-environment-jsdom@latest sinon@latest testcafe@latest",
135
+ "update:test-deps": "npm i @babel/eslint-parser@latest @open-wc/testing-helpers@latest @types/jest@latest eslint@7 eslint-plugin-no-jquery@latest eslint-plugin-testcafe@latest jest@latest jest-environment-jsdom@latest sinon@latest testcafe@latest",
136
136
  "update:test-deps:test": "npm run lint && npm run test && npm run test:e2e",
137
137
  "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",
138
138
  "update:build-deps": "npm i @babel/core@latest @babel/preset-env@latest @babel/plugin-proposal-class-properties@latest @babel/plugin-proposal-decorators@latest babel-loader@latest core-js@latest regenerator-runtime@latest sass@latest svgo@latest webpack@latest webpack-cli@latest",
139
139
  "update:build-deps:test": "npm run build",
140
140
  "DOCS:update:dev-deps": "Packages used for development",
141
- "update:dev-deps": "npm i @iiif/presentation-2@latest @iiif/presentation-3@latest concurrently@latest cpx2@latest http-server@latest live-server@latest",
141
+ "update:dev-deps": "npm i @iiif/presentation-2@latest @iiif/presentation-3@latest concurrently@latest cpx2@latest http-server@latest hypothesis@latest live-server@latest testcafe-browser-provider-browserstack@latest",
142
+ "DOCS:update:runtime-deps": "Core runtime dependencies; do an npm run build + visual check after updating",
143
+ "update:runtime-deps": "npm i @webcomponents/webcomponentsjs@latest interactjs@latest iso-language-codes@latest jquery@latest jquery-colorbox@latest jquery-ui@latest jquery-ui-touch-punch@latest jquery.browser@latest lit@latest soundmanager2@latest",
144
+ "update:runtime-deps:test": "npm run build",
142
145
  "DOCS:update:ia-deps": "Packages by @internetarchive",
143
- "update:ia-deps": "npm i @internetarchive/ia-activity-indicator@latest @internetarchive/ia-item-navigator@latest @internetarchive/icon-bookmark@latest @internetarchive/icon-close@latest @internetarchive/icon-dl@latest @internetarchive/icon-edit-pencil@latest @internetarchive/icon-ia-logo@latest @internetarchive/icon-magnify-minus@latest @internetarchive/icon-magnify-plus@latest @internetarchive/icon-search@latest @internetarchive/icon-share@latest @internetarchive/icon-toc@latest @internetarchive/icon-visual-adjustment@latest @internetarchive/modal-manager@latest @internetarchive/shared-resize-observer@latest"
146
+ "update:ia-deps": "npm i @internetarchive/bergamot-translator@latest @internetarchive/ia-activity-indicator@latest @internetarchive/ia-item-navigator@latest @internetarchive/icon-bookmark@latest @internetarchive/icon-close@latest @internetarchive/icon-dl@latest @internetarchive/icon-edit-pencil@latest @internetarchive/icon-ia-logo@latest @internetarchive/icon-magnify-minus@latest @internetarchive/icon-magnify-plus@latest @internetarchive/icon-search@latest @internetarchive/icon-share@latest @internetarchive/icon-toc@latest @internetarchive/icon-visual-adjustment@latest @internetarchive/modal-manager@latest @internetarchive/shared-resize-observer@latest"
144
147
  }
145
148
  }
package/src/BookReader.js CHANGED
@@ -41,7 +41,6 @@ import { ModeThumb } from './BookReader/ModeThumb.js';
41
41
  import { ImageCache } from './BookReader/ImageCache.js';
42
42
  import { PageContainer } from './BookReader/PageContainer.js';
43
43
  import { NAMED_REDUCE_SETS } from './BookReader/ReduceSet.js';
44
- import {BookReaderTextFragment} from './util/TextSelectionManager.js';
45
44
 
46
45
  /**
47
46
  * BookReader
@@ -156,6 +155,7 @@ BookReader.optionOverrides = BookReader.optionOverrides || {};
156
155
  /**
157
156
  * Setup
158
157
  * It is separate from the constructor, so plugins can extend.
158
+ * @constructor
159
159
  * @param {BookReaderOptions} options
160
160
  */
161
161
  BookReader.prototype.setup = function(options) {
@@ -645,6 +645,7 @@ BookReader.prototype.init = function() {
645
645
  this.pageScale = this.reduce; // preserve current reduce
646
646
 
647
647
  const params = this.initParams();
648
+ this.urlPlugin?.pullFromAddressBar();
648
649
  this.firstIndex = params.index ? params.index : 0;
649
650
 
650
651
  // Setup Navbars and other UI
@@ -1960,6 +1961,10 @@ BookReader.prototype.queryStringFromParams = function(
1960
1961
  ) {
1961
1962
  const newParams = new URLSearchParams(currQueryString);
1962
1963
 
1964
+ // Never write back UI-only params that are consumed on load
1965
+ // TODO: Should ideally stick around until next URL change
1966
+ newParams.delete('focus');
1967
+
1963
1968
  if (params.view) {
1964
1969
  // Set ?view=theater when fullscreen
1965
1970
  newParams.set('view', params.view);
@@ -1972,25 +1977,8 @@ BookReader.prototype.queryStringFromParams = function(
1972
1977
  newParams.set('q', params.search);
1973
1978
  }
1974
1979
 
1975
- /** @type {BookReaderTextFragment | null} */
1976
- let textFragmentParam = null;
1977
- // Need to pull out text separately to avoid the spaces becoming encoded as +, which
1978
- // the browser seems not to handle with the text fragment
1979
- if (newParams.get('text')) {
1980
- newParams.delete('text');
1981
- textFragmentParam = BookReaderTextFragment.fromUrl(currQueryString, this.book, this.firstIndex);
1982
- }
1983
-
1984
- // https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/toString
1985
- // Note: This method returns the query string without the question mark.
1986
- let result = newParams.toString();
1987
- if (textFragmentParam) {
1988
- if (result) result += '&';
1989
- result += `text=${textFragmentParam.toUrlString()}`;
1990
- }
1991
- if (result) result = '?' + result;
1992
-
1993
- return result;
1980
+ const result = newParams.toString();
1981
+ return result ? `?${result}` : '';
1994
1982
  };
1995
1983
 
1996
1984
  /**
@@ -5,6 +5,7 @@ import { applyVariables } from '../util/strings.js';
5
5
  import { Cache } from '../util/cache.js';
6
6
  import { toISO6391 } from './tts/utils.js';
7
7
  import { BookReaderTextFragment, renderHighlight, TextSelectionManager } from '../util/TextSelectionManager.js';
8
+ import { genMap, lookAroundWindow, zip } from '../util/generators.js';
8
9
  /** @typedef {import('../util/strings.js').StringWithVars} StringWithVars */
9
10
  /** @typedef {import('../BookReader/PageContainer.js').PageContainer} PageContainer */
10
11
 
@@ -66,8 +67,9 @@ export class TextSelectionPlugin extends BookReaderPlugin {
66
67
  this.textSelectionManager.init();
67
68
 
68
69
  // Init text fragment
69
- this.targetTextFragment = BookReaderTextFragment.fromUrl(location.search, this.br.book, this.br.firstIndex);
70
- if (this.targetTextFragment) {
70
+ const textParam = new URLSearchParams(location.search).get('text');
71
+ if (textParam) {
72
+ this.targetTextFragment = BookReaderTextFragment.fromString(textParam, this.br.book, this.br.firstIndex);
71
73
  const targetTextFragment = this.targetTextFragment;
72
74
  this.br.on('textLayerVisible', async (_, {pageContainerEl, textLayer}) => {
73
75
  const pageIndex = targetTextFragment.pageIndex;
@@ -428,94 +430,6 @@ function augmentLine(line) {
428
430
  };
429
431
  }
430
432
 
431
- /**
432
- * @template T
433
- * Get the i-th element of an iterable
434
- * @param {Iterable<T>} iterable
435
- * @param {number} index
436
- */
437
- export function genAt(iterable, index) {
438
- let i = 0;
439
- for (const x of iterable) {
440
- if (i == index) return x;
441
- i++;
442
- }
443
- return undefined;
444
- }
445
-
446
- /**
447
- * @template T
448
- * Generator version of filter
449
- * @param {Iterable<T>} iterable
450
- * @param {function(T): boolean} fn
451
- */
452
- export function* genFilter(iterable, fn) {
453
- for (const x of iterable) {
454
- if (fn(x)) yield x;
455
- }
456
- }
457
-
458
- /**
459
- * @template TFrom, TTo
460
- * Generator version of map
461
- * @param {Iterable<TFrom>} gen
462
- * @param {function(TFrom): TTo} fn
463
- * @returns {Iterable<TTo>}
464
- */
465
- export function* genMap(gen, fn) {
466
- for (const x of gen) yield fn(x);
467
- }
468
-
469
- /**
470
- * @template T
471
- * Generator that provides a sliding window of 3 elements,
472
- * prev, current, and next.
473
- * @param {Iterable<T>} gen
474
- * @returns {Iterable<[T | undefined, T, T | undefined]>}
475
- */
476
- export function* lookAroundWindow(gen) {
477
- let prev = undefined;
478
- let cur = undefined;
479
- let next = undefined;
480
- for (const x of gen) {
481
- if (typeof cur !== 'undefined') {
482
- next = x;
483
- yield [prev, cur, next];
484
- }
485
- prev = cur;
486
- cur = x;
487
- next = undefined;
488
- }
489
-
490
- if (typeof cur !== 'undefined') {
491
- yield [prev, cur, next];
492
- }
493
- }
494
-
495
- /**
496
- * @template T1, T2
497
- * Lazy zip implementation to avoid importing lodash
498
- * Expects iterators to be of the same length
499
- * @param {Iterable<T1>} gen1
500
- * @param {Iterable<T2>} gen2
501
- * @returns {Iterable<[T1, T2]>}
502
- */
503
- export function* zip(gen1, gen2) {
504
- const it1 = gen1[Symbol.iterator]();
505
- const it2 = gen2[Symbol.iterator]();
506
- while (true) {
507
- const r1 = it1.next();
508
- const r2 = it2.next();
509
- if (r1.done && r2.done) {
510
- return;
511
- }
512
- if (r1.done || r2.done) {
513
- throw new Error('zip: one of the iterators is done');
514
- }
515
- yield [r1.value, r2.value];
516
- }
517
- }
518
-
519
433
  /**
520
434
  * [left, bottom, right, top]
521
435
  * @param {Array<[number, number, number, number]>} bounds
@@ -104,6 +104,8 @@ export class SearchPlugin extends BookReaderPlugin {
104
104
  this.options.initialSearchTerm,
105
105
  { goToFirstResult: this.options.goToFirstResult, suppressFragmentChange: false },
106
106
  );
107
+ } else if (this.br.urlPlugin?.getUrlParam('focus') === 'search') {
108
+ this.searchView.toggleSidebar();
107
109
  }
108
110
  }
109
111
 
@@ -1,8 +1,18 @@
1
+ /**
2
+ * @typedef {Object} UrlSchemaEntry
3
+ * @property {string} name
4
+ * @property {'path' | 'query_param'} position
5
+ * @property {string} [default]
6
+ * @property {string} [deprecated_for]
7
+ * @property {boolean} [readOnly] - If true, the param is read from the URL but never written back
8
+ */
9
+
1
10
  export class UrlPlugin {
2
11
  constructor(options = {}) {
3
12
  this.bookReaderOptions = options;
4
13
 
5
14
  // the canonical order of elements is important in the path and query string
15
+ /** @type {UrlSchemaEntry[]} */
6
16
  this.urlSchema = [
7
17
  { name: 'page', position: 'path', default: 'n0' },
8
18
  { name: 'mode', position: 'path', default: '2up' },
@@ -11,6 +21,7 @@ export class UrlPlugin {
11
21
  { name: 'sort', position: 'query_param' },
12
22
  { name: 'view', position: 'query_param' },
13
23
  { name: 'admin', position: 'query_param' },
24
+ { name: 'focus', position: 'query_param', readOnly: true },
14
25
  ];
15
26
 
16
27
  this.urlState = {};
@@ -36,6 +47,7 @@ export class UrlPlugin {
36
47
  if (schema?.deprecated_for) {
37
48
  schema = this.urlSchema.find(schemaKey => schemaKey.name === schema.deprecated_for);
38
49
  }
50
+ if (schema?.readOnly) return;
39
51
  if (schema?.position == 'path') {
40
52
  pathParams[schema?.name] = urlState[key];
41
53
  } else {