<!DOCTYPE html> <html> <head> <title>Card List Parser</title> <style> #toastMessage { visibility: hidden; min-width: 250px; margin-left: -125px; background-color: #333; color: #fff; text-align: center; border-radius: 2px; padding: 16px; position: fixed; z-index: 1; left: 50%; bottom: 30px; font-size: 30px; } </style> </head> <select id="listSelector"> <option value="parsedList">Parsed List</option> <option value="addedList">Added List</option> <option value="removedList">Removed List</option> </select> <label><input type="checkbox" id="toggleEditor"> Hide Text Editor</label><br> <textarea id="textInput" rows="10" cols="50"></textarea><br> <button id="applyButton">Apply</button> <button id="resetButton">Reset</button> <input type="file" id="fileInput" /> <label><input type="checkbox" id="toggleInfo"> Show card names only</label> <button id="copyToClipboard">Copy to Clipboard</button> <div id="toastMessage">클립보드에 현재 리스트가 저장되었습니다.</div> <div id="cardListOutput"></div> <script> document.getElementById('listSelector').addEventListener('change', function() { const selectedList = this.value; let displayList; switch (selectedList) { case 'parsedList': sortCards(parsedList) currentDisplayList = parsedList; listType = 'parsedList'; break; case 'addedList': sortCards(addedList); currentDisplayList = addedList; listType = 'addedList'; break; case 'removedList': sortCards(removedList); currentDisplayList = removedList; listType = 'removedList'; break; default: } displayCards(currentDisplayList, false, listType); }); document.getElementById('toggleEditor').addEventListener('change', function() { const isHidden = this.checked; const editorElements = [document.getElementById('textInput'), document.getElementById('applyButton'), document.getElementById('resetButton')]; editorElements.forEach(element => { element.style.display = isHidden ? 'none' : 'block'; }); }); document.getElementById('fileInput').addEventListener('change', handleFileSelect, false); document.getElementById('toggleInfo').addEventListener('change', handleToggleChange, false); document.getElementById('applyButton').addEventListener('click', function() { const textInput = document.getElementById('textInput').value; applyText(textInput); }); document.getElementById('resetButton').addEventListener('click', function() { document.getElementById('textInput').value = ''; document.getElementById('cardListOutput').innerHTML = ''; }); document.getElementById('copyToClipboard').addEventListener('click', function() { const cardNames = currentDisplayList.map(card => card.name).join('\\n'); navigator.clipboard.writeText(cardNames).then(() => { const toast = document.getElementById('toastMessage'); toast.style.visibility = 'visible'; setTimeout(() => toast.style.visibility = 'hidden', 2500); }).catch(err => { console.error('클립보드 복사 실패:', err); }); }); let parsedList = []; let removedList = []; let addedList = []; let currentDisplayList = parsedList; let listType = "parsedList"; function applyText(text) { clearCardList(); parseCardList(parsedList, text); sortCards(parsedList); displayCards(parsedList, false, listType); } function clearCardList() { parsedList.length = 0; removedList.length = 0; addedList.length = 0; } function handleFileSelect(event) { const file = event.target.files[0]; if (!file) { return; } const reader = new FileReader(); reader.onload = function(fileEvent) { const text = fileEvent.target.result; applyText(text); }; reader.readAsText(file); } function handleToggleChange() { const isChecked = document.getElementById('toggleInfo').checked; displayCards(currentDisplayList, isChecked, listType); } function displayCards(displayList, showNamesOnly, listType) { const outputDiv = document.getElementById('cardListOutput'); outputDiv.innerHTML = ''; // 장수별 컬러 const quantityColorMap = { 2: 'blue', 3: 'green', 4: 'orange', 5: 'red' }; displayList.forEach((card, index) => { let color = quantityColorMap[card.quantity] || 'black'; // 카드 정보 표시 const cardElement = document.createElement('div'); // 카드 정보 구성 let cardInfo = `<strong style="color: ${color};">${card.name}</strong>`; if (!showNamesOnly) { cardInfo = `<br>${card.edition} (${card.condition}, Quantity: ${card.quantity}, Price: $${card.price.toFixed(2)}, Total: $${card.total.toFixed(2)})</br>` + cardInfo; } cardElement.innerHTML = cardInfo; if (listType == "parsedList") { // add 버튼 const addButton = document.createElement('button'); addButton.textContent = 'Add'; addButton.onclick = function() { if (card.quantity > 1) { card.quantity -= 1; addedList.push({...card, quantity: 1}); } else { addedList.push(card); displayList.splice(index, 1); } displayCards(displayList, showNamesOnly, listType); }; cardElement.appendChild(addButton); // remove 버튼 const removeButton = document.createElement('button'); removeButton.textContent = 'Remove'; removeButton.onclick = function() { if (card.quantity > 1) { card.quantity -= 1; removedList.push({...card, quantity: 1}); } else { removedList.push(card); displayList.splice(index, 1); } displayCards(displayList, showNamesOnly, listType); }; cardElement.appendChild(removeButton); } else if (listType === 'addedList' || listType === 'removedList') { // 복구 버튼 const restoreButton = document.createElement('button'); restoreButton.textContent = '복구'; restoreButton.onclick = function() { displayList.splice(index, 1); const existingCardIndex = parsedList.findIndex(c => c.name === card.name && c.edition === card.edition ); if (existingCardIndex !== -1) { parsedList[existingCardIndex].quantity += card.quantity; } else { parsedList.push({...card}); } displayCards(displayList, showNamesOnly, listType); }; cardElement.appendChild(restoreButton); } // 클릭 시, 바뀌는 배경 컬러 const bgColors = ['white', '#add8e6', '#90ee90']; let currentColorIndex = 0; // 클릭시 배경 변경 cardElement.style.cursor = 'pointer'; cardElement.onclick = function() { currentColorIndex = (currentColorIndex + 1) % bgColors.length; this.style.backgroundColor = bgColors[currentColorIndex]; }; outputDiv.appendChild(cardElement); }); } function parseCardList(_cards, cardListText) { const lines = cardListText.split('\\n'); let currentCondition = ''; lines.forEach(line => { if ((/^\\s*$/.test(line)) || (/SINGLES\\s*$/.test(line)) || (/Description\\s*/.test(line))) { return; } const firstColonIndex = line.indexOf(':'); if (firstColonIndex === -1) return; // 카드명, 판본 분리, ':'가 없으면 유효 X const name = line.substring(0, firstColonIndex).trim(); const restLine = line.substring(firstColonIndex + 1).trim(); const parts = restLine.split('\\t'); // 나머지 분리 if (parts.length >= 4) { const edition = parts[0].trim(); const condition = parts[1].trim(); const quantity = parseInt(parts[2]); const price = parseFloat(parts[3].substring(1)); const total = parseFloat(parts[4].substring(1)); _cards.push({ name, edition, condition, quantity, price, total }); } }); return _cards; } function sortCards(_cards) { const conditionOrder = { 'NM': 1, 'EX': 2, 'G': 3, 'VG': 4 }; // 상태 / 판본 / 이름 순 정렬 return _cards.sort((a, b) => { const conditionCompare = (conditionOrder[a.condition] || 5) - (conditionOrder[b.condition] || 5); if (conditionCompare !== 0) return conditionCompare; const editionCompare = a.edition.localeCompare(b.edition); if (editionCompare !== 0) return editionCompare; return a.name.localeCompare(b.name); }); } </script> </body> </html>
Added List / Remove List 에서 보여지는 UI는 복구 버튼이고 해당 버튼을 누르면 해당 리스트에서 원본 CardList로 카드를 옮김.
Add, Remove, 복구 버튼 모두 1장씩만 카드를 옮김
카드 리스트가 Apply를 통해 있는 상태에서 Copy to Clipboard 누르면 카드이름만 클립보드에 저장됨.