email-validation / index.html
sqibhe's picture
Add 2 files
2bae974 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Advanced Email Validation Bot</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.dropzone {
border: 2px dashed #4b5563;
border-radius: 0.5rem;
transition: all 0.3s ease;
}
.dropzone.active {
border-color: #3b82f6;
background-color: rgba(59, 130, 246, 0.05);
}
.progress-bar {
transition: width 0.3s ease;
}
.tooltip {
position: relative;
display: inline-block;
}
.tooltip .tooltip-text {
visibility: hidden;
width: 200px;
background-color: #1f2937;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -100px;
opacity: 0;
transition: opacity 0.3s;
}
.tooltip:hover .tooltip-text {
visibility: visible;
opacity: 1;
}
.status-badge {
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}
.status-valid {
background-color: #d1fae5;
color: #065f46;
}
.status-invalid {
background-color: #fee2e2;
color: #b91c1c;
}
.status-duplicate {
background-color: #fef3c7;
color: #92400e;
}
.status-typo {
background-color: #dbeafe;
color: #1e40af;
}
.status-unknown {
background-color: #e5e7eb;
color: #4b5563;
}
.status-undeliverable {
background-color: #f5f5f4;
color: #57534e;
}
.status-mx {
background-color: #ede9fe;
color: #5b21b6;
}
.fade-in {
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.insight-card {
transition: all 0.3s ease;
}
.insight-card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8 max-w-6xl">
<div class="text-center mb-8">
<h1 class="text-3xl font-bold text-gray-800 mb-2">Advanced Email Validation Bot</h1>
<p class="text-gray-600">Upload your Excel file to validate and clean email addresses</p>
</div>
<div class="bg-white rounded-lg shadow-md p-6 mb-8">
<div id="upload-section">
<div class="dropzone p-8 text-center cursor-pointer" id="dropzone">
<div class="flex flex-col items-center justify-center">
<i class="fas fa-file-excel text-4xl text-blue-500 mb-4"></i>
<h3 class="text-lg font-medium text-gray-700 mb-2">Drag & Drop your Excel file here</h3>
<p class="text-gray-500 mb-4">or</p>
<label for="file-upload" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-md cursor-pointer transition duration-300">
<i class="fas fa-upload mr-2"></i> Browse Files
</label>
<input id="file-upload" type="file" accept=".xlsx, .xls, .csv" class="hidden">
</div>
</div>
<div class="mt-4 text-sm text-gray-500">
<p>Supported formats: .xlsx, .xls, .csv</p>
<p>File should contain at least one column with email addresses</p>
</div>
</div>
<div id="processing-section" class="hidden">
<div class="flex items-center mb-4">
<div class="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center mr-3">
<i class="fas fa-cog text-blue-500 animate-spin"></i>
</div>
<h3 class="text-lg font-medium text-gray-700">Processing your file...</h3>
</div>
<div class="mb-4">
<div class="flex justify-between text-sm text-gray-600 mb-1">
<span>Progress</span>
<span id="progress-percent">0%</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div id="progress-bar" class="progress-bar bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
<div class="bg-gray-50 p-4 rounded-lg">
<div class="text-sm text-gray-500 mb-1">Emails Processed</div>
<div class="text-2xl font-bold text-gray-800" id="processed-count">0</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="text-sm text-gray-500 mb-1">Valid Emails</div>
<div class="text-2xl font-bold text-green-600" id="valid-count">0</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="text-sm text-gray-500 mb-1">Issues Found</div>
<div class="text-2xl font-bold text-red-600" id="issues-count">0</div>
</div>
</div>
</div>
<div id="results-section" class="hidden">
<div class="flex items-center mb-4">
<div class="w-8 h-8 rounded-full bg-green-100 flex items-center justify-center mr-3">
<i class="fas fa-check text-green-500"></i>
</div>
<h3 class="text-lg font-medium text-gray-700">Validation Complete</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm">
<div class="text-sm text-gray-500 mb-1">Total Emails</div>
<div class="text-2xl font-bold text-gray-800" id="total-emails">0</div>
</div>
<div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm">
<div class="text-sm text-gray-500 mb-1">Valid Emails</div>
<div class="text-2xl font-bold text-green-600" id="final-valid">0</div>
</div>
<div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm">
<div class="text-sm text-gray-500 mb-1">Invalid Emails</div>
<div class="text-2xl font-bold text-red-600" id="final-invalid">0</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6">
<div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm">
<div class="flex items-center justify-between mb-2">
<div class="text-sm text-gray-500">Duplicates</div>
<div class="text-lg font-bold text-amber-600" id="duplicate-count">0</div>
</div>
<div class="text-xs text-gray-400">Emails appearing multiple times</div>
</div>
<div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm">
<div class="flex items-center justify-between mb-2">
<div class="text-sm text-gray-500">Typos Found</div>
<div class="text-lg font-bold text-blue-600" id="typo-count">0</div>
</div>
<div class="text-xs text-gray-400">Potential email address typos</div>
</div>
<div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm">
<div class="flex items-center justify-between mb-2">
<div class="text-sm text-gray-500">Syntax Errors</div>
<div class="text-lg font-bold text-red-600" id="syntax-count">0</div>
</div>
<div class="text-xs text-gray-400">Invalid email formats</div>
</div>
<div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm">
<div class="flex items-center justify-between mb-2">
<div class="text-sm text-gray-500">MX Issues</div>
<div class="text-lg font-bold text-purple-600" id="mx-count">0</div>
</div>
<div class="text-xs text-gray-400">Domains without mail servers</div>
</div>
<div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm">
<div class="flex items-center justify-between mb-2">
<div class="text-sm text-gray-500">Undeliverable</div>
<div class="text-lg font-bold text-gray-600" id="undeliverable-count">0</div>
</div>
<div class="text-xs text-gray-400">Emails that may bounce</div>
</div>
<div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm">
<div class="flex items-center justify-between mb-2">
<div class="text-sm text-gray-500">Unknown Status</div>
<div class="text-lg font-bold text-gray-500" id="unknown-count">0</div>
</div>
<div class="text-xs text-gray-400">Could not verify deliverability</div>
</div>
</div>
<!-- MX Response Insights Section -->
<div class="mb-6">
<h4 class="font-medium text-gray-700 mb-4 flex items-center">
<i class="fas fa-server mr-2 text-blue-500"></i> MX Record Analysis
</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-white border border-blue-100 rounded-lg p-4 insight-card">
<div class="flex items-center mb-2">
<div class="w-10 h-10 bg-blue-50 rounded-full flex items-center justify-center mr-3">
<i class="fas fa-check-circle text-blue-500"></i>
</div>
<h5 class="font-medium text-gray-800">Valid MX Records</h5>
</div>
<div class="text-3xl font-bold text-blue-600 mb-2" id="valid-mx-count">0</div>
<div class="text-sm text-gray-600">
<span id="valid-mx-percent">0</span>% of emails have domains with properly configured mail servers
</div>
</div>
<div class="bg-white border border-purple-100 rounded-lg p-4 insight-card">
<div class="flex items-center mb-2">
<div class="w-10 h-10 bg-purple-50 rounded-full flex items-center justify-center mr-3">
<i class="fas fa-exclamation-triangle text-purple-500"></i>
</div>
<h5 class="font-medium text-gray-800">MX Issues Detected</h5>
</div>
<div class="text-3xl font-bold text-purple-600 mb-2" id="invalid-mx-count">0</div>
<div class="text-sm text-gray-600">
These domains cannot receive emails due to missing or incorrect MX records
</div>
<div class="mt-2">
<div class="text-xs font-medium text-gray-500 mb-1">Top Problematic Domains:</div>
<div class="text-xs text-gray-600" id="top-mx-issues"></div>
</div>
</div>
</div>
</div>
<!-- Deliverability Insights Section -->
<div class="mb-6">
<h4 class="font-medium text-gray-700 mb-4 flex items-center">
<i class="fas fa-paper-plane mr-2 text-green-500"></i> Email Deliverability Insights
</h4>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="bg-white border border-green-100 rounded-lg p-4 insight-card">
<div class="flex items-center mb-2">
<div class="w-10 h-10 bg-green-50 rounded-full flex items-center justify-center mr-3">
<i class="fas fa-check-circle text-green-500"></i>
</div>
<h5 class="font-medium text-gray-800">High Deliverability</h5>
</div>
<div class="text-3xl font-bold text-green-600 mb-2" id="deliverable-count">0</div>
<div class="text-sm text-gray-600">
<span id="deliverable-percent">0</span>% of emails are confirmed to be deliverable
</div>
</div>
<div class="bg-white border border-red-100 rounded-lg p-4 insight-card">
<div class="flex items-center mb-2">
<div class="w-10 h-10 bg-red-50 rounded-full flex items-center justify-center mr-3">
<i class="fas fa-times-circle text-red-500"></i>
</div>
<h5 class="font-medium text-gray-800">Undeliverable</h5>
</div>
<div class="text-3xl font-bold text-red-600 mb-2" id="undeliverable-insight-count">0</div>
<div class="text-sm text-gray-600">
These emails will likely bounce or be rejected by receiving servers
</div>
</div>
<div class="bg-white border border-gray-200 rounded-lg p-4 insight-card">
<div class="flex items-center mb-2">
<div class="w-10 h-10 bg-gray-50 rounded-full flex items-center justify-center mr-3">
<i class="fas fa-question-circle text-gray-500"></i>
</div>
<h5 class="font-medium text-gray-800">Unknown Status</h5>
</div>
<div class="text-3xl font-bold text-gray-600 mb-2" id="unknown-deliverability-count">0</div>
<div class="text-sm text-gray-600">
Could not verify deliverability due to server restrictions or timeouts
</div>
</div>
</div>
<div class="mt-4 bg-white border border-yellow-100 rounded-lg p-4 insight-card">
<div class="flex items-center mb-2">
<div class="w-10 h-10 bg-yellow-50 rounded-full flex items-center justify-center mr-3">
<i class="fas fa-chart-line text-yellow-500"></i>
</div>
<h5 class="font-medium text-gray-800">Overall Deliverability Score</h5>
</div>
<div class="flex items-center">
<div class="w-full bg-gray-200 rounded-full h-4 mr-4">
<div id="deliverability-score-bar" class="h-4 rounded-full" style="width: 0%; background: linear-gradient(90deg, #10b981 0%, #f59e0b 50%, #ef4444 100%);"></div>
</div>
<div class="text-2xl font-bold" id="deliverability-score">0</div>
<span class="ml-1 text-gray-500">/100</span>
</div>
<div class="mt-2 text-sm text-gray-600">
<span id="deliverability-rating" class="font-medium">Poor</span> -
<span id="deliverability-description">This list has significant deliverability issues that need attention</span>
</div>
</div>
</div>
<div class="mb-6">
<div class="flex justify-between items-center mb-2">
<h4 class="font-medium text-gray-700">Email Details</h4>
<div class="text-sm text-gray-500">
<span id="showing-count">0</span> of <span id="total-count">0</span> emails shown
</div>
</div>
<div class="overflow-x-auto">
<table class="min-w-full bg-white border border-gray-200 rounded-lg overflow-hidden">
<thead class="bg-gray-50">
<tr>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Details</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">MX Response</th>
</tr>
</thead>
<tbody id="email-table-body" class="divide-y divide-gray-200">
<!-- Email rows will be inserted here -->
</tbody>
</table>
</div>
</div>
<div class="flex flex-col sm:flex-row justify-between gap-4">
<button id="download-report" class="flex-1 bg-blue-500 hover:bg-blue-600 text-white px-6 py-3 rounded-md flex items-center justify-center transition duration-300">
<i class="fas fa-file-excel mr-2"></i> Download Cleaned Excel File
</button>
<button id="download-summary" class="flex-1 bg-gray-100 hover:bg-gray-200 text-gray-700 px-6 py-3 rounded-md flex items-center justify-center transition duration-300">
<i class="fas fa-file-alt mr-2"></i> Download Full Report (PDF)
</button>
<button id="start-over" class="flex-1 bg-white border border-gray-300 hover:bg-gray-50 text-gray-700 px-6 py-3 rounded-md flex items-center justify-center transition duration-300">
<i class="fas fa-redo mr-2"></i> Start Over
</button>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md p-6 mb-8 hidden" id="error-section">
<div class="flex items-center mb-4">
<div class="w-8 h-8 rounded-full bg-red-100 flex items-center justify-center mr-3">
<i class="fas fa-exclamation-triangle text-red-500"></i>
</div>
<h3 class="text-lg font-medium text-gray-700">Error Processing File</h3>
</div>
<div class="text-red-600 mb-4" id="error-message"></div>
<button id="try-again" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-md transition duration-300">
<i class="fas fa-redo mr-2"></i> Try Again
</button>
</div>
<div class="bg-white rounded-lg shadow-md p-6">
<h3 class="text-lg font-medium text-gray-700 mb-4">How It Works</h3>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div class="bg-gray-50 p-4 rounded-lg">
<div class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center mb-3">
<i class="fas fa-file-upload text-blue-500"></i>
</div>
<h4 class="font-medium text-gray-800 mb-1">1. Upload File</h4>
<p class="text-sm text-gray-600">Upload your Excel file containing email addresses</p>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="w-10 h-10 bg-purple-100 rounded-full flex items-center justify-center mb-3">
<i class="fas fa-check-circle text-purple-500"></i>
</div>
<h4 class="font-medium text-gray-800 mb-1">2. Validation</h4>
<p class="text-sm text-gray-600">We check each email for syntax, typos, duplicates, and deliverability</p>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center mb-3">
<i class="fas fa-chart-pie text-green-500"></i>
</div>
<h4 class="font-medium text-gray-800 mb-1">3. Analysis</h4>
<p class="text-sm text-gray-600">Get detailed statistics about your email list quality</p>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="w-10 h-10 bg-amber-100 rounded-full flex items-center justify-center mb-3">
<i class="fas fa-download text-amber-500"></i>
</div>
<h4 class="font-medium text-gray-800 mb-1">4. Download</h4>
<p class="text-sm text-gray-600">Download cleaned file with validation results and suggestions</p>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM elements
const dropzone = document.getElementById('dropzone');
const fileUpload = document.getElementById('file-upload');
const uploadSection = document.getElementById('upload-section');
const processingSection = document.getElementById('processing-section');
const resultsSection = document.getElementById('results-section');
const errorSection = document.getElementById('error-section');
const emailTableBody = document.getElementById('email-table-body');
// Progress elements
const progressBar = document.getElementById('progress-bar');
const progressPercent = document.getElementById('progress-percent');
const processedCount = document.getElementById('processed-count');
const validCount = document.getElementById('valid-count');
const issuesCount = document.getElementById('issues-count');
// Result elements
const totalEmails = document.getElementById('total-emails');
const finalValid = document.getElementById('final-valid');
const finalInvalid = document.getElementById('final-invalid');
const duplicateCount = document.getElementById('duplicate-count');
const typoCount = document.getElementById('typo-count');
const syntaxCount = document.getElementById('syntax-count');
const mxCount = document.getElementById('mx-count');
const undeliverableCount = document.getElementById('undeliverable-count');
const unknownCount = document.getElementById('unknown-count');
const showingCount = document.getElementById('showing-count');
const totalCount = document.getElementById('total-count');
// MX Insights elements
const validMxCount = document.getElementById('valid-mx-count');
const validMxPercent = document.getElementById('valid-mx-percent');
const invalidMxCount = document.getElementById('invalid-mx-count');
const topMxIssues = document.getElementById('top-mx-issues');
// Deliverability Insights elements
const deliverableCount = document.getElementById('deliverable-count');
const deliverablePercent = document.getElementById('deliverable-percent');
const undeliverableInsightCount = document.getElementById('undeliverable-insight-count');
const unknownDeliverabilityCount = document.getElementById('unknown-deliverability-count');
const deliverabilityScoreBar = document.getElementById('deliverability-score-bar');
const deliverabilityScore = document.getElementById('deliverability-score');
const deliverabilityRating = document.getElementById('deliverability-rating');
const deliverabilityDescription = document.getElementById('deliverability-description');
// Buttons
const downloadReport = document.getElementById('download-report');
const downloadSummary = document.getElementById('download-summary');
const startOver = document.getElementById('start-over');
const tryAgain = document.getElementById('try-again');
// Data storage
let emailData = [];
let processedEmails = [];
let workbook = null;
let domainStats = {};
// Common typos mapping
const commonTypos = {
'gmai.com': 'gmail.com',
'gmail.con': 'gmail.com',
'gmail.co': 'gmail.com',
'gmail.cm': 'gmail.com',
'gmial.com': 'gmail.com',
'gmal.com': 'gmail.com',
'gamil.com': 'gmail.com',
'gnail.com': 'gmail.com',
'gmsil.com': 'gmail.com',
'gmaill.com': 'gmail.com',
'gmail..com': 'gmail.com',
'gmail.comm': 'gmail.com',
'gmail.om': 'gmail.com',
'gmil.com': 'gmail.com',
'gmaul.com': 'gmail.com',
'gmaio.com': 'gmail.com',
'gmaiil.com': 'gmail.com',
'gmeil.com': 'gmail.com',
'gmil.com': 'gmail.com',
'gmaile.com': 'gmail.com',
'gmaim.com': 'gmail.com',
'hotnail.com': 'hotmail.com',
'homail.com': 'hotmail.com',
'hotmal.com': 'hotmail.com',
'hotmaill.com': 'hotmail.com',
'hotmail..com': 'hotmail.com',
'yahoo.co': 'yahoo.com',
'yaho.com': 'yahoo.com',
'yahho.com': 'yahoo.com',
'yahoocom': 'yahoo.com',
'yahooo.com': 'yahoo.com',
'yahoo.comm': 'yahoo.com',
'outlok.com': 'outlook.com',
'outllok.com': 'outlook.com',
'outlook..com': 'outlook.com'
};
// MX response messages
const mxResponses = {
valid: "Valid MX records found. Domain can receive emails.",
invalid: "No MX records found. Domain cannot receive emails.",
timeout: "MX lookup timed out. Could not verify mail servers.",
restricted: "MX lookup restricted by DNS settings.",
temporary: "Temporary MX lookup failure. Try again later."
};
// Deliverability messages
const deliverabilityMessages = {
deliverable: "Server confirmed email exists and can receive messages.",
undeliverable: "Server confirmed email does not exist or cannot receive messages.",
unknown: "Could not verify deliverability due to server restrictions.",
risky: "Email appears valid but may have deliverability issues.",
catchall: "Domain accepts all emails (catch-all), making verification less reliable."
};
// Event listeners
fileUpload.addEventListener('change', handleFileSelect);
dropzone.addEventListener('dragover', handleDragOver);
dropzone.addEventListener('dragleave', handleDragLeave);
dropzone.addEventListener('drop', handleDrop);
downloadReport.addEventListener('click', downloadCleanedFile);
downloadSummary.addEventListener('click', downloadSummaryReport);
startOver.addEventListener('click', resetForm);
tryAgain.addEventListener('click', resetForm);
// Functions
function handleFileSelect(e) {
const files = e.target.files;
if (files.length) {
processFile(files[0]);
}
}
function handleDragOver(e) {
e.preventDefault();
e.stopPropagation();
dropzone.classList.add('active');
}
function handleDragLeave(e) {
e.preventDefault();
e.stopPropagation();
dropzone.classList.remove('active');
}
function handleDrop(e) {
e.preventDefault();
e.stopPropagation();
dropzone.classList.remove('active');
const files = e.dataTransfer.files;
if (files.length) {
processFile(files[0]);
}
}
function processFile(file) {
const reader = new FileReader();
reader.onload = function(e) {
try {
const data = new Uint8Array(e.target.result);
workbook = XLSX.read(data, { type: 'array' });
// Extract all emails from the workbook
emailData = extractEmailsFromWorkbook(workbook);
if (emailData.length === 0) {
showError("No email addresses found in the file. Please upload a file with at least one column containing email addresses.");
return;
}
// Initialize domain stats
initDomainStats(emailData);
// Show processing section
uploadSection.classList.add('hidden');
processingSection.classList.remove('hidden');
resultsSection.classList.add('hidden');
errorSection.classList.add('hidden');
// Start processing emails
processEmails(emailData);
} catch (error) {
showError("Error reading the file. Please make sure it's a valid Excel file.");
console.error(error);
}
};
reader.onerror = function() {
showError("Error reading the file. Please try again.");
};
reader.readAsArrayBuffer(file);
}
function extractEmailsFromWorkbook(workbook) {
const emails = [];
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Process each sheet
workbook.SheetNames.forEach(sheetName => {
const worksheet = workbook.Sheets[sheetName];
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
// Process each row and cell
jsonData.forEach(row => {
if (Array.isArray(row)) {
row.forEach(cell => {
if (typeof cell === 'string') {
// Check if the cell looks like an email
if (emailRegex.test(cell.trim())) {
emails.push({
original: cell.trim(),
cleaned: cell.trim().toLowerCase(),
rowData: row // Store the entire row for later
});
}
}
});
}
});
});
return emails;
}
function initDomainStats(emails) {
domainStats = {};
// Count frequency of each domain
emails.forEach(email => {
const domain = email.cleaned.split('@')[1];
if (!domainStats[domain]) {
domainStats[domain] = {
count: 0,
validMx: 0,
invalidMx: 0,
deliverable: 0,
undeliverable: 0,
unknown: 0
};
}
domainStats[domain].count++;
});
}
function processEmails(emails) {
let processed = 0;
let valid = 0;
let issues = 0;
let duplicates = 0;
let typos = 0;
let syntaxErrors = 0;
let mxErrors = 0;
let undeliverable = 0;
let unknown = 0;
let deliverable = 0;
const totalEmails = emails.length;
const emailFrequency = {};
const processedEmailsList = [];
// Count frequency for duplicates
emails.forEach(email => {
const cleanEmail = email.cleaned;
emailFrequency[cleanEmail] = (emailFrequency[cleanEmail] || 0) + 1;
});
// Process each email
emails.forEach((email, index) => {
setTimeout(() => {
const result = validateEmail(email, emailFrequency);
// Update domain stats
const domain = email.cleaned.split('@')[1];
if (result.hasMxError) {
domainStats[domain].invalidMx++;
} else {
domainStats[domain].validMx++;
if (result.status === 'Valid') {
domainStats[domain].deliverable++;
deliverable++;
} else if (result.isUndeliverable) {
domainStats[domain].undeliverable++;
} else {
domainStats[domain].unknown++;
}
}
processedEmailsList.push({
...email,
...result
});
processed++;
// Update counters based on result
if (result.status === 'Valid') valid++;
else issues++;
if (result.isDuplicate) duplicates++;
if (result.hasTypo) typos++;
if (result.hasSyntaxError) syntaxErrors++;
if (result.hasMxError) mxErrors++;
if (result.isUndeliverable) undeliverable++;
if (result.status === 'Unknown') unknown++;
// Update progress
const progress = Math.round((processed / totalEmails) * 100);
progressBar.style.width = `${progress}%`;
progressPercent.textContent = `${progress}%`;
processedCount.textContent = processed;
validCount.textContent = valid;
issuesCount.textContent = issues;
// When all emails are processed
if (processed === totalEmails) {
showResults({
totalEmails,
valid,
invalid: issues,
duplicates,
typos,
syntaxErrors,
mxErrors,
undeliverable,
unknown,
deliverable,
processedEmails: processedEmailsList
});
}
}, index * 10); // Small delay to allow UI updates
});
}
function validateEmail(email, emailFrequency) {
const result = {
status: 'Valid',
isDuplicate: false,
hasTypo: false,
hasSyntaxError: false,
hasMxError: false,
isUndeliverable: false,
suggestion: null,
details: 'Email appears to be valid',
mxResponse: mxResponses.valid,
deliverability: deliverabilityMessages.deliverable
};
// Check for duplicates
if (emailFrequency[email.cleaned] > 1) {
result.isDuplicate = true;
result.status = 'Duplicate';
result.details = 'This email appears multiple times in your list';
return result;
}
// Check for syntax errors
if (!isValidEmailSyntax(email.original)) {
result.hasSyntaxError = true;
result.status = 'Invalid Syntax';
result.details = 'Email format is invalid (missing @, invalid characters, etc.)';
result.mxResponse = 'N/A - Invalid email format';
result.deliverability = 'N/A - Invalid email format';
return result;
}
// Check for typos
const typoCheck = checkForTypos(email.cleaned);
if (typoCheck.hasTypo) {
result.hasTypo = true;
result.status = 'Typo';
result.suggestion = typoCheck.suggestion;
result.details = `Possible typo detected. Did you mean ${typoCheck.suggestion}?`;
}
// Simulate MX check (in a real app, this would be an API call)
const domain = email.cleaned.split('@')[1];
const mxCheckResult = simulateMxCheck(domain);
if (!mxCheckResult.valid) {
result.hasMxError = true;
result.status = 'Invalid MX';
result.details = 'Domain does not have valid mail server records';
result.mxResponse = mxCheckResult.message;
result.deliverability = 'N/A - No mail servers found';
return result;
} else {
result.mxResponse = mxCheckResult.message;
}
// Simulate deliverability check (in a real app, this would be an API call)
const deliverabilityResult = simulateDeliverabilityCheck(email.cleaned);
if (deliverabilityResult.status === 'undeliverable') {
result.isUndeliverable = true;
result.status = 'Undeliverable';
result.details = 'Email address appears to be invalid or not accepting mail';
result.deliverability = deliverabilityResult.message;
} else if (deliverabilityResult.status === 'unknown') {
result.status = 'Unknown';
result.details = 'Could not verify deliverability';
result.deliverability = deliverabilityResult.message;
} else if (deliverabilityResult.status === 'risky') {
result.deliverability = deliverabilityResult.message;
} else if (deliverabilityResult.status === 'catchall') {
result.deliverability = deliverabilityResult.message;
}
return result;
}
function isValidEmailSyntax(email) {
// More comprehensive regex for email validation
const regex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return regex.test(email);
}
function checkForTypos(email) {
const result = {
hasTypo: false,
suggestion: null
};
const [localPart, domain] = email.split('@');
// Check for common domain typos
if (commonTypos[domain]) {
result.hasTypo = true;
result.suggestion = `${localPart}@${commonTypos[domain]}`;
return result;
}
// Check for doubled dots
if (domain.includes('..')) {
const correctedDomain = domain.replace(/\.+/g, '.');
result.hasTypo = true;
result.suggestion = `${localPart}@${correctedDomain}`;
return result;
}
// Check for common TLD typos
const tld = domain.split('.').pop();
const commonTldTypos = {
// Common "com" typos (keyboard proximity, missing letters, repeats)
'con': 'com', 'cpm': 'com', 'cim': 'com', 'col': 'com', 'cok': 'com',
'co': 'com', 'cm': 'com', 'comm': 'com', 'vom': 'com', 'ocm': 'com',
'cmo': 'com', 'comn': 'com', 'coom': 'com', 'commm': 'com', 'c0m': 'com',
'čom': 'com', // Accented character typo
'kom': 'com', // Phonetic error (non-English keyboards)
// Common "net" typos
'netl': 'net', 'ner': 'net', 'nte': 'net', 'ent': 'net', 'nett': 'net',
'nettt': 'net', 'n3t': 'net', 'nét': 'net', // Accented typo
// Common "org" typos
'orgn': 'org', 'og': 'org', 'ogr': 'org', 'or': 'org', 'rg': 'org',
'orgi': 'org', 'orq': 'org', '0rg': 'org', 'örg': 'org', // Accented/QWERTY errors
// Common "gov" typos
'gouv': 'gov', 'gob': 'gov', 'gv': 'gov', 'govv': 'gov', 'gove': 'gov',
'goev': 'gov', 'g0v': 'gov',
// Common "edu" typos
'edu.': 'edu', 'eddu': 'edu', 'ed': 'edu', 'eduu': 'edu', '3du': 'edu',
// Other generic TLDs
'intl': 'int', 'itn': 'int', 'inr': 'int', 'iint': 'int', // .int typos
'mil.': 'mil', 'mll': 'mil', 'miil': 'mil', // .mil typos
'biz': 'biz', 'bizz': 'biz', 'bzi': 'biz', // .biz typos (prevents overcorrection)
'infoo': 'info', 'infor': 'info', 'infi': 'info', // .info typos
// Trailing characters (e.g., accidental punctuation)
'org.': 'org', 'com.': 'com', 'net.': 'net', 'edu..': 'edu',
// Country TLD overcorrections (use with caution!)
'uk': 'com', // Common mistake for .uk vs .com
'cm': 'com', // Cameroon’s TLD often mistyped as .com
'om': 'com', // Oman’s TLD mistyped as .com
};
if (commonTldTypos[tld]) {
const correctedDomain = domain.replace(new RegExp(`${tld}$`), commonTldTypos[tld]);
result.hasTypo = true;
result.suggestion = `${localPart}@${correctedDomain}`;
return result;
}
return result;
}
function simulateMxCheck(domain) {
// In a real implementation, this would make a DNS query
// For simulation, we'll randomly fail for some domains
const badDomains = ['example.com', 'test.com', 'invalid.com', 'fakedomain.com'];
if (badDomains.includes(domain)) {
return {
valid: false,
message: mxResponses.invalid
};
}
// Randomly simulate other MX issues
const random = Math.random();
if (random < 0.05) {
return {
valid: false,
message: mxResponses.timeout
};
} else if (random < 0.1) {
return {
valid: false,
message: mxResponses.restricted
};
} else if (random < 0.15) {
return {
valid: false,
message: mxResponses.temporary
};
}
return {
valid: true,
message: mxResponses.valid
};
}
function simulateDeliverabilityCheck(email) {
// In a real implementation, this would make an SMTP check
// For simulation, we'll randomly mark some as undeliverable
const random = Math.random();
if (random < 0.1) {
return {
status: 'undeliverable',
message: deliverabilityMessages.undeliverable
};
} else if (random < 0.2) {
return {
status: 'unknown',
message: deliverabilityMessages.unknown
};
} else if (random < 0.3) {
return {
status: 'risky',
message: deliverabilityMessages.risky
};
} else if (random < 0.4) {
return {
status: 'catchall',
message: deliverabilityMessages.catchall
};
}
return {
status: 'deliverable',
message: deliverabilityMessages.deliverable
};
}
function showResults(stats) {
processingSection.classList.add('hidden');
resultsSection.classList.remove('hidden');
// Update summary stats
totalEmails.textContent = stats.totalEmails;
finalValid.textContent = stats.valid;
finalInvalid.textContent = stats.invalid;
duplicateCount.textContent = stats.duplicates;
typoCount.textContent = stats.typos;
syntaxCount.textContent = stats.syntaxErrors;
mxCount.textContent = stats.mxErrors;
undeliverableCount.textContent = stats.undeliverable;
unknownCount.textContent = stats.unknown;
// Calculate MX stats
const validMx = stats.totalEmails - stats.mxErrors;
const validMxPct = Math.round((validMx / stats.totalEmails) * 100);
validMxCount.textContent = validMx;
validMxPercent.textContent = validMxPct;
invalidMxCount.textContent = stats.mxErrors;
// Show top MX issues
const problematicDomains = Object.entries(domainStats)
.filter(([domain, stats]) => stats.invalidMx > 0)
.sort((a, b) => b[1].invalidMx - a[1].invalidMx)
.slice(0, 5);
if (problematicDomains.length > 0) {
topMxIssues.innerHTML = problematicDomains
.map(([domain, stats]) =>
`<div class="flex justify-between py-1">
<span class="truncate">${domain}</span>
<span class="font-medium">${stats.invalidMx} issues</span>
</div>`
).join('');
} else {
topMxIssues.textContent = "No significant MX issues detected";
}
// Update deliverability stats
deliverableCount.textContent = stats.deliverable;
const deliverablePct = Math.round((stats.deliverable / stats.totalEmails) * 100);
deliverablePercent.textContent = deliverablePct;
undeliverableInsightCount.textContent = stats.undeliverable;
unknownDeliverabilityCount.textContent = stats.unknown;
// Calculate deliverability score (0-100)
const deliverabilityScoreValue = Math.min(100, Math.max(0,
(stats.deliverable / stats.totalEmails) * 80 +
((stats.totalEmails - stats.undeliverable) / stats.totalEmails) * 20
));
deliverabilityScoreBar.style.width = `${deliverabilityScoreValue}%`;
deliverabilityScore.textContent = Math.round(deliverabilityScoreValue);
// Set deliverability rating
if (deliverabilityScoreValue >= 80) {
deliverabilityRating.textContent = "Excellent";
deliverabilityRating.className = "font-medium text-green-600";
deliverabilityDescription.textContent = "This list has excellent deliverability potential";
} else if (deliverabilityScoreValue >= 60) {
deliverabilityRating.textContent = "Good";
deliverabilityRating.className = "font-medium text-blue-600";
deliverabilityDescription.textContent = "This list has good deliverability with minor issues";
} else if (deliverabilityScoreValue >= 40) {
deliverabilityRating.textContent = "Fair";
deliverabilityRating.className = "font-medium text-yellow-600";
deliverabilityDescription.textContent = "This list may have deliverability issues that need attention";
} else {
deliverabilityRating.textContent = "Poor";
deliverabilityRating.className = "font-medium text-red-600";
deliverabilityDescription.textContent = "This list has significant deliverability issues that need attention";
}
// Show first 100 emails in the table
const emailsToShow = stats.processedEmails.slice(0, 100);
showingCount.textContent = emailsToShow.length;
totalCount.textContent = stats.totalEmails;
emailTableBody.innerHTML = '';
emailsToShow.forEach(email => {
const row = document.createElement('tr');
row.className = 'fade-in';
// Status badge
let statusClass = '';
if (email.status === 'Valid') statusClass = 'status-valid';
else if (email.status === 'Duplicate') statusClass = 'status-duplicate';
else if (email.status === 'Typo') statusClass = 'status-typo';
else if (email.status === 'Invalid Syntax') statusClass = 'status-invalid';
else if (email.status === 'Invalid MX') statusClass = 'status-mx';
else if (email.status === 'Undeliverable') statusClass = 'status-undeliverable';
else statusClass = 'status-unknown';
// Tooltip for details
const tooltipId = `tooltip-${Math.random().toString(36).substr(2, 9)}`;
row.innerHTML = `
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-800">${email.original}</td>
<td class="px-4 py-3 whitespace-nowrap">
<span class="status-badge ${statusClass}">${email.status}</span>
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-600">
<div class="tooltip">
<i class="fas fa-info-circle text-gray-400"></i>
<span class="tooltip-text">${email.details}${email.suggestion ? `<br><br>Suggestion: ${email.suggestion}` : ''}</span>
</div>
${email.suggestion ? `<span class="ml-2">${email.suggestion}</span>` : ''}
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-600">
<div class="tooltip">
<i class="fas fa-server text-gray-400"></i>
<span class="tooltip-text">${email.mxResponse}</span>
</div>
<span class="ml-2">${email.deliverability}</span>
</td>
`;
emailTableBody.appendChild(row);
});
// Store processed emails for download
processedEmails = stats.processedEmails;
workbook = addValidationResultsToWorkbook(workbook, processedEmails);
}
function addValidationResultsToWorkbook(workbook, processedEmails) {
// Create a map of original emails to their validation results
const emailMap = {};
processedEmails.forEach(email => {
emailMap[email.original] = {
status: email.status,
suggestion: email.suggestion || '',
mxResponse: email.mxResponse,
deliverability: email.deliverability
};
});
// Process each sheet
workbook.SheetNames.forEach(sheetName => {
const worksheet = workbook.Sheets[sheetName];
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
// Add headers for new columns if they don't exist
if (jsonData.length > 0) {
const headers = jsonData[0];
if (!headers.includes('Email Status')) {
headers.push('Email Status');
headers.push('Suggestion');
headers.push('MX Response');
headers.push('Deliverability');
}
}
// Process each row
for (let i = 1; i < jsonData.length; i++) {
const row = jsonData[i];
if (!Array.isArray(row)) continue;
// Find email in row
let emailInRow = null;
for (let j = 0; j < row.length; j++) {
const cell = row[j];
if (typeof cell === 'string' && isValidEmailSyntax(cell.trim())) {
emailInRow = cell.trim();
break;
}
}
// Add validation results
if (emailInRow && emailMap[emailInRow]) {
// Make sure row has enough columns
while (row.length < jsonData[0].length - 4) {
row.push('');
}
row.push(emailMap[emailInRow].status);
row.push(emailMap[emailInRow].suggestion);
row.push(emailMap[emailInRow].mxResponse);
row.push(emailMap[emailInRow].deliverability);
} else {
// For rows without email or invalid email
while (row.length < jsonData[0].length - 4) {
row.push('');
}
row.push('No valid email');
row.push('');
row.push('');
row.push('');
}
}
// Convert back to worksheet
const newWorksheet = XLSX.utils.aoa_to_sheet(jsonData);
workbook.Sheets[sheetName] = newWorksheet;
});
return workbook;
}
function downloadCleanedFile() {
if (!workbook) return;
// Convert workbook to binary
const wbout = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
// Create blob and download
const blob = new Blob([wbout], { type: 'application/octet-stream' });
saveAs(blob, 'cleaned_emails.xlsx');
}
function downloadSummaryReport() {
// In a real implementation, this would generate a PDF report
// For this demo, we'll create a more detailed text file
let reportText = `EMAIL VALIDATION REPORT\n`;
reportText += `================================\n`;
reportText += `Generated on: ${new Date().toLocaleString()}\n\n`;
// Summary stats
reportText += `SUMMARY STATISTICS\n`;
reportText += `------------------\n`;
reportText += `Total Emails Processed: ${totalEmails.textContent}\n`;
reportText += `Valid Emails: ${finalValid.textContent} (${Math.round((finalValid.textContent / totalEmails.textContent) * 100)}%)\n`;
reportText += `Invalid Emails: ${finalInvalid.textContent} (${Math.round((finalInvalid.textContent / totalEmails.textContent) * 100)}%)\n\n`;
// Detailed issues
reportText += `DETAILED ISSUES\n`;
reportText += `---------------\n`;
reportText += `- Duplicates: ${duplicateCount.textContent}\n`;
reportText += `- Typos Found: ${typoCount.textContent}\n`;
reportText += `- Syntax Errors: ${syntaxCount.textContent}\n`;
reportText += `- MX Record Issues: ${mxCount.textContent}\n`;
reportText += `- Undeliverable: ${undeliverableCount.textContent}\n`;
reportText += `- Unknown Status: ${unknownCount.textContent}\n\n`;
// MX Analysis
reportText += `MX RECORD ANALYSIS\n`;
reportText += `------------------\n`;
reportText += `Emails with valid MX records: ${validMxCount.textContent} (${validMxPercent.textContent}%)\n`;
reportText += `Emails with MX issues: ${invalidMxCount.textContent}\n\n`;
// Top MX issues
const topMxDomains = document.querySelectorAll('#top-mx-issues div');
if (topMxDomains.length > 0) {
reportText += `Top Domains with MX Issues:\n`;
topMxDomains.forEach(domain => {
reportText += ` - ${domain.textContent.trim()}\n`;
});
reportText += `\n`;
}
// Deliverability Analysis
reportText += `DELIVERABILITY ANALYSIS\n`;
reportText += `-----------------------\n`;
reportText += `Confirmed Deliverable: ${deliverableCount.textContent} (${deliverablePercent.textContent}%)\n`;
reportText += `Undeliverable: ${undeliverableInsightCount.textContent}\n`;
reportText += `Unknown Status: ${unknownDeliverabilityCount.textContent}\n\n`;
reportText += `Overall Deliverability Score: ${deliverabilityScore.textContent}/100\n`;
reportText += `Rating: ${deliverabilityRating.textContent}\n`;
reportText += `Description: ${deliverabilityDescription.textContent}\n\n`;
// Recommendations
reportText += `RECOMMENDATIONS\n`;
reportText += `---------------\n`;
if (parseInt(mxCount.textContent) > 0) {
reportText += `1. Address MX record issues for ${mxCount.textContent} emails to improve deliverability\n`;
}
if (parseInt(typoCount.textContent) > 0) {
reportText += `2. Review ${typoCount.textContent} emails with potential typos for correction\n`;
}
if (parseInt(duplicateCount.textContent) > 0) {
reportText += `3. Remove ${duplicateCount.textContent} duplicate emails to maintain list quality\n`;
}
if (parseInt(undeliverableCount.textContent) > 0) {
reportText += `4. Remove or verify ${undeliverableCount.textContent} undeliverable emails to improve sender reputation\n`;
}
const blob = new Blob([reportText], { type: 'text/plain;charset=utf-8' });
saveAs(blob, 'email_validation_report.txt');
}
function showError(message) {
uploadSection.classList.add('hidden');
processingSection.classList.add('hidden');
resultsSection.classList.add('hidden');
errorSection.classList.remove('hidden');
document.getElementById('error-message').textContent = message;
}
function resetForm() {
uploadSection.classList.remove('hidden');
processingSection.classList.add('hidden');
resultsSection.classList.add('hidden');
errorSection.classList.add('hidden');
// Reset progress
progressBar.style.width = '0%';
progressPercent.textContent = '0%';
processedCount.textContent = '0';
validCount.textContent = '0';
issuesCount.textContent = '0';
// Reset file input
fileUpload.value = '';
// Clear data
emailData = [];
processedEmails = [];
workbook = null;
domainStats = {};
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=sqibhe/email-validation" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>