Greasy Fork 增强

增进 Greasyfork 浏览体验。

当前为 2023-06-15 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Greasy Fork Enhance
  3. // @name:zh-CN Greasy Fork 增强
  4. // @namespace http://tampermonkey.net/
  5. // @version 0.3.8
  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. let no_run = [".json", ".user.js"];
  18. let is_run = true;
  19. no_run.forEach((suffix) => {
  20. if (window.location.href.endsWith(suffix)) {
  21. is_run = false;
  22. }
  23. });
  24. if (!is_run) return;
  25. let body = document.querySelector("body");
  26. let config = {
  27. opacity: {
  28. "default": 0.2,
  29. "hover": 0.8,
  30. "none": 0.8,
  31. "transition": "opacity 0.2s ease-in-out"
  32. }
  33. };
  34. function sanitify(s) {
  35. // Remove emojis (such a headache)
  36. s = s.replaceAll(/([\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2580-\u27BF]|\uD83E[\uDD10-\uDEFF]|\uFE0F)/g, "");
  37. // Trim spaces and newlines
  38. s = s.trim();
  39. // Replace spaces
  40. s = s.replaceAll(" ", "-");
  41. s = s.replaceAll("%20", "-");
  42. // No more multiple "-"
  43. s = s.replaceAll(/-+/g, "-");
  44. return s;
  45. }
  46. function process(node) { // Add anchor and assign id to given node; Add to outline. Return true if node is actually processed.
  47. if (node.childElementCount > 1 || node.classList.length > 0) return false; // Ignore complex nodes
  48. let text = node.textContent;
  49. node.id = sanitify(text); // Assign id
  50. // Add anchors
  51. let node_ = document.createElement('a');
  52. node_.className = 'anchor';
  53. node_.href = '#' + node.id;
  54. node.appendChild(node_);
  55. let list_item = document.createElement("li");
  56. outline.appendChild(list_item);
  57. let link = document.createElement("a");
  58. link.href = "#" + node.id;
  59. link.text = text;
  60. list_item.appendChild(link);
  61. return true;
  62. }
  63. // Css
  64. let css = document.createElement("style");
  65. css.textContent = `
  66. html {
  67. scroll-behavior: smooth;
  68. }
  69. a.anchor::before {
  70. content: "#";
  71. }
  72. a.anchor {
  73. opacity: 0;
  74. text-decoration: none;
  75. padding: 0px 0.5em;
  76. -moz-transition: all 0.25s ease-in-out;
  77. -o-transition: all 0.25s ease-in-out;
  78. -webkit-transition: all 0.25s ease-in-out;
  79. transition: all 0.25s ease-in-out;
  80. }
  81. h1:hover>a.anchor,
  82. h2:hover>a.anchor,
  83. h3:hover>a.anchor,
  84. h4:hover>a.anchor,
  85. h5:hover>a.anchor,
  86. h6:hover>a.anchor {
  87. opacity: 1;
  88. -moz-transition: all 0.25s ease-in-out;
  89. -o-transition: all 0.25s ease-in-out;
  90. -webkit-transition: all 0.25s ease-in-out;
  91. transition: all 0.25s ease-in-out;
  92. }
  93. a.button {
  94. margin: 0.5em 0 0 0;
  95. display: flex;
  96. align-items: center;
  97. justify-content: center;
  98. text-decoration: none;
  99. color: black;
  100. background-color: #a42121ab;
  101. border-radius: 50%;
  102. width: 2em;
  103. height: 2em;
  104. font-size: 1.8em;
  105. font-weight: bold;
  106. }
  107. div.lum-lightbox {
  108. z-index: 2;
  109. }
  110. div#float-buttons {
  111. position: fixed;
  112. bottom: 1em;
  113. right: 1em;
  114. display: flex;
  115. flex-direction: column;
  116. user-select: none;
  117. z-index: 1;
  118. }
  119. aside.panel {
  120. display: none;
  121. }
  122. .dynamic-opacity {
  123. transition: ${config.opacity.transition};
  124. opacity: ${config.opacity.default};
  125. }
  126. .dynamic-opacity:hover {
  127. opacity: ${config.opacity.hover};
  128. }
  129. input[type=file] {
  130. border-style: dashed;
  131. border-radius: 0.5em;
  132. padding: 0.5em;
  133. background: rgba(169, 169, 169, 0.4);
  134. }
  135. @media (any-hover: none) {
  136. .dynamic-opacity {
  137. opacity: ${config.opacity.none};
  138. }
  139. .dynamic-opacity:hover {
  140. opacity: ${config.opacity.none};
  141. }
  142. }
  143. @media screen and (min-width: 767px) {
  144. aside.panel {
  145. display: contents;
  146. line-height: 1.5;
  147. }
  148. ul.outline {
  149. position: sticky;
  150. float: right;
  151. padding: 0 0 0 0.5em;
  152. margin: 0 0.5em;
  153. max-height: 80vh;
  154. border: 1px solid #BBBBBB;
  155. border-left: 2px solid #F2E5E5;
  156. box-shadow: 0 0 5px #ddd;
  157. background: linear-gradient(to right, #fcf1f1, #FFF 1em);
  158. list-style: none;
  159. width: 10.5%;
  160. color: gray;
  161. border-radius: 5px;
  162. overflow-y: scroll;
  163. z-index: 1;
  164. }
  165. ul.outline > li {
  166. overflow: hidden;
  167. text-overflow: ellipsis;
  168. }
  169. ul.outline > li > a {
  170. color: gray;
  171. white-space: nowrap;
  172. text-decoration: none;
  173. }
  174. }`;
  175. document.querySelector("head").appendChild(css); // Inject css
  176. // Aside panel & Anchors
  177. let outline;
  178. let is_script = /^\/[^\/]+\/scripts/;
  179. let is_specific_script = /^\/[^\/]+\/scripts\/\d+/;
  180. let is_disccussion = /^\/[^\/]+\/discussions/;
  181. let is_users = /^\/[^\/]+\/users/;
  182. let path = window.location.pathname;
  183. if ((!is_script.test(path) && !is_disccussion.test(path) && !is_users.test(path)) || is_specific_script.test(path)) {
  184. let panel = document.createElement("aside");
  185. panel.className = "panel";
  186. body.insertBefore(panel, document.querySelector("body > div.width-constraint"));
  187. let reference_node = document.querySelector("body > div.width-constraint > section");
  188. outline = document.createElement("ul");
  189. outline.classList.add("outline");
  190. outline.classList.add("dynamic-opacity");
  191. outline.style.top = reference_node ? getComputedStyle(reference_node).marginTop : "1em";
  192. outline.style.marginTop = outline.style.top;
  193. panel.appendChild(outline);
  194. let flag = false;
  195. document.querySelectorAll("body > div.width-constraint h1, h2, h3, h4, h5, h6").forEach((node) => {
  196. flag = process(node) || flag; // Not `flag || process(node)`!
  197. });
  198. if (!flag) {
  199. panel.remove();
  200. }
  201. }
  202. // Navigate to hash
  203. let hash = window.location.hash.slice(1);
  204. if (hash) {
  205. let ele = document.getElementById(decodeURIComponent(hash));
  206. if (ele) {
  207. ele.scrollIntoView();
  208. }
  209. }
  210. // Buttons
  211. let buttons = document.createElement("div");
  212. buttons.id = "float-buttons";
  213. let to_top = document.createElement("a");
  214. to_top.classList.add("button");
  215. to_top.classList.add("dynamic-opacity");
  216. to_top.href = "#top";
  217. to_top.text = "↑";
  218. buttons.appendChild(to_top);
  219. body.appendChild(buttons);
  220. // Double click to get to top
  221. body.addEventListener("dblclick", (e) => {
  222. if (e.target == body) {
  223. to_top.click();
  224. }
  225. });
  226. // Fix current tab link
  227. let tab = document.querySelector("ul#script-links > li.current");
  228. if (tab) {
  229. let link = document.createElement("a");
  230. link.href = window.location.pathname;
  231. let orig_child = tab.firstChild;
  232. link.appendChild(orig_child);
  233. tab.appendChild(link);
  234. }
  235. let parts = window.location.pathname.split("/");
  236. if (parts.length <= 2 || (parts.length == 3 && parts[2] === '')) {
  237. let banner = document.querySelector("header#main-header div#site-name");
  238. let img = banner.querySelector("img");
  239. let text = banner.querySelector("#site-name-text > h1");
  240. let link1 = document.createElement("a");
  241. link1.href = window.location.pathname;
  242. img.parentNode.replaceChild(link1, img);
  243. link1.appendChild(img);
  244. let link2 = document.createElement("a");
  245. link2.href = window.location.pathname;
  246. link2.textContent = text.textContent;
  247. text.textContent = "";
  248. text.appendChild(link2);
  249. }
  250. })();