@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.
- package/BookReader/BookReader.css +14 -0
- package/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/plugins/plugin.archive_analytics.js +1 -1
- package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
- package/BookReader/plugins/plugin.autoplay.js +1 -1
- package/BookReader/plugins/plugin.autoplay.js.map +1 -1
- package/BookReader/plugins/plugin.iiif.js +1 -1
- package/BookReader/plugins/plugin.iiif.js.map +1 -1
- package/BookReader/plugins/plugin.resume.js +1 -1
- package/BookReader/plugins/plugin.resume.js.map +1 -1
- package/BookReader/plugins/plugin.text_selection.js +1 -1
- package/BookReader/plugins/plugin.text_selection.js.map +1 -1
- package/BookReader/plugins/plugin.tts.js +1 -1
- package/BookReader/plugins/plugin.tts.js.map +1 -1
- package/BookReader/plugins/plugin.url.js +1 -1
- package/BookReader/plugins/plugin.url.js.map +1 -1
- package/CHANGELOG.md +10 -0
- package/codecov.yml +1 -1
- package/package.json +1 -1
- package/src/BookReader/ImageCache.js +48 -15
- package/src/BookReader/Mode2UpLit.js +3 -2
- package/src/BookReader/PageContainer.js +41 -22
- package/src/BookReader/options.js +24 -3
- package/src/BookReader/utils.js +10 -0
- package/src/BookReader.js +89 -38
- package/src/BookReaderPlugin.js +16 -0
- package/src/css/_BRpages.scss +21 -2
- package/src/plugins/plugin.autoplay.js +98 -102
- package/src/plugins/plugin.iiif.js +16 -30
- package/src/plugins/plugin.resume.js +54 -51
- package/src/plugins/plugin.text_selection.js +68 -76
- package/src/plugins/tts/AbstractTTSEngine.js +2 -4
- package/src/plugins/tts/PageChunk.js +5 -9
- package/src/plugins/tts/PageChunkIterator.js +3 -5
- package/src/plugins/tts/plugin.tts.js +309 -329
- package/src/plugins/url/plugin.url.js +1 -1
- package/src/util/strings.js +1 -0
- package/tests/e2e/autoplay.test.js +8 -5
- package/tests/e2e/helpers/base.js +2 -2
- package/tests/e2e/helpers/mockSearch.js +6 -9
- package/tests/jest/BookReader/PageContainer.test.js +96 -55
- package/tests/jest/BookReader/utils.test.js +21 -0
- package/tests/jest/BookReader.test.js +13 -12
- package/tests/jest/plugins/plugin.autoplay.test.js +9 -22
- package/tests/jest/plugins/plugin.resume.test.js +19 -32
- package/tests/jest/plugins/plugin.text_selection.test.js +23 -24
@@ -1,128 +1,124 @@
|
|
1
|
-
|
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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
25
|
+
/** @override */
|
26
|
+
init() {
|
27
|
+
if (!this.options.enabled) return;
|
30
28
|
|
31
|
-
this.bind(
|
29
|
+
this.br.bind(EVENTS.stop, () => this.stop());
|
32
30
|
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
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.
|
51
|
+
jIcons.filter('.play').on('click', () => {
|
52
|
+
this.toggle();
|
53
53
|
return false;
|
54
54
|
});
|
55
55
|
|
56
|
-
jIcons.filter('.pause').click(
|
57
|
-
this.
|
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
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
if (
|
105
|
-
|
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
|
-
|
88
|
+
// flip immediately
|
89
|
+
this.br.next({ triggerStop: false, flipSpeed: this.options.flipSpeed });
|
108
90
|
}
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
11
|
-
|
12
|
-
this.
|
13
|
-
|
14
|
-
this.
|
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
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
*
|
45
|
-
*
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
57
|
-
|
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
|
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
|
-
|
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.
|
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:
|
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.
|
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:
|
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
|
-
|
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 {
|
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
|
-
|
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 {
|
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(
|
25
|
+
static async fetch(pageChunkUrl, leafIndex) {
|
25
26
|
const chunks = await $.ajax({
|
26
27
|
type: 'GET',
|
27
|
-
url:
|
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
|
}
|