Adds a CFL revision dropdown next to the creator's name on IMVU product pages, styled with better spacing and size.
// ==UserScript==
// @name IMVU Product Minimal Revision Viewer
// @namespace http://tampermonkey.net/
// @version 1.2
// @description Adds a CFL revision dropdown next to the creator's name on IMVU product pages, styled with better spacing and size.
// @author heapsofjoy
// @match *://*.imvu.com/shop/product.php?products_id=*
// @grant none
// ==/UserScript==
(function () {
'use strict';
const urlParams = new URLSearchParams(window.location.search);
const productId = urlParams.get('products_id');
if (!productId) return;
const baseUrl = `https://userimages-akm.imvu.com/productdata/${productId}`;
const authorElement = document.querySelector('h2 a[href*="manufacturers_id"]');
if (!authorElement) return;
const wrapper = document.createElement('div');
wrapper.style.display = 'flex';
wrapper.style.flexDirection = 'column';
wrapper.style.marginTop = '6px';
const toggleButton = document.createElement('button');
toggleButton.textContent = 'contents.json';
toggleButton.style.background = '#eee';
toggleButton.style.border = '1px solid #ccc';
toggleButton.style.borderRadius = '3px';
toggleButton.style.padding = '4px 8px';
toggleButton.style.fontSize = '12px';
toggleButton.style.cursor = 'pointer';
toggleButton.style.alignSelf = 'flex-start';
const dropdown = document.createElement('div');
dropdown.style.display = 'none';
dropdown.style.position = 'absolute';
dropdown.style.background = '#fff';
dropdown.style.border = '1px solid #ccc';
dropdown.style.padding = '8px';
dropdown.style.borderRadius = '4px';
dropdown.style.boxShadow = '0 2px 8px rgba(0,0,0,0.15)';
dropdown.style.maxHeight = '240px';
dropdown.style.overflowY = 'auto';
dropdown.style.fontSize = '13px';
dropdown.style.minWidth = '85px';
dropdown.style.zIndex = '999';
toggleButton.addEventListener('click', () => {
dropdown.style.display = dropdown.style.display === 'block' ? 'none' : 'block';
});
function checkRevision(revision, misses = 0, maxMisses = 10) {
const url = `${baseUrl}/${revision}/_contents.json`;
fetch(url, { method: 'HEAD' })
.then((response) => {
if (response.ok) {
const link = document.createElement('a');
link.href = url;
link.textContent = `Revision ${revision}`;
link.target = '_blank';
link.style.display = 'block';
link.style.color = '#007bff';
link.style.textDecoration = 'none';
link.style.margin = '6px 0';
link.style.padding = '4px 6px';
link.style.borderRadius = '3px';
link.addEventListener('mouseover', () => link.style.background = '#f0f0f0');
link.addEventListener('mouseout', () => link.style.background = 'transparent');
dropdown.appendChild(link);
checkRevision(revision + 1, 0, maxMisses);
} else {
if (misses < maxMisses) {
checkRevision(revision + 1, misses + 1, maxMisses);
}
}
})
.catch(() => {
if (misses < maxMisses) {
checkRevision(revision + 1, misses + 1, maxMisses);
}
});
}
checkRevision(1, 0, 10);
setTimeout(() => {
if (dropdown.children.length === 0) {
const msg = document.createElement('div');
msg.textContent = 'No revisions found.';
msg.style.color = '#666';
dropdown.appendChild(msg);
}
}, 2000);
const container = document.createElement('div');
container.style.position = 'relative';
container.appendChild(toggleButton);
container.appendChild(dropdown);
wrapper.appendChild(container);
authorElement.parentNode.appendChild(wrapper);
})();