Skip to content

Commit efa5486

Browse files
committed
fix #117
use window.postMessage to communicate with DeepL-Tab/Window
1 parent bdd2349 commit efa5486

3 files changed

Lines changed: 177 additions & 58 deletions

File tree

_buildscripts/build.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def readtext(filename):
8484
return filename.read_text(encoding='utf-8-sig')
8585

8686
def fill_meta(source, script_name):
87-
allowed_multiple = ['resource'] # Definierte Schlüssel, die mehrfach vorkommen dürfen
87+
allowed_multiple = ['resource', 'match'] # Definierte Schlüssel, die mehrfach vorkommen dürfen
8888
# grant muss man dann nochmal extra machen (none)
8989

9090
meta = ['// ==UserScript==']

eslint.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export default [
5252
"yoda": ["error", "always", { "onlyEquality": true }],
5353

5454
// keine Warnung für die init() Funktion
55-
"no-unused-vars": ["error", { "varsIgnorePattern": "init" }],
55+
"no-unused-vars": ["error", { "varsIgnorePattern": "init|ignored" }],
5656

5757
// Layout
5858
"@stylistic/js/array-bracket-newline": ["warn", "consistent"],

wfes-AddTranslationButtons.js

Lines changed: 175 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
// @name Add Translation Buttons
2-
// @version 2.1.6
2+
// @version 2.2.0
33
// @description Adds a button to translate the text associated with a wayspot
44
// @author AlterTobi
5+
// @match https://wayfarer.nianticlabs.com/*
6+
// @match https://www.deepl.com/*
57

68
(function() {
79
"use strict";
810

11+
const ORIGIN_WAYFARER = "https://wayfarer.nianticlabs.com";
12+
const ORIGIN_DEEPL = "https://www.deepl.com";
13+
const ORIGIN_GOOGLE = "https://translate.google.com";
14+
15+
// ----- BEGIN - the Wayfarer part ------
916
const myCSSId = "wfesTranslateCSS";
1017
const myStyle = `.wfesTranslate {
1118
color: #333;
@@ -24,83 +31,123 @@
2431
display: block; /* Button wird unterhalb des Selects angezeigt */
2532
text-decoration: none;
2633
color: #20B8E3;
34+
margin: 0 auto;
2735
}
2836
.dark .wfesTranslate select,
2937
.dark .wfesTranslate option {
3038
background: #000;
3139
}`;
3240

3341
const engines ={
34-
Google: {name: "Google", title: "Google translate", url: "https://translate.google.com/?sl=auto&q=", target: "wfesTranslateGoogle"},
35-
Deepl: {name: "Deepl", title: "DeepL translate", url: "https://www.deepl.com/translator#auto/"+navigator.language+"/", target: "wfesTranslateDeepl"}
42+
Google: {name: "Google", title: "Google translate", url: "https://translate.google.com/?sl=auto&q=", target: "wfesTranslateGoogle", twindow: null, origin: ORIGIN_GOOGLE},
43+
Deepl: {name: "Deepl", title: "DeepL translate", url: "https://www.deepl.com/translator#auto/"+navigator.language+"/", target: "wfesTranslateDeepl", twindow: null, origin: ORIGIN_DEEPL}
3644
};
3745

3846
const buttonID = "wfesTranslateButton";
3947
const storageName = "wfes_translateEngine";
4048
let currentEngine;
4149

42-
4350
function removeButton() {
4451
const button = document.getElementById(buttonID);
4552
if (button !== null) {
4653
button.remove();
4754
}
4855
}
4956

50-
function init() {
51-
window.wfes.f.addCSS(myCSSId, myStyle);
52-
window.wfes.f.localGet(storageName, "Deepl").then(e => {
53-
currentEngine = e;
54-
});
57+
function sendTextToTranslateWindow(fenster, text, origin) {
58+
try {
59+
fenster.postMessage({
60+
type: "translate",
61+
payload: text
62+
}, origin); // "*" = alle Ursprünge erlauben (für Google Translate nötig)
63+
} catch (e) {
64+
console.warn("Nachricht konnte nicht gesendet werden:", e.message);
65+
}
5566
}
5667

57-
function createButton(text) {
58-
window.wfes.f.waitForElem("wf-logo").then(elem=>{
59-
const buttonEl = document.getElementById(buttonID);
60-
if (null === buttonEl) {
61-
const div = document.createElement("div");
62-
div.className = "wfesTranslate";
63-
div.id = buttonID;
64-
const link = document.createElement("a");
65-
link.title = "Translate nomination";
66-
link.className = "wfesTranslateButton";
67-
link.innerHTML = '<span class="material-icons">translate</span>';
68-
69-
const select = document.createElement("select");
70-
select.title = "Select translation engine";
71-
72-
for (const engineName of Object.keys(engines)) {
73-
const engine = engines[engineName];
74-
const option = document.createElement("option");
75-
option.value = engine.name;
76-
77-
if (engine.name === currentEngine) {
78-
option.setAttribute("selected", "true");
79-
link.target = engine.target;
80-
link.href = engine.url + encodeURIComponent(text);
81-
}
82-
option.innerText = engine.title;
83-
select.appendChild(option);
68+
function onTranslateButtonClick(text) {
69+
const engine = engines[currentEngine];
70+
let url;
71+
const target = engine.target;
72+
73+
// Google und Deepl unterscheiden
74+
switch(currentEngine) {
75+
case "Google":
76+
url = engine.url + encodeURIComponent(text);
77+
// fenster per Link öffnen
78+
if (engine.twindow && !engine.twindow.closed) {
79+
engine.twindow.location.href = url;
80+
} else {
81+
engine.twindow = window.open(url, target); // neues Tab/Fenster
8482
}
83+
break;
84+
case "Deepl":
85+
url = engine.url;
86+
// fenster öffnen und nachricht senden
87+
if (engine.twindow && !engine.twindow.closed) {
88+
engine.twindow.focus();
89+
sendTextToTranslateWindow(engine.twindow, text, engine.origin);
90+
} else {
91+
window.addEventListener("message", (event) => {
92+
const dtype = engine.name + "-ready";
93+
if (dtype === event.data?.type && engine.twindow && !engine.twindow.closed) {
94+
sendTextToTranslateWindow(engine.twindow, text, engine.origin);
95+
}
96+
});
97+
engine.twindow = window.open(url, target); // öffne neues Tab/Fenster
98+
}
99+
break;
100+
default:
101+
console.warn("unbekannte Engine:", currentEngine, "not handeled");
102+
}
103+
}
85104

86-
select.addEventListener("change", function() {
87-
currentEngine = select.value;
88-
window.wfes.f.localSave(storageName, currentEngine);
89-
link.href = engines[currentEngine].url + encodeURIComponent(text);
90-
link.target = engines[currentEngine].target;
91-
});
92-
div.appendChild(select);
93-
div.appendChild(link);
94-
const container = elem.parentNode.parentNode;
95-
container.appendChild(div);
96-
} else {
97-
const a = buttonEl.querySelector("a");
98-
a.href = engines[currentEngine].url + encodeURIComponent(text);
99-
a.target = engines[currentEngine].url;
105+
function createButton(text) {
106+
window.wfes.f.waitForElem("wf-logo").then(elem => {
107+
// remove if exist
108+
removeButton();
109+
const div = document.createElement("div");
110+
div.className = "wfesTranslate";
111+
div.id = buttonID;
112+
113+
const select = document.createElement("select");
114+
select.title = "Select translation engine";
115+
116+
for (const engineName of Object.keys(engines)) {
117+
const engine = engines[engineName];
118+
const option = document.createElement("option");
119+
option.value = engine.name;
100120

121+
if (engine.name === currentEngine) {
122+
option.setAttribute("selected", "true");
123+
}
124+
125+
option.innerText = engine.title;
126+
select.appendChild(option);
101127
}
128+
129+
select.addEventListener("change", function() {
130+
currentEngine = select.value;
131+
window.wfes.f.localSave(storageName, currentEngine);
132+
});
133+
134+
const button = document.createElement("button");
135+
button.title = "Translate nomination";
136+
button.className = "wfesTranslateButton";
137+
button.innerHTML = '<span class="material-icons">translate</span>';
138+
button.addEventListener("click", function() {
139+
onTranslateButtonClick(text);
140+
});
141+
142+
div.appendChild(select);
143+
div.appendChild(button);
144+
145+
const container = elem.parentNode.parentNode;
146+
container.appendChild(div);
102147
})
103-
.catch((e) => {console.warn(GM_info.script.name, ": ", e);});
148+
.catch((e) => {
149+
console.warn(GM_info.script.name, ": ", e);
150+
});
104151
}
105152

106153
function addTranslationButtonsNew() {
@@ -162,11 +209,83 @@
162209
createButton(allText);
163210
}
164211

165-
init();
166-
window.addEventListener("WFESReviewPageNewLoaded", addTranslationButtonsNew);
167-
window.addEventListener("WFESReviewPageEditLoaded", addTranslationButtonsEdit);
168-
window.addEventListener("WFESReviewPagePhotoLoaded", addTranslationButtonsPhoto);
169-
window.addEventListener("WFESReviewDecisionSent", removeButton);
212+
function initWF() {
213+
window.addEventListener("WFESReviewPageNewLoaded", addTranslationButtonsNew);
214+
window.addEventListener("WFESReviewPageEditLoaded", addTranslationButtonsEdit);
215+
window.addEventListener("WFESReviewPagePhotoLoaded", addTranslationButtonsPhoto);
216+
window.addEventListener("WFESReviewDecisionSent", removeButton);
217+
218+
window.wfes.f.addCSS(myCSSId, myStyle);
219+
window.wfes.f.localGet(storageName, "Deepl").then(e => {
220+
currentEngine = e;
221+
});
222+
}
223+
// ----- END - the Wayfarer part ------
224+
225+
// common functions (can't use wfes functions in translation windows)
226+
function waitForElem(selector, maxWaitTime = 5000) {
227+
const startTime = Date.now();
228+
return new Promise((resolve, reject) => {
229+
const checkForElement = () => {
230+
const elem = document.querySelector(selector);
231+
if (elem) {
232+
resolve(elem);
233+
} else if (Date.now() - startTime >= maxWaitTime) {
234+
reject(new Error(`Timeout waiting for element with selector ${selector} after ${maxWaitTime/1000} seconds`));
235+
} else {
236+
setTimeout(checkForElement, 200);
237+
}
238+
};
239+
checkForElement();
240+
});
241+
}
242+
243+
// ----- BEGIN - the Deepl part ------
244+
const deeplInputArea = 'd-textarea[name="source"] div[contenteditable="true"]';
245+
246+
function initD() {
247+
waitForElem(deeplInputArea)
248+
.then( () => {
249+
console.log("readyCheck -> post");
250+
window.opener?.postMessage({ type: "Deepl-ready" }, ORIGIN_WAYFARER);
251+
})
252+
.catch((e) => {console.warn(GM_info.script.name, ": ", e);});
253+
254+
function setDeepLText(text) {
255+
waitForElem(deeplInputArea)
256+
.then( elem => {
257+
elem.innerHTML = `<p>${text.replace(/\n/g, "<br>")}</p>`;
258+
// DeepL über Änderungen informieren
259+
elem.dispatchEvent(new InputEvent("input", { bubbles: true }));
260+
})
261+
.catch((e) => {console.warn(GM_info.script.name, ": ", e);});
262+
}
263+
264+
window.addEventListener("message", (event) => {
265+
const msg = event.data;
266+
267+
if ("translate" === msg?.type && "string" === typeof msg.payload) {
268+
console.log("DeepL: Text erhalten:", msg.payload);
269+
setDeepLText(msg.payload);
270+
}
271+
});
272+
}
273+
// ----- END - the Deepl part ------
274+
275+
// ----- BEGIN - general instructions ------
276+
277+
switch(window.origin) {
278+
case ORIGIN_WAYFARER:
279+
console.log("Init Script loading:", GM_info.script.name, " - Wayfarer");
280+
initWF();
281+
break;
282+
case ORIGIN_DEEPL:
283+
console.log("Init Script loading:", GM_info.script.name, " - Deepl");
284+
initD();
285+
break;
286+
default:
287+
console.warn("unknown origin".window.origin, "not handled");
288+
}
170289

171290
/* we are done :-) */
172291
console.log("Script loaded:", GM_info.script.name, "v" + GM_info.script.version);

0 commit comments

Comments
 (0)