Steam - Glance Cards and Cheevos

Highlights Cards and Achievements at the top of game pages

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Steam - Glance Cards and Cheevos
// @namespace    Lex@GreasyFork
// @version      0.1.8.1
// @description  Highlights Cards and Achievements at the top of game pages
// @author       Lex
// @match        https://store.steampowered.com/app/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    
    // Gets the inner text of an element if it can be found otherwise returns undefined
    const txt = query => { const e = document.querySelector(query); return e && e.innerText.trim(); };

    const cardsNotify = `<span style="cursor:pointer" onclick="document.getElementById('category_block').scrollIntoView()"><img height="14" style="margin-bottom:-1px" src="https://steamstore-a.akamaihd.net/public/images/v6/ico/ico_cards.png"> Cards</span>`;
    const cheevosNotify = `<span style="cursor:pointer" onclick="document.getElementById('achievement_block').scrollIntoView()">🏆 {0} Achievements</span>`;
    const noSingleplayerNotify = `<span style="color:black;background:yellow">No singleplayer!</span>`;
    const profileLimitedNotify = `<span title="Game will not give +1">⚙️ Profile Limited</span>`;
    const learningAboutNotify = `<span title="Game will not give +1">⌛ Learning About</span>`;
    const freeNotify = `<span title="This item is free to add to your account">🆓 Free</span>`;
    const freeToPlayNotify = `<span title="This item is free to play.">💩 Free to Play</span>`;
    const adultOnlyNotify = `🔞 Adult only`;
    const delistedNotify = `<span title="This game is no longer available for purchase on Steam">👻 Delisted</span>`;
    const releaseDate = txt(".not_yet ~ h1 > span");
    const notYetAppend = releaseDate ? ". Planned release date: " + releaseDate : "";
    const notYetNotify = `<span title="This game is not yet available on Steam${notYetAppend}">🌅 Not yet available</span>`;
    const defaultNotify = `<span style="cursor:pointer" onclick="document.getElementById('category_block').scrollIntoView()">No cards or achievements</span>`;

    const hasCards = document.querySelector("img.category_icon[src$='ico_cards.png']") !== null;
    const hasAchievements = document.querySelector("#achievement_block .communitylink_achievement_images") !== null;
    const achievementCount = hasAchievements ? document.querySelector("#achievement_block .block_title").textContent.match(/\d+/)[0] : 0;
    const noSingleplayer = document.querySelector("img.category_icon[src$='ico_singlePlayer.png']") === null;
    const learningAbout = document.querySelector("img.category_icon[src$='ico_learning_about_game.png']") !== null;
    const profileLimited = document.querySelector("img.category_icon[src$='ico_info.png']") !== null;
    const adultOnly = document.querySelector("div.mature_content_notice") !== null;
    const priceElement = document.querySelector(".game_purchase_price,.discount_final_price");
    const price = priceElement ? priceElement.innerText.toLowerCase() : "";
    const isFree = price === "free";
    const isFreeToPlay = price === "free to play";
    const notYet = document.querySelector(".not_yet") !== null;
    
    let isDelisted = false;
    const noticeBox = document.querySelector(".notice_box_content");
    if (noticeBox !== null) {
        isDelisted = /(unlisted|no longer available)/.test(noticeBox.innerText);
    }

    let props = [];
    if (hasCards) props.push(cardsNotify);
    if (hasAchievements) props.push(cheevosNotify.replace("{0}", achievementCount));
    if (noSingleplayer) props.push(noSingleplayerNotify);
    if (profileLimited) props.push(profileLimitedNotify);
    if (learningAbout) props.push(learningAboutNotify);
    if (adultOnly) props.push(adultOnlyNotify);
    if (isFree) props.push(freeNotify);
    if (isFreeToPlay) props.push(freeToPlayNotify);
    if (isDelisted) props.push(delistedNotify);
    if (notYet) props.push(notYetNotify);
    if (!props.length) props.push(defaultNotify);

    // Create Glance container
    const div = document.createElement("div");
    div.style.fontSize = "110%";
    div.innerHTML = props.join(" ");
    document.querySelector(".glance_ctn").prepend(div);


    function expandRating() {
        document.querySelectorAll(".user_reviews_summary_row[data-tooltip-html]").forEach(row => {
            let rating = row.dataset.tooltipHtml.replace(/(\d+)%[^\d]*([\d,]*).*/, "($1% of $2 reviews)");
            // If Steam hasn't determined the rating yet, calculate it
            if (rating.startsWith("Need more")) {
                const total = parseInt(document.querySelector("label[for=review_type_all]").textContent.match(/[\d,]+/)[0].replace(/,/g,''));
                const pos = parseInt(document.querySelector("label[for=review_type_positive]").textContent.match(/[\d,]+/)[0].replace(/,/g,''));
                const score = Math.round(100*pos/total);
                rating = `(${score}% of ${total} reviews)`;
            }
            // Create a new span container
            const newSpan = document.createElement("span");
            newSpan.innerText = rating;
            newSpan.style.whiteSpace = "nowrap";

            const ratingSpan = row.querySelector(".responsive_hidden");
            if (ratingSpan) {
                // Hide the old rating span
                ratingSpan.style.display = "none";
                ratingSpan.after(newSpan);
            } else {
                // There's nothing to hide because Steam hasn't determined a rating yet
                const notEnough = row.querySelector(".not_enough_reviews");
                if (notEnough === null) return;
                notEnough.innerText += ' ';
                notEnough.after(newSpan);
            }
            newSpan.parentElement.style.whiteSpace = "normal";
        });
    }

    expandRating();
})();