adds .local/share/millennium and easyeffects

This commit is contained in:
2026-08-06 18:23:36 +02:00
parent f92fb90275
commit 8dc466cbac
2581 changed files with 339035 additions and 0 deletions
@@ -0,0 +1,130 @@
function createRankRow(rankTitle, rankValue, textColor) {
const rankRow = document.createElement("div");
rankRow.classList.add("user_reviews_summary_row");
const createDivWithClasses = (...classes) => {
const div = document.createElement("div");
div.classList.add(...classes);
return div;
};
const subtitleDiv = createDivWithClasses("subtitle", "column");
subtitleDiv.textContent = rankTitle;
const summaryDiv = createDivWithClasses("summary", "column", "protondb_rank");
const rankValueSpan = document.createElement("span");
rankValueSpan.classList.add("protondb_rank_value");
rankValueSpan.textContent = rankValue || "N/A";
rankValueSpan.style.color = colors[textColor] || colors.unknown;
rankValueSpan.setAttribute("data-tier", rankValue);
const link = document.createElement("a");
link.href = `https://protondb.com/app/${getAppId()}`;
link.textContent = "(?)";
const rankTitleSpan = document.createElement("span");
rankTitleSpan.classList.add("responsive_reviewdesc_short");
rankTitleSpan.textContent = rankTitle.toUpperCase();
summaryDiv.append(rankValueSpan, link, rankTitleSpan);
rankRow.append(subtitleDiv, summaryDiv);
return rankRow;
}
function createPlatformRow(isNative) {
const platformRow = document.createElement("div");
platformRow.classList.add("user_reviews_summary_row");
const createDivWithClasses = (...classes) => {
const div = document.createElement("div");
div.classList.add(...classes);
return div;
};
const icon = isNative ? "✔" : "✘";
const subtitleDiv = createDivWithClasses("subtitle", "column");
subtitleDiv.textContent = "Native Linux?";
const summaryDiv = createDivWithClasses(
"summary",
"column",
"protondb_isNative"
);
const platformValueSpan = document.createElement("span");
platformValueSpan.classList.add("protondb_isNative_value");
platformValueSpan.textContent = icon;
platformValueSpan.style.color = "#67c1f5";
platformValueSpan.setAttribute("data-isNative", isNative);
const link = document.createElement("a");
link.href = `https://protondb.com/app/${getAppId()}`;
link.textContent = "(?)";
const rankTitleSpan = document.createElement("span");
rankTitleSpan.classList.add("responsive_reviewdesc_short");
rankTitleSpan.textContent = isNative ? "Yes" : "No";
summaryDiv.append(platformValueSpan, link, rankTitleSpan);
platformRow.append(subtitleDiv, summaryDiv);
return platformRow;
}
function createIconElement(status, iconUrl) {
const icon = document.createElement("img");
var imgURL = chrome.runtime.getURL(`assets/${iconUrl}.png`);
icon.src = imgURL;
icon.classList.add("deck-status-icon");
icon.setAttribute("data-ds-deck-tier", status);
return icon;
}
function createLineElement() {
const line = document.createElement("div");
line.classList.add("vertical-line");
return line;
}
function insertLineBeforeItem(line, item) {
if (item.parentNode) item.parentNode.insertBefore(line, item);
item.prepend(line);
}
function insertIconBeforeItem(icon, item) {
if (item.parentNode) item.parentNode.insertBefore(icon, item);
item.prepend(icon);
}
function insertIconInList(icon, item) {
if (!item) return;
const gameListing = item.querySelector(".responsive_search_name_combined");
const reviewBox = gameListing.querySelector(
".col.search_reviewscore.responsive_secondrow"
);
makeItemRelative(reviewBox);
const review = reviewBox.querySelector(".search_review_summary");
icon.classList.add("on-list");
if (review) review.prepend(icon);
}
function colorLineElement(line, color) {
line.style.backgroundColor = color;
}
function insertIconInTabList(icon, item) {
if (item.classList.contains("tab_item_overlay")) {
item = item.parentNode;
}
const details = item.querySelector(".tab_item_details");
if (!details) return false;
icon.classList.add("on-tab-list");
const platformImgs = details.querySelectorAll(".platform_img");
if (platformImgs.length > 0) {
const latestPlatformImg = platformImgs[platformImgs.length - 1];
latestPlatformImg.insertAdjacentElement("afterend", icon);
}
}
@@ -0,0 +1,103 @@
// Browser API compatibility
const browserAPI = typeof browser !== 'undefined' ? browser : chrome;
// Mutation Observers
const observers = [];
// Shared Sets
const items = new Set();
const processedItems = new Set();
// Shared Colors
const colors = {};
//css classes ought to change time to time, examples given for that to remember them.
//to skip adding anything in parent
const LIST_ITEM_TITLE = "._3rrH9dPdtHVRMzAEw82AId";
const PAGE_TITLE = ".page_title_area.game_title_area.page_content";
const BUNDLE_CONTENTS = ".bundle_contents_preview";
const DLC_LIST = ".game_area_dlc_list";
const HOVER_MODAL = "._2VvFLg2irh9gPAhhxE4Kpo"; //try to find games that are hoverable with purchase option
const MINI_WIDGET_TITLE = "._9VjYX3CYMn2y-wWpAn00Y"; //check event page with stream
const FLOATING_STREAM = "._28O6dX6-Xf37oViWRRhvjz"; //check event page with stream
const PACKAGE_CONTENTS_CAPSULES = ".package_contents_capsules";
const BASE_GAME_OF_DLC = ".glance_details";
const GAME_AREA_DESCRIPTION = ".game_area_description";
const RECOMMENDATION_LINK = ".recommendation_link"; //check franchise page
const VALVE_INDEX = ".valveindex_purchase_grid";
const VALVE_INDEX_ALYX_PROMO = ".hlvr_included_sidebar";
const PURCHASE_NOTE = "#purchase_note"; //they sometimes put game hyperlink, eg: cod hq app
const DISCOVERY_QUEUE = ".discovery_queue_static";
const RECOMMENDED_GAME_REASON = ".home_content_reason"; //check game category
const BREADCRUMBS = "breadcrumbs"; // Skip breadcrumbs navigation - removed dot for class name
//to skip add anything if <a> tag has one of those classes
const RECOMMENDED_GAME_ON_STORE_BUTTON_HOVER = "btnv6_blue_hoverfade";
const RECOMMENDED_GAME_ON_STORE_BUTTON_SMALL = "btn_small_tall";
const SCREENSHOT = "screenshot";
const SIMILIAR_APP = "similarapp"; //check game page when logged in and find similiar to games you have played
const BUNDLE_ITEM = "bundle_contents_preview_item";
//Relative Classes
const PACKAGE_ITEM = ".bundle_package_item";
const STORE_SALE_WIDGET_MINI = ".StoreSaleWidgetContainer_mini";
const LARGE_GAME_ITEM = "._3DkfNrtTOLjNYd3yZliMzy"; //check ea play for this class
const IMPRESSION_TRACKED_ELEMENT = ".ImpressionTrackedElement"; //login and go category page
//banner game;
//21/07/2024
//we only need title for icon for aesthetic purposes but no icon on game cover art
const BANNER_GAME_TITLE = "_26-ICqi8UhxutiYJyQ7jw"; //check game category, prevent adding icons on game title on the right side
const BANNER_GAME_COVER_ART = "_3htJKARnLcsZPlG3Sqlvkg"; //check game category, prevent adding icons on game cover art
//skip adding icons
const HOVER_MODEL_BOTTOM_SECTION = "Xwe3c0LwQGRjmLBfrpFu6"; // hoverable games with purchase option
const ClassNamesToCheck = [
RECOMMENDED_GAME_ON_STORE_BUTTON_HOVER,
RECOMMENDED_GAME_ON_STORE_BUTTON_SMALL,
SCREENSHOT,
SIMILIAR_APP,
BUNDLE_ITEM,
BREADCRUMBS // Add breadcrumbs to direct class check
];
const ClassNamesToCheckInParent = [
LIST_ITEM_TITLE,
PAGE_TITLE,
BUNDLE_CONTENTS,
DLC_LIST,
HOVER_MODAL,
MINI_WIDGET_TITLE,
FLOATING_STREAM,
PACKAGE_CONTENTS_CAPSULES,
BASE_GAME_OF_DLC,
GAME_AREA_DESCRIPTION,
RECOMMENDATION_LINK,
VALVE_INDEX,
VALVE_INDEX_ALYX_PROMO,
PURCHASE_NOTE,
DISCOVERY_QUEUE,
RECOMMENDED_GAME_REASON,
BREADCRUMBS // Add breadcrumbs to parent check
];
const shouldSkipAddingLineGlobals = {
specificClassesToCheck: [BANNER_GAME_TITLE],
specificClassesToCheckInParent: [],
};
const shouldSkipAddingIconGlobals = {
specificClassesToCheck: [BANNER_GAME_COVER_ART, HOVER_MODEL_BOTTOM_SECTION],
specificClassesToCheckInParent: [],
};
const RelativeClassCheck = [];
const ParentRelativeClassCheck = [
PACKAGE_ITEM,
STORE_SALE_WIDGET_MINI,
LARGE_GAME_ITEM,
IMPRESSION_TRACKED_ELEMENT,
];
@@ -0,0 +1,67 @@
function setColors() {
chrome.storage.local.get(
{
borkedColor: "#FF0000",
bronzeColor: "#CD7F32",
silverColor: "#A6A6A6",
goldColor: "#CFB53B",
platinumColor: "#B4C7DC",
unknownColor: "#3D3D3D",
},
(result) => {
colors.borked = result.borkedColor;
colors.bronze = result.bronzeColor;
colors.silver = result.silverColor;
colors.gold = result.goldColor;
colors.platinum = result.platinumColor;
colors.unknown = result.unknownColor;
}
);
}
function parseAppId(item) {
const link = item.getAttribute("href");
const linkMatch = link ? link.match(/\/app\/(\d+)/) : null;
const dataAppId = item ? item.getAttribute("data-ds-appid") : null; // Get the value of data-ds-appid attribute
return linkMatch && linkMatch[1]
? linkMatch[1]
: dataAppId !== null
? dataAppId
: null;
}
function getAppId() {
var url = document.URL;
// Extract the number after "/app/"
var match = url.match(/\/app\/(\d+)/);
var appId = match ? match[1] : null;
return appId;
}
function getLineColor(tier) {
return colors[tier] || colors.unknown;
}
function hasAnyClass(element, classNames) {
const elementClassName = element.className;
for (const className of classNames) {
if (elementClassName.includes(className)) {
return true;
}
}
return false;
}
function hasAnyParentClass(element, classNames) {
let parent = element.parentElement;
for (const className of classNames) {
if (element.closest(className)) {
return true;
}
}
return false;
}
@@ -0,0 +1,149 @@
function shouldSkipAddingLine(item) {
if (!item) return true;
const parent = item?.parentNode ? item.parentNode : item;
// Skip if parent is a paragraph or item already has a line
if (parent?.tagName === "P" || item.querySelector(".vertical-line")) {
return true;
}
// Skip if item matches specific conditions
if (item.matches(".gutter_item") ||
hasAnyClass(item, shouldSkipAddingLineGlobals.specificClassesToCheck) ||
hasAnyParentClass(item, shouldSkipAddingLineGlobals.specificClassesToCheckInParent)) {
return true;
}
shouldMakeItemRelative(item);
return false;
}
function shouldSkipAddingIcon(item) {
if (!item) return true;
const parent = item?.parentNode ? item.parentNode : item;
// Skip if parent is a paragraph or item already has an icon
if (parent?.tagName === "P" || item.querySelector(".deck-status-icon")) {
return true;
}
// Skip if item matches specific conditions
if (item.matches(".gutter_item") ||
hasAnyClass(item, shouldSkipAddingIconGlobals.specificClassesToCheck) ||
hasAnyParentClass(item, shouldSkipAddingIconGlobals.specificClassesToCheckInParent)) {
return true;
}
shouldMakeItemRelative(item);
return false;
}
function shouldMakeItemRelative(item) {
if (!item || item.style.position === "relative") return false;
if (hasAnyClass(item, RelativeClassCheck)) {
makeItemRelative(item);
}
if (hasAnyParentClass(item, ParentRelativeClassCheck)) {
makeItemRelative(item.parentNode);
}
return false;
}
function makeItemRelative(item) {
if (item) item.style.position = "relative";
}
async function addLineToItem(item) {
if (shouldSkipAddingLine(item)) return;
const appId = parseAppId(item);
if (!appId) return;
try {
const data = await new Promise((resolve, reject) => {
browserAPI.runtime.sendMessage(
{ contentScriptQuery: "queryGame", appId },
(response) => {
if (browserAPI.runtime.lastError) {
reject(browserAPI.runtime.lastError);
return;
}
resolve(response);
}
);
});
const tier = data?.tier || "unknown";
const color = getLineColor(tier);
const line = createLineElement();
colorLineElement(line, color);
line.dataset.tier = tier;
insertLineBeforeItem(line, item);
} catch (error) {
console.error('[ProtonDB] Error adding line:', error.message);
}
}
async function addIconToItem(item, isItemInSearchBox = false) {
if (shouldSkipAddingIcon(item)) return;
const appId = parseAppId(item);
if (!appId) return;
try {
const data = await new Promise((resolve, reject) => {
browserAPI.runtime.sendMessage(
{ contentScriptQuery: "queryDeckStats", appId },
(response) => {
if (browserAPI.runtime.lastError) {
reject(browserAPI.runtime.lastError);
return;
}
resolve(response);
}
);
});
if (!data?.icon) return;
const icon = createIconElement(data.status, data.icon);
const url = document.URL;
if (isItemInSearchBox) {
insertIconBeforeItem(icon, item);
} else if (url.includes("https://store.steampowered.com/search/")) {
insertIconInList(icon, item);
} else if (isItemInTabList(item)) {
insertIconInTabList(icon, item);
} else {
insertIconBeforeItem(icon, item);
}
} catch (error) {
console.error('[ProtonDB] Error adding icon:', error.message);
}
}
function isItemInTabList(item) {
return item.classList.contains("tab_item") || item.classList.contains("tab_item_overlay");
}
function addElementsToItems(item, isItemInSearchBox = false) {
if (!item) return;
addLineToItem(item);
addIconToItem(item, isItemInSearchBox);
items.add(item);
}
function getNewItems() {
const selector = 'a[href^="https://store.steampowered.com/app/"]:not([class*="ReviewScore"]):not(a[class*="ReviewScore"]), a.home_marketing_message.small.app_impression_tracked';
return Array.from(document.querySelectorAll(selector))
.filter(item =>
!items.has(item) &&
!hasAnyClass(item, ClassNamesToCheck) &&
!hasAnyParentClass(item, ClassNamesToCheckInParent)
);
}
@@ -0,0 +1,72 @@
function handleContentMutation(mutationsList) {
mutationsList.forEach((mutation) => {
if (mutation.type === "childList" && mutation.addedNodes.length > 0) {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
const newItems = getNewItems(node);
if (newItems.length > 0) {
newItems.forEach((item) => {
addElementsToItems(
item,
item.parentNode?.id === "search_suggestion_contents"
? true
: false
);
});
}
}
});
}
});
}
function handleStorageMutation(changes, namespace) {
// Identify tiers that have changed color
const changedTiers = [];
for (let [key, { oldValue, newValue }] of Object.entries(changes)) {
if (key.endsWith("Color")) {
const tier = key.replace("Color", "");
colors[tier] = newValue;
changedTiers.push(tier);
}
}
// If any tiers had their colors changed, update the lines and rank value spans
if (changedTiers.length > 0) {
const selector = changedTiers
.map((tier) => `.vertical-line[data-tier="${tier}"]`)
.join(",");
const lines = document.querySelectorAll(selector);
lines.forEach((line) => {
const tier = line.dataset.tier;
line.style.backgroundColor = colors[tier];
// Update the rank value spans with the changed tier
const lowercaseTier = tier.toLowerCase();
const rankValueSpans = document.querySelectorAll(
`.protondb_rank_value[data-tier="${lowercaseTier}"]`
);
if (rankValueSpans.length === 0) return;
rankValueSpans.forEach((rankValueSpan) => {
if (colors.hasOwnProperty(lowercaseTier)) {
rankValueSpan.style.color = colors[lowercaseTier];
}
});
});
}
}
const contentObserver = new MutationObserver(handleContentMutation);
observers.push(contentObserver);
function startObservers() {
setColors();
observers.forEach((observer) => {
observer.observe(document, { childList: true, subtree: true });
});
}
// Listen to changes in the local storage
chrome.storage.onChanged.addListener(handleStorageMutation);