Barter.vg, Thumbnail

always display thumbnail of game

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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();
})();