Why document.execCommand('paste') is not working in my extension - javascript

I am creating a Google chrome extension which can read the contents of clipboard.
But I am unable to get the documentation for this. I want to get the clipboard content as in IE's clipboard API.
In the manifest file i gave permissions to
clipboardRead and clipboardWrite.
I have created a function in Background page as below
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method == "getClipData")
sendResponse({data: document.execCommand('paste')});
else
sendResponse({}); // snub them.
});
And in Content Script I am calling the function like this
chrome.extension.sendRequest({method: "getClipData"}, function(response) {
alert(response.data);
});
But this returns me undefined...

document.execCommand('paste') returns success or failure, not the contents of the clipboard.
The command triggers a paste action into the focused element in the background page. You have to create a TEXTAREA or DIV contentEditable=true in the background page and focus it to receive the paste content.
You can see an example of how to make this work in my BBCodePaste extension:
https://github.com/jeske/BBCodePaste
Here is one example of how to read the clipboard text in the background page:
bg = chrome.extension.getBackgroundPage(); // get the background page
bg.document.body.innerHTML= ""; // clear the background page
// add a DIV, contentEditable=true, to accept the paste action
var helperdiv = bg.document.createElement("div");
document.body.appendChild(helperdiv);
helperdiv.contentEditable = true;
// focus the helper div's content
var range = document.createRange();
range.selectNode(helperdiv);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
helperdiv.focus();
// trigger the paste action
bg.document.execCommand("Paste");
// read the clipboard contents from the helperdiv
var clipboardContents = helperdiv.innerHTML;
If you want plain-text instead of HTML, you can either use helperdiv.innerText, or you can switch to using a textarea. If you want to parse the HTML in some way, you can walk the HTML dom inside the DIV (again, see my BBCodePaste extension)

var str = document.execCommand('paste');
You will need to add the clipboardRead permission too.

We cant access clipboard from javascript instead IE for chrome and other browsers.
The hack for this is very simple: create own custom clipboard which store text on cut and from where we paste it directly
function copy(){
if (!window.x) {
x = {};
}
x.Selector = {};
x.Selector.getSelected = function() {
var t = '';
if (window.getSelection) {
t = window.getSelection();
} else if (document.getSelection) {
t = document.getSelection();
} else if (document.selection) {
t = document.selection.createRange().text;
}
return t;
}
var mytext = x.Selector.getSelected();
document.getElementById("book").innerHTML =mytext;
}
function cut(){
if (!window.x) {
x = {};
}
x.Selector = {};
x.Selector.getSelected = function() {
var t = '';
if (window.getSelection) {
t = window.getSelection();
} else if (document.getSelection) {
t = document.getSelection();
} else if (document.selection) {
t = document.selection.createRange().text;
}
return t;
}
var mytext = x.Selector.getSelected();
document.getElementById("book").innerHTML =mytext;
x.Selector.setSelected()="";
}
function paste()
{
var firstDivContent = document.getElementById('book');
var secondDivContent = document.getElementById('rte');
secondDivContent.innerHTML += firstDivContent.innerHTML;
rte.focus();
}
function clear()
{
document.getElementById('rte').innerHTML="";
rte.focus();
}
<button id="cut"onclick="cut();">Cut</button>
<button id="copy"onclick="copy();">Copy</button>
<button id="paste"onclick="paste();">Paste</button>
Working Div
<div id="rte" contenteditable="true" style="overflow:auto;padding:10px;height:80vh;border:2px solid black;" unselectable="off" ></div>
Own Clipboard(hack)
<div id="book" contenteditable="true"style="background-color:#555;color:white;"> </div>

Related

Why copy to clipboard doesn't work using JavaScript?

