CTRL+C to copy URL on mouse hover - javascript

I would like to hover my mouse on a URL and copy the URL with CTRL+Alt+C. This topic pretty much describes 99% of what I'm trying to do:
https://www.autohotkey.com/board/topic/111762-mouse-hover-copy-link/?p=662644
I've taken the userscript and modified it slightly, so that it gives me the URL after the "href" part. By the way, I'm not at all proficient with Javascript, I've simply played around with it and was lucky to get it working. Here's what I have:
This works great, but this copies the URL everytime I hover my mouse on a link. I don't want this, as it just adds a bunch of URLs to my clipboard.
At the bottom of that post, there's a Autohotkey component. It gets the tab title, rather than the URL.
How can I modify both the userscript and the Autohotkey to do what I want?
As a secondary question - I would like to create an additional userscript using the Javascript above as a reference. This new userscript will take the URL that my mouse is hovering on, change it so that it is prefixed with word:ofe|u| and pastes that into the URL bar when I click on the link while holding the Alt key. So basically:
Hover mouse on a URL that I am interested in (e.g. https www.google.com)
Userscript will modify the URL and change it to word:ofe|u|https://www.google.com
Hold down Alt + left click on the URL
word:ofe|u|https://www.google.com - page is loaded, or URL is pasted into the URL bar
UPDATE:
I've managed to get something going, not sure how I did it but I just played around with the codes I found on Google. Again, I do not know anything about Javascript.
https://pastebin.com/S9znPxBU
// ...
This works well, but if you do single press of CTRL+C, it just keeps copying URLs to the clipboard whenever you hover your mouse on a link. I want it to only start copying a URL to the clipboard everytime I press CTRL+C.

AutoHotkey won't help much here, as it can't access the URL directly. Thankfully, you can use the new JavaScript Clipboard API. It only works in secure contexts (AKA HTTPS) and the page needs to be in focus. Doing this using a browser extension would be perferred, since it can workaround those restrictions.
Try it, but first click on a blank area in the preview window to focus it.
// Userscript
"use strict";
window.addEventListener("load", () => {
const evOpts = {capture: true, passive: true};
let hoveredLink = null;
for (let link of document.getElementsByTagName("a")) {
link.addEventListener("mouseenter", () => {
hoveredLink = link;
}, evOpts);
link.addEventListener("mouseleave", () => {
hoveredLink = null;
}, evOpts);
}
window.addEventListener("keydown", (ev) => {
if (hoveredLink && ev.ctrlKey && ev.altKey && ev.code === "KeyC") // Ctrl+Alt+C
// Copy *absolute* URL to the clipboard
navigator.clipboard.writeText(new URL(hoveredLink.href, location.href)).then(()=>{
console.log("URL copied to clipboard!");
}, (err)=>{
console.error("Error copying URL to clipboard: ", err);
});
}, evOpts);
});
Google Youtube

Related

Bookmarklet to open a webpage and login

