您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
增进 Greasyfork 浏览体验。
当前为
- // ==UserScript==
- // @name Greasy Fork Enhance
- // @name:zh-CN Greasy Fork 增强
- // @namespace http://tampermonkey.net/
- // @version 0.6.3
- // @description Enhance your experience at Greasyfork.
- // @description:zh-CN 增进 Greasyfork 浏览体验。
- // @author PRO
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM_deleteValue
- // @grant GM_registerMenuCommand
- // @grant GM_unregisterMenuCommand
- // @match https://greasyfork.org/*
- // @require https://greasyfork.org/scripts/470224-tampermonkey-config/code/Tampermonkey%20Config.js?version=1244657
- // @icon https://greasyfork.org/vite/assets/blacklogo16-bc64b9f7.png
- // @license gpl-3.0
- // ==/UserScript==
- (function() {
- 'use strict';
- // Judge if the script should run
- let no_run = [".json", ".js"];
- let is_run = true;
- let idPrefix = "greasyfork-enhance-";
- no_run.forEach((suffix) => {
- if (window.location.pathname.endsWith(suffix)) {
- is_run = false;
- }
- });
- if (!is_run) return;
- // Config
- function _bollDest(name, title = undefined, default_value = true) {
- return {
- name: name,
- value: default_value,
- input: "current",
- processor: "not",
- formatter: "boolean",
- autoClose: false,
- title: title
- };
- }
- let config_desc = {
- "auto-hide-code": _bollDest("Auto hide code", "Hide long code blocks by default"),
- "auto-hide-rows": {
- name: "Min rows to hide",
- value: 10,
- processor: "int_range-1-",
- autoClose: false,
- title: "Minimum number of rows to hide"
- },
- "flat-layout": _bollDest("Flat layout", "Use flat layout for script list and descriptions", false),
- "animation": _bollDest("Animation", "Enable animation for toggling code blocks")
- };
- let config = GM_config(config_desc);
- // CSS
- const dynamicStyle = {
- "flat-layout": `
- .script-list li:not(.ad-entry) { padding-right: 0; } ol.script-list > li > article { display: flex; flex-direction: row; justify-content: space-between; align-items: center; }
- ol.script-list > li > article > h2 { width: 60%; overflow: hidden; text-overflow: ellipsis; margin-right: 0.5em; padding-right: 0.5em; border-right: 1px solid #DDDDDD; }
- .showing-all-languages .badge-js, .showing-all-languages .badge-css, .script-type { display: none; }
- ol.script-list > li > article > h2 > a.script-link { white-space: nowrap; }
- ol.script-list > li > article > h2 > span.script-description { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
- ol.script-list > li > article > div.script-meta-block { width: 40%; column-gap: 0; }
- ol.script-list > li[data-script-type="library"] > article > h2 { width: 80%; }
- ol.script-list > li[data-script-type="library"] > article > div.script-meta-block { width: 20%; column-count: 1; }
- ol.script-list > li > article > div.script-meta-block > dl.inline-script-stats { margin: 0; }
- ol.script-list > li > article > div.script-meta-block > dl.inline-script-stats > dd { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
- #script-info div.script-meta-block { float: right; column-count: 1; max-width: 300px; border-left: 1px solid #DDDDDD; margin-left: 1em; padding-left: 1em; }
- #additional-info { width: calc(100% - 2em - 2px); }`,
- "animation": `
- /* Toggle code animation */
- pre > code { transition: height 0.5s ease-in-out 0s; }
- /* Adapted from animate.css - https://animate.style/ */
- :root { --animate-duration: 1s; --animate-delay: 1s; --animate-repeat: 1; }
- .animate__animated { animation-duration: var(--animate-duration); animation-fill-mode: both; }
- .animate__animated.animate__fastest { animation-duration: calc(var(--animate-duration) / 3); }
- @keyframes tada {
- from { transform: scale3d(1, 1, 1); }
- 10%, 20% { transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); }
- 30%, 50%, 70%, 90% { transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); }
- 40%, 60%, 80% { transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); }
- to { transform: scale3d(1, 1, 1); }
- }
- .animate__tada { animation-name: tada; }
- @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
- .animate__fadeIn { animation-name: fadeIn; }
- @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } }
- .animate__fadeOut { -webkit-animation-name: fadeOut; animation-name: fadeOut; }`
- };
- // Functions
- let body = document.querySelector("body");
- function sanitify(s) {
- // Remove emojis (such a headache)
- s = s.replaceAll(/([\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2580-\u27BF]|\uD83E[\uDD10-\uDEFF]|\uFE0F)/g, "");
- // Trim spaces and newlines
- s = s.trim();
- // Replace spaces
- s = s.replaceAll(" ", "-");
- s = s.replaceAll("%20", "-");
- // No more multiple "-"
- s = s.replaceAll(/-+/g, "-");
- return s;
- }
- function process(node) { // Add anchor and assign id to given node; Add to outline. Return true if node is actually processed.
- if (node.childElementCount > 1 || node.classList.length > 0) return false; // Ignore complex nodes
- let text = node.textContent;
- if (!node.id) { // If the node has no id
- node.id = sanitify(text); // Then assign id
- }
- // Add anchors
- let node_ = document.createElement('a');
- node_.className = 'anchor';
- node_.href = '#' + node.id;
- node.appendChild(node_);
- let list_item = document.createElement("li");
- outline.appendChild(list_item);
- let link = document.createElement("a");
- link.href = "#" + node.id;
- link.text = text;
- list_item.appendChild(link);
- return true;
- }
- async function animate(node, animation) {
- return new Promise((resolve, reject) => {
- node.classList.add("animate__animated", "animate__" + animation);
- if (node.getAnimations().length == 0) {
- node.classList.remove("animate__animated", "animate__" + animation);
- reject("No animation available");
- }
- node.addEventListener('animationend', e => {
- e.stopPropagation();
- node.classList.remove("animate__animated", "animate__" + animation);
- resolve("Animation ended");
- }, { once: true });
- });
- }
- async function transition(node, height) {
- return new Promise((resolve, reject) => {
- node.style.height = height;
- if (node.getAnimations().length == 0) {
- resolve("No transition available");
- }
- node.addEventListener('transitionend', e => {
- e.stopPropagation();
- resolve("Transition ended");
- }, { once: true });
- });
- }
- function copyCode() {
- let code = this.parentNode.nextElementSibling;
- let text = code.textContent;
- navigator.clipboard.writeText(text).then(() => {
- this.textContent = "Copied!";
- animate(this, "tada").then(() => {
- this.textContent = "Copy code";
- }, () => {
- window.setTimeout(() => {
- this.textContent = "Copy code";
- }, 1000);
- });
- });
- }
- function toggleCode() {
- let code = this.parentNode.nextElementSibling;
- if (code.style.height == "0px") {
- code.style.willChange = "height";
- transition(code, code.getAttribute("data-height")).then(() => {
- code.style.willChange = "";
- });
- animate(this, "fadeOut").then(() => {
- this.textContent = "Hide code";
- animate(this, "fadeIn");
- }, () => {
- this.textContent = "Hide code";
- });
- } else {
- code.style.willChange = "height";
- transition(code, "0px").then(() => {
- code.style.willChange = "";
- });
- animate(this, "fadeOut").then(() => {
- this.textContent = "Show code";
- animate(this, "fadeIn");
- }, () => {
- this.textContent = "Show code";
- });
- }
- }
- function create_toolbar() {
- let toolbar = document.createElement("div");
- let copy = document.createElement("a");
- let toggle = document.createElement("a");
- toolbar.appendChild(copy);
- toolbar.appendChild(toggle);
- copy.textContent = "Copy code";
- copy.className = "code-operation";
- copy.title = "Copy code to clipboard";
- copy.addEventListener("click", copyCode);
- toggle.textContent = "Hide code";
- toggle.classList.add("code-operation", "animate__fastest");
- toggle.title = "Toggle code display";
- toggle.addEventListener("click", toggleCode);
- // Css
- toolbar.className = "code-toolbar";
- return toolbar;
- }
- function injectCSS(id, css) {
- let style = document.createElement("style");
- style.id = idPrefix + id;
- style.textContent = css;
- document.head.appendChild(style);
- }
- function cssHelper(id, enable) {
- let current = document.getElementById(idPrefix + id);
- if (current) {
- current.disabled = !enable;
- } else if (enable) {
- injectCSS(id, dynamicStyle[id]);
- }
- }
- // Basic css
- injectCSS("basic", `
- html { scroll-behavior: smooth; }
- a.anchor::before { content: "#"; }
- a.anchor { opacity: 0; text-decoration: none; padding: 0px 0.5em; transition: all 0.25s ease-in-out; }
- h1:hover>a.anchor, h2:hover>a.anchor, h3:hover>a.anchor,
- h4:hover>a.anchor, h5:hover>a.anchor, h6:hover>a.anchor { opacity: 1; transition: all 0.25s ease-in-out; }
- a.button { margin: 0.5em 0 0 0; display: flex; align-items: center; justify-content: center; text-decoration: none; color: black; background-color: #a42121ab; border-radius: 50%; width: 2em; height: 2em; font-size: 1.8em; font-weight: bold; }
- div.code-toolbar { display: flex; gap: 1em; }
- a.code-operation { cursor: pointer; font-style: italic; }
- div.lum-lightbox { z-index: 2; }
- div#float-buttons { position: fixed; bottom: 1em; right: 1em; display: flex; flex-direction: column; user-select: none; z-index: 1; }
- aside.panel { display: none; }
- .dynamic-opacity { transition: opacity 0.2s ease-in-out; opacity: 0.2; }
- .dynamic-opacity:hover { opacity: 0.8; }
- input[type=file] { border-style: dashed; border-radius: 0.5em; padding: 0.5em; background: rgba(169, 169, 169, 0.4); }
- table { border: 1px solid #8d8d8d; border-collapse: collapse; width: auto; }
- table td, table th { padding: 0.5em 0.75em; vertical-align: middle; border: 1px solid #8d8d8d; }
- @media (any-hover: none) { .dynamic-opacity { opacity: 0.8; } .dynamic-opacity:hover { opacity: 0.8; } }
- @media screen and (min-width: 767px) {
- aside.panel { display: contents; line-height: 1.5; }
- ul.outline { position: sticky; float: right; padding: 0 0 0 0.5em; margin: 0 0.5em -99vh; max-height: 80vh; border: 1px solid #BBBBBB; border-left: 2px solid #F2E5E5; box-shadow: 0 0 5px #ddd; background: linear-gradient(to right, #fcf1f1, #FFF 1em); list-style: none; width: 10.5%; color: gray; border-radius: 5px; overflow-y: scroll; z-index: 1; }
- ul.outline > li { overflow: hidden; text-overflow: ellipsis; }
- ul.outline > li > a { color: gray; white-space: nowrap; text-decoration: none; }
- }
- pre > code { overflow: hidden; display: block; }`);
- // Aside panel & Anchors
- let outline;
- let is_script = /^\/[^\/]+\/scripts/;
- let is_specific_script = /^\/[^\/]+\/scripts\/\d+/;
- let is_disccussion = /^\/[^\/]+\/discussions/;
- let path = window.location.pathname;
- if ((!is_script.test(path) && !is_disccussion.test(path)) || is_specific_script.test(path)) {
- let panel = document.createElement("aside");
- panel.className = "panel";
- body.insertBefore(panel, document.querySelector("body > div.width-constraint"));
- let reference_node = document.querySelector("body > div.width-constraint > section");
- outline = document.createElement("ul");
- outline.classList.add("outline");
- outline.classList.add("dynamic-opacity");
- outline.style.top = reference_node ? getComputedStyle(reference_node).marginTop : "1em";
- outline.style.marginTop = outline.style.top;
- panel.appendChild(outline);
- let flag = false;
- document.querySelectorAll("body > div.width-constraint h1, h2, h3, h4, h5, h6").forEach((node) => {
- flag = process(node) || flag; // Not `flag || process(node)`!
- });
- if (!flag) {
- panel.remove();
- }
- }
- // Navigate to hash
- let hash = window.location.hash.slice(1);
- if (hash) {
- let ele = document.getElementById(decodeURIComponent(hash));
- if (ele) {
- ele.scrollIntoView();
- }
- }
- // Buttons
- let buttons = document.createElement("div");
- buttons.id = "float-buttons";
- let to_top = document.createElement("a");
- to_top.classList.add("button");
- to_top.classList.add("dynamic-opacity");
- to_top.href = "#top";
- to_top.text = "↑";
- buttons.appendChild(to_top);
- body.appendChild(buttons);
- // Double click to get to top
- body.addEventListener("dblclick", (e) => {
- if (e.target === body) {
- to_top.click();
- }
- });
- // Fix current tab link
- let tab = document.querySelector("ul#script-links > li.current");
- if (tab) {
- let link = document.createElement("a");
- link.href = window.location.pathname;
- let orig_child = tab.firstChild;
- link.appendChild(orig_child);
- tab.appendChild(link);
- }
- let parts = window.location.pathname.split("/");
- if (parts.length <= 2 || (parts.length == 3 && parts[2] === '')) {
- let banner = document.querySelector("header#main-header div#site-name");
- let img = banner.querySelector("img");
- let text = banner.querySelector("#site-name-text > h1");
- let link1 = document.createElement("a");
- link1.href = window.location.pathname;
- img.parentNode.replaceChild(link1, img);
- link1.appendChild(img);
- let link2 = document.createElement("a");
- link2.href = window.location.pathname;
- link2.textContent = text.textContent;
- text.textContent = "";
- text.appendChild(link2);
- }
- // Toolbar for code blocks
- let code_blocks = document.getElementsByTagName("pre");
- let auto_hide = config["auto-hide-code"];
- let auto_hide_rows = config["auto-hide-rows"];
- for (let code_block of code_blocks) {
- if (code_block.firstChild.tagName === "CODE") {
- let height = getComputedStyle(code_block.firstChild).getPropertyValue("height");
- code_block.firstChild.style.height = height;
- code_block.firstChild.setAttribute("data-height", height);
- code_block.insertAdjacentElement("afterbegin", create_toolbar());
- }
- }
- // Auto hide code blocks
- function autoHide() {
- if (!auto_hide) {
- for (let code_block of code_blocks) {
- let toggle = code_block.firstChild.lastChild;
- if (toggle.textContent === "Show code") {
- toggle.click(); // Click the toggle button
- }
- }
- } else {
- for (let code_block of code_blocks) {
- let m = code_block.lastChild.textContent.match(/\n/g);
- let rows = m ? m.length : 0;
- let toggle = code_block.firstChild.lastChild;
- let hidden = toggle.textContent === "Show code";
- if (rows >= auto_hide_rows && !hidden || rows < auto_hide_rows && hidden) {
- code_block.firstChild.lastChild.click(); // Click the toggle button
- }
- }
- }
- }
- document.addEventListener("readystatechange", (e) => {
- if (e.target.readyState === "complete") {
- autoHide();
- }
- }, {once: true});
- // Initialize css
- for (let prop in dynamicStyle) {
- cssHelper(prop, config[prop]);
- }
- // Dynamically respond to config changes
- let callbacks = {
- "auto-hide-code": (after) => {
- auto_hide = after;
- autoHide();
- },
- "auto-hide-rows": (after) => {
- auto_hide_rows = after;
- autoHide();
- },
- "flat-layout": (after) => {
- const meta_orig = document.querySelector("#script-info > #script-content > .script-meta-block");
- const meta_mod = document.querySelector("#script-info > .script-meta-block");
- if (after && meta_orig) {
- const links = document.querySelector("#script-info > #script-links");
- links.after(meta_orig);
- } else if (!after && meta_mod) {
- const additional = document.querySelector("#script-info > #script-content > #additional-info");
- additional.before(meta_mod);
- }
- },
- };
- callbacks["flat-layout"](config["flat-layout"]);
- window.addEventListener(GM_config_event, e => {
- if (e.detail.type === "set") {
- let callback = callbacks[e.detail.prop];
- if (callback && (e.detail.before !== e.detail.after)) {
- callback(e.detail.after);
- }
- if (e.detail.prop in dynamicStyle) {
- cssHelper(e.detail.prop, e.detail.after);
- }
- }
- });
- })();