I'm trying this function:
function copyToClipboard(str) {
const el = document.createElement('textarea');
el.textContent = str;
el.setAttribute('readonly', '');
el.style.position = 'absolute';
el.style.left = '-9999px';
document.body.appendChild(el);
const selected =
document.getSelection().rangeCount > 0 ?
document.getSelection().getRangeAt(0) :
false;
el.select();
document.execCommand('copy');
document.body.removeChild(el);
if (selected) {
document.getSelection().removeAllRanges();
document.getSelection().addRange(selected);
}
alert("Success");}
I've tried with el.value = str; too.
What am I doing wrong?
The document.execCommand has been deprecated but still accessible across web browsers
The navigator.clipboard API is an alternative navigator.clipboard
You pass in the text to be copied to the clipboard like so
navigator.clipboard.writeText(str_to_copy).then(success_callback, failure_callback);
Note that the tab must be active for this to work else you won’t have permissions to copy the clipboard
The API is asynchronous so you can use the .then callback to alert the user if copying the clipboard was successful or not. Check out the Can I Use for its availability across web browsers.
You are creating an element that will be appended to DOM for a split second, just to allow the execCommand("copy")... And that will "display" at left -9999px.
So why the el.setAttribute('readonly', ''); ?
Just remove it and try again. My guess is the el.select(); just doesn't work on a readonly element.
Disclaimer: I did test nothing. But from what I read, this is the only weird thing to mention. Else is to find a duplicate answer or mark it as unreproducible.
Use this way to copy the text to clipboard.
function copyToClipboradFunc() {
let copiedText = document.getElementById("copyMe");
copiedText.select();
copiedText.setSelectionRange(0, 99999);
document.execCommand("copy");
console.log("Copied the text: " + copiedText.value);
}
<input type="text" value="Amoos Check Console" id="copyMe">
<button onclick="copyToClipboradFunc()">Copy to Clipboard</button>
Minor edits in your code.
/*
YOUR CODE
*/
function copyToClipboard(str) {
const el = document.createElement('textarea');
el.textContent = str;
el.setAttribute('readonly', '');
el.style.position = 'absolute';
el.style.left = '-9999px';
document.body.appendChild(el);
const selected =
document.getSelection().rangeCount > 0 ?
document.getSelection().getRangeAt(0) :
false;
el.select();
document.execCommand('copy');
document.body.removeChild(el);
if (selected) {
document.getSelection().removeAllRanges();
document.getSelection().addRange(selected);
}
alert("Copy Text: " + str );}
<!-- YOUR CODE -->
<button onclick="copyToClipboard('Your Function Copied')">Copy ( Original Function )</button>
Well, after some research I found the solution. Thanks to #VLAZ and #a.mola I found out that execCommand is deprecated. So I started to look for alternatives. I found about the clipboard API on this page Using the Clipboard API, that's from https://developer.mozilla.org/, so we know that's serious business. Anyway, here's my working function:
function copyToClipboard(str) {
navigator.permissions.query({
name: "clipboard-write"
}).then(result => {
if (result.state == "granted") {
navigator.clipboard.writeText(str).then(function () {
alert("Enlace copiado con succeso!");
}, function () {
alert("No fue posible copiar el enlace.");
});
}
});
};
I am using this. navigator.clipboard doesnt work for http.
function CopyToClipBoard(textToCopy) {
var successMessage = 'Success! The text was copied to your clipboard';
var errorMessage = 'Oops! Copy to clipboard failed. ';
// navigator clipboard api needs a secure context (https)
if (navigator.clipboard && window.isSecureContext) {
// navigator clipboard api method'
navigator.clipboard.writeText(textToCopy).then(
function () {
/* clipboard successfully set */
console.log(successMessage)
},
function () {
/* clipboard write failed */
console.warn(errorMessage)
}
)
} else
if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
// text area method
var textarea = document.createElement("textarea");
textarea.value = textarea.textContent = textToCopy;
textarea.style.opacity = "0";
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
var selection = document.getSelection();
var range = document.createRange();
range.selectNode(textarea);
selection.removeAllRanges();
selection.addRange(range);
try {
var successful = document.execCommand('copy'); // Security exception may be thrown by some browsers.
var msg = successful ? console.log(successMessage) : console.warn(errorMessage);
}
catch (ex) {
console.warn(errorMessage, ex);
}
finally {
selection.removeAllRanges();
document.body.removeChild(textarea);
}
}

document.getSelection().toString() is always " " in ie while working with iframe

