GitHub Code Colors

A userscript that adds a color swatch next to the code color definition

目前为 2018-07-16 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name GitHub Code Colors
  3. // @version 1.2.0
  4. // @description A userscript that adds a color swatch next to the code color definition
  5. // @license MIT
  6. // @author Rob Garrison
  7. // @namespace https://github.com/Mottie
  8. // @include https://github.com/*
  9. // @run-at document-idle
  10. // @grant GM.addStyle
  11. // @grant GM_addStyle
  12. // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js?updated=20180103
  13. // @require https://greasyfork.org/scripts/28721-mutations/code/mutations.js?version=597950
  14. // @icon https://assets-cdn.github.com/pinned-octocat.svg
  15. // ==/UserScript==
  16. (() => {
  17. "use strict";
  18.  
  19. GM.addStyle(`
  20. .ghcc-block { width:12px; height:12px; display:inline-block;
  21. vertical-align:middle; margin-right:4px; border-radius:3px;
  22. border:1px solid rgba(119, 119, 119, 0.5); }
  23. `);
  24.  
  25. const namedColors = [
  26. "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
  27. "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
  28. "burlywood", "cadetblue", "chartreuse", "chocolate", "coral",
  29. "cornflowerblue", "cornsilk", "crimson", "cyan", "darkblue", "darkcyan",
  30. "darkgoldenrod", "darkgray", "darkgrey", "darkgreen", "darkkhaki",
  31. "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred",
  32. "darksalmon", "darkseagreen", "darkslateblue", "darkslategray",
  33. "darkslategrey", "darkturquoise", "darkviolet", "deeppink", "deepskyblue",
  34. "dimgray", "dimgrey", "dodgerblue", "firebrick", "floralwhite",
  35. "forestgreen", "fuchsia", "gainsboro", "ghostwhite", "gold", "goldenrod",
  36. "gray", "grey", "green", "greenyellow", "honeydew", "hotpink",
  37. "indianred", "indigo", "ivory", "khaki", "lavender", "lavenderblush",
  38. "lawngreen", "lemonchiffon", "lightblue", "lightcoral", "lightcyan",
  39. "lightgoldenrodyellow", "lightgray", "lightgrey", "lightgreen",
  40. "lightpink", "lightsalmon", "lightseagreen", "lightskyblue",
  41. "lightslategray", "lightslategrey", "lightsteelblue", "lightyellow",
  42. "lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine",
  43. "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen",
  44. "mediumslateblue", "mediumspringgreen", "mediumturquoise",
  45. "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
  46. "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange",
  47. "orangered", "orchid", "palegoldenrod", "palegreen", "paleturquoise",
  48. "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum",
  49. "powderblue", "purple", "rebeccapurple", "red", "rosybrown", "royalblue",
  50. "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna",
  51. "silver", "skyblue", "slateblue", "slategray", "slategrey", "snow",
  52. "springgreen", "steelblue", "tan", "teal", "thistle", "tomato",
  53. "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow",
  54. "yellowgreen"
  55. ].join("|");
  56.  
  57. const regexNamed = new RegExp("^(" + namedColors + ")$", "i");
  58. // Ex: #123, #123456 or 0x123456 (unix style colors, used by three.js)
  59. const regexHex = /^(#|0x)([0-9A-F]{6,8}|[0-9A-F]{3,4})$/i;
  60. // Ex: rgb(0,0,0) or rgba(0,0,0,0.2)
  61. const regexRGB = /^rgba?(\([^\)]+\))?/i;
  62. const regexRGBA = /rgba/i;
  63. // Ex: hsl(0,0%,0%) or hsla(0,0%,0%,0.2);
  64. const regexHSL = /^hsla?(\([^\)]+\))?/i;
  65.  
  66. // Misc regex
  67. const regexQuotes = /['"]/g;
  68. const regexUnix = /^0x/;
  69. const regexPercent = /%%/g;
  70.  
  71. // Don't use a div, because GitHub-Dark adds a :hover background
  72. // color definition on divs
  73. const block = document.createElement("span");
  74. block.className = "ghcc-block";
  75.  
  76. function addNode(el, val) {
  77. const node = block.cloneNode();
  78. node.style.backgroundColor = val;
  79. // Don't add node if color is invalid
  80. if (node.style.backgroundColor !== "") {
  81. el.insertBefore(node, el.childNodes[0]);
  82. }
  83. }
  84.  
  85. function getTextContent(el) {
  86. return el ? el.textContent : "";
  87. }
  88.  
  89. function rgb(els, el, txt) {
  90. // Color in a string contains everything
  91. if (el.classList.contains("pl-s")) {
  92. txt = txt.match(regexRGB)[0];
  93. } else {
  94. // Rgb(a) colors contained in multiple "pl-c1" spans
  95. let indx = regexRGBA.test(txt) ? 4 : 3;
  96. const tmp = [];
  97. while (indx) {
  98. tmp.push(getTextContent(els.shift()));
  99. indx--;
  100. }
  101. txt += "(" + tmp.join(",") + ")";
  102. }
  103. addNode(el, txt);
  104. return els;
  105. }
  106.  
  107. function hsl(els, el, txt) {
  108. const tmp = /a$/i.test(txt);
  109. if (el.classList.contains("pl-s")) {
  110. // Color in a string contains everything
  111. txt = txt.match(regexHSL)[0];
  112. } else {
  113. // Traverse this HTML... & els only contains the pl-c1 nodes
  114. // <span class="pl-c1">hsl</span>(<span class="pl-c1">1</span>,
  115. // <span class="pl-c1">1</span><span class="pl-k">%</span>,
  116. // <span class="pl-c1">1</span><span class="pl-k">%</span>);
  117. // using getTextContent in case of invalid css
  118. txt = txt + "(" + getTextContent(els.shift()) + "," +
  119. getTextContent(els.shift()) + "%," +
  120. // Hsla needs one more parameter
  121. getTextContent(els.shift()) + "%" +
  122. (tmp ? "," + getTextContent(els.shift()) : "") + ")";
  123. }
  124. // Sometimes (previews only?) the .pl-k span is nested inside
  125. // the .pl-c1 span, so we end up with "%%"
  126. addNode(el, txt.replace(regexPercent, "%"));
  127. return els;
  128. }
  129.  
  130. // Loop with delay to allow user interaction
  131. function* addBlock(els) {
  132. while (els.length) {
  133. let el = els.shift();
  134. let txt = el.textContent;
  135. if (el.parentNode.classList.contains("pl-c1")) {
  136. // Ignore nested pl-c1 (see https://git.io/vFx8y)
  137. } else if (!el.querySelector(".ghcc-block")) {
  138. if (el.classList.contains("pl-s")) {
  139. txt = txt.replace(regexQuotes, "");
  140. }
  141. if (regexHex.test(txt) || regexNamed.test(txt)) {
  142. addNode(el, txt.replace(regexUnix, "#"));
  143. } else if (regexRGB.test(txt)) {
  144. els = rgb(els, el, txt);
  145. } else if (regexHSL.test(txt)) {
  146. els = hsl(els, el, txt);
  147. }
  148. }
  149. yield els;
  150. }
  151. }
  152.  
  153. function addColors() {
  154. if (document.querySelector(".highlight")) {
  155. let status;
  156. // .pl-c1 targets css hex colors, "rgb" and "hsl"
  157. const els = [...document.querySelectorAll(".pl-c1, .pl-s")];
  158. const iter = addBlock(els);
  159. const loop = () => {
  160. for (let i = 0; i < 40; i++) {
  161. status = iter.next();
  162. }
  163. if (!status.done) {
  164. requestAnimationFrame(loop);
  165. }
  166. };
  167. loop();
  168. }
  169. }
  170. document.addEventListener("ghmo:container", addColors);
  171. document.addEventListener("ghmo:preview", addColors);
  172. addColors();
  173.  
  174. })();