Skip to content

Commit 2cb909e

Browse files
authored
Make copy-to-clipboard configurable with multiple attributes (#2723)
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.
1 parent fd1081d commit 2cb909e

File tree

3 files changed

+200
-16
lines changed

3 files changed

+200
-16
lines changed

plugins/copy-to-clipboard/index.html

+157-5
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,171 @@
2020

2121
<section>
2222
<h1>How to use</h1>
23-
<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>
2423

25-
<p>The simplest way to include Clipboard.js is to use any of the
26-
<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
24+
<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>
25+
26+
<p>The simplest way to include clipboard.js is to use any of the
27+
<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
2728
if it's included in your <code>package.json</code>.
28-
If you don't load Clipboard.js yourself, the plugin will load it from a CDN for you.</p>
29+
If you don't load clipboard.js yourself, the plugin will load it from a CDN for you.</p>
30+
</section>
31+
32+
<section>
33+
<h1>Settings</h1>
34+
35+
<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>
36+
37+
<ul>
38+
<li><code class="token attr-name">data-prismjs-copy</code> — default message displayed by Copy to Clipboard;</li>
39+
<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>
40+
<li><code class="token attr-name">data-prismjs-copy-success</code> — a message displayed after a successful copying;</li>
41+
<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>
42+
</ul>
43+
44+
<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>
45+
46+
<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>
47+
</section>
48+
49+
<section>
50+
<h1>Examples</h1>
51+
52+
<h2>Sharing</h2>
53+
54+
<p>The following code blocks show modified messages and both use a half-second timeout. The other settings are set to default.</p>
55+
56+
<p>Source code:</p>
57+
58+
<pre><code class="language-html">&lt;body data-prismjs-copy-timeout=&quot;500&quot;&gt;
59+
&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;
60+
61+
&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-prismjs-copy=&quot;Copy the C snippet!&quot;&gt;int main() {
62+
return 0;
63+
}&lt;/code&gt;&lt;/pre&gt;
64+
&lt;/body&gt;</code></pre>
65+
66+
<p>Output:</p>
67+
68+
<div data-prismjs-copy-timeout="500">
69+
<pre><code class="language-js" data-prismjs-copy="Copy the JavaScript snippet!">console.log('Hello, world!');</code></pre>
70+
71+
<pre><code class="language-c" data-prismjs-copy="Copy the C snippet!">int main() {
72+
return 0;
73+
}</code></pre>
74+
</div>
75+
76+
<h2>Inheritance</h2>
77+
78+
<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>
79+
80+
<p>Source code:</p>
81+
82+
<pre><code class="language-html">&lt;body data-prismjs-copy=&quot;foo&quot;&gt;
83+
&lt;main data-prismjs-copy=&quot;bar&quot;&gt;
84+
&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-prismjs-copy=&quot;baz&quot;&gt;int main() {
85+
return 0;
86+
}&lt;/code&gt;&lt;/pre&gt;
87+
&lt;/main&gt;
88+
&lt;/body&gt;</code></pre>
89+
90+
<p>Output:</p>
91+
92+
<div data-prismjs-copy="foo">
93+
<div data-prismjs-copy="bar">
94+
<pre><code class="language-c" data-prismjs-copy="baz">int main() {
95+
return 0;
96+
}</code></pre>
97+
</div>
98+
</div>
99+
100+
<h2>i18n</h2>
101+
102+
<p>You can use the data attributes for internationalization.</p>
103+
104+
<p>The following code blocks use shared messages in Russian and the default 5-second timeout.</p>
105+
106+
<p>Source code:</p>
107+
108+
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
109+
&lt;html lang=&quot;ru&quot;&gt;
110+
&lt;!-- The head is omitted. --&gt;
111+
&lt;body
112+
data-prismjs-copy=&quot;Скопировать&quot;
113+
data-prismjs-copy-error=&quot;Нажмите Ctrl+C, чтобы скопировать&quot;
114+
data-prismjs-copy-success=&quot;Скопировано!&quot;
115+
&gt;
116+
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int main() {
117+
return 0;
118+
}&lt;/code&gt;&lt;/pre&gt;
119+
120+
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;console.log('Hello, world!');&lt;/code&gt;&lt;/pre&gt;
121+
&lt;/body&gt;
122+
&lt;/html&gt;</code></pre>
123+
124+
<p>Output:</p>
125+
126+
<div
127+
data-prismjs-copy="Скопировать"
128+
data-prismjs-copy-error="Нажмите Ctrl+C, чтобы скопировать"
129+
data-prismjs-copy-success="Скопировано!"
130+
>
131+
<pre><code class="language-c">int main() {
132+
return 0;
133+
}</code></pre>
134+
135+
<pre><code class="language-js">console.log('Hello, world!');</code></pre>
136+
</div>
137+
138+
<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>
139+
140+
<p>Source code:</p>
141+
142+
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
143+
&lt;html lang=&quot;en&quot;&gt;&lt;!-- The head is omitted. --&gt;
144+
&lt;body&gt;
145+
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;console.log('Hello, world!');&lt;/code&gt;&lt;/pre&gt;
146+
147+
&lt;pre
148+
lang=&quot;ru&quot;
149+
data-prismjs-copy=&quot;Скопировать&quot;
150+
data-prismjs-copy-error=&quot;Нажмите Ctrl+C, чтобы скопировать&quot;
151+
data-prismjs-copy-success=&quot;Скопировано!&quot;
152+
&gt;&lt;code class=&quot;language-js&quot;&gt;console.log('Привет, мир!');&lt;/code&gt;&lt;/pre&gt;
153+
154+
&lt;pre
155+
lang=&quot;zh-Hans-CN&quot;
156+
data-prismjs-copy=&quot;复制文本&quot;
157+
data-prismjs-copy-error=&quot;按Ctrl+C复制&quot;
158+
data-prismjs-copy-success=&quot;文本已复制!&quot;
159+
&gt;&lt;code class=&quot;language-js&quot;&gt;console.log('你好,世界!');&lt;/code&gt;&lt;/pre&gt;
160+
&lt;/body&gt;
161+
&lt;/html&gt;</code></pre>
162+
163+
<p>Output:</p>
164+
165+
<div>
166+
<pre><code class="language-js">console.log('Hello, world!');</code></pre>
167+
168+
<pre
169+
lang="ru"
170+
data-prismjs-copy="Скопировать"
171+
data-prismjs-copy-error="Нажмите Ctrl+C, чтобы скопировать"
172+
data-prismjs-copy-success="Скопировано!"
173+
><code class="language-js">console.log('Привет, мир!');</code></pre>
29174

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

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

35186
<script src="prism.js"></script>
187+
<script src="plugins/autoloader/prism-autoloader.js" data-autoloader-path="components/"></script>
36188
<script src="plugins/toolbar/prism-toolbar.js"></script>
37189
<script src="plugins/copy-to-clipboard/prism-copy-to-clipboard.js"></script>
38190
<script src="assets/utopia.js"></script>

plugins/copy-to-clipboard/prism-copy-to-clipboard.js

+42-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
(function(){
1+
(function () {
22
if (typeof self === 'undefined' || !self.Prism || !self.document) {
33
return;
44
}
@@ -21,7 +21,7 @@
2121
var script = document.createElement('script');
2222
var head = document.querySelector('head');
2323

24-
script.onload = function() {
24+
script.onload = function () {
2525
ClipboardJS = window.ClipboardJS;
2626

2727
if (ClipboardJS) {
@@ -35,13 +35,45 @@
3535
head.appendChild(script);
3636
}
3737

38+
/**
39+
* Traverses up the DOM tree to find data attributes that override the default plugin settings.
40+
*
41+
* @param {Element} startElement An element to start from.
42+
* @returns {Settings} The plugin settings.
43+
* @typedef {Record<"copy" | "copy-error" | "copy-success" | "copy-timeout", string | number>} Settings
44+
*/
45+
function getSettings(startElement) {
46+
/** @type {Settings} */
47+
var settings = {
48+
'copy': 'Copy',
49+
'copy-error': 'Press Ctrl+C to copy',
50+
'copy-success': 'Copied!',
51+
'copy-timeout': 5000
52+
};
53+
54+
var prefix = 'data-prismjs-';
55+
for (var key in settings) {
56+
var attr = prefix + key;
57+
var element = startElement;
58+
while (element && !element.hasAttribute(attr)) {
59+
element = element.parentElement;
60+
}
61+
if (element) {
62+
settings[key] = element.getAttribute(attr);
63+
}
64+
}
65+
return settings;
66+
}
67+
3868
Prism.plugins.toolbar.registerButton('copy-to-clipboard', function (env) {
69+
var element = env.element;
70+
71+
var settings = getSettings(element);
72+
3973
var linkCopy = document.createElement('button');
40-
linkCopy.textContent = 'Copy';
74+
linkCopy.textContent = settings['copy'];
4175
linkCopy.setAttribute('type', 'button');
4276

43-
var element = env.element;
44-
4577
if (!ClipboardJS) {
4678
callbacks.push(registerClipboard);
4779
} else {
@@ -57,22 +89,22 @@
5789
}
5890
});
5991

60-
clip.on('success', function() {
61-
linkCopy.textContent = 'Copied!';
92+
clip.on('success', function () {
93+
linkCopy.textContent = settings['copy-success'];
6294

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

68100
resetText();
69101
});
70102
}
71103

72104
function resetText() {
73105
setTimeout(function () {
74-
linkCopy.textContent = 'Copy';
75-
}, 5000);
106+
linkCopy.textContent = settings['copy'];
107+
}, settings['copy-timeout']);
76108
}
77109
});
78110
})();

plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)