How to put selection text in chrome.contextMenus? - google-chrome-extension

How can I add selection text in context.Menus?
I want to create a Chrome extension which will work similarly to the right-click search function in Google Chrome (i.e. right click on selected text -> "Search 'selection text')
I made a preview
I assume this is something with chrome.contextMenus.update but i don't know how to make it work
background.js:
chrome.runtime.onInstalled.addListener(function () {
var context = "selection";
var title = "Search";
var id = chrome.contextMenus.create({
"title": title,
"contexts": [context],
"id": "context" + context
});
});
// add click event
chrome.contextMenus.onClicked.addListener(onClickHandler);
// The onClicked callback function.
function onClickHandler(info, tab) {
var sText = info.selectionText;
var url = "https://www.google.com/search?source=hp&q=" + encodeURIComponent(sText);
window.open(url, '_blank');
};

Related

Header filtering - create Select field with down arrow

I am using Tabulator 5.x. I have a table with header filtering. The column in question is the last column "Transcribed". Is there a way to have the typical down arrow on the right side of the select box that shows the end user it is a drop down list similar to if you were using option in html? Rather than having to click on it filter field to see the choices.
I looked in documentation but do not see any examples using a down arrow. I also looked in the CSS, but did not anything if indeed it was there.
var table = new Tabulator("#transcription-table", {
height:"640px",
layout:"fitDataStretch",
ajaxURL:"get_transcriptions.php",
columns:[
{title:"ID", field:"id", headerSort:false, visible:false},
{title:"Song Title", field:"songtitle", width:350, sorter:"string", headerFilter:"input"},
{title:"Artist / Group", field:"artistgroup", widthGrow:1.5 ,sorter:"string", headerFilter:"input"},
{title:"Transcribed", field:"transcribed", widthGrow:1.2, sorter:"string", headerTooltip:"Transcribed into music notation", editor:"select", editorParams:{values:{"Yes":"Yes", "No":"No"}}, headerFilter:true, headerFilterParams:{values:{"Yes":"Yes", "No":"No", "":""}}},
]
});
Thank you.
You can create your own editor by extending editor module as
Tabulator.extendModule("edit", "editors", {
selectwithdrop: function (cell, onRendered, success, cancel, editorParams) {
var cellValue = cell.getValue().toUpperCase(),
input = document.createElement("select");
Object.keys(editorParams.values).forEach((key) => {
let option = document.createElement("option");
option.text = editorParams.values[key];
option.value = key;
input.add(option);
});
input.style.padding = "10px";
input.style.width = "100%";
input.style.boxSizing = "border-box";
input.style.border = "1px solid #4b4b4b";
input.style.borderRadius = "5px";
input.style.outline = "none";
input.value = cellValue;
// onRendered(function () {
// input.focus();
// input.style.height = "100%";
// });
function onChange(e) {
success(input.value);
}
//submit new value on blur or change
input.addEventListener("change", onChange);
// input.addEventListener("blur", onChange);
//submit new value on enter
return input;
},
});
Working Demo CodeSandBox

Chrome extension for selected text which matches a specific form [duplicate]

I am trying to create entries on the Chrome context menu based on what is selected.
I found several questions about this on Stackoverflow, and for all of them the answer is: use a content script with a "mousedown" listener that looks at the current selection and creates the Context Menu.
I implemented this, but it does not always work. Sometimes all the log messages say that the context menu was modified as I wanted, but the context menu that appears is not updated.
Based on this I suspected it was a race condition: sometimes chrome starts rendering the context menu before the code ran completely.
I tried adding a eventListener to "contextmenu" and "mouseup". The later triggers when the user selects the text with the mouse, so it changes the contextmenu much before it appears (even seconds). Even with this technique, I still see the same error happening!
This happens very often in Chrome 22.0.1229.94 (Mac), occasionally in Chromium 20.0.1132.47 (linux) and it did not happen in 2 minutes trying on Windows (Chrome 22.0.1229.94).
What is happening exactly? How can I fix that? Is there any other workaround?
Here is a simplified version of my code (not so simple because I am keeping the log messages):
manifest.json:
{
"name": "Test",
"version": "0.1",
"permissions": ["contextMenus"],
"content_scripts": [{
"matches": ["http://*/*", "https://*/*"],
"js": ["content_script.js"]
}],
"background": {
"scripts": ["background.js"]
},
"manifest_version": 2
}
content_script.js
function loadContextMenu() {
var selection = window.getSelection().toString().trim();
chrome.extension.sendMessage({request: 'loadContextMenu', selection: selection}, function (response) {
console.log('sendMessage callback');
});
}
document.addEventListener('mousedown', function(event){
if (event.button == 2) {
loadContextMenu();
}
}, true);
background.js
function SelectionType(str) {
if (str.match("^[0-9]+$"))
return "number";
else if (str.match("^[a-z]+$"))
return "lowercase string";
else
return "other";
}
chrome.extension.onMessage.addListener(function(msg, sender, sendResponse) {
console.log("msg.request = " + msg.request);
if (msg.request == "loadContextMenu") {
var type = SelectionType(msg.selection);
console.log("selection = " + msg.selection + ", type = " + type);
if (type == "number" || type == "lowercase string") {
console.log("Creating context menu with title = " + type);
chrome.contextMenus.removeAll(function() {
console.log("contextMenus.removeAll callback");
chrome.contextMenus.create(
{"title": type,
"contexts": ["selection"],
"onclick": function(info, tab) {alert(1);}},
function() {
console.log("ContextMenu.create callback! Error? " + chrome.extension.lastError);});
});
} else {
console.log("Removing context menu")
chrome.contextMenus.removeAll(function() {
console.log("contextMenus.removeAll callback");
});
}
console.log("handling message 'loadContextMenu' done.");
}
sendResponse({});
});
The contextMenus API is used to define context menu entries. It does not need to be called right before a context menu is opened. So, instead of creating the entries on the contextmenu event, use the selectionchange event to continuously update the contextmenu entry.
I will show a simple example which just displays the selected text in the context menu entry, to show that the entries are synchronized well.
Use this content script:
document.addEventListener('selectionchange', function() {
var selection = window.getSelection().toString().trim();
chrome.runtime.sendMessage({
request: 'updateContextMenu',
selection: selection
});
});
At the background, we're going to create the contextmenu entry only once. After that, we update the contextmenu item (using the ID which we get from chrome.contextMenus.create).
When the selection is empty, we remove the context menu entry if needed.
// ID to manage the context menu entry
var cmid;
var cm_clickHandler = function(clickData, tab) {
alert('Selected ' + clickData.selectionText + ' in ' + tab.url);
};
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
if (msg.request === 'updateContextMenu') {
var type = msg.selection;
if (type == '') {
// Remove the context menu entry
if (cmid != null) {
chrome.contextMenus.remove(cmid);
cmid = null; // Invalidate entry now to avoid race conditions
} // else: No contextmenu ID, so nothing to remove
} else { // Add/update context menu entry
var options = {
title: type,
contexts: ['selection'],
onclick: cm_clickHandler
};
if (cmid != null) {
chrome.contextMenus.update(cmid, options);
} else {
// Create new menu, and remember the ID
cmid = chrome.contextMenus.create(options);
}
}
}
});
To keep this example simple, I assumed that there's only one context menu entry. If you want to support more entries, create an array or hash to store the IDs.
Tips
Optimization - To reduce the number of chrome.contextMenus API calls, cache the relevant values of the parameters. Then, use a simple === comparison to check whether the contextMenu item need to be created/updated.
Debugging - All chrome.contextMenus methods are asynchronous. To debug your code, pass a callback function to the .create, .remove or .update methods.
MDN doc for menus.create(), 'title' param
You can use "%s" in the string. If you do this in a menu item, and some text is selected in the page when the menu is shown, then the selected text will be interpolated into the title.
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/menus/create
Thus
browser.contextMenus.create({
id: 'menu-search',
title: "Search '%s'", // selected text as %s
contexts: ['selection'], // show only if selection exist
})

