/** * 界面元素引用 (UI References) */ const tabs = document.querySelectorAll(".tab-btn"); const tabContents = document.querySelectorAll(".tab-content"); const binInput = document.getElementById("binInput"); const addBinBtn = document.getElementById("addBinBtn"); const generateCardsBtn = document.getElementById("generateCardsBtn"); const statusMessage = document.getElementById("statusMessage"); const binHistoryList = document.getElementById("binHistoryList"); // 地址管理相关元素 const nameInput = document.getElementById("nameInput"); const address1Input = document.getElementById("address1Input"); const address2Input = document.getElementById("address2Input"); const cityInput = document.getElementById("cityInput"); const stateInput = document.getElementById("stateInput"); const zipInput = document.getElementById("zipInput"); const addAddressBtn = document.getElementById("addAddressBtn"); const addressesList = document.getElementById("addressesList"); // 姓名管理相关元素 const firstNameInput = document.getElementById("firstNameInput"); const lastNameInput = document.getElementById("lastNameInput"); const addNameBtn = document.getElementById("addNameBtn"); const namesList = document.getElementById("namesList"); // 设置相关元素 const useLuhnValidation = document.getElementById("useLuhnValidation"); const DEFAULT_BIN = "552461xxxxxxxxxx"; /** * 格式化输入:BIN 输入框 * 自动转大写,移除非数字/X字符 */ binInput.addEventListener("input", (e) => { let val = e.target.value.toUpperCase(); val = val.replace(/[^0-9X]/g, ""); if (val.length > 19) { val = val.substring(0, 19); } e.target.value = val; }); /** * 格式化输入:粘贴处理 * 自动补全 X,确保格式符合 16 位卡号长度 */ binInput.addEventListener("blur", (e) => { let val = e.target.value.trim().replace(/[^0-9X]/g, ""); // 如果长度不足且非空,自动补 X if (val.length > 0 && val.length < 16) { const currentLen = val.replace(/X/g, "").length; const needed = 16 - currentLen; val = val + "X".repeat(needed); } e.target.value = val; }); // ========================================== // 设置菜单逻辑 // ========================================== const settingsToggleBtn = document.getElementById("settingsToggleBtn"); const miniSettings = document.getElementById("miniSettings"); if (settingsToggleBtn && miniSettings) { settingsToggleBtn.addEventListener("click", (e) => { e.stopPropagation(); miniSettings.classList.toggle("show"); settingsToggleBtn.classList.toggle("active"); }); // 点击外部关闭菜单 document.addEventListener("click", (e) => { if (!miniSettings.contains(e.target) && e.target !== settingsToggleBtn && !settingsToggleBtn.contains(e.target)) { miniSettings.classList.remove("show"); settingsToggleBtn.classList.remove("active"); } }); } // Luhn 校验开关 if (useLuhnValidation) { useLuhnValidation.addEventListener("change", () => { chrome.storage.local.set({ useLuhnValidation: useLuhnValidation.checked }); }); // 初始化状态 chrome.storage.local.get(["useLuhnValidation"], (data) => { if (data.useLuhnValidation !== undefined) { useLuhnValidation.checked = data.useLuhnValidation; } }); } // 数据源选择器逻辑 const addressSourceSelect = document.getElementById("addressSourceSelect"); const nameSourceSelect = document.getElementById("nameSourceSelect"); if (addressSourceSelect) { addressSourceSelect.addEventListener("change", () => { const val = addressSourceSelect.value; chrome.storage.local.set({ addressSource: val }); console.log("✅ Address source changed to:", val); }); chrome.storage.local.get(["addressSource"], (data) => { if (data.addressSource) { addressSourceSelect.value = data.addressSource; } else { // 默认设置为 static addressSourceSelect.value = "static"; chrome.storage.local.set({ addressSource: "static" }); } }); } if (nameSourceSelect) { nameSourceSelect.addEventListener("change", () => { const val = nameSourceSelect.value; chrome.storage.local.set({ nameSource: val }); console.log("✅ Name source changed to:", val); }); chrome.storage.local.get(["nameSource"], (data) => { if (data.nameSource) { nameSourceSelect.value = data.nameSource; } else { nameSourceSelect.value = "static"; chrome.storage.local.set({ nameSource: "static" }); } }); } // 初始化加载所有数据 loadData(); // ========================================== // 标签页切换逻辑 (Tabs) // ========================================== tabs.forEach(tab => { tab.addEventListener("click", () => { const targetId = tab.dataset.tab; tabs.forEach(t => t.classList.remove("active")); tabContents.forEach(c => c.classList.remove("active")); tab.classList.add("active"); document.getElementById(targetId + "-tab").classList.add("active"); }); }); const subTabs = document.querySelectorAll(".sub-tab-btn"); const subTabContents = document.querySelectorAll(".sub-tab-content"); subTabs.forEach(tab => { tab.addEventListener("click", () => { const targetId = tab.dataset.subtab; subTabs.forEach(t => t.classList.remove("active")); subTabContents.forEach(c => c.classList.remove("active")); tab.classList.add("active"); document.getElementById(targetId + "-subtab").classList.add("active"); }); }); // ========================================== // 核心功能:添加 BIN 和 生成卡片 // ========================================== addBinBtn.addEventListener("click", () => { const bin = binInput.value.trim(); if (!bin) return; chrome.storage.local.get(["binHistory"], (data) => { let history = data.binHistory || []; // 去重并添加到头部 history = history.filter(b => b !== bin); history.unshift(bin); // 限制历史记录数量 if (history.length > 20) history = history.slice(0, 20); chrome.storage.local.set({ binHistory: history, currentBin: bin }, () => { loadBinHistory(); showToast("BIN added to history"); }); }); }); generateCardsBtn.addEventListener("click", async () => { const bin = binInput.value.trim(); if (!bin) { showStatus("Please enter a BIN number", "error"); return; } if (bin.length < 6) { showStatus("BIN must be at least 6 digits", "error"); return; } const useLuhn = useLuhnValidation.checked; // UI 状态更新:处理中 generateCardsBtn.disabled = true; generateCardsBtn.innerHTML = "Processing..."; if (useLuhn) { showStatus("🔐 Generating cards with Luhn validation...", "loading"); } else { showStatus("⚡ Generating cards...", "loading"); } // 保存 BIN 历史 chrome.storage.local.get(["binHistory"], (data) => { let history = data.binHistory || []; history = history.filter(b => b !== bin); history.unshift(bin); if (history.length > 20) history = history.slice(0, 20); chrome.storage.local.set({ binHistory: history, currentBin: bin }, () => { loadBinHistory(); }); }); // 发送消息给后台生成卡片 chrome.runtime.sendMessage({ action: "generateCards", bin: bin, useValidation: useLuhn, stripeTabId: null }, (response) => { if (response && response.success) { const luhnMsg = useLuhn ? " (Luhn validated)" : ""; showStatus("✅ Generated " + response.cards.length + " cards" + luhnMsg + ". Filling form...", "loading"); // 查找 Stripe 标签页并注入填充指令 chrome.tabs.query({ url: ["https://checkout.stripe.com/*", "https://*.stripe.com/*"] }, (tabs) => { if (chrome.runtime.lastError) { resetGenerateButton(); showStatus("❌ Error: " + chrome.runtime.lastError.message, "error"); return; } if (tabs.length > 0) { // 优先选择当前激活的标签页 const targetTab = tabs.find(t => t.active) || tabs[0]; chrome.tabs.sendMessage(targetTab.id, { action: "fillForm" }, (fillResponse) => { resetGenerateButton(); if (chrome.runtime.lastError) { showStatus("❌ No Stripe checkout page found. Please open one first.", "error"); } else { showStatus("Form filled!", "success"); showToast("✅ Form filled successfully!"); } }); } else { resetGenerateButton(); showStatus("❌ No Stripe checkout page found. Please open one first.", "error"); } }); } else { resetGenerateButton(); showStatus("Failed to generate cards", "error"); showToast("❌ Failed to generate cards. Try again.", "error"); } }); }); function resetGenerateButton() { generateCardsBtn.disabled = false; generateCardsBtn.innerHTML = "🚀Fill Everything"; } // ========================================== // 历史记录管理 // ========================================== function loadBinHistory() { chrome.storage.local.get(["binHistory", "currentBin"], (data) => { const history = data.binHistory || []; const current = data.currentBin || DEFAULT_BIN; binInput.value = current; binHistoryList.innerHTML = ""; if (history.length === 0) { binHistoryList.innerHTML = "
No BINs saved yet
"; return; } history.forEach(bin => { const item = document.createElement("div"); item.className = "history-item"; const binText = document.createElement("span"); binText.textContent = bin; binText.className = "history-bin"; binText.addEventListener("click", () => { binInput.value = bin; chrome.storage.local.set({ currentBin: bin }); showToast("BIN selected"); }); const delBtn = document.createElement("button"); delBtn.textContent = "×"; delBtn.className = "delete-btn"; delBtn.addEventListener("click", (e) => { e.stopPropagation(); deleteBin(bin); }); item.appendChild(binText); item.appendChild(delBtn); binHistoryList.appendChild(item); }); }); } function deleteBin(bin) { chrome.storage.local.get(["binHistory"], (data) => { let history = data.binHistory || []; history = history.filter(b => b !== bin); chrome.storage.local.set({ binHistory: history }, () => { loadBinHistory(); showToast("BIN deleted"); }); }); } // ========================================== // 地址管理 // ========================================== addAddressBtn.addEventListener("click", () => { const name = nameInput.value.trim(); const addr1 = address1Input.value.trim(); const addr2 = address2Input.value.trim(); const city = cityInput.value.trim(); const state = stateInput.value.trim(); const zip = zipInput.value.trim(); if (!name || !addr1 || !city || !state || !zip) { showToast("Please fill all required fields", "error"); return; } // 简单的姓名拆分逻辑 const nameParts = name.split(" "); const first = nameParts[0] || name; const last = nameParts.slice(1).join(" ") || nameParts[0]; const newAddr = { id: Date.now(), name: name, firstName: first, lastName: last, address1: addr1, address2: addr2, city: city, state: state, stateCode: getStateCode(state), // 转换州简写 (如 California -> CA) postal: zip, countryText: "United States", countryValue: "US" }; chrome.storage.local.get(["customAddresses"], (data) => { const list = data.customAddresses || []; list.push(newAddr); chrome.storage.local.set({ customAddresses: list }, () => { clearAddressInputs(); loadAddresses(); showToast("Address added"); }); }); }); function clearAddressInputs() { nameInput.value = ""; address1Input.value = ""; address2Input.value = ""; cityInput.value = ""; stateInput.value = ""; zipInput.value = ""; } function loadAddresses() { chrome.storage.local.get(["customAddresses"], (data) => { const list = data.customAddresses || []; addressesList.innerHTML = ""; if (list.length === 0) { addressesList.innerHTML = "
No addresses saved yet
"; return; } list.forEach(addr => { const item = document.createElement("div"); item.className = "list-item"; const info = document.createElement("div"); info.className = "item-info"; info.innerHTML = ` ${addr.name}
${addr.address1}${addr.address2 ? ", " + addr.address2 : ""}
${addr.city}, ${addr.state} ${addr.postal}
`; const delBtn = document.createElement("button"); delBtn.textContent = "×"; delBtn.className = "delete-btn"; delBtn.addEventListener("click", () => deleteAddress(addr.id)); item.appendChild(info); item.appendChild(delBtn); addressesList.appendChild(item); }); }); } function deleteAddress(id) { chrome.storage.local.get(["customAddresses"], (data) => { let list = data.customAddresses || []; list = list.filter(item => item.id !== id); chrome.storage.local.set({ customAddresses: list }, () => { loadAddresses(); showToast("Address deleted"); }); }); } // ========================================== // 姓名管理 // ========================================== addNameBtn.addEventListener("click", () => { const first = firstNameInput.value.trim(); const last = lastNameInput.value.trim(); if (!first || !last) { showToast("Please enter both first and last name", "error"); return; } const newName = { id: Date.now(), firstName: first, lastName: last, fullName: first + " " + last }; chrome.storage.local.get(["customNames"], (data) => { const list = data.customNames || []; list.push(newName); chrome.storage.local.set({ customNames: list }, () => { firstNameInput.value = ""; lastNameInput.value = ""; loadNames(); showToast("Name added"); }); }); }); function loadNames() { chrome.storage.local.get(["customNames"], (data) => { const list = data.customNames || []; namesList.innerHTML = ""; if (list.length === 0) { namesList.innerHTML = "
No names saved yet
"; return; } list.forEach(item => { const div = document.createElement("div"); div.className = "list-item"; const info = document.createElement("div"); info.className = "item-info"; info.innerHTML = `${item.fullName}`; const delBtn = document.createElement("button"); delBtn.textContent = "×"; delBtn.className = "delete-btn"; delBtn.addEventListener("click", () => deleteName(item.id)); div.appendChild(info); div.appendChild(delBtn); namesList.appendChild(div); }); }); } function deleteName(id) { chrome.storage.local.get(["customNames"], (data) => { let list = data.customNames || []; list = list.filter(item => item.id !== id); chrome.storage.local.set({ customNames: list }, () => { loadNames(); showToast("Name deleted"); }); }); } // ========================================== // 辅助工具函数 // ========================================== /** * 将州全称转换为两字母缩写 (用于表单匹配) */ function getStateCode(stateName) { const map = { Alabama: "AL", Alaska: "AK", Arizona: "AZ", Arkansas: "AR", California: "CA", Colorado: "CO", Connecticut: "CT", Delaware: "DE", Florida: "FL", Georgia: "GA", Hawaii: "HI", Idaho: "ID", Illinois: "IL", Indiana: "IN", Iowa: "IA", Kansas: "KS", Kentucky: "KY", Louisiana: "LA", Maine: "ME", Maryland: "MD", Massachusetts: "MA", Michigan: "MI", Minnesota: "MN", Mississippi: "MS", Missouri: "MO", Montana: "MT", Nebraska: "NE", Nevada: "NV", "New Hampshire": "NH", "New Jersey": "NJ", "New Mexico": "NM", "New York": "NY", "North Carolina": "NC", "North Dakota": "ND", Ohio: "OH", Oklahoma: "OK", Oregon: "OR", Pennsylvania: "PA", "Rhode Island": "RI", "South Carolina": "SC", "South Dakota": "SD", Tennessee: "TN", Texas: "TX", Utah: "UT", Vermont: "VT", Virginia: "VA", Washington: "WA", "West Virginia": "WV", Wisconsin: "WI", Wyoming: "WY" }; return map[stateName] || stateName.substring(0, 2).toUpperCase(); } function showStatus(msg, type = "") { statusMessage.textContent = msg; statusMessage.className = "status-message " + type; statusMessage.style.display = "block"; if (type === "success" || type === "error") { setTimeout(() => { statusMessage.style.display = "none"; }, 5000); } } function showToast(msg, type = "success") { const toast = document.createElement("div"); toast.className = "toast " + type; toast.textContent = msg; document.body.appendChild(toast); // 动画显示 setTimeout(() => { toast.classList.add("show"); }, 10); // 自动消失 setTimeout(() => { toast.classList.remove("show"); setTimeout(() => toast.remove(), 300); }, 2000); } function loadData() { loadBinHistory(); loadAddresses(); loadNames(); } // ========================================== // Telegram 广告弹窗逻辑 // ========================================== const telegramModal = document.getElementById("telegramModal"); const modalCloseBtn = document.getElementById("modalCloseBtn"); const modalTimer = document.getElementById("modalTimer"); let countdownInterval = null; let autoCloseTimeout = null; function showTelegramModal() { telegramModal.classList.add("show"); let seconds = 5; modalTimer.textContent = seconds; // 倒计时 countdownInterval = setInterval(() => { seconds--; if (seconds > 0) { modalTimer.textContent = seconds; } else { modalTimer.textContent = "0"; clearInterval(countdownInterval); } }, 1000); // 5秒后自动关闭 autoCloseTimeout = setTimeout(() => { closeTelegramModal(); }, 5000); } function closeTelegramModal() { telegramModal.classList.remove("show"); if (countdownInterval) { clearInterval(countdownInterval); countdownInterval = null; } if (autoCloseTimeout) { clearTimeout(autoCloseTimeout); autoCloseTimeout = null; } } if (modalCloseBtn) { modalCloseBtn.addEventListener("click", (e) => { e.preventDefault(); closeTelegramModal(); }); } // 延迟显示广告,避免一打开就弹 if (telegramModal && modalCloseBtn && modalTimer) { setTimeout(() => { showTelegramModal(); }, 300); }