/** * 界面元素引用 (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 = "