Externalize clipboard copying to the [clippie](https://github.com/silverwind/clippie) module which I feel I can maintain outside this repo for shared benefit with my other projects. The module is feature-equivalent to the previous code and has one improvement where it sets `aria-hidden` on the fallback textarea, preventing screen readers from picking it up. Also it support `Array` of `content` as well to copy multiple items at once, in case it's ever needed.tags/v1.20.0-rc0
@@ -19,6 +19,7 @@ | |||
"add-asset-webpack-plugin": "2.0.1", | |||
"ansi-to-html": "0.7.2", | |||
"asciinema-player": "3.2.0", | |||
"clippie": "3.1.4", | |||
"css-loader": "6.7.3", | |||
"dropzone": "6.0.0-beta.2", | |||
"easymde": "2.18.0", | |||
@@ -2762,6 +2763,11 @@ | |||
"url": "https://github.com/chalk/strip-ansi?sponsor=1" | |||
} | |||
}, | |||
"node_modules/clippie": { | |||
"version": "3.1.4", | |||
"resolved": "https://registry.npmjs.org/clippie/-/clippie-3.1.4.tgz", | |||
"integrity": "sha512-jrW6sG1zcTEQr5MtCXJzszNmHWV9Fkaco8sAqFeuOApNFP/lRFcUi4JABMmxBJwFZLIvbw2BY3G5E+BjBqZMdQ==" | |||
}, | |||
"node_modules/cliui": { | |||
"version": "7.0.4", | |||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", |
@@ -19,6 +19,7 @@ | |||
"add-asset-webpack-plugin": "2.0.1", | |||
"ansi-to-html": "0.7.2", | |||
"asciinema-player": "3.2.0", | |||
"clippie": "3.1.4", | |||
"css-loader": "6.7.3", | |||
"dropzone": "6.0.0-beta.2", | |||
"easymde": "2.18.0", |
@@ -1,48 +1,9 @@ | |||
import {showTemporaryTooltip} from '../modules/tippy.js'; | |||
import {toAbsoluteUrl} from '../utils.js'; | |||
import {clippie} from 'clippie'; | |||
const {copy_success, copy_error} = window.config.i18n; | |||
export async function copyToClipboard(content) { | |||
if (content instanceof Blob) { | |||
const item = new ClipboardItem({[content.type]: content}); | |||
await navigator.clipboard.write([item]); | |||
} else { // text | |||
try { | |||
await navigator.clipboard.writeText(content); | |||
} catch { | |||
return fallbackCopyToClipboard(content); | |||
} | |||
} | |||
return true; | |||
} | |||
// Fallback to use if navigator.clipboard doesn't exist. Achieved via creating | |||
// a temporary textarea element, selecting the text, and using document.execCommand | |||
function fallbackCopyToClipboard(text) { | |||
if (!document.execCommand) return false; | |||
const tempTextArea = document.createElement('textarea'); | |||
tempTextArea.value = text; | |||
// avoid scrolling | |||
tempTextArea.style.top = 0; | |||
tempTextArea.style.left = 0; | |||
tempTextArea.style.position = 'fixed'; | |||
document.body.appendChild(tempTextArea); | |||
tempTextArea.select(); | |||
// if unsecure (not https), there is no navigator.clipboard, but we can still | |||
// use document.execCommand to copy to clipboard | |||
const success = document.execCommand('copy'); | |||
document.body.removeChild(tempTextArea); | |||
return success; | |||
} | |||
// For all DOM elements with [data-clipboard-target] or [data-clipboard-text], | |||
// this copy-to-clipboard will work for them | |||
export function initGlobalCopyToClipboardListener() { | |||
@@ -61,7 +22,7 @@ export function initGlobalCopyToClipboardListener() { | |||
e.preventDefault(); | |||
(async() => { | |||
const success = await copyToClipboard(text); | |||
const success = await clippie(text); | |||
showTemporaryTooltip(target, success ? copy_success : copy_error); | |||
})(); | |||
@@ -1,11 +1,11 @@ | |||
import {copyToClipboard} from './clipboard.js'; | |||
import {clippie} from 'clippie'; | |||
import {showTemporaryTooltip} from '../modules/tippy.js'; | |||
import {convertImage} from '../utils.js'; | |||
const {i18n} = window.config; | |||
async function doCopy(content, btn) { | |||
const success = await copyToClipboard(content); | |||
const success = await clippie(content); | |||
showTemporaryTooltip(btn, success ? i18n.copy_success : i18n.copy_error); | |||
} | |||
@@ -2,7 +2,7 @@ import $ from 'jquery'; | |||
import {svg} from '../svg.js'; | |||
import {invertFileFolding} from './file-fold.js'; | |||
import {createTippy} from '../modules/tippy.js'; | |||
import {copyToClipboard} from './clipboard.js'; | |||
import {clippie} from 'clippie'; | |||
import {toAbsoluteUrl} from '../utils.js'; | |||
export const singleAnchorRegex = /^#(L|n)([1-9][0-9]*)$/; | |||
@@ -190,7 +190,7 @@ export function initRepoCodeView() { | |||
currentTarget.closest('tr').outerHTML = blob; | |||
}); | |||
$(document).on('click', '.copy-line-permalink', async (e) => { | |||
const success = await copyToClipboard(toAbsoluteUrl(e.currentTarget.getAttribute('data-url'))); | |||
const success = await clippie(toAbsoluteUrl(e.currentTarget.getAttribute('data-url'))); | |||
if (!success) return; | |||
document.querySelector('.code-line-button')?._tippy?.hide(); | |||
}); |