🏠 Home 

You need to sign in or sign up before continuing.

GitHub Gist Copier

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();
})();