How to add some content when to paste copied lines? - javascript

I want to write a function that can add text into your clipboard and show the added text with your copied text when to paste it.
document.addEventListener('copy', function(e){
e.clipboardData.appendChild(document.createTextNode("hello,world"));
});
<p>I've included the basic site layout so we aren't wasting time creating the form </p>
<p>and making the site looks pretty.</p>
My try failed.
When you copy part of the second line ,for example looks pretty,and pasted it into a leafpad,it will show:
looks pretty hello,world
instead of
looks pretty

Read on this :
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard
This should work given that you have the permissions!
Note that permissions to navigator.clipboard are granted only for pages served over HTTPS.
document.addEventListener('copy', function(e){
appendToClip("+hello,world");
});
function updateClipboard(newClip) {
console.log('updateClipboard', newClip);
navigator.clipboard.writeText(newClip).then(function() {
/* clipboard successfully set */
}, function() {
/* clipboard write failed */
});
}
function appendToClip(text) {
console.log('appendToClip', text);
navigator.permissions.query({name: "clipboard-write"}).then(result => {
console.log(result.state);
// Listen for changes to the permission state
result.onchange = () => {
console.log(result.state);
if (result.state == "granted" || result.state == "prompt") {
navigator.clipboard.readText().then(
clipText =>
updateClipboard(clipText + text));
} else {
console.log('appendToClip: not allowed | ', result.state);
}
};
});
}
<div> test text </div>

Related

Can I emulate/provoke failure to copy to clipboard from JS in Chrome?