I am trying to create a bookmarklet to open a web page, populate credentials, and click login in one shot. Here is the needed bookmarklets:
For opening a webpage:
javascript:location.href='http://www.unt.edu'
For Credentials & login:
javascript:(function(){var d=document;s=d.querySelector;s.call(d,'input[name*=email]').value='YOUREMAIL#company.com'; s.call(d,'input[name*=pass]').value='SECRETPASSWORDHERE';s.call(d,'button[id*=login],input[type=button][id*=login],.btn-login').click(); }())
Is it possible to combine both of them considering the asynchronous behavior of opening the web page?
You can combine this in one bookmarklet, but you'll have to click it twice. The first click will open a website, and the next one will do the login.
javascript: (() => {
const url = 'https://stackoverflow.com/users/login';
if (location.href !== url) return (location.href = url);
document.querySelector('input#email').value = 'EMAIL';
document.querySelector('input#password').value = 'PASSWORD';
document.querySelector('button#submit-button').click();
})();
Consider using a userscript instead, which will run automatically on pageload. Set the userscript's #includes to run on http://www.unt.edu, then set the userscript's JS to the content of your current bookmarklet:
var d=document;s=d.querySelector;s.call(d,'input[name*=email]').value='YOUREMAIL#company.com'; s.call(d,'input[name*=pass]').value='SECRETPASSWORDHERE';s.call(d,'button[id*=login],input[type=button][id*=login],.btn-login').click(); }()
(though I'd recommend separating it out onto separate lines for readability and maintainability)
And then use a plain bookmark (not a bookmarklet) to http://www.unt.edu. This way, whenever you click on the bookmark, the page will load, and when the page loads, the userscript that logs you in will automatically run.

Trying to programmically paste

I am having trouble with execCommand('paste');
My code:
var copy = document.createElement("BUTTON");
copy.innerText = "Copy";
Sections.contextmenu.appendChild(copy);
copy.addEventListener("click", function(e) {
document.execCommand("copy");
});
var paste = document.createElement("BUTTON");
Sections.contextmenu.appendChild(paste);
paste.innerText = "Paste";
paste.addEventListener("click", function(e) {
console.log("Paste");
if (document.execCommand("paste")) {
console.log("pasted");
}
});
Copy worked right out of the box. I cannot get paste to work. I see "Paste" in the console, but nothing is pasted. I've read some things that say that this functionality needs to be explicitly turned on in Firefox. Is there no way (other than using flash... This is talked about in the research I've done) to execute "paste" in a content-editable element programmatically?
The paste command is disabled in web content (it’s only available in a browser extension). It’s disabled presumably because it would allow any website to steal the clipboard’s content. From the MDN documentation on execCommand:
paste
Pastes the clipboard contents at the insertion point (replaces current selection). Disabled for web content.
try the following code
console.log(document.exeCommand('paste')
If false maybe you need a permission to use it or your navigator can't support it
You can also use the Clipboard API, which would obliterate the exeCommand

Save/copy to clipboard image from page by chrome console

For my project I need to copy image (not url, image name. Only data for ability, for example, to paste it to "Microsoft Paint") from page to clipboard by Chrome console.
I tried this:
copy(document.getElementById('someimage'));
but it returns nothing... It only works with text.
If you don't know, then how to download this image by chrome console?
OR
How to make screenshot of the page and copy or download it using Chrome console?
P.s. I can't use any js libraries.
I have explored few things in chrome dev tools
https://homeguides.sfgate.com/stop-air-flow-ceiling-air-diffuser-28867.html - This is the website I am using it for reference.
In Chrome console try the following command
imageurl= document.getElementsByTagName('img')[0].currentSrc;
copy (imageurl);
Note: Here you can change the img [1] array if you want to get different images
Then if you press ctrl + v in your keyboard you could see the image url with https. Please see the above screenshot.
You can perform the ctrl+ v on your new tab to get the image loaded.
or You can try the following method.
Right click the image and click inspect element
You could see some image url. Copy that URL
Open new Tab and paste the URL
If you right click on it you can see "Save Images" option.
Hope it will help you in someway.
As you mentioned you are using Selenium, here's how to save an image with Selenium:
You need to get the image's URL, load it (using ImageIO in this example) and save it. For example, in Java you would do something like this:
try {
driver = new ChromeDriver();
driver.get("http://...");
WebElement img = driver.findElement(By.cssSelector("#selector"));
BufferedImage buffer = ImageIO.read(new URL(img.getAttribute("src")));
ImageIO.write(buffer, "png", new File("image.png"));
} catch (Exception e) {
e.printStackTrace();
} finally {
driver.close();
}
If you want to copy it directly, your class needs to implement java.awt.datatransfer.ClipboardOwner and then you would do something like this:
try {
driver = new ChromeDriver();
driver.get("http://...");
WebElement img = driver.findElement(By.cssSelector("#selector"));
TransferableImage transferable = new TransferableImage(ImageIO.read(new URL(img.getAttribute("src"))));
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(transferable, this );
} catch (Exception e) {
e.printStackTrace();
} finally {
driver.close();
}
Regarding your other questions, here's how to take a screenshot using Chrome DevTools:
There are 3 Capture... commands in Chrome DevTools. Just follow these steps to get to them:
Open DevTools.
Go to the Elements tab and click on the element you want to take the screenshot of.
Press Cmd + P on Mac or Ctrl + P on Windows.
Type > screen. You will get 3 relevant suggestions:
Mobile Capture full size screenshot: Captures the whole page, including the non-visible (out of viewport) area.
Mobile Capture node screenshot: Captures a single node, in this case, the element you clicked in the second step.
Mobile Capture screenshot: Captures the visible area of the page (viewport).
Click on any of them and the screenshot will download automatically.
However, keep in mind this feature doesn't always work fine, especially the Capture node screenshot one, so it might be better to capture the visible area of the page and crop the afterwards.

How to add a right click on my Firefox extension's icon?

Good Day!
I've searched and searched again and I didn't find any help for this problem...
Context :
I've developed a Google Chrome extension that is very simple: send email to somebody with one click. To configure this, there is an option page on this extension to set the email address to which to send. My Google Chrome extension is available here (English translation, just the text, not the install).
Users have asked me to make this extension for Firefox so I'm working on it!
I've read tutorials on cfx and it's OK. But I need to have my extension respond to a right-click on my extension's icon in the toolbar. Not on the page, on my icon.
I'm using ActionButton (or ToggleButton) to add my icon to this the toolbar but I can't find a way to add a menu on the right click (there's already the default Firefox context menu but I want to add "Options".)
If somebody has the solution it would be great!
(I'm not familiar with XUL. So, if possible, a JavaScript only solution, please ^^ )
PS : I'm French so please excuse me for my bad English
EDIT : I've found how to set preferences in my "package.json" file but I want my own window.
And if we could "bind" the button "Options" in Add-on Manager to my own window it would be perfect!
EDIT 2 : as it's not clear for everyone I would detail here what I want for my extension :
- simple click (left click) on the icon get the current URL and send it to a mail address (OK for this)
- simple click ONLY DO THIS. This extension aims to be very very simple !
- right click on the icon shows Firefox's context-menu and I want to add "Options" here to show my options page
- Addon Manager could have a "Options" button near "Deactivate" and "Debug" and I want this option button to show my options page.
=> 2 way to see my option page : by the right click or by the addon manager and this is why I need your help !
General UI comments
Using right-click to directly activate your function is contrary to the general UI that is used system wide. Right-click is, system-wide (on some systems), used to open the context menu. In Firefox in the toolbar this is used to bring up the context menu for the toolbar area. This is what your users are generally going to expect to happen when they use right-click. You are probably better off either using something like shift-left-click, or letting the user define what combination is to be used to activate your function. If it is that you are attempting to add an option to the context menu, then that would normally be accessed via right-click.
Alternatives used in other add-ons:
A second section to your button with a down arrow. Clicking on the down arrow opens an expanded action or options menu.
Use the tooltip to display an action or options menu when the mouse is hovered over your button. This is done by creating a custom tooltip by enclosing the popup within a <tooltip id="myTooltip"></tooltip> element and referencing it in the button with <button tooltip="myTooltip"/> (tooltip property, tooltip attribute).
Using right-click
The problem appears to be that the Add-on SDK ActionButton has abstracted away your ability to have listeners for arbitrary events. It also does not give you access to the actual event object which is normally passed to event handlers (listeners). Further, its click event appears to actually be a command event, not a click event. One of the significant differences between a click and a command event is that the command event does not normally fire on a right-click.
You are going to need to gain access to the button outside of the ActionButton interface and add a listener for click events and then in your click event handler, you can make a choice to perform your action based on the state of event.button and event.shiftKey.
Adapting some code based on what I posted as an answer for a different question, you are going to want something like (not tested with modifications):
function loadUi(buttonId) {
if (window === null || typeof window !== "object") {
//If you do not already have a window reference, you need to obtain one:
// Add a "/" to un-comment the code appropriate for your add-on type.
/* Add-on SDK:
var window = require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
/* Overlay and bootstrap (from almost any context/scope):
var window=Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
//*/
}
forEachCustomizableUiById(buttonId, loadIntoButton, window);
}
function forEachCustomizableUiById(buttonId ,func, myWindow) {
let groupWidgetWrap = myWindow.CustomizableUI.getWidget(buttonId);
groupWidgetWrap.instances.forEach(function(windowUiWidget) {
//For each button do the load task.
func(windowUiWidget.node);
});
}
function loadIntoButton(buttonElement) {
//Make whatever changes to the button you want to here.
//You may need to save some information about the original state
// of the button.
buttonElement.addEventListener("click",handleClickEvent,true);
}
function unloadUi(buttonId) {
if (window === null || typeof window !== "object") {
//If you do not already have a window reference, you need to obtain one:
// Add a "/" to un-comment the code appropriate for your add-on type.
/* Add-on SDK:
var window = require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
/* Overlay and bootstrap (from almost any context/scope):
var window=Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
//*/
}
forEachCustomizableUiById(buttonId, unloadFromButton, window);
}
function unloadFromButton(buttonElement) {
//Return the button to its original state
buttonElement.removeEventListener("click",handleClickEvent,true);
}
function handleClickEvent(event) {
If( (event.button & 2) == 2 && event.shiftKey){
event.preventDefault();
event.stopPropagation();
doMyThing();
}
}
function doMyThing() {
//Whatever it is that you are going to do.
}
As should be implied by the above code, you will need to make sure to remove your listener when uninstalling/disabling your add-on. You will also want to make sure that loadUi() gets called when a new window opens so that your handler is added to the new button.
Adding to the context menu
There is no direct way to change the context menu just for your icon. The ID for the context menu is toolbar-context-menu. What you can do is add items to the context menu which are normally hidden="true". When you get the event that the right-click has happened on your icon you can change the state of hidden for those items you added. Then in an event handler that is called on the the popuphidden event for the context menu (<menupopup id="toolbar-context-menu">) you can set the state of hidden="true" for the <menuitem> element(s) which you have added to the <menupopup id="toolbar-context-menu"> in each browser window.
Something along the lines of:
function loadIntoContextMenu(win){
let doc = win.ownerDocument;
let contextPopupEl = doc.getElementById("toolbar-context-menu");
contextPopupEl.insertAdjacentHTML("beforeend",
'<menuitem label="My Item A" id="myExtensionPrefix-context-itemA"'
+ ' oncommand="doMyThingA();" hidden="true" />'
+ '<menuitem label="My Item B" id="myExtensionPrefix-context-itemB"'
+ ' oncommand="doMyThingB();" hidden="true" />');
contextPopupEl.addEventListener("popuphidden",hideMyContextMenuItems,true);
}
function unloadFromContextMenu(win){
let doc = win.ownerDocument;
let contextPopupEl = doc.getElementById("toolbar-context-menu");
let itemA = doc.getElementById("myExtensionPrefix-context-itemA");
let itemB = doc.getElementById("myExtensionPrefix-context-itemB");
contextPopupEl.removeChild(itemA);
contextPopupEl.removeChild(itemB);
contextPopupEl.removeEventListener("popuphidden",hideContextMenuItems,true);
}
function setHiddenMyContextMenuItems(element,text){
//The element is the context menu.
//text is what you want the "hidden" attribute to be set to.
let child = element.firstChild;
while(child !== null){
if(/myExtensionPrefix-context-item[AB]/.test(child.id)){
child.setAttribute("hidden",text);
}
child = child.nextSibling;
}
}
function showContextMenuItems(event){
//The target of this event is the button for which you want to change the
// context menu. We need to find the context menu element.
let contextmenuEl = event.target.ownerDocument
.getElementById("toolbar-context-menu");
setHiddenMyContextMenuItems(contextmenuEl,"false");
}
function hideContextMenuItems(event){
//This is called for the popuphidden event of the context menu.
setHiddenMyContextMenuItems(event.target,"true");
}
//Change the handleClickEvent function in the code within the earlier section:
function handleClickEvent(event) {
If( (event.button & 2) == 2){
//don't prevent propagation, nor the default as the context menu
// showing is desired.
showContextMenuItems(event);
}
}
Again, I have not tested this. It should demonstrate one way to accomplish what you desire.
However, given that we are talking about the context-menu, it is probably better to use the contextmenu event rather than the click event and testing for a right-click. In which case, we would change some of the functions above to be the following:
function loadIntoButton(buttonElement) {
//Make whatever changes to the button you want to here.
buttonElement.addEventListener("contextmenu",handleContextmenuEvent,true);
}
function handleContextmenuEvent(event) {
showContextMenuItems(event);
}
You can obtain each open primary browser window through the use of nsIWindowMediator. The following function, from MDN, will run the function you pass to it once for each open window:
Components.utils.import("resource://gre/modules/Services.jsm");
function forEachOpenWindow(todo) // Apply a function to all open browser windows
{
var windows = Services.wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
todo(windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow));
}
}
In the Add-on SDK:
function forEachOpenWindow(todo) // Apply a function to all open browser windows
var windows = require("sdk/windows");
for (let window of windows.browserWindows) {
todo(windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow));
}
}
You can add a listener which calls loadIntoContextMenu for new windows with the following code (also from MDN):
Components.utils.import("resource://gre/modules/Services.jsm");
Services.wm.addListener(WindowListener);
var WindowListener =
{
onOpenWindow: function(xulWindow)
{
var window = xulWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindow);
function onWindowLoad()
{
window.removeEventListener("load",onWindowLoad);
if (window.document.documentElement.getAttribute("windowtype") == "navigator:browser"){
loadIntoContextMenu(window);
//It would be better to only do this for the current window, but
// it does not hurt to do it to all of them again.
loadUi(buttonId);
}
}
window.addEventListener("load",onWindowLoad);
},
onCloseWindow: function(xulWindow) { },
onWindowTitleChange: function(xulWindow, newTitle) { }
};
I have implemented a menu-button that has a primary and secondary action. Although it isn't right/left click, the button has two sides:
This allows you to associate two different actions with your button without altering the usual context menu flow of Firefox. Download the files on GitHub and store them in your lib folder.
Usage is similar to other button types. Include the following code in main.js (or any js file in the lib directory)
const { MenuButton } = require('./menu-button');
var btn = MenuButton({
id: 'my-menu-button',
label: 'My menu-button',
icon: {
"16": "./firefox-16.png",
"32": "./firefox-32.png"
},
onClick: click
});
The click function will be passed the same state object as the toggle and action buttons, and will be passed an additional boolean argument: isMenu. It should be used like so
function click(state, isMenu) {
if (isMenu) {
//menu-button clicked
} else {
//icon clicked
}
}
I tried your extension on Chrome after answering this question and see that my answer probably isn't what you're looking for, so I'll make a different suggestion (leaving the other answer up because I think it is useful for people looking for multiple actions on a single button).
One thing I would say is that (some) Chrome users know that the Options menu item refers to the extension and not browser options. Those users know that the menu item is there, and use it to change their extension settings. Firefox users wouldn't expect that to be the case, because the context menu actions all affect the browser, not the extension. In the same way, (some) Firefox users know that to change their extension settings, they must navigate to about:addons (or Tools/Addons) and click the Preferences button next to the extension. This is the expected route to changing your preferences. So I would argue that adding a context-menu option is very complicated and not a good solution.
Instead, if your users haven't yet set their preferences, I think you should do what you already do in Chrome: create a Panel, associate it with your button (by using position: button in the panel constructor), and tell your users that they need to set their preferences by navigating to Tools/Addons. If you use the simple prefs module, a Preferences button will appear next to your extension and the options that you set in your package.json will be editable there.
Unfortunately, this is a very basic page, and won't look like the nice HTML options page you made.
Bonne chance.
Besides all the reservations in the other answers and without reusing the existing toolbar contextmenu, here is how:
const utils = require('sdk/window/utils');
const window = utils.getMostRecentBrowserWindow();
const doc = window.document;
const XUL_NS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
const { getNodeView } = require("sdk/view/core");
let ButContext = doc.createElementNS(XUL_NS,'menupopup');
ButContext.setAttribute("id","customIDString");
menuitem = doc.createElementNS(XUL_NS,"menuitem");
menuitem.setAttribute("label", "customLabelString");
ButContext.appendChild(item);
var myBut = require("sdk/ui/button/action").ActionButton({
id: "myButton",
label: "Right click me!"
// And other options
});
//Here is the context magic
getNodeView(myBut).setAttribute('context', "customIDString");
//either ; gets removed together with the button
getNodeView(myBut).appendChild(ButContext);
//or ; the correct place but needs to be removed manually
doc.getElementById("mainPopupSet").appendChild(ButContext);