Chrome Extension context menu, differentiate image and link event

In my chrome extension I'm adding two context items "Get link" and "Get Image". The main difference being when setting them both up they have the "context" of link and image respectively. But when right clicking on an image that is acting as a link you get the option of both:
when either of those are clicked the data that comes into the listener seems to be identical, I need to be able to differentiate the two to know if the context is that of an image or a link to handle them differently. Here is my code:
chrome.runtime.onInstalled.addListener(function() {
var context = "image";
var title = "Copy Image";
var id = chrome.contextMenus.create({"title": title, "contexts":[context],
"id": "context" + context});
});
chrome.runtime.onInstalled.addListener(function() {
var context = "link";
var title = "Copy link";
var id = chrome.contextMenus.create({"title": title, "contexts":[context],
"id": "context" + context});
});
chrome.contextMenus.onClicked.addListener(onClickHandler);
function onClickHandler(info, tab) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id, {action: "imageAdded", subject: info.srcUrl}, function(response) {
});
If you want know which menu item was clicked, you can get the id value of the clicked context menu item in the menuItemId property of the object passed into the onClicked handler:
function onClickHandler(info, tab) {
console.log(info.menuItemId);
//...
}
Take a look at Parameter of onClicked callback, you could differentiate the image/link via mediaType
One of 'image', 'video', or 'audio' if the context menu was activated on one of these types of elements.

