feat: inject user js/css
This commit is contained in:
@@ -44,7 +44,7 @@
|
||||
"description": "__MSG_open_options__"
|
||||
}
|
||||
},
|
||||
"permissions": ["<all_urls>", "storage", "contextMenus"],
|
||||
"permissions": ["<all_urls>", "storage", "contextMenus", "scripting"],
|
||||
"icons": {
|
||||
"16": "images/logo16.png",
|
||||
"32": "images/logo32.png",
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"description": "__MSG_open_options__"
|
||||
}
|
||||
},
|
||||
"permissions": ["storage", "contextMenus"],
|
||||
"permissions": ["storage", "contextMenus", "scripting"],
|
||||
"host_permissions": ["<all_urls>"],
|
||||
"icons": {
|
||||
"16": "images/logo16.png",
|
||||
|
||||
@@ -10,6 +10,8 @@ import {
|
||||
MSG_OPEN_TRANBOX,
|
||||
MSG_CONTEXT_MENUS,
|
||||
MSG_COMMAND_SHORTCUTS,
|
||||
MSG_INJECT_JS,
|
||||
MSG_INJECT_CSS,
|
||||
CMD_TOGGLE_TRANSLATE,
|
||||
CMD_TOGGLE_STYLE,
|
||||
CMD_OPEN_OPTIONS,
|
||||
@@ -22,6 +24,8 @@ import { sendTabMsg } from "./libs/msg";
|
||||
import { trySyncAllSubRules } from "./libs/subRules";
|
||||
import { tryClearCaches } from "./libs";
|
||||
import { saveRule } from "./libs/rules";
|
||||
import { getCurTabId } from "./libs/msg";
|
||||
import { injectInlineJs, injectInternalCss } from "./libs/injector";
|
||||
|
||||
globalThis.ContextType = "BACKGROUND";
|
||||
|
||||
@@ -139,6 +143,40 @@ browser.runtime.onMessage.addListener(
|
||||
case MSG_SAVE_RULE:
|
||||
saveRule(args);
|
||||
break;
|
||||
case MSG_INJECT_JS:
|
||||
getCurTabId()
|
||||
.then((tabId) =>
|
||||
browser.scripting.executeScript({
|
||||
target: { tabId: tabId, allFrames: true },
|
||||
func: injectInlineJs,
|
||||
args: [args],
|
||||
world: "MAIN",
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
// skip
|
||||
})
|
||||
.catch((error) => {
|
||||
sendResponse({ error: error.message });
|
||||
});
|
||||
break;
|
||||
case MSG_INJECT_CSS:
|
||||
getCurTabId()
|
||||
.then((tabId) =>
|
||||
browser.scripting.executeScript({
|
||||
target: { tabId: tabId, allFrames: true },
|
||||
func: injectInternalCss,
|
||||
args: [args],
|
||||
world: "MAIN",
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
// skip
|
||||
})
|
||||
.catch((error) => {
|
||||
sendResponse({ error: error.message });
|
||||
});
|
||||
break;
|
||||
case MSG_CONTEXT_MENUS:
|
||||
const { contextMenuType } = args;
|
||||
addContextMenus(contextMenuType);
|
||||
|
||||
@@ -407,6 +407,22 @@ export const I18N = {
|
||||
zh: `0、支持正则表达式匹配。1、多条术语用换行或分号“;”隔开。2、术语和译文用英文逗号“,”隔开。3、没有译文视为不翻译术语。4、留空表示采用全局设置。`,
|
||||
en: `0. Supports regular expression matching. 1. Separate multiple terms with newlines or semicolons ";". 2. Terms and translations are separated by English commas ",". 3. If there is no translation, the term will be deemed not to be translated. 4. Leave blank to adopt the global setting.`,
|
||||
},
|
||||
selector_style: {
|
||||
zh: `选择器样式`,
|
||||
en: `Selector Style`,
|
||||
},
|
||||
selector_parent_style: {
|
||||
zh: `选择器父样式`,
|
||||
en: `Selector Parent Style`,
|
||||
},
|
||||
inject_js: {
|
||||
zh: `注入JS`,
|
||||
en: `Inject JS`,
|
||||
},
|
||||
inject_css: {
|
||||
zh: `注入CSS`,
|
||||
en: `Inject CSS`,
|
||||
},
|
||||
root_selector: {
|
||||
zh: `根选择器`,
|
||||
en: `Root Selector`,
|
||||
@@ -759,4 +775,8 @@ export const I18N = {
|
||||
zh: `是否同时翻译页面标题`,
|
||||
en: `Translate Page Title`,
|
||||
},
|
||||
more: {
|
||||
zh: `更多`,
|
||||
en: `More`,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -66,6 +66,8 @@ export const MSG_TRANS_PUTRULE = "trans_putrule";
|
||||
export const MSG_TRANS_CURRULE = "trans_currule";
|
||||
export const MSG_CONTEXT_MENUS = "context_menus";
|
||||
export const MSG_COMMAND_SHORTCUTS = "command_shortcuts";
|
||||
export const MSG_INJECT_JS = "inject_js";
|
||||
export const MSG_INJECT_CSS = "inject_css";
|
||||
|
||||
export const THEME_LIGHT = "light";
|
||||
export const THEME_DARK = "dark";
|
||||
@@ -335,6 +337,10 @@ export const GLOBLA_RULE = {
|
||||
transOpen: "false",
|
||||
bgColor: "",
|
||||
textDiyStyle: "",
|
||||
selectStyle: "-webkit-line-clamp: unset; max-height: none; height: auto;",
|
||||
parentStyle: "-webkit-line-clamp: unset; max-height: none; height: auto;",
|
||||
injectJs: "",
|
||||
injectCss: "",
|
||||
};
|
||||
|
||||
// 输入框翻译
|
||||
|
||||
@@ -18,6 +18,10 @@ export const DEFAULT_RULE = {
|
||||
transOpen: GLOBAL_KEY,
|
||||
bgColor: "",
|
||||
textDiyStyle: "",
|
||||
selectStyle: "",
|
||||
parentStyle: "",
|
||||
injectJs: "",
|
||||
injectCss: "",
|
||||
};
|
||||
|
||||
const DEFAULT_DIY_STYLE = `color: #666;
|
||||
|
||||
35
src/libs/injector.js
Normal file
35
src/libs/injector.js
Normal file
@@ -0,0 +1,35 @@
|
||||
// Function to inject inline JavaScript code
|
||||
export const injectInlineJs = (code) => {
|
||||
const el = document.createElement("script");
|
||||
el.setAttribute("data-source", "KISS-Calendar injectInlineJs");
|
||||
el.setAttribute("type", "text/javascript");
|
||||
el.textContent = code;
|
||||
document.body.appendChild(el);
|
||||
};
|
||||
|
||||
// Function to inject external JavaScript file
|
||||
export const injectExternalJs = (src) => {
|
||||
const el = document.createElement("script");
|
||||
el.setAttribute("data-source", "KISS-Calendar injectExternalJs");
|
||||
el.setAttribute("type", "text/javascript");
|
||||
el.setAttribute("src", src);
|
||||
document.body.appendChild(el);
|
||||
};
|
||||
|
||||
// Function to inject internal CSS code
|
||||
export const injectInternalCss = (styles) => {
|
||||
const el = document.createElement("style");
|
||||
el.setAttribute("data-source", "KISS-Calendar injectInternalCss");
|
||||
el.textContent = styles;
|
||||
document.head.appendChild(el);
|
||||
};
|
||||
|
||||
// Function to inject external CSS file
|
||||
export const injectExternalCss = (href) => {
|
||||
const el = document.createElement("link");
|
||||
el.setAttribute("data-source", "KISS-Calendar injectExternalCss");
|
||||
el.setAttribute("rel", "stylesheet");
|
||||
el.setAttribute("type", "text/css");
|
||||
el.setAttribute("href", href);
|
||||
document.head.appendChild(el);
|
||||
};
|
||||
@@ -68,6 +68,10 @@ export const matchRule = async (
|
||||
rule.selector = rule.selector?.trim() || globalRule.selector;
|
||||
rule.keepSelector = rule.keepSelector?.trim() || globalRule.keepSelector;
|
||||
rule.terms = rule.terms?.trim() || globalRule.terms;
|
||||
rule.selectStyle = rule.selectStyle?.trim() || globalRule.selectStyle;
|
||||
rule.parentStyle = rule.parentStyle?.trim() || globalRule.parentStyle;
|
||||
rule.injectJs = rule.injectJs?.trim() || globalRule.injectJs;
|
||||
rule.injectCss = rule.injectCss?.trim() || globalRule.injectCss;
|
||||
if (rule.textStyle === GLOBAL_KEY) {
|
||||
rule.textStyle = globalRule.textStyle;
|
||||
rule.bgColor = globalRule.bgColor;
|
||||
@@ -116,6 +120,10 @@ export const checkRules = (rules) => {
|
||||
selector,
|
||||
keepSelector,
|
||||
terms,
|
||||
selectStyle,
|
||||
parentStyle,
|
||||
injectJs,
|
||||
injectCss,
|
||||
translator,
|
||||
fromLang,
|
||||
toLang,
|
||||
@@ -128,6 +136,10 @@ export const checkRules = (rules) => {
|
||||
selector: type(selector) === "string" ? selector : "",
|
||||
keepSelector: type(keepSelector) === "string" ? keepSelector : "",
|
||||
terms: type(terms) === "string" ? terms : "",
|
||||
selectStyle: type(selectStyle) === "string" ? selectStyle : "",
|
||||
parentStyle: type(parentStyle) === "string" ? parentStyle : "",
|
||||
injectJs: type(injectJs) === "string" ? injectJs : "",
|
||||
injectCss: type(injectCss) === "string" ? injectCss : "",
|
||||
bgColor: type(bgColor) === "string" ? bgColor : "",
|
||||
textDiyStyle: type(textDiyStyle) === "string" ? textDiyStyle : "",
|
||||
translator: matchValue([GLOBAL_KEY, ...OPT_TRANS_ALL], translator),
|
||||
|
||||
@@ -4,6 +4,8 @@ import {
|
||||
TRANS_MIN_LENGTH,
|
||||
TRANS_MAX_LENGTH,
|
||||
MSG_TRANS_CURRULE,
|
||||
MSG_INJECT_JS,
|
||||
MSG_INJECT_CSS,
|
||||
OPT_STYLE_DASHLINE,
|
||||
OPT_STYLE_FUZZY,
|
||||
SHADOW_KEY,
|
||||
@@ -17,6 +19,7 @@ import { updateFetchPool, clearFetchPool } from "./fetch";
|
||||
import { debounce, genEventName } from "./utils";
|
||||
import { runFixer } from "./webfix";
|
||||
import { apiTranslate } from "../apis";
|
||||
import { sendBgMsg } from "./msg";
|
||||
|
||||
/**
|
||||
* 翻译类
|
||||
@@ -262,7 +265,8 @@ export class Translator {
|
||||
};
|
||||
|
||||
_register = () => {
|
||||
if (this._rule.fromLang === this._rule.toLang) {
|
||||
const { fromLang, toLang, injectJs, injectCss } = this._rule;
|
||||
if (fromLang === toLang) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -271,6 +275,10 @@ export class Translator {
|
||||
runFixer(this._fixerSetting);
|
||||
}
|
||||
|
||||
// 注入用户JS/CSS
|
||||
injectJs && sendBgMsg(MSG_INJECT_JS, injectJs);
|
||||
injectCss && sendBgMsg(MSG_INJECT_CSS, injectCss);
|
||||
|
||||
// 搜索节点
|
||||
this._queryNodes();
|
||||
|
||||
@@ -397,6 +405,11 @@ export class Translator {
|
||||
}
|
||||
});
|
||||
|
||||
// 移除用户JS/CSS
|
||||
document
|
||||
.querySelectorAll(`[data-source^="KISS-Calendar"]`)
|
||||
?.forEach((el) => el.remove());
|
||||
|
||||
// 清空节点集合
|
||||
this._rootNodes.clear();
|
||||
this._tranNodes.clear();
|
||||
@@ -500,12 +513,11 @@ export class Translator {
|
||||
// if (this._setting.transOnly) {
|
||||
// el.innerHTML = "";
|
||||
// }
|
||||
const { selectStyle, parentStyle } = this._rule;
|
||||
el.appendChild(traEl);
|
||||
el.style.cssText +=
|
||||
"-webkit-line-clamp: unset; max-height: none; height: auto;";
|
||||
el.style.cssText += selectStyle;
|
||||
if (el.parentElement) {
|
||||
el.parentElement.style.cssText +=
|
||||
"-webkit-line-clamp: unset; max-height: none; height: auto;";
|
||||
el.parentElement.style.cssText += parentStyle;
|
||||
}
|
||||
|
||||
// console.log({ q, keeps });
|
||||
|
||||
@@ -62,11 +62,16 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
const [disabled, setDisabled] = useState(editMode);
|
||||
const [errors, setErrors] = useState({});
|
||||
const [formValues, setFormValues] = useState(initFormValues);
|
||||
const [showMore, setShowMore] = useState(!rules);
|
||||
const {
|
||||
pattern,
|
||||
selector,
|
||||
keepSelector = "",
|
||||
terms = "",
|
||||
selectStyle = "",
|
||||
parentStyle = "",
|
||||
injectJs = "",
|
||||
injectCss = "",
|
||||
translator,
|
||||
fromLang,
|
||||
toLang,
|
||||
@@ -312,6 +317,47 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
{showMore && (
|
||||
<>
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("selector_style")}
|
||||
name="selectStyle"
|
||||
value={selectStyle}
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
/>
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("selector_parent_style")}
|
||||
name="parentStyle"
|
||||
value={parentStyle}
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
/>
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("inject_js")}
|
||||
name="injectJs"
|
||||
value={injectJs}
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
/>
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("inject_css")}
|
||||
name="injectCss"
|
||||
value={injectCss}
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{textStyle === OPT_STYLE_DIY && (
|
||||
<TextField
|
||||
size="small"
|
||||
@@ -353,6 +399,17 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
{i18n("delete")}
|
||||
</Button>
|
||||
)}
|
||||
{!showMore && (
|
||||
<Button
|
||||
size="small"
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
setShowMore(true);
|
||||
}}
|
||||
>
|
||||
{i18n("more")}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
@@ -366,6 +423,17 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
>
|
||||
{i18n("cancel")}
|
||||
</Button>
|
||||
{!showMore && (
|
||||
<Button
|
||||
size="small"
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
setShowMore(true);
|
||||
}}
|
||||
>
|
||||
{i18n("more")}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
@@ -378,6 +446,17 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
<Button size="small" variant="outlined" onClick={handleCancel}>
|
||||
{i18n("cancel")}
|
||||
</Button>
|
||||
{!showMore && (
|
||||
<Button
|
||||
size="small"
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
setShowMore(true);
|
||||
}}
|
||||
>
|
||||
{i18n("more")}
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
|
||||
Reference in New Issue
Block a user