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 browser from "webextension-polyfill";
|
||||
import translate from "src/common/translate";
|
||||
import { initSettings, getSettings, handleSettingsChange } from "src/settings/settings";
|
||||
import { updateLogLevel, overWriteLogLevel } from "src/common/log";
|
||||
import { getSettings } from "src/settings/settings";
|
||||
import TranslateButton from "./TranslateButton";
|
||||
import TranslatePanel from "./TranslatePanel";
|
||||
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 result = await translate(text, "auto", targetLang);
|
||||
return result;
|
||||
|
@ -71,15 +31,10 @@ const matchesTargetLang = async selectedText => {
|
|||
return matchsLangs;
|
||||
};
|
||||
|
||||
const waitTime = time => {
|
||||
return new Promise(resolve => setTimeout(() => resolve(), time));
|
||||
};
|
||||
|
||||
export default class TranslateContainer extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isInit: false,
|
||||
shouldShowButton: false,
|
||||
buttonPosition: { x: 0, y: 0 },
|
||||
shouldShowPanel: false,
|
||||
|
@ -88,75 +43,26 @@ export default class TranslateContainer extends Component {
|
|||
candidateText: "",
|
||||
statusText: "OK"
|
||||
};
|
||||
this.selectedText = "";
|
||||
this.selectedPosition = { x: 0, y: 0 };
|
||||
this.init();
|
||||
this.selectedText = props.selectedText;
|
||||
this.selectedPosition = props.selectedPosition;
|
||||
}
|
||||
|
||||
init = async () => {
|
||||
await initSettings();
|
||||
this.setState({ isInit: true });
|
||||
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();
|
||||
componentDidMount = () => {
|
||||
if (this.props.shouldTranslate) this.showPanel();
|
||||
else this.handleTextSelect(this.props.clickedPosition);
|
||||
};
|
||||
|
||||
disableExtensionByUrlList = () => {
|
||||
const disableUrls = getSettings("disableUrlList").split("\n");
|
||||
let pageUrl;
|
||||
try {
|
||||
pageUrl = top.location.href;
|
||||
} catch (e) {
|
||||
pageUrl = document.referrer;
|
||||
handleTextSelect = async clickedPosition => {
|
||||
const onSelectBehavior = getSettings("whenSelectText");
|
||||
if (onSelectBehavior === "dontShowButton") return this.props.removeContainer();
|
||||
|
||||
if (getSettings("ifCheckLang")) {
|
||||
const matchesLang = await matchesTargetLang(this.selectedText);
|
||||
if (matchesLang) return this.props.removeContainer();
|
||||
}
|
||||
|
||||
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) 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();
|
||||
}
|
||||
if (onSelectBehavior === "showButton") this.showButton(clickedPosition);
|
||||
else if (onSelectBehavior === "showPanel") this.showPanel(clickedPosition);
|
||||
};
|
||||
|
||||
showButton = clickedPosition => {
|
||||
|
@ -200,50 +106,7 @@ export default class TranslateContainer extends Component {
|
|||
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 = () => {
|
||||
if (!this.state.isInit) return null;
|
||||
return (
|
||||
<div>
|
||||
<TranslateButton
|
||||
|
|
|
@ -1,8 +1,91 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
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";
|
||||
|
||||
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;
|
||||
const handleMessage = async request => {
|
||||
const empty = new Promise(resolve => {
|
||||
|
@ -12,45 +95,87 @@ const handleMessage = async request => {
|
|||
});
|
||||
|
||||
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":
|
||||
return isEnabled;
|
||||
case "enableExtension":
|
||||
insertElement();
|
||||
isEnabled = true;
|
||||
break;
|
||||
case "disableExtension":
|
||||
removeElement();
|
||||
removeTranslatecontainer();
|
||||
isEnabled = false;
|
||||
break;
|
||||
default:
|
||||
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");
|
||||
if (!element) return;
|
||||
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
element.parentNode.removeChild(element);
|
||||
isEnabled = false;
|
||||
};
|
||||
|
||||
let isFirst = true;
|
||||
const insertElement = () => {
|
||||
const showTranslateContainer = (
|
||||
selectedText,
|
||||
selectedPosition,
|
||||
clickedPosition = null,
|
||||
shouldTranslate = false
|
||||
) => {
|
||||
const element = document.getElementById("simple-translate");
|
||||
if (element) return;
|
||||
if (!isEnabled) return;
|
||||
|
||||
document.body.insertAdjacentHTML("beforeend", "<div id='simple-translate'></div>");
|
||||
ReactDOM.render(
|
||||
<TranslateContainer
|
||||
removeElement={removeElement}
|
||||
insertElement={insertElement}
|
||||
isFirst={isFirst}
|
||||
removeContainer={removeTranslatecontainer}
|
||||
selectedText={selectedText}
|
||||
selectedPosition={selectedPosition}
|
||||
clickedPosition={clickedPosition}
|
||||
shouldTranslate={shouldTranslate}
|
||||
/>,
|
||||
document.getElementById("simple-translate")
|
||||
);
|
||||
isFirst = false;
|
||||
isEnabled = true;
|
||||
};
|
||||
|
||||
insertElement();
|
||||
|
|
Loading…
Reference in a new issue