Skip to content

Commit 68e48a9

Browse files
committed
Reapply "restore feature 183-4"
This reverts commit 2c3a1b9.
1 parent 185ecd3 commit 68e48a9

File tree

5 files changed

+149
-44
lines changed

5 files changed

+149
-44
lines changed

src/background.js

Lines changed: 85 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ import * as util from "/src/util";
1212
import SettingUtil from "/src/util/setting_util.js";
1313
import _util from "/src/util/lodash_util.js";
1414

15+
var fallbackEngineActList = ["google", "bing", "baidu", "papago", "deepl", "yandex"];
16+
var fallbackEngineCrashTime = { google: 1, bing: 2, baidu: 3 };
17+
var fallbackEngineCrashCount = {};
18+
var fallbackWaitTime = 1000 * 60 * 60; // 1 hour
19+
var fallbackEngineSwapList = ["google", "bing", "baidu"];
20+
var fallbackMaxRetry = fallbackEngineSwapList.length;
21+
1522
var setting;
1623
var recentTranslated = "";
1724
var introSiteUrl =
@@ -79,32 +86,14 @@ function addMessageListener() {
7986

8087
//translate function====================================================
8188

82-
async function translate({ text, sourceLang, targetLang, engine }) {
83-
var engine = engine || setting["translatorVendor"];
84-
return (
85-
(await getTranslateCached(text, sourceLang, targetLang, engine)) || {
86-
targetText: `${engine} is broken`,
87-
transliteration: "",
88-
sourceLang: "",
89-
targetLang: setting["translateTarget"],
90-
isBroken: true,
91-
}
92-
);
93-
}
94-
95-
const getTranslateCached = util.cacheFn(getTranslate);
96-
97-
async function getTranslate(text, sourceLang, targetLang, engine) {
98-
return await translator[engine].translate(text, sourceLang, targetLang);
99-
}
100-
10189
async function translateWithReverse({
10290
text,
10391
sourceLang,
10492
targetLang,
10593
reverseLang,
10694
engine,
10795
}) {
96+
var engine = engine || setting["translatorVendor"];
10897
var response = await translate({ text, sourceLang, targetLang, engine });
10998
//if to,from lang are same and reverse translate on
11099
if (
@@ -125,6 +114,83 @@ async function translateWithReverse({
125114
return response;
126115
}
127116

117+
async function translate({ text, sourceLang, targetLang, engine }) {
118+
return (
119+
(await translateWithFallbackEngine(
120+
text,
121+
sourceLang,
122+
targetLang,
123+
engine
124+
)) || {
125+
targetText: `${engine} is broken`,
126+
transliteration: "",
127+
sourceLang: "",
128+
targetLang: setting["translateTarget"],
129+
isBroken: true,
130+
}
131+
);
132+
}
133+
134+
async function translateWithFallbackEngine(
135+
text,
136+
sourceLang,
137+
targetLang,
138+
engine,
139+
retry = 0
140+
) {
141+
if (retry > fallbackMaxRetry) {
142+
return null;
143+
}
144+
if (fallbackEngineCrashCount[engine] == null) {
145+
fallbackEngineCrashCount[engine] = 0;
146+
}
147+
if (fallbackEngineCrashTime[engine] == null) {
148+
fallbackEngineCrashTime[engine] = 4;
149+
}
150+
let translateResult;
151+
var isFallbackApply =
152+
setting["fallbackTranslatorEngine"] == "true" &&
153+
fallbackEngineActList.includes(engine);
154+
var sortedEngines = Object.keys(fallbackEngineCrashTime)
155+
.filter((engine_cur) => fallbackEngineSwapList.includes(engine_cur))
156+
.filter((engine_cur) => engine_cur != engine)
157+
.sort((a, b) => fallbackEngineCrashTime[a] - fallbackEngineCrashTime[b]);
158+
var swapEngine = sortedEngines[0];
159+
160+
if (
161+
fallbackEngineCrashTime[engine] < new Date().getTime() ||
162+
!isFallbackApply
163+
) {
164+
translateResult = await getTranslateCached(
165+
text,
166+
sourceLang,
167+
targetLang,
168+
engine
169+
);
170+
}
171+
172+
if (isFallbackApply && !translateResult) {
173+
fallbackEngineCrashCount[engine] += 1; // increase crash count
174+
fallbackEngineCrashTime[engine] =
175+
new Date().getTime() +
176+
fallbackWaitTime * fallbackEngineCrashCount[engine]; // set next retry time
177+
translateResult = await translateWithFallbackEngine(
178+
text,
179+
sourceLang,
180+
targetLang,
181+
swapEngine,
182+
retry + 1
183+
);
184+
}
185+
return translateResult;
186+
}
187+
188+
const getTranslateCached = util.cacheFn(getTranslate);
189+
190+
async function getTranslate(text, sourceLang, targetLang, engine) {
191+
return await translator[engine].translate(text, sourceLang, targetLang);
192+
}
193+
128194
//setting ============================================================
129195

130196
async function getSetting() {

src/contentScript.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,7 @@ var listenText = "";
7070
injectGoogleDocAnnotation(); //check google doc and add annotation env var
7171
loadDestructor(); //remove previous tooltip script
7272
await getSetting(); //load setting
73-
if (checkExcludeUrl()) {
74-
return;
75-
}
73+
checkExcludeUrl(); //check url is excluded or not in the whitelist
7674
await dom_util.waitJquery(); //wait jquery load
7775
detectPDF(); //check current page is pdf
7876
checkVideo(); // check video site for subtitle
@@ -85,7 +83,12 @@ var listenText = "";
8583
startMouseoverDetector(); // start current mouseover text detector
8684
startTextSelectDetector(); // start current text select detector
8785
} catch (error) {
88-
console.log(error);
86+
if (error instanceof util.TooltipUrlExcludeError) {
87+
// Do nothing
88+
// console.log(error);
89+
} else {
90+
console.log(error);
91+
}
8992
}
9093
})();
9194

@@ -813,9 +816,14 @@ function addMsgListener() {
813816

814817
function checkExcludeUrl() {
815818
var url = util.getCurrentUrl();
816-
return matchUrl(url, setting["websiteExcludeList"]);
819+
var isExcludeBan = matchUrl(url, setting["websiteExcludeList"]);
820+
var isWhiteListBan = setting["websiteWhiteList"]?.length != 0 && !matchUrl(url, setting["websiteWhiteList"]);
821+
if (isExcludeBan || isWhiteListBan) {
822+
throw new util.TooltipUrlExcludeError();
823+
}
817824
}
818825

826+
819827
// setting handling & container style===============================================================
820828

821829
async function getSetting() {

src/event/mouseover.js

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -106,23 +106,23 @@ export async function getMouseoverText(x, y) {
106106
var range = getPointedRange(x, y);
107107

108108
//get text from range
109-
var mouseoverText = getTextFromRange(range, x, y, false, mouseoverType);
109+
var mouseoverText = getTextFromRange(range, mouseoverType);
110110
// if fail detect using expand range use seg range
111111
if (
112112
!isFirefox() &&
113113
(mouseoverType === "word" || mouseoverType === "sentence") &&
114114
!mouseoverText["mouseoverText"] &&
115-
checkContainerDetectText(range, x, y) // check if container detect text
115+
checkContainerDetectText(range) // check if container detect text
116116
) {
117-
return getTextFromRange(range, x, y, true, mouseoverType);
117+
return getTextFromRange(range, mouseoverType, true);
118118
}
119119

120120
return mouseoverText;
121121
}
122122

123-
function getTextFromRange(range, x, y, useSegmentation, mouseoverType) {
123+
function getTextFromRange(range, mouseoverType, useSegmentation= false) {
124124
var output = { mouseoverText: "", mouseoverRange: range };
125-
var wordRange = expandRange(range, mouseoverType, useSegmentation, x, y);
125+
var wordRange = expandRange(range, mouseoverType, useSegmentation);
126126
if (checkXYInElement(wordRange, clientX, clientY)) {
127127
output["mouseoverText"] = util.extractTextFromRange(wordRange);
128128
output["mouseoverRange"] = wordRange;
@@ -131,7 +131,7 @@ function getTextFromRange(range, x, y, useSegmentation, mouseoverType) {
131131
return output;
132132
}
133133

134-
function expandRange(range, type, useSegmentation, x, y) {
134+
function expandRange(range, type, useSegmentation) {
135135
try {
136136
if (!range) {
137137
return;
@@ -141,11 +141,7 @@ function expandRange(range, type, useSegmentation, x, y) {
141141
range = getContainerRange(range);
142142
} else if (isFirefox() || useSegmentation) {
143143
// for firefox, use segmentation to extract word
144-
145-
// console.time("expandRangeWithSeg");
146-
range = expandRangeWithSeg(range, type, x, y);
147-
148-
// console.timeEnd("expandRangeWithSeg");
144+
range = expandRangeWithSeg(range, type);
149145
} else {
150146
// for chrome, use range expand
151147
range = getExpandRange(range, type);
@@ -323,16 +319,18 @@ export function checkXYInElement(ele, x, y) {
323319
}
324320
}
325321

326-
function checkContainerDetectText(rangeOri, x, y) {
327-
var mouseoverText = getTextFromRange(rangeOri, x, y, false, "container");
322+
function checkContainerDetectText(rangeOri) {
323+
var mouseoverText = getTextFromRange(rangeOri, "container");
328324
return mouseoverText["mouseoverText"] ? mouseoverText : null;
329325
}
330326

331327
//firefox word break ====================================
332-
function expandRangeWithSeg(rangeOri, type = "word", x, y) {
328+
function expandRangeWithSeg(rangeOri, type = "word") {
333329
//check text exist over container for fast checking
334330
var range = rangeOri.cloneRange();
331+
const { x, y } = getRangeCenterXY(range);
335332
var rangeContainer = expandRange(range, "container");
333+
336334
const textNode = rangeContainer.commonAncestorContainer;
337335
// var wholeText = textNode.innerText;
338336
// console.timeEnd("expandRangeWithSeginit");
@@ -411,6 +409,18 @@ function isPointInRange(range, x, y) {
411409
return false;
412410
}
413411

412+
function getRangeCenterXY(range) {
413+
if (!range || !range.getClientRects) {
414+
return {x: 0, y: 0};
415+
}
416+
const rects = range.getClientRects();
417+
if (rects.length === 0) return {x: 0, y: 0};
418+
const rect = rects[0];
419+
const centerX = rect.left + rect.width / 2;
420+
const centerY = rect.top + rect.height / 2;
421+
return { x: centerX, y: centerY };
422+
}
423+
414424
function getWordSegmentInfo(text, type) {
415425
const segmenter = new Intl.Segmenter("en-US", { granularity: type });
416426
const wordsMeta = [...segmenter.segment(text)];
@@ -596,7 +606,7 @@ async function getGoogleDocText(x, y, mouseoverType) {
596606
var textElement;
597607
var rect = getGoogleDocRect(x, y);
598608
var { textElement, range } = getGoogleDocCaretRange(rect, x, y);
599-
var mouseoverText = await getTextFromRange(range, x, y, false, mouseoverType);
609+
var mouseoverText = await getTextFromRange(range, mouseoverType);
600610
textElement?.remove();
601611
return mouseoverText;
602612
}

src/util/index.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,4 +617,12 @@ export function getAllShadows(el = document.body) {
617617
const childResults = childShadows.map((child) => getAllShadows(child));
618618
const result = Array.from(childShadows);
619619
return result.concat(childResults).flat();
620-
}
620+
}
621+
622+
export class TooltipUrlExcludeError extends Error {
623+
constructor(name = "TooltipUrlExcludeError", message = "URL is excluded or not in the whitelist.", details = {}) {
624+
super(message);
625+
this.name = name;
626+
this.details = details;
627+
}
628+
}

src/util/setting_default.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ var translatorList = {
4242
"yandex (Experimental)": "yandex",
4343
"baidu (Experimental)": "baidu",
4444
"papago (Experimental)": "papago",
45+
"googleWebImage (Experimental)": "googleWebImage",
4546
"googleGTX (Experimental)": "googleGTX",
4647
"googleWeb (Experimental)": "googleWeb",
4748
"googleV2 (Experimental)": "googleV2",
48-
"googleWebImage (Experimental)": "googleWebImage",
49-
// chatgpt: "chatgpt",
49+
// "chatgpt (Experimental)": "chatgpt",
5050
// "lingva (Experimental)": "lingva",
5151
// "libreTranslate (Experimental)": "libreTranslate",
5252
// "duckduckgo (Experimental)": "duckduckgo",
@@ -328,7 +328,7 @@ export var settingDict = {
328328
settingTab: "graphic",
329329
},
330330
mouseoverTextHighlightColor: {
331-
default: "#329B7B",
331+
default: "#21dc6d40",
332332
i18nKey: "Mouseover_Text_Highlight_Color",
333333
optionList: {},
334334
optionType: "colorPicker",
@@ -413,7 +413,7 @@ export var settingDict = {
413413
settingTab: "speech",
414414
},
415415
voicePanelBackgroundColor: {
416-
default: "#002918",
416+
default: "#333333",
417417
i18nKey: "Voice_Panel_Background_Color",
418418
optionList: {},
419419
optionType: "colorPicker",
@@ -465,6 +465,12 @@ export var settingDict = {
465465
optionList: voiceRateListWithDefault,
466466
settingTab: "advanced",
467467
},
468+
fallbackTranslatorEngine: {
469+
default: "true",
470+
i18nKey: "Fallback_Translator_Engine",
471+
optionList: toggleList,
472+
settingTab: "advanced",
473+
},
468474

469475
// exclude
470476
langExcludeList: {
@@ -481,6 +487,13 @@ export var settingDict = {
481487
optionType: "comboBox",
482488
settingTab: "exclude",
483489
},
490+
websiteWhiteList: {
491+
default: [],
492+
i18nKey: "Whitelist_Website",
493+
optionList: "",
494+
optionType: "comboBox",
495+
settingTab: "exclude",
496+
},
484497

485498
// remains
486499
subtitleButtonToggle: {

0 commit comments

Comments
 (0)