// 👋 Hola, usa 🐒Tampermonkey 👇
// https://www.tampermonkey.net/
// ==UserScript==
// @name Xbox Store Price & Deals Filter
// @name:es Filtros de Precio y Ofertas para Xbox Store
// @name:it Filtri Prezzi e Offerte per Xbox Store
// @name:fr Filtres de Prix et Offres pour Xbox Store
// @name:de Preis- und Angebotsfilter für Xbox Store
// @namespace https://jlcareglio.github.io/
// @version 0.8.14
// @description Add price range filters and deal filters to Xbox store. Filter by Game Pass discounts, specific discount percentages, and price ranges.
// @description:es Agrega filtros de rango de precios y ofertas a la tienda Xbox. Filtra por descuentos de Game Pass, porcentajes específicos de descuento y rangos de precios.
// @description:it Aggiunge filtri per fascia di prezzo e offerte allo store Xbox. Filtra per sconti Game Pass, percentuali di sconto specifiche e fasce di prezzo.
// @description:fr Ajoute des filtres de gamme de prix et d'offres au magasin Xbox. Filtre par réductions Game Pass, pourcentages de réduction spécifiques et gammes de prix.
// @description:de Fügt Preisbereich- und Angebotsfilter zum Xbox-Store hinzu. Filtert nach Game Pass-Rabatten, spezifischen Rabattprozenten und Preisbereichen.
// @icon https://www.google.com/s2/favicons?sz=64&domain=xbox.com
// @grant none
// @author Jesús Lautaro Careglio Albornoz
// @source https://gist.githubusercontent.com/JLCareglio/9cbddea558658f695983a64b9cece6a6/raw/
// @match https://www.xbox.com/*/games/all-games*
// @match https://www.xbox.com/*/games/browse*
// @match https://www.xbox.com/*/Search/Result*
// @supportURL https://gist.githubusercontent.com/JLCareglio/9cbddea558658f695983a64b9cece6a6/
// @downloadURL none
// ==/UserScript==
// MARK: CONSTANTS
const SELECTORS = {
GAME_PASS_DISCOUNT: "Price-module__afterPriceTextContainer___r7fdq",
DISCOUNT_TAG: "ProductCard-module__discountTag___OjGFy",
FINAL_PRICE: "ProductCard-module__price___cs1xr",
FILTER_LIST: "SortAndFilters-module__filterList___T81LH",
BUTTON_APPLY_FILTERS: "ApplyFiltersButton-module__applyButton___faTvE",
BUTTON_SHOW_FILTERS: "SortAndFilters-module__button___OeFeU",
LOAD_MORE_ROW: "BrowsePage-module__loadMoreRow___sx0qx",
XBOX_SPINNER_FILTER: "XboxSpinner-module__xenonFilter___lbfRN",
};
// MARK: DOM HELPER
const DOMHelper = {
waitForElement(selector) {
return new Promise((resolve) => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(() => {
if (document.querySelector(selector)) {
observer.disconnect();
resolve(document.querySelector(selector));
}
});
observer.observe(document.documentElement, {
childList: true,
subtree: true,
});
});
},
};
// MARK: FILTER CREATOR
const FilterCreator = {
createFilter(id, title, options, additionalContent = "") {
const li = document.createElement("li");
li.className = "SortAndFilters-module__li___aV+Oo";
const svgExpanded = `
`;
const svgCollapsed = `
`;
li.innerHTML = `
`;
return li;
},
addFilterHandlers(filterElement, filterFn, stateManager) {
const button = filterElement.querySelector(
".SelectionDropdown-module__titleContainer___YyoD0"
);
const optionsDiv = filterElement.querySelector("div[style]");
const options = filterElement.querySelectorAll(
".Selections-module__selectionContainer___m2xzM"
);
button.addEventListener("click", () => {
const isExpanded = button.getAttribute("aria-expanded") === "true";
button.setAttribute("aria-expanded", !isExpanded);
optionsDiv.classList.toggle("hidden");
});
options.forEach((option) => {
const input = option.querySelector("input");
if (!input) return;
const toggleFilter = () => {
const isSelected = option.getAttribute("aria-selected") === "true";
const newState = !isSelected;
if (input.type === "radio") {
options.forEach((opt) => {
const otherInput = opt.querySelector('input[type="radio"]');
if (otherInput && otherInput.name === input.name) {
opt.setAttribute("aria-selected", "false");
opt.setAttribute("aria-checked", "false");
otherInput.checked = false;
}
});
}
option.setAttribute("aria-selected", newState);
option.setAttribute("aria-checked", newState);
input.checked = newState;
// Aplicar filtro
filterFn();
stateManager.save();
};
option.addEventListener("click", (e) => {
if (e.target !== input) {
e.preventDefault();
toggleFilter();
}
});
input.addEventListener("change", () => {
if (input.type === "radio") {
options.forEach((opt) => {
const otherInput = opt.querySelector('input[type="radio"]');
if (otherInput && otherInput.name === input.name) {
opt.setAttribute("aria-selected", otherInput.checked);
opt.setAttribute("aria-checked", otherInput.checked);
}
});
} else {
option.setAttribute("aria-selected", input.checked);
option.setAttribute("aria-checked", input.checked);
}
// Aplicar filtro
filterFn();
stateManager.save();
});
});
},
};
// MARK: FILTER LOGIC
const FilterLogic = {
applyAllFilters() {
const productCards = document.querySelectorAll(
".ProductCard-module__cardWrapper___6Ls86"
);
const selectedDiscountFilter =
document.querySelector('input[name="discountGroup"]:checked')?.value ||
"none";
const onlyGamePass =
document.querySelector("#gamePassOnly_checkbox")?.checked || false;
const customDiscountPercent = parseInt(
document.querySelector("#customDiscountPercent")?.value || "0"
);
productCards.forEach((card) => {
const minPrice =
parseFloat(document.querySelector("#priceMin").value) || 0;
const maxPrice =
parseFloat(document.querySelector("#priceMax").value) || Infinity;
const showFree = document.querySelector("#free_checkbox").checked;
const showPaid = document.querySelector("#paid_checkbox").checked;
const showUnpurchasable = document.querySelector(
"#unpurchasable_checkbox"
).checked;
const priceElement = card.querySelector(`.${SELECTORS.FINAL_PRICE}`);
let shouldShow = true;
if (!priceElement) {
shouldShow = showUnpurchasable;
} else {
const priceText = priceElement.innerText;
const hasNumbers = /\d/.test(priceText);
const isFree = !hasNumbers;
if (isFree) {
shouldShow = showFree;
} else {
shouldShow = showPaid;
if (shouldShow) {
const priceString = priceText.trim().replace(/[^\d.,]/g, "");
let price;
const match = priceString.match(/^(.*?)[\.,](\d{2})$/);
if (match) {
const integerPart = match[1].replace(/[.,]/g, "");
const decimalPart = match[2];
price = parseFloat(`${integerPart}.${decimalPart}`);
} else {
price = parseFloat(priceString.replace(/[.,]/g, ""));
}
shouldShow =
!isNaN(price) &&
price >= minPrice &&
(maxPrice === 0 || price <= maxPrice);
}
}
}
// Verificar descuentos
if (shouldShow) {
const discountTag = card.querySelector(`.${SELECTORS.DISCOUNT_TAG}`);
const hasGamePassDiscount = card.querySelector(
`.${SELECTORS.GAME_PASS_DISCOUNT}`
);
const discountPercentage = discountTag
? parseInt(discountTag.innerText.replace(/[^0-9]/g, ""))
: 0;
if (onlyGamePass && selectedDiscountFilter !== "none") {
shouldShow = hasGamePassDiscount ? true : false;
// Si tiene Game Pass, ahora verificamos el porcentaje de descuento
if (shouldShow) {
switch (selectedDiscountFilter) {
case "anyDiscount":
shouldShow = discountTag !== null;
break;
case "discount50plus":
shouldShow = discountPercentage >= 50;
break;
case "discount75plus":
shouldShow = discountPercentage >= 75;
break;
case "discountCustom":
shouldShow = discountPercentage >= customDiscountPercent;
break;
}
}
} else if (onlyGamePass) {
shouldShow = hasGamePassDiscount ? true : false;
} else if (selectedDiscountFilter !== "none") {
switch (selectedDiscountFilter) {
case "anyDiscount":
shouldShow = discountTag !== null;
break;
case "discount50plus":
shouldShow = discountPercentage >= 50;
break;
case "discount75plus":
shouldShow = discountPercentage >= 75;
break;
case "discountCustom":
shouldShow = discountPercentage >= customDiscountPercent;
break;
}
}
}
card.parentElement.style.display = shouldShow ? "" : "none";
});
},
};
// MARK: STATE MANAGER
class FilterStateManager {
constructor() {
this.state = {
priceMin: "",
priceMax: "",
free: true,
paid: true,
unpurchasable: true,
discountGroup: "none",
customDiscountPercent: "",
gamePassOnly: false,
};
}
save() {
this.state = {
priceMin: document.querySelector("#priceMin")?.value || "",
priceMax: document.querySelector("#priceMax")?.value || "",
free: document.querySelector("#free_checkbox")?.checked || false,
paid: document.querySelector("#paid_checkbox")?.checked || false,
unpurchasable:
document.querySelector("#unpurchasable_checkbox")?.checked || false,
discountGroup:
document.querySelector('input[name="discountGroup"]:checked')?.value ||
"none",
customDiscountPercent:
document.querySelector("#customDiscountPercent")?.value || "",
gamePassOnly:
document.querySelector("#gamePassOnly_checkbox")?.checked || false,
};
}
load() {
if (!this.state) return;
if (document.querySelector("#priceMin"))
document.querySelector("#priceMin").value = this.state.priceMin;
if (document.querySelector("#priceMax"))
document.querySelector("#priceMax").value = this.state.priceMax;
if (document.querySelector("#free_checkbox"))
document.querySelector("#free_checkbox").checked = this.state.free;
if (document.querySelector("#paid_checkbox"))
document.querySelector("#paid_checkbox").checked = this.state.paid;
if (document.querySelector("#unpurchasable_checkbox"))
document.querySelector("#unpurchasable_checkbox").checked =
this.state.unpurchasable;
if (
this.state.discountGroup !== "none" &&
document.querySelector(`#${this.state.discountGroup}_radio`)
) {
document.querySelector(
`#${this.state.discountGroup}_radio`
).checked = true;
}
if (document.querySelector("#customDiscountPercent"))
document.querySelector("#customDiscountPercent").value =
this.state.customDiscountPercent;
if (document.querySelector("#gamePassOnly_checkbox"))
document.querySelector("#gamePassOnly_checkbox").checked =
this.state.gamePassOnly;
}
}
// MARK: MAIN
class XboxStoreFilter {
constructor() {
this.isInitialized = false;
this.stateManager = new FilterStateManager();
this.lastFilterList = null;
}
async initialize() {
const filterList = await DOMHelper.waitForElement(
`.${SELECTORS.FILTER_LIST}`
);
if (filterList) {
this.setupFilters(filterList);
this.setupObservers();
this.setupEventListeners();
}
}
setupFilters(filterList) {
const existingPriceFilter = filterList.querySelector("#PriceRange");
const existingOffersFilter = filterList.querySelector("#Offers");
if (existingPriceFilter) existingPriceFilter.closest("li")?.remove();
if (existingOffersFilter) existingOffersFilter.closest("li")?.remove();
const priceFilterFn = () => {
FilterLogic.applyAllFilters();
this.stateManager.save();
return true;
};
const offersFilterFn = () => {
FilterLogic.applyAllFilters();
this.stateManager.save();
return true;
};
const priceFilter = FilterCreator.createFilter(
"PriceRange",
"Precio",
[
{
id: "free",
label: "Mostrar Gratis",
defaultSelected: this.stateManager.state.free,
},
{
id: "paid",
label: "Mostrar de Pago",
defaultSelected: this.stateManager.state.paid,
},
{
id: "unpurchasable",
label: "Mostrar Incomprable",
defaultSelected: this.stateManager.state.unpurchasable,
},
],
`
`
);
const offersFilter = FilterCreator.createFilter("Offers", "Oferta", [
{
id: "anyDiscount",
label: "Con descuento",
type: "radio",
group: "discountGroup",
},
{
id: "discount50plus",
label: "50% o más",
type: "radio",
group: "discountGroup",
},
{
id: "discount75plus",
label: "75% o más",
type: "radio",
group: "discountGroup",
},
{
id: "discountCustom",
label: ` % o más`,
type: "radio",
group: "discountGroup",
},
{ id: "gamePassOnly", label: "Solo con Game Pass", type: "checkbox" },
]);
filterList.appendChild(priceFilter);
filterList.appendChild(offersFilter);
FilterCreator.addFilterHandlers(
priceFilter,
priceFilterFn,
this.stateManager
);
FilterCreator.addFilterHandlers(
offersFilter,
offersFilterFn,
this.stateManager
);
const priceInputs = document.querySelectorAll("#priceMin, #priceMax");
priceInputs.forEach((input) => {
input.addEventListener("change", () => {
FilterLogic.applyAllFilters();
this.stateManager.save();
});
});
document
.querySelector("#customDiscountPercent")
?.addEventListener("change", (e) => {
const radio = document.querySelector("#discountCustom_radio");
if (radio) {
radio.checked = true;
radio.dispatchEvent(new Event("change"));
this.stateManager.save();
}
});
this.stateManager.load();
FilterLogic.applyAllFilters();
}
setupObservers() {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === "childList") {
const currentFilterList = document.querySelector(
`.${SELECTORS.FILTER_LIST}`
);
// Si aparece una nueva lista de filtros y es diferente a la última observada
if (currentFilterList && currentFilterList !== this.lastFilterList) {
this.lastFilterList = currentFilterList;
this.setupFilters(currentFilterList);
this.isInitialized = true;
}
// Si desaparece la lista de filtros, reiniciamos para la próxima
if (!document.querySelector(`.${SELECTORS.FILTER_LIST}`)) {
this.isInitialized = false;
this.lastFilterList = null;
}
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
}
setupEventListeners() {
const applyButton = document.querySelector(
`.${SELECTORS.BUTTON_APPLY_FILTERS}`
);
if (applyButton) {
applyButton.addEventListener("click", () => {
FilterLogic.applyAllFilters();
this.stateManager.save();
});
}
}
}
// MARK: INITIALIZE APP
(async () => {
const app = new XboxStoreFilter();
await app.initialize();
})();