Insert code into html only when translating text
This commit is contained in:
parent
6fb3a79767
commit
c78a27a7e0
|
@ -1,51 +1,11 @@
|
||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import browser from "webextension-polyfill";
|
import browser from "webextension-polyfill";
|
||||||
import translate from "src/common/translate";
|
import translate from "src/common/translate";
|
||||||
import { initSettings, getSettings, handleSettingsChange } from "src/settings/settings";
|
import { getSettings } from "src/settings/settings";
|
||||||
import { updateLogLevel, overWriteLogLevel } from "src/common/log";
|
|
||||||
import TranslateButton from "./TranslateButton";
|
import TranslateButton from "./TranslateButton";
|
||||||
import TranslatePanel from "./TranslatePanel";
|
import TranslatePanel from "./TranslatePanel";
|
||||||
import "../styles/TranslateContainer.scss";
|
import "../styles/TranslateContainer.scss";
|
||||||
|
|
||||||
const getSelectedText = () => {
|
|
||||||
const element = document.activeElement;
|
|
||||||
const isInTextField = element.tagName === "INPUT" || element.tagName === "TEXTAREA";
|
|
||||||
const selectedText = isInTextField
|
|
||||||
? element.value.substring(element.selectionStart, element.selectionEnd)
|
|
||||||
: window.getSelection().toString();
|
|
||||||
return selectedText;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getSelectedPosition = () => {
|
|
||||||
const element = document.activeElement;
|
|
||||||
const isInTextField = element.tagName === "INPUT" || element.tagName === "TEXTAREA";
|
|
||||||
const selectedRect = isInTextField
|
|
||||||
? element.getBoundingClientRect()
|
|
||||||
: window
|
|
||||||
.getSelection()
|
|
||||||
.getRangeAt(0)
|
|
||||||
.getBoundingClientRect();
|
|
||||||
|
|
||||||
let selectedPosition;
|
|
||||||
const panelReferencePoint = getSettings("panelReferencePoint");
|
|
||||||
switch (panelReferencePoint) {
|
|
||||||
case "topSelectedText":
|
|
||||||
selectedPosition = {
|
|
||||||
x: selectedRect.left + selectedRect.width / 2,
|
|
||||||
y: selectedRect.top
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case "bottomSelectedText":
|
|
||||||
default:
|
|
||||||
selectedPosition = {
|
|
||||||
x: selectedRect.left + selectedRect.width / 2,
|
|
||||||
y: selectedRect.bottom
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return selectedPosition;
|
|
||||||
};
|
|
||||||
|
|
||||||
const translateText = async (text, targetLang = getSettings("targetLang")) => {
|
const translateText = async (text, targetLang = getSettings("targetLang")) => {
|
||||||
const result = await translate(text, "auto", targetLang);
|
const result = await translate(text, "auto", targetLang);
|
||||||
return result;
|
return result;
|
||||||
|
@ -71,15 +31,10 @@ const matchesTargetLang = async selectedText => {
|
||||||
return matchsLangs;
|
return matchsLangs;
|
||||||
};
|
};
|
||||||
|
|
||||||
const waitTime = time => {
|
|
||||||
return new Promise(resolve => setTimeout(() => resolve(), time));
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class TranslateContainer extends Component {
|
export default class TranslateContainer extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
isInit: false,
|
|
||||||
shouldShowButton: false,
|
shouldShowButton: false,
|
||||||
buttonPosition: { x: 0, y: 0 },
|
buttonPosition: { x: 0, y: 0 },
|
||||||
shouldShowPanel: false,
|
shouldShowPanel: false,
|
||||||
|
@ -88,75 +43,26 @@ export default class TranslateContainer extends Component {
|
||||||
candidateText: "",
|
candidateText: "",
|
||||||
statusText: "OK"
|
statusText: "OK"
|
||||||
};
|
};
|
||||||
this.selectedText = "";
|
this.selectedText = props.selectedText;
|
||||||
this.selectedPosition = { x: 0, y: 0 };
|
this.selectedPosition = props.selectedPosition;
|
||||||
this.init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init = async () => {
|
componentDidMount = () => {
|
||||||
await initSettings();
|
if (this.props.shouldTranslate) this.showPanel();
|
||||||
this.setState({ isInit: true });
|
else this.handleTextSelect(this.props.clickedPosition);
|
||||||
document.addEventListener("mouseup", this.handleMouseUp);
|
|
||||||
document.addEventListener("keydown", this.handleKeyDown);
|
|
||||||
browser.storage.onChanged.addListener(handleSettingsChange);
|
|
||||||
browser.runtime.onMessage.addListener(this.handleMessage);
|
|
||||||
overWriteLogLevel();
|
|
||||||
updateLogLevel();
|
|
||||||
if (this.props.isFirst) this.disableExtensionByUrlList();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
disableExtensionByUrlList = () => {
|
handleTextSelect = async clickedPosition => {
|
||||||
const disableUrls = getSettings("disableUrlList").split("\n");
|
const onSelectBehavior = getSettings("whenSelectText");
|
||||||
let pageUrl;
|
if (onSelectBehavior === "dontShowButton") return this.props.removeContainer();
|
||||||
try {
|
|
||||||
pageUrl = top.location.href;
|
if (getSettings("ifCheckLang")) {
|
||||||
} catch (e) {
|
const matchesLang = await matchesTargetLang(this.selectedText);
|
||||||
pageUrl = document.referrer;
|
if (matchesLang) return this.props.removeContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
const matchesPageUrl = urlPattern => {
|
if (onSelectBehavior === "showButton") this.showButton(clickedPosition);
|
||||||
const pattern = urlPattern
|
else if (onSelectBehavior === "showPanel") this.showPanel(clickedPosition);
|
||||||
.trim()
|
|
||||||
.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, match => (match === "*" ? ".*" : "\\" + match));
|
|
||||||
if (pattern === "") return false;
|
|
||||||
return RegExp("^" + pattern + "$").test(pageUrl);
|
|
||||||
};
|
|
||||||
|
|
||||||
const isMatched = disableUrls.some(matchesPageUrl);
|
|
||||||
if (isMatched) this.props.removeElement();
|
|
||||||
};
|
|
||||||
|
|
||||||
handleMessage = async request => {
|
|
||||||
const empty = new Promise(resolve => {
|
|
||||||
setTimeout(() => {
|
|
||||||
return resolve("");
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
|
|
||||||
switch (request.message) {
|
|
||||||
case "getTabUrl":
|
|
||||||
if (window == window.parent) return location.href;
|
|
||||||
else return empty;
|
|
||||||
case "getSelectedText":
|
|
||||||
if (this.selectedText.length === 0) return empty;
|
|
||||||
else return this.selectedText;
|
|
||||||
case "translateSelectedText":
|
|
||||||
this.selectedText = getSelectedText();
|
|
||||||
if (this.selectedText.length === 0) return;
|
|
||||||
this.selectedPosition = getSelectedPosition();
|
|
||||||
this.hideButton();
|
|
||||||
this.showPanel();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleKeyDown = e => {
|
|
||||||
if (e.key === "Escape") {
|
|
||||||
this.hideButton();
|
|
||||||
this.hidePanel();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
showButton = clickedPosition => {
|
showButton = clickedPosition => {
|
||||||
|
@ -200,50 +106,7 @@ export default class TranslateContainer extends Component {
|
||||||
this.setState({ shouldShowPanel: false });
|
this.setState({ shouldShowPanel: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleMouseUp = async e => {
|
|
||||||
await waitTime(0);
|
|
||||||
const isLeftClick = e.button === 0;
|
|
||||||
const isInPasswordField = e.target.tagName === "INPUT" && e.target.type === "password";
|
|
||||||
const isInThisElement = document.querySelector("#simple-translate").contains(e.target);
|
|
||||||
if (!isLeftClick) return;
|
|
||||||
if (isInPasswordField) return;
|
|
||||||
if (isInThisElement) return;
|
|
||||||
this.hideButton();
|
|
||||||
this.hidePanel();
|
|
||||||
|
|
||||||
this.selectedText = getSelectedText();
|
|
||||||
this.selectedPosition = getSelectedPosition();
|
|
||||||
const clickedPosition = { x: e.clientX, y: e.clientY };
|
|
||||||
|
|
||||||
if (this.selectedText.length === 0) return;
|
|
||||||
this.handleTextSelect(clickedPosition);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleTextSelect = async clickedPosition => {
|
|
||||||
const onSelectBehavior = getSettings("whenSelectText");
|
|
||||||
if (onSelectBehavior === "dontShowButton") return;
|
|
||||||
|
|
||||||
if (getSettings("ifCheckLang")) {
|
|
||||||
const matchesLang = await matchesTargetLang(this.selectedText);
|
|
||||||
if (matchesLang) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onSelectBehavior === "showButton") {
|
|
||||||
this.showButton(clickedPosition);
|
|
||||||
} else if (onSelectBehavior === "showPanel") {
|
|
||||||
this.showPanel(clickedPosition);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
document.removeEventListener("mouseup", this.handleMouseUp);
|
|
||||||
document.removeEventListener("keydown", this.handleKeyDown);
|
|
||||||
browser.storage.onChanged.removeListener(handleSettingsChange);
|
|
||||||
browser.runtime.onMessage.removeListener(this.handleMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
render = () => {
|
render = () => {
|
||||||
if (!this.state.isInit) return null;
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<TranslateButton
|
<TranslateButton
|
||||||
|
|
|
@ -1,8 +1,91 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import browser from "webextension-polyfill";
|
import browser from "webextension-polyfill";
|
||||||
|
import { initSettings, getSettings, handleSettingsChange } from "src/settings/settings";
|
||||||
|
import { updateLogLevel, overWriteLogLevel } from "src/common/log";
|
||||||
import TranslateContainer from "./components/TranslateContainer";
|
import TranslateContainer from "./components/TranslateContainer";
|
||||||
|
|
||||||
|
const init = async () => {
|
||||||
|
await initSettings();
|
||||||
|
document.addEventListener("mouseup", handleMouseUp);
|
||||||
|
document.addEventListener("keydown", handleKeyDown);
|
||||||
|
browser.storage.onChanged.addListener(handleSettingsChange);
|
||||||
|
browser.runtime.onMessage.addListener(handleMessage);
|
||||||
|
overWriteLogLevel();
|
||||||
|
updateLogLevel();
|
||||||
|
disableExtensionByUrlList();
|
||||||
|
};
|
||||||
|
init();
|
||||||
|
|
||||||
|
const handleMouseUp = async e => {
|
||||||
|
await waitTime(10);
|
||||||
|
const isLeftClick = e.button === 0;
|
||||||
|
const isInPasswordField = e.target.tagName === "INPUT" && e.target.type === "password";
|
||||||
|
const isInThisElement =
|
||||||
|
document.querySelector("#simple-translate") &&
|
||||||
|
document.querySelector("#simple-translate").contains(e.target);
|
||||||
|
if (!isLeftClick) return;
|
||||||
|
if (isInPasswordField) return;
|
||||||
|
if (isInThisElement) return;
|
||||||
|
removeTranslatecontainer();
|
||||||
|
|
||||||
|
const selectedText = getSelectedText();
|
||||||
|
if (selectedText.length === 0) return;
|
||||||
|
|
||||||
|
const clickedPosition = { x: e.clientX, y: e.clientY };
|
||||||
|
const selectedPosition = getSelectedPosition();
|
||||||
|
showTranslateContainer(selectedText, selectedPosition, clickedPosition);
|
||||||
|
};
|
||||||
|
|
||||||
|
const waitTime = time => {
|
||||||
|
return new Promise(resolve => setTimeout(() => resolve(), time));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSelectedText = () => {
|
||||||
|
const element = document.activeElement;
|
||||||
|
const isInTextField = element.tagName === "INPUT" || element.tagName === "TEXTAREA";
|
||||||
|
const selectedText = isInTextField
|
||||||
|
? element.value.substring(element.selectionStart, element.selectionEnd)
|
||||||
|
: window.getSelection().toString();
|
||||||
|
return selectedText;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSelectedPosition = () => {
|
||||||
|
const element = document.activeElement;
|
||||||
|
const isInTextField = element.tagName === "INPUT" || element.tagName === "TEXTAREA";
|
||||||
|
const selectedRect = isInTextField
|
||||||
|
? element.getBoundingClientRect()
|
||||||
|
: window
|
||||||
|
.getSelection()
|
||||||
|
.getRangeAt(0)
|
||||||
|
.getBoundingClientRect();
|
||||||
|
|
||||||
|
let selectedPosition;
|
||||||
|
const panelReferencePoint = getSettings("panelReferencePoint");
|
||||||
|
switch (panelReferencePoint) {
|
||||||
|
case "topSelectedText":
|
||||||
|
selectedPosition = {
|
||||||
|
x: selectedRect.left + selectedRect.width / 2,
|
||||||
|
y: selectedRect.top
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case "bottomSelectedText":
|
||||||
|
default:
|
||||||
|
selectedPosition = {
|
||||||
|
x: selectedRect.left + selectedRect.width / 2,
|
||||||
|
y: selectedRect.bottom
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return selectedPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = e => {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
removeTranslatecontainer();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let isEnabled = true;
|
let isEnabled = true;
|
||||||
const handleMessage = async request => {
|
const handleMessage = async request => {
|
||||||
const empty = new Promise(resolve => {
|
const empty = new Promise(resolve => {
|
||||||
|
@ -12,45 +95,87 @@ const handleMessage = async request => {
|
||||||
});
|
});
|
||||||
|
|
||||||
switch (request.message) {
|
switch (request.message) {
|
||||||
|
case "getTabUrl":
|
||||||
|
if (!isEnabled) return empty;
|
||||||
|
if (window == window.parent) return location.href;
|
||||||
|
else return empty;
|
||||||
|
case "getSelectedText": {
|
||||||
|
if (!isEnabled) return empty;
|
||||||
|
const selectedText = getSelectedText();
|
||||||
|
if (selectedText.length === 0) return empty;
|
||||||
|
else return selectedText;
|
||||||
|
}
|
||||||
|
case "translateSelectedText": {
|
||||||
|
if (!isEnabled) return empty;
|
||||||
|
const selectedText = getSelectedText();
|
||||||
|
if (selectedText.length === 0) return;
|
||||||
|
const selectedPosition = getSelectedPosition();
|
||||||
|
removeTranslatecontainer();
|
||||||
|
showTranslateContainer(selectedText, selectedPosition, null, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "getEnabled":
|
case "getEnabled":
|
||||||
return isEnabled;
|
return isEnabled;
|
||||||
case "enableExtension":
|
case "enableExtension":
|
||||||
insertElement();
|
isEnabled = true;
|
||||||
break;
|
break;
|
||||||
case "disableExtension":
|
case "disableExtension":
|
||||||
removeElement();
|
removeTranslatecontainer();
|
||||||
|
isEnabled = false;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return empty;
|
return empty;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
browser.runtime.onMessage.addListener(handleMessage);
|
|
||||||
|
|
||||||
const removeElement = () => {
|
const disableExtensionByUrlList = () => {
|
||||||
|
const disableUrls = getSettings("disableUrlList").split("\n");
|
||||||
|
let pageUrl;
|
||||||
|
try {
|
||||||
|
pageUrl = top.location.href;
|
||||||
|
} catch (e) {
|
||||||
|
pageUrl = document.referrer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchesPageUrl = urlPattern => {
|
||||||
|
const pattern = urlPattern
|
||||||
|
.trim()
|
||||||
|
.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, match => (match === "*" ? ".*" : "\\" + match));
|
||||||
|
if (pattern === "") return false;
|
||||||
|
return RegExp("^" + pattern + "$").test(pageUrl);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isMatched = disableUrls.some(matchesPageUrl);
|
||||||
|
if (isMatched) isEnabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeTranslatecontainer = async () => {
|
||||||
const element = document.getElementById("simple-translate");
|
const element = document.getElementById("simple-translate");
|
||||||
if (!element) return;
|
if (!element) return;
|
||||||
|
|
||||||
ReactDOM.unmountComponentAtNode(element);
|
ReactDOM.unmountComponentAtNode(element);
|
||||||
element.parentNode.removeChild(element);
|
element.parentNode.removeChild(element);
|
||||||
isEnabled = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let isFirst = true;
|
const showTranslateContainer = (
|
||||||
const insertElement = () => {
|
selectedText,
|
||||||
|
selectedPosition,
|
||||||
|
clickedPosition = null,
|
||||||
|
shouldTranslate = false
|
||||||
|
) => {
|
||||||
const element = document.getElementById("simple-translate");
|
const element = document.getElementById("simple-translate");
|
||||||
if (element) return;
|
if (element) return;
|
||||||
|
if (!isEnabled) return;
|
||||||
|
|
||||||
document.body.insertAdjacentHTML("beforeend", "<div id='simple-translate'></div>");
|
document.body.insertAdjacentHTML("beforeend", "<div id='simple-translate'></div>");
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<TranslateContainer
|
<TranslateContainer
|
||||||
removeElement={removeElement}
|
removeContainer={removeTranslatecontainer}
|
||||||
insertElement={insertElement}
|
selectedText={selectedText}
|
||||||
isFirst={isFirst}
|
selectedPosition={selectedPosition}
|
||||||
|
clickedPosition={clickedPosition}
|
||||||
|
shouldTranslate={shouldTranslate}
|
||||||
/>,
|
/>,
|
||||||
document.getElementById("simple-translate")
|
document.getElementById("simple-translate")
|
||||||
);
|
);
|
||||||
isFirst = false;
|
|
||||||
isEnabled = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
insertElement();
|
|
||||||
|
|
Loading…
Reference in a new issue