I have a web page with some JavaScript code that copies stuff to the clipboard similar to what this demo does: https://davidwalsh.name/demo/javascript-copy-clipboard.php
My code is something like this:
// Within a listener that is triggered by a click on some button:
var copiedText = "...something";
navigator.clipboard.writeText(copiedText).then(
function() {
console.log("Succesfully copied");
},
function() {
console.log("FAILED to copy!!!!!!");
}
);
This works fine, but it always succeeds. I need to test that the behavior is correct when copying fails for whatever reason.
How can I cause the copy to fail on purpose so that I can test the behavior of my code in that situation?
I never get a prompt asking me permission to write to the clipboard.
In Chrome, I have tried going to the site settings for the site, and under "Clipboard" selecting "Block", but it does nothing (I guess that's only for reading from the clipboard).
Sure, just provide an invalid argument which will throw an exception during the stringification algorithm:
Note that this demo will work in your own page, but the success case won't work in the Stack Overflow code snippet iframe sandbox (where there is no clipboard permission).
function copyThenLog (input) {
navigator.clipboard.writeText(input).then(
() => console.log({success: true}),
(ex) => {
console.log({success: false});
console.error(ex);
},
);
}
document.querySelector('.success').addEventListener('click', () => {
const input = 'hello world';
copyThenLog(input);
});
document.querySelector('.fail').addEventListener('click', () => {
const input = {
toString () {
throw new Error('Oops');
}
};
copyThenLog(input);
});
<div class="success">Click me to succeed</div>
<div class="fail">Click me to fail</div>

copy string from the local storage when click on button in Jquery

I want to copy the string when I click on the button and text.select() function is not working on the string.
Can anyone tell me how can I do this
//getting the text from local storage
let text = window.localStorage.getItem('content');//return string
//select the text
text.select();//this give the error because this only accept the HTML collection
//range
text.setSelectionRange(0, 999999999);
//copy command
document.execCommand("copy");
Use the Clipboard API:
const text = window.localStorage.getItem('content');
navigator.clipboard.writeText(text).then(function() {
/* clipboard successfully set */
}, function() {
/* clipboard write failed */
});
There's how you can copy whole localStorage as JSON string
async function copyJson() {
try {
await navigator.clipboard.writeText(
JSON.stringify(localStorage)
);
console.log('Content copied to clipboard');
/* Resolved - text copied to clipboard successfully */
} catch (err) {
console.error('Failed to copy: ', err);
/* Rejected - text failed to copy to the clipboard */
}
}
to make this function work on click, add onclick="copyJson()" to button properties in HTML

How to fetch copied to clipboard content in cypress

How to get the 'clipboard' content in cypress. I have a button in my web application, on click of button system will perform 'copy to clipboard' and a message will get displayed. Below is an example of the url content that is copy to clipboard ( this url content is different from web site url)
https://someurl.net/machines/0c459829-a5b1-4d4b-b3c3-18b03c1c969a/attachments/a30ceca7-198e-4d87-a550-04c97fbb9231/download
I have double check that there is no href attribute in that button tag. So I have use a plugin called clipboardy and I have added plugins/index.js file
const clipboardy = require('clipboardy');
module.exports = ( on ) => {
on('task', {
getClipboard () {
return clipboardy.readSync();
}
});
};
In my test I have used cy.task() to get the clipboard content, but this is not printing the actual url content
cy.get('td').find('div').find('span').find('button').find('i').eq(0).click().then(()=>{
cy.task('getClipboard').then((data)=>{
console.log("Helloooo:"+data);
})
})
<td class="sc-hgRTRy duUdhJ">
<div>
<span class="sc-bYwvMP jTmLTC">
<span class="sc-jzJRlG iVpVVy">
<span role="button" aria-expanded="true" aria-haspopup="true" aria-owns="5aa03785-1370-455e-a838-4154f7481a7b">
<button class="sc-feJyhm cJOKrG">
<i class="icon fas fa-link sc-htpNat cQABgO" aria-hidden="true" data-component-type="icon">
</i>
</button>
</span>
</span>
</span>
</div>
</td>
If you don't want to use clipboardy package you can also use this:
cy.window().then((win) => {
win.navigator.clipboard.readText().then((text) => {
expect(text).to.eq('your copied text');
});
});
in your case, it turns to something like
cy.get('td').find('div').find('span').find('button').find('i').eq(0).click().then(()=>{
cy.window().then((win) => {
win.navigator.clipboard.readText().then((text) => {
console.log("Helloooo:"+text);
});
});
})
Accessing clipboard can be worked around, but the main issue is that the document.execCommand('copy') doesn't work (as stated above), which I reckon is the primary (and only?) way for your app to programmatically put a text to user's clipboard.
Assuming it happens somehow (or is fixed upstream), the checking the clipboard's contents can be done e.g. by using clipboardy:
npm i -D clipboardy
plugins/index.js:
const clipboardy = require('clipboardy');
module.exports = ( on ) => {
on('task', {
getClipboard () {
return clipboardy.readSync();
}
});
};
In your specification:
describe('test', () => {
it('test', () => {
cy.document().then( doc => {
doc.body.innerHTML = '<input id="inp">';
});
cy.get('#inp').type('test{selectall}');
cy.document().then( doc => {
doc.execCommand('copy');
});
cy.task('getClipboard').should('contain', 'test');
});
});
I hope this code will be usefull for you.
Thank you.
I have found this nice and simple solution of testing the clipboard content in Gleb Bahmutov's video Access the clipboard from Cypress test using Electron browser.
cy.window().its('navigator.clipboard')
.invoke('readText').should('equal', 'copied text')
It uses the new Clipboard API (Chrome, FF and Opera: 2018; Safari and Edge: 2020; IE: no)
In the video, Gleb also suggets to test a case for browsers not supporting the Clipboard API, using an old (and deprecated) method of document.execCommand('copy').
It expects the page to have an input field or textarea to paste the content. Let's assume it's textarea#paste-here.
// delete navigator.clipboard to force the app to use execCommand
cy.visit('index.html', {
onBeforeLoad(window) {
delete window.navigator.__proto__.clipboard
}
})
cy.document().then(document => cy.spy(document, 'execCommand').as('exec'))
/* trigger the copy action here... cy.get(...).click() */
cy.get('#exec').should('have.been.calledOnceWith', 'copy')
cy.get('#paste-here').focus()
cy.document().invoke('execCommand', 'paste')
cy.get('#paste-here').should('have.value', 'copied text')
Due to the clipboard permissions dialog, this test can be automated (CI) only in Electron browser. To make it running in Chrome, take a look at another Gleb's video Give Chrome Browser Clipboard Permissions from Cypress Test. Otherwise, you can limit the test to be run only in Electron using Test configuration object:
describe('Clipboard', {browser: 'electron'}, () => {
...
Late but if anyone is getting "Document is not focused" error from #marzzy's answer:
cy.window().then((win) => {
win.navigator.clipboard.readText().then((text) => {
expect(text).to.eq('your copied text');
});
});
It's either because:
You are executing code from the devtools console or snippets
Or if you're using Chrome you have to give it clipboard permission like this:
cy.wrap(
Cypress.automation('remote:debugger:protocol', {
command: 'Browser.grantPermissions',
params: {
permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'],
origin: window.location.origin,
},
}),
);
Source: https://www.youtube.com/watch?v=4eEc3x24D64
I managed to get the copied link by printing out the yielded value after clicking the button and then searched the object in the console in order to find the link. I found the link in [object]._vClipboard.clipboardAction.text, as you can see below:
cy.get('[data-test=copy-link-button]').click().then(($clipboard) => {
const copiedLink = $clipboard[0]._vClipboard.clipboardAction.text;
cy.visit(copiedLink);
});
I hope that this will help.
Reading this really helped me solve my own issue, I had to use a mixture from the OP and the first comment. This is what worked for me:
cy.get(':nth-child(2) > .mat-card > .row > .col-sm-3 > .mat-focus-indicator').click().then(() => {
cy.task('getClipboard').then(($clip) => {
const url = $clip;
cy.log('this is what was in clipboard', url);
cy.visit(url);
});
});
This gave me the URL i wanted from the button I clicked. I then just passed that right into cy.visit to visit the url i just copied. Worked great.
For me, none of the answers helped me taking de data from the clipboard. The next code worked:
First we will allow the access to the clipboard:
cy.window().its('navigator.permissions')
.invoke('query', { name: 'clipboard-read' })
.its('state').then(cy.log)
Then we access the clipboard
cy.window().its('navigator.clipboard')
.invoke('readText')
.then((text) => {
cy.log(text)
})
Hope this works for anyone here
Code at https://stackoverflow.com/a/61653634/2742007 should be adapted to call clipboardy functions supported by browsers, as commented by "opike": mind the async and read() instead of readSync().
const clipboardy = require('clipboardy');
module.exports = (on, config) => {
on('task', {
async getClipboard() {
return clipboardy.read();
}
});
}
Note: Above usage of window.navigator https://stackoverflow.com/a/68871590/5667941 , only works for Electron. For Chrome user permisson would be required
Try using cy.window() and you can use the navigator.clipboard as normal for pasting and copying.
it("Paste cliboard numbers", () => {
cy.window()
.then((win) => {
return win.navigator.clipboard.writeText("234");
`})`
.then(() => {
cy.get("[data-test=paste]").click();
cy.get("[data-test=calc-input]").should("have.text", 234);
});
});
None of the other answares worked for me so far with the cypress ui but I managedto find a solution without any additional libs or plugins by using the Cypress.Promises and the window.navigator directly ... and here it is:
cy.window().then(win =>
new Cypress.Promise((resolve, reject) =>
win.navigator
.clipboard
.readText()
.then(resolve)
.catch(reject))
).should('eq', '`myExpectedClipboardValue')
You have to give clipboard read permission.Befor click button to have copied text add this.
cy.wrap(Cypress.automation("remote:debugger:protocol", {
command: "Browser.grantPermissions",
params: {
permissions: ["clipboardReadWrite", "clipboardSanitizedWrite"],
// make the permission tighter by allowing the current origin only
origin: window.location.origin
}
}));

Implementing 'Paste' in custom context menu

here is the problem I am trying to solve - I am not sure it is possible at all. I have a web app and I need to enable data copy/paste from the app and to the app, and I have a problem with paste. If I past with CTRL + V shortcut I can get the data from the clipboard using
e.originalEvent.clipboardData.getData('text')
in 'paste' eventhandler and it works fine. What I need to enable is 'Paste' from custom context menu and my first try was to dispatch paste event manually like this
var event = new KeyboardEvent('paste', {
view: window,
bubbles: true,
cancelable: true
});
document.dispatchEvent(event);
and it actually hit paste eventhandler, but I couldn't get access to clipboard data like in the previous case. I understand that this is forbidden because of security issues - if this was allowed any page would be able to access data from the clipboard. My question is how to implement this - we are able to copy data from excel to e.g. google drive document and paste it there using a custom context menu (http://pokit.org/get/?1b5f6f4f0ef4b80bb8637649121bcd75.jpg), so I believe it is possible. Thank u all!
So, in my web application I have a custom context menu which has 'Paste' action (bunch of '<li>' tags in a popup). And when the user click on 'Paste' I call this function
if (browser === 'CHROME') {
var extensionId = 'some_id';
chrome.runtime.sendMessage(extensionId, { message: "getClipboardData" },
function (clipboardData) {
console.log('Clipboard data: ', clipboardData);
var txt = $('.helper_textarea');
$(txt).val(clipboardData);
// Call 'paste' function we have clipboard data
}
);
}
In my extension I have i paste.js file I have
function getDataFromClipboard() {
var bg = chrome.extension.getBackgroundPage();
var helperTextArea = bg.document.getElementById('sandbox');
if (helperTextArea == null) {
helperTextArea = bg.document.createElement('textarea');
document.body.appendChild(helperTextArea);
}
helperTextArea.value = '';
helperTextArea.select();
// Clipboard data
var clipboardData = '';
bg.document.execCommand("Paste");
clipboardData = helperTextArea.value;
helperTextArea.value = '';
return clipboardData;
}
chrome.runtime.onMessageExternal.addListener(
function(req, sender, callback) {
if (req) {
if (req.message) {
if (req.message == "installed") {
console.log('Checking is extension is installed!');
callback(true);
}
else if(req.message = "getClipboardData") {
console.log('Get clipboard data');
callback(getDataFromClipboard());
}
}
}
return true;
}
);
And in manifest file
"background" : {
"scripts" : [ "paste.js" ]
},
"externally_connectable": {
"matches": ["*://localhost:*/*"]
},
and of course
"permissions": ["clipboardRead" ],
I use this function to check if extension is added
isExtensionInstalled: function (extensionId, callback) {
chrome.runtime.sendMessage(extensionId, { message: "installed" },
function (reply) {
if (reply) {
callback(true);
} else {
callback(false);
}
});
},
And this is working great. Now the problem is how to port this to Edge. What is equivalent to 'chrome.runtime.sendMessage' in Edge? Thanks for your help.

turn on and off chrome extension

I am working on a chrome extension ,this extension have 2 icons in the browser action (On & Off) ;
basically when it is On the background execute the script.js (Inject the file:script.js)
using the chrome.tabs.executeScript(tab.id,{file:"script.js",function(){});
I had problems to turn it off !
I have tried to use messages communication between the background.js and the script.js but this does not work neither .
If I understand correctly, your extension should have two states, On and Off. Clicking the extension icon toggles it on/off.
In this case you should use storage so the extension knows what state it is in. So on a click event, use something like:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.storage.sync.get('state', function(data) {
if (data.state === 'on') {
chrome.storage.sync.set({state: 'off'});
//do something, removing the script or whatever
} else {
chrome.storage.sync.set({state: 'on'});
//inject your script
}
});
});
Note though that this is happening at the extension/browser level and will apply to all tabs, so you may need something more complex that records both the tab ID and the state.
You then have the choice to either always run a content script and check the on/off state before performing some action, or inject and remove the script. I'm not sure if you remove a script though. Depending on what the script does, you may just want to refresh the page (i.e. if your script messes with the DOM and you want to undo that when turning the extension off).
background.js
var enable=false;
chrome.browserAction.onClicked.addListener(function (tab) {
enable = enable ? false : true;
if(enable){
//turn on...
chrome.browserAction.setIcon({ path: 'icon.png' });
chrome.browserAction.setBadgeText({ text: 'ON' });
chrome.tabs.executeScript(null, { file: 'content.js' });
}else{
//turn off...
chrome.browserAction.setIcon({ path: 'disable.png'});
chrome.browserAction.setBadgeText({ text: '' });
}
});
To add onto what #david-gilbertson stated for making it active and inactive for certain tabs, I have created that functionality here. I also took added some functions for removing and adding tabs to the array. Enjoy!
function addTab(array, new_tab_id)
{
array.push(new_tab_id);
//then call the set to update with modified value
chrome.storage.sync.set({
active_tabs:array
}, function() {
console.log("added tab");
});
}
function removeTab(array, rem_tab_id)
{
const index = array.indexOf(rem_tab_id);
if (index > -1) {
array.splice(index, 1);
}
//then call the set to update with modified value
chrome.storage.sync.set({
active_tabs:array
}, function() {
console.log("removed tab");
});
}
chrome.browserAction.onClicked.addListener(function (tab) {`enter code here`
chrome.storage.sync.get({active_tabs : []}, function(data) {
if (data.active_tabs.includes(request.tab_id)) {
removeTab(data.active_tabs, request.tab_id)
console.log("Turned Off ".concat(request.tab_id))
document.removeEventListener("mousemove", highlightCurrentHover, false);
} else {
addTab(data.active_tabs, request.tab_id)
console.log("Turned On ".concat(request.tab_id))
document.addEventListener('mousemove', highlightCurrentHover, false);
}
});
);

Categories