I'm working on a translate chrome extension. I want to get the user selection and translate it after the user click on a context menu voice. I've implemented this code but not work as expected. I've tested it on instagram and seems working into the DM input field. The same code will not work as expected with facebook text input for messages or status textarea. How I can manage the selected text replacing after that it's translated?
background.js
const processSelection = (info, tab) => {
axios({
method: 'GET',
url: 'https://translate.googleapis.com/translate_a/single',
params: {
client: 'gtx',
sl: 'auto',
tl: 'en',
dt: 't',
q: info.selectionText
}
}).then( (res) => {
browser.tabs.sendMessage(tab.id, {translation: res.data[0][0][0]});
});
}
browser.contextMenus.create({
id: 'selection-translate',
title: 'Translate selection',
contexts: ['editable','selection']
});
browser.contextMenus.onClicked.addListener(processSelection);
content-script.js
browser.runtime.onMessage.addListener( (message) => {
console.log(message);
const selectedText = window.getSelection();
const range = selectedText.getRangeAt(0);
console.log(range);
// I've tried to use deleteContents() but without selecting the node it will not work
range.selectNodeContents(range.commonAncestorContainer);
range.deleteContents();
// sometimes the text is appended outside the text input/textarea if selectNodeContents isnt used
range.insertNode(document.createTextNode(message.translation));
});
Another doubt I have is about context menu. If I want to add some submenu to the main one, how I will handle the click event?I want to give the user the ability to select the destination language with other context menus, but not sure how to proceed.
Thank you for the help.
Related
Using Node Webkit, is it possible to create save as with multiple option of the file format?
Desired result is something like this :
With native NW at most I can only display two options. The first option is the pattern written in the 'accept' attribute, and an auto generated "All files (.)" Option.
What I need is more than two options in the Save As Type field.
Is there a way to achieve this?
What you will have to do is to give different 'description' and 'accept' of each option you want to show.
This way the saveAs dialog will show you the options and you can save your file with that source.
You can use showSaveFilePicker.
async function saveFile () {
const result = await window.showSaveFilePicker({
types: [
{
description: 'Web Page, complete(*.htm;*.html)',
accept: {
'file/*':['.html']
}
},
{
description: 'Web Page, complete(*.htm;*.html)',
accept: {
'file/*': ['.html'],
}
},
{
description: 'Web Page, HTML only(*.htm,*.html)',
accept: {
'file/*': ['.html'],
}
},
{
description: 'Text File (*.text,*.text) HTML only(*.htm,*.html)',
accept: {
'file/*': ['.txt'],
}
}
]
});
return result;
}
I come from a low-level programming background, so JS and NodeJS are a new realm for me.
I am trying to create an application that begins by displaying a CLI menu to the user. Upon the user selecting a menu option, a corresponding functionality will be carried out. Once that functionality completes, I want the menu to be re-displayed.
A very simple way of handling this in Python and embedded C is to enclose the menu in a while(1) loop and then terminate the program/script process when the user selects the corresponding menu option. However, in NodeJS, you cannot run a menu in a while(1) loop -- the functions called corresponding to each menu option never actually get called and the menu simply re-displays immediately.
In other words, what is the NodeJS equivalent of:
while(1) {
displayMenuToUser();
// Wait for user to select which menu option they want
if (quitMenuOptionSelectedByUser) {
terminateProcess();
} else {
executeFunctionCorrespondingToTheSelectedMenuOption();
// At this point the menu should be re-displayed so the user can select another option
}
}
You can use Inquirer.js
I made this example which keeps looping if you answer yes on the Go again? question:
var inquirer = require('inquirer');
const showMenu = () => {
inquirer
.prompt([{
name: 'age',
type: 'input',
message: 'What\'s your age?',
}, {
name: 'country',
type: 'list',
message: 'Where do you live?',
choices: ['USA', 'China', 'Germany', 'France'],
}, {
name: 'back',
type: 'input',
message: 'Go again?',
choices: ['yes', 'no'],
}]
).then((answers) => {
console.log(`\nMy age is ${answers.age} and I live in ${answers.country}.\n`);
if (answers.back === 'yes') {
return showMenu();
}
})
.catch((err) => {
console.log(err);
});
}
showMenu();
I'm using npm pacakge to open mail client with data:
https://www.npmjs.com/package/react-native-mail-compose
Also, I'm using their example:
import MailCompose from 'react-native-mail-compose';
// later in your code...
async sendMail() {
try {
await MailCompose.send({
toRecipients: ['to1#example.com', 'to2#example.com'],
ccRecipients: ['cc1#example.com'],
bccRecipients: ['bcc1#example.com', 'bcc2#example.com'],
subject: 'This is subject',
text: 'This is body',
html: '<p>This is <b>html</b> body</p>', // Or, use this if you want html body. Note that some Android mail clients / devices don't support this properly.
attachments: [{
filename: 'mytext', // [Optional] If not provided, UUID will be generated.
ext: '.txt',
mimeType: 'text/plain',
text: 'Hello my friend', // Use this if the data is in UTF8 text.
data: '...BASE64_ENCODED_STRING...', // Or, use this if the data is not in plain text.
}],
});
} catch (e) {
// e.code may be 'cannotSendMail' || 'cancelled' || 'saved' || 'failed'
}
}
and call this function on button press. All data is OK, except body, for example here in Subject there is "This is subject", in CC of mail clients, there is "cc1#example.com", but body is always empty, I can't ever see "This is body" in mail client.
I've fixed it with another package react-native-send-intent.
It works like a charm! :)
I see, give this package a try.
https://github.com/anarchicknight/react-native-communications
Its very simple to use
In my code, I wish to be able to call the function notify each time with a different link, and the specific notification created opens a new tab with that page when clicked
Note: I'm very confused by the documentation, please don't just link me to that, I really want an explanation with an example. Thanks
This is what I have so far:
function notify(title, msg, link, callback) {
//no idea what to do with the link here
var options = {
title: title,
message: msg,
type: "basic",
iconUrl: "Icon.png"
};
return chrome.notifications.create("", options, callback);
}
And to call the function:
notify("title", "message", "http://www.example.com", function(notification) { });
which would display
Title
Message
and upon clicking, would open a new tab to http://www.example.com
then later, I would call it again with
notify("title", "message", "http://www.google.com", function(notification) { });
and that one would open a new tab to http://www.google.com when clicked
Thanks if you can help!
There is no direct support for attaching an onclick handler to a notification. Instead, if you click a notification's body, it will generate a general chrome.notification.onClicked event, providing the ID of the notification.
You will have, therefore, to maintain your own matching of IDs to URLs.
var urls = {};
chrome.notifications.onClicked.addListener(notifyClick);
chrome.notifications.onClosed.addListener(notifyClose);
function notify(title, msg, link, callback) {
var options = {
title: title,
message: msg,
type: "basic",
iconUrl: "Icon.png",
isClickable: true // New in Chrome 32, alters the appearance?
};
return chrome.notifications.create("", options, function(id) {
// New ID will be generated
urls[id] = link;
});
}
function notifyClick(id) {
// Look up the URL:
chrome.tabs.create({url: urls[id]});
// Optionally, close the notification:
// chrome.notifications.clear(id, function(){});
}
function notifyClose(id, byUser) {
// Clean up the matching
delete urls[id];
}
Please note: there is a caveat regarding closing notification programmatically. If the notification is closed from the message center, it will not immediately disappear.
I am trying to create an option in the right-click menu that is dynamic based on the user's action. If the user selects some text, then right-clicks, the option will say "Display It". If the user right-clicks without selecting some text, the option will say "Select Some Text First" and be grayed out. I am wondering how do I achieve this?
I currently have it so that the option will appear only when the user has selected some text. I am unsure how to modify it to meet my second requirements.
chrome.contextMenus.create ({
title:"Display It!", contexts:["selection"], onclick:function(info,tab) {
chrome.tabs.sendRequest(
tab.id,
{callFunction: "displaySidebar", info: info},
function(response) {console.log(response);}
);
}
});
You cant grey an item out...Chrome has gone to a bit of effort to only make context menu items appear when its relevant which is why i guess theres no grey out option. Your way goes against what Chrome have tried to implement and I think you really should rethink the way you go about this.
Saying that, you can use the chrome.contextMenus.update to change a menu item.
The following code is about as good as your going to get it your way (seriously, rethink this idea)....
function selectedTrueOnClick(info, tab) {
chrome.tabs.sendRequest(
tab.id, {
callFunction: "displaySidebar",
info: info
}, function(response) {
console.log(response);
});
}
function selectedFalseOnClick(info, tab) {
//
}
var contextMenuID = chrome.contextMenus.create({
title: "Select some text",
contexts: ["all"],
onclick: selectedFalseOnClick
});
function contextMenuUpdate(selected) {
if (selected) chrome.contextMenus.update(contextMenuID, {
title: 'You selected "%s"',
contexts: ["all"],
onclick: selectedTrueOnClick
});
else chrome.contextMenus.update(contextMenuID, {
title: "Select some text",
contexts: ["all"],
onclick: selectedTrueOnClick
});
}
contextMenuUpdate(false);
I was looking to accomplish the same thing as the original post, and was able to get it working using some message passing. Regardless of whether it's bad practice or not, I used the enabled(true/false) contextMenu property to leave my context menu option present, but grayed out.
I created a context menu. The important property is the id. The rest is mostly arbitrary because it will be changed dynamically.
In content.js
//This event listener will determine if the context menu should be updated
//based on if the right-button was clicked and if there is a selection or not
document.addEventListener("mousedown", function(event){
if (event.button !== 2) {
return false;
}
var selected = window.getSelection().toString();
if(event.button == 2 && selected != '') {
//get selected text and send request to bkgd page to create menu
chrome.runtime.sendMessage({
'message': 'updateContextMenu',
'selection': true});
} else {
chrome.runtime.sendMessage({
'message': 'updateContextMenu',
'selection': false});
}
}, true);
In background.js:
//add a message listener that will modify the context menu however you see fit
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.message == 'updateContextMenu') {
if (request.selection) {
chrome.contextMenus.update('contextMenuId',{
'title': 'New Title',
'enabled': true,
"contexts": ["all"],
'onclick': someFunction
});
} else {
chrome.contextMenus.update('contextMenuId',{
'title': 'Select some text first',
'enabled': false,
"contexts": ["all"]
});
}
} else {
sendResponse({});
}
});
//The original context menu. The important property is the id. The rest is mostly
//arbitrary because it will be changed dynamically by the listener above.
chrome.contextMenus.create({
'id': 'contextMenuId',
'enabled': false,
'title': 'Some Title',
"contexts": ["all"]
});