Chrome Extension - Duplication of the event listener? - event-handling

Introduction
I've got a problem with my extension for Chrome. It supposed to show a small overlay popup window (created in jQuery) with search results from google based on your text selection. Basically you should be able to highlight a text on any page, right click on it (context menu), click on "Search for 'selected keyword'" and a small window pops up in the same tab as an overlay with all search results from google or different search engine.
The problem
Now the extension works really well and without problems, HOWEVER after extensive using of this extension (let's say 1 hour) when I'll highlight another keyword and search for it, extension REMEMBERS last keyword and shows wrong search results. Then again, when I'll highlight another keywords it rememebrs the keyword I've just highlighted but previously didn't get the results for it. It's like a chaining process and I'm always one keyword behind.
My thoughts
I think that the cause of this problem might be event listeners, because when this problem occurs I've got a log from console.log(chrome.extension.onRequest.hasListeners()); which says true. It means that there are 2 or more event listeners attached and they shouldn't as I'm removing them by chrome.extension.onRequest.removeListener(listener1);
Maybe it happens when the actual web page is still loading and I'm highlighting keyword, clicking on context menu and extension can't load yet but somehow event listeners firing... I really can't fully replicate this problem... and what causes this problem.
Manifest File
{
"name": "Search Accelerator",
"version": "1.0",
"manifest_version": 2,
"description": "Search Accelerator extension",
"icons": {
"16": "icon19.png",
"48": "icon48.png",
"128": "icon128.png"
},
"background": {
"scripts": ["content_script.js"]
},
"minimum_chrome_version": "18",
"permissions": [
"contextMenus",
"tabs",
"http://*/*",
"https://*/*",
"management",
"webRequest"
]
}
Content Script
chrome.contextMenus.create({ "title": 'Search for "%s"',
"contexts":['selection'], "onclick": getClickHandler() });
function getClickHandler() { return function(info, tab) {
console.log(chrome.extension.onRequest.hasListeners());
chrome.extension.onRequest.addListener(function listener1 (request, sender, sendResponse) {
var url = "http://www.google.com/?s=" + info.selectionText;
console.log(url); var keywordObj = {keyword: url};
if (request.keywordRequest == "Yes") {
console.log(keywordObj);
sendResponse(keywordObj);
};
chrome.extension.onRequest.removeListener(listener1); } );
chrome.tabs.executeScript(null, { file: "jquery.js" }, function() {
chrome.tabs.executeScript(null, { file: "popup.js" });
}); }; };
Popup js
chrome.extension.sendRequest({keywordRequest: "Yes"}, function(response) {
$(document).ready(function() {
if($("#e14_accelerator")) {
$("#e14_accelerator").remove();
}
var rkeyword = response.keyword;
$("body").append("<div id=\"e14_accelerator\" style=\"position: fixed;top: 30px;right: -330px;z-index: 999999; \"><iframe style=\"border:5px solid #c9c9c9;-webkit-box-shadow: 1px 1px 15px 1px rgba(0, 0, 0, 0.2);\" src=\""+ rkeyword +"\" width=\"328\" height=\"240\"></iframe></div>");
$("#e14_accelerator").animate({right:-13},500);
$(document).click(function() {
$("#e14_accelerator").remove();
});
$("#e14_accelerator").click(function() {
return false;
});
});
});
Error messages that appear when this problem occur:
Error during tabs.executeScript: Cannot access contents of url "chrome-devtools://devtools/devtools.html?docked=true&dockSide=bottom&toolbarColor=rgba(230,230,230,1)&textColor=rgba(0,0,0,1)". Extension manifest must request permission to access this host. sendRequest:21
chromeHidden.handleResponse sendRequest:21
Error during tabs.executeScript: Cannot access contents of url "chrome-devtools://devtools/devtools.html?docked=true&dockSide=bottom&toolbarColor=rgba(230,230,230,1)&textColor=rgba(0,0,0,1)". Extension manifest must request permission to access this host. sendRequest:21
chromeHidden.handleResponse

Related

List Report custom action in extension not displayed

I have created a Custom Action to a List Report FE through an extension. The whole purpose is to export the data to a spreadsheet. While testing it from VS Code, all the added extensions do work. But after the app is deployed, none work.
FES is running SAPUI5 version 1.52.18 (S/4HANA 1709), in case you wonder.
Can't figure out if this is an error with the installation or in the extension I created.
Please find the code below:
Manifest:
"extensions": {
"sap.ui.viewExtensions": {
"sap.suite.ui.generic.template.ListReport.view.ListReport": {}
},
"sap.ui.controllerExtensions": {
"sap.suite.ui.generic.template.ListReport.view.ListReport": {
"controllerName": "com.sap.inventoryvariance.ext.controller.ListReportExt",
"sap.ui.generic.app": {
"ZGM_INV_VAR": {
"EntitySet": "ZGM_INV_VAR",
"Actions": {
"onExport": {
"id": "ExportBtn",
"text": "Export",
"press": "onExport",
"requiresSelection": false
}
Extension:
sap.ui.controller("com.sap.inventoryvariance.ext.controller.ListReportExt", {
onExport : function(oEvent) {
alert('ExportToExcel');
}
});
I even tried using the setting "useExportToExcel": true. While testing from VS Code, both work (but VS Code lowest SAPUI5 version is 1.65):
But from the deployed app, nothing happens:

How do I define a custom snippet using VScode "Surround" extension

I'm using a VScode extension called "Surround"
In the docs for this extension it shows how to define a custom snippet. What I want is really simple. I would like to add the ability to surround selected text with tags, ie <> </> while placing the cursors in the middle where I define what type of tag that is (html, img, button, etc)
The docs show this example:
{
"surround.custom": {
// command name must be unique
"yourCommandName": {
// label must be unique
"label": "Your Snippet Label",
"description": "Your Snippet Description",
"snippet": "burrito { $TM_SELECTED_TEXT }$0", // <-- snippet goes here.
"languageIds": ["html", "javascript", "typescript", "markdown"]
},
// You can add more ...
}
}
I can almost parse it, except I don't know what the placeholders are representing. I assume { $TM_SELECTED_TEXT } is the text I've selected but what is the trailing $0 used for? Also, how can I place 2 cursors in between the opening and closing tags?
Thanks in advance.

Within a VSCode extension is it possible to have a panel that switches between a webview and tree view

i want to add a new explorer panel into vscode. I want it to display either a treeView or a webView depending on if the user has connected to my backend application. I can see something similar in the base of vscode in the folder view. When no folder is open this view is shown
and when you have a folder open it looks like
For anyone else who finds this question, the behaviour seen in the file explorer is achievable through a Welcome Message.
A view's welcome message will show when the tree for that view is empty.
Preview
Welcome Message
Normal tree view
Example
In your package.json, declare:
The view
The view welcome message
The command which the welcome message button should execute
"contributes": {
"commands": [
{
"command": "myExtension.myCommand",
"title": "My Custom Command"
}
],
"views": {
"explorer": [
{
"id": "myCustomView",
"name": "My Custom View",
"contextualTitle": "My Custom View"
}
]
},
"viewsWelcome": [
{
"view": "myCustomView",
"contents": "Welcome to my custom view! [learn more](https://google.com/).\n[Get Started](command:myExtension.myCommand)"
}
]
}
In your extension.ts
Define the button command
Hook up the view to the view provider
import * as vscode from 'vscode';
import { CustomViewProvider } from './CustomViewProvider';
export function activate(context: vscode.ExtensionContext) {
// Add the custom view
const customViewProvider = new CustomViewProvider();
vscode.window.registerTreeDataProvider('myCustomView', customViewProvider);
// Add the command
let myCustomCommand = vscode.commands.registerCommand('myExtension.myCommand', () => {
vscode.window.showInformationMessage('This is my custom command!');
});
context.subscriptions.push(myCustomCommand);
}
export function deactivate() { }
In CustomViewProvider.ts, define when your view is empty or not.
import * as vscode from 'vscode';
export class CustomViewProvider implements vscode.TreeDataProvider<vscode.TreeItem> {
getTreeItem(element: vscode.TreeItem): vscode.TreeItem {
return element;
}
getChildren(element?: vscode.TreeItem): Thenable<vscode.TreeItem[]> {
// NOTE:
// When TRUE, the welcome message will show
// When FALSE, the welcome message will NOT show
var showEmptyView = true;
if (showEmptyView) {
return Promise.resolve([]);
}
return Promise.resolve([
new vscode.TreeItem('This view is not empty!')
]);
}
}
As of VS Code 1.25, views may only contain tree views. Support for showing a webview in the side bar is tracked by https://github.com/Microsoft/vscode/issues/46585
If all you need is a button or simple prompt, you can use a tree view with a single node in the first case

Navigate to the same view but with a different parameters

I'm trying to create a launchpad app using a list of tiles, the problem is that when I click in a tile it could be a app then I navigate to app url or it could be an group of apps or other groups than I need to navigate to the same view of the launchpad but with a new list of tiles. For now, I just want to navigate to navigate to the same view when I click in a tile and do it with slide transition but if I create a route with the same target of default route the view does not render when I start the application, it only works if I ser the target of the route when I create an other target with the same view name.
where is a part of my manifest:
"routes": [
{
"pattern": "",
"name": "group",
"target": "group"
},
{
"pattern": "group",
"name": "group2",
"target": "group2"
}
],
"targets": {
"group": {
"viewName": "TileGroup",
"viewLevel" : 1
},
"group2": {
"viewName": "TileGroup",
"viewLevel" : 2
}
}
}
ans here the controller of my TileGroup view for now.
sap.ui.define([
"sap/ui/core/UIComponent"
],
sap.ui.controller("pelissari.soficom.launchpad.controller.TileGroup", {
onInit: function() {
var oModel = new sap.ui.model.json.JSONModel();
oModel.loadData("./model/data.json");
this.getView().setModel(oModel);
},
onPress: function (oEvent) {
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("group2");
}
}));
With this code I think that the navigation is working because the url of the app changes when I click in a tile but the view do not change.
If you are seeing the URL being changed, then you have the first part of the solution ready. The only thing is that you shouldn't navigate to a new target. Instead, you should navigate to the same target, but passing another tilegroup identifier using the second parameter of the navTo method, e.g.:
oRouter.navTo("tileView", {group: "group1"});
The second part involves getting an event raised in your controller whenever the URL changes, so that you can act on the change.
To catch the ID passed using the navTo method, you should change the route pattern in your manifest accordingly, e.g.:
"pattern": "tiles/:group:"
tiles indicates a fixed part in your URL, while :group: specifies an optional parameter called group. If you want to do this from the root (I think that's what you planned to do), it should be
"pattern": ":group:"
To inform the router that you want to get triggered when the URL changes, you can set a call-back. You can do so by inserting the code below into the onInit handler of your controller:
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.getRoute("group").attachPatternMatched(this._onPatternMatched, this);
When this is in your onInit handler, the _onPatternMatched handler is invoked when there was a change in the URL that involves target group. The latter is useful, otherwise your method would get triggered for every URL change, even when the view linked to your controller is not visible.
From the _onPatternMatched method, you should read back what the group ID is that should be displayed, so that you can change the tiles. You can do this by reading the arguments parameter from the event parameter:
_onObjectMatched : function (oEvent) {
var groupId = oEvent.getParameter("arguments").group;
console.log("Group ID: " + groupId);
},
The essentials of routing are very well explained in the UI5 walk-through step 31, 32 and 33. Please especially take note of part 32, which explains routing with parameters.

How can I make a chrome packaged app which runs in fullscreen at startup?

Currently it seems that the fullscreen ability can only be activated from a user action (mouse/keyboard event). Is there a way to circumvent this?
Now, you can just add the state:"fullscreen" property on your main .js:
chrome.app.runtime.onLaunched.addListener(
function() {
chrome.app.window.create('index.html',
{
state: "fullscreen",
}
);
}
);
Make sure you don't add resizable: false or bounds properties, and you add a "fullscreen" permision on the manifest.json.
{
...
"permissions": [
...
"fullscreen"
]
}
You can use the HTML5 Fullscreen API, which requires a user action:
button.addEventListener('click', function() {
document.body.webkitRequestFullscreen();
});
or the more "app-y" way using the AppWindow object, which doesn't require a user action:
chrome.app.window.current().fullscreen();
Both need the "fullscreen" permission in the manifest.json.
I have got it working with below code
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html', {
'width': 1024,
'height': 768
},
function(win) {
win.maximize();
});
});
Check out https://plus.google.com/100132233764003563318/posts/2SuD7MVd8mG referring to recently landed changelist https://chromiumcodereview.appspot.com/12205002. You can lift sample code from either of those sources:
document.body.addEventListener('click', function() {
document.body.webkitRequestFullscreen();
});
Make sure in your manifest you're requesting the "fullscreen" permission and that you're testing on a sufficiently recent Chrome build (beta channel ought to have this feature by now, and dev definitely does).
Your question specifically refers to packaged apps, but in case anyone reading this answer missed this, this will work only with Chrome packaged apps.
main.js
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html',{},function(window) {
window.fullscreen();
});
});
manifest.json
{
...
"manifest_version": 2,
"minimum_chrome_version": "23",
"app": {
"background": {
"scripts": ["main.js"]
}
},
"permissions": ["fullscreen"]
}
Edit: Per BeardFist's comment, my original answer was wrong on two fronts. You can do this on a Chrome Extension (which this is tagged as), but probably not on a packaged app.
For extensions you can make it go fullscreen using the state:"fullscreen" option, which can be applied with chrome.window.update. The code below chains the creation of the window via chrome.windows.create with chrome.window.update. In my experiments you could not set the fullscreen state directly through the window creation.
chrome.windows.create(
{url:"PATH/TO/POPUP"},
function(newWindow) {
chrome.windows.update(newWindow.id, {state:"fullscreen"})
});