// ==UserScript== // @name Wish.com - Price filter & more // @namespace http://tampermonkey.net/ // @version 3.6 // @description Filtering by min/max price, allow hidding nsfw products, see reviews // @author Shuunen // @match https://*.wish.com/* // @grant none // @downloadURL none // ==/UserScript== $(document).ready(function() { console.log('wish price filter : init'); var minPrice = 0; var maxPrice = 1000; var minStars = parseInt(localStorage.abwMinStars) || 1; var hideNsfw = localStorage.abwHideNsfw !== 'false'; // 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; var 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); }; } function fetchData(id, productEl, originalPicture) { console.log('will get data for', id); 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; const priceEl = productEl.find('.feed-actual-price'); priceEl.html(totalPrice + ' ' + getCurrency()); const nsfwMatches = html.match(/sex|lingerie|crotch|masturbat|vibrator|bdsm|bondage|nipple/gi); if (nsfwMatches && nsfwMatches.length) { productEl.addClass('abw-nsfw'); } showHideProduct(productEl, originalPicture); }) .catch((error) => { console.error('did not managed to found ratings for product "', id, '"', error); }); } var currency = null; function getCurrency(){ if(currency){ return currency; } $('.feed-actual-price').each(function(index, el){ if(currency) { return; } var arr = el.textContent.replace(/\d+/, '_').split('_'); if(arr.length === 2){ console.log('currency found at iteration', index); currency = arr[1]; } }); console.log('detected currency :', currency); return currency; } var loadedUrl = '//main.cdn.wish.com/fd9acde14ab5/img/ajax_loader_16.gif?v=13'; function getData(element) { const productEl = $(element.tagName ? element : element.currentTarget); if (productEl.hasClass('abw-with-data')) { // console.log('product has already data'); return; } productEl.addClass('abw-with-data'); const image = productEl.find('a.display-pic'); if (!image || !image[0]) { console.error('did not found image on product', productEl); return; } var href = image.attr('href'); if (!href) { console.error('did not found href on product', productEl); return; } const id = href.split('/').reverse()[0]; const originalPicture = image[0].style.backgroundImage; image[0].style.backgroundImage = 'url(' + loadedUrl + ')'; image[0].style.backgroundSize = '10%'; fetchData(id, productEl, originalPicture); } function getNextData() { const amount = 10; console.log('getting next', amount, 'items data'); $('.feed-product-item:visible:not(.abw-with-data):lt(' + amount + ')').each((index, element) => { setTimeout(() => getData(element), index * 300); }); } function showHideProduct(element, originalPicture) { const productEl = $(element); const priceEl = productEl.find('.feed-actual-price'); var price = priceEl.text().replace(/\D/g, ''); var nbStars = productEl.find('img.abw-star').size(); var priceOk = price <= maxPrice; if (minPrice && minPrice > 0) { priceOk = priceOk && price >= minPrice; } if (priceOk && minStars && minStars > 0 && productEl.hasClass('abw-with-data')) { priceOk = nbStars >= minStars; } if (priceOk && hideNsfw && productEl.hasClass('abw-nsfw')) { priceOk = false; } if (originalPicture) { const image = productEl.find('a.display-pic'); if (!image || !image[0]) { console.error('did not found image on product', productEl); return; } image[0].style.backgroundImage = originalPicture; image[0].style.backgroundSize = '100%'; } if (priceOk) { productEl.show('fast'); if (!productEl.hasClass('abw-on-hover')) { productEl.addClass('abw-on-hover'); productEl.hover(getData); } } else { productEl.hide('fast'); } } function showHideProducts(event) { console.log('wish price filter : showHideProducts'); setTimeout(hideUseless, 100); setTimeout(getNextData, 100); $('.feed-product-item').each((index, element) => { showHideProduct(element); }); } // prepare a debounced function var showHideProductsDebounced = debounce(showHideProducts, 1000); // activate when window is scrolled window.onscroll = showHideProductsDebounced; function hideUseless() { // hide already rated products in order hsitory // $('.edit-rating-button').parents('.transaction-expanded-row-item').remove(); // hide products that can't be rated // $('.late-box').parents('.transaction-expanded-row-item').remove(); $('.transaction-expanded-row-item .rate-button').parents('.transaction-expanded-row-item').addClass('abw-has-rate'); $('.transaction-expanded-row-item:not(.abw-has-rate)').remove(); // delete useless marketing stuff $('.discount-banner, .urgency-inventory, .feed-crossed-price, .product-boost-rect, .header-hello, .badge-details, .faster-shipping-wrapper').remove(); // delete sms reminders $('.transaction-opt-in-banner, .sms-div').remove() } setTimeout(hideUseless, 100); // 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 nsfw : '; html += '
'; $('#header-left').after(html); } // get elements var hideNsfwCheckbox = $('#wtc_hide_nsfw'); var minStarsInput = $('#wtc_min_stars'); var minPriceInput = $('#wtc_min_price'); var maxPriceInput = $('#wtc_max_price'); // restore previous choices hideNsfwCheckbox.attr('checked', hideNsfw); minStarsInput.val(minStars); // start filtering by default setTimeout(() => { showHideProductsDebounced(); getNextData(); }, 1000); // when input value change hideNsfwCheckbox.change((event) => { hideNsfw = event.currentTarget.checked; localStorage.abwHideNsfw = hideNsfw; // console.log('hideNsfw is now', hideNsfw); showHideProductsDebounced(); }); minPriceInput.change((event) => { minPrice = parseInt(event.currentTarget.value) || 0; // console.log('minPrice is now', minPrice); showHideProductsDebounced(); }); maxPriceInput.change((event) => { maxPrice = parseInt(event.currentTarget.value) || 1000; // console.log('maxPrice is now', maxPrice); showHideProductsDebounced(); }); minStarsInput.change((event) => { minStars = parseInt(event.currentTarget.value); localStorage.abwMinStars = minStars; // console.log('minStars is now', minStars); showHideProductsDebounced(); }); });