How do I get the context Menu "oncreate" Event - google-chrome

I write a Chrome extension. This extension should add items to the Chrome context menĂ¼ if a text is selected. This should happen dynamically everytime the contextmenu is opened.
As on this image:
The Problem:
I can't find any event that is triggered (and works) if the contextmenu opens. The following code sample doesn't work.
window.addEventListener('contextmenu', function(){
DynamicMenu.generateMenu(window.getSelection());
});
It's also impossible to trigger the adding of dynamic items from the "test" item:
chrome.contextMenus.create(
{
"title": "test",
"contexts" :["selection"],
"onclick" : DynamicMenu.generateMenu
});
Thanks for your help!
Update (how it now works):
Manifest:
...
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["document_event.js"],
"run_at" : "document_idle"
}
],
...
document_event.js:
window.addEventListener('mousedown', function(e) {
var rightclick;
if (!e) var e = window.event;
if (e.which) rightclick = (e.which == 3);
else if (e.button) rightclick = (e.button == 2);
if(rightclick){
var searchText = window.getSelection().toString();
chrome.extension.sendRequest({search: searchText}, function(response) {
...
});
}
});
content.js
...
chrome.extension.onRequest.addListener(
function(request, sender, sendResponse) {
var a = request.search;
...
sendResponse({});
});
...

UPDATE: contextMenu onCreate Event
It's less of an official event per se, as it is a simple async callback built into the chrome.extension.create() method signature:
[integer|string] chrome.contextMenus.create(objectcreateProps,function cb)
Parameters:
REQUIRED: an object representing optional createProperties. All the properties are optional, but, the createProperties object itself is required as the first parameter to the create() method. So, if you don't want to use an custom options/properties, simply supply an empty object {}
OPTIONAL: a function which it callbacks when the create() method completes.
Return Value:
Either:
an auto-generated integer if you do not provide an id property in the createProperties object parameter when calling the method.
a string representing the generatedId of the context menu/menu item. A string is only returned if you supplied one in the createProperties object parameter when you called the create() method, and if successfully created, the string you supplied will be the EXACT VALUE that is returned.
The latest docs for the chrome.contextMenus.create API are here:
https://developer.chrome.com/extensions/contextMenus#create
.
In your case/example, you should modify your code like so:
var gId; //the id that is generated if the menu is created successfully
gId = chrome.contextMenus.create(
{//start createProperties
"title": "test",
"contexts" :["selection"],
"onclick" : DynamicMenu.generateMenu
},//end createProperties
function cmItmCreated_handler(){ //your callback (create event, if you will)
if (typeof chrome.runtime.lastError === "undefined") {
console.log("context menu/item created. 'gId' var contains generated id");
}
else {
console.error(chrome.runtime.lastError);
}
}//end function cmItmCreated_handler()
);

Have you tried tracking the right click event? The chrome extension context menu APIs mostly deal with clicks on the new context menu items.

Related

Chrome extension content_script add html with onchange function, function is undefined

This is my first try on creating a chrome extension and most stuff is already working out. Except for one thing.
I created a script that insert a SELECT the SELECT has a onchange that calls a function that is defined in my content script. But when the select onchange fires, the function that is calling is undefined.
Code samples:
manifest.js
{
"name": "Extra Filters",
"version": "0.0.1",
"manifest_version": 2,
"content_scripts": [
{
"matches": ["..."],
"js": ["content.js"]
}
]
}
content.js
function CreateSelect(index, options) {
if(options == null) return;
let html = `<select onchange='AddFilter(${index}, this)'>`;
options.forEach(option => {
html += `<option
value="${option}">
${option}
</option>`;
});
html += `</select>`;
return html;
}
function AddFilter(index, value) {
console.log(index, value);
}
So when the select changes, I get the error: Uncaught ReferenceError: AddFilter is not defined

Autodesk Forge Viewer3d search using attributeNames

I'm trying to implement .search() and restrict attributeNames using the optional parameter but it always brings back an empty array.
https://developer.autodesk.com/en/docs/viewer/v2/reference/javascript/viewer3d/
Can someone clarify how this filter is being applied? I was expecting it to look at the returned property.displayName but apparently that's not the case.
Example:
viewer.search('13-097', function (ids) {
console.log(ids);
var id = ids[0];
viewer.getProperties(id, function (obj) {
console.log(obj.properties);
});
}, function (e) { });
viewer.search('13-097', function (ids) {
console.log(ids);
}, function (e) { }, ['ADDRESS']);
Output:
first search:
[8095]
second search:
[]
from object 8095, properties:
10:Object
displayCategory:"DWF - Construction"
displayName:"ADDRESS"
displayValue:"13-097"
hidden:false
type:20
units:null
Please note the Autodesk.Viewing.Viewer3D.search() method is NOT case sensitive on the text parameter, but it IS case sensitive on the attributeNames parameter, and you need to use the full name of the attribute.
If you are using the displayName of properties to correlate, note that viewer.getProperties() is currently returning the displayName. When there is no displayName, then (and only then) attribute name is returned.
Below is a sample I tried before (adjusted to your dataset):
function search() {
viewer.clearSelection(); // remove previously highlighted searches
var searchStr = '13-097';
var searchPropList = new Array('ADDRESS');
viewer.search(searchStr, searchCallback, searchErrorCallback, searchPropList);
}
function searchCallback(ids) {
alert(ids.length);
}
function searchErrorCallback(error) {
console.log(error);
}
EDIT (Oct 24, 2016)
The Viewer 2.11 .getProperties method returns attributes, which can be used on the .search attributesNames parameter.

