diff --git a/README.md b/README.md index 5bb09f0..267e595 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,33 @@ # Email-Variations-Generator Generate variation of duplicate valid Email for your email address + +**Live demo:** https://fea-email-variations-generator.vercel.app/ + +## How it works + +Gmail ignores dots (`.`) in the username part of an address, so +`johndoe@gmail.com`, `john.doe@gmail.com` and `j.o.h.n.d.o.e@gmail.com` all +deliver to the same inbox. This tool lists every way of placing dots in your +username, which is handy for sign-up testing, filtering, or spotting where an +address was shared. + +- Existing dots are ignored, so `john.doe@gmail.com` and `johndoe@gmail.com` + give the same 64 variations (never `john..doe`). +- A `+tag` suffix (e.g. `john+news@gmail.com`) is kept unchanged; only the + part before `+` is varied. +- A username of *n* characters has 2^(n-1) variations. The output is capped at + 8,192 (every variation for usernames up to 14 characters) to keep the page + responsive; the page tells you when results are truncated. +- Results can be exported to an `.xlsx` file (via [SheetJS](https://sheetjs.com/)). + +> Dot-insensitivity is a Gmail / Google Workspace behavior. Most other +> providers treat dotted addresses as different mailboxes. + +## Run locally + +It's a static page with no build step: open `index.html` in a browser, or +serve the folder (e.g. `npx serve .`). + +## License + +[MIT](LICENSE) diff --git a/index.html b/index.html index 7251c5a..811f01c 100644 --- a/index.html +++ b/index.html @@ -5,29 +5,33 @@ Email Variations Generator + -

Email Variations Generator

-

FEA in the url is an abbr for : Fady Ehab Amer

+

Email Variations Generator

+

FEA in the URL is an abbreviation for: Fady Ehab Amer

- - -
- - -
+
+ + +
+ + +
+
- + +

- + diff --git a/main.js b/main.js index 7e9ceff..4879faf 100644 --- a/main.js +++ b/main.js @@ -1,11 +1,32 @@ -function generateEmailVariations(email) { +// The number of variations doubles with every character of the username +// (2^(n-1)), so a long username would freeze the page. Cap the output; 8192 +// covers every variation of usernames up to 14 characters. +const MAX_VARIATIONS = 8192; + +// Total number of dot variations for an email (may exceed MAX_VARIATIONS). +function countEmailVariations(email) { + let localPart = email.split("@")[0].split("+")[0].replace(/\./g, ""); + return 2 ** Math.max(localPart.length - 1, 0); +} + +function generateEmailVariations(email, limit = MAX_VARIATIONS) { let [localPart, domain] = email.split("@"); + // Gmail ignores dots in the username, so start from the dot-free form. + // Otherwise existing dots get combined with inserted ones and produce + // invalid addresses such as "john..doe@gmail.com". + // A "+tag" suffix is kept as-is; only the username before it is varied. + let plusIndex = localPart.indexOf("+"); + let tag = plusIndex === -1 ? "" : localPart.slice(plusIndex); + localPart = (plusIndex === -1 ? localPart : localPart.slice(0, plusIndex)).replace(/\./g, ""); + let variations = new Set(); function addDots(prefix, remaining) { + if (variations.size >= limit) return; + if (!remaining) { - variations.add(prefix + "@" + domain); + variations.add(prefix + tag + "@" + domain); return; } @@ -23,29 +44,34 @@ function generateEmailVariations(email) { return Array.from(variations); } -let previouslyGeneratedEmail = ''; +// Email and variations currently shown in the table (used by the export). +let currentEmail = ''; +let currentVariations = []; function generateAndDisplayVariations() { let emailInput = document.getElementById('emailInput'); - let email = emailInput.value; + let email = emailInput.value.trim(); let errorMsg = document.getElementById('errorMsg'); + let statusMsg = document.getElementById('statusMsg'); let exportBtn = document.getElementById('exportBtn'); // Email validation if (!validateEmail(email)) { - alert("Please enter a valid email address!"); - return; - } - - // Check if the email is the same as the previously generated one - if (email === previouslyGeneratedEmail) { - alert("This email has already been generated!"); + errorMsg.textContent = "Please enter a valid email address."; + emailInput.setAttribute('aria-invalid', 'true'); + emailInput.focus(); return; } + errorMsg.textContent = ''; + emailInput.removeAttribute('aria-invalid'); let resultsTable = document.getElementById('results'); let validEmails = generateEmailVariations(email); + let total = countEmailVariations(email); + statusMsg.textContent = total > validEmails.length + ? `Showing the first ${validEmails.length.toLocaleString()} of ${total.toLocaleString()} possible variations.` + : `${validEmails.length.toLocaleString()} variations generated.`; // Clear previous results resultsTable.innerHTML = ''; @@ -65,21 +91,23 @@ function generateAndDisplayVariations() { // Enable the export button exportBtn.disabled = false; - // Set this email as the previously generated one - previouslyGeneratedEmail = email; - - // Disable the email input to prevent changing the email - emailInput.disabled = true; + currentEmail = email; + currentVariations = validEmails; } function validateEmail(email) { - var re = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/; + // Local part: dot-separated atoms (no leading, trailing or double dots), + // "+" allowed for tags. Domain: dot-separated labels that don't start or + // end with "-", followed by a TLD of 2+ letters (e.g. ".photography"). + var re = /^[a-zA-Z0-9_%+-]+(\.[a-zA-Z0-9_%+-]+)*@([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/; return re.test(email); } function exportToExcel() { - let email = document.getElementById('emailInput').value; - let validEmails = generateEmailVariations(email); + // Export exactly what is displayed, even if the input was edited since. + let email = currentEmail; + let validEmails = currentVariations; + if (!validEmails.length) return; let wb = XLSX.utils.book_new(); let ws_name = "EmailVariations"; diff --git a/style.css b/style.css index e4f97d1..9a1f9dd 100644 --- a/style.css +++ b/style.css @@ -4,7 +4,8 @@ body { background-color: #f4f4f4; } -h2 { +h1, +.subtitle { text-align: center; color: #333; } @@ -81,3 +82,8 @@ td { .controls button { flex-grow: 1; } + +#errorMsg { + color: #b00020; + font-weight: bold; +}