@internetarchive/bookreader 5.0.0-88 → 5.0.0-89

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 (47) hide show
  1. package/BookReader/BookReader.css +14 -0
  2. package/BookReader/BookReader.js +1 -1
  3. package/BookReader/BookReader.js.map +1 -1
  4. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  5. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  6. package/BookReader/plugins/plugin.autoplay.js +1 -1
  7. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  8. package/BookReader/plugins/plugin.iiif.js +1 -1
  9. package/BookReader/plugins/plugin.iiif.js.map +1 -1
  10. package/BookReader/plugins/plugin.resume.js +1 -1
  11. package/BookReader/plugins/plugin.resume.js.map +1 -1
  12. package/BookReader/plugins/plugin.text_selection.js +1 -1
  13. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  14. package/BookReader/plugins/plugin.tts.js +1 -1
  15. package/BookReader/plugins/plugin.tts.js.map +1 -1
  16. package/BookReader/plugins/plugin.url.js +1 -1
  17. package/BookReader/plugins/plugin.url.js.map +1 -1
  18. package/CHANGELOG.md +10 -0
  19. package/codecov.yml +1 -1
  20. package/package.json +1 -1
  21. package/src/BookReader/ImageCache.js +48 -15
  22. package/src/BookReader/Mode2UpLit.js +3 -2
  23. package/src/BookReader/PageContainer.js +41 -22
  24. package/src/BookReader/options.js +24 -3
  25. package/src/BookReader/utils.js +10 -0
  26. package/src/BookReader.js +89 -38
  27. package/src/BookReaderPlugin.js +16 -0
  28. package/src/css/_BRpages.scss +21 -2
  29. package/src/plugins/plugin.autoplay.js +98 -102
  30. package/src/plugins/plugin.iiif.js +16 -30
  31. package/src/plugins/plugin.resume.js +54 -51
  32. package/src/plugins/plugin.text_selection.js +68 -76
  33. package/src/plugins/tts/AbstractTTSEngine.js +2 -4
  34. package/src/plugins/tts/PageChunk.js +5 -9
  35. package/src/plugins/tts/PageChunkIterator.js +3 -5
  36. package/src/plugins/tts/plugin.tts.js +309 -329
  37. package/src/plugins/url/plugin.url.js +1 -1
  38. package/src/util/strings.js +1 -0
  39. package/tests/e2e/autoplay.test.js +8 -5
  40. package/tests/e2e/helpers/base.js +2 -2
  41. package/tests/e2e/helpers/mockSearch.js +6 -9
  42. package/tests/jest/BookReader/PageContainer.test.js +96 -55
  43. package/tests/jest/BookReader/utils.test.js +21 -0
  44. package/tests/jest/BookReader.test.js +13 -12
  45. package/tests/jest/plugins/plugin.autoplay.test.js +9 -22
  46. package/tests/jest/plugins/plugin.resume.test.js +19 -32
  47. package/tests/jest/plugins/plugin.text_selection.test.js +23 -24
@@ -1,128 +1,124 @@
1
- /*global BookReader */
1
+ // @ts-check
2
+ import { EVENTS } from "../BookReader/events";
3
+ import { parseAnimationSpeed } from "../BookReader/utils";
4
+ import { BookReaderPlugin } from "../BookReaderPlugin";
2
5
 
3
6
  /**
4
7
  * Plugin which adds an autoplay feature. Useful for kiosk situations.
5
8
  */
