// ==UserScript== // @name GreasyFork/SleazyFork Star Rating Display & Bridge // @name:de GreasyFork/SleazyFork Sterne-Bewertungsanzeige & Brücke // @name:en GreasyFork/SleazyFork Star Rating Display & Bridge // @name:fr GreasyFork/SleazyFork Affichage der l'évaluation par étoiles & Pont // @name:zh-CN GreasyFork/SleazyFork 星级评分显示 & 桥接 // // @description Turns the visual 5nd green/yellow/red bar into a 0..5 star rating. For SleazyFork, it also redirects broken links to GreasyFork. // @description:de Verwandelt die visuelle 5-stufige grüne/gelbe/rote Leiste in eine 0..5-Sterne-Bewertung. Für SleazyFork werden zudem defekte Links automatisch auf GreasyFork umgeleitet. // @description:en Turns the visual 5-level green/yellow/red bar into a 0..5 star rating. For SleazyFork, it also redirects broken links to GreasyFork. // @description:fr Transforme la barre visuelle verte/jaune/rouge à 5 niveaux en une évaluation de 0 à 5 étoiles. Pour SleazyFork, il redirige également les liens rompus vers GreasyFork. // @description:zh-CN 将视觉上的绿/黄/紅 5 级进度条转换为 0..5 星级评分。对于 SleazyFork,它还将损坏的链接重定向到 GreasyFork。 // // @version 0.0.4 // @author Wack.3gp (https://greasyfork.org/users/4792) // @copyright 2026+, Wack.3gp // @namespace https://greasyfork.org/users/4792 // @license CC BY-NC-SA-4.0; https://creativecommons.org/licenses/by-nc-sa/4.0/ // @icon https://www.google.com/s2/favicons?sz=64&domain=greasyfork.org // // @match https://greasyfork.org/*/scripts* // @match https://greasyfork.org/*/users/* // @match https://sleazyfork.org/*/scripts* // @match https://sleazyfork.org/*/users/* // // @grant GM_xmlhttpRequest // @connect sleazyfork.org // @run-at document-end // // @supportURL https://greasyfork.org/scripts/576222/feedback // @compatible Chrome tested with Tampermonkey // @contributionURL https://www.paypal.com/donate/?hosted_button_id=BYW9D395KJWZ2 // @contributionAmount €1.00 // @downloadURL none // ==/UserScript== (function() { 'use strict'; const addStyles = function() { if (document.getElementById('star-rating-style')) return; const style = document.createElement('style'); style.id = 'star-rating-style'; style.textContent = ` .script-list-ratings { display: inline-block !important; min-width: 130px; vertical-align: middle; } .rating-link { text-decoration: none !important; color: inherit !important; display: inline-flex !important; cursor: pointer !important; border: none !important; position: relative; z-index: 100; } .rating-box { display: inline-flex; align-items: center; gap: 6px; font-family: sans-serif; } .rating-num { font-weight: bold; color: #333; font-size: 13px; } .star-outer { position: relative; display: inline-block; font-size: 16px; color: #ddd; letter-spacing: 1px; } .star-outer::before { content: '★★★★★'; } .star-inner { position: absolute; top: 0; left: 0; white-space: nowrap; overflow: hidden; color: #f39c12; } .star-inner::before { content: '★★★★★'; } .rating-total { color: #888; font-size: 12px; } .rating-link:hover .rating-num { color: #f39c12; } .rating-link:hover .star-outer { filter: brightness(1.1); } `; document.head.appendChild(style); }; const checkLinkBridge = function(container) { if (!window.location.hostname.includes('sleazyfork.org')) return; const scriptLink = container.querySelector('.script-link'); if (!scriptLink || scriptLink.dataset.bridgeChecked) return; scriptLink.dataset.bridgeChecked = "true"; GM_xmlhttpRequest({ method: "HEAD", url: scriptLink.href, onload: function(response) { if (response.status === 404) { const allLinks = container.querySelectorAll('a'); allLinks.forEach(a => { const href = a.getAttribute('href'); if (href) { if (href.startsWith('/')) { a.href = "https://greasyfork.org" + href; } else if (href.includes('sleazyfork.org')) { a.href = href.replace('sleazyfork.org', 'greasyfork.org'); } } }); } } }); }; const transformRatings = function() { const ratingContainers = document.querySelectorAll('.script-list-ratings'); ratingContainers.forEach(function(el) { const parentLi = el.closest('li'); if (parentLi) { checkLinkBridge(parentLi); } if (el.querySelector('.rating-box')) return; const goodEl = el.querySelector('.good-rating-count'); const okEl = el.querySelector('.ok-rating-count'); const badEl = el.querySelector('.bad-rating-count'); if (!goodEl && !okEl && !badEl) return; let feedbackUrl = ""; if (parentLi) { const allLinks = parentLi.querySelectorAll('a'); for (let i = 0; i < allLinks.length; i++) { const a = allLinks[i]; const match = a.href.match(/\/scripts\/(\d+)/); if (match) { const baseUrl = a.href.split('/scripts/')[0]; feedbackUrl = baseUrl + '/scripts/' + match[1] + '/feedback'; break; } } } if (!feedbackUrl) { const urlMatch = window.location.pathname.match(/\/scripts\/(\d+)/); if (urlMatch) { const langPart = window.location.origin + window.location.pathname.split('/scripts/')[0]; feedbackUrl = langPart + '/scripts/' + urlMatch[1] + '/feedback'; } } const good = parseInt(goodEl ? goodEl.textContent : 0) || 0; const ok = parseInt(okEl ? okEl.textContent : 0) || 0; const bad = parseInt(badEl ? badEl.textContent : 0) || 0; const total = good + ok + bad; const avg = total > 0 ? ((good * 5) + (ok * 3) + (bad * 1)) / total : 0; const percent = (avg / 5) * 100; if (!feedbackUrl) feedbackUrl = "#"; el.innerHTML = ''; const link = document.createElement('a'); link.href = feedbackUrl; link.className = 'rating-link'; link.title = 'Feedback (Avg: ' + avg.toFixed(2) + ')'; link.innerHTML = `
`; el.appendChild(link); }); }; addStyles(); transformRatings(); const observer = new MutationObserver(transformRatings); observer.observe(document.body, { childList: true, subtree: true }); })();