Fixes the infinite spinning issue on Amazon Vine when trying to select a product variant
// ==UserScript==
// @name Vine Infinite Spinning Fix
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Fixes the infinite spinning issue on Amazon Vine when trying to select a product variant
// @author Prismaris
// @match https://www.amazon.com.au/vine/vine-items*
// @match https://www.amazon.com.be/vine/vine-items*
// @match https://www.amazon.com.br/vine/vine-items*
// @match https://www.amazon.ca/vine/vine-items*
// @match https://www.amazon.cn/vine/vine-items*
// @match https://www.amazon.eg/vine/vine-items*
// @match https://www.amazon.fr/vine/vine-items*
// @match https://www.amazon.de/vine/vine-items*
// @match https://www.amazon.in/vine/vine-items*
// @match https://www.amazon.it/vine/vine-items*
// @match https://www.amazon.co.jp/vine/vine-items*
// @match https://www.amazon.com.mx/vine/vine-items*
// @match https://www.amazon.nl/vine/vine-items*
// @match https://www.amazon.pl/vine/vine-items*
// @match https://www.amazon.sa/vine/vine-items*
// @match https://www.amazon.sg/vine/vine-items*
// @match https://www.amazon.es/vine/vine-items*
// @match https://www.amazon.se/vine/vine-items*
// @match https://www.amazon.com.tr/vine/vine-items*
// @match https://www.amazon.ae/vine/vine-items*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
const origFetch = window.fetch;
function showToast(message) {
const toast = document.createElement('div');
toast.textContent = message;
toast.style.position = 'fixed';
toast.style.bottom = '20px';
toast.style.left = '50%';
toast.style.transform = 'translateX(-50%)';
toast.style.backgroundColor = '#333';
toast.style.color = 'white';
toast.style.padding = '10px 20px';
toast.style.borderRadius = '5px';
toast.style.zIndex = '1000';
document.body.appendChild(toast);
setTimeout(() => {
toast.remove();
}, 3000);
}
window.fetch = async (...args) => {
let response = await origFetch(...args);
const url = args[0] || "";
if (url.startsWith("api/recommendations")) {
let responseData = await response.clone().json();
let fixed = 0;
let arrKeyNames = [];
if (responseData.result && responseData.result.variations) {
// Make the list of all keys/dimensions across all variants:
for (const variation of responseData.result.variations) {
for (const key in variation.dimensions) {
if (!arrKeyNames.includes(key)) {
arrKeyNames.push(key);
}
}
}
responseData.result.variations = responseData.result.variations.map((variation) => {
if (Object.keys(variation.dimensions || {}).length === 0) {
variation.dimensions = {
asin_no: variation.asin,
};
fixed++;
return variation;
}
if (arrKeyNames.length > 0) {
for (const key of arrKeyNames) {
if (!variation.dimensions[key]) {
variation.dimensions[key] = "VH FIX";
fixed++;
}
}
}
for (const key in variation.dimensions) {
let newValue;
//Replace all non-standard characters
newValue = variation.dimensions[key].replace(/[^a-zA-Z0-9\\(){}/.,"'¼½¾+&%# -]/g, "_");
if (newValue !== variation.dimensions[key]) {
variation.dimensions[key] = newValue;
fixed++;
}
//& without a space after it will crash, add a space after it.
newValue = variation.dimensions[key].replace(/[&]([^\\s])/g, "& $1");
if (newValue !== variation.dimensions[key]) {
variation.dimensions[key] = newValue;
fixed++;
}
//Escape the special characters
newValue = variation.dimensions[key].replace(/[/\\()[\]{}]/g, "\\$&");
if (newValue !== variation.dimensions[key]) {
variation.dimensions[key] = newValue;
fixed++;
}
// Remove apostrophes / single quotes which can break jQuery selectors
newValue = variation.dimensions[key].replace(/'+/g, "");
if (newValue !== variation.dimensions[key]) {
variation.dimensions[key] = newValue;
fixed++;
}
// Normalize comma-separated lists to ensure a space after commas
newValue = variation.dimensions[key].replace(/,\s*/g, ", ");
if (newValue !== variation.dimensions[key]) {
variation.dimensions[key] = newValue;
fixed++;
}
// Any variation ending with a space will crash, ensure there is no space at the end.
newValue = variation.dimensions[key].replace(/\s+$/g, "");
if (newValue !== variation.dimensions[key]) {
variation.dimensions[key] = newValue;
fixed++;
}
// The core of the issue is when a special character is at the end of a variation, the jQuery UI which amazon uses will attempt to evaluate it and fail since it attempts to utilize it as part of an html attribute.
// In order to resolve this, we make the string safe for an html attribute by escaping the special characters.
if (!variation.dimensions[key].match(/[a-z0-9)\\]]$/i)) {
variation.dimensions[key] = variation.dimensions[key] + ` VH${fixed}`;
fixed++;
}
// Any variation with a : or ) without a space after will crash, ensure : always has a space after.
newValue = variation.dimensions[key].replace(/([:)])([^\\s])/g, "$1 $2");
if (newValue !== variation.dimensions[key]) {
variation.dimensions[key] = newValue;
fixed++;
}
// Any variation with a ( with a space after will crash, ensure never has a space after.
newValue = variation.dimensions[key].replace(/([(])\\s/g, "$1");
if (newValue !== variation.dimensions[key]) {
variation.dimensions[key] = newValue;
fixed++;
}
// Any variation with a / with a space before it will crash, remove the space before.
newValue = variation.dimensions[key].replace(/(\\s[/])/g, "/");
if (newValue !== variation.dimensions[key]) {
variation.dimensions[key] = newValue;
fixed++;
}
// Any variation with a | by ;.
newValue = variation.dimensions[key].replace(/([|])/g, "-");
if (newValue !== variation.dimensions[key]) {
variation.dimensions[key] = newValue;
fixed++;
}
}
return variation;
});
}
if (fixed > 0) {
console.log(`Vine Infinite Spinning Fix: ${fixed} variation(s) fixed.`);
showToast(`${fixed} variation(s) fixed.`);
}
return new Response(JSON.stringify(responseData));
}
return response;
};
})();