adds .local/share/millennium and easyeffects
This commit is contained in:
+130
@@ -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);
|
||||
}
|
||||
}
|
||||
+103
@@ -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,
|
||||
];
|
||||
+67
@@ -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;
|
||||
}
|
||||
+149
@@ -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)
|
||||
);
|
||||
}
|
||||
+72
@@ -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);
|
||||
Reference in New Issue
Block a user