// ==UserScript== // @name Wish.com - Price filter & more // @namespace http://tampermonkey.net/ // @version 3.1 // @description Filtering by min/max price, allow hidding free products, see reviews // @author Shuunen // @match https://*.wish.com/* // @grant none // @downloadURL none // ==/UserScript== $(document).ready(function() { console.log('wish price filter : init'); // Returns a function, that, as long as it continues to be invoked, will not // be triggered. The function will be called after it stops being called for // N milliseconds. If `immediate` is passed, trigger the function on the // leading edge, instead of the trailing. function debounce(func, wait, immediate) { var timeout; return function() { var context = this, args = arguments; var later = function() { timeout = null; if (!immediate) func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; } var activeRequests = 0; function fetchData(id, productEl, delay) { console.log('will get data for', id); return new Promise(resolve => { setTimeout(() => { const url = 'https://www.wish.com/c/' + id; console.log('getting data for', id); fetch(url) .then(r => r.text()) .then(html => { const dataMatches = html.match(/"aggregateRating" : ([\w\W\n]+"\n}),/gi); const dataStr = dataMatches[0]; const data = JSON.parse( '{' + dataStr.replace('},', '}').replace(/\n/g, '') + '}' ); const ratings = Math.round(data.aggregateRating.ratingValue * 100) / 100; const count = Math.round(data.aggregateRating.ratingCount); console.log(id, ': found a rating of', ratings, 'over', count, 'reviews :)'); let roundedRatings = Math.round(ratings); let ratingsStr = ''; while(roundedRatings--) { ratingsStr += ''; } if(count > 0){ ratingsStr += '
over ' + count + ' reviews'; } else { ratingsStr = 'no reviews !'; } productEl.find('.feed-details-row2').css('display','flex').css('align-items','center').html(ratingsStr); const shippingMatches = html.match(/"localized_shipping":\s{"localized_value":\s(\d)/i); const shippingFees = parseInt(shippingMatches[1]); console.log(id, ': shipping fees',shippingFees, '€'); const priceMatches = productEl.find('.feed-actual-price').text().match(/\d+/); const price = parseInt((priceMatches && priceMatches.length) ? priceMatches[0] : 0); console.log(id, ': base price',price, '€'); const totalPrice = (shippingFees + price) + ' €'; productEl.find('.feed-actual-price').html(totalPrice); activeRequests--; showHideProductsDebounced(); resolve({ id, ratings, count, shippingFees, price, totalPrice }); }) .catch(error => { activeRequests--; console.error('did not managed to found ratings for product "', id, '"', error); }); },delay); }); } var loadedUrl = '//main.cdn.wish.com/fd9acde14ab5/img/ajax_loader_16.gif?v=13'; function getData() { const productEl = $(this); if(!productEl.hasClass('abw-with-data')){ productEl.addClass('abw-with-data'); const image = productEl.find('a.display-pic'); const id = image.attr('href').split('/').reverse()[0]; const originalPicture = image[0].style.backgroundImage; image[0].style.backgroundImage = 'url('+loadedUrl+')'; image[0].style.backgroundSize = '10%'; fetchData(id, productEl, 200).then(() => { image[0].style.backgroundImage = originalPicture; image[0].style.backgroundSize = '100%'; }); } } function showHideProducts() { // hide already rated products in order hsitory $('.edit-rating-button').parents('.transaction-expanded-row-item').hide(); // hide products that can't be rated $('.late-box').parents('.transaction-expanded-row-item').hide(); // delete useless marketing stuff $('.discount-banner, .urgency-inventory, .feed-crossed-price, .product-boost-rect, .header-hello').remove(); console.log('wish price filter : showHideProducts'); var minPrice = parseInt($("#wtc_min_price").val()) || 0; var maxPrice = parseInt($("#wtc_max_price").val()) || 1000; var minStars = parseInt($("#wtc_min_stars").val()) || 0; var hideFree = $("#wtc_hide_free").is(':checked'); localStorage.abwHidefree = hideFree; localStorage.abwMinStars = minStars; // console.log('wish price filter : hide free items ?', hideFree); var items = $(".feed-actual-price"); $.each(items, function(index) { var product = $(this).parent().parent().parent().parent(); var price = $(this).text().replace(/\D/g,''); var nbStars = product.find('img.abw-star').size(); var priceOk = (price <= maxPrice); if(minPrice && minPrice > 0){ priceOk = priceOk && (price >= minPrice); } if(priceOk && hideFree && this.textContent.includes('Free')){ priceOk = false; } if(minStars && minStars > 0 && product.hasClass('abw-with-data')){ priceOk = priceOk && (nbStars >= minStars); } product.toggle(priceOk); if(priceOk && !product.hasClass('abw-on-hover')) { product.addClass('abw-on-hover'); product.hover(getData); } }); } // insert controllers if ($("#nav-search").length > 0){ $('#mobile-app-buttons').hide(); $('#nav-search-input-wrapper').width(320); var html = '
'; html += 'Min / Max Price :  /'; html += 'Min stars :  '; html += 'Hide free : '; html += '
'; $("#header-left").after(html); } // restore previous choices var hideFreeCheckbox = $("#wtc_hide_free"); hideFreeCheckbox.attr('checked', (localStorage.abwHidefree === 'true')); var minStars = $("#wtc_min_stars"); minStars.val(parseInt(localStorage.abwMinStars) || 1); // setup cron like job /* function cron(){ } setInterval(cron, 1000); */ // prepare a debounced function var showHideProductsDebounced = debounce(showHideProducts, 500); // trigger twice by default showHideProductsDebounced(); setTimeout(showHideProductsDebounced, 1000); // when window is scrolled window.onscroll = showHideProductsDebounced; // when input value change $("#wtc_hide_free").change(showHideProductsDebounced); $("#wtc_min_price").keydown(showHideProductsDebounced); $("#wtc_max_price").keydown(showHideProductsDebounced); $("#wtc_min_stars").keydown(showHideProductsDebounced); });