feat: Restructured core logic to support automatic page scanning and rich text translation
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// Function to inject inline JavaScript code
|
||||
export const injectInlineJs = (code) => {
|
||||
const el = document.createElement("script");
|
||||
el.setAttribute("data-source", "KISS-Calendar injectInlineJs");
|
||||
el.setAttribute("data-source", "kiss-inject injectInlineJs");
|
||||
el.setAttribute("type", "text/javascript");
|
||||
el.textContent = code;
|
||||
document.body?.appendChild(el);
|
||||
@@ -10,7 +10,7 @@ export const injectInlineJs = (code) => {
|
||||
// Function to inject external JavaScript file
|
||||
export const injectExternalJs = (src) => {
|
||||
const el = document.createElement("script");
|
||||
el.setAttribute("data-source", "KISS-Calendar injectExternalJs");
|
||||
el.setAttribute("data-source", "kiss-inject injectExternalJs");
|
||||
el.setAttribute("type", "text/javascript");
|
||||
el.setAttribute("src", src);
|
||||
document.body?.appendChild(el);
|
||||
@@ -19,7 +19,7 @@ export const injectExternalJs = (src) => {
|
||||
// Function to inject internal CSS code
|
||||
export const injectInternalCss = (styles) => {
|
||||
const el = document.createElement("style");
|
||||
el.setAttribute("data-source", "KISS-Calendar injectInternalCss");
|
||||
el.setAttribute("data-source", "kiss-inject injectInternalCss");
|
||||
el.textContent = styles;
|
||||
document.head?.appendChild(el);
|
||||
};
|
||||
@@ -27,7 +27,7 @@ export const injectInternalCss = (styles) => {
|
||||
// Function to inject external CSS file
|
||||
export const injectExternalCss = (href) => {
|
||||
const el = document.createElement("link");
|
||||
el.setAttribute("data-source", "KISS-Calendar injectExternalCss");
|
||||
el.setAttribute("data-source", "kiss-inject injectExternalCss");
|
||||
el.setAttribute("rel", "stylesheet");
|
||||
el.setAttribute("type", "text/css");
|
||||
el.setAttribute("href", href);
|
||||
|
||||
@@ -6,13 +6,13 @@ import {
|
||||
OPT_STYLE_ALL,
|
||||
OPT_LANGS_FROM,
|
||||
OPT_LANGS_TO,
|
||||
OPT_TIMING_ALL,
|
||||
// OPT_TIMING_ALL,
|
||||
GLOBLA_RULE,
|
||||
} from "../config";
|
||||
import { loadOrFetchSubRules } from "./subRules";
|
||||
import { getRulesWithDefault, setRules } from "./storage";
|
||||
import { trySyncRules } from "./sync";
|
||||
import { FIXER_ALL } from "./webfix";
|
||||
// import { FIXER_ALL } from "./webfix";
|
||||
import { kissLog } from "./log";
|
||||
|
||||
/**
|
||||
@@ -68,15 +68,17 @@ export const matchRule = async (
|
||||
[
|
||||
"selector",
|
||||
"keepSelector",
|
||||
"rootsSelector",
|
||||
"ignoreSelector",
|
||||
"terms",
|
||||
"selectStyle",
|
||||
"parentStyle",
|
||||
"injectJs",
|
||||
"injectCss",
|
||||
"fixerSelector",
|
||||
// "fixerSelector",
|
||||
"transStartHook",
|
||||
"transEndHook",
|
||||
"transRemoveHook",
|
||||
// "transRemoveHook",
|
||||
].forEach((key) => {
|
||||
if (!rule[key]?.trim()) {
|
||||
rule[key] = globalRule[key];
|
||||
@@ -89,12 +91,15 @@ export const matchRule = async (
|
||||
"toLang",
|
||||
"transOpen",
|
||||
"transOnly",
|
||||
"transTiming",
|
||||
// "transTiming",
|
||||
"autoScan",
|
||||
"hasRichText",
|
||||
"hasShadowroot",
|
||||
"transTag",
|
||||
"transTitle",
|
||||
"transSelected",
|
||||
"detectRemote",
|
||||
"fixerFunc",
|
||||
// "fixerFunc",
|
||||
].forEach((key) => {
|
||||
if (rule[key] === undefined || rule[key] === GLOBAL_KEY) {
|
||||
rule[key] = globalRule[key];
|
||||
@@ -146,6 +151,8 @@ export const checkRules = (rules) => {
|
||||
pattern,
|
||||
selector,
|
||||
keepSelector,
|
||||
rootsSelector,
|
||||
ignoreSelector,
|
||||
terms,
|
||||
selectStyle,
|
||||
parentStyle,
|
||||
@@ -159,21 +166,26 @@ export const checkRules = (rules) => {
|
||||
bgColor,
|
||||
textDiyStyle,
|
||||
transOnly,
|
||||
transTiming,
|
||||
autoScan,
|
||||
hasRichText,
|
||||
hasShadowroot,
|
||||
// transTiming,
|
||||
transTag,
|
||||
transTitle,
|
||||
transSelected,
|
||||
detectRemote,
|
||||
skipLangs,
|
||||
fixerSelector,
|
||||
fixerFunc,
|
||||
// fixerSelector,
|
||||
// fixerFunc,
|
||||
transStartHook,
|
||||
transEndHook,
|
||||
transRemoveHook,
|
||||
// transRemoveHook,
|
||||
}) => ({
|
||||
pattern: pattern.trim(),
|
||||
selector: type(selector) === "string" ? selector : "",
|
||||
keepSelector: type(keepSelector) === "string" ? keepSelector : "",
|
||||
rootsSelector: type(rootsSelector) === "string" ? rootsSelector : "",
|
||||
ignoreSelector: type(ignoreSelector) === "string" ? ignoreSelector : "",
|
||||
terms: type(terms) === "string" ? terms : "",
|
||||
selectStyle: type(selectStyle) === "string" ? selectStyle : "",
|
||||
parentStyle: type(parentStyle) === "string" ? parentStyle : "",
|
||||
@@ -187,18 +199,21 @@ export const checkRules = (rules) => {
|
||||
textStyle: matchValue([GLOBAL_KEY, ...OPT_STYLE_ALL], textStyle),
|
||||
transOpen: matchValue([GLOBAL_KEY, "true", "false"], transOpen),
|
||||
transOnly: matchValue([GLOBAL_KEY, "true", "false"], transOnly),
|
||||
transTiming: matchValue([GLOBAL_KEY, ...OPT_TIMING_ALL], transTiming),
|
||||
autoScan: matchValue([GLOBAL_KEY, "true", "false"], autoScan),
|
||||
hasRichText: matchValue([GLOBAL_KEY, "true", "false"], hasRichText),
|
||||
hasShadowroot: matchValue([GLOBAL_KEY, "true", "false"], hasShadowroot),
|
||||
// transTiming: matchValue([GLOBAL_KEY, ...OPT_TIMING_ALL], transTiming),
|
||||
transTag: matchValue([GLOBAL_KEY, "span", "font"], transTag),
|
||||
transTitle: matchValue([GLOBAL_KEY, "true", "false"], transTitle),
|
||||
transSelected: matchValue([GLOBAL_KEY, "true", "false"], transSelected),
|
||||
detectRemote: matchValue([GLOBAL_KEY, "true", "false"], detectRemote),
|
||||
skipLangs: type(skipLangs) === "array" ? skipLangs : [],
|
||||
fixerSelector: type(fixerSelector) === "string" ? fixerSelector : "",
|
||||
// fixerSelector: type(fixerSelector) === "string" ? fixerSelector : "",
|
||||
transStartHook: type(transStartHook) === "string" ? transStartHook : "",
|
||||
transEndHook: type(transEndHook) === "string" ? transEndHook : "",
|
||||
transRemoveHook:
|
||||
type(transRemoveHook) === "string" ? transRemoveHook : "",
|
||||
fixerFunc: matchValue([GLOBAL_KEY, ...FIXER_ALL], fixerFunc),
|
||||
// transRemoveHook:
|
||||
// type(transRemoveHook) === "string" ? transRemoveHook : "",
|
||||
// fixerFunc: matchValue([GLOBAL_KEY, ...FIXER_ALL], fixerFunc),
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
56
src/libs/shadowroot.js
Normal file
56
src/libs/shadowroot.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import { kissLog } from "./log";
|
||||
|
||||
/**
|
||||
* @class ShadowRootMonitor
|
||||
* @description 通过覆写 Element.prototype.attachShadow 来监控页面上所有新创建的 Shadow DOM
|
||||
*/
|
||||
export class ShadowRootMonitor {
|
||||
/**
|
||||
* @param {function(ShadowRoot): void} callback - 当一个新的 shadowRoot 被创建时调用的回调函数。
|
||||
*/
|
||||
constructor(callback) {
|
||||
if (typeof callback !== "function") {
|
||||
throw new Error("Callback must be a function.");
|
||||
}
|
||||
|
||||
this.callback = callback;
|
||||
this.isMonitoring = false;
|
||||
this.originalAttachShadow = Element.prototype.attachShadow;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始监控 shadowRoot 的创建。
|
||||
*/
|
||||
start() {
|
||||
if (this.isMonitoring) {
|
||||
return;
|
||||
}
|
||||
const monitorInstance = this;
|
||||
|
||||
Element.prototype.attachShadow = function (...args) {
|
||||
const shadowRoot = monitorInstance.originalAttachShadow.apply(this, args);
|
||||
if (shadowRoot) {
|
||||
try {
|
||||
monitorInstance.callback(shadowRoot);
|
||||
} catch (error) {
|
||||
kissLog(error, "Error in ShadowRootMonitor callback");
|
||||
}
|
||||
}
|
||||
return shadowRoot;
|
||||
};
|
||||
|
||||
this.isMonitoring = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止监控,并恢复原始的 attachShadow 方法。
|
||||
*/
|
||||
stop() {
|
||||
if (!this.isMonitoring) {
|
||||
return;
|
||||
}
|
||||
|
||||
Element.prototype.attachShadow = this.originalAttachShadow;
|
||||
this.isMonitoring = false;
|
||||
}
|
||||
}
|
||||
102
src/libs/style.js
Normal file
102
src/libs/style.js
Normal file
@@ -0,0 +1,102 @@
|
||||
import { css } from "@emotion/css";
|
||||
import {
|
||||
OPT_STYLE_NONE,
|
||||
OPT_STYLE_LINE,
|
||||
OPT_STYLE_DOTLINE,
|
||||
OPT_STYLE_DASHLINE,
|
||||
OPT_STYLE_WAVYLINE,
|
||||
OPT_STYLE_DASHBOX,
|
||||
OPT_STYLE_FUZZY,
|
||||
OPT_STYLE_HIGHLIGHT,
|
||||
OPT_STYLE_BLOCKQUOTE,
|
||||
OPT_STYLE_DIY,
|
||||
DEFAULT_COLOR,
|
||||
} from "../config";
|
||||
|
||||
const genLineStyle = (style, color) => `
|
||||
opacity: 0.6;
|
||||
-webkit-opacity: 0.6;
|
||||
text-decoration-line: underline;
|
||||
text-decoration-style: ${style};
|
||||
text-decoration-color: ${color};
|
||||
text-decoration-thickness: 2px;
|
||||
text-underline-offset: 0.3em;
|
||||
-webkit-text-decoration-line: underline;
|
||||
-webkit-text-decoration-style: ${style};
|
||||
-webkit-text-decoration-color: ${color};
|
||||
-webkit-text-decoration-thickness: 2px;
|
||||
-webkit-text-underline-offset: 0.3em;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
-webkit-opacity: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
const genStyles = ({ textDiyStyle, bgColor = DEFAULT_COLOR }) => ({
|
||||
// 无样式
|
||||
[OPT_STYLE_NONE]: ``,
|
||||
// 下划线
|
||||
[OPT_STYLE_LINE]: genLineStyle("solid", bgColor),
|
||||
// 点状线
|
||||
[OPT_STYLE_DOTLINE]: genLineStyle("dotted", bgColor),
|
||||
// 虚线
|
||||
[OPT_STYLE_DASHLINE]: genLineStyle("dashed", bgColor),
|
||||
// 波浪线
|
||||
[OPT_STYLE_WAVYLINE]: genLineStyle("wavy", bgColor),
|
||||
// 虚线框
|
||||
[OPT_STYLE_DASHBOX]: `
|
||||
color: ${bgColor || DEFAULT_COLOR};
|
||||
border: 1px dashed ${bgColor || DEFAULT_COLOR};
|
||||
background: transparent;
|
||||
display: block;
|
||||
padding: 0.2em 0.5em;
|
||||
box-sizing: border-box;
|
||||
`,
|
||||
// 模糊
|
||||
[OPT_STYLE_FUZZY]: `
|
||||
filter: blur(0.2em);
|
||||
-webkit-filter: blur(0.2em);
|
||||
&:hover {
|
||||
filter: none;
|
||||
-webkit-filter: none;
|
||||
}
|
||||
`,
|
||||
// 高亮
|
||||
[OPT_STYLE_HIGHLIGHT]: `
|
||||
color: #fff;
|
||||
background-color: ${bgColor || DEFAULT_COLOR};
|
||||
`,
|
||||
// 引用
|
||||
[OPT_STYLE_BLOCKQUOTE]: `
|
||||
opacity: 0.6;
|
||||
-webkit-opacity: 0.6;
|
||||
display: block;
|
||||
padding: 0 0.75em;
|
||||
border-left: 0.25em solid ${bgColor || DEFAULT_COLOR};
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
-webkit-opacity: 1;
|
||||
}
|
||||
`,
|
||||
// 自定义
|
||||
[OPT_STYLE_DIY]: textDiyStyle,
|
||||
});
|
||||
|
||||
export const genTextClass = ({ textDiyStyle, bgColor = DEFAULT_COLOR }) => {
|
||||
const styles = genStyles({ textDiyStyle, bgColor });
|
||||
const textClass = {};
|
||||
let textStyles = "";
|
||||
Object.entries(styles).forEach(([k, v]) => {
|
||||
textClass[k] = css`
|
||||
${v}
|
||||
`;
|
||||
});
|
||||
Object.entries(styles).forEach(([k, v]) => {
|
||||
textStyles += `
|
||||
.${textClass[k]} {
|
||||
${v}
|
||||
}
|
||||
`;
|
||||
});
|
||||
return [textClass, textStyles];
|
||||
};
|
||||
@@ -1,34 +1,14 @@
|
||||
export const loadingSvg = `
|
||||
<svg viewBox="0 0 100 100" style="display:inline-block; width:100%; height: 100%; max-width: 24; max-height: 24;">
|
||||
<circle fill="#209CEE" stroke="none" cx="6" cy="50" r="6">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="1s"
|
||||
type="translate"
|
||||
values="0 15 ; 0 -15; 0 15"
|
||||
repeatCount="indefinite"
|
||||
begin="0.1"
|
||||
/>
|
||||
</circle>
|
||||
<circle fill="#209CEE" stroke="none" cx="30" cy="50" r="6">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="1s"
|
||||
type="translate"
|
||||
values="0 10 ; 0 -10; 0 10"
|
||||
repeatCount="indefinite"
|
||||
begin="0.2"
|
||||
/>
|
||||
</circle>
|
||||
<circle fill="#209CEE" stroke="none" cx="54" cy="50" r="6">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="1s"
|
||||
type="translate"
|
||||
values="0 5 ; 0 -5; 0 5"
|
||||
repeatCount="indefinite"
|
||||
begin="0.3"
|
||||
/>
|
||||
</circle>
|
||||
<svg viewBox="0 0 100 100"
|
||||
style="display: inline-block; width: 1em; height: 1em; vertical-align: middle;">
|
||||
<circle fill="#209CEE" stroke="none" cx="6" cy="50" r="6">
|
||||
<animateTransform attributeName="transform" dur="1s" type="translate" values="0 15 ; 0 -15; 0 15" repeatCount="indefinite" begin="0.1"/>
|
||||
</circle>
|
||||
<circle fill="#209CEE" stroke="none" cx="30" cy="50" r="6">
|
||||
<animateTransform attributeName="transform" dur="1s" type="translate" values="0 10 ; 0 -10; 0 10" repeatCount="indefinite" begin="0.2"/>
|
||||
</circle>
|
||||
<circle fill="#209CEE" stroke="none" cx="54" cy="50" r="6">
|
||||
<animateTransform attributeName="transform" dur="1s" type="translate" values="0 5 ; 0 -5; 0 5" repeatCount="indefinite" begin="0.3"/>
|
||||
</circle>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -177,7 +177,7 @@ export const sha256 = async (text, salt) => {
|
||||
* 生成随机事件名称
|
||||
* @returns
|
||||
*/
|
||||
export const genEventName = () => btoa(Math.random()).slice(3, 11);
|
||||
export const genEventName = () => `kiss-${btoa(Math.random()).slice(3, 11)}`;
|
||||
|
||||
/**
|
||||
* 判断两个 Set 是否相同
|
||||
@@ -302,3 +302,16 @@ export const extractJson = (raw) => {
|
||||
const match = s.match(/\{[\s\S]*\}/);
|
||||
return match ? match[0] : "{}";
|
||||
};
|
||||
|
||||
/**
|
||||
* 空闲执行
|
||||
* @param {*} cb
|
||||
* @param {*} timeout
|
||||
* @returns
|
||||
*/
|
||||
export const scheduleIdle = (cb, timeout = 200) => {
|
||||
if (window.requestIdleCallback) {
|
||||
return requestIdleCallback(cb, { timeout });
|
||||
}
|
||||
return setTimeout(cb, timeout);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user