You need to sign in or sign up before continuing.
Adds copy button to Gist files for easy code copying.
// ==UserScript== // @name GitHub Gist Copier // @description Adds copy button to Gist files for easy code copying. // @icon https://github.githubassets.com/favicons/favicon-dark.svg // @version 1.0 // @author afkarxyz // @namespace https://github.com/afkarxyz/misc-scripts/ // @supportURL https://github.com/afkarxyz/misc-scripts/issues // @license MIT // @run-at document-end // @match https://gist.github.com/* // @grant GM_xmlhttpRequest // @grant GM_setClipboard // @connect githubusercontent.com // ==/UserScript== (function() { 'use strict'; function noop() { } function debounce(f, delay) { let timeoutId = null; return function (...args) { if (timeoutId) { clearTimeout(timeoutId); } timeoutId = setTimeout(() => { f.apply(this, args); }, delay); }; } function createCopyButton(fileElement) { const fileActionElement = fileElement.querySelector('.file-actions'); if (!fileActionElement) { return noop; } const rawButton = fileActionElement.querySelector('a[href*="/raw/"]'); if (!rawButton) { return noop; } const button = document.createElement('button'); button.className = 'btn-octicon gist-copy-button'; button.style.marginRight = '5px'; button.innerHTML = ` <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copy"> <path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path> </svg> <svg style="display: none;" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check color-fg-success"> <path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path> </svg> `; const copyIcon = button.querySelector('.octicon-copy'); const checkIcon = button.querySelector('.octicon-check'); let timeoutId = null; const copyHandler = (e) => { if (timeoutId) { return; } e.preventDefault(); const rawUrl = rawButton.href; GM_xmlhttpRequest({ method: 'GET', url: rawUrl, onload: function(response) { if (response.status === 200) { GM_setClipboard(response.responseText, { type: 'text', mimetype: 'text/plain' }); copyIcon.style.display = 'none'; checkIcon.style.display = 'inline-block'; timeoutId = setTimeout(() => { copyIcon.style.display = 'inline-block'; checkIcon.style.display = 'none'; timeoutId = null; }, 500); } }, onerror: function(error) { timeoutId = null; } }); }; button.addEventListener('click', copyHandler); fileActionElement.insertBefore(button, fileActionElement.firstChild); return () => { button.removeEventListener('click', copyHandler); if (timeoutId) { clearTimeout(timeoutId); } button.remove(); }; } function runGistCopy() { let removeAllListeners = noop; function tryCreateCopyButtons() { removeAllListeners(); const fileElements = [...document.querySelectorAll('.file')]; const removeListeners = fileElements.map(createCopyButton); removeAllListeners = () => { removeListeners.map((f) => f()); [...document.querySelectorAll('.gist-copy-button')].forEach((el) => { el.remove(); }); }; } setTimeout(tryCreateCopyButtons, 300); const observer = new MutationObserver(debounce(() => { if (document.querySelectorAll('.file').length > 0 && document.querySelectorAll('.gist-copy-button').length === 0) { tryCreateCopyButtons(); } }, 100)); observer.observe(document.body, { childList: true, subtree: true }); if (window.onurlchange === null) { window.addEventListener('urlchange', debounce(tryCreateCopyButtons, 16)); } } runGistCopy(); })();