คุณต้องเข้าสู่ระบบหรือลงทะเบียนก่อนดำเนินการต่อ
Shows detailed ratings for: olx.ro, olx.bg, olx.ua, olx.pt, and olx.pl
// ==UserScript== // @name OLX Detailed Ratings // @name:ro OLX Detalii Ratinguri // @name:bg OLX Подробни Оценки // @name:ua OLX Детальні Оцінки // @name:pt OLX Avaliações Detalhadas // @name:pl OLX Szczegółowe Oceny // @description Shows detailed ratings for: olx.ro, olx.bg, olx.ua, olx.pt, and olx.pl // @description:ro Detalii ratinguri pentru: olx.ro, olx.bg, olx.ua, olx.pt și olx.pl // @description:bg Подробни оценки за: olx.ro, olx.bg, olx.ua, olx.pt и olx.pl // @description:ua Детальні оцінки для: olx.ro, olx.bg, olx.ua, olx.pt та olx.pl // @description:pt Mostra avaliações detalhadas para: olx.ro, olx.bg, olx.ua, olx.pt e olx.pl // @description:pl Pokazuje szczegółowe oceny dla: olx.ro, olx.bg, olx.ua, olx.pt i olx.pl // @author NWP // @namespace https://greasyfork.org/users/877912 // @version 0.2 // @license MIT // @match *://www.olx.ro/* // @match *://www.olx.bg/* // @match *://www.olx.ua/* // @match *://www.olx.pt/* // @match *://www.olx.pl/* // @grant none // @run-at document-end // ==/UserScript== (function () { "use strict"; let debug = false; let globalScore = null; let maxScore = null; let ratingsCount = null; let user_score_data = null; let translationRatings = null; let lastCapturedTranslations = null; let lastCapturedConfig = null; let retriesCount = 0; const maxRetries = 50; // window.__PAGE_TRANSLATIONS is used for product page // window.__INIT_CONFIG__ is used for user page const isEmptyObject = (obj) => { return obj && Object.keys(obj).length === 0 && obj.constructor === Object; }; const log = (...args) => { if (debug) { console.log(...args); } }; const captureData = () => { try { if (window.__PAGE_TRANSLATIONS__ && window.__PAGE_TRANSLATIONS__ !== lastCapturedTranslations) { lastCapturedTranslations = window.__PAGE_TRANSLATIONS__; log('Captured window.__PAGE_TRANSLATIONS__:', lastCapturedTranslations); handleWindowVariable(); } if (window.__INIT_CONFIG__ && window.__INIT_CONFIG__ !== lastCapturedConfig) { lastCapturedConfig = window.__INIT_CONFIG__; log('Captured window.__INIT_CONFIG__:', lastCapturedConfig); handleWindowVariable(); } if (!translationRatings || Object.keys(translationRatings).length === 0) { if (retriesCount < maxRetries) { retriesCount++; log(`Translation data not valid yet, retrying... (${retriesCount}/${maxRetries})`); setTimeout(captureData, 100); } else { console.error('Max retries reached. Translation data could not be retrieved.'); } } } catch (error) { console.error('Error in captureData:', error); } }; const originalFetch = window.fetch; window.fetch = async (...args) => { try { const requestUrl = args[0]; log('Making API request with URL:', requestUrl); if (requestUrl.startsWith("https://khonor.eu-sharedservices.olxcdn.com/api/olx/") && requestUrl.includes("/score/rating")) { try { const response = await originalFetch(...args); log('API response received:', response); const clonedResponse = response.clone(); clonedResponse.json().then((rating_data) => { log('Rating data received:', rating_data); user_score_data = rating_data.body[0]; globalScore = rating_data.body[0].data.score; maxScore = rating_data.body[0].data.range.max; ratingsCount = rating_data.body[0].data.ratings; checkAndUpdateSentimentText(); }).catch((error) => { console.error("Error reading response body:", error); }); return response; } catch (error) { console.error('Error during fetch interception:', error); return originalFetch(...args); } } else { return originalFetch(...args); } } catch (error) { console.error('Error in custom fetch handling:', error); return originalFetch(...args); } }; function waitForElement(selector, callback, timeout = 10000) { try { const startTime = Date.now(); const checkInterval = setInterval(() => { try { let element; if (window.location.href.startsWith("https://www.olx.ro/d/oferta")) { const elements = document.querySelectorAll(selector); element = elements.length > 1 ? elements[1] : null; } else if (window.location.href.startsWith("https://www.olx.pl/d/oferta")) { const elements = document.querySelectorAll(selector); element = elements.length > 1 ? elements[1] : null; } else { element = document.querySelector(selector); } if (element) { clearInterval(checkInterval); callback(element); } else if (Date.now() - startTime > timeout) { clearInterval(checkInterval); console.error(`Timeout: Element ${selector} not found within ${timeout}ms`); } } catch (error) { console.error('Error in waitForElement interval check:', error); clearInterval(checkInterval); } }, 100); } catch (error) { console.error('Error in waitForElement:', error); } } function checkAndUpdateSentimentText() { try { if (!translationRatings || Object.keys(translationRatings).length === 0) { if (retriesCount < maxRetries) { retriesCount++; log(`Translation data not ready yet. Deferring update... (${retriesCount}/${maxRetries})`); setTimeout(checkAndUpdateSentimentText, 100); } else { console.error('Max retries reached. Could not update sentiment text.'); } return; } updateSentimentText(); } catch (error) { console.error('Error in checkAndUpdateSentimentText:', error); } } function updateSentimentText() { try { const currentUrl = window.location.href; let sentimentSpanSelector; if (currentUrl.startsWith("https://www.olx.ro/d/oferta/")) { sentimentSpanSelector = 'div.css-1k5snlb'; } else if (currentUrl.startsWith("https://www.olx.ro/oferte/")) { sentimentSpanSelector = 'div.css-1k5snlb'; } else if (currentUrl.startsWith("https://www.olx.pl/d/oferta/")) { sentimentSpanSelector = 'p.css-1usyphe'; } else if (currentUrl.startsWith("https://www.olx.pl/oferty/")) { sentimentSpanSelector = 'p.css-1usyphe, p.css-1omjrm'; } else { sentimentSpanSelector = 'span[data-testid="sentiment-title"]'; } waitForElement(sentimentSpanSelector, (sentimentSpan) => { try { log('Found sentiment span:', sentimentSpan); if (sentimentSpan && globalScore !== null && sentimentSpan.dataset.scoreUpdated !== "true") { sentimentSpan.style.fontWeight = "bold"; sentimentSpan.style.color = "white"; sentimentSpan.dataset.scoreUpdated = "true"; const score = user_score_data.data.score; const bucketSpec = user_score_data.bucketSpec; const foundRange = bucketSpec.find((bucket) => score >= bucket.range.min && score <= bucket.range.max); if (foundRange) { const scoreContainer = document.createElement("div"); scoreContainer.style.display = "flex"; scoreContainer.style.flexDirection = "column"; scoreContainer.style.alignItems = "flex-start"; scoreContainer.style.marginTop = "0.3125rem"; if (currentUrl.startsWith("https://www.olx.pl/")) { const oldRatingLabel = document.createElement("span"); oldRatingLabel.id = "old_rating"; oldRatingLabel.style.fontSize = "0.938rem"; oldRatingLabel.style.fontWeight = "bold"; oldRatingLabel.style.color = "white"; oldRatingLabel.textContent = `Stara ocena: ${translationRatings[user_score_data.data.label]}`; scoreContainer.appendChild(oldRatingLabel); } const inlineContainer = document.createElement("div"); inlineContainer.style.display = "flex"; inlineContainer.style.alignItems = "center"; const fullRangeComparison = `[${foundRange.range.min} - ${score} - ${foundRange.range.max}]`; const scoreText = document.createElement("span"); scoreText.id = "score"; scoreText.textContent = `[${globalScore}/${maxScore}] ${fullRangeComparison}`; scoreText.style.fontSize = "0.875rem"; scoreText.style.fontWeight = "bold"; scoreText.style.color = "white"; inlineContainer.appendChild(scoreText); appendSVG(inlineContainer); scoreContainer.appendChild(inlineContainer); const totalRatingsElement = document.querySelector('span[data-testid="total-ratings"]'); if (totalRatingsElement !== null) { totalRatingsElement.style.fontSize = "0.875rem"; totalRatingsElement.style.fontWeight = "bold"; totalRatingsElement.style.color = "white"; } else { const totalRatingsSpan = document.createElement("span"); totalRatingsSpan.id = "total_rating"; totalRatingsSpan.style.fontSize = "0.875rem"; totalRatingsSpan.style.fontWeight = "bold"; totalRatingsSpan.style.color = "white"; let language = document.querySelector('meta[http-equiv="content-language"]')?.getAttribute("content"); if (language == "pl") { totalRatingsSpan.textContent = `(${ratingsCount} ${ratingsCount == 1 ? "stara ocena" : "stare oceny"})`; } else if (language == "ro"){ totalRatingsSpan.textContent = `(${ratingsCount} ${ratingsCount == 1 ? "rating vechi" : "ratinguri vechi"})`; } else { totalRatingsSpan.textContent = `(${ratingsCount} ${ratingsCount == 1 ? "rating" : "ratings"})`; } scoreContainer.appendChild(totalRatingsSpan); } sentimentSpan.after(scoreContainer); } } else { log('Sentiment span not found, globalScore is null, or score already updated'); } } catch (error) { console.error('Error in waitForElement callback (updateSentimentText):', error); } }); } catch (error) { console.error('Error in updateSentimentText:', error); } } function appendSVG(inlineContainer) { try { const svgNamespace = "http://www.w3.org/2000/svg"; const svgElement = document.createElementNS(svgNamespace, "svg"); svgElement.setAttribute("width", "1.2rem"); svgElement.setAttribute("height", "1.2rem"); svgElement.setAttribute("viewBox", "0 0 24 24"); svgElement.setAttribute("fill", "currentColor"); svgElement.id = "rating_legend_tooltip"; svgElement.style.marginTop = "0.05rem"; svgElement.style.marginLeft = "0.3125rem"; svgElement.style.cursor = "pointer"; svgElement.style.transform = 'rotate(180deg)'; const path = document.createElementNS(svgNamespace, "path"); path.setAttribute("d", "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z"); svgElement.appendChild(path); inlineContainer.appendChild(svgElement); const tooltip = document.createElement("div"); tooltip.className = "tooltip"; tooltip.style.position = "absolute"; tooltip.style.display = "none"; tooltip.style.border = "1px solid #ccc"; tooltip.style.backgroundColor = "#f9f9f9"; tooltip.style.padding = "0.625rem"; tooltip.style.zIndex = "1000"; document.body.appendChild(tooltip); let tooltipVisible = false; svgElement.addEventListener("click", (event) => { try { event.stopPropagation(); tooltipVisible = !tooltipVisible; if (tooltipVisible) { showTooltip(event, tooltip, svgElement); } else { hideTooltip(tooltip); } } catch (error) { console.error('Error in SVG click event (appendSVG):', error); } }); document.addEventListener("click", (event) => { try { if (!svgElement.contains(event.target) && tooltipVisible) { hideTooltip(tooltip); tooltipVisible = false; } } catch (error) { console.error('Error in document click event (appendSVG):', error); } }, true); svgElement.style.fill = 'white'; svgElement.addEventListener('click', function(event) { event.preventDefault(); }); svgElement.addEventListener('click', function(event) { event.stopPropagation(); }); } catch (error) { console.error('Error in appendSVG:', error); } } function showTooltip(event, tooltip, svgElement) { try { event.stopPropagation(); updateTooltipContent(tooltip); const svgRect = svgElement.getBoundingClientRect(); tooltip.style.left = `${svgRect.right + window.scrollX}px`; tooltip.style.top = `${svgRect.bottom + window.scrollY}px`; tooltip.style.display = "block"; } catch (error) { console.error('Error in showTooltip:', error); } } function updateTooltipContent(tooltip) { try { let tooltipContent = ""; if (!translationRatings || Object.keys(translationRatings).length === 0) { if (retriesCount < maxRetries) { retriesCount++; log(`Translation data not available, retrying... (${retriesCount}/${maxRetries})`); setTimeout(() => { updateTooltipContent(tooltip); }, 100); } else { console.error('Max retries reached. Could not update tooltip content.'); } return; } user_score_data.bucketSpec.slice().reverse().forEach((bucket) => { if (bucket.bucketName !== "none" && bucket.range.min !== null && bucket.range.max !== null) { tooltipContent += `<p style="margin-top: 0.3125rem; margin-bottom: 0.3125rem;"><strong>${translationRatings[bucket.bucketName] || bucket.bucketName}</strong>: ${bucket.range.min} - ${bucket.range.max}</p>`; } }); tooltip.innerHTML = tooltipContent; } catch (error) { console.error('Error in updateTooltipContent:', error); } } function hideTooltip(tooltip) { try { tooltip.style.display = "none"; } catch (error) { console.error('Error in hideTooltip:', error); } } const handleWindowVariable = () => { try { let translationsValid = false; if (window.__PAGE_TRANSLATIONS__ && !isEmptyObject(window.__PAGE_TRANSLATIONS__)) { const ratingDataNames = JSON.parse(window.__PAGE_TRANSLATIONS__); if (ratingDataNames.pageTranslations && ratingDataNames.pageTranslations.adview && ratingDataNames.pageTranslations.adview["srt.rating.superTitle"] && ratingDataNames.pageTranslations.adview["srt.rating.goodTitle"] && ratingDataNames.pageTranslations.adview["srt.rating.fairTitle"] && ratingDataNames.pageTranslations.adview["srt.rating.poorTitle"]) { translationRatings = { super: ratingDataNames.pageTranslations.adview["srt.rating.superTitle"], good: ratingDataNames.pageTranslations.adview["srt.rating.goodTitle"], fair: ratingDataNames.pageTranslations.adview["srt.rating.fairTitle"], poor: ratingDataNames.pageTranslations.adview["srt.rating.poorTitle"], }; log('%cwindow.__PAGE_TRANSLATIONS__ found and valid:', 'color: green;', translationRatings); translationsValid = true; } else { console.warn('window.__PAGE_TRANSLATIONS__ found but missing required translation keys.'); } } if (!translationsValid && window.__INIT_CONFIG__) { const ratingDataNames = JSON.parse(window.__INIT_CONFIG__); const locale = ratingDataNames.appConfig.locale; translationRatings = { super: ratingDataNames.language.messages[locale]["srt.rating.superTitle"], good: ratingDataNames.language.messages[locale]["srt.rating.goodTitle"], fair: ratingDataNames.language.messages[locale]["srt.rating.fairTitle"], poor: ratingDataNames.language.messages[locale]["srt.rating.poorTitle"], }; log('%cwindow.__INIT_CONFIG__ used as fallback:', 'color: red;', translationRatings); } checkAndUpdateSentimentText(); } catch (error) { console.error('Error in handleWindowVariable:', error); } }; const observer = new MutationObserver((mutationsList, observer) => { try { for (let mutation of mutationsList) { if (mutation.type === 'childList' || mutation.type === 'attributes') { try { captureData(); } catch (error) { console.error('Error in captureData during MutationObserver:', error); } } } if (translationRatings && Object.keys(translationRatings).length > 0) { log('Translation data captured. Disconnecting observer.'); observer.disconnect(); } } catch (error) { console.error('Error in MutationObserver callback:', error); } }); observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true }); document.addEventListener('DOMContentLoaded', () => { try { captureData(); } catch (error) { console.error('Error on DOMContentLoaded:', error); } }); log('OLX Rating Modifier Script Initialized'); })();