GitHub Collapse In Comment

A userscript that adds a header that can toggle long code and quote blocks in comments

当前为 2019-01-29 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name GitHub Collapse In Comment
  3. // @version 1.0.19
  4. // @description A userscript that adds a header that can toggle long code and quote blocks in comments
  5. // @license MIT
  6. // @author Rob Garrison
  7. // @namespace https://github.com/Mottie
  8. // @include https://github.com/*
  9. // @include https://gist.github.com/*
  10. // @run-at document-idle
  11. // @grant GM_addStyle
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @grant GM_registerMenuCommand
  15. // @require https://gf.qytechs.cn/scripts/28721-mutations/code/mutations.js?version=666427
  16. // @icon https://assets-cdn.github.com/pinned-octocat.svg
  17. // ==/UserScript==
  18. (() => {
  19. "use strict";
  20. /*
  21. Idea from: https://github.com/dear-github/dear-github/issues/166 &
  22. https://github.com/isaacs/github/issues/208
  23. examples:
  24. https://github.com/Mottie/tablesorter/issues/569
  25. https://github.com/jquery/jquery/issues/3195
  26. */
  27. // hide code/quotes longer than this number of lines
  28. let minLines = GM_getValue("gcic-max-lines", 10),
  29. startCollapsed = GM_getValue("gcic-start-collapsed", true);
  30. // extract syntax type from class name
  31. const regex = /highlight(?:-[^\s]+)+/;
  32.  
  33. // syntax highlight class name lookup table
  34. const syntaxClass = {
  35. basic: "HTML",
  36. cs: "C#",
  37. fsharp: "F#",
  38. gfm: "Markdown",
  39. jq: "JSONiq",
  40. shell: "Bash (shell)",
  41. tcl: "Glyph",
  42. tex: "LaTex"
  43. };
  44.  
  45. GM_addStyle(`
  46. .gcic-block {
  47. border:#eee 1px solid;
  48. padding:2px 8px 2px 10px;
  49. border-radius:5px 5px 0 0;
  50. position:relative;
  51. top:1px;
  52. cursor:pointer;
  53. font-weight:bold;
  54. display:block;
  55. }
  56. .gcic-block + .highlight {
  57. border-top:none;
  58. }
  59. .gcic-block + .email-signature-reply {
  60. margin-top:0;
  61. }
  62. .gcic-block:after {
  63. content:"\u25bc ";
  64. float:right;
  65. }
  66. .gcic-block-closed {
  67. border-radius:5px;
  68. margin-bottom:10px;
  69. }
  70. .gcic-block-closed:after {
  71. transform: rotate(90deg);
  72. }
  73. .gcic-block-closed + .highlight, .gcic-block-closed + .email-signature-reply,
  74. .gcic-block-closed + pre {
  75. display:none;
  76. }
  77. `);
  78.  
  79. function makeToggle(name, lines) {
  80. /* full list of class names from (look at "tm_scope" value)
  81. https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
  82. here are some example syntax highlighted class names:
  83. highlight-text-html-markdown-source-gfm-apib
  84. highlight-text-html-basic
  85. highlight-source-fortran-modern
  86. highlight-text-tex
  87. */
  88. let n = (name || "").match(regex);
  89. if (n && n[0]) {
  90. n = n[0].replace(
  91. /(highlight[-\s]|(source-)|(text-)|(html-)|(markdown-)|(-modern))/g, ""
  92. );
  93. n = (syntaxClass[n] || n).toUpperCase().trim();
  94. }
  95. return `${n || "Block"} (${lines} lines)`;
  96. }
  97.  
  98. function addToggles() {
  99. // issue comments
  100. if ($("#discussion_bucket")) {
  101. let indx = 0;
  102. const block = document.createElement("a"),
  103. els = $$(".markdown-body pre, .email-signature-reply"),
  104. len = els.length;
  105.  
  106. // "flash" = blue box styling
  107. block.className = `gcic-block border flash${
  108. startCollapsed ? " gcic-block-closed" : ""
  109. }`;
  110. block.href = "#";
  111.  
  112. // loop with delay to allow user interaction
  113. const loop = () => {
  114. let el, wrap, node, syntaxClass, numberOfLines,
  115. // max number of DOM insertions per loop
  116. max = 0;
  117. while (max < 20 && indx < len) {
  118. if (indx >= len) {
  119. return;
  120. }
  121. el = els[indx];
  122. if (el && !el.classList.contains("gcic-has-toggle")) {
  123. numberOfLines = el.innerHTML.split("\n").length;
  124. if (numberOfLines > minLines) {
  125. syntaxClass = "";
  126. wrap = closest(".highlight", el);
  127. if (wrap && wrap.classList.contains("highlight")) {
  128. syntaxClass = wrap.className;
  129. } else {
  130. // no syntax highlighter defined (not wrapped)
  131. wrap = el;
  132. }
  133. node = block.cloneNode();
  134. node.innerHTML = makeToggle(syntaxClass, numberOfLines);
  135. wrap.parentNode.insertBefore(node, wrap);
  136. el.classList.add("gcic-has-toggle");
  137. if (startCollapsed) {
  138. el.display = "none";
  139. }
  140. max++;
  141. }
  142. }
  143. indx++;
  144. }
  145. if (indx < len) {
  146. setTimeout(() => {
  147. loop();
  148. }, 200);
  149. }
  150. };
  151. loop();
  152. }
  153. }
  154.  
  155. function addBindings() {
  156. document.addEventListener("click", event => {
  157. let els, indx, flag;
  158. const el = event.target;
  159. if (el && el.classList.contains("gcic-block")) {
  160. event.preventDefault();
  161. // shift + click = toggle all blocks in a single comment
  162. // shift + ctrl + click = toggle all blocks on page
  163. if (event.shiftKey) {
  164. els = $$(
  165. ".gcic-block",
  166. event.ctrlKey || event.metaKey ? "" : closest(".markdown-body", el)
  167. );
  168. indx = els.length;
  169. flag = el.classList.contains("gcic-block-closed");
  170. while (indx--) {
  171. els[indx].classList.toggle("gcic-block-closed", !flag);
  172. }
  173. } else {
  174. el.classList.toggle("gcic-block-closed");
  175. }
  176. removeSelection();
  177. }
  178. });
  179. }
  180.  
  181. function update() {
  182. let toggles = $$(".gcic-block"),
  183. indx = toggles.length;
  184. while (indx--) {
  185. toggles[indx].parentNode.removeChild(toggles[indx]);
  186. }
  187. toggles = $$(".gcic-has-toggle");
  188. indx = toggles.length;
  189. while (indx--) {
  190. toggles[indx].classList.remove("gcic-has-toggle");
  191. }
  192. addToggles();
  193. }
  194.  
  195. function $(selector, el) {
  196. return (el || document).querySelector(selector);
  197. }
  198.  
  199. function $$(selector, el) {
  200. return Array.from((el || document).querySelectorAll(selector));
  201. }
  202.  
  203. function closest(selector, el) {
  204. while (el && el.nodeType === 1) {
  205. if (el.matches(selector)) {
  206. return el;
  207. }
  208. el = el.parentNode;
  209. }
  210. return null;
  211. }
  212.  
  213. function removeSelection() {
  214. // remove text selection - https://stackoverflow.com/a/3171348/145346
  215. const sel = window.getSelection ? window.getSelection() : document.selection;
  216. if (sel) {
  217. if (sel.removeAllRanges) {
  218. sel.removeAllRanges();
  219. } else if (sel.empty) {
  220. sel.empty();
  221. }
  222. }
  223. }
  224.  
  225. GM_registerMenuCommand("Set GitHub Collapse In Comment Max Lines", () => {
  226. let val = prompt("Minimum number of lines before adding a toggle:",
  227. minLines);
  228. val = parseInt(val, 10);
  229. if (val) {
  230. minLines = val;
  231. GM_setValue("gcic-max-lines", val);
  232. update();
  233. }
  234. });
  235.  
  236. GM_registerMenuCommand("Set GitHub Collapse In Comment Initial State", () => {
  237. let val = prompt(
  238. "Start with blocks (c)ollapsed or (e)xpanded (first letter necessary):",
  239. startCollapsed ? "collapsed" : "expanded"
  240. );
  241. if (val) {
  242. val = /^c/.test(val || "");
  243. startCollapsed = val;
  244. GM_setValue("gcic-start-collapsed", val);
  245. update();
  246. }
  247. });
  248.  
  249. document.addEventListener("ghmo:container", addToggles);
  250. document.addEventListener("ghmo:preview", addToggles);
  251. addBindings();
  252. addToggles();
  253.  
  254. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址