// ==UserScript==
// @name Barter.vg, Thumbnail
// @namespace http://tampermonkey.net/
// @version 0.5.11
// @description always display thumbnail of game
// @icon https://www.google.com/s2/favicons?sz=64&domain=barter.vg
// @author You
// @match https://barter.vg/*
// @match https://bartervg.com/*
// @grant none
// @run-at document-end
// ==/UserScript==
(function() {
const STORAGE_KEY = "BarterToSteamThumbnailMaps";
const pages = {
bundles : location.pathname.indexOf("/bundles/") == 0,
bundle: location.pathname.indexOf("/bundle/") == 0,
giveways: location.pathname.indexOf("/giveways/") == 0,
browse: location.pathname.indexOf("/browse/") == 0,
wishlist: location.pathname.indexOf("/w/") > 0,
tradable: location.pathname.indexOf("/t/") > 0,
blacklist: location.pathname.indexOf("/b/") > 0,
scrachpad: location.pathname.indexOf("/c/") > 0,
library: location.pathname.indexOf("/l/") > 0,
offer: location.pathname.indexOf("/o/") > 0,
info: location.pathname.indexOf("/i/") == 0,
traded: location.pathname.indexOf("/d/") > 0,
fulfilled: location.pathname.indexOf("/f/") > 0,
matching: location.pathname.indexOf("/m/") > 0,
editing: location.pathname.indexOf("/e/") > 0,
revoked: location.pathname.indexOf("/v/") > 0,
};
console.log("[bt] page info", pages);
const lazyLoading = [];
const thumbnailsMap = restoreThumbnailMap();
const sidRegex = /(app|sub)\/\d+/g;
function restoreThumbnailMap() {
try {
var map = JSON.parse(localStorage[STORAGE_KEY]);
} finally {
return typeof map == "object" ? map : {};
}
}
function saveThumbnailMap() {
localStorage[STORAGE_KEY] = JSON.stringify(thumbnailsMap);
}
function makeThumbUrl(id) {
return `https://steamcdn-a.akamaihd.net/steam/${id.replace("/", "s/")}/capsule_184x69.jpg`;
}
function makeHeaderUrl(id) {
return `https://steamcdn-a.akamaihd.net/steam/${id.replace("/", "s/")}/header.jpg`;
}
// bid : Barter ID
// sid : Steam Store ID (sub/app)
function bidToSid(bid) {
var sid = thumbnailsMap[bid];
if (!sid) return;
if (sid.indexOf("/i/") == 0) {
sid = thumbnailsMap[sid.replace("/i/", "")];
}
return sid;
}
function applyToMatching(barter) {
var bid = barter.href.match(/\d+/g)[0];
var sid = bidToSid(bid);
if (!sid) return;
var li = barter.closest("li");
li.setAttribute("thumbnail", "");
lazyLoading.push({
element: li,
backgroundImage: `url('${makeThumbUrl(sid)}'), url('${makeHeaderUrl(sid)}'), var(--unknown-thumbnail)`,
rect: li.getBoundingClientRect()
});
}
function applyToCollection(barter) {
if (barter.href.indexOf("#") >= 0) return;
var bid = barter.href.match(/\d+/g)[0];
var tr = barter.closest("tr");
if (!tr) return;
var td = barter.closest("td");
if (!td) return;
var sid = bidToSid(bid),
steam = tr.querySelector("a[href*='/app/'], a[href*='/sub/']"),
isSub = steam && steam.href.indexOf("/sub/") >= 0;
if (steam) {
if (!isSub || !sid) {
sid = steam.href.match(sidRegex)[0];
thumbnailsMap[bid] = sid;
/// console.log(`[bt] saved /i/${bid} to ${sid} (${barter.innerText})`);
}
}
var background = sid ? `url('${makeThumbUrl(sid)}'), url('${makeHeaderUrl(sid)}'), var(--unknown-thumbnail)` : `var(--unknown-thumbnail)`;
var nid;
if (isSub && tr.classList.contains("included")) {
var next = tr.nextSibling;
if (next) {
var baseSteam = next.querySelector("[href*='/sub/'], [href*='/app/']"), baseBarter = next.querySelector("[href*='/i/']");
if (baseSteam) {
nid = baseSteam.href.match(sidRegex)[0];
thumbnailsMap[bid] = nid;
// console.log(`[bt] base game is ${nid}`);
} else if (baseBarter) {
nid = bidToSid(baseBarter.href.match(/\d+/g)[0]);
}
if (nid) {
background = `url('${makeThumbUrl(nid)}'), url('${makeHeaderUrl(nid)}'), ${background}`;
}
}
}
if (isSub || sid || nid) {
td.setAttribute("thumbnail", "");
lazyLoading.push({
element: td,
backgroundImage: background,
rect: td.getBoundingClientRect()
});
}
}
function applyToOffer(barter) {
if (barter.href.indexOf("#") >= 0) return;
var bid = barter.href.match(/\d+/g)[0];
var tr = barter.closest("tr");
if (!tr) return;
var td = barter.closest("td");
if (!td) return;
var sid = bidToSid(bid),
steam = tr.querySelector("a[href*='/app/'], a[href*='/sub/']"),
isSub = steam && steam.href.indexOf("/sub/") >= 0;
if (steam) {
if (!isSub || !sid) {
sid = steam.href.match(sidRegex)[0];
thumbnailsMap[bid] = sid;
/// console.log(`[bt] saved /i/${bid} to ${sid} (${barter.innerText})`);
}
}
var background = sid ? `url('${makeThumbUrl(sid)}'), url('${makeHeaderUrl(sid)}'), var(--unknown-thumbnail)` : `var(--unknown-thumbnail)`;
var nid;
if (isSub && tr.classList.contains("included")) {
var next = tr.nextSibling;
if (next) {
var baseSteam = next.querySelector("[href*='/sub/'], [href*='/app/']"), baseBarter = next.querySelector("[href*='/i/']");
if (baseSteam) {
nid = baseSteam.href.match(sidRegex)[0];
thumbnailsMap[bid] = nid;
// console.log(`[bt] base game is ${nid}`);
} else if (baseBarter) {
nid = bidToSid(baseBarter.href.match(/\d+/g)[0]);
}
if (nid) {
background = `url('${makeThumbUrl(nid)}'), url('${makeHeaderUrl(nid)}'), ${background}`;
}
}
}
if (sid || nid) {
td.setAttribute("thumbnail", "");
lazyLoading.push({
element: td,
backgroundImage: background,
parent: barter.closest("div"),
});
}
}
var scrollTimer = 0;
function onScroll(ev) {
if (scrollTimer > 0) return;
scrollTimer = setTimeout(function () {
var item, scrollTop, clientHeight, itemTop, preloadingMargin;
for (var i = 0; i < lazyLoading.length; i++) {
item = lazyLoading[i];
if (pages.offer) {
if (ev.target != item.parent) continue;
scrollTop = ev.target.getBoundingClientRect().top;
clientHeight = ev.target.clientHeight;
itemTop = item.element.getBoundingClientRect().top;
preloadingMargin = 100;
} else {
scrollTop = 0;
clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
itemTop = item.element.getBoundingClientRect().top;
preloadingMargin = 500;
}
if (scrollTop - preloadingMargin < itemTop && itemTop < scrollTop + clientHeight + preloadingMargin) {
//if (itemTop < scrollTop + preloadingMargin) {
item.element.style.setProperty("--thumbnail", item.backgroundImage);
// console.log(item.element.parentNode.innerText, scrollTop, clientHeight, itemTop);
lazyLoading.splice(i, 1);
i--;
}
}
scrollTimer = 0;
}, 50);
}
function onMatchingPage() {
var items = document.querySelectorAll(`.matchcol a[href*='/i/']`);
for (var i = 0; i < items.length; i++) {
applyToMatching(items[i]);
}
}
function onCollectionPage() {
var items = document.querySelectorAll(`.collection tr a[href*='/i/']`);
for (var i = 0; i < items.length; i++) {
applyToCollection(items[i])
}
}
function onOfferPage() {
var items = document.querySelectorAll(`.collection tr a[href*='/i/']`);
for (var i = 0; i < items.length; i++) {
applyToOffer(items[i]);
}
}
function compareNumbers(a, b) {
return a - b;
}
function fetchBaseGame() {
var tags = document.querySelectorAll(".tag");
if (tags.length == 0) return;
tags = Array.prototype.filter.call(tags, t => t.innerText.trim() == "included");
tags = Array.prototype.map.call(tags, t => {
var b = t.closest("li").querySelector("a[href*='/i/']");
return parseInt(b.href.match(/\d+/g)[0]);
});
Array.prototype.sort.call(tags, compareNumbers);
return tags[0];
}
function onInfoPage() {
// save thumbnail map
var sid, bid = location.pathname.match(/\d+/)[0];
var steamHeader = document.querySelector(`#main > a[href*='/sub/'] img, #main > a[href*='/app/'] img`);
var isSub = steamHeader && steamHeader.src.indexOf("/subs/") >= 0;
if (steamHeader) {
if (isSub) {
var baseBid = fetchBaseGame();
if (baseBid) {
thumbnailsMap[bid] = `/i/${baseBid}`;
console.log(`[bt] saved ${bid} to ${thumbnailsMap[bid]}`);
}
sid = bidToSid(bid);
if (sid) {
steamHeader.src = makeHeaderUrl(sid);
console.log("[bt] header image was replaced");
}
} else {
var steam = steamHeader.closest("a");
sid = steam.href.match(sidRegex)[0];
thumbnailsMap[bid] = sid;
console.log(`[bt] saved /i/${bid} to ${sid}`);
}
} else {
var steamVersion = document.querySelector("#bc a[href*='/i/']:has(img[src*='steam.png'])");
if (!steamVersion) return;
thumbnailsMap[bid] = `/i/${steamVersion.href.match(/\d+/g)[0]}`;
console.log(`[bt] saved /i/${bid} to ${thumbnailsMap[bid]}`);
}
}
function delay(ts) {
return new Promise((callback) => {
setTimeout(callback, ts);
});
}
function onLoaded () {
// console.log("items", items);
if (pages.info) {
onInfoPage();
} else if (pages.offer) {
onOfferPage();
} else if (pages.matching) {
onMatchingPage();
} else if (pages.bundle) {
// do noting
} else {
onCollectionPage();
}
// console.log("lazyLoading", lazyLoading);
saveThumbnailMap();
if (pages.offer) {
document.querySelectorAll(".collectionSelect").forEach(async function (el) {
await delay(100);
el.addEventListener("scroll", onScroll);
onScroll.call(el, { target: el });
});
} else {
window.addEventListener("scroll", onScroll);
window.dispatchEvent(new Event("scroll"));
}
var style = document.createElement("style");
style.innerHTML = `
body {
--unknown-thumbnail: url('https://bartervg.com/imgs/ico/steam184_69.png');
}
.matchcol [thumbnail] {
position: relative;
}
.matchcol [thumbnail]:before {
display: inline-block;
position: absolute;
content: "";
width: 184px;
min-height: 30px;
right: 0;
pointer-events: none;
opacity: 0.5;
z-index: -1;
background-image: var(--thumbnail);
background-repeat: no-repeat;
background-position: center;
background-size: cover;
}
.collection td { vertical-align: middle; padding-top: 0; padding-bottom: 0; }
.collection td abbr + div { display: inline-block; }
.collection [thumbnail]:before {
display: inline-block;
position: relative;
content: "";
margin-right: 8px;
width: 184px;
min-height: 30px;
vertical-align: middle;
background-image: var(--thumbnail);
background-repeat: no-repeat;
background-position: center;
background-size: cover;
}
.showMoreTable tr.included td[thumbnail] { padding-left: 8px; }
#listEdit { z-index: 100; }
`;
document.querySelector("head").appendChild(style);
}
onLoaded();
})();