I wanted to get the selected text onmouseup.
I also tried
setTimeout(() => {
var selectedData = contentWrapper.getSelection().toString();
alert(selectedData)
}, 1008)
html code
<iframe src="url"></iframe>
js code
var contentWrapper=document.getElementsByTagName("iframe")[0].contentDocument;
contentWrapper.body.addEventListener('mouseup', this.CheckSelections, false);
function CheckSelections() {
var selectedData = contentWrapper.getSelection().toString();
alert(selectedData)
}
This way it works in IE, Chrome & FF (testing on local), but I am not able to touch iframe here.
function showSelection() {
var selectedData = document.getSelection().toString();
alert(selectedData)
}
var contentWrapper;
function attachIF() {
if(location.href == "https://stacksnippets.net/js") return;
var ifr = document.createElement("iframe");
ifr.src="javascript:'"+ // Access is denied. Here on stackoverflow.com
"<script>window.onload=function(){"+
"document.write(\\'<script>if(document.domain)document.domain=\\\""+document.domain+"\\\";"+
"document.write(\""+parent.document.body.innerText+"\");"+
"<\\\\/script>\\');"+
"document.close();"+
"parent.contentWrapper = document;"+
"document.body.onmouseup=parent.CheckSelections;"+
"};<\/script>'";
document.body.appendChild(ifr);
}
function CheckSelections() {
var selectedData = contentWrapper.getSelection().toString();
alert(selectedData)
}
<body onmouseup="showSelection()" onload=attachIF()>
<iframe src="https://stacksnippets.net/js"></iframe>
Some text to be selected.
</body>

Select text inside editable field in CKEditor widget [duplicate]

