From f5593d08dc6d615e650fe5e954b300d1895212b7 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sun, 2 Apr 2023 11:25:36 +0200 Subject: [PATCH] Use clippie module to copy to clipboard (#23801) 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. --- package-lock.json | 6 +++++ package.json | 1 + web_src/js/features/clipboard.js | 43 ++---------------------------- web_src/js/features/copycontent.js | 4 +-- web_src/js/features/repo-code.js | 4 +-- 5 files changed, 13 insertions(+), 45 deletions(-) diff --git a/package-lock.json b/package-lock.json index 313fa515ae..7bbe13abc3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index dc781c6a32..640b4fa20f 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/web_src/js/features/clipboard.js b/web_src/js/features/clipboard.js index 07c439504e..bcbdae2704 100644 --- a/web_src/js/features/clipboard.js +++ b/web_src/js/features/clipboard.js @@ -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); })(); diff --git a/web_src/js/features/copycontent.js b/web_src/js/features/copycontent.js index 5a4b99ae9b..e51953625d 100644 --- a/web_src/js/features/copycontent.js +++ b/web_src/js/features/copycontent.js @@ -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); } diff --git a/web_src/js/features/repo-code.js b/web_src/js/features/repo-code.js index 1c59913132..c0dced44eb 100644 --- a/web_src/js/features/repo-code.js +++ b/web_src/js/features/repo-code.js @@ -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(); });