document.execCommand(‘cut’/‘copy’) was denied in bookmarklet - javascript

I am working on a bookmarklet that makes an href link of the current browser tab and copies it to the clipboard. This bookmarklet works in Safari:
javascript:
!function(a){
var%20b=document.createElement("textarea"),
c=document.getSelection();
b.textContent=a,document.body.appendChild(b),
c.removeAllRanges(),b.select(),
document.execCommand("copy"),
c.removeAllRanges(),
document.body.removeChild(b)}
('<a%20title="'+document.title+'"%20href="'+document.location.href+'">'+document.title+'</a>');
But in Firefox 65, I get the error "document.execCommand(‘cut’/‘copy’) was denied because it was not called from inside a short running user-generated event handler." In looking at Copying to clipboard with document.execCommand('copy') fails with big texts I'm trying to generate the html of the link before the function to solve the issue pointed out in the answer. But, with the code below, I get a new browser tab with the text "true" and no copied link to the clipboard.
javascript:
const text = ('<a%20title="'+document.title+'"%20href="'+document.location.href+'">'+document.title+'</a>');
!function(a){
var%20b=document.createElement("textarea"),
c=document.getSelection();
b.textContent=a,document.body.appendChild(b),
c.removeAllRanges(),
b.select(),
document.execCommand("copy"),
c.removeAllRanges(),
document.body.removeChild(b)}('text');
Is this a timing issue with the generation of the href link? Or something else?

Your problem is not the same than in the other Q/A: In your case, you don't have any user-triggered event.
So no, it is not a timing issue, it's just that you need such an event.
To force it, you could show a splash screen, requiring that the bookmarklet's user clicks on the page. From this click event you'd call execCommand('copy').
javascript:(function(a){
var splash = document.createElement('div'),
msg = document.createElement('span');
splash.style='position:fixed;top:0;left:0;width:100vw;height:100vh;display:flex;justify-content:center;align-items:center;background:#FFF;z-index:999999';
msg.textContent = 'click me';
splash.append(msg);
// wait for the click event
splash.onclick = evt => {
var b=document.createElement("textarea"),
c=document.getSelection();
b.textContent=a,
document.body.appendChild(b),
c.removeAllRanges(),
b.select(),
document.execCommand("copy"),
c.removeAllRanges(),
document.body.removeChild(b),
document.body.removeChild(splash);
};
document.body.append(splash);
})
Here is a live example of what happens (obviously not as a bookmarklet):
(function(a){
var splash = document.createElement('div'),
msg = document.createElement('span');
splash.style='position:fixed;top:0;left:0;width:100vw;height:100vh;display:flex;justify-content:center;align-items:center;background:#FFF;z-index:999999';
msg.textContent = 'click me';
splash.append(msg);
// wait for the click event
splash.onclick = evt => {
var b=document.createElement("textarea"),
c=document.getSelection();
b.textContent=a,
document.body.appendChild(b),
c.removeAllRanges(),
b.select(),
document.execCommand("copy"),
c.removeAllRanges(),
document.body.removeChild(b),
document.body.removeChild(splash);
};
document.body.append(splash);
})
('<a%20title="'+document.title+'"%20href="'+document.location.href+'">'+document.title+'</a>');
<textarea>You can paste here to check what's been copied</textarea>

Related

How can I select the innerHTML when right-clicking on an e-mail link?

