Steam - Glance Cards and Cheevos

Highlights Cards and Achievements at the top of game pages

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