I have made a plugin for CKEditor, but it relies on the currently selected text.
In FF and Chrome I can use:
var selectedText = editor.getSelection().getNative();
but this doesn't work in IE and I only get [object Object]
Any suggestions?
This is what I use:
var mySelection = editor.getSelection();
if (CKEDITOR.env.ie) {
mySelection.unlock(true);
selectedText = mySelection.getNative().createRange().text;
} else {
selectedText = mySelection.getNative();
}
Use:
editor.getSelection().getSelectedText();
Or:
CKEDITOR.instances["txtTexto"].getSelection().getSelectedText()
"txtTexto" = ID of textarea tag
To those who want to prefill fields with a selection, just do it like that and safe yourself a long journey.
onShow: function() {
this.setValueOf( 'tab-id', 'field-id', editor.getSelection().getSelectedText().toString() );
},
Have a nice day!
In the newer versions of CKEDITOR, there seems to be a way easier method:
var selectedHTML = editor
.getSelectedHtml()
.getHtml(); //result: <p>test</p>
#TheApprentice
You put it like this:
( function(){
var getSelectedText = function(editor) {
var selectedText = '';
var selection = editor.getSelection();
if (selection.getType() == CKEDITOR.SELECTION_TEXT) {
if (CKEDITOR.env.ie) {
selection.unlock(true);
selectedText = selection.getNative().createRange().text;
} else {
selectedText = selection.getNative();
}
}
return(selectedText);
}
...
with a call like this:
onShow: function() {
// Get the element currently selected by the user
var editor = this.getParentEditor();
var selectedContent = getSelectedText(editor);

Cannot Pass Callback Function On Mouseup Event - JavaScript

I'm trying to pass a callback function as an argument in a previous function that is triggered by a mouseup in a chrome extension. So basically I'm aiming to trigger the quickTranslate function right after the lwsGetText finishes running. But I can't figure out how to do this since the function lwsGetText is triggered by a mouseup. Here's my code:
Content Script (js)
(function () {
// Holds text being selected in browser
var lwsSelectedText = '';
// Adds pop-up to current webpage
function lwsAddContent(callback) {
// Get body tag
var body = document.getElementsByTagName('body');
// add invisible div
document.body.innerHTML += '<div id="myModal" class="modal"><div class="modal-content"><span class="close">×</span><div id="lwsSpanishDiv"><p id="lwsSpanishTitle">Spanish</p><textarea id="lwsSpanishTextArea">Hello</textarea></div><div id="lwsEnglishDiv"><p id="lwsEnglishTitle">English</p><textarea id="lwsEnglishTextArea">Hello 2</textarea></div></div></div>';
callback(lwsSetUpTextGetter);
}
// Make the pop-up visible and set up close button
function lwsActivateContent(callback) {
var modal = document.getElementById('myModal');
// Get the textarea
var txtarea = document.getElementById("myTxtArea");
// Get the <span> element that closes the modal
var span = document.getElementsByClassName("close")[0];
span.onclick = function () {
modal.style.display = "none";
}
callback(quickTranslate);
}
// Initialize ability to select and grab text from browser
function lwsSetUpTextGetter(callback) {
//Set the onmouseup function to lwsGetText
document.onmouseup = lwsGetText;
//Handling clicking outside webpage?
if (!document.all) document.captureEvents(Event.MOUSEUP);
}
//Gets selected text
function lwsGetText(callback,e) {
// Get access to spanish text area
var spanishText = document.getElementById('lwsSpanishTextArea');
//Get text
lwsSelectedText = (document.all) ? document.selection.createRange().text : document.getSelection();
//if nothing is selected, do nothing
if (lwsSelectedText != '') {
// test: does browser grab text correctly?
alert(lwsSelectedText);
// Set spanish text area content to the selected text from browser
spanishText.value = lwsSelectedText;
// --Error Here--
callback();
}
}
function quickTranslate() {
alert("Quick Translate");
var url = "https://translate.yandex.net/api/v1.5/tr.json/translate",
keyAPI = "trnsl.1.1.20130922T110455Z.4a9208e68c61a760.f819c1db302ba637c2bea1befa4db9f784e9fbb8";
var englishTextArea = document.getElementById('lwsEnglishTextArea');
var spanishTextArea = document.getElementById('lwsSpanishTextArea');
englishTextArea.value = 'Working...';
var xhr = new XMLHttpRequest(),
textAPI = spanishTextArea.value,
langAPI = 'en';
data = "key=" + keyAPI + "&text=" + textAPI + "&lang=" + langAPI;
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(data);
xhr.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var res = this.responseText;
var json = JSON.parse(res);
if (json.code == 200) {
englishTextArea.value = json.text[0];
}
else {
englishTextArea.value = "Error Code: " + json.code;
}
}
}
}
// When document ready
$(document).ready(function () {
lwsAddContent(lwsActivateContent);
});
})();
If someone could help me figure out how to implement the quickTranslate function as a callback function, that would be awesome! Thanks!
As you know the event handler you're assigning takes one parameter, the event. However you can create a closure around the "callback" variable by doing something like:
function lwsGetText(callback) {
return function (e) {
// Get access to spanish text area
var spanishText = document.getElementById('lwsSpanishTextArea');
//Get text
lwsSelectedText = (document.all) ? document.selection.createRange().text : document.getSelection();
//if nothing is selected, do nothing
if (lwsSelectedText != '') {
// test: does browser grab text correctly?
alert(lwsSelectedText);
// Set spanish text area content to the selected text from browser
spanishText.value = lwsSelectedText;
// --Error Here--
callback();
}
};
}
You would then pass the value quickTranslate as the "callback" parameter when you set the handler, not sure how you're doing that so here is a guess:
myElement.addEventListener("mouseup", lwsGetText(quickTranslate));

Stopping content script in chrome extension

I have developed chrome extension , I have added ,
chrome.browserAction.onClicked.addListener
which will start the script once clicked on , the script in turn will add a div at the bottom of the web page on the tab the browser action is clicked ,
All I have to do is , I need to add a close link which will stop the content script and close the div at the bottom ,
I have tried windows.close() , self.close() but nothing seems to work ,I would atleast want it to work in a way that 2nd time some one clicks on browser action, the script should stop.
Here is my code,
background.js
chrome.browserAction.onClicked.addListener( function() {
chrome.tabs.executeScript( { file: 'myscript.js' } );
});
myscript.js
document.body.appendChild(div);
document.addEventListener("click",
function (e) {
e.preventDefault();
var check = e.target.getAttribute("id");
var check_class = e.target.getAttribute("class");
if(check=="ospy_" || check=="ospy_id" || check=="ospy_text" || check=="ospy_el" || check=="ospy_class" || check=="ospy_name" || check=="ospy_href" || check=="ospy_src"|| check=="ospy_wrapper"|| check=="ospy_style"|| check=="ospy_rx"|| check=="ospy_con"|| check_class=="ospy_td"|| check=="ospy_main_tab"|| check_class=="ospy_tab" || check_class=="ospy_ip"|| check_class=="ospy_lab")
{
}
else{
document.getElementById('ospy_id').value = "";
document.getElementById('ospy_class').value = "";
document.getElementById('ospy_el').value = "";
document.getElementById('ospy_name').value = "";
document.getElementById('ospy_style').value = "";
document.getElementById('ospy_href').value = "";
document.getElementById('ospy_text').value = "";
document.getElementById('ospy_src').value = "";
document.getElementById('ospy_con').value = "";
document.getElementById('ospy_').value = "";
document.getElementById('ospy_rx').value = "";
var dom_id=e.target.getAttribute("id");
// var dom_id = e.target.id.toString();
var dom_name = e.target.name.toString();
var dom_class = e.target.className.toString();
// var dom_class = this.class;
var dom_html = e.target.innerHTML;
var dom_href = e.target.getAttribute("href");
var dom_text = e.target.text;
var dom_el= e.target.tagName;
var dom_src= e.target.src;
//var XPATH = e.target.innerHTML;
var rel_xpath = "";
var field ="";
var field_value = "";
field="id";
field_value = dom_id;
rel_xpath = dom_el+"[#"+field+"='"+field_value+"']";
if(dom_id == null){
field="href";
field_value= dom_href;
//var rel_xpath = dom_el+"[contains(text(), '"+dom_text+"')]";
rel_xpath = dom_el+"[#"+field+"='"+field_value+"']";
if(dom_href==null || dom_href=="#")
{
field="src";
field_value= dom_src;
rel_xpath = dom_el+"[#"+field+"='"+field_value+"']";
//rel_xpath = "nope nothing";
if(dom_src==null)
{
var rel_xpath = dom_el+"[contains(text(), '"+dom_text+"')]";
if(dom_text=="")
{
field="class";
field_value= dom_class;
rel_xpath = dom_el+"[#"+field+"='"+field_value+"']";
}
}
}
}
var con_xpath = "";
var con_xpath = dom_el+"[contains(text(), '"+dom_text+"')]";
if(dom_text==null)
{
con_xpath = "NA";
}
var css ="color: ";
css += getComputedStyle(e.target).color;
css +="\nWidth: ";
css += getComputedStyle(e.target).width;
css +="\nHeight: ";
css += getComputedStyle(e.target).height;
css +="\nbg: ";
css += getComputedStyle(e.target).background;
css +="\nfont: ";
css += getComputedStyle(e.target).font;
css +="\nvertical-align: ";
css += getComputedStyle(e.target).verticalalign;
css +="\nmargin: ";
css += getComputedStyle(e.target).margin;
var node = getXPath(e.target.parentNode);
document.getElementById('ospy_id').value = dom_id;
document.getElementById('ospy_class').value = dom_class;
document.getElementById('ospy_el').value = dom_el;
document.getElementById('ospy_name').value = dom_name;
document.getElementById('ospy_style').value = css;
document.getElementById('ospy_href').value = dom_href;
document.getElementById('ospy_text').value = dom_text;
document.getElementById('ospy_src').value = dom_src;
document.getElementById('ospy_').value = node;
document.getElementById('ospy_rx').value =rel_xpath;
document.getElementById('ospy_con').value =con_xpath;
}},
false);
window.close() is for closing a window, so no wonder it does not work.
"Unloading" a content-script is not possible, but if you want to remove an element (e.g. your div) from the DOM, just do:
elem.parentNode.removeChild(elem);
(Whether you bind this behaviour to a link/button in your <div> or have the browser-action, trigger an event in the background-page that sends a message to the corresponding content-script which in turn removes the element is up to you. (But clearly the former is much more straight-forward and efficient.)
If you, also, want your script to stop performing some other operation (e.g. handling click events) you could (among other things) set a flag variable to false (when removing the <div>) and then check that flag before proceeding with the operation (e.g. handling the event):
var enabled = true;
document.addEventListener('click', function (evt) {
if (!enabled) {
/* Do nothing */
return;
}
/* I am 'enabled' - I'll handle this one */
evt.preventDefault();
...
/* In order to "disable" the content-script: */
div.parentNode.removeChild(div);
enabled = false;
Notes:
If you plan on re-enabling the content-script upon browser-action button click, it is advisable to implement a little mechanism, where the background-page sends a message to the content-script asking it to re-enable itself. If the content-script is indeed injected but disabled, it should respond back (to confirm it got the message) and re-enable itself. If there is no response (meaning this is the first time the user clicks the button on this page, the background-page injects the content script.
If it is likely for the content script to be enabled-disabled multiple times in a web-pages life-cycle, it would be more efficient to "hide" the <div> instead of removing it (e.g.: div.style.display = 'none';).
If you only need to disable event handler, instead of using the enabled flag, it is probably more efficient to keep a reference to the listener you want to disable and call removeEventListener().
E.g.:
function clickListener(evt) {
evt.preventDefault();
...
}
document.addEventListener('click', clickListener);
/* In order to "disable" the content-script: */
div.parentNode.removeChild(div);
document.removeEventListener('click', clickListener);

Categories