2023-08-05 15:32:51 +08:00
|
|
|
|
import { createRoot } from "react-dom/client";
|
2023-08-09 13:22:10 +08:00
|
|
|
|
import {
|
|
|
|
|
|
APP_LCNAME,
|
|
|
|
|
|
TRANS_MIN_LENGTH,
|
|
|
|
|
|
TRANS_MAX_LENGTH,
|
|
|
|
|
|
MSG_TRANS_CURRULE,
|
2024-03-14 18:06:28 +08:00
|
|
|
|
MSG_INJECT_JS,
|
|
|
|
|
|
MSG_INJECT_CSS,
|
2023-08-21 16:06:21 +08:00
|
|
|
|
OPT_STYLE_DASHLINE,
|
|
|
|
|
|
OPT_STYLE_FUZZY,
|
2023-08-25 17:07:53 +08:00
|
|
|
|
SHADOW_KEY,
|
2024-03-16 23:37:27 +08:00
|
|
|
|
OPT_TIMING_PAGESCROLL,
|
|
|
|
|
|
OPT_TIMING_PAGEOPEN,
|
|
|
|
|
|
OPT_TIMING_MOUSEOVER,
|
2024-01-19 17:18:05 +08:00
|
|
|
|
DEFAULT_TRANS_APIS,
|
2024-03-21 15:07:50 +08:00
|
|
|
|
DEFAULT_FETCH_LIMIT,
|
|
|
|
|
|
DEFAULT_FETCH_INTERVAL,
|
2023-08-09 13:22:10 +08:00
|
|
|
|
} from "../config";
|
2023-08-05 15:32:51 +08:00
|
|
|
|
import Content from "../views/Content";
|
2023-08-31 13:38:06 +08:00
|
|
|
|
import { updateFetchPool, clearFetchPool } from "./fetch";
|
2023-12-18 11:46:37 +08:00
|
|
|
|
import { debounce, genEventName } from "./utils";
|
2023-12-27 15:44:02 +08:00
|
|
|
|
import { runFixer } from "./webfix";
|
2024-01-19 17:18:05 +08:00
|
|
|
|
import { apiTranslate } from "../apis";
|
2024-03-14 18:06:28 +08:00
|
|
|
|
import { sendBgMsg } from "./msg";
|
2024-03-15 10:35:30 +08:00
|
|
|
|
import { isExt } from "./client";
|
|
|
|
|
|
import { injectInlineJs, injectInternalCss } from "./injector";
|
2024-03-19 18:07:18 +08:00
|
|
|
|
import { kissLog } from "./log";
|
2023-08-23 17:53:46 +08:00
|
|
|
|
|
2023-08-05 15:32:51 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 翻译类
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class Translator {
|
|
|
|
|
|
_rule = {};
|
2023-08-30 18:05:37 +08:00
|
|
|
|
_setting = {};
|
|
|
|
|
|
_rootNodes = new Set();
|
|
|
|
|
|
_tranNodes = new Map();
|
2023-08-24 16:21:01 +08:00
|
|
|
|
_skipNodeNames = [
|
|
|
|
|
|
APP_LCNAME,
|
|
|
|
|
|
"style",
|
|
|
|
|
|
"svg",
|
|
|
|
|
|
"img",
|
|
|
|
|
|
"audio",
|
|
|
|
|
|
"video",
|
|
|
|
|
|
"textarea",
|
|
|
|
|
|
"input",
|
|
|
|
|
|
"button",
|
|
|
|
|
|
"select",
|
|
|
|
|
|
"option",
|
|
|
|
|
|
"head",
|
|
|
|
|
|
"script",
|
2023-08-25 17:07:53 +08:00
|
|
|
|
"iframe",
|
2023-08-24 16:21:01 +08:00
|
|
|
|
];
|
2023-09-02 14:14:27 +08:00
|
|
|
|
_eventName = genEventName();
|
2023-10-25 13:26:31 +08:00
|
|
|
|
_mouseoverNode = null;
|
2024-01-19 16:02:53 +08:00
|
|
|
|
_keepSelector = [null, null];
|
2024-02-02 12:10:27 +08:00
|
|
|
|
_terms = [];
|
2024-01-19 17:18:05 +08:00
|
|
|
|
_docTitle = "";
|
2023-08-05 15:32:51 +08:00
|
|
|
|
|
2023-08-23 17:53:46 +08:00
|
|
|
|
// 显示
|
2023-08-05 15:32:51 +08:00
|
|
|
|
_interseObserver = new IntersectionObserver(
|
|
|
|
|
|
(intersections) => {
|
|
|
|
|
|
intersections.forEach((intersection) => {
|
|
|
|
|
|
if (intersection.isIntersecting) {
|
|
|
|
|
|
this._render(intersection.target);
|
|
|
|
|
|
this._interseObserver.unobserve(intersection.target);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
threshold: 0.1,
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2023-08-23 17:53:46 +08:00
|
|
|
|
// 变化
|
2023-08-05 15:32:51 +08:00
|
|
|
|
_mutaObserver = new MutationObserver((mutations) => {
|
|
|
|
|
|
mutations.forEach((mutation) => {
|
2023-08-24 14:57:54 +08:00
|
|
|
|
if (
|
2023-08-24 16:21:01 +08:00
|
|
|
|
!this._skipNodeNames.includes(mutation.target.localName) &&
|
2023-08-24 14:57:54 +08:00
|
|
|
|
mutation.addedNodes.length > 0
|
|
|
|
|
|
) {
|
2023-08-25 17:07:53 +08:00
|
|
|
|
const nodes = Array.from(mutation.addedNodes).filter((node) => {
|
2023-08-24 16:21:01 +08:00
|
|
|
|
if (
|
|
|
|
|
|
this._skipNodeNames.includes(node.localName) ||
|
|
|
|
|
|
node.id === APP_LCNAME
|
|
|
|
|
|
) {
|
|
|
|
|
|
return false;
|
2023-08-24 14:57:54 +08:00
|
|
|
|
}
|
2023-08-24 16:21:01 +08:00
|
|
|
|
return true;
|
2023-08-24 14:57:54 +08:00
|
|
|
|
});
|
2023-08-25 17:07:53 +08:00
|
|
|
|
if (nodes.length > 0) {
|
|
|
|
|
|
// const rootNode = mutation.target.getRootNode();
|
|
|
|
|
|
// todo
|
2023-08-24 14:57:54 +08:00
|
|
|
|
this._reTranslate();
|
2023-08-05 15:32:51 +08:00
|
|
|
|
}
|
2023-08-24 14:57:54 +08:00
|
|
|
|
}
|
2023-08-05 15:32:51 +08:00
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2023-08-24 16:21:01 +08:00
|
|
|
|
// 插入 shadowroot
|
2023-08-24 14:57:54 +08:00
|
|
|
|
_overrideAttachShadow = () => {
|
|
|
|
|
|
const _this = this;
|
|
|
|
|
|
const _attachShadow = HTMLElement.prototype.attachShadow;
|
|
|
|
|
|
HTMLElement.prototype.attachShadow = function () {
|
|
|
|
|
|
_this._reTranslate();
|
|
|
|
|
|
return _attachShadow.apply(this, arguments);
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-03-21 15:07:50 +08:00
|
|
|
|
_updatePool(translator) {
|
|
|
|
|
|
if (!translator) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
|
fetchInterval = DEFAULT_FETCH_INTERVAL,
|
|
|
|
|
|
fetchLimit = DEFAULT_FETCH_LIMIT,
|
|
|
|
|
|
} = this._setting.transApis[translator] || {};
|
2023-08-31 13:38:06 +08:00
|
|
|
|
updateFetchPool(fetchInterval, fetchLimit);
|
2024-03-21 15:07:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
constructor(rule, setting) {
|
2023-08-24 14:57:54 +08:00
|
|
|
|
this._overrideAttachShadow();
|
2023-08-30 18:05:37 +08:00
|
|
|
|
|
|
|
|
|
|
this._setting = setting;
|
|
|
|
|
|
this._rule = rule;
|
|
|
|
|
|
|
2024-01-19 16:02:53 +08:00
|
|
|
|
this._keepSelector = (rule.keepSelector || "")
|
|
|
|
|
|
.split(SHADOW_KEY)
|
|
|
|
|
|
.map((item) => item.trim());
|
2024-02-02 12:10:27 +08:00
|
|
|
|
this._terms = (rule.terms || "")
|
2024-01-19 16:02:53 +08:00
|
|
|
|
.split(/\n|;/)
|
|
|
|
|
|
.map((item) => item.split(",").map((item) => item.trim()))
|
|
|
|
|
|
.filter(([term]) => Boolean(term));
|
|
|
|
|
|
|
2024-03-21 15:07:50 +08:00
|
|
|
|
this._updatePool(rule.translator);
|
|
|
|
|
|
|
2023-08-08 13:29:15 +08:00
|
|
|
|
if (rule.transOpen === "true") {
|
2023-08-05 15:32:51 +08:00
|
|
|
|
this._register();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-31 00:18:57 +08:00
|
|
|
|
get setting() {
|
|
|
|
|
|
return this._setting;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-02 14:14:27 +08:00
|
|
|
|
get eventName() {
|
|
|
|
|
|
return this._eventName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-05 15:32:51 +08:00
|
|
|
|
get rule() {
|
2023-08-09 13:22:10 +08:00
|
|
|
|
// console.log("get rule", this._rule);
|
2023-08-05 15:32:51 +08:00
|
|
|
|
return this._rule;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-09 13:22:10 +08:00
|
|
|
|
set rule(rule) {
|
|
|
|
|
|
// console.log("set rule", rule);
|
|
|
|
|
|
this._rule = rule;
|
|
|
|
|
|
|
|
|
|
|
|
// 广播消息
|
2023-09-02 14:14:27 +08:00
|
|
|
|
const eventName = this._eventName;
|
2023-08-09 13:22:10 +08:00
|
|
|
|
window.dispatchEvent(
|
2023-09-02 14:14:27 +08:00
|
|
|
|
new CustomEvent(eventName, {
|
2023-08-09 13:22:10 +08:00
|
|
|
|
detail: {
|
|
|
|
|
|
action: MSG_TRANS_CURRULE,
|
|
|
|
|
|
args: rule,
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-05 15:32:51 +08:00
|
|
|
|
updateRule = (obj) => {
|
2023-08-09 13:22:10 +08:00
|
|
|
|
this.rule = { ...this.rule, ...obj };
|
2024-03-21 15:07:50 +08:00
|
|
|
|
this._updatePool(obj.translator);
|
2023-08-05 15:32:51 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
toggle = () => {
|
2023-08-09 13:22:10 +08:00
|
|
|
|
if (this.rule.transOpen === "true") {
|
|
|
|
|
|
this.rule = { ...this.rule, transOpen: "false" };
|
2023-08-05 15:32:51 +08:00
|
|
|
|
this._unRegister();
|
|
|
|
|
|
} else {
|
2023-08-09 13:22:10 +08:00
|
|
|
|
this.rule = { ...this.rule, transOpen: "true" };
|
2023-08-05 15:32:51 +08:00
|
|
|
|
this._register();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-08-21 16:06:21 +08:00
|
|
|
|
toggleStyle = () => {
|
|
|
|
|
|
const textStyle =
|
|
|
|
|
|
this.rule.textStyle === OPT_STYLE_FUZZY
|
|
|
|
|
|
? OPT_STYLE_DASHLINE
|
|
|
|
|
|
: OPT_STYLE_FUZZY;
|
|
|
|
|
|
this.rule = { ...this.rule, textStyle };
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-01-19 17:18:05 +08:00
|
|
|
|
translateText = async (text) => {
|
|
|
|
|
|
const { translator, fromLang, toLang } = this._rule;
|
|
|
|
|
|
const apiSetting =
|
|
|
|
|
|
this._setting.transApis?.[translator] || DEFAULT_TRANS_APIS[translator];
|
|
|
|
|
|
const [trText] = await apiTranslate({
|
|
|
|
|
|
text,
|
|
|
|
|
|
translator,
|
|
|
|
|
|
fromLang,
|
|
|
|
|
|
toLang,
|
|
|
|
|
|
apiSetting,
|
|
|
|
|
|
});
|
|
|
|
|
|
return trText;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-08-25 22:48:11 +08:00
|
|
|
|
_querySelectorAll = (selector, node) => {
|
|
|
|
|
|
try {
|
2023-08-26 00:08:12 +08:00
|
|
|
|
return Array.from(node.querySelectorAll(selector));
|
2023-08-25 22:48:11 +08:00
|
|
|
|
} catch (err) {
|
2024-03-19 18:07:18 +08:00
|
|
|
|
kissLog(selector, "querySelectorAll err");
|
2023-08-25 22:48:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
return [];
|
2023-08-25 17:07:53 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-08-26 00:08:12 +08:00
|
|
|
|
_queryFilter = (selector, rootNode) => {
|
|
|
|
|
|
return this._querySelectorAll(selector, rootNode).filter(
|
|
|
|
|
|
(node) => this._queryFilter(selector, node).length === 0
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-09-22 15:33:02 +08:00
|
|
|
|
_queryShadowNodes = (selector, rootNode) => {
|
|
|
|
|
|
this._rootNodes.add(rootNode);
|
|
|
|
|
|
this._queryFilter(selector, rootNode).forEach((item) => {
|
|
|
|
|
|
if (!this._tranNodes.has(item)) {
|
|
|
|
|
|
this._tranNodes.set(item, "");
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
Array.from(rootNode.querySelectorAll("*"))
|
|
|
|
|
|
.map((item) => item.shadowRoot)
|
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
|
.forEach((item) => {
|
|
|
|
|
|
this._queryShadowNodes(selector, item);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-08-24 14:57:54 +08:00
|
|
|
|
_queryNodes = (rootNode = document) => {
|
2023-08-25 17:07:53 +08:00
|
|
|
|
// const childRoots = Array.from(rootNode.querySelectorAll("*"))
|
|
|
|
|
|
// .map((item) => item.shadowRoot)
|
|
|
|
|
|
// .filter(Boolean);
|
|
|
|
|
|
// const childNodes = childRoots.map((item) => this._queryNodes(item));
|
|
|
|
|
|
// const nodes = Array.from(rootNode.querySelectorAll(this.rule.selector));
|
|
|
|
|
|
// return nodes.concat(childNodes).flat();
|
|
|
|
|
|
|
|
|
|
|
|
this._rootNodes.add(rootNode);
|
|
|
|
|
|
this._rule.selector
|
|
|
|
|
|
.split(";")
|
|
|
|
|
|
.map((item) => item.trim())
|
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
|
.forEach((selector) => {
|
|
|
|
|
|
if (selector.includes(SHADOW_KEY)) {
|
|
|
|
|
|
const [outSelector, inSelector] = selector
|
|
|
|
|
|
.split(SHADOW_KEY)
|
|
|
|
|
|
.map((item) => item.trim());
|
|
|
|
|
|
if (outSelector && inSelector) {
|
2023-08-25 22:48:11 +08:00
|
|
|
|
const outNodes = this._querySelectorAll(outSelector, rootNode);
|
2023-08-25 17:07:53 +08:00
|
|
|
|
outNodes.forEach((outNode) => {
|
|
|
|
|
|
if (outNode.shadowRoot) {
|
2023-09-22 15:33:02 +08:00
|
|
|
|
// this._rootNodes.add(outNode.shadowRoot);
|
|
|
|
|
|
// this._queryFilter(inSelector, outNode.shadowRoot).forEach(
|
|
|
|
|
|
// (item) => {
|
|
|
|
|
|
// if (!this._tranNodes.has(item)) {
|
|
|
|
|
|
// this._tranNodes.set(item, "");
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
// );
|
|
|
|
|
|
this._queryShadowNodes(inSelector, outNode.shadowRoot);
|
2023-08-25 17:07:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2023-08-26 00:08:12 +08:00
|
|
|
|
this._queryFilter(selector, rootNode).forEach((item) => {
|
2023-08-26 13:10:13 +08:00
|
|
|
|
if (!this._tranNodes.has(item)) {
|
|
|
|
|
|
this._tranNodes.set(item, "");
|
|
|
|
|
|
}
|
2023-08-25 17:07:53 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2023-08-24 14:57:54 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-08-05 15:32:51 +08:00
|
|
|
|
_register = () => {
|
2024-03-16 23:37:27 +08:00
|
|
|
|
const { fromLang, toLang, injectJs, injectCss, fixerSelector, fixerFunc } =
|
|
|
|
|
|
this._rule;
|
2024-03-14 18:06:28 +08:00
|
|
|
|
if (fromLang === toLang) {
|
2023-09-06 14:57:02 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-27 15:44:02 +08:00
|
|
|
|
// webfix
|
2024-03-16 23:37:27 +08:00
|
|
|
|
if (fixerSelector && fixerFunc !== "-") {
|
|
|
|
|
|
runFixer(fixerSelector, fixerFunc);
|
2023-12-27 15:44:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-03-14 18:06:28 +08:00
|
|
|
|
// 注入用户JS/CSS
|
2024-03-15 10:35:30 +08:00
|
|
|
|
if (isExt) {
|
|
|
|
|
|
injectJs && sendBgMsg(MSG_INJECT_JS, injectJs);
|
|
|
|
|
|
injectCss && sendBgMsg(MSG_INJECT_CSS, injectCss);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
injectJs && injectInlineJs(injectJs);
|
|
|
|
|
|
injectCss && injectInternalCss(injectCss);
|
|
|
|
|
|
}
|
2024-03-14 18:06:28 +08:00
|
|
|
|
|
2023-08-25 17:07:53 +08:00
|
|
|
|
// 搜索节点
|
|
|
|
|
|
this._queryNodes();
|
|
|
|
|
|
|
|
|
|
|
|
this._rootNodes.forEach((node) => {
|
|
|
|
|
|
// 监听节点变化;
|
|
|
|
|
|
this._mutaObserver.observe(node, {
|
|
|
|
|
|
childList: true,
|
|
|
|
|
|
subtree: true,
|
|
|
|
|
|
// characterData: true,
|
|
|
|
|
|
});
|
2023-08-05 15:32:51 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2023-10-25 13:26:31 +08:00
|
|
|
|
if (
|
2024-03-16 23:37:27 +08:00
|
|
|
|
!this._rule.transTiming ||
|
|
|
|
|
|
this._rule.transTiming === OPT_TIMING_PAGESCROLL
|
2023-10-25 13:26:31 +08:00
|
|
|
|
) {
|
|
|
|
|
|
// 监听节点显示
|
|
|
|
|
|
this._tranNodes.forEach((_, node) => {
|
2023-09-06 18:00:18 +08:00
|
|
|
|
this._interseObserver.observe(node);
|
2023-10-25 13:26:31 +08:00
|
|
|
|
});
|
2024-03-16 23:37:27 +08:00
|
|
|
|
} else if (this._rule.transTiming === OPT_TIMING_PAGEOPEN) {
|
2024-01-19 17:55:18 +08:00
|
|
|
|
// 全文直接翻译
|
|
|
|
|
|
this._tranNodes.forEach((_, node) => {
|
|
|
|
|
|
this._render(node);
|
|
|
|
|
|
});
|
2023-10-25 13:26:31 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// 监听鼠标悬停
|
|
|
|
|
|
window.addEventListener("keydown", this._handleKeydown);
|
|
|
|
|
|
this._tranNodes.forEach((_, node) => {
|
2023-11-13 11:02:25 +08:00
|
|
|
|
node.addEventListener("mouseenter", this._handleMouseover);
|
|
|
|
|
|
node.addEventListener("mouseleave", this._handleMouseout);
|
2023-10-25 13:26:31 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
2024-01-19 17:18:05 +08:00
|
|
|
|
|
|
|
|
|
|
// 翻译页面标题
|
2024-03-16 23:37:27 +08:00
|
|
|
|
if (this._rule.transTitle === "true" && !this._docTitle) {
|
2024-02-02 11:13:41 +08:00
|
|
|
|
const title = document.title;
|
2024-02-02 11:20:39 +08:00
|
|
|
|
this._docTitle = title;
|
|
|
|
|
|
this.translateText(title).then((trText) => {
|
|
|
|
|
|
document.title = `${trText} | ${title}`;
|
|
|
|
|
|
});
|
2024-01-19 17:18:05 +08:00
|
|
|
|
}
|
2023-09-06 18:00:18 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
_handleMouseover = (e) => {
|
2023-11-13 11:02:25 +08:00
|
|
|
|
// console.log("mouseenter", e);
|
2023-10-25 13:26:31 +08:00
|
|
|
|
if (!this._tranNodes.has(e.target)) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-03-16 23:37:27 +08:00
|
|
|
|
const key = this._rule.transTiming.slice(3);
|
|
|
|
|
|
if (this._rule.transTiming === OPT_TIMING_MOUSEOVER || e[key]) {
|
2023-11-13 11:02:25 +08:00
|
|
|
|
e.target.removeEventListener("mouseenter", this._handleMouseover);
|
|
|
|
|
|
e.target.removeEventListener("mouseleave", this._handleMouseout);
|
2023-09-06 18:00:18 +08:00
|
|
|
|
this._render(e.target);
|
2023-10-25 13:26:31 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
this._mouseoverNode = e.target;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
_handleMouseout = (e) => {
|
2023-11-13 11:02:25 +08:00
|
|
|
|
// console.log("mouseleave", e);
|
2023-10-25 13:26:31 +08:00
|
|
|
|
if (!this._tranNodes.has(e.target)) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this._mouseoverNode = null;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
_handleKeydown = (e) => {
|
|
|
|
|
|
// console.log("keydown", e);
|
2024-03-16 23:37:27 +08:00
|
|
|
|
const key = this._rule.transTiming.slice(3);
|
2023-10-25 13:26:31 +08:00
|
|
|
|
if (e[key] && this._mouseoverNode) {
|
|
|
|
|
|
this._mouseoverNode.removeEventListener(
|
2023-11-13 11:02:25 +08:00
|
|
|
|
"mouseenter",
|
2023-10-25 13:26:31 +08:00
|
|
|
|
this._handleMouseover
|
|
|
|
|
|
);
|
2023-11-13 11:02:25 +08:00
|
|
|
|
this._mouseoverNode.removeEventListener(
|
|
|
|
|
|
"mouseleave",
|
|
|
|
|
|
this._handleMouseout
|
|
|
|
|
|
);
|
2023-10-25 13:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
const node = this._mouseoverNode;
|
|
|
|
|
|
this._render(node);
|
|
|
|
|
|
this._mouseoverNode = null;
|
2023-09-06 18:00:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-08-05 15:32:51 +08:00
|
|
|
|
_unRegister = () => {
|
2024-01-19 17:18:05 +08:00
|
|
|
|
// 恢复页面标题
|
2024-02-02 11:20:39 +08:00
|
|
|
|
if (this._docTitle) {
|
2024-02-02 11:13:41 +08:00
|
|
|
|
document.title = this._docTitle;
|
2024-02-02 11:20:39 +08:00
|
|
|
|
this._docTitle = "";
|
2024-02-02 11:13:41 +08:00
|
|
|
|
}
|
2024-01-19 17:18:05 +08:00
|
|
|
|
|
2023-08-05 15:32:51 +08:00
|
|
|
|
// 解除节点变化监听
|
|
|
|
|
|
this._mutaObserver.disconnect();
|
|
|
|
|
|
|
2023-08-25 22:48:11 +08:00
|
|
|
|
// 解除节点显示监听
|
2023-09-06 18:00:18 +08:00
|
|
|
|
// this._interseObserver.disconnect();
|
2023-08-05 15:32:51 +08:00
|
|
|
|
|
2024-03-13 16:35:40 +08:00
|
|
|
|
// 移除键盘监听
|
|
|
|
|
|
window.removeEventListener("keydown", this._handleKeydown);
|
|
|
|
|
|
|
|
|
|
|
|
this._tranNodes.forEach((innerHTML, node) => {
|
|
|
|
|
|
if (
|
2024-03-16 23:37:27 +08:00
|
|
|
|
!this._rule.transTiming ||
|
|
|
|
|
|
this._rule.transTiming === OPT_TIMING_PAGESCROLL
|
2024-03-13 16:35:40 +08:00
|
|
|
|
) {
|
|
|
|
|
|
// 解除节点显示监听
|
2023-09-06 18:00:18 +08:00
|
|
|
|
this._interseObserver.unobserve(node);
|
2024-03-16 23:37:27 +08:00
|
|
|
|
} else if (this._rule.transTiming !== OPT_TIMING_PAGEOPEN) {
|
2024-03-13 16:35:40 +08:00
|
|
|
|
// 移除鼠标悬停监听
|
2023-11-13 11:02:25 +08:00
|
|
|
|
// node.style.pointerEvents = "none";
|
|
|
|
|
|
node.removeEventListener("mouseenter", this._handleMouseover);
|
|
|
|
|
|
node.removeEventListener("mouseleave", this._handleMouseout);
|
2024-03-13 16:35:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-03-14 16:26:17 +08:00
|
|
|
|
// 移除/恢复元素
|
2024-03-16 23:37:27 +08:00
|
|
|
|
if (innerHTML && this._rule.transOnly === "true") {
|
2024-03-13 16:35:40 +08:00
|
|
|
|
node.innerHTML = innerHTML;
|
2024-03-14 16:26:17 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
node.querySelector(APP_LCNAME)?.remove();
|
2024-03-13 16:35:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2024-03-14 18:06:28 +08:00
|
|
|
|
// 移除用户JS/CSS
|
2024-03-15 10:35:30 +08:00
|
|
|
|
this._removeInjector();
|
2024-03-14 18:06:28 +08:00
|
|
|
|
|
2023-08-25 17:07:53 +08:00
|
|
|
|
// 清空节点集合
|
|
|
|
|
|
this._rootNodes.clear();
|
|
|
|
|
|
this._tranNodes.clear();
|
|
|
|
|
|
|
2023-08-11 16:48:09 +08:00
|
|
|
|
// 清空任务池
|
2023-08-31 13:38:06 +08:00
|
|
|
|
clearFetchPool();
|
2023-08-05 15:32:51 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2024-03-15 10:35:30 +08:00
|
|
|
|
_removeInjector = () => {
|
|
|
|
|
|
document
|
|
|
|
|
|
.querySelectorAll(`[data-source^="KISS-Calendar"]`)
|
|
|
|
|
|
?.forEach((el) => el.remove());
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-08-24 14:57:54 +08:00
|
|
|
|
_reTranslate = debounce(() => {
|
2023-08-25 17:07:53 +08:00
|
|
|
|
if (this._rule.transOpen === "true") {
|
2024-03-17 12:09:32 +08:00
|
|
|
|
window.removeEventListener("keydown", this._handleKeydown);
|
|
|
|
|
|
this._mutaObserver.disconnect();
|
|
|
|
|
|
this._interseObserver.disconnect();
|
2024-03-15 10:35:30 +08:00
|
|
|
|
this._removeInjector();
|
2023-08-25 17:07:53 +08:00
|
|
|
|
this._register();
|
2023-08-24 14:57:54 +08:00
|
|
|
|
}
|
2024-03-17 12:09:32 +08:00
|
|
|
|
}, this._setting.transInterval);
|
2023-08-24 14:57:54 +08:00
|
|
|
|
|
2024-01-03 10:10:54 +08:00
|
|
|
|
_invalidLength = (q) =>
|
2024-01-02 17:55:59 +08:00
|
|
|
|
!q ||
|
|
|
|
|
|
q.length < (this._setting.minLength ?? TRANS_MIN_LENGTH) ||
|
|
|
|
|
|
q.length > (this._setting.maxLength ?? TRANS_MAX_LENGTH);
|
|
|
|
|
|
|
2023-08-05 15:32:51 +08:00
|
|
|
|
_render = (el) => {
|
2023-08-26 13:10:13 +08:00
|
|
|
|
let traEl = el.querySelector(APP_LCNAME);
|
|
|
|
|
|
|
2023-08-09 13:22:10 +08:00
|
|
|
|
// 已翻译
|
2023-08-26 13:10:13 +08:00
|
|
|
|
if (traEl) {
|
2024-03-16 23:37:27 +08:00
|
|
|
|
if (this._rule.transOnly === "true") {
|
2024-03-13 16:35:40 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-26 13:10:13 +08:00
|
|
|
|
const preText = this._tranNodes.get(el);
|
|
|
|
|
|
const curText = el.innerText.trim();
|
|
|
|
|
|
// const traText = traEl.innerText.trim();
|
|
|
|
|
|
|
|
|
|
|
|
// todo
|
|
|
|
|
|
// 1. traText when loading
|
|
|
|
|
|
// 2. replace startsWith
|
|
|
|
|
|
if (curText.startsWith(preText)) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
traEl.remove();
|
2023-08-05 15:32:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-02 17:55:59 +08:00
|
|
|
|
let q = el.innerText.trim();
|
2024-03-16 23:37:27 +08:00
|
|
|
|
if (this._rule.transOnly === "true") {
|
2024-03-13 16:35:40 +08:00
|
|
|
|
this._tranNodes.set(el, el.innerHTML);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this._tranNodes.set(el, q);
|
|
|
|
|
|
}
|
2024-01-02 17:55:59 +08:00
|
|
|
|
const keeps = [];
|
2024-01-19 16:02:53 +08:00
|
|
|
|
|
|
|
|
|
|
// 保留元素
|
|
|
|
|
|
const [matchSelector, subSelector] = this._keepSelector;
|
|
|
|
|
|
if (matchSelector || subSelector) {
|
2024-01-02 17:55:59 +08:00
|
|
|
|
let text = "";
|
|
|
|
|
|
el.childNodes.forEach((child) => {
|
2024-01-12 16:04:34 +08:00
|
|
|
|
if (
|
|
|
|
|
|
child.nodeType === 1 &&
|
2024-01-19 16:02:53 +08:00
|
|
|
|
((matchSelector && child.matches(matchSelector)) ||
|
|
|
|
|
|
(subSelector && child.querySelector(subSelector)))
|
2024-01-12 16:04:34 +08:00
|
|
|
|
) {
|
2024-01-03 10:10:54 +08:00
|
|
|
|
if (child.nodeName === "IMG") {
|
|
|
|
|
|
child.style.cssText += `width: ${child.width}px;`;
|
|
|
|
|
|
child.style.cssText += `height: ${child.height}px;`;
|
|
|
|
|
|
}
|
2024-01-19 16:02:53 +08:00
|
|
|
|
text += `[${keeps.length}]`;
|
2024-01-02 17:55:59 +08:00
|
|
|
|
keeps.push(child.outerHTML);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
text += child.textContent;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (keeps.length > 0) {
|
2024-02-26 16:34:53 +08:00
|
|
|
|
// textContent会保留些无用的换行符,严重影响翻译质量
|
|
|
|
|
|
if (q.includes("\n")) {
|
|
|
|
|
|
q = text;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
q = text.replaceAll("\n", " ");
|
|
|
|
|
|
}
|
2024-01-02 17:55:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-19 16:02:53 +08:00
|
|
|
|
// 太长或太短
|
|
|
|
|
|
if (this._invalidLength(q.replace(/\[(\d+)\]/g, "").trim())) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 专业术语
|
2024-02-02 12:10:27 +08:00
|
|
|
|
if (this._terms.length > 0) {
|
2024-02-02 15:44:44 +08:00
|
|
|
|
for (const term of this._terms) {
|
2024-02-02 12:10:27 +08:00
|
|
|
|
const re = new RegExp(term[0], "g");
|
|
|
|
|
|
q = q.replace(re, (t) => {
|
|
|
|
|
|
const text = `[${keeps.length}]`;
|
2024-03-19 09:55:56 +08:00
|
|
|
|
keeps.push(`<i class="kiss-trem">${term[1] || t}</i>`);
|
2024-02-02 12:10:27 +08:00
|
|
|
|
return text;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2024-01-19 16:02:53 +08:00
|
|
|
|
}
|
2024-01-02 17:55:59 +08:00
|
|
|
|
|
2023-08-26 13:10:13 +08:00
|
|
|
|
traEl = document.createElement(APP_LCNAME);
|
|
|
|
|
|
traEl.style.visibility = "visible";
|
2024-03-16 23:37:27 +08:00
|
|
|
|
// if (this._rule.transOnly === "true") {
|
2024-03-14 16:26:17 +08:00
|
|
|
|
// el.innerHTML = "";
|
|
|
|
|
|
// }
|
2024-03-14 18:06:28 +08:00
|
|
|
|
const { selectStyle, parentStyle } = this._rule;
|
2023-08-26 13:10:13 +08:00
|
|
|
|
el.appendChild(traEl);
|
2024-03-14 18:06:28 +08:00
|
|
|
|
el.style.cssText += selectStyle;
|
2023-08-24 14:57:54 +08:00
|
|
|
|
if (el.parentElement) {
|
2024-03-14 18:06:28 +08:00
|
|
|
|
el.parentElement.style.cssText += parentStyle;
|
2023-08-24 14:57:54 +08:00
|
|
|
|
}
|
2023-08-05 15:32:51 +08:00
|
|
|
|
|
2023-08-26 13:10:13 +08:00
|
|
|
|
const root = createRoot(traEl);
|
2024-03-14 16:26:17 +08:00
|
|
|
|
root.render(<Content q={q} keeps={keeps} translator={this} $el={el} />);
|
2023-08-05 15:32:51 +08:00
|
|
|
|
};
|
|
|
|
|
|
}
|