I have the following code. This is what it does. When you highlight/select some text on a webpage by clicking and dragging, right click to see the context menu, and when the menu item is clicked, the highlighted text shows up in an alert box. I want to modify this code so that the user doesn't have to highlight any text by click and drag. All they have to do is right click on a word and choose the context menu item. The word/text they right clicked on should appear as alert.
let contextMenuItem = {
"id": "helloWorld",
"title": "Hello World",
"contexts": ["selection"]
};
chrome.contextMenus.removeAll(function() {
chrome.contextMenus.create(contextMenuItem);
});
chrome.contextMenus.onClicked.addListener(function(clickData) {
let inputString = clickData.selectionText;
alert(inputString);
})
First of all, extensions have a "strict" permission model. When you give permission for contextMenus, you are limited to the following contexts:
"all", "page", "frame", "selection", "link", "editable", "image", "video", "audio", "launcher", "browser_action", or "page_action"
Bad UX
If it had a "word" or even a "text" context, it creates a non consistent user experience. Users are not familiar with right click doing actions on text within a web browser.
If you wanted such action, you need to introduce a content-script to add a mouse event to "automatically" select that word using the JavaScript Selection APIs. If you wanted this, you need to expose more "permissions" to your extension to support this experience. Users might not like that.
Example
If this is the experience the extension needs, just create a content-script which automatically selects that word. Something like this will work, which will create a caret range from the mouse position and modify its selection to that word. Note, within the permissions, I just enabled google.com, this is where the content script will inject.
contentscript.js
document.addEventListener('mouseup', (e) => {
if (e.button !== 2) {
return
}
const selection = window.getSelection()
selection.removeAllRanges()
const range = document.caretRangeFromPoint(e.clientX, e.clientY)
range.expand('word')
selection.addRange(range)
});
background.js
const menu = chrome.contextMenus.create({
'id': 'helloWorld',
'title': 'Hello "%s"',
'contexts': ['selection']
})
chrome.contextMenus.onClicked.addListener((clickData) => {
const inputString = clickData.selectionText
console.log(inputString)
});
manifest.json
{
"name": "contextMenus",
"version": "1.0",
"minimum_chrome_version": "35.0",
"manifest_version": 2,
"permissions": [
"contextMenus"
],
"background": {
"scripts": [
"background.js"
]
},
"content_scripts": [
{
"matches": ["https://*.google.com/*"],
"js": ["contentscript.js"]
}
]
}
Related
async feed_first(stepContext)
{
let company_card = MessageFactory.attachment(
CardFactory.adaptiveCard(testfeedback)
);
return await stepContext.prompt("textPrompt", {
prompt: company_card
});
}
async feed_second(stepContext) {
console.log("enter feedback second");
console.log(stepContext.context.activity.value);
}
{
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "Container",
"items": [
{
"type": "Input.ChoiceSet",
"placeholder": "Placeholder text",
"choices": [
{
"title": " 1",
"value": " 1"
},
{
"title": " 2",
"value": " 2"
}
],
"style": "expanded",
"id": "cho"
},
{
"type": "ActionSet",
"actions": [
{
"type": "Action.Submit",
"title": "Submit"
}
]
}
]
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
so for this code what happens is, the card gets displayed, but on the bot emulator when the submit button is clicked, nothing happens. the console displays " Running dialog with message activity" and the same card is prompted again. the bot doesn't flow to the second step(feed_second) of waterfall dialog. what i would like for the code to do is to display "enter feedback second" on the console and then show the selected choice with the id "cho" for stepContext.context.activity.value agiain on the console. side note- i have added "feed_first" and "feed_second" while declaring the WaterfallDialog, so that's not the the issue
If you want to use an Adaptive Card's inputs with a text prompt, you will need to access the activity's value and serialize it into the activity's text somehow, as seen in this example. This information is in the dialogs section of my Adaptive Cards blog post, which you should read.
In JavaScript you can serialize the value into text like this:
/**
*
* #param {TurnContext} turnContext
*/
async sendValueToDialogAsync(turnContext)
{
// Serialize value
var json = JSON.stringify(turnContext.activity.value);
// Assign the serialized value to the turn context's activity
turnContext.activity.text = json;
// Create a dialog context
var dc = await this.dialogs.createContext(turnContext);
// Continue the dialog with the modified activity
await dc.continueDialog();
}
In your case, if you only need the result of one input then you can just use that property instead of serializing anything:
turnContext.activity.text = turnContext.activity.value.cho;
Trying to add hovers to add hovers to my VS Code extension. I was able to syntax highlighting and commands to work, but stuck on adding this hover feature.
I think my blocker is how to properly implement the HoverProvider API. I'm doing a simple test below for a hover provider that activates when a series of tokens are recognized as the keyword HELLO. The hover I've implemented in my testing. I'm using vsce package to package and test my extension locally.
My command for the extension works, but when I hover over the word "HELLO", my hover does not appear.
./client/extension.js
const vscode = require('vscode');
function activate(context) {
console.log('Congratulations, your extension "star-rod" is now active!');
let disposable = vscode.commands.registerCommand('extension.mamar', () => {
vscode.window.showInformationMessage("The Star Rod... is powerful beyond belief. It can grant any wish. For as long as we can remember, Bowser has been making wishes like, for instance... 'I'd like to trounce Mario' or 'I want Princess Peach to like me.' Of course, Stars ignore such selfish wishes. As a result, his wishes were never granted.");
});
context.subscriptions.push(disposable);
vscode.languages.registerHoverProvider('javascript', {
provideHover(document, position, token) {
const range = document.getWordRangeAtPosition(position);
const word = document.getText(range);
if (word == "HELLO") {
return new vscode.Hover({
language: "Hello language",
value: "Hello Value"
});
}
}
});
}
function deactivate() { }
module.exports = {
activate,
deactivate
}
./package.json
{
"name": "star-rod-script",
"publisher": "sonicspiral",
"displayName": "Star Rod Script",
"description": "Syntax highlighting for Paper Mario 64 ROM patching tool",
"version": "1.0.1",
"repository": {
"type": "git",
"url": "https://github.com/gregdegruy/star-rod.git"
},
"categories": [
"Programming Languages"
],
"activationEvents": [
"onCommand:extension.mamar",
"onLanguage:star-rod-script"
],
"engines": {
"vscode": "^1.31.0"
},
"main": "./client/extension.js",
"contributes": {
"capabilities": {
"hoverProvider": "true"
},
"commands": [
{
"command": "extension.mamar",
"title": "Mamar"
}
],
"languages": [
{
"id": "star-rod-script",
"extensions": [
".bpat",
".bscr",
".mpat",
".mscr"
],
"aliases": [
"Star Rod Script",
"mscr"
],
"configuration": "./language-configuration.json"
}
],
"grammars": [
{
"language": "star-rod-script",
"scopeName": "source.mscr",
"path": "./syntaxes/mscr.tmLanguage.json"
}
]
},
"devDependencies": {
"js-yaml": "^3.12.1",
"vscode": "^1.1.29"
}
}
Your code allowed me to get Hovers working in my first extension. I think your mistake is having javascript as the selector: vscode.DocumentSelector. Is that there from code you copied? That should probably be set to star-rod-script for your extension.
I also don't have "capabilities": {"hoverProvider": "true"} in mine. I changed your code to:
disposable = vscode.languages.registerHoverProvider('star-rod-script', { // or 'star rod script'
//....
});
context.subscriptions.push(disposable);
I don't know the nuances of how you apply your extension to certain documents, but it doesn't look like you're trying to apply the hover to javascript docs. You need the selector to include the docs your extension works with. In my case that's covered by my extension name which is the language mode that shows up in the vscode status bar. More info on document-selectors.
Not sure if it's needed, but I also took the return and pushed it onto the subscriptions array. Works without that, but I think that's proper??
Your package.json looks a bit odd. I bet your extension is not activated. The "contributes/capabilites" value is something I haven't seen before. Remove that and instead change your activationEvents to:
"activationEvents": [
"onLanguage:star-rod-script"
],
I am creating the extension for Firefox (I use ver.65) which suppose to search a movie's title on Filmweb website (an equivalent of IMDB). It would happen by using the selection on any website and combine with Filmweb's search side address, and then go to that address on a new tab.
I tried to use document.getSelection instead of window.getSelection but it didn't work.
filmwebExt.js
const contextMenuItem = {
id: "search-on-Filmweb",
title: "Search on Filmweb",
contexts: ["selection"]
};
function getSelectionText() {
console.log('window.getSelection: ',window.getSelection());
var text = "true";
if (window.getSelection()) {
text = window.getSelection().toString();
console.log(text); //empty in debbuging console
} else if (document.selection && document.selection.type !== "Control") {
text = document.selection.createRange().text;
}
console.log(text); //empty in debbuging console
return text;
}
console.log('second window.getSelection: ',window.getSelection());
browser.contextMenus.create(contextMenuItem);
browser.contextMenus.onClicked.addListener(function (info) {
const selectedText = getSelectionText();
const url = 'https://www.filmweb.pl/search?q=';
const fullUrlAddress = url + selectedText;
if (info.menuItemId === "search-on-Filmweb") {
console.log('comparison: ',info.menuItemId === "search-on-Filmweb");
console.log("selectedText ",selectedText," fullUrlAddress ",fullUrlAddress);
window.open(fullUrlAddress, '_blank');
}
});
manifest.json
{
"manifest_version": 2,
"name": "Filmweb_Search",
"version": "1.0",
"description": "Adds Filmweb search option in context menu",
"applications": {
"gecko": {
"id": "wisznu#gmail.com"
}
},
"background": {
"scripts": [
"filmwebExt.js"
]
},
"icons": {
"48": "icons/Filmweb_icon48x48.png",
"96": "icons/Filmweb_icon96x96.png"
},
"content_scripts": [
{
"matches": [
"*://*/*"
],
"js": [
"filmwebExt.js"
]
}
],
"permissions": [
"tabs",
"activeTab",
"<all_urls>",
"contextMenus"
]
}
Currently, the context menu item shows in the context menu properly, but the debugging console shows that window.getSelection() returns null values in an object and empty string for window.getSelection().toString()
If the infrastructure of Firefox Add-On is still similar to what it was years ago, the problem here is that you cannot access from the process where the context menu is, to the selection of the document.
I believe that is for this very reason that the info object was added, so that you can have the information you need in the process where your code is running.
The object info has a property called selectionText, and that's what you have to use.
And for opening a new tab, it's better if you use the tabs API.
So, to summarize, your filmwebExt.js's file would looks like:
const contextMenuItem = {
id: "search-on-Filmweb",
title: "Search on Filmweb",
contexts: ["selection"]
};
browser.contextMenus.create(contextMenuItem);
browser.contextMenus.onClicked.addListener(info => {
if (info.menuItemId === "search-on-Filmweb") {
const url = "https://www.filmweb.pl/search?q=" + info.selectionText;
browser.tabs.create({ url });
});
I am currently developing a Chrome Extension and I want that the pageAction icon appears for an amount of URL's (around 500).
In my background.js file (among other code), I have this:
// Called when the url of a tab changes
function checkForValidUrl(tabId, changeInfo, tab) {
// If the tabs url starts with "http://specific_domain"
if (tab.url.indexOf('http://specific_domain') == 0) {
// Shows the page action
chrome.pageAction.show(tabId);
}
};
// Listen for any changes to the URL of any tab
chrome.tabs.onUpdated.addListener(checkForValidUrl);
I can get it to work on a specific site already, but I want to change the 'http://specific_domain' to a list of permitted sites, like a white list. I have access to a JSON file that is online.
Here's a snippet of it:
{
"antelife.com": {
"fbid": "AntElifecom",
"type": "store",
"name": "AntElife"
},
"amazon.com": {
"fbid": "Amazon",
"type": "store",
"name": "Amazon"
},
"ebay.com": {
"fbid": "eBay",
"type": "store",
"name": "eBay"
},
"mobilegeeks.com": {
"fbid": "mobilegeekscom",
"type": "publisher",
"name": "Mobile Geeks"
}
}
I want to extract the domains, and somehow iterate for all of them and if they are on the list, the pageAction icon should appear. Something like this:
function checkForValidUrl(tabId, changeInfo, tab) {
for (var i = 0, iLength = whiteListUrl.length; i < iLength; i++) {
if (tab.url.indexOf('http://www.'+whiteListUrl[i]) > -1) {
chrome.pageAction.show(tabId);
notification.show();
break;
}
}
};
chrome.tabs.onUpdated.addListener(checkForValidUrl);
Any kind of help should be useful. Many thanks.
If you have a json string you can get url's list in this way;
var urlObj = JSON.parse(jsonString);
var whiteListUrl = Object.keys(urlObj);
I've a problem with permissions, I want to inject my javascript into a page served from https, http and file
Below the manifest file
{
"background":{
"scripts":["background.js"]
},
"permissions":["tabs"],
"browser_action": {
"default_icon": "img/icone.png",
"default_title": "displayer."
},
"icons" : {
"128" : "img/icone_128.png",
"48" : "img/icone_48.png",
"32" : "img/icone_32.png",
"24" : "img/icone_24.png",
"16" : "img/icone_16.png"
},
"manifest_version": 2,
"name": "displayer.",
"description": "This extension helps you to compare your wireframe with your current coded page..",
"version": "1.0.1"
}
And the injected javascript (background.js)
chrome.browserAction.onClicked.addListener(function (tab) { //Fired when User Clicks ICON
if (tab.url.indexOf("http://*/*, https://*/*, file://*/*") != -1) { // Inspect whether the place where user clicked matches with our list of URL
chrome.tabs.executeScript(tab.id,
{"file": "contentscript.js"},
function () { // Execute your code
console.log("Script Executed .. "); // Notification on Completion
});
chrome.tabs.insertCSS(null, {file: "grid.css"});
}
});
I already tried to "permissions" :["https:///, http:///, file:///"], but it doesn't work.
Any help is much appreciated.
The fastest way to provide all the permissions is to include, literally, "<all_urls>" into the permissions set.
In general, read the match pattern documentation.
Your other problem is pointed out by Rob W in the comments: indexOf('http://*/*') searches for literally that string, with asterisks.
You can do, as he recommended, tab.url.indexOf('http:') == 0 and so on.