Open a new tab with a selected word in page

Is possible to create an extension for google chrome do: to select a word, click the right button and open a new tab using the word as part of a url?
Example of word: test
Go to page: http://www.example.com/test
You can simply use chrome.contextMenus.create and chrome.tabs.create in the background.js. I've created the code and it works with me.
function sendSearch(selectedText) {
var serviceCall = 'http://www.example.com/' + selectedText;
chrome.tabs.create({url: serviceCall});
}
chrome.contextMenus.create(
{
title: "Find '%s' on example.com!",
contexts:["selection"],
onclick: function(info, tab) {
sendSearch(info.selectionText);
}
});
Based on the Best practices when using event pages #Xan mentioned. You can use chrome.contextMenus.onClicked instead. Like:
function sendSearch(selectedText) {
var serviceCall = 'http://www.example.com/' + selectedText;
chrome.tabs.create({url: serviceCall});
}
chrome.contextMenus.create(
{
title: "Find '%s' on example.com!",
contexts:["selection"],
"id": "ViewSelectedLink"
});
function contextClicked(info, tab) {
if (info.menuItemId == "ViewSelectedLink" ) {
sendSearch(info.selectionText);
}
}
chrome.contextMenus.onClicked.addListener(contextClicked);

Multiple Tab Chrome Extension Issue

I've created a basic extension that searches Google if the URL/HTML content fulfill certain requirements. It works for the most part, but fails miserably when there are multiple instances of the extension. For example, if I load tab A and then tab B, but click on the page action for tab A, I will be directed to a search of tab B's content.
I don't know how to silo the script to each tab, so that clicking tab A's page action will always result in a search for tab A stuff. How can that be done? I'd appreciate your suggestions!
background.js
title = "";
luckySearchURL = "http://www.google.com/search?btnI=I%27m+Feeling+Lucky&ie=UTF-8&oe=UTF-8&q=";
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.title != "") {
title = request.title;
sendResponse({confirm: "WE GOT IT."});
}
});
chrome.tabs.onUpdated.addListener(function(tabId, change, tab) {
if (change.status === "complete" && title !== "") {
chrome.pageAction.show(tabId);
}
});
chrome.pageAction.onClicked.addListener(function(tab) {
chrome.tabs.create({url: luckySearchURL + title})
})
contentscript.js
function getSearchContent() {
url = document.URL;
if (url.indexOf("example.com/") > -1)
return "example";
}
if (window === top) {
content = getSearchContent();
if (content !== null) {
chrome.runtime.sendMessage({title: content}, function(response) {
console.log(response.confirm); })
};
}
You could do something like store the title with its associated tabId, that way when you click on the pageAction it uses the correct title. The changes would be just these:
background.js
title= [];
[...]
chrome.runtime.onMessage.addListener(function(request,sender,sendResponse){
if (request.title != "") {
title.push({tabId:sender.tab.id, title:request.title});
sendResponse({confirm: "WE GOT IT."});
}
});
[...]
chrome.pageAction.onClicked.addListener(function(tab) {
title.forEach(function(v,i,a){
if(v.tabId == tab.id){
chrome.tabs.create({url: luckySearchURL + v.title});
// Here I am going to remove it from the array because otherwise the
// array would grow without bounds, but it would be better to remove
// it when the tab is closed so that you can use the pageAction more
// than once.
a.splice(i,1);
}
});
});
You're facing this issue because of window === top. So your title variable gets its value from the last opened tab. So if B is opened after A, title gets its value from B. Try this: Detect Tab Id which called the script, fetch the url of that tab, which then becomes your title variable. As below:
chrome.pageAction.onClicked.addListener(function(tab) {
chrome.tabs.query({active:true},function(tabs){
//this function gets tabs details of the active tab, the tab that clicked the pageAction
var urltab = tabs[0].url;
//get the url of the tab that called this script - in your case, tab A or B.
chrome.tabs.create({url: urltab + title});
});
});

Resources