Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dece4fcdb | ||
|
|
df950a1bd2 | ||
|
|
74b9ee31fa | ||
|
|
64cd55fe58 | ||
|
|
e80ede14fb | ||
|
|
45ba9d3320 | ||
|
|
47c7048538 | ||
|
|
f9bfa8101f | ||
|
|
620ac464eb | ||
|
|
62289f8ab8 | ||
|
|
d84594da96 | ||
|
|
e1d74aae6a | ||
|
|
c4980d9eb7 | ||
|
|
882d83c6b7 |
2
.env
2
.env
@@ -2,7 +2,7 @@ GENERATE_SOURCEMAP=false
|
||||
|
||||
REACT_APP_NAME=KISS Translator
|
||||
REACT_APP_NAME_CN=简约翻译
|
||||
REACT_APP_VERSION=1.5.5
|
||||
REACT_APP_VERSION=1.5.7
|
||||
REACT_APP_HOMEPAGE=https://github.com/fishjar/kiss-translator
|
||||
REACT_APP_OPTIONSPAGE=https://kiss-translator.rayjar.com/options
|
||||
REACT_APP_OPTIONSPAGE2=https://fishjar.github.io/kiss-translator/options.html
|
||||
|
||||
@@ -89,7 +89,7 @@ const userscriptWebpack = (config, env) => {
|
||||
// @grant GM.getValue
|
||||
// @grant GM.deleteValue
|
||||
// @grant GM.info
|
||||
// @grant unsafeWindow
|
||||
// @inject-into content
|
||||
// @connect translate.googleapis.com
|
||||
// @connect api-edge.cognitive.microsofttranslator.com
|
||||
// @connect edge.microsoft.com
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "kiss-translator",
|
||||
"description": "A minimalist bilingual translation Extension & Greasemonkey Script",
|
||||
"version": "1.5.5",
|
||||
"version": "1.5.7",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
@@ -37,8 +37,7 @@
|
||||
"react-app/jest"
|
||||
],
|
||||
"globals": {
|
||||
"GM": true,
|
||||
"unsafeWindow": true
|
||||
"GM": true
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "__MSG_app_name__",
|
||||
"description": "__MSG_app_description__",
|
||||
"version": "1.5.5",
|
||||
"version": "1.5.7",
|
||||
"default_locale": "en",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 3,
|
||||
"name": "__MSG_app_name__",
|
||||
"description": "__MSG_app_description__",
|
||||
"version": "1.5.5",
|
||||
"version": "1.5.7",
|
||||
"default_locale": "en",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
||||
|
||||
@@ -193,8 +193,8 @@ export const I18N = {
|
||||
en: `1. The asterisk (*) wildcard is supported. 2. Multiple URLs can be separated by English commas ",".`,
|
||||
},
|
||||
selector_helper: {
|
||||
zh: `1、遵循CSS选择器规则。2、留空表示采用全局设置。`,
|
||||
en: `1. Follow CSS selector rules. 2. Leave blank to adopt the global setting.`,
|
||||
zh: `1、遵循CSS选择器规则。2、留空表示采用全局设置。3、多个CSS选择器之间用“;”隔开。4、“shadow root”选择器和内部选择器用“>>>”隔开。`,
|
||||
en: `1. Follow the CSS selector rules. 2. Leave blank to adopt the global setting. 3. Separate multiple CSS selectors with ";". 4. The "shadow root" selector and the internal selector are separated by ">>>".`,
|
||||
},
|
||||
translate_switch: {
|
||||
zh: `开启翻译`,
|
||||
|
||||
@@ -28,7 +28,7 @@ const RULES = [
|
||||
},
|
||||
{
|
||||
pattern: `www.foxnews.com`,
|
||||
selector: `h1, h2, .title, .sidebar [data-type="Title"], .article-content :is(li, p, h1, h2, h3, h4, h5, h6, dd); [data-spotim-module="conversation"]>div >>> [data-spot-im-class="message-text"] p, [data-spot-im-class="message-text"]`,
|
||||
selector: `h1, h2, .title, .sidebar [data-type="Title"], .article-content ${DEFAULT_SELECTOR}; [data-spotim-module="conversation"]>div >>> [data-spot-im-class="message-text"] p, [data-spot-im-class="message-text"]`,
|
||||
},
|
||||
{
|
||||
pattern: `bearblog.dev, www.theverge.com, www.tampermonkey.net/documentation.php`,
|
||||
|
||||
@@ -12,7 +12,7 @@ import { isIframe } from "./libs/iframe";
|
||||
/**
|
||||
* 入口函数
|
||||
*/
|
||||
(async () => {
|
||||
const init = async () => {
|
||||
const href = isIframe ? document.referrer : document.location.href;
|
||||
const setting = await getSetting();
|
||||
const rules = await getRules();
|
||||
@@ -38,4 +38,15 @@ import { isIframe } from "./libs/iframe";
|
||||
}
|
||||
return { data: translator.rule };
|
||||
});
|
||||
};
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
await init();
|
||||
} catch (err) {
|
||||
const $err = document.createElement("div");
|
||||
$err.innerText = `KISS-Translator: ${err.message}`;
|
||||
$err.style.cssText = "background:red; color:#fff; z-index:10000;";
|
||||
document.body.prepend($err);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -19,7 +19,7 @@ import { msAuth } from "./auth";
|
||||
* @param {*} init
|
||||
* @returns
|
||||
*/
|
||||
const fetchGM = async (input, { method = "GET", headers, body } = {}) =>
|
||||
export const fetchGM = async (input, { method = "GET", headers, body } = {}) =>
|
||||
new Promise((resolve, reject) => {
|
||||
GM.xmlHttpRequest({
|
||||
method,
|
||||
@@ -74,11 +74,21 @@ const fetchApi = async ({ input, init = {}, translator, token }) => {
|
||||
}
|
||||
|
||||
if (isGm) {
|
||||
const connects = GM?.info?.script?.connects || [];
|
||||
let info;
|
||||
if (window.KISS_GM) {
|
||||
info = await window.KISS_GM.getInfo();
|
||||
} else {
|
||||
info = GM.info;
|
||||
}
|
||||
const connects = info?.script?.connects || [];
|
||||
const url = new URL(input);
|
||||
const isSafe = connects.find((item) => url.hostname.endsWith(item));
|
||||
if (isSafe) {
|
||||
return fetchGM(input, init);
|
||||
if (window.KISS_GM) {
|
||||
return window.KISS_GM.fetch(input, init);
|
||||
} else {
|
||||
return fetchGM(input, init);
|
||||
}
|
||||
}
|
||||
}
|
||||
return fetch(input, init);
|
||||
|
||||
97
src/libs/gm.js
Normal file
97
src/libs/gm.js
Normal file
@@ -0,0 +1,97 @@
|
||||
import { fetchGM } from "./fetch";
|
||||
|
||||
/**
|
||||
* 注入页面的脚本,请求并接受GM接口信息
|
||||
* @param {*} param0
|
||||
*/
|
||||
export const injectScript = (ping) => {
|
||||
const MSG_GM_xmlHttpRequest = "xmlHttpRequest";
|
||||
const MSG_GM_setValue = "setValue";
|
||||
const MSG_GM_getValue = "getValue";
|
||||
const MSG_GM_deleteValue = "deleteValue";
|
||||
const MSG_GM_info = "info";
|
||||
let GM_info;
|
||||
|
||||
const promiseGM = (action, args, timeout = 5000) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const pong = btoa(Math.random()).slice(3, 11);
|
||||
const handleEvent = (e) => {
|
||||
window.removeEventListener(pong, handleEvent);
|
||||
const { data, error } = e.detail;
|
||||
if (error) {
|
||||
reject(new Error(error));
|
||||
} else {
|
||||
resolve(data);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener(pong, handleEvent);
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(ping, { detail: { action, args, pong } })
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
window.removeEventListener(pong, handleEvent);
|
||||
reject(new Error("timeout"));
|
||||
}, timeout);
|
||||
});
|
||||
|
||||
window.KISS_GM = {
|
||||
fetch: (input, init) => promiseGM(MSG_GM_xmlHttpRequest, { input, init }),
|
||||
setValue: (key, val) => promiseGM(MSG_GM_setValue, { key, val }),
|
||||
getValue: (key) => promiseGM(MSG_GM_getValue, { key }),
|
||||
deleteValue: (key) => promiseGM(MSG_GM_deleteValue, { key }),
|
||||
getInfo: () => {
|
||||
if (GM_info) {
|
||||
return GM_info;
|
||||
}
|
||||
return promiseGM(MSG_GM_info);
|
||||
},
|
||||
};
|
||||
window.APP_NAME = process.env.REACT_APP_NAME;
|
||||
};
|
||||
|
||||
/**
|
||||
* 监听并回应页面对GM接口的请求
|
||||
* @param {*} param0
|
||||
*/
|
||||
export const handlePing = async (e) => {
|
||||
const MSG_GM_xmlHttpRequest = "xmlHttpRequest";
|
||||
const MSG_GM_setValue = "setValue";
|
||||
const MSG_GM_getValue = "getValue";
|
||||
const MSG_GM_deleteValue = "deleteValue";
|
||||
const MSG_GM_info = "info";
|
||||
const { action, args, pong } = e.detail;
|
||||
let res;
|
||||
try {
|
||||
switch (action) {
|
||||
case MSG_GM_xmlHttpRequest:
|
||||
const { input, init } = args;
|
||||
res = await fetchGM(input, init);
|
||||
break;
|
||||
case MSG_GM_setValue:
|
||||
const { key, val } = args;
|
||||
await GM.setValue(key, val);
|
||||
res = val;
|
||||
break;
|
||||
case MSG_GM_getValue:
|
||||
res = await GM.getValue(args.key);
|
||||
break;
|
||||
case MSG_GM_deleteValue:
|
||||
await GM.deleteValue(args.key);
|
||||
res = "ok";
|
||||
break;
|
||||
case MSG_GM_info:
|
||||
res = GM.info;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`message action is unavailable: ${action}`);
|
||||
}
|
||||
|
||||
window.dispatchEvent(new CustomEvent(pong, { detail: { data: res } }));
|
||||
} catch (err) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(pong, { detail: { error: err.message } })
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -99,6 +99,10 @@ export const matchRule = async (
|
||||
* @returns
|
||||
*/
|
||||
export const detectLang = async (q) => {
|
||||
const res = await browser?.i18n.detectLanguage(q);
|
||||
return res?.languages?.[0]?.language;
|
||||
try {
|
||||
const res = await browser?.i18n?.detectLanguage(q);
|
||||
return res?.languages?.[0]?.language;
|
||||
} catch (err) {
|
||||
console.log("[detect lang]", err);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,8 +4,8 @@ async function set(key, val) {
|
||||
if (isExt) {
|
||||
await browser.storage.local.set({ [key]: val });
|
||||
} else if (isGm) {
|
||||
const oldValue = await GM.getValue(key);
|
||||
await GM.setValue(key, val);
|
||||
const oldValue = await (window.KISS_GM || GM).getValue(key);
|
||||
await (window.KISS_GM || GM).setValue(key, val);
|
||||
window.dispatchEvent(
|
||||
new StorageEvent("storage", {
|
||||
key,
|
||||
@@ -31,7 +31,7 @@ async function get(key) {
|
||||
const val = await browser.storage.local.get([key]);
|
||||
return val[key];
|
||||
} else if (isGm) {
|
||||
const val = await GM.getValue(key);
|
||||
const val = await (window.KISS_GM || GM).getValue(key);
|
||||
return val;
|
||||
}
|
||||
return window.localStorage.getItem(key);
|
||||
@@ -41,8 +41,8 @@ async function del(key) {
|
||||
if (isExt) {
|
||||
await browser.storage.local.remove([key]);
|
||||
} else if (isGm) {
|
||||
const oldValue = await GM.getValue(key);
|
||||
await GM.deleteValue(key);
|
||||
const oldValue = await (window.KISS_GM || GM).getValue(key);
|
||||
await (window.KISS_GM || GM).deleteValue(key);
|
||||
window.dispatchEvent(
|
||||
new StorageEvent("storage", {
|
||||
key,
|
||||
|
||||
@@ -9,19 +9,28 @@ import { trySyncAllSubRules } from "./libs/rules";
|
||||
import { isGm } from "./libs/browser";
|
||||
import { MSG_TRANS_TOGGLE, MSG_TRANS_PUTRULE } from "./config";
|
||||
import { isIframe } from "./libs/iframe";
|
||||
import { handlePing, injectScript } from "./libs/gm";
|
||||
|
||||
/**
|
||||
* 入口函数
|
||||
*/
|
||||
(async () => {
|
||||
const init = async () => {
|
||||
// 设置页面
|
||||
if (
|
||||
document.location.href.includes(process.env.REACT_APP_OPTIONSPAGE_DEV) ||
|
||||
document.location.href.includes(process.env.REACT_APP_OPTIONSPAGE) ||
|
||||
document.location.href.includes(process.env.REACT_APP_OPTIONSPAGE2)
|
||||
) {
|
||||
unsafeWindow.GM = GM;
|
||||
unsafeWindow.APP_NAME = process.env.REACT_APP_NAME;
|
||||
// unsafeWindow.GM = GM;
|
||||
// unsafeWindow.APP_NAME = process.env.REACT_APP_NAME;
|
||||
const ping = btoa(Math.random()).slice(3, 11);
|
||||
window.addEventListener(ping, handlePing);
|
||||
// window.eval(`(${injectScript})("${ping}")`); // eslint-disable-line
|
||||
const script = document.createElement("script");
|
||||
script.textContent = `(${injectScript})("${ping}")`;
|
||||
if (document.head) {
|
||||
document.head.append(script);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,22 +83,37 @@ import { isIframe } from "./libs/iframe";
|
||||
|
||||
// 注册菜单
|
||||
if (isGm) {
|
||||
GM.registerMenuCommand(
|
||||
"Toggle Translate",
|
||||
(event) => {
|
||||
translator.toggle();
|
||||
},
|
||||
"Q"
|
||||
);
|
||||
GM.registerMenuCommand(
|
||||
"Toggle Style",
|
||||
(event) => {
|
||||
translator.toggleStyle();
|
||||
},
|
||||
"C"
|
||||
);
|
||||
try {
|
||||
GM.registerMenuCommand(
|
||||
"Toggle Translate",
|
||||
(event) => {
|
||||
translator.toggle();
|
||||
},
|
||||
"Q"
|
||||
);
|
||||
GM.registerMenuCommand(
|
||||
"Toggle Style",
|
||||
(event) => {
|
||||
translator.toggleStyle();
|
||||
},
|
||||
"C"
|
||||
);
|
||||
} catch (err) {
|
||||
console.log("[registerMenuCommand]", err);
|
||||
}
|
||||
}
|
||||
|
||||
// 同步订阅规则
|
||||
trySyncAllSubRules(setting);
|
||||
};
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
await init();
|
||||
} catch (err) {
|
||||
const $err = document.createElement("div");
|
||||
$err.innerText = `KISS-Translator: ${err.message}`;
|
||||
$err.style.cssText = "background:red; color:#fff; z-index:10000;";
|
||||
document.body.prepend($err);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -62,6 +62,7 @@ export default function Options() {
|
||||
if (isGm && !ready) {
|
||||
return (
|
||||
<center>
|
||||
<p>{`KISS Translator v${process.env.REACT_APP_VERSION}`}</p>
|
||||
<CircularProgress />
|
||||
</center>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user