I am creating a Chrome Extension similar to the "Search on Google" when you right click on a selected text. However, I need mine to also work when right clicking on a mailto: e-mail link. How can I select the innerHTML, to select the e-mail address, and pass this information onto the extension to be searched?
I managed to make it work with the selected text (when highlighting text on the website) and right-clicking, but not when right-clicking on a hyperlinked e-mail address.
for(var i=0; i<numentries; i++)
{
//alert(_all[i][3]);
if(_all[i][3])
{
_all[i][0] = chrome.contextMenus.create({"title": _all[i][1], "contexts":["selection", "link"], "onclick": searchOnClick});
//alert("Menuitem created");
}
else _all[i][0] = -1;
}
var ask_options = getItem("_askoptions")=="true"? true : false;
if(ask_options){
//show separator
chrome.contextMenus.create({"type": "separator", "contexts":["selection", "link"]});
//show the item for linking to extension options
chrome.contextMenus.create({"title": "Options", "contexts":["selection", "link"], "onclick": function(){chrome.tabs.create({"url":"options.html"});}});
}
}
function searchOnClick(info, tab)
{
var itemindex = 0;
for(var i=0; i<numentries; i++)
{
if(info.menuItemId == _all[i][0])
{
//alert(i);
itemindex = i;
}
}
var ask_fg = getItem("_askbg")=="true"? false : true;
var ask_next = getItem("_asknext")=="true"? true : false;
var index = 1000;
var targetURL = _all[itemindex][2].replace("TESTSEARCH", info.selectionText);
targetURL = targetURL.replace("%s", info.selectionText);
Right now, it's only searching for the selection. When I attempt to search for a e-mail address hyperlink, the searched word is "undefined".
I need to change "undefined" to the e-mail address in the hyperlink.
Here is what I need to happen: https://i.imgur.com/2qJrwmk.png
You need to add an event listener for the contextmenu.
Using the example cat gave, I created a quick jsfiddle:
https://jsfiddle.net/kds2Lze8/
The code below adds the event listener to the document and is triggered on right click. Using that event you can then get the source element and ultimately the innerHTML.
Hope it helps!
document.addEventListener('contextmenu', function(ev) {
ev.preventDefault();
alert(ev.srcElement.innerHTML);
return false;
}, false);
I'm not sure about some of the Chrome-extension-specific stuff (and your snippet is giving an error that I had trouble debugging without your HTML markup), but I think this script will demonstrate how to do what you want.
Edit:
You did indeed say you wanted to know how to run the script in response to a right-click, but I omitted that part. Sorry about that. The revised version should clarify that. It logs the innerHTML of the clicked element (although not on left-clicks) if the element is an anchor whose href attribute starts with mailto:.
// Run the 'checkAnchorForEmail' function on non-primary click events
document.addEventListener("auxclick", checkAnchorForEmail);
function checkAnchorForEmail(event){ //'event' will be our local name for any event that triggers this function
// Refer to the event's target element as 'clickedElement'
let clickedElement = event.target;
// If the element was an anchor with an 'href' attribute...
if(clickedElement.tagName.toLowerCase() === "a" && clickedElement.href){
// Define a string to identify anchors with emails
let comparedString = "mailto:";
// Only respond if the href begins with that particular string
if(clickedElement.href.indexOf(comparedString) === 0){
// Now we know the user right-clicked* an `a` element with an email address and can act accordingly
console.log(clickedElement.innerHTML);
}
}
}
// *Note that the 'auxclick' event is triggered by any non-primary button click. To isolate right-clicks, the 'contextmenu' event may be useful.
test#google.com<br />
google docs<br />
test2#google.com
One other thing:
If you need to prevent the context menu from appearing until your script has completed its tasks, you can use event.preventDefault();, but then you would need to show the menu manually later. One way to do this is by firing the 'contextmenu' event on the target element.
It's possible that doing so would cause this script to run again, creating an infinite loop. If this happens, you might try calling the preventDefault method conditionally like this (untested):
function checkAnchorForEmail(event){
// The above code goes here...
// Now we know the user right-clicked* an `a` element with an email address and can act accordingly
if(event.target.dataset.ready != "true"){ // Check the data-ready attribute
// event.preventDefault();
// event.target.dataset.ready = "true" // Set the data-ready attribute
// Make your changes to the context menu here
}
else{
// The changes have already been made, so show the context menu here
// (maybe using a technique like the one in the link below)
}
}
Here is a suggestion for using the MouseEvent interface to open the context menu, as mentioned in the in-code comments.

Is This Possible in a Browser? [JavaScript / HTML]

I have Javascript code that opens ISBNs of books on Amazon using hyperlinks (feel free to try, for example: 0133098648). I would like the URL to open up on a new window with all links clicked in a new tab on that same window. It appears one can only open in a new tab of the current window, or a new window every time.
Is what I am looking to do even possible? I've been reading that something like this is restricted by browsers for security reasons; Maybe there's a work around? I've been pulling my hair out trying to find a solution for this, if I could it would make my life much more easier.
A picture to describe my question: http://imgur.com/a/5lUP4
Please use JSfiddle example: http://jsfiddle.net/mq1efed2 ( Code does not work on Stackoveflow)
<html>
<div><b>ISBN Hyperlinker</b></div>
<textarea id=numbers placeholder="paste isbn numbers as csv here" style="width:100%" rows="8" >
</textarea>
<div><b>Hyperlinked text:</b></div>
<div id="output" style="white-space: pre"></div>
<script>
//the input box.
var input = document.getElementById('numbers');
var output = document.getElementById('output')
var base =
'https://www.amazon.ca/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords='
//adding an event listener for change on the input box
input.addEventListener('input', handler, false);
//function that runs when the change event is emitted
function handler () {
var items = input.value.split(/\b((?:\d\s*?){10,13})\b/gm);
// Build DOM for output
var container = document.createElement('span');
items.map(function (item, index) {
if (index % 2) { // it is the part that matches the split regex:
var link = document.createElement('a');
link.textContent = item.trim();
link.setAttribute('target', '_blank');
link.setAttribute('href', base + item.replace(/\D+/g, ''));
container.appendChild(link);
} else { // it is the text next to the matches
container.appendChild(document.createTextNode(item))
}
});
// Replace output
output.innerHTML = '';
output.appendChild(container);
}
handler(); // run on load
</script>
</html>
No, this isn't possible. Web pages cannot dictate whether or not content opens up in tabs or new windows. It's up to the user/browser to decide.
At best, you can open a new window with window.open(), but that doesn't give you control over tabs in a specific window later.

CustomEvent() to an XUL tab - Firefox addon

For many reasons, I have to open up XUL in a tab, instead of using a standard window. I wish to send custom events to this tab, and here's how my code looks like :
myextension.js :
..
var pTab = window.gBrowser.loadOneTab("chrome://myextension/path/options.xul",
{inBackground: false});
var pWin = window;
var event = new pWin.CustomEvent("prefwindow-event");
pWin.dispatchEvent(event);
..
options.xul code:
window.addEventListener("load", init, false);
window.addEventListener("prefwindow-event", myevent, false, true);
..
myevent: function(e) {
dump("My event : " + e.details );
},
..
However, I don't get the event. I have tried all possible options. Enabled/Disabled useCapture and wantsUntrusted of addEventListener(). After realizing that there are restrictions in sending custom events between windows, I also tried dispatching event with the tab element, like this :
pTab.dispatchEvent(event);
That wouldn't work either. 1) The event dispatch would work perfectly fine if I use a dialog window instead of a tab (openDialog instead of loadOneTab). 2) The tab element only inherits dispatchEvent and not a CustomEvent
So the question is, is there a way to send custom events and have it received in a tab?
var event = new pTab.linkedBrowser._contentWindow.CustomEvent("prefwindow-event");
edit:
Proof of concept, open scratchpad, switch environment to browser, run the following snippet
var url = 'data:text/html;charset=utf-8,';
var content = '<html><script>addEventListener("Hello",function(){alert("Hello")},false)</script></html>'
var tab = gBrowser.loadOneTab(url+encodeURIComponent(content), {inBackground: false});
tab.addEventListener("load", function(){
var evt = new tab.linkedBrowser.contentWindow.CustomEvent("Hello");
tab.linkedBrowser.contentWindow.dispatchEvent(evt);
}, false);

Why doesn't noautohide work in an XUL panel?

I'm developing a Firefox extension, trying to get a panel to overlay a browser element. Here is my javascript code:
var panel = oldTabBrowser.contentDocument.createElement('panel');
panel.setAttribute('noautohide','true');
var label = oldTabBrowser.contentDocument.createElement('label');
label.setAttribute('value','my text');
panel.appendChild(label);
elem.appendChild(panel);
panel.openPopup(elem, "overlap",0,0);
alert(panel.getAttribute('noautohide'));
The noautohide attribute is set fine and the panel appears as expected, but on clicking anywhere, the panel disappears. What am I missing?
I have a strong suspicion that noautohide attribute doesn't work correctly. At least for <xul:tooltip> the result isn't the one I expected. You can however make sure that your panel doesn't close prematurely using popuphiding event:
var canClose = false;
panel.addEventListener("popuphiding", function(event)
{
if (!canClose)
{
// Too early to close, prevent it
event.preventDefault();
}
}, false);
panel.openPopup(elem, "overlap",0,0);
...
// Now it is ok to close
canClose = true;
panel.hidePopup();

jquery thickbox reference problem

Completely restated my question:
Problem: Losing reference to an iFrame with Mozilla firefox 3.6 and 4.0
More info:
- Works fine in internet explorer 8 64-bit and 32-bit version.
How to reproduce? In Mozilla: Open the editor accordion menu. Click the 'editor openen' link, in the editor fill in some random text, then click 'bestand opslaan'. Fill in a name and click on 'save'. The content of the editor will be downloaded in HTML format.
Close the save file dialog box by clickin outside of it or on the specified buttons. Click on the 'bestand opslaan' button again and try to save your content to a file. You'll see nothing happening.
The problem isn't there in IE8. Try opening it in there.
Firebug tells me this the second time you open the save dialog:
iFrame.document is null
Example Link: http://www.newsletter.c-tz.nl/
More info:
- switched from thickbox to colorbox to try and resolve this issue and because thickbox isn't supported for a long time now.
- colorbox gives me the same problem so I don't think it is this.
- tried googling for iframe reference error and like, found nothing.
- tried putting the iframe code outside of the div that is called by the colorbox script, it retains it reference but not when I put it back inside that div.
Thanks to: JohnP who offered to open a 'hunt' on this.
Edit:
I thought maybe the saveFile.php file was causing trouble to the parent of the iframe but after removing it from the action variable in the editor.php script it still fails with the same error after you open the dialog for a second time.
Can someone maybe write a script that iterates through iframes by name and when the rignt iframe is found reference it to a var? I want to try it but don't know how..
I can't explain why it's work the first time for Firefox, but in Firefox the function to used for get iframe is different of IE : How to get the body's content of an iframe in Javascript?.
So, replace your JavaScript function "saveToFile" to this :
function saveToFile() {
var saveAsFileName = document.getElementById('saveAs_filename').value;
var currentContent = tinyMCE.editors["editor_textarea"].getContent();
var editorFileName = document.getElementById('editor_filename');
var iFrameTag = document.getElementById('saveAs_Iframe');
var iFrame;
if ( iFrameTag.contentDocument )
{ // FF
iFrame = iFrameTag.contentDocument;
}
else if ( iFrame.contentWindow )
{ // IE
iFrame = iFrameTag.contentWindow.document;
}
var inframeEditorFileName = iFrame.getElementById('editor_filename');
var inframeEditorContent = iFrame.getElementById('editor_textarea');
editorFileName.value = saveAsFileName;
inframeEditorFileName.value = saveAsFileName;
inframeEditorContent.value = currentContent;
iFrame.editor_self.submit();
}
I replace the function with Firebug and it's works for me.
Update :
You can also used a crossbrowser solution, more simple, thanks to jQuery :
function saveToFile() {
var saveAsFileName = document.getElementById('saveAs_filename').value;
var currentContent = tinyMCE.editors["editor_textarea"].getContent();
var editorFileName = document.getElementById('editor_filename');
editorFileName.value = saveAsFileName;
$("#saveAs_Iframe").contents().find("#editor_filename").val(saveAsFileName)
$("#saveAs_Iframe").contents().find("#editor_textarea").val(currentContent)
$("#saveAs_Iframe").contents().find("form[name=editor_self]").submit();
}

Categories