Greasy Fork 增强

增进 Greasyfork 浏览体验。

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

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