Chrome extension - pass function result from content script to popup.js

I have extension with popup.html which has popup.js in it.
I use popup.js to call function located in content script using chrome.tabs.sendMessage -method.
This is working nicely, but..
How do I return the value of the function back to popup.js ? Do I need to set up a listener on the popup.js aswell or what ?
On my popup.js I have:
chrome.tabs.sendMessage(tab.id, {
expiryRequest: 'expiry '
}, function (response) {
if (response.refreshResponse === true) {
console.log('Expiry taken');
} else {
console.log('Expiry NOT taken');
}
});
This part works great..
On my content script I read certain div into a vartiable "result".
At the end of the function on content script I have used.
return result;
or
return true;
None of those returns anything back tuo popup.js.
What do I need to change in order to get my return to work from content script to popup.js ?
You should not return your result, but send it back.
chrome.runtime.onMessage callbacks take 3 parameters: the message, the sender information, and the sendResponse callback.
To pass a response back, sendResponse must be called:
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
if(message.ping) sendResponse({pong: true});
});
However, there's an additional trick to it. The event listener should either reply immediately (i.e. synchronously, before it exits) or signal that it will reply later. This is done with the return value:
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
if(message.ping) {
chrome.storage.local.get("shouldReply", function(result){
// This is asynchronous: by now the listener returned
if(result.shouldReply) sendResponse({pong: true});
});
}
return true; // Indicate that you will eventually call sendResponse
});
Unless you do this, sendResponse reference is invalidated when the listener exits and an undefined response is sent.
One more caveat: you should call sendResponse no more than once; it will generate an error otherwise.

addListener's sendResponse called from callback returns previous message

In the beginning I thought that the problem is with chrome.runtime.sendMessage() I was sending two messages. One for accessing localstorage and another one to get/read the file data, but nothing changed after I merged them in one sendMessage which means that the actual problem is window.webkitRequestFileSystem() it's returning the previous file instead of the current one.
Is there a better/faster way of storing something client side ? (I'm willing to try everything) ?
manifest.json
{
"manifest_version": 2,
...
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["content.js"],
"run_at": "document_end"
}
],
"background": {
"scripts": ["background.js"]
},
"permissions": [
"unlimitedStorage"
]
}
background.js
var theme = '';
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if(request.method == "getTheme") {
themes = JSON.parse(localStorage["themes"]);
themeName = "";
for (var i = themes.length - 1; i >= 0; i--) {
if(request.url.indexOf(themes[i]) !== -1) {
themeName = themes[i];
}
};
window.webkitRequestFileSystem(window.PERSISTENT, 0, readFromFileStorage.bind(window, themeName), errorHandler);
sendResponse({data: theme});
}
});
function readFromFileStorage(filename, fs) {
fs.root.getFile(filename, {}, function(fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
theme = this.result;
};
reader.readAsText(file);
}, errorHandler);
}, errorHandler);
}
function errorHandler(e) {
console.log('Error: ' + e.message);
}
content.js
chrome.runtime.sendMessage({method: "getTheme", url: location.href}, function(response) {
console.log(response.data);
});
As your question is not an SSCCE it is hard to test it, but I think your problem is understanding of JS asynchronous nature.
So how your code will actually execute:
First window.webkitRequestFileSystem(PERSISTENT, 0, successCallback, errorHandler); will be executed
Then sendResponse({data: theme}); will send the response with whatever is stored in theme
Then either successCallback either errorHandler will be called depending on file request success. If successCallback will be called then you'll have your theme variable filled with the value you want. But it will be to late as you already sent the response.
The next time you pass the message you will receive previous theme value (point 2) as your code will find new value only after you'll send the value.
A solution may be to call sendResponse inside of successCallback after you find your desired value (you'll have to pass sendResponse into readFromFileStorage). If you'll do so then you may consider to add sendResponse into errorHandler function just to be sure that your code will always get a valid response.
In case that you'll move the sendResponse into a callback then you have to return true from addListener function, otherwise on addListener return channel will be closed and response will not be sent.

Firing a javascript function from a dynamically created button

Updated code and issue:
I am creating a test harness for my RPC server. Currently it consists of a page which immeadiately fires off an AJAX request to retrieve all functions on the server. Once that is returned it creates a list of buttons so I can click to test. Eventually I will add dialog boxes to test parameter passing to the functions but currently I want to just fire off the basic request when I click the button. The issue I am seeing is that the onclick function is always firing the last function in the list presumably because when the click is fired key is set to the last value in the array. I thought to pass button.innerHTML value but that too suffers that the last button.innerHTML is that of the final key.
What do I need to do to fire off the action correctly?
Here is the business end of the code:
$(document).ready(function() {
$.jsonRPC.setup({
endPoint: '//api.localhost/index.php'
});
$.jsonRPC.request('getExampleData', {
params: [],
success: function(result) {
for (var key in result.result) {
console.log(key+' => '+result.result[key]);
var button = document.createElement('button');
button.innerHTML = result.result[key];
button.onclick = function() { callRPCFunction(result.result[key]); return false; }
var foo = document.getElementById("page");
foo.appendChild(button);
}
},
error: function(result) {
console.log(result);
}
});
});
function callRPCFunction(target) {
$.jsonRPC.request(target, {
params: [],
success: function(result) {
console.log(result);
},
error: function(result) {
console.log(result);
}
});
}
Assignment to element.onClick will not work until the element is added to the DOM. You may call element.onClick(callRPCFunction(result.result[key])); after foo.appendChild(element);. That might work!
You may use jQuery's live() here, it was created for these purposes:
$(element).live('click', callRPCFunction(result.result[key])