Skip to content

Instantly share code, notes, and snippets.

@qb20nh
Last active October 5, 2024 04:06
Show Gist options
  • Save qb20nh/b0ee5285b11493fead661e86bc8da85b to your computer and use it in GitHub Desktop.
Save qb20nh/b0ee5285b11493fead661e86bc8da85b to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name SO copy code snippet
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Adds copy button to code snippets
// @author me
// @match https://stackoverflow.com/questions/*
// @icon https://www.google.com/s2/favicons?domain=stackoverflow.com&sz=64
// @grant none
// @require https://cdnjs.cloudflare.com/ajax/libs/cash/8.1.1/cash.min.js
// ==/UserScript==
(function() {
'use strict';
function copyDivToClipBoard(elem) {
const range = document.createRange();
range.selectNode(elem);
getSelection().removeAllRanges(); // clear current selection
getSelection().addRange(range); // to select text
document.execCommand("copy");
getSelection().removeAllRanges();// to deselect
}
const $ = window.$
const style = `
.js-post-body>.pre_container {
position: relative;
overflow: auto;
}
.js-post-body>.pre_container>button {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: 6px;
top: 6px;
border-radius: 4px;
opacity: 0;
transition: opacity 0.2s ease;
cursor: pointer;
border-style: solid;
}
.js-post-body>.pre_container>button, #tooltip {
width: 30px;
height: 30px;
}
#tooltip {
position: absolute;
visibility: hidden;
pointer-events: none;
margin: 0;
padding: 0;
border: 0;
}
.js-post-body>.pre_container>button:hover {
filter: invert(0.1);
}
.js-post-body>.pre_container>button:active>svg {
transform: translate(1px, 1px);
}
.js-post-body>.pre_container:hover>button {
opacity: 1;
}
`;
const html = `
<button aria-label="Copy" title="Copy">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 460" width="20" height="20" style="outline: none;" aria-hidden="true" tabindex="-1">
<title>Copy</title>
<path d="M426 0H172c-18 0-33 15-33 33v77h30V33c0-2 1-3 3-3h254c2 0 3 1 3 3v254c0 2-1 3-3 3h-75v30h75c18 0 33-15 33-33V33c0-18-15-33-33-33z"></path>
<path d="M288 140H34c-18 0-33 15-33 33v254c0 18 15 33 33 33h254c18 0 33-15 33-33V173c0-18-15-33-33-33zm0 290H34c-2 0-3-1-3-3V173c0-2 1-3 3-3h254c2 0 3 1 3 3v254c0 2-1 3-3 3z"></path>
</svg>
</button>
`;
const $tooltip = $(`
<input type="text" id="tooltip" tabindex="-1" aria-hidden="true">
`);
const container = `<div class="pre_container"></div>`;
const $style = $('<style>').html(style);
function showTooltip(elem, msg) {
const { top, left } = elem.getBoundingClientRect();
const tooltip = window.tooltip;
// Naive approach, first pass
tooltip.style.top = `${top + window.scrollY}px`;
tooltip.style.left = `${left + window.scrollX}px`;
tooltip.setCustomValidity(msg);
tooltip.reportValidity();
}
$('body').append($style).append($tooltip);
$('.js-post-body>pre').each(function () {
const pre = this;
const $pre = $(this);
$pre.wrap($(container));
$pre.parent().append($(html));
$pre.parent().children('button').on('click', function () {
copyDivToClipBoard(pre);
showTooltip(this, 'Copied!');
});
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment