完成扩展
This commit is contained in:
285
extension/ui/options/options.css
Normal file
285
extension/ui/options/options.css
Normal file
@@ -0,0 +1,285 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
background: #1a1a1a;
|
||||
color: #e0e0e0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 32px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 2px solid #333;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
header p {
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 24px;
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
padding: 12px 24px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
color: #888;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.tab-btn:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
color: #4CAF50;
|
||||
border-bottom-color: #4CAF50;
|
||||
}
|
||||
|
||||
/* Tab Content */
|
||||
.tab-content {
|
||||
display: none;
|
||||
animation: fadeIn 0.3s;
|
||||
}
|
||||
|
||||
.tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.tab-content h2 {
|
||||
font-size: 22px;
|
||||
margin-bottom: 8px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.section-desc {
|
||||
color: #888;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
/* Form Elements */
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.form-group input[type="text"],
|
||||
.form-group input[type="number"],
|
||||
.form-group select {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
background: #252525;
|
||||
border: 1px solid #333;
|
||||
border-radius: 6px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.form-group input:focus,
|
||||
.form-group select:focus {
|
||||
outline: none;
|
||||
border-color: #4CAF50;
|
||||
}
|
||||
|
||||
.form-group input[type="checkbox"] {
|
||||
margin-right: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Module Config */
|
||||
.module-config {
|
||||
background: #252525;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.module-config h3 {
|
||||
font-size: 16px;
|
||||
color: #4CAF50;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn-primary,
|
||||
.btn-secondary,
|
||||
.btn-danger {
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #45a049;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #333;
|
||||
color: white;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #3a3a3a;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #f44336;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #da190b;
|
||||
}
|
||||
|
||||
/* Stats Grid */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: #252525;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: #4CAF50;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 13px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
/* Data Management */
|
||||
.data-actions {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.data-preview {
|
||||
background: #252525;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.data-preview h3 {
|
||||
font-size: 16px;
|
||||
margin-bottom: 12px;
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
.data-preview pre {
|
||||
background: #1a1a1a;
|
||||
border: 1px solid #333;
|
||||
border-radius: 4px;
|
||||
padding: 16px;
|
||||
overflow-x: auto;
|
||||
font-size: 12px;
|
||||
color: #ccc;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* About Section */
|
||||
.about-content {
|
||||
max-width: 700px;
|
||||
}
|
||||
|
||||
.about-content h3 {
|
||||
font-size: 18px;
|
||||
color: #4CAF50;
|
||||
margin-top: 24px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.about-content p {
|
||||
margin-bottom: 12px;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.about-content ul {
|
||||
margin-left: 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.about-content li {
|
||||
margin-bottom: 8px;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.warning-text {
|
||||
color: #ff9800;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
text-align: center;
|
||||
margin-top: 48px;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid #333;
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
}
|
||||
192
extension/ui/options/options.html
Normal file
192
extension/ui/options/options.html
Normal file
@@ -0,0 +1,192 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Payment Automation Suite - Settings</title>
|
||||
<link rel="stylesheet" href="options.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>🔧 Payment Automation Suite - Settings</h1>
|
||||
<p>Advanced configuration for authorized testing</p>
|
||||
</header>
|
||||
|
||||
<nav class="tabs">
|
||||
<button class="tab-btn active" data-tab="api">API Keys</button>
|
||||
<button class="tab-btn" data-tab="modules">Module Config</button>
|
||||
<button class="tab-btn" data-tab="data">Data Management</button>
|
||||
<button class="tab-btn" data-tab="about">About</button>
|
||||
</nav>
|
||||
|
||||
<!-- Tab 1: API Keys -->
|
||||
<section class="tab-content active" id="tab-api">
|
||||
<h2>Captcha Solver API Keys</h2>
|
||||
<p class="section-desc">Configure API keys for automated captcha solving services</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="api-capsolver">CapSolver API Key</label>
|
||||
<input type="text" id="api-capsolver" placeholder="Enter your CapSolver API key">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="api-2captcha">2Captcha API Key</label>
|
||||
<input type="text" id="api-2captcha" placeholder="Enter your 2Captcha API key">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="api-nopecha">NopeCHA API Key</label>
|
||||
<input type="text" id="api-nopecha" placeholder="Enter your NopeCHA API key">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="api-nocaptchaai">NoCaptchaAI API Key</label>
|
||||
<input type="text" id="api-nocaptchaai" placeholder="Enter your NoCaptchaAI API key">
|
||||
</div>
|
||||
|
||||
<button class="btn-primary" id="save-api-keys">Save API Keys</button>
|
||||
</section>
|
||||
|
||||
<!-- Tab 2: Module Configuration -->
|
||||
<section class="tab-content" id="tab-modules">
|
||||
<h2>Module Configuration</h2>
|
||||
<p class="section-desc">Fine-tune settings for each module</p>
|
||||
|
||||
<!-- Captcha Solver Config -->
|
||||
<div class="module-config">
|
||||
<h3>Captcha Solver</h3>
|
||||
<div class="form-group">
|
||||
<label for="cs-debug">
|
||||
<input type="checkbox" id="cs-debug">
|
||||
Debug Mode
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="cs-service">API Service</label>
|
||||
<select id="cs-service">
|
||||
<option value="capsolver">CapSolver</option>
|
||||
<option value="2captcha">2Captcha</option>
|
||||
<option value="nopecha">NopeCHA</option>
|
||||
<option value="nocaptchaai">NoCaptchaAI</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="cs-delay">Solve Delay (ms)</label>
|
||||
<input type="number" id="cs-delay" min="0" max="5000" step="100">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GOG Payment Config -->
|
||||
<div class="module-config">
|
||||
<h3>GOG Payment Handler</h3>
|
||||
<div class="form-group">
|
||||
<label for="gog-bins">BIN List (comma-separated)</label>
|
||||
<input type="text" id="gog-bins" placeholder="424242,411111,378282">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="gog-rotate">
|
||||
<input type="checkbox" id="gog-rotate">
|
||||
Auto-rotate BIN
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AutoFill Config -->
|
||||
<div class="module-config">
|
||||
<h3>Auto Fill</h3>
|
||||
<div class="form-group">
|
||||
<label for="af-country">Default Country</label>
|
||||
<select id="af-country">
|
||||
<option value="US">United States</option>
|
||||
<option value="GB">United Kingdom</option>
|
||||
<option value="CN">China</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="af-delay">Fill Delay (ms)</label>
|
||||
<input type="number" id="af-delay" min="0" max="2000" step="50">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn-primary" id="save-module-config">Save Configuration</button>
|
||||
</section>
|
||||
|
||||
<!-- Tab 3: Data Management -->
|
||||
<section class="tab-content" id="tab-data">
|
||||
<h2>Data Management</h2>
|
||||
<p class="section-desc">View and manage captured data</p>
|
||||
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="total-captchas">0</div>
|
||||
<div class="stat-label">Captchas Solved</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="total-forms">0</div>
|
||||
<div class="stat-label">Forms Filled</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="total-cards">0</div>
|
||||
<div class="stat-label">Cards Generated</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="total-payments">0</div>
|
||||
<div class="stat-label">Payments Captured</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="data-actions">
|
||||
<button class="btn-secondary" id="export-json">Export as JSON</button>
|
||||
<button class="btn-secondary" id="export-csv">Export as CSV</button>
|
||||
<button class="btn-danger" id="clear-data">Clear All Data</button>
|
||||
</div>
|
||||
|
||||
<div class="data-preview">
|
||||
<h3>Captured Data Preview</h3>
|
||||
<pre id="data-preview-content">No data captured yet</pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Tab 4: About -->
|
||||
<section class="tab-content" id="tab-about">
|
||||
<h2>About</h2>
|
||||
<div class="about-content">
|
||||
<h3>⚠️ Legal Disclaimer</h3>
|
||||
<p>This extension is designed for <strong>authorized security testing and educational purposes only</strong>.</p>
|
||||
|
||||
<p>Unauthorized use of this tool against payment systems, captcha services, or any other protected systems without explicit written permission is:</p>
|
||||
<ul>
|
||||
<li>A violation of the Computer Fraud and Abuse Act (CFAA) in the United States</li>
|
||||
<li>A violation of similar cybercrime laws in other jurisdictions</li>
|
||||
<li>A breach of Terms of Service for payment gateways and captcha providers</li>
|
||||
<li>A violation of PCI DSS compliance requirements</li>
|
||||
</ul>
|
||||
|
||||
<p><strong>You must have explicit authorization</strong> from the system owner before using this extension.</p>
|
||||
|
||||
<h3>📋 Version Information</h3>
|
||||
<p>Version: 1.0.0</p>
|
||||
<p>Build: Manifest V3 (Chrome/Edge)</p>
|
||||
|
||||
<h3>🛡️ Responsible Use</h3>
|
||||
<p>This tool should only be used in:</p>
|
||||
<ul>
|
||||
<li>Authorized penetration testing engagements</li>
|
||||
<li>Bug bounty programs with explicit permission</li>
|
||||
<li>Educational security research</li>
|
||||
<li>Development and testing environments you own</li>
|
||||
</ul>
|
||||
|
||||
<p class="warning-text">The developers of this extension assume no liability for misuse.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<p>© 2025 Payment Automation Suite - For Authorized Testing Only</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="options.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
298
extension/ui/options/options.js
Normal file
298
extension/ui/options/options.js
Normal file
@@ -0,0 +1,298 @@
|
||||
/**
|
||||
* Options Page Logic
|
||||
*/
|
||||
|
||||
// Tab switching
|
||||
const tabBtns = document.querySelectorAll('.tab-btn');
|
||||
const tabContents = document.querySelectorAll('.tab-content');
|
||||
|
||||
tabBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const targetTab = btn.dataset.tab;
|
||||
|
||||
// Remove active class from all
|
||||
tabBtns.forEach(b => b.classList.remove('active'));
|
||||
tabContents.forEach(c => c.classList.remove('active'));
|
||||
|
||||
// Add active class to clicked
|
||||
btn.classList.add('active');
|
||||
document.getElementById(`tab-${targetTab}`).classList.add('active');
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize on load
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
await loadAllSettings();
|
||||
await loadStats();
|
||||
await loadDataPreview();
|
||||
|
||||
// Set up event listeners
|
||||
document.getElementById('save-api-keys').addEventListener('click', saveAPIKeys);
|
||||
document.getElementById('save-module-config').addEventListener('click', saveModuleConfig);
|
||||
document.getElementById('export-json').addEventListener('click', exportJSON);
|
||||
document.getElementById('export-csv').addEventListener('click', exportCSV);
|
||||
document.getElementById('clear-data').addEventListener('click', clearData);
|
||||
});
|
||||
|
||||
/**
|
||||
* Load all settings from storage
|
||||
*/
|
||||
async function loadAllSettings() {
|
||||
try {
|
||||
const config = await chrome.storage.sync.get(['apiKeys', 'modules']);
|
||||
|
||||
// Load API keys
|
||||
if (config.apiKeys) {
|
||||
document.getElementById('api-capsolver').value = config.apiKeys.capsolver || '';
|
||||
document.getElementById('api-2captcha').value = config.apiKeys.twocaptcha || '';
|
||||
document.getElementById('api-nopecha').value = config.apiKeys.nopecha || '';
|
||||
document.getElementById('api-nocaptchaai').value = config.apiKeys.nocaptchaai || '';
|
||||
}
|
||||
|
||||
// Load module configs
|
||||
if (config.modules) {
|
||||
// Captcha Solver
|
||||
const cs = config.modules.captchaSolver?.config;
|
||||
if (cs) {
|
||||
document.getElementById('cs-debug').checked = cs.debug || false;
|
||||
document.getElementById('cs-service').value = cs.apiService || 'capsolver';
|
||||
document.getElementById('cs-delay').value = cs.solveDelay || 800;
|
||||
}
|
||||
|
||||
// GOG Payment
|
||||
const gog = config.modules.gogPayment?.config;
|
||||
if (gog) {
|
||||
document.getElementById('gog-bins').value = gog.bins?.join(',') || '';
|
||||
document.getElementById('gog-rotate').checked = gog.autoRotateBIN || false;
|
||||
}
|
||||
|
||||
// AutoFill
|
||||
const af = config.modules.autoFill?.config;
|
||||
if (af) {
|
||||
document.getElementById('af-country').value = af.defaultCountry || 'US';
|
||||
document.getElementById('af-delay').value = af.fillDelay || 300;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Options] Failed to load settings:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save API Keys
|
||||
*/
|
||||
async function saveAPIKeys() {
|
||||
const apiKeys = {
|
||||
capsolver: document.getElementById('api-capsolver').value.trim(),
|
||||
twocaptcha: document.getElementById('api-2captcha').value.trim(),
|
||||
nopecha: document.getElementById('api-nopecha').value.trim(),
|
||||
nocaptchaai: document.getElementById('api-nocaptchaai').value.trim()
|
||||
};
|
||||
|
||||
try {
|
||||
await chrome.storage.sync.set({ apiKeys });
|
||||
showNotification('API keys saved successfully', 'success');
|
||||
} catch (error) {
|
||||
console.error('[Options] Failed to save API keys:', error);
|
||||
showNotification('Failed to save API keys', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save Module Configuration
|
||||
*/
|
||||
async function saveModuleConfig() {
|
||||
try {
|
||||
const config = await chrome.storage.sync.get(['modules']);
|
||||
|
||||
// Update Captcha Solver config
|
||||
config.modules.captchaSolver.config.debug = document.getElementById('cs-debug').checked;
|
||||
config.modules.captchaSolver.config.apiService = document.getElementById('cs-service').value;
|
||||
config.modules.captchaSolver.config.solveDelay = parseInt(document.getElementById('cs-delay').value);
|
||||
|
||||
// Update GOG Payment config
|
||||
const bins = document.getElementById('gog-bins').value.split(',').map(b => b.trim()).filter(b => b);
|
||||
config.modules.gogPayment.config.bins = bins;
|
||||
config.modules.gogPayment.config.autoRotateBIN = document.getElementById('gog-rotate').checked;
|
||||
|
||||
// Update AutoFill config
|
||||
config.modules.autoFill.config.defaultCountry = document.getElementById('af-country').value;
|
||||
config.modules.autoFill.config.fillDelay = parseInt(document.getElementById('af-delay').value);
|
||||
|
||||
await chrome.storage.sync.set(config);
|
||||
showNotification('Module configuration saved successfully', 'success');
|
||||
} catch (error) {
|
||||
console.error('[Options] Failed to save module config:', error);
|
||||
showNotification('Failed to save configuration', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load statistics
|
||||
*/
|
||||
async function loadStats() {
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({ type: 'GET_STATS' });
|
||||
|
||||
if (response && response.success) {
|
||||
const stats = response.stats;
|
||||
document.getElementById('total-captchas').textContent = stats.captchasSolved || 0;
|
||||
document.getElementById('total-forms').textContent = stats.formsFilled || 0;
|
||||
document.getElementById('total-cards').textContent = stats.cardsGenerated || 0;
|
||||
document.getElementById('total-payments').textContent = stats.paymentsCaptured || 0;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Options] Failed to load stats:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load data preview
|
||||
*/
|
||||
async function loadDataPreview() {
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({ type: 'EXPORT_DATA' });
|
||||
|
||||
if (response && response.success) {
|
||||
const data = response.data;
|
||||
const preview = document.getElementById('data-preview-content');
|
||||
|
||||
if (data && data.length > 0) {
|
||||
preview.textContent = JSON.stringify(data, null, 2);
|
||||
} else {
|
||||
preview.textContent = 'No data captured yet';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Options] Failed to load data preview:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export data as JSON
|
||||
*/
|
||||
async function exportJSON() {
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({ type: 'EXPORT_DATA' });
|
||||
|
||||
if (response && response.success) {
|
||||
const data = response.data;
|
||||
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
||||
downloadBlob(blob, `payment-automation-data-${Date.now()}.json`);
|
||||
showNotification('Data exported as JSON', 'success');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Options] Failed to export JSON:', error);
|
||||
showNotification('Export failed', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export data as CSV
|
||||
*/
|
||||
async function exportCSV() {
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({ type: 'EXPORT_DATA' });
|
||||
|
||||
if (response && response.success) {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
showNotification('No data to export', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert to CSV
|
||||
const headers = Object.keys(data[0]);
|
||||
const csv = [
|
||||
headers.join(','),
|
||||
...data.map(row => headers.map(h => JSON.stringify(row[h] || '')).join(','))
|
||||
].join('\n');
|
||||
|
||||
const blob = new Blob([csv], { type: 'text/csv' });
|
||||
downloadBlob(blob, `payment-automation-data-${Date.now()}.csv`);
|
||||
showNotification('Data exported as CSV', 'success');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Options] Failed to export CSV:', error);
|
||||
showNotification('Export failed', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all captured data
|
||||
*/
|
||||
async function clearData() {
|
||||
if (!confirm('Are you sure you want to clear all captured data? This cannot be undone.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await chrome.runtime.sendMessage({ type: 'CLEAR_DATA' });
|
||||
await loadDataPreview();
|
||||
await loadStats();
|
||||
showNotification('All data cleared', 'success');
|
||||
} catch (error) {
|
||||
console.error('[Options] Failed to clear data:', error);
|
||||
showNotification('Failed to clear data', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download blob as file
|
||||
*/
|
||||
function downloadBlob(blob, filename) {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show notification
|
||||
*/
|
||||
function showNotification(message, type) {
|
||||
// Create notification element
|
||||
const notification = document.createElement('div');
|
||||
notification.textContent = message;
|
||||
notification.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
padding: 16px 24px;
|
||||
background: ${type === 'success' ? '#4CAF50' : '#f44336'};
|
||||
color: white;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||
z-index: 10000;
|
||||
animation: slideIn 0.3s ease-out;
|
||||
`;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// Remove after 3 seconds
|
||||
setTimeout(() => {
|
||||
notification.style.animation = 'slideOut 0.3s ease-out';
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(notification);
|
||||
}, 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Add CSS animations
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes slideIn {
|
||||
from { transform: translateX(400px); opacity: 0; }
|
||||
to { transform: translateX(0); opacity: 1; }
|
||||
}
|
||||
@keyframes slideOut {
|
||||
from { transform: translateX(0); opacity: 1; }
|
||||
to { transform: translateX(400px); opacity: 0; }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
Reference in New Issue
Block a user