[260111] 프로젝트 이전 및 초기화

This commit is contained in:
2026-01-11 21:02:18 +09:00
commit 4564805a2b
8874 changed files with 95182 additions and 0 deletions

991
cardList.html Normal file
View File

@@ -0,0 +1,991 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<title>Card List</title>
<script src="/script/navigation.js"></script>
<script type="module" src="/i18n/i18n.js"></script>
<link href="/style/navigation.css" rel="stylesheet" />
<link href="/style/style.css" rel="stylesheet" />
<link href="/style/output.css" rel="stylesheet" />
</head>
<body>
<style>
.homeview_container {
display: flex;
flex-direction: column;
}
.ads_coupang iframe {
max-height: 96px;
}
@media screen and (min-width: 640px) {
.ads_coupang {
max-width: 100%;
overflow: hidden;
}
.homeview_container {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.ads_coupang iframe {
max-height: 128px;
}
}
#cardList {
padding-bottom: 128px;
}
</style>
<div id="app">
<!-- 로딩 -->
<div
id="loading-screen"
class="fixed top-0 left-0 w-full h-full bg-white flex justify-center items-center"
>
<div
class="animate-spin rounded-full h-32 w-32 border-t-2 border-b-2 border-gray-900"
></div>
</div>
<div class="homeview_container max-w-6xl lg:max-w-7xl mx-auto">
<div class="sm:col-span-1">
<div
class="aside-widget w-full mx-auto max-w-2xl sm:max-w-4xl lg:max-w-5xl xl:max-w-7xl flex gap-1 justify-between px-4 xl:px-2 sm:mt-20"
>
<div class="w-full flex flex-col gap-2 transition-all">
<div class="">
<div class="relative">
<label
for="name"
class="absolute -top-2 left-2 inline-block bg-white px-1 text-xs font-medium text-gray-900"
>Card Search</label
>
<input
type="text"
id="searchQuery"
class="block w-full rounded-md border-0 px-3 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
placeholder="ex) UA04BT/IMS-1-082"
/>
</div>
</div>
<div class="flex flex-col">
<h4 class="mt-4 font-bold text-lg">COLOR</h4>
<div class="flex flex-wrap gap-2 mt-4">
<button
type="button"
class="color-filter-btn px-3 py-1 rounded border"
data-color="All"
>
All
</button>
<button
type="button"
class="color-filter-btn px-3 py-1 rounded"
data-color="Red"
>
Red
</button>
<button
type="button"
class="color-filter-btn px-3 py-1 rounded"
data-color="Blue"
>
Blue
</button>
<button
type="button"
class="color-filter-btn px-3 py-1 rounded"
data-color="Green"
>
Green
</button>
<button
type="button"
class="color-filter-btn px-3 py-1 rounded"
data-color="Yellow"
>
Yellow
</button>
<button
type="button"
class="color-filter-btn px-3 py-1 rounded"
data-color="Purple"
>
Purple
</button>
<button
type="button"
class="color-filter-btn px-3 py-1 rounded"
data-color="AP"
>
AP
</button>
<button
type="button"
class="color-filter-btn px-3 py-1 rounded"
data-color="Multi"
>
Multi
</button>
</div>
</div>
<div class="flex flex-col aside-accordion-widget">
<h4 class="mt-4 font-bold text-lg">TYPE</h4>
<div
class="checkbox-wrapper w-full flex flex-col gap-2 items-start py-2"
>
<div>
<input id="includeBasic" type="checkbox" checked />
<label for="includeBasic" data-i18n="includeBasic"></label>
</div>
<div>
<input id="includeAP" type="checkbox" />
<label
for="includeAP"
data-i18n="includeActionPoint"
></label>
</div>
<div>
<input id="includeParallel" type="checkbox" />
<label
for="includeParallel"
data-i18n="includeParallel"
></label>
</div>
</div>
<h4 class="mt-4 font-bold text-lg">TRIGGER</h4>
<div class="radio-wrapper w-full flex flex-col gap-2 py-2">
<div>
<input
id="includeAllTrigger"
type="radio"
name="triggerType"
value="allTrigger"
checked
/>
<label
for="includeAllTrigger"
class="ml-2"
data-i18n="includeAllTrigger"
>Include All Trigger</label
>
</div>
<div>
<input
id="includeColorTrigger"
type="radio"
name="triggerType"
value="colorTrigger"
/>
<label
for="includeColorTrigger"
class="ml-2"
data-i18n="includeColorTrigger"
>Include Color Trigger</label
>
</div>
<div>
<input
id="includeSpecialTrigger"
type="radio"
name="triggerType"
value="specialTrigger"
/>
<label
for="includeSpecialTrigger"
class="ml-2"
data-i18n="includeSpecialTrigger"
>Include Special Trigger</label
>
</div>
<div>
<input
id="includeFinalTrigger"
type="radio"
name="triggerType"
value="finalTrigger"
/>
<label
for="includeFinalTrigger"
class="ml-2"
data-i18n="includeFinalTrigger"
>Include Final Trigger</label
>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="SelectedSeriesCardList col-span-3 mt-4 sm:mt-0">
<div
id="totalCount"
class="result_total_count text-center text-xl mb-8"
>
<span data-i18n="SearchResults"></span>:
<span id="SearchResultsCounter">0</span>
<span data-i18n="SearchResultsCounter"></span>
</div>
<div id="cardList">
<div
class="mx-auto max-w-2xl mb-24 px-4 sm:px-6 sm:max-w-4xl lg:max-w-5xl lg:px-8 xl:max-w-7xl"
>
<div
class="mt-6 grid grid-cols-3 gap-x-6 gap-y-6 lg:grid-cols-4 lg:gap-x-5 xl:grid-cols-5 xl:gap-x-8"
id="card-grid"
></div>
</div>
</div>
</div>
</div>
<div class="w-full fixed bottom-0 z-50">
<!-- 프리뷰 버튼 -->
<div class="w-full go-result-page-btn rounded-t-xl">
<!-- Coupang -->
<div class="ads_coupang">
<iframe
src="https://ads-partners.coupang.com/widgets.html?id=915533&template=carousel&trackingCode=AF6561083&subId=&width=1280&height=128&tsource="
width="100%"
height="128"
frameborder="0"
scrolling="no"
referrerpolicy="unsafe-url"
browsingtopics
></iframe>
</div>
<div
id="goToResultPage"
class="w-full h-12 cursor-pointer flex justify-center items-center bg-indigo-500 px-3 py-2 font-semibold text-white shadow-sm hover:bg-indigo-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500"
data-i18n="makeDeckList"
></div>
</div>
<!-- 푸터 카드 현황 -->
<div
class="bottom-line w-full bg-black text-white text-center text-sm flex gap-4 justify-around items-center z-50"
>
<div class="copyright hidden sm:block">
KEY CARD: <span id="keyCardCount">0</span>/4
</div>
<div class="flex gap-4 select-none">
<div id="colorCount">
COLOR: <span id="colorCardCount">0</span>/4
</div>
<span>|</span>
<div id="finalCount">
FINAL: <span id="finalCardCount">0</span>/4
</div>
<span>|</span>
<div id="specialCount">
SPECIAL: <span id="specialCardCount">0</span>/4
</div>
<span>|</span>
<div id="totalCardsCount">
TOTAL: <span id="totalCardCount">0</span>
</div>
</div>
</div>
</div>
</div>
<div id="imageOverlay" class="overlay">
<img src="" alt="Card Preview" />
</div>
<script src="/script/loading.js"></script>
<script>
// 1. 변수 선언 및 초기화
let cardList;
let selectedCards = [];
let selectedColors = new Set(["All"]);
const colorButtons = document.querySelectorAll(".color-filter-btn");
const searchQueryInput = document.getElementById("searchQuery");
const includeBasicCheckbox = document.getElementById("includeBasic");
const includeAPCheckbox = document.getElementById("includeAP");
const includeParallelCheckbox =
document.getElementById("includeParallel");
const includeAllTriggerRadio =
document.getElementById("includeAllTrigger");
const includeSpecialTriggerRadio = document.getElementById(
"includeSpecialTrigger"
);
const includeColorTriggerRadio = document.getElementById(
"includeColorTrigger"
);
const includeFinalTriggerRadio = document.getElementById(
"includeFinalTrigger"
);
const keyCardCountElement = document.getElementById("keyCardCount");
const colorCardCountElement = document.getElementById("colorCardCount");
const finalCardCountElement = document.getElementById("finalCardCount");
const specialCardCountElement =
document.getElementById("specialCardCount");
const totalCardCountElement = document.getElementById("totalCardCount");
// 2. URL 파라미터 및 로컬 스토리지 관리
function getQueryParam(param) {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get(param);
}
const seriesKey = getQueryParam("series");
const nameKey = getQueryParam("seriesName");
if (seriesKey) {
localStorage.setItem(
"selectedSeries",
JSON.stringify({ key: seriesKey, name: nameKey })
);
}
// 3. 데이터 로딩 및 초기화
async function loadCardData() {
try {
const module = await import(`./datas/${seriesKey}.js`);
cardList = module.cardList;
const urlParams = new URLSearchParams(window.location.search);
const cardsParam = urlParams.get("cards");
if (cardsParam) {
const parsedCards = JSON.parse(decodeURIComponent(cardsParam));
cardList = updateCardListFromStorage(cardList, parsedCards);
selectedCards = parsedCards;
displayCardList(cardList);
updateCardList();
} else {
const savedSelectedCards = localStorage.getItem("selectedCards");
if (savedSelectedCards) {
const parsedCards = JSON.parse(
decodeURIComponent(savedSelectedCards)
);
cardList = updateCardListFromStorage(cardList, parsedCards);
selectedCards = parsedCards;
displayCardList(cardList);
updateCardList();
} else {
applyInitialFilter();
}
}
const loadingScreen = document.getElementById("loading-screen");
if (loadingScreen) {
loadingScreen.style.display = "none";
}
updateCounters();
} catch (error) {
console.error("Failed to load card data:", error);
}
}
document.addEventListener("DOMContentLoaded", async () => {
const loadingScreen = document.getElementById("loading-screen");
if (loadingScreen) {
loadingScreen.style.display = "flex";
}
await loadCardData();
const goToResultPage = document.getElementById("goToResultPage");
if (goToResultPage) {
goToResultPage.addEventListener("click", () => {
const queryParams = encodeURIComponent(
JSON.stringify(selectedCards)
);
const seriesKey = getQueryParam("series");
const nameKey = getQueryParam("seriesName");
localStorage.setItem("selectedCards", queryParams);
localStorage.setItem(
"selectedSeries",
JSON.stringify({ key: seriesKey, name: nameKey })
);
window.location.href = `/result.html?series=${seriesKey}&seriesName=${nameKey}&cards=${queryParams}`;
});
}
});
function applyInitialFilter() {
const filteredList = cardList.filter(
(card) => card.color !== "AP" && !card.parallel
);
document.getElementById("includeBasic").checked = true;
document.getElementById("includeAP").checked = false;
document.getElementById("includeParallel").checked = false;
displayCardList(filteredList);
}
function updateCardListFromStorage(cardList, savedSelectedCards) {
const cardMap = new Map(cardList.map((card) => [card.imgSrc, card]));
savedSelectedCards.forEach((savedCard) => {
const uniqueKey = savedCard.imgSrc;
const existingCard = cardMap.get(uniqueKey);
if (existingCard) {
existingCard.count = savedCard.count;
existingCard.key = savedCard.key;
}
});
return Array.from(cardMap.values());
}
const RARITY_STYLE_RULES = [
{
// 🌈 레인보우: 별표 포함 or UR
match: (r) => r.includes("★") || ["UR"].includes(r),
background:
"linear-gradient(45deg, rgba(204, 75, 149, 0.8) 10%, rgba(102, 148, 210, 0.8), rgba(113, 194, 228, 0.8), rgba(129, 197, 101, 0.8), rgba(240, 219, 76, 0.8))",
},
{
// ⚙ 메탈: SP / PcC / PcU / PcR
match: (r) => ["SP", "PcC", "PcU", "PcR"].includes(r),
background:
"linear-gradient(90deg, rgba(139,155,175,0.85), rgba(231,239,247,0.9) 50%, rgba(139,155,175,0.85))",
},
{
// 🟡 SR 계열
match: (r) => ["SR", "PcSR"].includes(r),
background: "#F2F320",
},
];
function applyRarityStyle(rarityDiv, rarity) {
// 기본 배경
rarityDiv.style.backgroundColor = "rgba(255, 255, 255, 0.8)";
for (const rule of RARITY_STYLE_RULES) {
if (rule.match(rarity)) {
rarityDiv.style.background = rule.background;
break; // 첫 매칭 규칙만 적용
}
}
}
// 4. 카드 목록 표시 및 업데이트
function displayCardList(cardList) {
const cardGrid = document.getElementById("card-grid");
cardGrid.innerHTML = "";
// 광고 배너 추가
// const totalCards = cardList.length;
// let adCount = 0;
// if (totalCards >= 50) {
// adCount = Math.floor(totalCards / 150) + 1;
// }
// const adIndices = [];
// if (adCount > 0) {
// const segmentSize = Math.floor(totalCards / adCount);
// for (let i = 0; i < adCount; i++) {
// const start = i * segmentSize;
// const end = (i === adCount - 1) ? totalCards - 1 : (i + 1) * segmentSize - 1;
// const randIndex = Math.floor(Math.random() * (end - start + 1)) + start;
// adIndices.push(randIndex);
// }
// }
cardList.forEach((card, index) => {
const cardContainer = document.createElement("div");
cardContainer.className =
"cardContainer group relative z-10 hover:z-40 select-none flex flex-col";
cardContainer.setAttribute("data-imgsrc", card.imgSrc);
const imgSrc = `images/${seriesKey}/${card.imgSrc.split("/").pop()}`;
const img = document.createElement("img");
img.src = imgSrc;
img.alt = "";
img.className =
"h-full w-full object-contain object-center transition-opacity lg:h-full lg:w-full";
img.onerror = () => handleImageError(card, img);
const imageContainer = document.createElement("div");
imageContainer.className =
"card_image cursor-pointer aspect-w-1 w-full mb-2 lg:mb-3 xl:mb-4 rounded-md lg:aspect-none";
imageContainer.appendChild(img);
imageContainer.addEventListener("click", () => {
showOverlay(card, index, cardList);
});
const countDiv = document.createElement("div");
countDiv.className = "absolute top-0 right-0";
const infoDiv = document.createElement("div");
infoDiv.className = "flex justify-between";
if (card.rarity) {
const rarityDiv = document.createElement("div");
rarityDiv.className =
"card-rarity font-bold inline-flex items-center rounded-full px-2 text-xs ring-1 ring-inset ring-gray-500/10";
rarityDiv.textContent = card.rarity;
applyRarityStyle(rarityDiv, card.rarity.trim());
infoDiv.appendChild(rarityDiv);
}
const numberDiv = document.createElement("div");
numberDiv.className = "flex items-center text-xs";
numberDiv.textContent =
card.color === "AP" ? "ACTION POINT" : card.Number.slice(-5);
infoDiv.appendChild(numberDiv);
const countControlDiv = document.createElement("div");
countControlDiv.className = "pt-1 flex justify-between z-10";
const decrementButton = document.createElement("button");
decrementButton.textContent = "-";
decrementButton.className =
"decrementButton cursor-pointer rounded-lg bg-black w-8 h-8 text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disabled:z-10 disabled:opacity-0 disabled:cursor-not-allowed touch-manipulation";
if (card.count == 0) {
decrementButton.classList.add("opacity-0", "cursor-not-allowed");
}
decrementButton.addEventListener("click", (event) =>
decrementCount(card, event)
);
countControlDiv.appendChild(decrementButton);
const countSpan = document.createElement("span");
countSpan.textContent = card.count;
countSpan.className = "mx-1 w-8 text-center pt-1";
countControlDiv.appendChild(countSpan);
const incrementButton = document.createElement("button");
incrementButton.textContent = "+";
incrementButton.className =
"incrementButton cursor-pointer rounded-lg bg-black w-8 h-8 text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disabled:opacity-0 disabled:cursor-not-allowed touch-manipulation";
if (card.count == 4) {
incrementButton.classList.add("opacity-0", "cursor-not-allowed");
}
incrementButton.addEventListener("click", (event) =>
incrementCount(card, event)
);
countControlDiv.appendChild(incrementButton);
const keyCardButton = document.createElement("button");
keyCardButton.textContent = "KEY CARD";
keyCardButton.className =
"keyCardButton cursor-pointer rounded-lg bg-white w-full px-2 py-1 mt-1 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50";
if (card.key) {
keyCardButton.classList.add("opacity-100");
keyCardButton.classList.remove("opacity-50");
} else {
keyCardButton.classList.add("opacity-50");
keyCardButton.classList.remove("opacity-100");
}
keyCardButton.addEventListener("click", (event) =>
toggleKeyCard(card, event)
);
cardContainer.appendChild(imageContainer);
cardContainer.appendChild(countDiv);
cardContainer.appendChild(infoDiv);
cardContainer.appendChild(countControlDiv);
cardContainer.appendChild(keyCardButton);
cardGrid.appendChild(cardContainer);
// 광고 프레임
// const adFrames = [
// `<iframe src="https://ads-partners.coupang.com/widgets.html?id=915488&template=carousel&trackingCode=AF6561083&subId=&width=200&height=360&tsource=" class="absolute top-0 left-0 w-full h-full" frameborder="0" scrolling="no" referrerpolicy="unsafe-url" browsingtopics></iframe>`,
// `<iframe src="https://ads-partners.coupang.com/widgets.html?id=915505&template=carousel&trackingCode=AF6561083&subId=&width=200&height=360&tsource=" class="absolute top-0 left-0 w-full h-full" frameborder="0" scrolling="no" referrerpolicy="unsafe-url" browsingtopics></iframe>`,
// `<iframe src="https://ads-partners.coupang.com/widgets.html?id=915509&template=carousel&trackingCode=AF6561083&subId=&width=200&height=360&tsource=" class="absolute top-0 left-0 w-full h-full" frameborder="0" scrolling="no" referrerpolicy="unsafe-url" browsingtopics></iframe>`,
// `<iframe src="https://ads-partners.coupang.com/widgets.html?id=915510&template=carousel&trackingCode=AF6561083&subId=&width=200&height=360&tsource=" class="absolute top-0 left-0 w-full h-full" frameborder="0" scrolling="no" referrerpolicy="unsafe-url" browsingtopics></iframe>`,
// `<iframe src="https://ads-partners.coupang.com/widgets.html?id=915514&template=carousel&trackingCode=AF6561083&subId=&width=200&height=360&tsource=" class="absolute top-0 left-0 w-full h-full" frameborder="0" scrolling="no" referrerpolicy="unsafe-url" browsingtopics></iframe>`
// ];
// if (adIndices.includes(index)) {
// console.log("coupang:", index);
// const bannerContainer = document.createElement("div");
// bannerContainer.className = "relative w-full h-full flex flex-col my-2";
// const adIndex = adIndices.indexOf(index) % adFrames.length;
// bannerContainer.innerHTML = adFrames[adIndex];
// cardGrid.appendChild(bannerContainer);
// }
});
function showOverlay(card, index, cardList) {
console.log(card, index, cardList);
const overlay = document.getElementById("imageOverlay");
const img = overlay.querySelector("img");
const imgSrc = `images/${seriesKey}/${card.imgSrc.split("/").pop()}`;
let imageLoaded = false;
img.onload = () => {
imageLoaded = true;
};
img.onerror = () => {
handleOverlayImageError(img);
imageLoaded = true;
};
img.src = imgSrc;
overlay.style.display = "flex";
const handleKeyDown = (event) => {
if (event.key === "ArrowLeft" && imageLoaded) {
index = (index - 1 + cardList.length) % cardList.length;
imageLoaded = false;
showOverlay(cardList[index], index, cardList);
event.preventDefault();
} else if (event.key === "ArrowRight" && imageLoaded) {
index = (index + 1) % cardList.length;
imageLoaded = false;
showOverlay(cardList[index], index, cardList);
event.preventDefault();
}
};
document.addEventListener("keydown", handleKeyDown);
overlay.onclick = (event) => {
if (event.target === overlay) {
overlay.style.display = "none";
document.removeEventListener("keydown", handleKeyDown);
}
};
}
function handleOverlayImageError(img) {
img.src = "/images/uapr/comingsoon.png";
img.onerror = null;
}
const SearchResultsCounter = document.getElementById(
"SearchResultsCounter"
);
SearchResultsCounter.textContent = cardList.length;
colorButtons.forEach((btn) => {
const color = btn.dataset.color;
if (selectedColors.has(color)) {
btn.style.opacity = 1;
} else {
btn.style.opacity = 0.5;
}
if (selectedColors.size === 0) {
colorButtons[0].style.opacity = 1;
}
});
}
function handleImageError(card, img) {
img.src = "/images/uapr/comingsoon.png";
img.onerror = null;
}
// 5. 카드 개수 조절 및 키 카드 관리
function decrementCount(card, event) {
if (card.count > 0) {
event.stopPropagation();
card.count--;
if (card.count === 0) {
card.key = false;
}
updateCardDisplay(card, document.activeElement);
updateCardCount(card, card.count);
updateLocalStorage();
updateQueryParameters();
}
}
function incrementCount(card, event) {
const maxCount = card.maxCount || 4;
const sameNumberCards = cardList.filter(
(c) => c.Number === card.Number
);
const totalCount = sameNumberCards.reduce((sum, c) => sum + c.count, 0);
if (totalCount < maxCount) {
event.stopPropagation();
card.count++;
updateCardDisplay(card, document.activeElement);
updateCardCount(card, card.count);
updateLocalStorage();
updateQueryParameters();
}
}
function updateCardDisplay(card, element) {
const cardContainers = document.querySelectorAll(
`.cardContainer[data-imgsrc="${card.imgSrc}"]`
);
let targetContainer = null;
cardContainers.forEach((container) => {
targetContainer = container;
});
if (targetContainer) {
const countSpan = targetContainer.querySelector(
"span.mx-1.w-8.text-center.pt-1"
);
if (countSpan) {
countSpan.textContent = card.count;
}
updateButtonState(targetContainer, card);
}
updateLocalStorage();
updateCounters();
}
function updateButtonState(container, card) {
const decrementButton = container.querySelector(
"button.decrementButton"
);
const incrementButton = container.querySelector(
"button.incrementButton"
);
const keyCardButton = container.querySelector("button.keyCardButton");
// 같은 Number를 가진 카드들의 총 개수 계산
const sameNumberCards = cardList.filter(
(c) => c.Number === card.Number
);
const totalCount = sameNumberCards.reduce((sum, c) => sum + c.count, 0);
const maxCount = card.maxCount || 4;
if (decrementButton) {
decrementButton.classList.toggle("opacity-0", card.count === 0);
decrementButton.classList.toggle(
"cursor-not-allowed",
card.count === 0
);
}
if (incrementButton) {
incrementButton.classList.toggle("opacity-0", totalCount >= maxCount);
incrementButton.classList.toggle(
"cursor-not-allowed",
totalCount >= maxCount
);
}
if (keyCardButton) {
keyCardButton.disabled = card.count === 0;
if (card.count === 0) {
card.key = false;
}
keyCardButton.classList.toggle(
"opacity-100",
card.key && card.count > 0
);
keyCardButton.classList.toggle(
"opacity-50",
!card.key || card.count === 0
);
}
// 같은 Number를 가진 모든 카드의 + 버튼 상태 업데이트
sameNumberCards.forEach((sameCard) => {
if (sameCard !== card) {
const otherContainer = document.querySelector(
`.cardContainer[data-imgsrc="${sameCard.imgSrc}"]`
);
if (otherContainer) {
const otherIncrementButton = otherContainer.querySelector(
"button.incrementButton"
);
if (otherIncrementButton) {
otherIncrementButton.classList.toggle(
"opacity-0",
totalCount >= maxCount
);
otherIncrementButton.classList.toggle(
"cursor-not-allowed",
totalCount >= maxCount
);
}
}
}
});
}
function updateCounters() {
let keyCount = 0;
let colorCount = 0;
let finalCount = 0;
let specialCount = 0;
let totalCount = 0;
cardList.forEach((card) => {
if (card.key && card.count > 0) {
keyCount++;
}
if (card.trigger === "COLOR" && card.count > 0) {
colorCount += card.count;
}
if (card.trigger === "FINAL" && card.count > 0) {
finalCount += card.count;
}
if (card.trigger === "SPECIAL" && card.count > 0) {
specialCount += card.count;
}
totalCount += card.count;
});
keyCardCountElement.textContent = keyCount;
colorCardCountElement.textContent = colorCount;
finalCardCountElement.textContent = finalCount;
specialCardCountElement.textContent = specialCount;
totalCardCountElement.textContent = totalCount;
}
function toggleKeyCard(card, event) {
event.stopPropagation();
if (card.count === 0) {
return;
}
const maxKeyCards = 4;
const currentKeyCards = cardList.filter(
(c) => c.key && c.count > 0
).length;
if (card.key) {
card.key = false;
} else if (currentKeyCards < maxKeyCards) {
card.key = true;
}
updateCardDisplay(card, document.activeElement);
updateLocalStorage();
updateQueryParameters();
}
function updateSelectedCards() {
selectedCards = cardList.filter((card) => card.count > 0);
}
function updateCardCount(card, count) {
card.count = count;
updateSelectedCards();
updateLocalStorage();
}
// 6. 로컬 스토리지 업데이트 및 결과 페이지 이동
function updateLocalStorage() {
const queryParams = encodeURIComponent(JSON.stringify(selectedCards));
localStorage.setItem("selectedCards", queryParams);
}
// 7. 검색 및 필터링 기능
searchQueryInput.addEventListener("input", updateCardList);
colorButtons.forEach((button) => {
button.addEventListener("click", () => {
const color = button.dataset.color;
if (color === "All") {
selectedColors.clear();
selectedColors.add("All");
} else {
selectedColors.delete("All");
if (selectedColors.has(color)) {
selectedColors.delete(color);
} else {
selectedColors.add(color);
}
}
if (selectedColors.size === 0) {
selectedColors.add("All");
}
updateCardList();
});
});
function updateCardList() {
const includeBasic = includeBasicCheckbox.checked;
const includeAP = includeAPCheckbox.checked;
const includeParallel = includeParallelCheckbox.checked;
console.log(
`TYPE FILTER - [Basic: ${includeBasic}], [AP: ${includeAP}], [Parallel: ${includeParallel}]`
);
let filteredCardList = cardList;
filteredCardList = filteredCardList.filter((card) => {
if (includeBasic && card.color !== "AP" && !card.parallel)
return true;
if (includeAP && card.color === "AP") return true;
if (includeParallel && card.parallel) return true;
return false;
});
if (!selectedColors.has("All")) {
filteredCardList = filteredCardList.filter((card) => {
if (card.color === "Multi") {
return selectedColors.has("ALL COLOR");
} else {
return selectedColors.has(card.color);
}
});
}
const selectedTrigger = document.querySelector(
'input[name="triggerType"]:checked'
).value;
switch (selectedTrigger) {
case "specialTrigger":
filteredCardList = filteredCardList.filter(
(card) => card.trigger === "SPECIAL"
);
break;
case "colorTrigger":
filteredCardList = filteredCardList.filter(
(card) => card.trigger === "COLOR"
);
break;
case "finalTrigger":
filteredCardList = filteredCardList.filter(
(card) => card.trigger === "FINAL"
);
break;
default:
break;
}
const searchQuery = searchQueryInput.value.toLowerCase();
if (searchQuery) {
filteredCardList = filteredCardList.filter((card) => {
return card.Number.toLowerCase().includes(searchQuery);
});
}
displayCardList(filteredCardList);
}
includeBasicCheckbox.addEventListener("change", updateCardList);
includeAPCheckbox.addEventListener("change", updateCardList);
includeParallelCheckbox.addEventListener("change", updateCardList);
includeAllTriggerRadio.addEventListener("change", updateCardList);
includeSpecialTriggerRadio.addEventListener("change", updateCardList);
includeColorTriggerRadio.addEventListener("change", updateCardList);
includeFinalTriggerRadio.addEventListener("change", updateCardList);
function updateQueryParameters() {
const queryParams = encodeURIComponent(JSON.stringify(selectedCards));
const seriesKey = getQueryParam("series");
const nameKey = getQueryParam("seriesName");
const newUrl = new URL(window.location.href);
newUrl.searchParams.set("cards", queryParams);
window.history.replaceState({}, "", newUrl);
}
</script>
</body>
</html>