diff --git a/package-lock.json b/package-lock.json index 1a678c50f4..a93c8168f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1025,17 +1025,6 @@ } } }, - "clipboard": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", - "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", - "optional": true, - "requires": { - "good-listener": "^1.2.2", - "select": "^1.1.2", - "tiny-emitter": "^2.0.0" - } - }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", @@ -1563,12 +1552,6 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, - "delegate": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", - "optional": true - }, "deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", @@ -2545,15 +2528,6 @@ "sparkles": "^1.0.0" } }, - "good-listener": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", - "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", - "optional": true, - "requires": { - "delegate": "^3.1.2" - } - }, "graceful-fs": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", @@ -5730,12 +5704,6 @@ "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=", "dev": true }, - "select": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", - "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=", - "optional": true - }, "semver": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", @@ -6308,12 +6276,6 @@ "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", "dev": true }, - "tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", - "optional": true - }, "tmp": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", diff --git a/package.json b/package.json index a9fd2d37fa..1c13095f60 100755 --- a/package.json +++ b/package.json @@ -29,9 +29,6 @@ "author": "Lea Verou", "license": "MIT", "readmeFilename": "README.md", - "optionalDependencies": { - "clipboard": "^2.0.0" - }, "devDependencies": { "chai": "^4.2.0", "danger": "^10.5.0", diff --git a/plugins/copy-to-clipboard/index.html b/plugins/copy-to-clipboard/index.html index e8cf3e3105..9fdd6c568d 100644 --- a/plugins/copy-to-clipboard/index.html +++ b/plugins/copy-to-clipboard/index.html @@ -21,12 +21,7 @@

How to use

-

The plugin depends on clipboard.js and the Prism Toolbar plugin. In addition to including the plugin file with your PrismJS build, ensure they are loaded before the plugin.

- -

The simplest way to include clipboard.js is to use any of the - recommended CDNs. If you're using Browserify, clipboard.js will be loaded automatically - if it's included in your package.json. - If you don't load clipboard.js yourself, the plugin will load it from a CDN for you.

+

The plugin depends on the Prism Toolbar plugin. In addition to including the plugin file with your PrismJS build, ensure it is loaded before the plugin.

diff --git a/plugins/copy-to-clipboard/prism-copy-to-clipboard.js b/plugins/copy-to-clipboard/prism-copy-to-clipboard.js index 8ebf659856..d6e744fca9 100644 --- a/plugins/copy-to-clipboard/prism-copy-to-clipboard.js +++ b/plugins/copy-to-clipboard/prism-copy-to-clipboard.js @@ -9,30 +9,73 @@ return; } - var ClipboardJS = window.ClipboardJS || undefined; - - if (!ClipboardJS && typeof require === 'function') { - ClipboardJS = require('clipboard'); + /** + * When the given elements is clicked by the user, the given text will be copied to clipboard. + * + * @param {HTMLElement} element + * @param {CopyInfo} copyInfo + * + * @typedef CopyInfo + * @property {() => string} getText + * @property {() => void} success + * @property {(reason: unknown) => void} error + */ + function registerClipboard(element, copyInfo) { + element.addEventListener('click', function () { + copyTextToClipboard(copyInfo); + }); } - var callbacks = []; + // https://stackoverflow.com/a/30810322/7595472 + + /** @param {CopyInfo} copyInfo */ + function fallbackCopyTextToClipboard(copyInfo) { + var textArea = document.createElement("textarea"); + textArea.value = copyInfo.getText(); - if (!ClipboardJS) { - var script = document.createElement('script'); - var head = document.querySelector('head'); + // Avoid scrolling to bottom + textArea.style.top = "0"; + textArea.style.left = "0"; + textArea.style.position = "fixed"; - script.onload = function () { - ClipboardJS = window.ClipboardJS; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); - if (ClipboardJS) { - while (callbacks.length) { - callbacks.pop()(); + try { + var successful = document.execCommand('copy'); + setTimeout(function () { + if (successful) { + copyInfo.success(); + } else { + copyInfo.error(); } - } - }; + }, 1); + } catch (err) { + setTimeout(function () { + copyInfo.error(err); + }, 1); + } - script.src = 'https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js'; - head.appendChild(script); + document.body.removeChild(textArea); + } + /** @param {CopyInfo} copyInfo */ + function copyTextToClipboard(copyInfo) { + if (navigator.clipboard) { + navigator.clipboard.writeText(copyInfo.getText()).then(copyInfo.success, copyInfo.error); + } else { + fallbackCopyTextToClipboard(copyInfo); + } + } + + /** + * Selects the text content of the given element. + * + * @param {Element} element + */ + function selectElementText(element) { + // https://stackoverflow.com/a/20079910/7595472 + window.getSelection().selectAllChildren(element) } /** @@ -74,32 +117,27 @@ linkCopy.textContent = settings['copy']; linkCopy.setAttribute('type', 'button'); - if (!ClipboardJS) { - callbacks.push(registerClipboard); - } else { - registerClipboard(); - } - - return linkCopy; - - function registerClipboard() { - var clip = new ClipboardJS(linkCopy, { - 'text': function () { - return element.textContent; - } - }); - - clip.on('success', function () { + registerClipboard(linkCopy, { + getText: function () { + return element.textContent; + }, + success: function () { linkCopy.textContent = settings['copy-success']; resetText(); - }); - clip.on('error', function () { + }, + error: function () { linkCopy.textContent = settings['copy-error']; + setTimeout(function () { + selectElementText(element); + }, 1); + resetText(); - }); - } + } + }); + + return linkCopy; function resetText() { setTimeout(function () { diff --git a/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js b/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js index 3a03d17678..d45ee5f21a 100644 --- a/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js +++ b/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js @@ -1 +1 @@ -!function(){if("undefined"!=typeof self&&self.Prism&&self.document)if(Prism.plugins.toolbar){var c=window.ClipboardJS||void 0;c||"function"!=typeof require||(c=require("clipboard"));var u=[];if(!c){var t=document.createElement("script"),o=document.querySelector("head");t.onload=function(){if(c=window.ClipboardJS)for(;u.length;)u.pop()()},t.src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js",o.appendChild(t)}Prism.plugins.toolbar.registerButton("copy-to-clipboard",function(t){var o=t.element,e=function(t){var o={copy:"Copy","copy-error":"Press Ctrl+C to copy","copy-success":"Copied!","copy-timeout":5e3};for(var e in o){for(var n="data-prismjs-"+e,r=t;r&&!r.hasAttribute(n);)r=r.parentElement;r&&(o[e]=r.getAttribute(n))}return o}(o),n=document.createElement("button");return n.textContent=e.copy,n.setAttribute("type","button"),c?r():u.push(r),n;function r(){var t=new c(n,{text:function(){return o.textContent}});t.on("success",function(){n.textContent=e["copy-success"],i()}),t.on("error",function(){n.textContent=e["copy-error"],i()})}function i(){setTimeout(function(){n.textContent=e.copy},e["copy-timeout"])}})}else console.warn("Copy to Clipboard plugin loaded before Toolbar plugin.")}(); \ No newline at end of file +!function(){function c(t,e){t.addEventListener("click",function(){!function(t){navigator.clipboard?navigator.clipboard.writeText(t.getText()).then(t.success,t.error):function(e){var t=document.createElement("textarea");t.value=e.getText(),t.style.top="0",t.style.left="0",t.style.position="fixed",document.body.appendChild(t),t.focus(),t.select();try{var o=document.execCommand("copy");setTimeout(function(){o?e.success():e.error()},1)}catch(t){setTimeout(function(){e.error(t)},1)}document.body.removeChild(t)}(t)}(e)})}"undefined"!=typeof self&&self.Prism&&self.document&&(Prism.plugins.toolbar?Prism.plugins.toolbar.registerButton("copy-to-clipboard",function(t){var e=t.element,o=function(t){var e={copy:"Copy","copy-error":"Press Ctrl+C to copy","copy-success":"Copied!","copy-timeout":5e3};for(var o in e){for(var n="data-prismjs-"+o,r=t;r&&!r.hasAttribute(n);)r=r.parentElement;r&&(e[o]=r.getAttribute(n))}return e}(e),n=document.createElement("button");return n.textContent=o.copy,n.setAttribute("type","button"),c(n,{getText:function(){return e.textContent},success:function(){n.textContent=o["copy-success"],r()},error:function(){n.textContent=o["copy-error"],setTimeout(function(){!function(t){window.getSelection().selectAllChildren(t)}(e)},1),r()}}),n;function r(){setTimeout(function(){n.textContent=o.copy},o["copy-timeout"])}}):console.warn("Copy to Clipboard plugin loaded before Toolbar plugin."))}(); \ No newline at end of file