Greasy Fork 增强

增进 Greasyfork 浏览体验。

目前为 2023-05-28 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Greasy Fork Enhance
  3. // @name:zh-CN Greasy Fork 增强
  4. // @namespace http://tampermonkey.net/
  5. // @version 0.2.4
  6. // @description Enhance your experience at Greasyfork.
  7. // @description:zh-CN 增进 Greasyfork 浏览体验。
  8. // @author PRO
  9. // @match https://greasyfork.org/*
  10. // @icon https://greasyfork.org/vite/assets/blacklogo16-bc64b9f7.png
  11. // @license gpl-3.0
  12. // @grant none
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17. // TODO: 毛玻璃/半透明
  18. // HACK: z-index fix; is_display
  19. let body = document.querySelector("body");
  20. function sanitify(s) {
  21. // Remove emojis (such a headache)
  22. s = s.replaceAll(/([\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2580-\u27BF]|\uD83E[\uDD10-\uDEFF]|\uFE0F)/g, "");
  23. // Trim spaces and newlines
  24. s = s.trim();
  25. // Replace spaces
  26. s = s.replaceAll(" ", "-");
  27. s = s.replaceAll("%20", "-");
  28. // No more multiple "-"
  29. s = s.replaceAll(/-+/g, "-");
  30. return s;
  31. }
  32. function process(node) { // Add anchor and assign id to given node; Add to outline. Return true if node is actually processed.
  33. if (node.childElementCount > 1 || node.classList.length > 0) return false; // Ignore complex nodes
  34. node.id = sanitify(node.textContent); // Assign id
  35. // Add anchors
  36. let node_ = document.createElement('a');
  37. node_.className = 'anchor';
  38. node_.href = '#' + node.id;
  39. node.appendChild(node_);
  40. let list_item = document.createElement("li");
  41. outline.appendChild(list_item);
  42. let link = document.createElement("a");
  43. link.href = "#" + node.id;
  44. link.text = node.id;
  45. list_item.appendChild(link);
  46. return true;
  47. }
  48. // Css
  49. let css = document.createElement("style");
  50. css.textContent = `
  51. html {
  52. scroll-behavior: smooth;
  53. }
  54. a.anchor::before {
  55. content: "#";
  56. }
  57. a.anchor {
  58. opacity: 0;
  59. text-decoration: none;
  60. padding: 0px 0.5em;
  61. -moz-transition: all 0.25s ease-in-out;
  62. -o-transition: all 0.25s ease-in-out;
  63. -webkit-transition: all 0.25s ease-in-out;
  64. transition: all 0.25s ease-in-out;
  65. }
  66. h1:hover>a.anchor,
  67. h2:hover>a.anchor,
  68. h3:hover>a.anchor,
  69. h4:hover>a.anchor,
  70. h5:hover>a.anchor,
  71. h6:hover>a.anchor {
  72. opacity: 1;
  73. -moz-transition: all 0.25s ease-in-out;
  74. -o-transition: all 0.25s ease-in-out;
  75. -webkit-transition: all 0.25s ease-in-out;
  76. transition: all 0.25s ease-in-out;
  77. }
  78. @media (any-hover: none) {
  79. a.anchor {
  80. opacity: 1;
  81. }
  82. }
  83. a.button {
  84. margin: 0.5em 0 0 0;
  85. display: flex;
  86. align-items: center;
  87. justify-content: center;
  88. text-decoration: none;
  89. color: black;
  90. background-color: #a42121ab;
  91. border-radius: 50%;
  92. width: 2em;
  93. height: 2em;
  94. font-size: 1.8em;
  95. font-weight: bold;
  96. opacity: 0.8;
  97. }
  98. div.lum-lightbox {
  99. z-index: 2;
  100. }
  101. div#float-buttons {
  102. position: fixed;
  103. bottom: 0;
  104. right: 0;
  105. display: flex;
  106. flex-direction: column;
  107. user-select: none;
  108. padding: 1em;
  109. z-index: 1;
  110. }
  111. aside.panel {
  112. display: none;
  113. }
  114. @media screen and (min-width: 767px) {
  115. aside.panel {
  116. display: block;
  117. line-height: 1.5;
  118. padding: 0;
  119. position: sticky;
  120. top: 0;
  121. height: 0;
  122. z-index: 1;
  123. }
  124. ul.outline {
  125. float: right;
  126. padding: 0 0 0 0.5em;
  127. margin: 0 0.5em;
  128. border: 1px solid #BBBBBB;
  129. border-left: 2px solid #F2E5E5;
  130. box-shadow: 0 0 5px #ddd;
  131. background: linear-gradient(to right, #fcf1f1, #FFF 1em);
  132. list-style: none;
  133. width: 10.5%;
  134. color: gray;
  135. border-radius: 5px;
  136. opacity: 0.8;
  137. }
  138. ul.outline > li {
  139. overflow: hidden;
  140. text-overflow: ellipsis;
  141. }
  142. ul.outline > li > a {
  143. color: gray;
  144. white-space: nowrap;
  145. text-decoration: none;
  146. }
  147. }`;
  148. document.querySelector("head").appendChild(css); // Inject css
  149. // Aside panel & Anchors
  150. let outline;
  151. let no_display = ["scripts", "discussions", "code", "versions", "versions/new"];
  152. let is_display = true;
  153. no_display.forEach((p) => {
  154. is_display = is_display && !window.location.pathname.endsWith(p) && !window.location.pathname.endsWith(p + "/");
  155. });
  156. if (is_display) {
  157. let panel = document.createElement("aside");
  158. panel.className = "panel";
  159. body.insertBefore(panel, document.querySelector("body > div.width-constraint"));
  160. let dummy = document.createElement("div");
  161. let reference_node = document.querySelector("body > div.width-constraint > section");
  162. dummy.style.display = "none";
  163. panel.appendChild(dummy);
  164. outline = document.createElement("ul");
  165. outline.className = "outline";
  166. outline.style.marginTop = reference_node ? getComputedStyle(reference_node).marginTop : "1em";
  167. panel.appendChild(outline);
  168. let flag = false;
  169. document.querySelectorAll("body > div.width-constraint h1, h2, h3, h4, h5, h6").forEach((node) => {
  170. flag = process(node) || flag; // Not `flag || process(node)`!
  171. });
  172. if (!flag) {
  173. let placeholder = document.createElement("li");
  174. placeholder.textContent = "Nothing to show.";
  175. outline.appendChild(placeholder);
  176. }
  177. }
  178. // Navigate to hash
  179. let hash = window.location.hash.slice(1);
  180. if (hash) {
  181. let ele = document.getElementById(decodeURIComponent(hash));
  182. if (ele) {
  183. ele.scrollIntoView();
  184. }
  185. }
  186. // Buttons
  187. let buttons = document.createElement("div");
  188. buttons.id = "float-buttons";
  189. let to_top = document.createElement("a");
  190. to_top.className = "button";
  191. to_top.href = "#top";
  192. to_top.text = "↑";
  193. buttons.appendChild(to_top);
  194. body.appendChild(buttons);
  195. // Double click to get to top
  196. body.addEventListener("dblclick", (e) => {
  197. if (e.target == body) {
  198. to_top.click();
  199. }
  200. });
  201. })();