diff --git a/assets/js/click-to-copy.js b/assets/js/click-to-copy.js
new file mode 100644
index 0000000000..5fdb041a86
--- /dev/null
+++ b/assets/js/click-to-copy.js
@@ -0,0 +1,86 @@
+let codeListings = document.querySelectorAll('.highlight > pre');
+
+for (let index = 0; index < codeListings.length; index++)
+{
+ const codeSample = codeListings[index].querySelector('code');
+ const copyButton = document.createElement("button");
+ copyButton.setAttribute('type', 'button');
+ copyButton.onclick = function() { copyCode(codeSample); };
+ copyButton.classList.add('fas', 'fa-copy');
+
+ const buttonTooltip = document.createElement('div');
+ buttonTooltip.classList.add('c2c-tooltip');
+ buttonTooltip.setAttribute('role', 'tooltip');
+ buttonTooltip.innerHTML += 'Copy to clipboard';
+
+ const buttonDiv = document.createElement('div');
+ buttonDiv.classList.add('click-to-copy');
+
+ // Use Popper to create and handle the tooltip behavior.
+
+ const popperInstance = Popper.createPopper(copyButton, buttonTooltip,
+ {
+ modifiers:
+ [
+ {
+ name: 'offset',
+ options:
+ {
+ offset: [0, -48],
+ },
+ },
+ ],
+ });
+
+ copyButton.addEventListener('click', () =>
+ {
+ buttonTooltip.innerHTML = 'Copied!';
+ });
+
+ copyButton.addEventListener('mouseenter', () =>
+ {
+ buttonTooltip.setAttribute('show-tooltip', '');
+
+ // Enable eventListeners when the code block is on the viewport
+
+ popperInstance.setOptions((options) => ({
+ ...options,
+ modifiers:
+ [
+ ...options.modifiers,
+ { name: 'eventListeners', enabled: true },
+ ],
+ }));
+ popperInstance.update();
+ });
+
+ copyButton.addEventListener('mouseleave', () =>
+ {
+ buttonTooltip.removeAttribute('show-tooltip');
+
+ // Reset the message in case the button was clicked
+ buttonTooltip.innerHTML = 'Copy to clipboard';
+
+ // Disble eventListeners when the code block is NOT on the viewport
+
+ popperInstance.setOptions((options) => ({
+ ...options,
+ modifiers:
+ [
+ ...options.modifiers,
+ { name: 'eventListeners', enabled: false },
+ ],
+ }));
+ });
+
+ buttonDiv.append(copyButton);
+ buttonDiv.append(buttonTooltip);
+ codeListings[index].insertBefore(buttonDiv, codeSample);
+
+}
+
+function copyCode(codeSample)
+{
+ navigator.clipboard.writeText(codeSample.textContent.trim());
+}
+
diff --git a/assets/scss/_code.scss b/assets/scss/_code.scss
index d491beeb45..b96850bff6 100644
--- a/assets/scss/_code.scss
+++ b/assets/scss/_code.scss
@@ -7,10 +7,56 @@
margin: 2rem 0;
padding: 0;
-
+ position: relative;
+
+ .click-to-copy {
+ display: block;
+ text-align: right;
+ height: 1ex;
+ }
+
pre {
margin: 0;
padding: 1rem;
+
+ // Default click-to-copy button
+
+ button {
+ position: absolute;
+ color: $gray-400;
+ border-radius: 3px;
+ border-width: 0;
+ background-color: inherit;
+ box-shadow: 1px 1px $gray-400;
+ right: 8px;
+ top: 6px;
+
+ &:hover {
+ color: $dark;
+ background-color: $gray-400;
+ }
+ &:active {
+ color: $dark;
+ background-color: $gray-400;
+ transform: translateY(2px);
+ }
+ }
+
+ .c2c-tooltip {
+ background: $dark;
+ color: $white;
+ padding: 2px 4px;
+ border-radius: 3px;
+ display: block;
+ visibility: hidden;
+ opacity: 0;
+ transition: visibility 0s, opacity 0.5s linear;
+ }
+
+ .c2c-tooltip[show-tooltip] {
+ visibility: visible;
+ opacity: 1;
+ }
}
}
diff --git a/layouts/partials/scripts.html b/layouts/partials/scripts.html
index cbe15dc7cb..1ecc0c5f1b 100644
--- a/layouts/partials/scripts.html
+++ b/layouts/partials/scripts.html
@@ -48,7 +48,7 @@
{{ printf "onload='renderMathInElement(%s, %s);'" (( .Site.Params.katex.html_dom_element | default "document.body" ) | safeJS ) ( printf "%s" ( $.Site.Params.katex.options | jsonify )) | safeHTMLAttr }}>
{{ end -}}
-{{ $jsPopper := resources.GetRemote "https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" -}}
+{{ $jsPopper := resources.GetRemote "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.9.2/umd/popper.min.js" -}}
{{ $jsBs := resources.Get "vendor/bootstrap/dist/js/bootstrap.js" -}}
{{ $jsBase := resources.Get "js/base.js" -}}
{{ $jsAnchor := resources.Get "js/anchor.js" -}}
@@ -70,6 +70,9 @@
{{ if .Site.Params.prism_syntax_highlighting -}}
+{{ else -}}
+ {{ $c2cJS := resources.Get "js/click-to-copy.js" | minify | fingerprint }}
+
{{ end -}}