6
- jQuery.extend(BookReader.defaultOptions, {
7
- enableAutoPlayPlugin: true,
8
- });
9
-
10
- /**
11
- * @override BookReader.setup
12
- */
13
- BookReader.prototype.setup = (function(super_) {
14
- return function (options) {
15
- super_.call(this, options);
16
-
17
- this.autoTimer = null;
18
- this.flipDelay = 5000;
19
- };
20
- })(BookReader.prototype.setup);
9
+ export class AutoplayPlugin extends BookReaderPlugin {
10
+ options = {
11
+ enabled: true,
12
+ /**
13
+ * @type {number | 'fast' | 'slow'}
14
+ * How quickly the flip animation should run.
15
+ **/
16
+ flipSpeed: 1500,
17
+ /** How long to pause on each page between flips */
18
+ flipDelay: 5000,
19
+ /** Allow controlling the autoflip/speed/delay from the url */
20
+ urlParams: true,
21
+ }
21
22
 
22
- /**
23
- * @override BookReader.init
24
- */
25
- BookReader.prototype.init = (function(super_) {
26
- return function (options) {
27
- super_.call(this, options);
23
+ timer = null;
28
24
 
29
- if (!this.options.enableAutoPlayPlugin) return;
25
+ /** @override */
26
+ init() {
27
+ if (!this.options.enabled) return;
30
28
 
31
- this.bind(BookReader.eventNames.stop, () => this.autoStop());
29
+ this.br.bind(EVENTS.stop, () => this.stop());
32
30
 
33
- const urlParams = new URLSearchParams(window.location.search);
34
- if (urlParams.get('autoflip') === '1') {
35
- this.autoToggle();
31
+ if (this.options.urlParams) {
32
+ const urlParams = new URLSearchParams(window.location.search);
33
+ if (urlParams.get('flipSpeed')) {
34
+ this.options.flipSpeed = parseAnimationSpeed(urlParams.get('flipSpeed')) || this.options.flipSpeed;
35
+ }
36
+ if (urlParams.get('flipDelay')) {
37
+ this.options.flipDelay = parseAnimationSpeed(urlParams.get('flipDelay')) || this.options.flipDelay;
38
+ }
39
+ if (urlParams.get('autoflip') === '1') {
40
+ this.toggle();
41
+ }
36
42
  }
37
- };
38
- })(BookReader.prototype.init);
39
-
40
- /**
41
- * @override BookReader.bindNavigationHandlers
42
- */
43
- BookReader.prototype.bindNavigationHandlers = (function(super_) {
44
- return function() {
45
- super_.call(this);
43
+ }
46
44
 
47
- if (!this.options.enableAutoPlayPlugin) return;
45
+ /** @override */
46
+ _bindNavigationHandlers() {
47
+ if (!this.options.enabled) return;
48
48
 
49
- const jIcons = this.$('.BRicon');
49
+ const jIcons = this.br.$('.BRicon');
50
50
 
51
- jIcons.filter('.play').click(() => {
52
- this.autoToggle();
51
+ jIcons.filter('.play').on('click', () => {
52
+ this.toggle();
53
53
  return false;
54
54
  });
55
55
 
56
- jIcons.filter('.pause').click(() => {
57
- this.autoToggle();
56
+ jIcons.filter('.pause').on('click', () => {
57
+ this.toggle();
58
58
  return false;
59
59
  });
60
- };
61
- })(BookReader.prototype.bindNavigationHandlers);
62
-
63
- /**
64
- * Starts autoplay mode
65
- * @param {object} overrides
66
- * @param {number} overrides.flipSpeed
67
- * @param {number} overrides.flipDelay
68
- */
69
- BookReader.prototype.autoToggle = function(overrides) {
70
- if (!this.options.enableAutoPlayPlugin) return;
71
-
72
- const options = $.extend({
73
- flipSpeed: this.flipSpeed,
74
- flipDelay: this.flipDelay,
75
- }, overrides);
76
-
77
- this.flipSpeed = typeof options.flipSpeed === "number" ? options.flipSpeed : this.flipSpeed;
78
- this.flipDelay = typeof options.flipDelay === "number" ? options.flipDelay : this.flipDelay;
79
- this.trigger(BookReader.eventNames.stop);
80
-
81
- let bComingFrom1up = false;
82
- if (this.constMode2up != this.mode) {
83
- bComingFrom1up = true;
84
- this.switchMode(this.constMode2up);
85
60
  }
86
61
 
87
- if (null == this.autoTimer) {
88
- // $$$ Draw events currently cause layout problems when they occur during animation.
89
- // There is a specific problem when changing from 1-up immediately to autoplay in RTL so
90
- // we workaround for now by not triggering immediate animation in that case.
91
- // See https://bugs.launchpad.net/gnubook/+bug/328327
92
- if (('rl' == this.pageProgression) && bComingFrom1up) {
93
- // don't flip immediately -- wait until timer fires
94
- } else {
95
- // flip immediately
96
- this.next({ triggerStop: false });
62
+ /**
63
+ * Starts autoplay mode
64
+ * @param {object} overrides
65
+ * @param {number} overrides.flipSpeed
66
+ * @param {number} overrides.flipDelay
67
+ */
68
+ toggle(overrides = null) {
69
+ if (!this.options.enabled) return;
70
+
71
+ Object.assign(this.options, overrides);
72
+ this.br.trigger(EVENTS.stop);
73
+
74
+ let bComingFrom1up = false;
75
+ if (this.br.constMode2up != this.br.mode) {
76
+ bComingFrom1up = true;
77
+ this.br.switchMode(this.br.constMode2up);
97
78
  }
98
79
 
99
- this.$('.play').hide();
100
- this.$('.pause').show();
101
- this.autoTimer = setInterval(() => {
102
- if (this.animating) return;
103
-
104
- if (Math.max(this.twoPage.currentIndexL, this.twoPage.currentIndexR) >= this.book.getNumLeafs() - 1) {
105
- this.prev({ triggerStop: false }); // $$$ really what we want?
80
+ if (null == this.timer) {
81
+ // $$$ Draw events currently cause layout problems when they occur during animation.
82
+ // There is a specific problem when changing from 1-up immediately to autoplay in RTL so
83
+ // we workaround for now by not triggering immediate animation in that case.
84
+ // See https://bugs.launchpad.net/gnubook/+bug/328327
85
+ if (('rl' == this.br.pageProgression) && bComingFrom1up) {
86
+ // don't flip immediately -- wait until timer fires
106
87
  } else {
107
- this.next({ triggerStop: false });
88
+ // flip immediately
89
+ this.br.next({ triggerStop: false, flipSpeed: this.options.flipSpeed });
108
90
  }
109
- }, this.flipDelay);
110
- } else {
111
- this.autoStop();
91
+
92
+ this.br.$('.play').hide();
93
+ this.br.$('.pause').show();
94
+ this.timer = setInterval(() => {
95
+ if (this.br.animating) return;
96
+
97
+ if (Math.max(this.br.twoPage.currentIndexL, this.br.twoPage.currentIndexR) >= this.br.book.getNumLeafs() - 1) {
98
+ this.br.prev({ triggerStop: false, flipSpeed: this.options.flipSpeed }); // $$$ really what we want?
99
+ } else {
100
+ this.br.next({ triggerStop: false, flipSpeed: this.options.flipSpeed });
101
+ }
102
+ }, parseAnimationSpeed(this.options.flipDelay));
103
+ } else {
104
+ this.stop();
105
+ }
112
106
  }
113
- };
114
107
 
115
- /**
116
- * Stop autoplay mode, allowing animations to finish
117
- */
118
- BookReader.prototype.autoStop = function() {
119
- if (!this.options.enableAutoPlayPlugin) return;
120
-
121
- if (null != this.autoTimer) {
122
- clearInterval(this.autoTimer);
123
- this.flipSpeed = 'fast';
124
- this.$('.pause').hide();
125
- this.$('.play').show();
126
- this.autoTimer = null;
108
+ /**
109
+ * Stop autoplay mode, allowing animations to finish
110
+ */
111
+ stop() {
112
+ if (!this.options.enabled) return;
113
+
114
+ if (null != this.timer) {
115
+ clearInterval(this.timer);
116
+ this.br.$('.pause').hide();
117
+ this.br.$('.play').show();
118
+ this.timer = null;
119
+ }
127
120
  }
128
- };
121
+ }
122
+
123
+ const BookReader = /** @type {typeof import('../BookReader').default} */(window.BookReader);
124
+ BookReader?.registerPlugin('autoplay', AutoplayPlugin);
@@ -1,17 +1,22 @@
1
1
  // @ts-check
2
+ import { BookReaderPlugin } from '../BookReaderPlugin';
3
+
2
4
  const BookReader = /** @type {typeof import('../BookReader').default} */(window.BookReader);
3
5
 
4
- export const DEFAULT_OPTIONS = {
5
- enabled: true,
6
- /** @type {import('@iiif/presentation-3').Manifest | import('@iiif/presentation-2').Manifest} */
7
- manifest: null,
8
- };
6
+ export class IiifPlugin extends BookReaderPlugin {
7
+ options = {
8
+ enabled: true,
9
+ /** @type {import('@iiif/presentation-3').Manifest | import('@iiif/presentation-2').Manifest} */
10
+ manifest: null,
11
+ }
9
12
 
10
- class IIIFPlugin {
11
- constructor(options = DEFAULT_OPTIONS, optionVariables) {
12
- this.options = options;
13
- this.optionVariables = optionVariables;
14
- this.manifest = options.manifest;
13
+ setup(options) {
14
+ super.setup(options);
15
+ this.manifest = this.options.manifest;
16
+
17
+ if (this.options.enabled) {
18
+ Object.assign(this.br.options, this.load());
19
+ }
15
20
  }
16
21
 
17
22
  load() {
@@ -133,23 +138,4 @@ function resolveInternationalString(internationalString) {
133
138
  return (internationalString[navigator.language] || internationalString[anyLang])[0];
134
139
  }
135
140
 
136
- export class BookReaderWithIIIFPlugin extends BookReader {
137
- setup(options) {
138
- const pluginOpts = Object.assign(
139
- {},
140
- DEFAULT_OPTIONS,
141
- options.plugins.iiif,
142
- );
143
-
144
- if (pluginOpts.enabled) {
145
- this.iiifPlugin = new IIIFPlugin(pluginOpts, options.vars);
146
- // Write this back; this way the plugin is the source of truth, and BR just
147
- // contains a reference to it.
148
- options.plugins.iiif = this.iiifPlugin.options;
149
- Object.assign(options, this.iiifPlugin.load());
150
- }
151
- return super.setup(options);
152
- }
153
- }
154
- window.BookReader = BookReaderWithIIIFPlugin;
155
- export default BookReaderWithIIIFPlugin;
141
+ BookReader?.registerPlugin('iiif', IiifPlugin);
@@ -1,3 +1,5 @@
1
+ import { EVENTS } from '../BookReader/events.js';
2
+ import { BookReaderPlugin } from '../BookReaderPlugin.js';
1
3
  import * as docCookies from '../util/docCookies.js';
2
4
 
3
5
  /* global BookReader */
@@ -8,61 +10,62 @@ BookReader.docCookies = docCookies;
8
10
  /**
9
11
  * Plugin to remember the current page number in a cookie
10
12
  */
11
- jQuery.extend(BookReader.defaultOptions, {
12
- enablePageResume: true,
13
- /** @type {string|null} eg '/', '/details/id' */
14
- resumeCookiePath: null,
15
- });
13
+ export class ResumePlugin extends BookReaderPlugin {
14
+ options = {
15
+ enabled: true,
16
+ /** @type {string|null} eg '/', '/details/id' */
17
+ cookiePath: null,
18
+ }
16
19
 
17
- /** @override */
18
- BookReader.prototype.init = (function(super_) {
19
- return function() {
20
- super_.call(this);
21
- if (this.options.enablePageResume) {
22
- this.bind(BookReader.eventNames.fragmentChange, () => {
23
- const params = this.paramsFromCurrent();
20
+ /** @override */
21
+ init() {
22
+ if (this.options.enabled) {
23
+ this.br.bind(EVENTS.fragmentChange, () => {
24
+ const params = this.br.paramsFromCurrent();
24
25
  this.updateResumeValue(params.index);
25
26
  });
26
27
  }
27
- };
28
- })(BookReader.prototype.init);
28
+ }
29
29
 
30
- /**
31
- * Gets page resume value, for remembering reader's page
32
- * Can be overridden for different implementation
33
- *
34
- * @return {number|null}
35
- */
36
- BookReader.prototype.getResumeValue = function() {
37
- const val = BookReader.docCookies.getItem('br-resume');
38
- if (val !== null) return parseInt(val);
39
- else return null;
40
- };
30
+ /**
31
+ * Gets page resume value, for remembering reader's page
32
+ * Can be overridden for different implementation
33
+ *
34
+ * @return {number|null}
35
+ */
36
+ getResumeValue() {
37
+ const val = BookReader.docCookies.getItem('br-resume');
38
+ if (val !== null) return parseInt(val);
39
+ else return null;
40
+ }
41
41
 
42
- /**
43
- * Return cookie path using pathname up to /page/... or /mode/...
44
- * using window.location.pathname for urlPathPart:
45
- * - matches encoding
46
- * - ignores querystring part
47
- * - ignores fragment part (after #)
48
- * @param {string} urlPathPart - window.location.pathname
49
- */
50
- BookReader.prototype.getCookiePath = function(urlPathPart) {
51
- return urlPathPart.match('.+?(?=/page/|/mode/|$)')[0];
52
- };
42
+ /**
43
+ * Return cookie path using pathname up to /page/... or /mode/...
44
+ * using window.location.pathname for urlPathPart:
45
+ * - matches encoding
46
+ * - ignores querystring part
47
+ * - ignores fragment part (after #)
48
+ * @param {string} urlPathPart - window.location.pathname
49
+ */
50
+ getCookiePath(urlPathPart) {
51
+ return urlPathPart.match('.+?(?=/page/|/mode/|$)')[0];
52
+ }
53
53
 
54
- /**
55
- * Sets page resume value, for remembering reader's page
56
- * Can be overridden for different implementation
57
- *
58
- * @param {Number} index leaf index
59
- * @param {string} [cookieName]
60
- */
61
- BookReader.prototype.updateResumeValue = function(index, cookieName) {
62
- const ttl = new Date(+new Date + 12096e5); // 2 weeks
63
- // For multiple files in item, leave resumeCookiePath blank
64
- // It's likely we can remove resumeCookiePath using getCookiePath()
65
- const path = this.options.resumeCookiePath
66
- || this.getCookiePath(window.location.pathname);
67
- BookReader.docCookies.setItem(cookieName || 'br-resume', index, ttl, path, null, false);
68
- };
54
+ /**
55
+ * Sets page resume value, for remembering reader's page
56
+ * Can be overridden for different implementation
57
+ *
58
+ * @param {Number} index leaf index
59
+ * @param {string} [cookieName]
60
+ */
61
+ updateResumeValue(index, cookieName) {
62
+ const ttl = new Date(+new Date + 12096e5); // 2 weeks
63
+ // For multiple files in item, leave cookiePath blank
64
+ // It's likely we can remove cookiePath using getCookiePath()
65
+ const path = this.options.cookiePath
66
+ || this.getCookiePath(window.location.pathname);
67
+ BookReader.docCookies.setItem(cookieName || 'br-resume', index, ttl, path, null, false);
68
+ }
69
+ }
70
+
71
+ BookReader?.registerPlugin('resume', ResumePlugin);
@@ -1,23 +1,13 @@
1
1
  //@ts-check
2
2
  import { createDIVPageLayer } from '../BookReader/PageContainer.js';
3
3
  import { SelectionObserver } from '../BookReader/utils/SelectionObserver.js';
4
+ import { BookReaderPlugin } from '../BookReaderPlugin.js';
4
5
  import { applyVariables } from '../util/strings.js';
5
6
  /** @typedef {import('../util/strings.js').StringWithVars} StringWithVars */
6
7
  /** @typedef {import('../BookReader/PageContainer.js').PageContainer} PageContainer */
7
8
 
8
9
  const BookReader = /** @type {typeof import('../BookReader').default} */(window.BookReader);
9
10
 
10
- export const DEFAULT_OPTIONS = {
11
- enabled: true,
12
- /** @type {StringWithVars} The URL to fetch the entire DJVU xml. Supports options.vars */
13
- fullDjvuXmlUrl: null,
14
- /** @type {StringWithVars} The URL to fetch a single page of the DJVU xml. Supports options.vars. Also has {{pageIndex}} */
15
- singlePageDjvuXmlUrl: null,
16
- /** Whether to fetch the XML as a jsonp */
17
- jsonp: false,
18
- };
19
- /** @typedef {typeof DEFAULT_OPTIONS} TextSelectionPluginOptions */
20
-
21
11
  /**
22
12
  * @template T
23
13
  */
@@ -39,30 +29,73 @@ export class Cache {
39
29
  }
40
30
  }
41
31
 
42
- export class TextSelectionPlugin {
32
+ export class TextSelectionPlugin extends BookReaderPlugin {
33
+ options = {
34
+ enabled: true,
35
+ /** @type {StringWithVars} The URL to fetch the entire DJVU xml. Supports options.vars */
36
+ fullDjvuXmlUrl: null,
37
+ /** @type {StringWithVars} The URL to fetch a single page of the DJVU xml. Supports options.vars. Also has {{pageIndex}} */
38
+ singlePageDjvuXmlUrl: null,
39
+ /** Whether to fetch the XML as a jsonp */
40
+ jsonp: false,
41
+ }
42
+
43
+ /**@type {PromiseLike<JQuery<HTMLElement>|undefined>} */
44
+ djvuPagesPromise = null;
45
+
46
+ /** @type {Cache<{index: number, response: any}>} */
47
+ pageTextCache = new Cache();
48
+
43
49
  /**
44
- * @param {'lr' | 'rl'} pageProgression In the future this should be in the ocr file
45
- * since a book being right to left doesn't mean the ocr is right to left. But for
46
- * now we do make that assumption.
50
+ * Sometimes there are too many words on a page, and the browser becomes near
51
+ * unusable. For now don't render text layer for pages with too many words.
47
52
  */
48
- constructor(options = DEFAULT_OPTIONS, optionVariables, pageProgression = 'lr') {
49
- this.options = options;
50
- this.optionVariables = optionVariables;
51
- /**@type {PromiseLike<JQuery<HTMLElement>|undefined>} */
52
- this.djvuPagesPromise = null;
53
+ maxWordRendered = 2500;
54
+
55
+ /**
56
+ * @param {import('../BookReader.js').default} br
57
+ */
58
+ constructor(br) {
59
+ super(br);
60
+ // In the future this should be in the ocr file
61
+ // since a book being right to left doesn't mean the ocr is right to left. But for
62
+ // now we do make that assumption.
53
63
  /** Whether the book is right-to-left */
54
- this.rtl = pageProgression === 'rl';
64
+ this.rtl = this.br.pageProgression === 'rl';
65
+ this.selectionObserver = new SelectionObserver('.BRtextLayer', this._onSelectionChange);
66
+ }
55
67
 
56
- /** @type {Cache<{index: number, response: any}>} */
57
- this.pageTextCache = new Cache();
68
+ /** @override */
69
+ init() {
70
+ if (!this.options.enabled) return;
58
71
 
59
- /**
60
- * Sometimes there are too many words on a page, and the browser becomes near
61
- * unusable. For now don't render text layer for pages with too many words.
62
- */
63
- this.maxWordRendered = 2500;
72
+ this.loadData();
64
73
 
65
- this.selectionObserver = new SelectionObserver('.BRtextLayer', this._onSelectionChange);
74
+ this.selectionObserver.attach();
75
+ new SelectionObserver('.BRtextLayer', (selectEvent) => {
76
+ // Track how often selection is used
77
+ if (selectEvent == 'started') {
78
+ this.br._plugins.archiveAnalytics?.sendEvent('BookReader', 'SelectStart');
79
+
80
+ // Set a class on the page to avoid hiding it when zooming/etc
81
+ this.br.refs.$br.find('.BRpagecontainer--hasSelection').removeClass('BRpagecontainer--hasSelection');
82
+ $(window.getSelection().anchorNode).closest('.BRpagecontainer').addClass('BRpagecontainer--hasSelection');
83
+ }
84
+ }).attach();
85
+ }
86
+
87
+ /**
88
+ * @override
89
+ * @param {PageContainer} pageContainer
90
+ * @returns {PageContainer}
91
+ */
92
+ _configurePageContainer(pageContainer) {
93
+ // Disable if thumb mode; it's too janky
94
+ // .page can be null for "pre-cover" region
95
+ if (this.br.mode !== this.br.constModeThumb && pageContainer.page) {
96
+ this.createTextLayer(pageContainer);
97
+ }
98
+ return pageContainer;
66
99
  }
67
100
 
68
101
  /**
@@ -79,18 +112,16 @@ export class TextSelectionPlugin {
79
112
  }
80
113
  }
81
114
 
82
- init() {
83
- this.selectionObserver.attach();
84
-
115
+ loadData() {
85
116
  // Only fetch the full djvu xml if the single page url isn't there
86
117
  if (this.options.singlePageDjvuXmlUrl) return;
87
118
  this.djvuPagesPromise = $.ajax({
88
119
  type: "GET",
89
- url: applyVariables(this.options.fullDjvuXmlUrl, this.optionVariables),
120
+ url: applyVariables(this.options.fullDjvuXmlUrl, this.br.options.vars),
90
121
  dataType: this.options.jsonp ? "jsonp" : "html",
91
122
  cache: true,
92
123
  xhrFields: {
93
- withCredentials: window.br.protected,
124
+ withCredentials: this.br.protected,
94
125
  },
95
126
  error: (e) => undefined,
96
127
  }).then((res) => {
@@ -115,11 +146,11 @@ export class TextSelectionPlugin {
115
146
  }
116
147
  const res = await $.ajax({
117
148
  type: "GET",
118
- url: applyVariables(this.options.singlePageDjvuXmlUrl, this.optionVariables, { pageIndex: index }),
149
+ url: applyVariables(this.options.singlePageDjvuXmlUrl, this.br.options.vars, { pageIndex: index }),
119
150
  dataType: this.options.jsonp ? "jsonp" : "html",
120
151
  cache: true,
121
152
  xhrFields: {
122
- withCredentials: window.br.protected,
153
+ withCredentials: this.br.protected,
123
154
  },
124
155
  error: (e) => undefined,
125
156
  });
@@ -410,46 +441,7 @@ export class TextSelectionPlugin {
410
441
  }
411
442
  }
412
443
 
413
- export class BookreaderWithTextSelection extends BookReader {
414
- init() {
415
- const options = Object.assign({}, DEFAULT_OPTIONS, this.options.plugins.textSelection);
416
- if (options.enabled) {
417
- this.textSelectionPlugin = new TextSelectionPlugin(options, this.options.vars, this.pageProgression);
418
- // Write this back; this way the plugin is the source of truth, and BR just
419
- // contains a reference to it.
420
- this.options.plugins.textSelection = options;
421
- this.textSelectionPlugin.init();
422
-
423
- new SelectionObserver('.BRtextLayer', (selectEvent) => {
424
- // Track how often selection is used
425
- if (selectEvent == 'started') {
426
- this._plugins.archiveAnalytics?.sendEvent('BookReader', 'SelectStart');
427
-
428
- // Set a class on the page to avoid hiding it when zooming/etc
429
- this.refs.$br.find('.BRpagecontainer--hasSelection').removeClass('BRpagecontainer--hasSelection');
430
- $(window.getSelection().anchorNode).closest('.BRpagecontainer').addClass('BRpagecontainer--hasSelection');
431
- }
432
- }).attach();
433
- }
434
-
435
- super.init();
436
- }
437
-
438
- /**
439
- * @param {number} index
440
- */
441
- _createPageContainer(index) {
442
- const pageContainer = super._createPageContainer(index);
443
- // Disable if thumb mode; it's too janky
444
- // .page can be null for "pre-cover" region
445
- if (this.mode !== this.constModeThumb && pageContainer.page) {
446
- this.textSelectionPlugin?.createTextLayer(pageContainer);
447
- }
448
- return pageContainer;
449
- }
450
- }
451
- window.BookReader = BookreaderWithTextSelection;
452
- export default BookreaderWithTextSelection;
444
+ BookReader?.registerPlugin('textSelection', TextSelectionPlugin);
453
445
 
454
446
 
455
447
  /**
@@ -6,8 +6,7 @@ import { hasLocalStorage } from './utils.js';
6
6
  /**
7
7
  * @export
8
8
  * @typedef {Object} TTSEngineOptions
9
- * @property {String} server
10
- * @property {String} bookPath
9
+ * @property {import('@/src/util/strings.js').StringWithVars} pageChunkUrl
11
10
  * @property {ISO6391} bookLanguage
12
11
  * @property {Function} onLoadingStart
13
12
  * @property {Function} onLoadingComplete
@@ -85,8 +84,7 @@ export default class AbstractTTSEngine {
85
84
  this.opts.onLoadingStart();
86
85
 
87
86
  this._chunkIterator = new PageChunkIterator(numLeafs, leafIndex, {
88
- server: this.opts.server,
89
- bookPath: this.opts.bookPath,
87
+ pageChunkUrl: this.opts.pageChunkUrl,
90
88
  pageBufferSize: 5,
91
89
  });
92
90
 
@@ -1,3 +1,5 @@
1
+ import { applyVariables } from "../../util/strings.js";
2
+
1
3
  /**
2
4
  * Class to manage a 'chunk' (approximately a paragraph) of text on a page.
3
5
  */
@@ -16,24 +18,18 @@ export default class PageChunk {
16
18
  }
17
19
 
18
20
  /**
19
- * @param {string} server
20
- * @param {string} bookPath
21
+ * @param {import('@/src/util/strings.js').StringWithVars} pageChunkUrl
21
22
  * @param {number} leafIndex
22
23
  * @return {Promise<PageChunk[]>}
23
24
  */
24
- static async fetch(server, bookPath, leafIndex) {
25
+ static async fetch(pageChunkUrl, leafIndex) {
25
26
  const chunks = await $.ajax({
26
27
  type: 'GET',
27
- url: `https://${server}/BookReader/BookReaderGetTextWrapper.php`,
28
+ url: applyVariables(pageChunkUrl, { pageIndex: leafIndex }),
28
29
  cache: true,
29
30
  xhrFields: {
30
31
  withCredentials: window.br.protected,
31
32
  },
32
- data: {
33
- path: `${bookPath}_djvu.xml`,
34
- page: leafIndex,
35
- callback: 'false',
36
- },
37
33
  });
38
34
  return PageChunk._fromTextWrapperResponse(leafIndex, chunks);
39
35
  }