GitHub Collapse In Comment

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

当前为 2021-07-21 提交的版本,查看 最新版本

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

QingJ © 2025

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