Skip to content

Commit

Permalink
Make copy-to-clipboard configurable with multiple attributes (#2723)
Browse files Browse the repository at this point in the history
Resolves #1438.

Makes the `copy-to-clipboard` plugin consider the following HTML attributes:

- `data-prismjs-copy`,
- `data-prismjs-copy-error`,
- `data-prismjs-copy-success`,
- `data-prismjs-copy-timeout`.

Use those attributes to translate the toolbar for the plugin.
  • Loading branch information
edukisto authored Jan 31, 2021
1 parent fd1081d commit 2cb909e
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 16 deletions.
162 changes: 157 additions & 5 deletions plugins/copy-to-clipboard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,171 @@

<section>
<h1>How to use</h1>
<p>In addition to including the plugin file with your PrismJS build, ensure <a href="https://clipboardjs.com/">Clipboard.js</a> is loaded before the plugin.</p>

<p>The simplest way to include Clipboard.js is to use any of the
<a href="https://github.com/zenorocha/clipboard.js/wiki/CDN-Providers">recommended CDNs</a>. If you're using Browserify, Clipboard.js will be loaded automatically
<p>The plugin depends on <a href="https://clipboardjs.com/">clipboard.js</a> and the Prism <a href="https://prismjs.com/plugins/toolbar/">Toolbar</a> plugin. In addition to including the plugin file with your PrismJS build, ensure they are loaded before the plugin.</p>

<p>The simplest way to include clipboard.js is to use any of the
<a href="https://github.com/zenorocha/clipboard.js/wiki/CDN-Providers">recommended CDNs</a>. If you're using Browserify, clipboard.js will be loaded automatically
if it's included in your <code>package.json</code>.
If you don't load Clipboard.js yourself, the plugin will load it from a CDN for you.</p>
If you don't load clipboard.js yourself, the plugin will load it from a CDN for you.</p>
</section>

<section>
<h1>Settings</h1>

<p>By default, the plugin shows messages in English and sets a 5-second timeout after a click. You can use the following HTML5 data attributes to override the default settings:</p>

<ul>
<li><code class="token attr-name">data-prismjs-copy</code> — default message displayed by Copy to Clipboard;</li>
<li><code class="token attr-name">data-prismjs-copy-error</code> — a message displayed after failing copying, prompting the user to press <code>Ctrl+C</code>;</li>
<li><code class="token attr-name">data-prismjs-copy-success</code> — a message displayed after a successful copying;</li>
<li><code class="token attr-name">data-prismjs-copy-timeout</code> — a timeout (in milliseconds) after copying. Once the timeout passed, the success or error message will revert back to the default message. The value should be a non-negative integer.</li>
</ul>

<p>The plugin traverses up the DOM tree to find each of these attributes. The search starts at every <code class="token tag">pre code</code> element and stops at the closest ancestor element that has a desired attribute or at the worst case, at the <code class="token tag">html</code> element.</p>

<p><strong>Warning!</strong> Although possible, you definitely shouldn't add these attributes to the <code class="token tag">html</code> element, because a human-readable text should be placed <em>after</em> the character encoding declaration (<code>&lt;meta charset=&quot;...&quot;&gt;</code>), and the latter <a href="https://html.spec.whatwg.org/multipage/semantics.html#charset">must be</a> serialized completely within the first 512 (in older browsers) or 1024 bytes of the document. Consider using the <code class="token tag">body</code> element or one of its descendants.</p>
</section>

<section>
<h1>Examples</h1>

<h2>Sharing</h2>

<p>The following code blocks show modified messages and both use a half-second timeout. The other settings are set to default.</p>

<p>Source code:</p>

<pre><code class="language-html">&lt;body data-prismjs-copy-timeout=&quot;500&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-prismjs-copy=&quot;Copy the JavaScript snippet!&quot;&gt;console.log('Hello, world!');&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-prismjs-copy=&quot;Copy the C snippet!&quot;&gt;int main() {
return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/body&gt;</code></pre>

<p>Output:</p>

<div data-prismjs-copy-timeout="500">
<pre><code class="language-js" data-prismjs-copy="Copy the JavaScript snippet!">console.log('Hello, world!');</code></pre>

<pre><code class="language-c" data-prismjs-copy="Copy the C snippet!">int main() {
return 0;
}</code></pre>
</div>

<h2>Inheritance</h2>

<p>The plugin always use the closest ancestor element that has a desired attribute, so it's possible to override any setting on any descendant. In the following example, the <code class="token attr-value">baz</code> message is used. The other settings are set to default.</p>

<p>Source code:</p>

<pre><code class="language-html">&lt;body data-prismjs-copy=&quot;foo&quot;&gt;
&lt;main data-prismjs-copy=&quot;bar&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-prismjs-copy=&quot;baz&quot;&gt;int main() {
return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/main&gt;
&lt;/body&gt;</code></pre>

<p>Output:</p>

<div data-prismjs-copy="foo">
<div data-prismjs-copy="bar">
<pre><code class="language-c" data-prismjs-copy="baz">int main() {
return 0;
}</code></pre>
</div>
</div>

<h2>i18n</h2>

<p>You can use the data attributes for internationalization.</p>

<p>The following code blocks use shared messages in Russian and the default 5-second timeout.</p>

<p>Source code:</p>

<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ru&quot;&gt;
&lt;!-- The head is omitted. --&gt;
&lt;body
data-prismjs-copy=&quot;Скопировать&quot;
data-prismjs-copy-error=&quot;Нажмите Ctrl+C, чтобы скопировать&quot;
data-prismjs-copy-success=&quot;Скопировано!&quot;
&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int main() {
return 0;
}&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;console.log('Hello, world!');&lt;/code&gt;&lt;/pre&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>

<p>Output:</p>

<div
data-prismjs-copy="Скопировать"
data-prismjs-copy-error="Нажмите Ctrl+C, чтобы скопировать"
data-prismjs-copy-success="Скопировано!"
>
<pre><code class="language-c">int main() {
return 0;
}</code></pre>

<pre><code class="language-js">console.log('Hello, world!');</code></pre>
</div>

<p>The next HTML document is in English, but some code blocks show messages in Russian and simplified Mainland Chinese. The other settings are set to default.</p>

<p>Source code:</p>

<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;&lt;!-- The head is omitted. --&gt;
&lt;body&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;console.log('Hello, world!');&lt;/code&gt;&lt;/pre&gt;

&lt;pre
lang=&quot;ru&quot;
data-prismjs-copy=&quot;Скопировать&quot;
data-prismjs-copy-error=&quot;Нажмите Ctrl+C, чтобы скопировать&quot;
data-prismjs-copy-success=&quot;Скопировано!&quot;
&gt;&lt;code class=&quot;language-js&quot;&gt;console.log('Привет, мир!');&lt;/code&gt;&lt;/pre&gt;

&lt;pre
lang=&quot;zh-Hans-CN&quot;
data-prismjs-copy=&quot;复制文本&quot;
data-prismjs-copy-error=&quot;按Ctrl+C复制&quot;
data-prismjs-copy-success=&quot;文本已复制!&quot;
&gt;&lt;code class=&quot;language-js&quot;&gt;console.log('你好,世界!');&lt;/code&gt;&lt;/pre&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>

<p>Output:</p>

<div>
<pre><code class="language-js">console.log('Hello, world!');</code></pre>

<pre
lang="ru"
data-prismjs-copy="Скопировать"
data-prismjs-copy-error="Нажмите Ctrl+C, чтобы скопировать"
data-prismjs-copy-success="Скопировано!"
><code class="language-js">console.log('Привет, мир!');</code></pre>

<pre data-src="plugins/copy-to-clipboard/prism-copy-to-clipboard.js"></pre>
<pre
lang="zh-Hans-CN"
data-prismjs-copy="复制文本"
data-prismjs-copy-error="按Ctrl+C复制"
data-prismjs-copy-success="文本已复制!"
><code class="language-js">console.log('你好,世界!');</code></pre>
</div>
</section>

<footer data-src="assets/templates/footer.html" data-type="text/html"></footer>

<script src="prism.js"></script>
<script src="plugins/autoloader/prism-autoloader.js" data-autoloader-path="components/"></script>
<script src="plugins/toolbar/prism-toolbar.js"></script>
<script src="plugins/copy-to-clipboard/prism-copy-to-clipboard.js"></script>
<script src="assets/utopia.js"></script>
Expand Down
52 changes: 42 additions & 10 deletions plugins/copy-to-clipboard/prism-copy-to-clipboard.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(function(){
(function () {
if (typeof self === 'undefined' || !self.Prism || !self.document) {
return;
}
Expand All @@ -21,7 +21,7 @@
var script = document.createElement('script');
var head = document.querySelector('head');

script.onload = function() {
script.onload = function () {
ClipboardJS = window.ClipboardJS;

if (ClipboardJS) {
Expand All @@ -35,13 +35,45 @@
head.appendChild(script);
}

/**
* Traverses up the DOM tree to find data attributes that override the default plugin settings.
*
* @param {Element} startElement An element to start from.
* @returns {Settings} The plugin settings.
* @typedef {Record<"copy" | "copy-error" | "copy-success" | "copy-timeout", string | number>} Settings
*/
function getSettings(startElement) {
/** @type {Settings} */
var settings = {
'copy': 'Copy',
'copy-error': 'Press Ctrl+C to copy',
'copy-success': 'Copied!',
'copy-timeout': 5000
};

var prefix = 'data-prismjs-';
for (var key in settings) {
var attr = prefix + key;
var element = startElement;
while (element && !element.hasAttribute(attr)) {
element = element.parentElement;
}
if (element) {
settings[key] = element.getAttribute(attr);
}
}
return settings;
}

Prism.plugins.toolbar.registerButton('copy-to-clipboard', function (env) {
var element = env.element;

var settings = getSettings(element);

var linkCopy = document.createElement('button');
linkCopy.textContent = 'Copy';
linkCopy.textContent = settings['copy'];
linkCopy.setAttribute('type', 'button');

var element = env.element;

if (!ClipboardJS) {
callbacks.push(registerClipboard);
} else {
Expand All @@ -57,22 +89,22 @@
}
});

clip.on('success', function() {
linkCopy.textContent = 'Copied!';
clip.on('success', function () {
linkCopy.textContent = settings['copy-success'];

resetText();
});
clip.on('error', function () {
linkCopy.textContent = 'Press Ctrl+C to copy';
linkCopy.textContent = settings['copy-error'];

resetText();
});
}

function resetText() {
setTimeout(function () {
linkCopy.textContent = 'Copy';
}, 5000);
linkCopy.textContent = settings['copy'];
}, settings['copy-timeout']);
}
});
})();
2 changes: 1 addition & 1 deletion plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 2cb909e

Please sign in to comment.