What is the correct way to load a linked page, execute an event, and then unload it in a Chrome extension?

I have a Chrome extension that allows you to download data from certain sites.
For the past few days, I have attempted to add a feature that will download data from the sites when they are linked from other sites without having to visit the page and click the addon created download button IF the link to the site ends with #ndslink.
I figured out a solution, but it is INCREDIBLY sloppy and I am looking for a better way to implement this.
Here's the behavior:
Site A links Site B with an href ending in #nddownload. The link is clicked.
The extension disables the default action (open a new window), and instead creates an iframe on Site A and loads the linked URL into the frame.
The extension now runs Site B's content script, which specifically looks for #nddownload in the url, and, when found, proceeds to download some data that would normally be downloaded through a manual "Download" button added onto the page via the extension.
Here is my code.
Site B's content script:
var decklist = [];
$('.col-name').each(function(i, el) {
// IRRELEVANT CODE TRIMMMED
}
});
var data = decklist.join("\r\n");
var saveData = (function () {
// IRRELEVANT CODE TRIMMMED
}());
$(document).ready(function(){
var html = $('.t-deck-title').html();
fileName = $('.t-deck-title').text() + '.txt';
//html = html.replace(/hearthstonedeckdl/, '</br><a class="download" href="#download">DOWNLOAD</a>');
$('.t-deck-title').html(html);
if (window.location.href.indexOf("#ndslink") > -1) {
saveData(data, fileName);
}
$(document).on('click', 'a[href="#download"]', function(){
saveData(data, fileName);
});
});
Script loaded on all URLs (incl Site A):
var $jg = jQuery.noConflict();
$jg(document).ready(function(){
$jg('a[href$="#ndslink').click(function(e) {
e.preventDefault();
});
$jg(document).on('click', 'a[href$="#ndslink"]', function(){
//saveData(data, fileName);
var frameurl = $jg(this).text();
$jg('body').prepend('<iframe id="nddownload" />');
$jg("#nddownload").attr("src", frameurl);
//e.preventDefault();
// Send link to background and download.
});
});
I feel as if I am committing a horrible sin by going this route, but being new to Chrome extensions and generally being inexperienced I could not find a proper way to handle this. I've probably also broken my extension 10 different ways while attempting to do this so a real solution would be much appreciated.
I'm also actually unsure how to go about properly destroying the iframe once the saveData function completes.

Categories