// ==UserScript== // @name GitHub Copy Code Snippet // @version 0.3.10 // @description A userscript adds a copy to clipboard button on hover of markdown code snippets // @license MIT // @author Rob Garrison // @namespace https://github.com/Mottie // @match https://gist.github.com/* // @run-at document-idle // @grant GM_addStyle // @require https://greasyfork.org/scripts/28721-mutations/code/mutations.js?version=1108163 // @icon https://github.githubassets.com/pinned-octocat.svg // @updateURL https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-copy-code-snippet.user.js // @downloadURL https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-copy-code-snippet.user.js // @supportURL https://github.com/Mottie/GitHub-userscripts/issues // ==/UserScript== (() => { "use strict"; let copyId = 0; const markdownSelector = ".markdown-body, .markdown-format", codeSelector = "pre:not(.gh-csc-pre)", copyButton = document.createElement("clipboard-copy"); copyButton.className = "btn btn-sm tooltipped tooltipped-w gh-csc-button"; copyButton.setAttribute("aria-label", "Copy to clipboard"); // This hint isn't working yet (GitHub needs to fix it) copyButton.setAttribute("data-copied-hint", "Copied!"); copyButton.innerHTML = ` `; GM_addStyle(` .gh-csc-wrap { position: relative; } .gh-csc-wrap:hover .gh-csc-button { display: block; } .gh-csc-button { display: none; padding: 3px 6px; position: absolute; top: 3px; right: 3px; z-index: 20; } .gh-csc-wrap.ghd-code-wrapper .gh-csc-button { right: 31px; } .gh-csc-button svg { vertical-align: text-bottom; } `); function addButton(wrap, code) { if (!wrap.classList.contains("gh-csc-wrap")) { copyId++; // See comments from sindresorhus/refined-github/issues/1278 code.id = `gh-csc-${copyId}`; copyButton.setAttribute("for", `gh-csc-${copyId}`); wrap.classList.add("gh-csc-wrap"); wrap.insertBefore(copyButton.cloneNode(true), wrap.childNodes[0]); } } function init() { const markdown = document.querySelector(markdownSelector); if (markdown) { [...document.querySelectorAll(markdownSelector)].forEach(md => { [...md.querySelectorAll(codeSelector)].forEach(pre => { let code = pre.querySelector("code"); let wrap = pre.parentNode; if (code) { // pre > code addButton(pre, code); } else if (wrap.classList.contains("highlight")) { // div.highlight > pre addButton(wrap, pre); } }); }); } } document.addEventListener("ghmo:container", init); document.addEventListener("ghmo:comments", init); init(); })();
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: