[260111] 프로젝트 이전 및 초기화
30
.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Node.js
|
||||
node_modules/
|
||||
package-lock.json
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Build output
|
||||
build/
|
||||
dist/
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# Editor/IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
.DS_Store
|
||||
|
||||
# 기타
|
||||
/coverage
|
||||
4
.prettierrc
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"tabWidth": 2,
|
||||
"useTabs": false
|
||||
}
|
||||
14
.prettierrc.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"arrowParens": "avoid",
|
||||
"bracketSameLine": true,
|
||||
"bracketSpacing": true,
|
||||
"endOfLine": "auto",
|
||||
"htmlWhitespaceSensitivity": "ignore",
|
||||
"printWidth": 100,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 4,
|
||||
"trailingComma": "all",
|
||||
"useTabs": false,
|
||||
"vueIndentScriptAndStyle": false
|
||||
}
|
||||
991
cardList.html
Normal 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>
|
||||
44
datas/seriesList.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import title from '../i18n/title.js';
|
||||
|
||||
const currentLanguage = localStorage.getItem('lang') || 'ko';
|
||||
|
||||
const seriesList = [
|
||||
{ key: 'ua01', name: title.ua01[currentLanguage] },
|
||||
{ key: 'ua02', name: title.ua02[currentLanguage] },
|
||||
{ key: 'ua03', name: title.ua03[currentLanguage] },
|
||||
{ key: 'ua04', name: title.ua04[currentLanguage] },
|
||||
{ key: 'ua05', name: title.ua05[currentLanguage] },
|
||||
{ key: 'ua06', name: title.ua06[currentLanguage] },
|
||||
{ key: 'ua07', name: title.ua07[currentLanguage] },
|
||||
{ key: 'ua08', name: title.ua08[currentLanguage] },
|
||||
{ key: 'ua09', name: title.ua09[currentLanguage] },
|
||||
{ key: 'ua10', name: title.ua10[currentLanguage] },
|
||||
{ key: 'ua11', name: title.ua11[currentLanguage] },
|
||||
{ key: 'ua12', name: title.ua12[currentLanguage] },
|
||||
{ key: 'ua13', name: title.ua13[currentLanguage] },
|
||||
{ key: 'ua14', name: title.ua14[currentLanguage] },
|
||||
{ key: 'ua15', name: title.ua15[currentLanguage] },
|
||||
{ key: 'ua16', name: title.ua16[currentLanguage] },
|
||||
{ key: 'ua17', name: title.ua17[currentLanguage] },
|
||||
{ key: 'ua18', name: title.ua18[currentLanguage] },
|
||||
{ key: 'ua19', name: title.ua19[currentLanguage] },
|
||||
{ key: 'ua20', name: title.ua20[currentLanguage] },
|
||||
{ key: 'ua21', name: title.ua21[currentLanguage] },
|
||||
{ key: 'ua22', name: title.ua22[currentLanguage] },
|
||||
{ key: 'ua23', name: title.ua23[currentLanguage] },
|
||||
{ key: 'ua24', name: title.ua24[currentLanguage] },
|
||||
{ key: 'ua25', name: title.ua25[currentLanguage] },
|
||||
{ key: 'ua26', name: title.ua26[currentLanguage] },
|
||||
{ key: 'ua27', name: title.ua27[currentLanguage] },
|
||||
{ key: 'ua28', name: title.ua28[currentLanguage] },
|
||||
{ key: 'ua29', name: title.ua29[currentLanguage] },
|
||||
{ key: 'ua30', name: title.ua30[currentLanguage] },
|
||||
{ key: 'ua31', name: title.ua31[currentLanguage] },
|
||||
{ key: 'ua32', name: title.ua32[currentLanguage] },
|
||||
{ key: 'ua33', name: title.ua33[currentLanguage] },
|
||||
{ key: 'ua34', name: title.ua34[currentLanguage] },
|
||||
{ key: 'ua35', name: title.ua35[currentLanguage] },
|
||||
{ key: 'ua36', name: title.ua36[currentLanguage] },
|
||||
];
|
||||
|
||||
export default seriesList; // seriesList를 default export
|
||||
2767
datas/ua01.js
Normal file
2911
datas/ua02.js
Normal file
2749
datas/ua03.js
Normal file
4030
datas/ua04.js
Normal file
2871
datas/ua05.js
Normal file
1618
datas/ua06.js
Normal file
2730
datas/ua07.js
Normal file
2819
datas/ua08.js
Normal file
1640
datas/ua09.js
Normal file
2728
datas/ua10.js
Normal file
1689
datas/ua11.js
Normal file
1759
datas/ua12.js
Normal file
1608
datas/ua13.js
Normal file
1658
datas/ua14.js
Normal file
2954
datas/ua15.js
Normal file
1708
datas/ua16.js
Normal file
1648
datas/ua17.js
Normal file
1759
datas/ua18.js
Normal file
1710
datas/ua19.js
Normal file
1688
datas/ua20.js
Normal file
1726
datas/ua21.js
Normal file
1177
datas/ua22.js
Normal file
2777
datas/ua23.js
Normal file
1627
datas/ua24.js
Normal file
1196
datas/ua25.js
Normal file
1317
datas/ua26.js
Normal file
1667
datas/ua27.js
Normal file
1226
datas/ua28.js
Normal file
3109
datas/ua29.js
Normal file
2859
datas/ua30.js
Normal file
1699
datas/ua31.js
Normal file
1178
datas/ua32.js
Normal file
1246
datas/ua33.js
Normal file
1187
datas/ua34.js
Normal file
1666
datas/ua35.js
Normal file
1748
datas/ua36.js
Normal file
1688
datas/ua37.js
Normal file
1238
datas/ua38.js
Normal file
1217
datas/ua39.js
Normal file
1688
datas/ua40.js
Normal file
1698
datas/ua41.js
Normal file
1778
datas/ua42.js
Normal file
1198
datas/ua43.js
Normal file
1709
datas/ua44.js
Normal file
1397
datas/ua45.js
Normal file
1348
datas/ua46.js
Normal file
1779
datas/ua47.js
Normal file
1007
datas/ua99 sample mini.js
Normal file
1318
datas/ua99 sample.js
Normal file
54
datas/uapr.js
Normal file
@@ -0,0 +1,54 @@
|
||||
const uaprData = [
|
||||
{
|
||||
imgSrc: '/uapr/UAPR_2023-AP01.png',
|
||||
Number: 'UAPR/2023-AP01',
|
||||
count: 0,
|
||||
key: false,
|
||||
color: 'AP',
|
||||
parallel: false,
|
||||
rarity: '',
|
||||
trigger: null,
|
||||
},
|
||||
{
|
||||
imgSrc: '/uapr/UAPR_2023-AP02.png',
|
||||
Number: 'UAPR/2023-AP02',
|
||||
count: 0,
|
||||
key: false,
|
||||
color: 'AP',
|
||||
parallel: false,
|
||||
rarity: '',
|
||||
trigger: null,
|
||||
},
|
||||
{
|
||||
imgSrc: '/uapr/UAPR_2023-AP03.png',
|
||||
Number: 'UAPR/2023-AP03',
|
||||
count: 0,
|
||||
key: false,
|
||||
color: 'AP',
|
||||
parallel: false,
|
||||
rarity: '',
|
||||
trigger: null,
|
||||
},
|
||||
{
|
||||
imgSrc: '/uapr/UAPR_2023-AP04.png',
|
||||
Number: 'UAPR/2023-AP04',
|
||||
count: 0,
|
||||
key: false,
|
||||
color: 'AP',
|
||||
parallel: false,
|
||||
rarity: '',
|
||||
trigger: null,
|
||||
},
|
||||
{
|
||||
imgSrc: '/uapr/UAPR_2023-AP05.png',
|
||||
Number: 'UAPR/2023-AP05',
|
||||
count: 0,
|
||||
key: false,
|
||||
color: 'AP',
|
||||
parallel: false,
|
||||
rarity: '',
|
||||
trigger: null,
|
||||
},
|
||||
];
|
||||
|
||||
export default uaprData;
|
||||
BIN
favicon.ico
Normal file
|
After Width: | Height: | Size: 103 KiB |
60
i18n/i18n.js
Normal file
@@ -0,0 +1,60 @@
|
||||
// 계층구조 사용시
|
||||
async function loadTranslations(lang) {
|
||||
try {
|
||||
const response = await fetch(`/i18n/translations.${lang}.json`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load translations for ${lang}`);
|
||||
}
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
async function localizePage(lang) {
|
||||
const translations = await loadTranslations(lang);
|
||||
|
||||
const elements = document.querySelectorAll('[data-i18n]');
|
||||
elements.forEach(element => {
|
||||
const key = element.dataset.i18n;
|
||||
const localizedText = findNestedValue(translations, key);
|
||||
if (localizedText) {
|
||||
element.textContent = localizedText;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function findNestedValue(obj, key) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
return obj[key];
|
||||
}
|
||||
for (const prop in obj) {
|
||||
if (typeof obj[prop] === 'object' && obj[prop] !== null) {
|
||||
const result = findNestedValue(obj[prop], key);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const storedLang = localStorage.getItem('lang');
|
||||
|
||||
if (storedLang) {
|
||||
await localizePage(storedLang);
|
||||
return;
|
||||
}
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const queryLang = urlParams.get('lang');
|
||||
|
||||
const lang = queryLang || 'ko';
|
||||
|
||||
localStorage.setItem('lang', lang);
|
||||
await localizePage(lang);
|
||||
});
|
||||
|
||||
// 계층구조 미사용시 코드는 주석 처리된 상태로 유지
|
||||
91
i18n/translations.en.json
Normal file
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"all": "All",
|
||||
"metaDeck": "Meta Deck",
|
||||
"titlePrompt": "Please select a title",
|
||||
"selectTitle": "Select Title",
|
||||
"deckName": "Deck Name",
|
||||
"deckNamePlaceHolder": "Please enter the name",
|
||||
"myDeckHistory": "My Deck History",
|
||||
"notHistory": "No saved history",
|
||||
"preview": "Preview",
|
||||
"back": "Back",
|
||||
"close": "Close",
|
||||
"reset": "Reset",
|
||||
"dataBackup": "Data Backup",
|
||||
"dataRecovery": "Data Recovery",
|
||||
"saveAsImage": "Save as Image",
|
||||
"selectDeckTemplate": "Select Deck Template",
|
||||
"nowSaving": "Now Saving ...",
|
||||
"search": "Search",
|
||||
"searchPlaceHolder": "ex) 008 ...",
|
||||
"noSearchResults": "No search results.",
|
||||
"scrollingNotice": "This site is optimized for PC (Chrome browser).",
|
||||
"cardTypeTitle": "Cards Type",
|
||||
"includeParallel": "Show parallel cards",
|
||||
"includeActionPoint": "Show AP Cards",
|
||||
"onlyParallel": "Show only parallel cards",
|
||||
"onlyActionPoint": "Show only AP Cards",
|
||||
"triggerTypeTitle": "Trigger Type",
|
||||
"includeAllTrigger": "ALL",
|
||||
"includeColorTrigger": "COLOR",
|
||||
"includeFinalTrigger": "FINAL",
|
||||
"includeSpecialTrigger": "SPECIAL",
|
||||
"resetCountConfirm": "Anything written to this set will be deleted. Do you still want to reset it?",
|
||||
"SearchResults": "Search Results",
|
||||
"SearchResultsCounter": "Cards",
|
||||
"limitOverAlert": "There are cards exceeding the quantity limit. Should we proceed as is?",
|
||||
"highQualityTitle": "Save in high definition",
|
||||
"highQualityDesc": "※ Caution: Quality is doubled, file size is quadrupled!",
|
||||
"previewMessage1": "Preview is not supported on mobile.",
|
||||
"previewMessage2": "After saving the image, please check.",
|
||||
"series": {
|
||||
"ua01": "Code Geass: Lelouch of the Rebellion",
|
||||
"ua02": "Jujutsu Kaisen",
|
||||
"ua03": "HUNTER×HUNTER",
|
||||
"ua04": "THE iDOLM@STER SHINY COLORS",
|
||||
"ua05": "Demon Slayer: Kimetsu no Yaiba",
|
||||
"ua06": "Tales of ARISE",
|
||||
"ua07": "That Time I Got Reincarnated as a Slime",
|
||||
"ua08": "Bleach: Thousand-Year Blood War",
|
||||
"ua09": "Me & Roboco",
|
||||
"ua10": "My Hero Academia",
|
||||
"ua11": "Gintama",
|
||||
"ua12": "Blue Lock",
|
||||
"ua13": "TEKKEN 7",
|
||||
"ua14": "Dr.STONE",
|
||||
"ua15": "Sword Art Online",
|
||||
"ua16": "SYNDUALITY Noir",
|
||||
"ua17": "Toriko",
|
||||
"ua18": "GODDESS OF VICTORY:NIKKE",
|
||||
"ua19": "Haikyu!!",
|
||||
"ua20": "BLACK CLOVER",
|
||||
"ua21": "Yu Yu Hakusho",
|
||||
"ua22": "GAMERA -Rebirth-",
|
||||
"ua23": "Attack on Titan",
|
||||
"ua24": "SHY",
|
||||
"ua25": "Undead Unluck",
|
||||
"ua26": "The 100 Girlfriends Who Really, Really, Really, Really Love You",
|
||||
"ua27": "Gakuen iDOLM@STER",
|
||||
"ua28": "Kaiju No.8",
|
||||
"ua29": "Kamen Rider",
|
||||
"ua30": "Arknights",
|
||||
"ua31": "Puella Magi Madoka Magica",
|
||||
"ua32": "Shangri-La Frontier",
|
||||
"ua33": "2.5 Dimensional Seduction",
|
||||
"ua34": "CODE GEASS: Roze of the Recapture",
|
||||
"ua35": "ONE-PUNCH MAN",
|
||||
"ua36": "Macross",
|
||||
"ua37": "Fullmetal Alchemist",
|
||||
"ua38": "WIND BREAKER",
|
||||
"ua39": "ULTIMATE MUSCLE",
|
||||
"ua40": "Re:ZERO -Starting Life in Another World-",
|
||||
"ua41": "Rurouni Kenshin: Meiji Kenkaku Romantan",
|
||||
"ua42": "Monogatari Series",
|
||||
"ua43": "SAKAMOTO DAYS",
|
||||
"ua44": "Rebuild of Evangelion",
|
||||
"ua45": "To Love Ru",
|
||||
"ua46": "Kagurabachi",
|
||||
"ua47": "Tokyo Ghoul"
|
||||
},
|
||||
"uapr": "UNION ARENA"
|
||||
}
|
||||
107
i18n/translations.ja.json
Normal file
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"all": "全て",
|
||||
"metaDeck": "メタデッキ",
|
||||
"titlePrompt": "タイトルを選択してください",
|
||||
"selectTitle": "タイトルを選択",
|
||||
"deckName": "デッキ名",
|
||||
"deckNamePlaceHolder": "名前を入力してください",
|
||||
"myDeckHistory": "私のデッキ記録",
|
||||
"notHistory": "保存された記録はありません",
|
||||
"close": "CLOSE",
|
||||
"reset": "リセット",
|
||||
"dataBackup": "データバックアップ",
|
||||
"dataRecovery": "データ復元",
|
||||
"selectDeckTemplate": "Select Deck Template",
|
||||
"nowSaving": "保存中...",
|
||||
"search": "検索",
|
||||
"searchPlaceHolder": "ex) 008 ...",
|
||||
"noSearchResults": "検索結果がありません。",
|
||||
"scrollingNotice": "このサイトはPC(クロームブラウザ)に最適化されています。",
|
||||
"cardTypeTitle": "カード種類",
|
||||
"onlyParallel": "パラレルカードのみを表示",
|
||||
"onlyActionPoint": "APカードのみを表示",
|
||||
"triggerTypeTitle": "トリガー効果",
|
||||
"includeAllTrigger": "ALL",
|
||||
"includeColorTrigger": "COLOR",
|
||||
"includeFinalTrigger": "FINAL",
|
||||
"includeSpecialTrigger": "SPECIAL",
|
||||
"resetCountConfirm": "このセットに記録されている内容はすべて削除されます。\nそれでもリセットしますか?",
|
||||
"limitOverAlert": "制限数を超えるカードがあります。このまま続行しますか?",
|
||||
"highQualityTitle": "高画質で保存する",
|
||||
"highQualityDesc": "※ 注意: 品質は2倍、容量は4倍です!",
|
||||
"previewMessage1": "モバイルでのプレビューはサポートされていません。",
|
||||
"previewMessage2": "画像を保存した後、確認してください。",
|
||||
"series": {
|
||||
"ua01": "コードギアス 反逆のルルーシュ",
|
||||
"ua02": "呪術廻戦",
|
||||
"ua03": "HUNTER×HUNTER",
|
||||
"ua04": "アイドルマスター シャイニーカラーズ",
|
||||
"ua05": "鬼滅の刃",
|
||||
"ua06": "Tales of ARISE",
|
||||
"ua07": "転生したらスライムだった件",
|
||||
"ua08": "BLEACH 千年血戦篇",
|
||||
"ua09": "僕とロボコ",
|
||||
"ua10": "僕のヒーローアカデミア",
|
||||
"ua11": "銀魂",
|
||||
"ua12": "ブルーロック",
|
||||
"ua13": "鉄拳7",
|
||||
"ua14": "Dr.STONE",
|
||||
"ua15": "ソードアート・オンライン",
|
||||
"ua16": "SYNDUALITY Noir",
|
||||
"ua17": "トリコ",
|
||||
"ua18": "勝利の女神:NIKKE",
|
||||
"ua19": "ハイキュー‼",
|
||||
"ua20": "ブラッククローバー",
|
||||
"ua21": "幽☆遊☆白書",
|
||||
"ua22": "GAMERA -Rebirth-",
|
||||
"ua23": "進撃の巨人",
|
||||
"ua24": "SHY",
|
||||
"ua25": "アンデッドアンラック",
|
||||
"ua26": "君のことが大大大大大好きな100人の彼女",
|
||||
"ua27": "学園アイドルマスター",
|
||||
"ua28": "怪獣8号",
|
||||
"ua29": "仮面ライダー",
|
||||
"ua30": "アークナイツ",
|
||||
"ua31": "魔法少女まどか☆マギカ",
|
||||
"ua32": "シャングリラ・フロンティア",
|
||||
"ua33": "2.5次元の誘惑",
|
||||
"ua34": "コードギアス 奪還のロゼ",
|
||||
"ua35": "ワンパンマン",
|
||||
"ua36": "マクロス",
|
||||
"ua37": "鋼の錬金術師",
|
||||
"ua38": "WIND BREAKER",
|
||||
"ua39": "キン肉マン",
|
||||
"ua40": "Re:ゼロから始める異世界生活",
|
||||
"ua41": "るろうに剣心 -明治剣客浪漫譚-",
|
||||
"ua42": "〈物語〉シリーズ",
|
||||
"ua43": "SAKAMOTO DAYS",
|
||||
"ua44": "ヱヴァンゲリヲン新劇場版",
|
||||
"ua45": "To LOVEる-とらぶる-",
|
||||
"ua46": "カグラバチ",
|
||||
"ua47": "東京喰種トーキョーグール"
|
||||
},
|
||||
"uapr": "UNION ARENA",
|
||||
"CardList": {
|
||||
"ResultsCounter": {
|
||||
"SearchResults": "検索結果",
|
||||
"SearchResultsCounter": "件"
|
||||
},
|
||||
"CardType": {
|
||||
"includeBasic": "기본 카드",
|
||||
"includeParallel": "パラレルカード",
|
||||
"includeActionPoint": "APカード"
|
||||
},
|
||||
"TriggerType": {
|
||||
"includeAllTrigger": "모든 트리거",
|
||||
"includeColorTrigger": "COLOR",
|
||||
"includeSpecialTrigger": "SPECIAL",
|
||||
"includeFinalTrigger": "FINAL"
|
||||
},
|
||||
"makeDeckList": "덱 리스트 만들기"
|
||||
},
|
||||
"Result": {
|
||||
"preview": "プレビュー",
|
||||
"back": "戻る",
|
||||
"saveAsImage": "画像として保存"
|
||||
}
|
||||
}
|
||||
103
i18n/translations.ko.json
Normal file
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"all": "전체",
|
||||
"metaDeck": "메타 덱",
|
||||
"titlePrompt": "작품을 선택해주세요",
|
||||
"selectTitle": "작품 선택",
|
||||
"deckName": "덱 이름",
|
||||
"deckNamePlaceHolder": "이름을 입력해주세요",
|
||||
"myDeckHistory": "나의 덱 기록",
|
||||
"notHistory": "저장된 기록이 없습니다",
|
||||
"close": "닫기",
|
||||
"reset": "초기화",
|
||||
"dataBackup": "데이터 백업",
|
||||
"dataRecovery": "데이터 복구",
|
||||
"selectDeckTemplate": "덱 템플릿 선택",
|
||||
"nowSaving": "이미지로 저장 중 ...",
|
||||
"search": "검색",
|
||||
"searchPlaceHolder": "예) 008 ...",
|
||||
"noSearchResults": "검색 결과가 없습니다.",
|
||||
"scrollingNotice": "본 사이트는 PC(크롬 브라우저)에 최적화 되어있습니다.",
|
||||
"cardTypeTitle": "카드 종류",
|
||||
"onlyParallel": "패러렐 카드만 표시",
|
||||
"onlyActionPoint": "액션 포인트 카드만 표시",
|
||||
"triggerTypeTitle": "트리거 효과",
|
||||
"resetCountConfirm": "이 세트에 기록된 모든 내용은 삭제됩니다. 그래도 초기화하시겠습니까?",
|
||||
"limitOverAlert": "제한 수량을 초과하는 카드가 있습니다. 이대로 계속 진행할까요?",
|
||||
"highQualityTitle": "고화질로 저장하기",
|
||||
"highQualityDesc": "※ 주의: 품질은 2배, 용량은 4배!",
|
||||
"previewMessage1": "모바일에서는 미리보기를 지원하지 않습니다.",
|
||||
"previewMessage2": "이미지를 저장한 후 확인해주세요.",
|
||||
"series": {
|
||||
"ua01": "코드 기어스: 반역의 를르슈",
|
||||
"ua02": "주술회전",
|
||||
"ua03": "HUNTER×HUNTER",
|
||||
"ua04": "아이돌 마스터 샤이니 컬러즈",
|
||||
"ua05": "귀멸의칼날",
|
||||
"ua06": "Tales of ARISE",
|
||||
"ua07": "전생했더니 슬라임이었던 건에 대하여",
|
||||
"ua08": "BLEACH 천년혈전 편",
|
||||
"ua09": "나와 로보코",
|
||||
"ua10": "나의 히어로 아카데미아",
|
||||
"ua11": "은혼",
|
||||
"ua12": "블루 록",
|
||||
"ua13": "철권7",
|
||||
"ua14": "Dr.STONE",
|
||||
"ua15": "소드 아트 온라인",
|
||||
"ua16": "신듀얼리티 느와르",
|
||||
"ua17": "토리코",
|
||||
"ua18": "승리의여신:NIKKE",
|
||||
"ua19": "하이큐ー‼",
|
||||
"ua20": "블랙 클로버",
|
||||
"ua21": "유유백서",
|
||||
"ua22": "가메라 -리버스-",
|
||||
"ua23": "진격의 거인",
|
||||
"ua24": "SHY",
|
||||
"ua25": "언데드 언럭",
|
||||
"ua26": "너를 너무너무너무너무 좋아하는 100명의 그녀",
|
||||
"ua27": "학원 아이돌 마스터",
|
||||
"ua28": "괴수 8호",
|
||||
"ua29": "가면 라이더",
|
||||
"ua30": "명일방주",
|
||||
"ua31": "마법소녀 마도카☆마기카",
|
||||
"ua32": "샹그릴라 프론티어",
|
||||
"ua33": "2.5차원의 유혹",
|
||||
"ua34": "코드기어스 탈환의로제",
|
||||
"ua35": "원펀맨",
|
||||
"ua36": "마크로스",
|
||||
"ua37": "강철의 연금술사",
|
||||
"ua38": "WIND BREAKER",
|
||||
"ua39": "근육맨",
|
||||
"ua40": "Re: 제로부터 시작하는 이세계 생활",
|
||||
"ua41": "바람의 검심 -메이지 검객 낭만기-",
|
||||
"ua42": "모노가타리 시리즈",
|
||||
"ua43": "사카모토 데이즈",
|
||||
"ua44": "에반게리온 신극장판",
|
||||
"ua45": "To LOVE 트러블",
|
||||
"ua46": "카구라바치",
|
||||
"ua47": "도쿄 구울"
|
||||
},
|
||||
"uapr": "UNION ARENA",
|
||||
"CardList": {
|
||||
"ResultsCounter": {
|
||||
"SearchResults": "검색결과",
|
||||
"SearchResultsCounter": "건"
|
||||
},
|
||||
"CardType": {
|
||||
"includeBasic": "기본 카드",
|
||||
"includeParallel": "패러렐 카드",
|
||||
"includeActionPoint": "액션 포인트(AP)"
|
||||
},
|
||||
"TriggerType": {
|
||||
"includeAllTrigger": "모든 트리거",
|
||||
"includeColorTrigger": "COLOR",
|
||||
"includeSpecialTrigger": "SPECIAL",
|
||||
"includeFinalTrigger": "FINAL"
|
||||
},
|
||||
"makeDeckList": "덱 리스트 만들기"
|
||||
},
|
||||
"Result": {
|
||||
"preview": "미리보기",
|
||||
"back": "돌아가기",
|
||||
"saveAsImage": "이미지로 저장"
|
||||
}
|
||||
}
|
||||
BIN
images/logo_unionarena.png
Normal file
|
After Width: | Height: | Size: 421 KiB |
BIN
images/ua01/EX02BT_CGH-2-001.png
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
images/ua01/EX02BT_CGH-2-002.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
images/ua01/EX02BT_CGH-2-003.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
images/ua01/EX02BT_CGH-2-004.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
images/ua01/EX02BT_CGH-2-005.png
Normal file
|
After Width: | Height: | Size: 181 KiB |
BIN
images/ua01/EX02BT_CGH-2-005_p1.png
Normal file
|
After Width: | Height: | Size: 212 KiB |
BIN
images/ua01/EX02BT_CGH-2-006.png
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
images/ua01/EX02BT_CGH-2-006_p1.png
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
images/ua01/EX02BT_CGH-2-007.png
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
images/ua01/EX02BT_CGH-2-008.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
images/ua01/EX02BT_CGH-2-009.png
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
images/ua01/EX02BT_CGH-2-010.png
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
images/ua01/EX02BT_CGH-2-011.png
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
images/ua01/EX02BT_CGH-2-012.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
images/ua01/EX02BT_CGH-2-013.png
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
images/ua01/EX02BT_CGH-2-013_p1.png
Normal file
|
After Width: | Height: | Size: 171 KiB |
BIN
images/ua01/EX02BT_CGH-2-014.png
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
images/ua01/EX02BT_CGH-2-015.png
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
images/ua01/EX02BT_CGH-2-015_p1.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
images/ua01/EX02BT_CGH-2-016.png
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
images/ua01/EX02BT_CGH-2-016_p1.png
Normal file
|
After Width: | Height: | Size: 204 KiB |
BIN
images/ua01/EX02BT_CGH-2-017.png
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
images/ua01/EX02BT_CGH-2-018.png
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
images/ua01/EX02BT_CGH-2-019.png
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
images/ua01/EX02BT_CGH-2-020.png
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
images/ua01/EX02BT_CGH-2-021.png
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
images/ua01/EX02BT_CGH-2-022.png
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
images/ua01/EX02BT_CGH-2-023.png
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
images/ua01/EX02BT_CGH-2-024.png
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
images/ua01/EX02BT_CGH-2-024_p1.png
Normal file
|
After Width: | Height: | Size: 225 KiB |
BIN
images/ua01/EX02BT_CGH-2-025.png
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
images/ua01/EX02BT_CGH-2-026.png
Normal file
|
After Width: | Height: | Size: 224 KiB |
BIN
images/ua01/EX02BT_CGH-2-026_p1.png
Normal file
|
After Width: | Height: | Size: 230 KiB |
BIN
images/ua01/EX02BT_CGH-2-027.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
images/ua01/EX02BT_CGH-2-028.png
Normal file
|
After Width: | Height: | Size: 161 KiB |
BIN
images/ua01/EX02BT_CGH-2-029.png
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
images/ua01/EX02BT_CGH-2-030.png
Normal file
|
After Width: | Height: | Size: 225 KiB |
BIN
images/ua01/EX02BT_CGH-2-030_p1.png
Normal file
|
After Width: | Height: | Size: 230 KiB |
BIN
images/ua01/EX02BT_CGH-2-031.png
Normal file
|
After Width: | Height: | Size: 148 KiB |