@internetarchive/bookreader 5.0.0-98 → 5.0.0-99
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 +15 -11
- package/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +156 -156
- package/BookReader/ia-bookreader-bundle.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.translate.js +135 -1
- package/BookReader/plugins/plugin.translate.js.map +1 -1
- package/package.json +1 -1
- package/src/BookReader.js +8 -4
- package/src/css/_BRpages.scss +1 -9
- package/src/css/_TextSelection.scss +16 -2
- package/src/plugins/plugin.text_selection.js +5 -168
- package/src/plugins/translate/TranslationManager.js +13 -21
- package/src/plugins/translate/plugin.translate.js +58 -22
- package/src/util/TextSelectionManager.js +282 -0
package/package.json
CHANGED
package/src/BookReader.js
CHANGED
|
@@ -1005,7 +1005,8 @@ BookReader.prototype.zoom = function(direction) {
|
|
|
1005
1005
|
} else {
|
|
1006
1006
|
this.activeMode.zoom('out');
|
|
1007
1007
|
}
|
|
1008
|
-
this.plugins.textSelection?.stopPageFlip(this.refs.$brContainer);
|
|
1008
|
+
this.plugins.textSelection?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
|
|
1009
|
+
this.plugins.translate?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
|
|
1009
1010
|
return;
|
|
1010
1011
|
};
|
|
1011
1012
|
|
|
@@ -1252,7 +1253,8 @@ BookReader.prototype.switchMode = function(
|
|
|
1252
1253
|
const eventName = mode + 'PageViewSelected';
|
|
1253
1254
|
this.trigger(BookReader.eventNames[eventName]);
|
|
1254
1255
|
|
|
1255
|
-
this.plugins.textSelection?.stopPageFlip(this.refs.$brContainer);
|
|
1256
|
+
this.plugins.textSelection?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
|
|
1257
|
+
this.plugins.translate?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
|
|
1256
1258
|
};
|
|
1257
1259
|
|
|
1258
1260
|
BookReader.prototype.updateBrClasses = function() {
|
|
@@ -1324,7 +1326,8 @@ BookReader.prototype.enterFullscreen = async function(bindKeyboardControls = tru
|
|
|
1324
1326
|
}
|
|
1325
1327
|
this.jumpToIndex(currentIndex);
|
|
1326
1328
|
|
|
1327
|
-
this.plugins.textSelection?.stopPageFlip(this.refs.$brContainer);
|
|
1329
|
+
this.plugins.textSelection?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
|
|
1330
|
+
this.plugins.translate?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
|
|
1328
1331
|
// Add "?view=theater"
|
|
1329
1332
|
this.trigger(BookReader.eventNames.fragmentChange);
|
|
1330
1333
|
// trigger event here, so that animations,
|
|
@@ -1370,7 +1373,8 @@ BookReader.prototype.exitFullScreen = async function () {
|
|
|
1370
1373
|
await this.activeMode.mode1UpLit.updateComplete;
|
|
1371
1374
|
}
|
|
1372
1375
|
|
|
1373
|
-
this.plugins.textSelection?.stopPageFlip(this.refs.$brContainer);
|
|
1376
|
+
this.plugins.textSelection?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
|
|
1377
|
+
this.plugins.translate?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
|
|
1374
1378
|
// Remove "?view=theater"
|
|
1375
1379
|
this.trigger(BookReader.eventNames.fragmentChange);
|
|
1376
1380
|
this.refs.$br.removeClass('BRfullscreenAnimation');
|
package/src/css/_BRpages.scss
CHANGED
|
@@ -91,16 +91,8 @@
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
svg.BRPageLayer {
|
|
95
|
-
position: absolute;
|
|
96
|
-
top: 0;
|
|
97
|
-
left: 0;
|
|
98
|
-
right: 0;
|
|
99
|
-
bottom: 0;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
94
|
// Hides page layers during page flip animation
|
|
103
|
-
.BRpageFlipping .
|
|
95
|
+
.BRpageFlipping .BRtextLayer {
|
|
104
96
|
display: none;
|
|
105
97
|
}
|
|
106
98
|
|
|
@@ -54,6 +54,21 @@
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
// Style URI TextFragments, eg #:~:text=example
|
|
58
|
+
.BRtextLayer ::target-text {
|
|
59
|
+
// Similar colour to the default one used in Safari, Firefox. Note Chrome uses a purple colour
|
|
60
|
+
background-color: hsla(45, 80%, 66%, 0.6);
|
|
61
|
+
color: transparent;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.BRtranslateLayer ::selection {
|
|
65
|
+
background: hsla(210, 74%, 62%, 0.4);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.BRtranslateLayer ::-moz-selection {
|
|
69
|
+
background: hsla(210, 74%, 62%, 0.4);
|
|
70
|
+
}
|
|
71
|
+
|
|
57
72
|
.BRparagraphElement br {
|
|
58
73
|
visibility: hidden;
|
|
59
74
|
}
|
|
@@ -130,8 +145,7 @@
|
|
|
130
145
|
}
|
|
131
146
|
|
|
132
147
|
.BRtextLayer.showingTranslation {
|
|
133
|
-
|
|
134
|
-
pointer-events: none;
|
|
148
|
+
display: none;
|
|
135
149
|
}
|
|
136
150
|
|
|
137
151
|
.BRtranslateLayer .BRparagraphElement.BRtranslateHidden {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
//@ts-check
|
|
2
2
|
import { createDIVPageLayer } from '../BookReader/PageContainer.js';
|
|
3
|
-
import { SelectionObserver } from '../BookReader/utils/SelectionObserver.js';
|
|
4
3
|
import { BookReaderPlugin } from '../BookReaderPlugin.js';
|
|
5
4
|
import { applyVariables } from '../util/strings.js';
|
|
6
5
|
import { Cache } from '../util/cache.js';
|
|
7
6
|
import { toISO6391 } from './tts/utils.js';
|
|
7
|
+
import { TextSelectionManager } from '../util/TextSelectionManager.js';
|
|
8
8
|
/** @typedef {import('../util/strings.js').StringWithVars} StringWithVars */
|
|
9
9
|
/** @typedef {import('../BookReader/PageContainer.js').PageContainer} PageContainer */
|
|
10
10
|
|
|
@@ -20,7 +20,7 @@ export class TextSelectionPlugin extends BookReaderPlugin {
|
|
|
20
20
|
singlePageDjvuXmlUrl: null,
|
|
21
21
|
/** Whether to fetch the XML as a jsonp */
|
|
22
22
|
jsonp: false,
|
|
23
|
-
/** Mox words
|
|
23
|
+
/** Mox words that can be selected when the text layer is protected */
|
|
24
24
|
maxProtectedWords: 200,
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -46,7 +46,7 @@ export class TextSelectionPlugin extends BookReaderPlugin {
|
|
|
46
46
|
// now we do make that assumption.
|
|
47
47
|
/** Whether the book is right-to-left */
|
|
48
48
|
this.rtl = this.br.pageProgression === 'rl';
|
|
49
|
-
this.
|
|
49
|
+
this.textSelectionManager = new TextSelectionManager('.BRtextLayer', this.br, {selectionElement: ['.BRwordElement', '.BRspace']}, this.options.maxProtectedWords);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/** @override */
|
|
@@ -54,72 +54,9 @@ export class TextSelectionPlugin extends BookReaderPlugin {
|
|
|
54
54
|
if (!this.options.enabled) return;
|
|
55
55
|
|
|
56
56
|
this.loadData();
|
|
57
|
-
|
|
58
|
-
this.selectionObserver.attach();
|
|
59
|
-
new SelectionObserver('.BRtextLayer', (selectEvent) => {
|
|
60
|
-
// Track how often selection is used
|
|
61
|
-
if (selectEvent == 'started') {
|
|
62
|
-
this.br.plugins.archiveAnalytics?.sendEvent('BookReader', 'SelectStart');
|
|
63
|
-
|
|
64
|
-
// Set a class on the page to avoid hiding it when zooming/etc
|
|
65
|
-
this.br.refs.$br.find('.BRpagecontainer--hasSelection').removeClass('BRpagecontainer--hasSelection');
|
|
66
|
-
$(window.getSelection().anchorNode).closest('.BRpagecontainer').addClass('BRpagecontainer--hasSelection');
|
|
67
|
-
}
|
|
68
|
-
}).attach();
|
|
69
|
-
|
|
70
|
-
if (this.br.protected) {
|
|
71
|
-
document.addEventListener('selectionchange', this._limitSelection);
|
|
72
|
-
// Prevent right clicking when selected text
|
|
73
|
-
$(document.body).on('contextmenu dragstart copy', (e) => {
|
|
74
|
-
const selection = document.getSelection();
|
|
75
|
-
if (selection?.toString()) {
|
|
76
|
-
const intersectsTextLayer = $('.BRtextLayer')
|
|
77
|
-
.toArray()
|
|
78
|
-
.some(el => selection.containsNode(el, true));
|
|
79
|
-
if (intersectsTextLayer) {
|
|
80
|
-
e.preventDefault();
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
}
|
|
57
|
+
this.textSelectionManager.init();
|
|
86
58
|
}
|
|
87
59
|
|
|
88
|
-
_limitSelection = () => {
|
|
89
|
-
const selection = window.getSelection();
|
|
90
|
-
if (!selection.rangeCount) return;
|
|
91
|
-
|
|
92
|
-
const range = selection.getRangeAt(0);
|
|
93
|
-
|
|
94
|
-
// Check if range.startContainer is inside the sub-tree of .BRContainer
|
|
95
|
-
const startInBr = !!range.startContainer.parentElement.closest('.BRcontainer');
|
|
96
|
-
const endInBr = !!range.endContainer.parentElement.closest('.BRcontainer');
|
|
97
|
-
if (!startInBr && !endInBr) return;
|
|
98
|
-
if (!startInBr || !endInBr) {
|
|
99
|
-
// weird case, just clear the selection
|
|
100
|
-
selection.removeAllRanges();
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Find the last allowed word in the selection
|
|
105
|
-
const lastAllowedWord = genAt(
|
|
106
|
-
genFilter(
|
|
107
|
-
walkBetweenNodes(range.startContainer, range.endContainer),
|
|
108
|
-
(node) => node.classList?.contains('BRwordElement'),
|
|
109
|
-
),
|
|
110
|
-
this.options.maxProtectedWords - 1,
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
if (!lastAllowedWord || range.endContainer.parentNode == lastAllowedWord) return;
|
|
114
|
-
|
|
115
|
-
const newRange = document.createRange();
|
|
116
|
-
newRange.setStart(range.startContainer, range.startOffset);
|
|
117
|
-
newRange.setEnd(lastAllowedWord.firstChild, lastAllowedWord.textContent.length);
|
|
118
|
-
|
|
119
|
-
selection.removeAllRanges();
|
|
120
|
-
selection.addRange(newRange);
|
|
121
|
-
};
|
|
122
|
-
|
|
123
60
|
/**
|
|
124
61
|
* @override
|
|
125
62
|
* @param {PageContainer} pageContainer
|
|
@@ -134,20 +71,6 @@ export class TextSelectionPlugin extends BookReaderPlugin {
|
|
|
134
71
|
return pageContainer;
|
|
135
72
|
}
|
|
136
73
|
|
|
137
|
-
/**
|
|
138
|
-
* @param {'started' | 'cleared'} type
|
|
139
|
-
* @param {HTMLElement} target
|
|
140
|
-
*/
|
|
141
|
-
_onSelectionChange = (type, target) => {
|
|
142
|
-
if (type === 'started') {
|
|
143
|
-
this.textSelectingMode(target);
|
|
144
|
-
} else if (type === 'cleared') {
|
|
145
|
-
this.defaultMode(target);
|
|
146
|
-
} else {
|
|
147
|
-
throw new Error(`Unknown type ${type}`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
74
|
loadData() {
|
|
152
75
|
// Only fetch the full djvu xml if the single page url isn't there
|
|
153
76
|
if (this.options.singlePageDjvuXmlUrl) return;
|
|
@@ -204,92 +127,6 @@ export class TextSelectionPlugin extends BookReaderPlugin {
|
|
|
204
127
|
}
|
|
205
128
|
}
|
|
206
129
|
|
|
207
|
-
/**
|
|
208
|
-
* Intercept copied text to remove any styling applied to it
|
|
209
|
-
* @param {JQuery} $container
|
|
210
|
-
*/
|
|
211
|
-
interceptCopy($container) {
|
|
212
|
-
$container[0].addEventListener('copy', (event) => {
|
|
213
|
-
const selection = document.getSelection();
|
|
214
|
-
event.clipboardData.setData('text/plain', selection.toString());
|
|
215
|
-
event.preventDefault();
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Applies mouse events when in default mode
|
|
221
|
-
* @param {HTMLElement} textLayer
|
|
222
|
-
*/
|
|
223
|
-
defaultMode(textLayer) {
|
|
224
|
-
const $pageContainer = $(textLayer).closest('.BRpagecontainer');
|
|
225
|
-
textLayer.style.pointerEvents = "none";
|
|
226
|
-
$pageContainer.find("img").css("pointer-events", "auto");
|
|
227
|
-
|
|
228
|
-
$(textLayer).off(".textSelectPluginHandler");
|
|
229
|
-
const startedMouseDown = this.mouseIsDown;
|
|
230
|
-
let skipNextMouseup = this.mouseIsDown;
|
|
231
|
-
if (startedMouseDown) {
|
|
232
|
-
textLayer.style.pointerEvents = "auto";
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Need to stop propagation to prevent DragScrollable from
|
|
236
|
-
// blocking selection
|
|
237
|
-
$(textLayer).on("mousedown.textSelectPluginHandler", (event) => {
|
|
238
|
-
this.mouseIsDown = true;
|
|
239
|
-
if ($(event.target).is(".BRwordElement, .BRspace")) {
|
|
240
|
-
event.stopPropagation();
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
$(textLayer).on("mouseup.textSelectPluginHandler", (event) => {
|
|
245
|
-
this.mouseIsDown = false;
|
|
246
|
-
textLayer.style.pointerEvents = "none";
|
|
247
|
-
if (skipNextMouseup) {
|
|
248
|
-
skipNextMouseup = false;
|
|
249
|
-
event.stopPropagation();
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* This mode is active while there is a selection on the given textLayer
|
|
256
|
-
* @param {HTMLElement} textLayer
|
|
257
|
-
*/
|
|
258
|
-
textSelectingMode(textLayer) {
|
|
259
|
-
const $pageContainer = $(textLayer).closest('.BRpagecontainer');
|
|
260
|
-
// Make text layer consume all events
|
|
261
|
-
textLayer.style.pointerEvents = "all";
|
|
262
|
-
// Block img from getting long-press to save while selecting
|
|
263
|
-
$pageContainer.find("img").css("pointer-events", "none");
|
|
264
|
-
|
|
265
|
-
$(textLayer).off(".textSelectPluginHandler");
|
|
266
|
-
|
|
267
|
-
$(textLayer).on('mousedown.textSelectPluginHandler', (event) => {
|
|
268
|
-
this.mouseIsDown = true;
|
|
269
|
-
event.stopPropagation();
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
// Prevent page flip on click
|
|
273
|
-
$(textLayer).on('mouseup.textSelectPluginHandler', (event) => {
|
|
274
|
-
this.mouseIsDown = false;
|
|
275
|
-
event.stopPropagation();
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Initializes text selection modes if there is a text layer on the page
|
|
281
|
-
* @param {JQuery} $container
|
|
282
|
-
*/
|
|
283
|
-
stopPageFlip($container) {
|
|
284
|
-
/** @type {JQuery<HTMLElement>} */
|
|
285
|
-
const $textLayer = $container.find('.BRtextLayer');
|
|
286
|
-
if (!$textLayer.length) return;
|
|
287
|
-
$textLayer.each((i, s) => this.defaultMode(s));
|
|
288
|
-
if (!this.br.protected) {
|
|
289
|
-
this.interceptCopy($container);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
130
|
/**
|
|
294
131
|
* @param {PageContainer} pageContainer
|
|
295
132
|
*/
|
|
@@ -344,7 +181,7 @@ export class TextSelectionPlugin extends BookReaderPlugin {
|
|
|
344
181
|
textLayer.appendChild(paragEl);
|
|
345
182
|
}
|
|
346
183
|
$container.append(textLayer);
|
|
347
|
-
this.stopPageFlip($container);
|
|
184
|
+
this.textSelectionManager.stopPageFlip($container);
|
|
348
185
|
this.br.trigger('textLayerRendered', {
|
|
349
186
|
pageIndex,
|
|
350
187
|
pageContainer,
|
|
@@ -3,27 +3,6 @@ import { Cache } from '../../util/cache.js';
|
|
|
3
3
|
import { BatchTranslator } from '@internetarchive/bergamot-translator/translator.js';
|
|
4
4
|
import { toISO6391 } from '../tts/utils.js';
|
|
5
5
|
|
|
6
|
-
export const langs = /** @type {{[lang: string]: string}} */ {
|
|
7
|
-
"bg": "Bulgarian",
|
|
8
|
-
"ca": "Catalan",
|
|
9
|
-
"cs": "Czech",
|
|
10
|
-
"nl": "Dutch",
|
|
11
|
-
"en": "English",
|
|
12
|
-
"et": "Estonian",
|
|
13
|
-
"de": "German",
|
|
14
|
-
"fr": "French",
|
|
15
|
-
"is": "Icelandic",
|
|
16
|
-
"it": "Italian",
|
|
17
|
-
"nb": "Norwegian Bokmål",
|
|
18
|
-
"nn": "Norwegian Nynorsk",
|
|
19
|
-
"fa": "Persian",
|
|
20
|
-
"pl": "Polish",
|
|
21
|
-
"pt": "Portuguese",
|
|
22
|
-
"ru": "Russian",
|
|
23
|
-
"es": "Spanish",
|
|
24
|
-
"uk": "Ukrainian",
|
|
25
|
-
};
|
|
26
|
-
|
|
27
6
|
export class TranslationManager {
|
|
28
7
|
/** @type {Cache<{index: string, response: string}>} */
|
|
29
8
|
alreadyTranslated = new Cache(100);
|
|
@@ -167,4 +146,17 @@ export class TranslationManager {
|
|
|
167
146
|
|
|
168
147
|
return promise;
|
|
169
148
|
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Checks and updates the status of a model that is currently being retreived from IA
|
|
152
|
+
* @param {string} fromLanguage
|
|
153
|
+
* @param {string} toLanguage
|
|
154
|
+
* @returns {Promise<boolean>}
|
|
155
|
+
*/
|
|
156
|
+
getTranslationModel = async(fromLanguage, toLanguage) => {
|
|
157
|
+
return this.translator.backing.getTranslationModel({from: fromLanguage, to: toLanguage})
|
|
158
|
+
.catch((err) => {
|
|
159
|
+
console.error(`An error occurred while trying to retreive the ${fromLanguage}-${toLanguage} model`);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
170
162
|
}
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
import { html, LitElement } from 'lit';
|
|
3
3
|
import { BookReaderPlugin } from '../../BookReaderPlugin.js';
|
|
4
4
|
import { customElement, property } from 'lit/decorators.js';
|
|
5
|
-
import {
|
|
5
|
+
import { TranslationManager } from "./TranslationManager.js";
|
|
6
6
|
import { toISO6391 } from '../tts/utils.js';
|
|
7
7
|
import { sortBy } from '../../../src/BookReader/utils.js';
|
|
8
|
+
import { TextSelectionManager } from '../../../src/util/TextSelectionManager.js';
|
|
9
|
+
import '@internetarchive/ia-activity-indicator/ia-activity-indicator.js';
|
|
8
10
|
|
|
9
11
|
// @ts-ignore
|
|
10
12
|
const BookReader = /** @type {typeof import('@/src/BookReader.js').default} */(window.BookReader);
|
|
@@ -34,7 +36,7 @@ export class TranslatePlugin extends BookReaderPlugin {
|
|
|
34
36
|
* Current language code that is being translated From. Defaults to EN currently
|
|
35
37
|
* @type {!string}
|
|
36
38
|
*/
|
|
37
|
-
langFromCode
|
|
39
|
+
langFromCode;
|
|
38
40
|
|
|
39
41
|
/**
|
|
40
42
|
* Current language code that is being translated To
|
|
@@ -53,6 +55,12 @@ export class TranslatePlugin extends BookReaderPlugin {
|
|
|
53
55
|
*/
|
|
54
56
|
userToggleTranslate;
|
|
55
57
|
|
|
58
|
+
/**
|
|
59
|
+
* @type {boolean} loadingModel - Shows loading animation while downloading lang model
|
|
60
|
+
*/
|
|
61
|
+
loadingModel = true;
|
|
62
|
+
|
|
63
|
+
|
|
56
64
|
async init() {
|
|
57
65
|
const currentLanguage = toISO6391(this.br.options.bookLanguage.replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, ""));
|
|
58
66
|
this.langFromCode = currentLanguage ?? "en";
|
|
@@ -176,28 +184,15 @@ export class TranslatePlugin extends BookReaderPlugin {
|
|
|
176
184
|
"width": $(originalParagraphStyle).css("width"),
|
|
177
185
|
"font-size": fontSize,
|
|
178
186
|
});
|
|
179
|
-
|
|
180
|
-
// Note: We'll likely want to switch to using the same logic as
|
|
181
|
-
// TextSelectionPlugin's selection, which allows for e.g. click-to-flip
|
|
182
|
-
// to work simultaneously with text selection.
|
|
183
|
-
translatedParagraph.addEventListener('mousedown', (e) => {
|
|
184
|
-
e.stopPropagation();
|
|
185
|
-
e.stopImmediatePropagation();
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
translatedParagraph.addEventListener('mouseup', (e) => {
|
|
189
|
-
e.stopPropagation();
|
|
190
|
-
e.stopImmediatePropagation();
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
translatedParagraph.addEventListener('dragstart', (e) =>{
|
|
194
|
-
e.preventDefault();
|
|
195
|
-
});
|
|
196
187
|
pageTranslationLayer.append(translatedParagraph);
|
|
197
188
|
}
|
|
198
189
|
|
|
199
190
|
if (paragraph.textContent.length !== 0) {
|
|
200
191
|
const pagePriority = parseFloat(pageIndex) + priority + pidx;
|
|
192
|
+
this.translationManager.getTranslationModel(this.langFromCode, this.langToCode).then(() => {
|
|
193
|
+
this._panel.loadingModel = false;
|
|
194
|
+
this.loadingModel = false;
|
|
195
|
+
});
|
|
201
196
|
const translatedText = await this.translationManager.getTranslation(this.langFromCode, this.langToCode, pageIndex, pidx, paragraph.textContent, pagePriority);
|
|
202
197
|
// prevent duplicate spans from appearing if exists
|
|
203
198
|
translatedParagraph.firstElementChild?.remove();
|
|
@@ -216,6 +211,8 @@ export class TranslatePlugin extends BookReaderPlugin {
|
|
|
216
211
|
}
|
|
217
212
|
}
|
|
218
213
|
});
|
|
214
|
+
|
|
215
|
+
this.textSelectionManager?.stopPageFlip(this.br.refs.$brContainer);
|
|
219
216
|
await Promise.all(paragraphTranslationPromises);
|
|
220
217
|
this.br.trigger('translateLayerRendered', {
|
|
221
218
|
leafIndex: pageIndex,
|
|
@@ -246,6 +243,7 @@ export class TranslatePlugin extends BookReaderPlugin {
|
|
|
246
243
|
|
|
247
244
|
clearAllTranslations() {
|
|
248
245
|
document.querySelectorAll('.BRtranslateLayer').forEach(el => el.remove());
|
|
246
|
+
document.querySelectorAll('.showingTranslation').forEach(el => el.classList.remove('showingTranslation'));
|
|
249
247
|
}
|
|
250
248
|
|
|
251
249
|
/**
|
|
@@ -290,16 +288,21 @@ export class TranslatePlugin extends BookReaderPlugin {
|
|
|
290
288
|
|
|
291
289
|
// Update the from language
|
|
292
290
|
this.langFromCode = selectedLangFrom;
|
|
291
|
+
this._panel.requestUpdate();
|
|
293
292
|
|
|
294
293
|
// Add 'From' language to 'To' list if not already present
|
|
295
294
|
if (!this.translationManager.toLanguages.some(lang => lang.code === selectedLangFrom)) {
|
|
296
|
-
this.translationManager.toLanguages.push({
|
|
295
|
+
this.translationManager.toLanguages.push({
|
|
296
|
+
code: selectedLangFrom,
|
|
297
|
+
name: this.translationManager.fromLanguages.find((entry) => entry.code == selectedLangFrom).name,
|
|
298
|
+
});
|
|
297
299
|
}
|
|
298
300
|
|
|
299
301
|
// Update the 'To' languages list and set the correct 'To' language
|
|
300
302
|
this._panel.toLanguages = this.translationManager.toLanguages;
|
|
301
303
|
|
|
302
304
|
console.log(this.langFromCode, this.langToCode);
|
|
305
|
+
this._render();
|
|
303
306
|
if (this.langFromCode !== this.langToCode) {
|
|
304
307
|
this.translateActivePageContainerElements();
|
|
305
308
|
}
|
|
@@ -308,18 +311,27 @@ export class TranslatePlugin extends BookReaderPlugin {
|
|
|
308
311
|
handleToLangChange = async (e) => {
|
|
309
312
|
this.clearAllTranslations();
|
|
310
313
|
this.langToCode = e.detail.value;
|
|
314
|
+
this._render();
|
|
311
315
|
this.translateActivePageContainerElements();
|
|
312
316
|
}
|
|
313
317
|
|
|
314
318
|
handleToggleTranslation = async () => {
|
|
315
319
|
this.userToggleTranslate = !this.userToggleTranslate;
|
|
316
320
|
this.translationManager.active = this.userToggleTranslate;
|
|
321
|
+
// Init textSelectionManager only after the translation is active
|
|
322
|
+
if (!this.textSelectionManager) {
|
|
323
|
+
this.textSelectionManager = new TextSelectionManager('.BRtranslateLayer', this.br, {selectionElement: [".BRlineElement"]}, 1);
|
|
324
|
+
this.textSelectionManager.init();
|
|
325
|
+
}
|
|
326
|
+
this._render();
|
|
317
327
|
if (!this.userToggleTranslate) {
|
|
318
328
|
this.clearAllTranslations();
|
|
319
329
|
this.br.trigger('translationDisabled', { });
|
|
330
|
+
this.textSelectionManager.detach();
|
|
320
331
|
} else {
|
|
321
332
|
this.br.trigger('translationEnabled', { });
|
|
322
333
|
this.translateActivePageContainerElements();
|
|
334
|
+
this.textSelectionManager.attach();
|
|
323
335
|
}
|
|
324
336
|
}
|
|
325
337
|
|
|
@@ -336,6 +348,10 @@ export class TranslatePlugin extends BookReaderPlugin {
|
|
|
336
348
|
this._panel = e.target;
|
|
337
349
|
this._panel.fromLanguages = this.translationManager.fromLanguages;
|
|
338
350
|
this._panel.toLanguages = this.translationManager.toLanguages;
|
|
351
|
+
this._panel.userTranslationActive = this.userToggleTranslate;
|
|
352
|
+
this._panel.detectedToLang = this.langToCode;
|
|
353
|
+
this._panel.detectedFromLang = this.langFromCode;
|
|
354
|
+
this._panel.loadingModel = this.loadingModel;
|
|
339
355
|
}
|
|
340
356
|
}"
|
|
341
357
|
@langFromChanged="${this.handleFromLangChange}"
|
|
@@ -344,9 +360,10 @@ export class TranslatePlugin extends BookReaderPlugin {
|
|
|
344
360
|
.fromLanguages="${this.translationManager.fromLanguages}"
|
|
345
361
|
.toLanguages="${this.translationManager.toLanguages}"
|
|
346
362
|
.disclaimerMessage="${this.options.panelDisclaimerText}"
|
|
347
|
-
.userTranslationActive=${
|
|
363
|
+
.userTranslationActive=${this.userToggleTranslate}
|
|
348
364
|
.detectedFromLang=${this.langFromCode}
|
|
349
365
|
.detectedToLang=${this.langToCode}
|
|
366
|
+
.loadingModel=${this.loadingModel}
|
|
350
367
|
class="translate-panel"
|
|
351
368
|
/>`,
|
|
352
369
|
};
|
|
@@ -364,6 +381,7 @@ export class BrTranslatePanel extends LitElement {
|
|
|
364
381
|
@property({ type: Boolean }) userTranslationActive = false;
|
|
365
382
|
@property({ type: String }) detectedFromLang = '';
|
|
366
383
|
@property({ type: String }) detectedToLang = '';
|
|
384
|
+
@property({ type: Boolean }) loadingModel;
|
|
367
385
|
|
|
368
386
|
/** @override */
|
|
369
387
|
createRenderRoot() {
|
|
@@ -425,6 +443,10 @@ export class BrTranslatePanel extends LitElement {
|
|
|
425
443
|
<div class="footer" id="status" style="margin-top:5%">
|
|
426
444
|
${this._statusWarning()}
|
|
427
445
|
</div>
|
|
446
|
+
|
|
447
|
+
<div class="lang-models-loading">
|
|
448
|
+
${this._languageModelStatus()}
|
|
449
|
+
</div>
|
|
428
450
|
</div>`;
|
|
429
451
|
}
|
|
430
452
|
_onLangFromChange(event) {
|
|
@@ -439,6 +461,7 @@ export class BrTranslatePanel extends LitElement {
|
|
|
439
461
|
if (this._getSelectedLang('to') !== this._getSelectedLang('from')) {
|
|
440
462
|
this.prevSelectedLang = this._getSelectedLang('from');
|
|
441
463
|
}
|
|
464
|
+
this.loadingModel = true;
|
|
442
465
|
this.detectedFromLang = event.target.value;
|
|
443
466
|
}
|
|
444
467
|
|
|
@@ -454,6 +477,7 @@ export class BrTranslatePanel extends LitElement {
|
|
|
454
477
|
if (this._getSelectedLang('from') !== event.target.value) {
|
|
455
478
|
this.prevSelectedLang = this._getSelectedLang('from');
|
|
456
479
|
}
|
|
480
|
+
this.loadingModel = true;
|
|
457
481
|
this.detectedToLang = event.target.value;
|
|
458
482
|
}
|
|
459
483
|
|
|
@@ -485,5 +509,17 @@ export class BrTranslatePanel extends LitElement {
|
|
|
485
509
|
}
|
|
486
510
|
return "";
|
|
487
511
|
}
|
|
488
|
-
}
|
|
489
512
|
|
|
513
|
+
_languageModelStatus() {
|
|
514
|
+
if (this.userTranslationActive) {
|
|
515
|
+
if (this.loadingModel) {
|
|
516
|
+
return html`
|
|
517
|
+
<ia-activity-indicator mode="processing" style="display:block; width: 40px; height: 40px; margin: 0 auto;"></ia-activity-indicator>
|
|
518
|
+
<p>Downloading language model</p>
|
|
519
|
+
`;
|
|
520
|
+
}
|
|
521
|
+
return html`<p>Language model loaded</p>`;
|
|
522
|
+
}
|
|
523
|
+
return "";
|
|
524
|
+
}
|
|
525
|
+
}
|