// ==UserScript== // @name GreasyFork/SleazyFork Star Rating Display // @name:de GreasyFork/SleazyFork Sterne-Bewertungsanzeige // @name:en GreasyFork/SleazyFork Star Rating Display // @name:fr GreasyFork/SleazyFork Affichage der l'évaluation par étoiles // @name:zh-CN GreasyFork/SleazyFork 星级评分显示 // // @description Turns the visual 3-level green/yellow/red bar into a 0..5 star rating. // @description:de Verwandelt die visuelle 3-stufige grüne/gelbe/rote Leiste in eine 0..5-Sterne-Bewertung. // @description:en Turns the visual 3-level green/yellow/red bar into a 0..5 star rating. // @description:fr Transforme la barre visuelle verte/jaune/rouge à 3 niveaux en une évaluation de 0 à 5 étoiles. // @description:zh-CN 将视觉上的绿/黄/紅 3 级进度条转换为 0..5 星级评分。 // // @version 0.0.8 // @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://greasyfork.org/vite/assets/blacklogo16-DftkYuVe.png // // @match https://greasyfork.org/*/scripts* // @match https://greasyfork.org/*/users/* // @match https://sleazyfork.org/*/scripts* // @match https://sleazyfork.org/*/users/* // // @grant GM_xmlhttpRequest // @grant GM_info // @grant GM_notification // @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 https://update.greasyfork.icu/scripts/576222/GreasyForkSleazyFork%20Star%20Rating%20Display.user.js // @updateURL https://update.greasyfork.icu/scripts/576222/GreasyForkSleazyFork%20Star%20Rating%20Display.meta.js // ==/UserScript== (function() { 'use strict'; const incrementVisibility = () => { let views = parseInt(localStorage.getItem('star_rating_views') || '0'); views++; localStorage.setItem('star_rating_views', views); if (views === 2500) { const donationUrl = (typeof GM_info !== 'undefined') ? GM_info.script.header.match(/@contributionURL\s+(.+)/)[1] : "https://www.paypal.com/donate?hosted_button_id=BYW9D395KJWZ2"; GM_notification({ title: '☕ Support Wack.3gp', text: `You've viewed ${views} script ratings! Enjoying the star display? Click here to buy me a coffee.`, image: 'https://greasyfork.org/vite/assets/blacklogo96-CxYTSM_T.png', timeout: 10000, onclick: function() { window.open(donationUrl, "_blank"); } }); localStorage.setItem('star_rating_views', '0'); } }; 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; incrementVisibility(); 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 }); })();