Greasy Fork 增强

增进 Greasyfork 浏览体验。

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

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