|
// ==UserScript== |
|
// @name Xbox-Now Enhanced Filters |
|
// @namespace https://jlcareglio.github.io/ |
|
// @version 3.5 |
|
// @description Enhanced Xbox-Now Filters with advanced pillbox UI for a seamless and intuitive experience |
|
// @author Jesús Lautaro Careglio Albornoz |
|
// @source https://gist.githubusercontent.com/JLCareglio/1e4b0838bdf31e21ed749cfcd89a3a47/raw/01_Xbox-Now-Enhanced-Filters.user.js |
|
// @match *://*.xbox-now.com/* |
|
// @updateURL https://gist.githubusercontent.com/JLCareglio/1e4b0838bdf31e21ed749cfcd89a3a47/raw/01_Xbox-Now-Enhanced-Filters.user.js |
|
// @downloadURL https://gist.githubusercontent.com/JLCareglio/1e4b0838bdf31e21ed749cfcd89a3a47/raw/01_Xbox-Now-Enhanced-Filters.user.js |
|
// @license MIT |
|
// @compatible firefox |
|
// @compatible chrome |
|
// @compatible opera |
|
// @compatible safari |
|
// @compatible edge |
|
// @compatible brave |
|
// @icon https://www.google.com/s2/favicons?sz=64&domain=xbox-now.com |
|
// @grant none |
|
// @supportURL https://gist.github.com/JLCareglio/1e4b0838bdf31e21ed749cfcd89a3a47 |
|
// @require https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4 |
|
// ==/UserScript== |
|
|
|
(() => { |
|
"use strict"; |
|
|
|
const style = document.createElement("style"); |
|
style.textContent = ` |
|
.collapse { visibility: visible !important; } |
|
@keyframes fadeIn { |
|
from { opacity: 0; transform: scale(0.95); } |
|
to { opacity: 1; transform: scale(1); } |
|
} |
|
.animate-fade-in { |
|
animation: fadeIn 0.2s ease-out; |
|
} |
|
@keyframes highlight-fade { |
|
from { background-color: #dcfce7; } |
|
to { background-color: #e5e7eb; } |
|
} |
|
.animate-highlight { |
|
animation: highlight-fade 1.5s ease-out forwards; |
|
} |
|
`; |
|
document.head.appendChild(style); |
|
|
|
const hideSVG = |
|
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-8/10 h-8/10"><path d="M3.53 2.47a.75.75 0 0 0-1.06 1.06l18 18a.75.75 0 1 0 1.06-1.06l-18-18ZM22.676 12.553a11.249 11.249 0 0 1-2.631 4.31l-3.099-3.099a5.25 5.25 0 0 0-6.71-6.71L7.759 4.577a11.217 11.217 0 0 1 4.242-.827c4.97 0 9.185 3.223 10.675 7.69.12.362.12.752 0 1.113Z" /><path d="M15.75 12c0 .18-.013.357-.037.53l-4.244-4.243A3.75 3.75 0 0 1 15.75 12ZM12.53 15.713l-4.243-4.244a3.75 3.75 0 0 0 4.244 4.243Z" /><path d="M6.75 12c0-.619.107-1.213.304-1.764l-3.1-3.1a11.25 11.25 0 0 0-2.63 4.31c-.12.362-.12.752 0 1.114 1.489 4.467 5.704 7.69 10.675 7.69 1.5 0 2.933-.294 4.242-.827l-2.477-2.477A5.25 5.25 0 0 1 6.75 12Z" /></svg>'; |
|
const filterSVG = |
|
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-8/10 h-8/10"><path fill-rule="evenodd" d="M3.792 2.938A49.069 49.069 0 0 1 12 2.25c2.797 0 5.54.236 8.209.688a1.857 1.857 0 0 1 1.541 1.836v1.044a3 3 0 0 1-.879 2.121l-6.182 6.182a1.5 1.5 0 0 0-.439 1.061v2.927a3 3 0 0 1-1.658 2.684l-1.757.878A.75.75 0 0 1 9.75 21v-5.818a1.5 1.5 0 0 0-.44-1.06L3.13 7.938a3 3 0 0 1-.879-2.121V4.774c0-.897.64-1.683 1.542-1.836Z" clip-rule="evenodd" /></svg>'; |
|
const minimizeSVG = |
|
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-9 h-9"><path fill-rule="evenodd" d="M12.53 16.28a.75.75 0 0 1-1.06 0l-7.5-7.5a.75.75 0 0 1 1.06-1.06L12 14.69l6.97-6.97a.75.75 0 1 1 1.06 1.06l-7.5 7.5Z" clip-rule="evenodd" /></svg>'; |
|
const cancelSVG = |
|
'<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-9 h-9"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" /></svg>'; |
|
|
|
const defaultFilters = { |
|
includeTags: { enabled: true, tags: ["PC", "GAME PASS"] }, |
|
excludeTags: { enabled: false, tags: [] }, |
|
hiddenGames: { enabled: true, games: [] }, |
|
}; |
|
|
|
// Función para procesar los filtros cargados desde localStorage |
|
const processStoredFilters = () => { |
|
const storedFilters = localStorage.getItem("filters"); |
|
if (!storedFilters) return defaultFilters; |
|
|
|
try { |
|
const parsed = JSON.parse(storedFilters); |
|
// Aseguramos que los valores de los filtros sean arrays válidos |
|
["includeTags", "excludeTags", "hiddenGames"].forEach(key => { |
|
if (parsed[key] && parsed[key].enabled !== undefined) { |
|
// Si hay valores, nos aseguramos de que sean arrays |
|
if (Array.isArray(parsed[key].tags)) { |
|
parsed[key].tags = parsed[key].tags.filter(tag => typeof tag === 'string'); |
|
} else if (parsed[key].games) { |
|
parsed[key].games = parsed[key].games.filter(game => typeof game === 'string'); |
|
} |
|
} else { |
|
// Si la estructura no es válida, usamos los valores por defecto |
|
parsed[key] = { ...defaultFilters[key] }; |
|
} |
|
}); |
|
return parsed; |
|
} catch (e) { |
|
console.error("Error al procesar los filtros guardados:", e); |
|
return { ...defaultFilters }; |
|
} |
|
}; |
|
|
|
const filters = processStoredFilters(); |
|
|
|
function filterGames() { |
|
document |
|
.querySelectorAll(".box-body.comparison-table-entry") |
|
.forEach((gameEntry) => { |
|
const storeButton = gameEntry.querySelector("a.btn"); |
|
if (!storeButton) return; |
|
|
|
const labels = gameEntry.querySelectorAll(".label"); |
|
const hasTags = (tags) => |
|
Array.from(labels).some((label) => |
|
tags.some((tag) => |
|
label.textContent.toUpperCase().includes(tag.toUpperCase()) |
|
) |
|
); |
|
|
|
const isIncluded = |
|
!filters.includeTags.enabled || |
|
filters.includeTags.tags.length === 0 || |
|
hasTags(filters.includeTags.tags); |
|
|
|
const isExcluded = |
|
filters.excludeTags.enabled && |
|
filters.excludeTags.tags.length > 0 && |
|
hasTags(filters.excludeTags.tags); |
|
|
|
const isHidden = |
|
filters.hiddenGames.enabled && |
|
filters.hiddenGames.games.includes(storeButton.title); |
|
|
|
const shouldBeVisible = isIncluded && !isExcluded && !isHidden; |
|
gameEntry.style.display = shouldBeVisible ? "" : "none"; |
|
}); |
|
} |
|
|
|
document |
|
.querySelectorAll(".box-body.comparison-table-entry") |
|
.forEach((gameEntry) => { |
|
const storeButton = gameEntry.querySelector("a.btn"); |
|
const btnHide = document.createElement("button"); |
|
const btnConfig = document.createElement("button"); |
|
const btnContainer = document.createElement("div"); |
|
btnHide.innerHTML = hideSVG; |
|
btnHide.className = |
|
"flex-1 flex items-center justify-center h-full p-0 m-0 hover:bg-[#8d0040]"; |
|
btnConfig.innerHTML = filterSVG; |
|
btnConfig.className = |
|
"flex-1 flex items-center justify-center h-full p-0 m-0 hover:bg-[#8d0040]"; |
|
btnContainer.className = |
|
"text-white bg-[#a6004c] border border-[#8d0040] mt-2 flex h-[34px] w-full overflow-hidden [&>button:not(:last-child)]:border-r [&>button:not(:last-child)]:border-r-white"; |
|
btnContainer.appendChild(btnHide); |
|
btnContainer.appendChild(btnConfig); |
|
storeButton.parentNode.appendChild(btnContainer); |
|
|
|
btnHide.addEventListener("click", () => { |
|
const gameName = storeButton.title; |
|
if (!filters.hiddenGames.games.includes(gameName)) { |
|
filters.hiddenGames.games.push(gameName); |
|
localStorage.setItem("filters", JSON.stringify(filters)); |
|
|
|
// Actualizar el textarea de hiddenGames si el modal está abierto |
|
const hiddenGamesTextarea = document.getElementById('hiddenGames-values'); |
|
if (hiddenGamesTextarea) { |
|
hiddenGamesTextarea.value = filters.hiddenGames.games.map(escapeCommas).join(','); |
|
hiddenGamesTextarea.dispatchEvent(new CustomEvent("renderpills")); |
|
} |
|
} |
|
filterGames(); |
|
}); |
|
|
|
btnConfig.addEventListener("click", () => { |
|
const modal = document.getElementById("medium-modal"); |
|
modal.classList.remove("hidden"); |
|
}); |
|
}); |
|
|
|
function createFilterSection(id, title, description, placeholder) { |
|
const values = filters[id].tags || filters[id].games || []; |
|
const textareaValue = Array.isArray(values) ? values.join(", ") : ""; |
|
|
|
return ` |
|
<div class="bg-white rounded-xl shadow-sm p-4 border border-slate-200/80"> |
|
<div class="flex items-center justify-between"> |
|
<div class="flex items-center gap-3 cursor-pointer" onclick="document.getElementById('${id}-enabled').click()"> |
|
<h4 class="text-base font-semibold text-slate-800">${title}</h4> |
|
</div> |
|
<label class="relative inline-flex items-center cursor-pointer mb-0!"> |
|
<input type="checkbox" id="${id}-enabled" class="sr-only peer" ${ |
|
filters[id].enabled ? "checked" : "" |
|
}> |
|
<div class="w-15 h-9 bg-slate-200 rounded-full peer peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[#a6004c]/30 peer-checked:after:translate-x-5.5 peer-checked:after:border-white after:content-[''] after:absolute after:top-[1.3px] after:left-[2.3px] after:bg-white after:border-slate-300 after:border after:rounded-full after:h-8 after:w-8 after:transition-all peer-checked:bg-[#a6004c]"></div> |
|
</label> |
|
</div> |
|
<p class="text-sm text-slate-500 mt-1 mb-3 font-normal">${description}</p> |
|
|
|
<div id="${id}-pills-container" class="w-full p-2.5 border border-slate-300 rounded-lg min-h-24 text-sm bg-slate-50 focus-within:ring-2 focus-within:ring-[#a6004c]/50 focus-within:border-[#a6004c] transition flex flex-wrap gap-2 items-start content-start overflow-y-auto"> |
|
<!-- Pills will be generated here --> |
|
</div> |
|
|
|
<textarea |
|
id="${id}-values" |
|
class="w-full p-2.5 border border-slate-300 rounded-lg h-24 text-sm bg-slate-50 focus:ring-2 focus:ring-[#a6004c]/50 focus:border-[#a6004c] transition hidden" |
|
placeholder="${placeholder}" |
|
>${textareaValue}</textarea> |
|
</div> |
|
`; |
|
} |
|
|
|
function initializePillInputs() { |
|
["includeTags", "excludeTags", "hiddenGames"].forEach((id) => { |
|
const pillsContainer = document.getElementById(`${id}-pills-container`); |
|
const textarea = document.getElementById(`${id}-values`); |
|
let isEditing = false; |
|
let lastAdded = []; |
|
let renderPills; |
|
|
|
const toTextareaView = () => { |
|
if (isEditing) return; |
|
pillsContainer.classList.add("hidden"); |
|
textarea.classList.remove("hidden"); |
|
if ( |
|
textarea.value.trim().length > 0 && |
|
!textarea.value.trim().endsWith(",") |
|
) |
|
textarea.value += ", "; |
|
|
|
textarea.focus(); |
|
textarea.selectionStart = textarea.selectionEnd = textarea.value.length; |
|
}; |
|
|
|
const createPill = (value) => { |
|
const pill = document.createElement("span"); |
|
pill.className = |
|
"inline-flex items-center bg-slate-200 text-slate-800 text-sm font-medium rounded-full overflow-hidden animate-fade-in"; |
|
pill.onclick = (e) => e.stopPropagation(); |
|
|
|
if (lastAdded.includes(value)) pill.classList.add("animate-highlight"); |
|
|
|
const pillText = document.createElement("span"); |
|
pillText.textContent = value; |
|
pillText.className = |
|
"cursor-pointer pl-2.5 pr-2 py-1 hover:bg-slate-300/60 transition-colors"; |
|
pillText.onclick = (e) => { |
|
e.stopPropagation(); |
|
editPill(pill); |
|
}; |
|
|
|
const deleteBtn = document.createElement("button"); |
|
deleteBtn.type = "button"; |
|
deleteBtn.className = |
|
"px-1.5 py-1 border-l border-slate-400/20 text-slate-500 hover:text-[#a6004c] hover:bg-slate-300/60 transition-colors self-stretch flex items-center"; |
|
deleteBtn.innerHTML = cancelSVG.replace("w-9 h-9", "w-4 h-4"); |
|
deleteBtn.onclick = (e) => { |
|
e.stopPropagation(); |
|
const currentValues = textarea.value.split(",").map((v) => v.trim()); |
|
const index = currentValues.indexOf(value); |
|
if (index > -1) { |
|
currentValues.splice(index, 1); |
|
textarea.value = currentValues.join(", "); |
|
} |
|
renderPills(); |
|
}; |
|
|
|
pill.appendChild(pillText); |
|
pill.appendChild(deleteBtn); |
|
return pill; |
|
}; |
|
|
|
const editPill = (pill) => { |
|
isEditing = true; |
|
const pillText = pill.querySelector("span"); |
|
const deleteBtn = pill.querySelector("button"); |
|
const currentValue = pillText.textContent; |
|
|
|
const input = document.createElement("input"); |
|
input.type = "text"; |
|
input.value = currentValue; |
|
input.className = |
|
"bg-transparent focus:outline-none w-auto py-1 pl-2.5 pr-2"; |
|
input.style.width = `${currentValue.length + 3}ch`; |
|
input.onclick = (e) => e.stopPropagation(); |
|
|
|
pillText.classList.add("hidden"); |
|
deleteBtn.classList.add("hidden"); |
|
pill.insertBefore(input, deleteBtn); |
|
|
|
input.focus(); |
|
|
|
const saveChanges = () => { |
|
isEditing = false; |
|
const newValue = input.value.trim().toUpperCase(); |
|
const currentValues = textarea.value.split(",").map((v) => v.trim()); |
|
const index = currentValues.indexOf(currentValue); |
|
|
|
if (newValue && newValue !== currentValue) { |
|
if (index > -1) { |
|
currentValues[index] = newValue; |
|
textarea.value = currentValues.join(", "); |
|
} |
|
} else if (!newValue) { |
|
if (index > -1) { |
|
currentValues.splice(index, 1); |
|
textarea.value = currentValues.join(", "); |
|
} |
|
} |
|
renderPills(); |
|
}; |
|
|
|
input.onblur = saveChanges; |
|
input.onkeydown = (e) => { |
|
if (e.key === "Enter") { |
|
e.preventDefault(); |
|
saveChanges(); |
|
} else if (e.key === "Escape") { |
|
e.preventDefault(); |
|
renderPills(); |
|
isEditing = false; |
|
} |
|
}; |
|
}; |
|
|
|
// Función para escapar comas en los valores |
|
const escapeCommas = (str) => str.replace(/,/g, '\\,'); |
|
|
|
// Función para deshacer el escape de comas |
|
const unescapeCommas = (str) => str.replace(/\\,/g, ','); |
|
|
|
// Función para dividir por comas que no estén escapadas |
|
const splitByUnescapedCommas = (str) => { |
|
return str |
|
.split(/(?<!\\),/) |
|
.map(s => s.trim()) |
|
.filter(s => s.length > 0) |
|
.map(s => s.replace(/\\,/g, ',')); |
|
}; |
|
|
|
const addValues = (inputValue) => { |
|
// Primero normalizamos el input |
|
const normalizedInput = inputValue.replace(/\s*,\s*/g, ','); |
|
const newValues = splitByUnescapedCommas(normalizedInput) |
|
.map(v => v.trim()) |
|
.filter(v => v); |
|
|
|
if (newValues.length > 0) { |
|
const currentValues = textarea.value |
|
? splitByUnescapedCommas(textarea.value) |
|
: []; |
|
|
|
lastAdded = newValues.filter(v => |
|
!currentValues.some(cv => cv === v) |
|
); |
|
|
|
const combined = [...new Set([...currentValues, ...newValues])]; |
|
textarea.value = combined.map(escapeCommas).join(','); |
|
} |
|
renderPills(); |
|
}; |
|
|
|
const createAddInput = () => { |
|
const addInput = document.createElement("input"); |
|
addInput.type = "text"; |
|
addInput.placeholder = "+ Add"; |
|
addInput.className = |
|
"bg-transparent focus:outline-none text-sm p-1 w-20 animate-fade-in"; |
|
addInput.onclick = (e) => e.stopPropagation(); |
|
|
|
addInput.onkeydown = (e) => { |
|
if (e.key === "Enter") { |
|
e.preventDefault(); |
|
addValues(addInput.value); |
|
} |
|
}; |
|
|
|
addInput.onpaste = (e) => { |
|
e.preventDefault(); |
|
const pasteData = (e.clipboardData || window.clipboardData).getData( |
|
"text" |
|
); |
|
addValues(pasteData); |
|
}; |
|
|
|
return addInput; |
|
}; |
|
|
|
renderPills = () => { |
|
// Obtenemos los valores actuales manejando las comas escapadas |
|
let values = textarea.value.trim() |
|
? splitByUnescapedCommas(textarea.value) |
|
: []; |
|
|
|
// Eliminamos duplicados usando un Set y creamos array de objetos para ordenar |
|
const uniqueValues = [...new Set(values)]; |
|
const itemsToSort = uniqueValues.map(value => ({ |
|
original: value, |
|
normalized: value.toUpperCase() // Normalizamos para ordenar sin importar mayúsculas/minúsculas |
|
})); |
|
|
|
// Ordenamos basándonos en el valor normalizado |
|
itemsToSort.sort((a, b) => a.normalized.localeCompare(b.normalized)); |
|
|
|
// Extraemos los valores originales ordenados |
|
values = itemsToSort.map(item => item.original); |
|
|
|
// Actualizamos el textarea con los valores escapados |
|
textarea.value = values.map(escapeCommas).join(','); |
|
|
|
pillsContainer.innerHTML = ""; |
|
let firstNewPill = null; |
|
values.forEach((value) => { |
|
// Mostramos el valor sin escapar en la interfaz |
|
const pill = createPill(value); |
|
pillsContainer.appendChild(pill); |
|
if (lastAdded.some(added => added === value) && !firstNewPill) { |
|
firstNewPill = pill; |
|
} |
|
}); |
|
|
|
if (firstNewPill) { |
|
firstNewPill.querySelector("span").focus(); |
|
} |
|
lastAdded = []; |
|
|
|
const addInput = createAddInput(); |
|
pillsContainer.appendChild(addInput); |
|
|
|
textarea.classList.add("hidden"); |
|
pillsContainer.classList.remove("hidden"); |
|
}; |
|
|
|
pillsContainer.addEventListener("click", toTextareaView); |
|
textarea.addEventListener("blur", renderPills); |
|
textarea.addEventListener("renderpills", renderPills); |
|
textarea.addEventListener("keydown", (e) => { |
|
if (e.key === "Enter") { |
|
e.preventDefault(); |
|
const beforeValues = textarea.value.trim() |
|
? splitByUnescapedCommas(textarea.value).map(v => v.trim()) |
|
: []; |
|
|
|
renderPills(); |
|
|
|
const afterValues = textarea.value.trim() |
|
? splitByUnescapedCommas(textarea.value).map(v => v.trim()) |
|
: []; |
|
|
|
lastAdded = afterValues.filter(v => !beforeValues.includes(v)); |
|
} |
|
}); |
|
|
|
renderPills(); |
|
}); |
|
} |
|
|
|
const modalHTML = ` |
|
<div id="medium-modal" class="fixed inset-0 z-50 hidden"> |
|
<div class="min-h-screen w-full flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm" data-modal-hide> |
|
<div class="w-full max-w-2xl bg-slate-100 rounded-2xl shadow-2xl flex flex-col max-h-[90vh] overflow-hidden border border-slate-200/80"> |
|
<div class="relative p-5 border-b border-slate-200/80"> |
|
<h3 class="text-lg font-semibold text-slate-800 text-center">Enhanced Filter Settings</h3> |
|
<div class="absolute top-3.5 right-3.5 flex gap-1"> |
|
<button type="button" class="p-2 text-slate-500 hover:text-[#a6004c] transition-colors rounded-full hover:bg-slate-200/70" data-modal-hide aria-label="Minimize Filter Settings"> |
|
${minimizeSVG} |
|
</button> |
|
<button type="button" class="p-2 text-slate-500 hover:text-[#a6004c] transition-colors rounded-full hover:bg-slate-200/70" data-discard-filters data-modal-hide aria-label="Discard Changes"> |
|
${cancelSVG} |
|
</button> |
|
</div> |
|
</div> |
|
<div class="flex-1 overflow-y-auto p-4 bg-slate-50"> |
|
<div class="space-y-4"> |
|
${createFilterSection( |
|
"includeTags", |
|
"Include Games with Tags", |
|
"Only show games that have at least one of these tags.", |
|
"Examples: PC, GAME PASS, XSX Optimized, etc." |
|
)} |
|
${createFilterSection( |
|
"excludeTags", |
|
"Exclude Games with Tags", |
|
"Hide games that have any of these tags.", |
|
"Examples: Preorder, EA, GAME PASS, etc" |
|
)} |
|
${createFilterSection( |
|
"hiddenGames", |
|
"Hidden Games", |
|
"Games that will remain hidden from view.", |
|
"Examples: EA SPORTS, F1, NBA, etc." |
|
)} |
|
</div> |
|
</div> |
|
<div class="flex justify-center gap-3 p-4 bg-slate-100 border-t border-slate-200/80"> |
|
<button id="cancel-filters" type="button" data-discard-filters data-modal-hide class="px-5 py-2.5 text-sm font-medium text-slate-700 bg-white border border-slate-300 hover:bg-slate-100 focus:ring-4 focus:outline-none focus:ring-slate-200 rounded-lg transition-colors"> |
|
Discard |
|
</button> |
|
<button id="apply-filters" type="button" class="px-5 py-2.5 text-sm font-medium text-white! bg-[#a6004c] hover:bg-[#8d0040] focus:ring-4 focus:outline-none focus:ring-[#a6004c]/50 rounded-lg transition-colors"> |
|
Apply Filters |
|
</button> |
|
</div> |
|
</div> |
|
</div> |
|
</div>`; |
|
|
|
document.body.insertAdjacentHTML("beforeend", modalHTML); |
|
initializePillInputs(); |
|
|
|
// Función para dividir por comas que no estén escapadas |
|
const splitByUnescapedCommas = (str) => { |
|
if (!str) return []; |
|
return str |
|
.split(/(?<!\\),/) |
|
.map(s => s.trim()) |
|
.filter(s => s.length > 0) |
|
.map(s => s.replace(/\\,/g, ',')); |
|
}; |
|
|
|
// Función para escapar comas en los valores |
|
const escapeCommas = (str) => str.replace(/,/g, '\\,'); |
|
|
|
function restoreInitialFilters() { |
|
let initialFilters = JSON.parse(JSON.stringify(filters)); |
|
["includeTags", "excludeTags", "hiddenGames"].forEach((id) => { |
|
const filter = initialFilters[id]; |
|
document.getElementById(`${id}-enabled`).checked = filter.enabled; |
|
const textarea = document.getElementById(`${id}-values`); |
|
const values = filter.tags || filter.games || []; |
|
textarea.value = values.map(escapeCommas).join(','); |
|
textarea.dispatchEvent(new CustomEvent("renderpills")); |
|
}); |
|
} |
|
|
|
document.querySelectorAll("[data-modal-hide]").forEach((element) => { |
|
element.addEventListener("click", (e) => { |
|
if ( |
|
e.target === element || |
|
e.target.tagName === "svg" || |
|
e.target.tagName === "path" |
|
) { |
|
e.preventDefault(); |
|
document.getElementById("medium-modal").classList.add("hidden"); |
|
} |
|
}); |
|
}); |
|
|
|
document.querySelectorAll("[data-discard-filters]").forEach((element) => { |
|
element.addEventListener("click", (e) => { |
|
if ( |
|
e.target === element || |
|
e.target.tagName === "svg" || |
|
e.target.tagName === "path" |
|
) { |
|
e.preventDefault(); |
|
restoreInitialFilters(); |
|
} |
|
}); |
|
}); |
|
|
|
document.getElementById("apply-filters")?.addEventListener("click", () => { |
|
["includeTags", "excludeTags", "hiddenGames"].forEach((id) => { |
|
const enabled = document.getElementById(`${id}-enabled`).checked; |
|
const textarea = document.getElementById(`${id}-values`); |
|
const values = splitByUnescapedCommas(textarea.value); |
|
|
|
filters[id] = { |
|
enabled, |
|
[id === "hiddenGames" ? "games" : "tags"]: values, |
|
}; |
|
}); |
|
|
|
localStorage.setItem("filters", JSON.stringify(filters)); |
|
document.getElementById("medium-modal").classList.add("hidden"); |
|
filterGames(); |
|
}); |
|
|
|
filterGames(); |
|
|
|
const filterButton = document.querySelector('a[href="#filterCollapse"]'); |
|
if (!filterButton) return; |
|
const originalRow = filterButton.closest(".row"); |
|
if (!originalRow) return; |
|
const originalText = filterButton.querySelector("span").textContent.trim(); |
|
|
|
const col = originalRow.querySelector(".col-md-6"); |
|
if (col) col.classList.replace("col-md-6", "col-md-3"); |
|
|
|
const newRow = document.createElement("div"); |
|
newRow.className = "col-md-3 col-xs-12 input-group px-[15px]! pb-[15px]!"; |
|
|
|
const icon = document.createElement("div"); |
|
icon.className = "input-group-addon w-[38px]!"; |
|
icon.innerHTML = filterSVG; |
|
icon.firstElementChild.setAttribute("class", "w-full h-full"); |
|
|
|
const link = document.createElement("a"); |
|
link.style.cssText = |
|
"padding-bottom: 5px; text-align: left; cursor: pointer;"; |
|
link.className = "btn btn-white btn-block btn-flat"; |
|
link.innerHTML = `<span>${originalText} +</span>`; |
|
link.addEventListener("click", () => { |
|
const modal = document.getElementById("medium-modal"); |
|
// Asegurarnos de que los valores se inicialicen correctamente |
|
["includeTags", "excludeTags", "hiddenGames"].forEach((id) => { |
|
const filter = filters[id]; |
|
const values = id === "hiddenGames" ? filter.games : filter.tags; |
|
const textarea = document.getElementById(`${id}-values`); |
|
if (textarea) { |
|
// Usamos la función escapeCommas para manejar las comas correctamente |
|
textarea.value = values.map(escapeCommas).join(','); |
|
// Disparamos el evento para renderizar las píldoras |
|
textarea.dispatchEvent(new CustomEvent("renderpills")); |
|
} |
|
}); |
|
modal.classList.remove("hidden"); |
|
}); |
|
|
|
newRow.appendChild(icon); |
|
newRow.appendChild(link); |
|
originalRow.appendChild(newRow); |
|
})(); |