Last active
November 2, 2021 19:08
-
-
Save andre-st/ae556e9966738a5b3d7d2ff773196207 to your computer and use it in GitHub Desktop.
Adds price-filter UI controls to your own Amazon wishlist pages #injectjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
////////////////////////////////////////////////////////////////////////////// | |
// | |
// AMAZON.DE WISHLESS - WISHLIST PRICE FILTERING | |
// | |
// - adds UI elements to every Amazon wishlist control bar | |
// - optionally filter by price range | |
// - optionally filter by free shipping | |
// - keeps filter settings across wishlists | |
// | |
mixin( ["https://www.amazon.de/hz/wishlist/", "https://www.amazon.de/gp/registry/wishlist/"], () => | |
{ | |
const DEFAULT_MAXPRICE = 10.0; | |
const DEFAULT_MINPRICE = 0.0; | |
const STOR_PREFIX = 'wishless_'; | |
const COLOR_ENABLED = '#dfffef'; | |
const COLOR_DISABLED = '#ffeeee'; | |
const LOCALE = opts.language.replace( '_', '-' ); // 'opts' from Amazon JS, "de_DE" -> "de-DE" | |
const LOCALE_DECIMAL_SEP = Intl.NumberFormat( LOCALE ).formatToParts( 1.1 ).find( part => part.type === 'decimal' ).value; | |
const priceSymbolElm = document.querySelector( '.icp-currency-symbol' ) || | |
document.querySelector( '.a-price-symbol' ); | |
const LOCALE_CURRENCY = priceSymbolElm.innerText; | |
const FREESHIP_TXT_FOR = { | |
'de-DE': [ 'kostenfrei', 'kostenlos' ], | |
'en-GB': [ 'Free' ], | |
'nl-NL': [ 'Gratis' ], | |
'pl-PL': [ 'Darmowa' ], | |
'tr-TR': [ 'Bedava' ], | |
'cs-CZ': [ 'Zdarma' ]}; | |
const LOCALE_FREESHIP_TXT = FREESHIP_TXT_FOR[LOCALE] || ['Error']; | |
if( !FREESHIP_TXT_FOR[LOCALE] ) | |
console.log( '[Wishless]: Please add "' + LOCALE + '" to FREESHIP_TXT_FOR' ); | |
if( !document.querySelector( '#my-lists-tab.a-active' )) | |
return console.log( "[Wishless] Not your wishlist. Will not filter any prices." ); | |
// Add filter-control so user can easily disable, enable or modify price-filtering: | |
const container = document.querySelector( '#control-bar > .a-span-last' ); | |
if( !container ) | |
return console.log( "[Wishless] Cannot find control-bar on your wishlist site" ); | |
const priceWidget = document.createElement( 'SPAN' ); | |
const cbPrice = document.createElement( 'INPUT' ); | |
const inMinPrice = document.createElement( 'INPUT' ); | |
const inMaxPrice = document.createElement( 'INPUT' ); | |
const cbFreeShip = document.createElement( 'INPUT' ); | |
const lbFreeShip = document.createElement( 'LABEL' ); | |
cbFreeShip.setAttribute( 'type', 'checkbox' ); | |
cbPrice .setAttribute( 'type', 'checkbox' ); | |
inMinPrice.setAttribute( 'type', 'number' ); | |
inMinPrice.setAttribute( 'min', 0.00 ); | |
inMinPrice.setAttribute( 'step', 0.10 ); | |
inMaxPrice.setAttribute( 'type', 'number' ); | |
inMaxPrice.setAttribute( 'min', 0.00 ); | |
inMaxPrice.setAttribute( 'step', 0.10 ); | |
// Keep filter settings when visiting other wishlists: | |
// localStorage saves strings only, so boolean converted | |
cbFreeShip.checked = localStorage.getItem( STOR_PREFIX + 'beFreeShip' ) == 'true'; | |
cbPrice .checked = localStorage.getItem( STOR_PREFIX + 'bePrice' ) == 'true'; | |
inMinPrice.value = localStorage.getItem( STOR_PREFIX + 'minPrice' ) || DEFAULT_MINPRICE; | |
inMaxPrice.value = localStorage.getItem( STOR_PREFIX + 'maxPrice' ) || DEFAULT_MAXPRICE; | |
saveSettings = () => { | |
localStorage.setItem( STOR_PREFIX + 'beFreeShip', cbFreeShip.checked ); | |
localStorage.setItem( STOR_PREFIX + 'bePrice', cbPrice .checked ); | |
localStorage.setItem( STOR_PREFIX + 'minPrice', inMinPrice.value ); | |
localStorage.setItem( STOR_PREFIX + 'maxPrice', inMaxPrice.value ); | |
}; | |
cbFreeShip.addEventListener( 'change', saveSettings ); | |
cbPrice .addEventListener( 'change', saveSettings ); | |
inMinPrice.addEventListener( 'input', saveSettings ); | |
inMaxPrice.addEventListener( 'input', saveSettings ); | |
lbFreeShip .appendChild( cbFreeShip ); | |
lbFreeShip .appendChild( document.createTextNode( LOCALE_FREESHIP_TXT[0] )); | |
priceWidget.appendChild( cbPrice ); | |
priceWidget.appendChild( inMinPrice ); | |
priceWidget.appendChild( document.createTextNode( '\u2013' )); | |
priceWidget.appendChild( inMaxPrice ); | |
priceWidget.appendChild( document.createTextNode( LOCALE_CURRENCY )); | |
cbFreeShip .style.cssText = 'vertical-align: unset; margin-right: 0.25em'; | |
cbPrice .style.cssText = 'vertical-align: unset'; | |
inMinPrice .style.cssText = 'width: 4em; margin: 0 0 0 0.35em'; | |
inMaxPrice .style.cssText = 'width: 4em; margin: 0 0.35em 0 0'; | |
priceWidget.style.cssText = 'vertical-align: middle; display: inline-block; font: unset; margin-right: 15px; padding: 5px;'; | |
lbFreeShip .style.cssText = 'vertical-align: middle; display: inline-block; font: unset; margin-right: 15px; padding: 0 5px; line-height: 41px'; | |
container.insertBefore( lbFreeShip, container.children[0] ); | |
container.insertBefore( priceWidget, container.children[0] ); | |
// Adjust the control bar layout: | |
const barCols = document.querySelectorAll( '#control-bar .a-column' ); | |
if( barCols.length >= 2 ) | |
{ | |
barCols[0].classList.replace( 'a-span6', 'a-span2' ); | |
barCols[1].classList.replace( 'a-span6', 'a-span10' ); | |
} | |
setInterval( ()=> | |
{ | |
priceWidget.style.backgroundColor = cbPrice .checked ? COLOR_ENABLED : COLOR_DISABLED; | |
lbFreeShip .style.backgroundColor = cbFreeShip.checked ? COLOR_ENABLED : COLOR_DISABLED; | |
const prods = document.querySelectorAll( 'li[data-price]' ); | |
for( var i = 0; i < prods.length; i++ ) | |
{ | |
const usedPriceElm = cbFreeShip.checked ? null : prods[i].querySelector( '.itemUsedAndNewPrice' ); | |
const freeShipElm = prods[i].querySelector( '.wl-item-delivery-badge' ); | |
var price = parseFloat( prods[i].getAttribute( 'data-price' ) || -1 ); | |
if( usedPriceElm ) | |
{ | |
const usedPriceStr = usedPriceElm.innerText; | |
const usedPriceMat = /.*?(?<amount>[0-9,.']+).*/.exec( usedPriceStr ); | |
const amountParts = usedPriceMat.groups.amount.split( LOCALE_DECIMAL_SEP ); | |
const amountMain = amountParts[0].replace( /[^0-9]/, '' ); // Remove group separator (thousand etc) | |
const amountFract = amountParts[1]; | |
const usedPrice = parseFloat( amountMain + '.' + amountFract ); | |
if( usedPrice < price ) | |
price = usedPrice; | |
} | |
const isFreeShip = freeShipElm && LOCALE_FREESHIP_TXT.some( t => freeShipElm.innerText.toLowerCase().includes( t.toLowerCase() )); | |
const isInBudget = price >= inMinPrice.value && price <= inMaxPrice.value; | |
const canDisplay = ( !cbPrice .checked || isInBudget ) && | |
( !cbFreeShip.checked || isFreeShip ); | |
if( canDisplay ) | |
{ | |
prods[i].style.display = 'list-item'; | |
if( usedPriceElm ) | |
usedPriceElm.style.fontSize = cbPrice.checked ? '1.7em' : 'unset'; | |
} | |
else | |
{ | |
prods[i].style.display = 'none'; | |
} | |
} | |
if( !cbPrice.checked && !cbFreeShip.checked ) return; // Don't load full wishlist | |
// Force loading next pages by triggering Amazon's scroll-handler | |
if( document.querySelector( '.wl-see-more-spinner' )) | |
{ | |
// Amazon's scroll-handler seems to check the scroll-position. | |
// Scrolling forward and back immediately is faster than the renderer, so we won't notice it. | |
// That speed might be the reason why we still need to trigger the scroll-handler in between. | |
// It's ugly but works. Ideally we could call Amazon's page loader function directly, | |
// but it seems burried somewhere deep in minified code with lot of indirection: | |
// maybe sth near window.AmazonUIPageJS._namespace( "NavSharedAssets" ).trigger( "wl-load-more-items" ); | |
// | |
const SCROLLBY = 1500; | |
const scrollEvent = document.createEvent( 'Event' ); | |
scrollEvent.initEvent( "scroll", true, false ); | |
window.scrollBy( 0, SCROLLBY ); | |
window.dispatchEvent( scrollEvent ); | |
window.scrollBy( 0, -SCROLLBY ); | |
} | |
}, 1500 ); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Can be used with any Javascript injection extension such as https://github.com/andre-st/chrome-injectjs