// ==UserScript== // @name GitHub Code Colors // @version 2.0.9 // @description A userscript that adds a color swatch next to the code color definition // @license MIT // @author Rob Garrison // @namespace https://github.com/Mottie // @match https://github.com/* // @match https://gist.github.com/* // @run-at document-idle // @grant GM.addStyle // @grant GM_addStyle // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js?updated=20180103 // @require https://greasyfork.org/scripts/28721-mutations/code/mutations.js?version=1108163 // @require https://greasyfork.org/scripts/387811-color-bundle/code/color-bundle.js?version=719499 // @icon https://github.githubassets.com/pinned-octocat.svg // @updateURL https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-code-colors.user.js // @downloadURL https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-code-colors.user.js // @supportURL https://github.com/Mottie/GitHub-userscripts/issues // ==/UserScript== /* global Color */ (() => { "use strict"; // whitespace:initial => overrides code-wrap css in content GM.addStyle(` .ghcc-block { width:14px; height:14px; display:inline-block; vertical-align:middle; margin-right:4px; border-radius:4px; border:1px solid rgba(119, 119, 119, 0.5); position:relative; background-image:none; cursor:pointer; } .ghcc-popup { position:absolute; background:#222; color:#eee; min-width:350px; top:100%; left:0px; padding:10px; z-index:100; white-space:pre; cursor:text; text-align:left; -webkit-user-select:text; -moz-user-select:text; -ms-user-select:text; user-select:text; } .markdown-body .highlight pre, .markdown-body pre { overflow-y:visible !important; } .ghcc-copy { padding:2px 6px; margin-right:4px; background:transparent; border:0; }`); const namedColors = Object.keys(Color.namedColors); const namedColorsList = namedColors.reduce((acc, name) => { acc[name] = `rgb(${Color.namedColors[name].join(", ")})`; return acc; }, {}); const copyButton = document.createElement("clipboard-copy"); copyButton.className = "btn btn-sm btn-blue tooltipped tooltipped-w ghcc-copy"; 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 = ` `; // Misc regex const regex = { quotes: /['"]/g, unix: /^0x/, percent: /%%/g }; // Don't use a div, because GitHub-Dark adds a :hover background // color definition on divs const block = document.createElement("button"); block.className = "ghcc-block"; block.tabIndex = 0; // prevent submitting on click in comment preview block.type = "button"; block.onclick = "event => event.stopPropagation()"; const br = document.createElement("br"); const popup = document.createElement("span"); popup.className = "ghcc-popup"; const formats = { named: { regex: new RegExp("^(" + namedColors.join("|") + ")$", "i"), convert: color => { const rgb = color.rgb().toString(); if (Object.values(namedColorsList).includes(rgb)) { // There may be more than one named color // e.g. "slategray" & "slategrey" return Object.keys(namedColorsList) .filter(n => namedColorsList[n] === rgb) .join("
"); } return ""; }, }, hex: { // Ex: #123, #123456 or 0x123456 (unix style colors, used by three.js) regex: /^(#|0x)([0-9A-F]{6,8}|[0-9A-F]{3,4})$/i, convert: color => `${color.hex().toString()}`, }, rgb: { regex: /^rgba?(\([^\)]+\))?/i, regexAlpha: /rgba/i, find: (els, el, txt) => { // Color in a string contains everything if (el.classList.contains("pl-s")) { txt = txt.match(formats.rgb.regex)[0]; } else { // Rgb(a) colors contained in multiple "pl-c1" spans let indx = formats.rgb.regexAlpha.test(txt) ? 4 : 3; const tmp = []; while (indx) { tmp.push(getTextContent(els.shift())); indx--; } txt += "(" + tmp.join(",") + ")"; } addNode(el, txt); return els; }, convert: color => { const rgb = color.rgb().alpha(1).toString(); const rgba = color.rgb().toString(); return `${rgb}${rgb === rgba ? "" : "; " + rgba}`; } }, hsl: { // Ex: hsl(0,0%,0%) or hsla(0,0%,0%,0.2); regex: /^hsla?(\([^\)]+\))?/i, find: (els, el, txt) => { const tmp = /a$/i.test(txt); if (el.classList.contains("pl-s")) { // Color in a string contains everything txt = txt.match(formats.hsl.regex)[0]; } else { // Traverse this HTML... & els only contains the pl-c1 nodes // hsl(1, // 1%, // 1%); // using getTextContent in case of invalid css txt = txt + "(" + getTextContent(els.shift()) + "," + getTextContent(els.shift()) + "%," + // Hsla needs one more parameter getTextContent(els.shift()) + "%" + (tmp ? "," + getTextContent(els.shift()) : "") + ")"; } // Sometimes (previews only?) the .pl-k span is nested inside // the .pl-c1 span, so we end up with "%%" addNode(el, txt.replace(regex.percent, "%")); return els; }, convert: color => { const hsl = color.hsl().alpha(1).round().toString(); const hsla = color.hsl().round().toString(); return `${hsl}${hsl === hsla ? "" : "; " + hsla}`; } }, hwb: { convert: color => color.hwb().round().toString() }, cymk: { convert: color => { const cmyk = color.cmyk().round().array(); // array of numbers return `device-cmyk(${cmyk.shift()}, ${cmyk.join("%, ")})`; } }, }; function showPopup(el) { const popup = createPopup(el.style.backgroundColor); el.appendChild(popup); } function hidePopup(el) { el.textContent = ""; } function checkPopup(event) { const el = event.target; if (el && el.classList.contains("ghcc-block")) { if (event.type === "click") { if (el.textContent) { hidePopup(el) } else { showPopup(el); } } } if (event.type === "keyup" && event.key === "Escape") { // hide all popups [...document.querySelectorAll(".ghcc-block")].forEach(el => { el.textContent = ""; }); } } function createPopup(val) { const color = Color(val); const el = popup.cloneNode(); const fragment = document.createDocumentFragment(); Object.keys(formats).forEach(type => { if (typeof formats[type].convert === "function") { const val = formats[type].convert(color); if (val) { const button = copyButton.cloneNode(true); button.value = val; fragment.appendChild(button); fragment.appendChild(document.createTextNode(val)); fragment.appendChild(br.cloneNode()); } } }); el.appendChild(fragment); return el; } function addNode(el, val) { const node = block.cloneNode(); node.style.backgroundColor = val; // Don't add node if color is invalid if (node.style.backgroundColor !== "") { el.insertBefore(node, el.childNodes[0]); } } function getTextContent(el) { return el ? el.textContent : ""; } // Loop with delay to allow user interaction function* addBlock(els) { let last = ""; while (els.length) { let el = els.shift(); let txt = el.textContent; if ( // No swatch for JavaScript Math.tan last === "Math" || // Ignore nested pl-c1 (see https://git.io/fNF3N) el.parentNode && el.parentNode.classList.contains("pl-c1") ) { // noop } else if (!el.querySelector(".ghcc-block")) { if (el.classList.contains("pl-s")) { txt = txt.replace(regex.quotes, ""); } if (formats.hex.regex.test(txt) || formats.named.regex.test(txt)) { addNode(el, txt.replace(regex.unix, "#")); } else if (formats.rgb.regex.test(txt)) { els = formats.rgb.find(els, el, txt); } else if (formats.hsl.regex.test(txt)) { els = formats.hsl.find(els, el, txt); } } last = txt; yield els; } } function addColors() { if (document.querySelector(".highlight")) { let status; // .pl-c1 targets css hex colors, "rgb" and "hsl" const els = [...document.querySelectorAll(".pl-c1, .pl-s, .pl-en, .pl-pds")]; const iter = addBlock(els); const loop = () => { for (let i = 0; i < 40; i++) { status = iter.next(); } if (!status.done) { requestAnimationFrame(loop); } }; loop(); } } document.addEventListener("ghmo:container", addColors); document.addEventListener("ghmo:preview", addColors); document.addEventListener("click", checkPopup); document.addEventListener("keyup", checkPopup); addColors(); })(); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy