From f7c7f9920de7b7178e161d2ff7c22543bfb90705 Mon Sep 17 00:00:00 2001 From: Sanjeev Deori Date: Sun, 19 Oct 2025 21:45:28 +0530 Subject: [PATCH] Add a Random Password Generator along with its README.md --- .../RandomPasswordGenrator/README.md | 65 ++++ .../RandomPasswordGenrator/index.html | 78 +++++ .../RandomPasswordGenrator/script.js | 285 ++++++++++++++++++ .../RandomPasswordGenrator/styles.css | 113 +++++++ 4 files changed, 541 insertions(+) create mode 100644 Domains/Frontend/MiniProjects/RandomPasswordGenrator/README.md create mode 100644 Domains/Frontend/MiniProjects/RandomPasswordGenrator/index.html create mode 100644 Domains/Frontend/MiniProjects/RandomPasswordGenrator/script.js create mode 100644 Domains/Frontend/MiniProjects/RandomPasswordGenrator/styles.css diff --git a/Domains/Frontend/MiniProjects/RandomPasswordGenrator/README.md b/Domains/Frontend/MiniProjects/RandomPasswordGenrator/README.md new file mode 100644 index 00000000..3484704a --- /dev/null +++ b/Domains/Frontend/MiniProjects/RandomPasswordGenrator/README.md @@ -0,0 +1,65 @@ +# ๐Ÿ” Secure Password Generator + +**Contributor:** [Sanjeev Deori](https://github.com/SanjeevDeori) + +## ๐Ÿงพ Description +A **secure and customizable password generator** web app that helps users create strong passwords for online accounts. Includes options for password length, character types, live strength estimation, and the ability to save passwords locally. + +--- + +## ๐Ÿš€ Features +- Generate passwords of user-defined length +- Include options for: + - Uppercase letters (Aโ€“Z) + - Lowercase letters (aโ€“z) + - Numbers (0โ€“9) + - Symbols (!@#$%^&*) +- Copy password to clipboard +- Display password strength visually +- Save generated passwords locally +- Responsive layout for desktop and mobile + +--- + +## ๐Ÿ’ก Bonus Features +- Dark/Light mode toggle +- Save passwords with custom labels +- Manage saved passwords: Copy, Use, Delete +- LocalStorage-backed saved password list + +--- + +## ๐Ÿงฉ Tech Stack +- **HTML5** โ€“ Structure and layout +- **CSS3** โ€“ Styling, responsiveness, and dark/light theme +- **JavaScript (Vanilla)** โ€“ Password generation logic, clipboard functionality, localStorage + +--- + +## ๐Ÿ•น๏ธ How to Use +1. Open `index.html` in your browser. +2. Select the desired password length and character options. +3. Click **Generate** to create a new password. +4. Use the **Copy** button to copy it to the clipboard. +5. Optionally, save the password with a label using the **Save** button. +6. View, use, or delete saved passwords in the saved passwords section. + +--- + +## ๐Ÿ“ธ Screenshots +*(Add screenshots or GIFs here to showcase the UI, dark/light mode, and saved passwords feature.)* + +--- + +## ๐Ÿ—๏ธ Setup & Run Locally +1. Clone the repository: + ```bash + git clone https://github.com/SanjeevDeori/password-generator.git + +2. Open the project folder: +```bash + +cd password-generator + + +3. Open index.html in your browser to use the app. \ No newline at end of file diff --git a/Domains/Frontend/MiniProjects/RandomPasswordGenrator/index.html b/Domains/Frontend/MiniProjects/RandomPasswordGenrator/index.html new file mode 100644 index 00000000..0e243586 --- /dev/null +++ b/Domains/Frontend/MiniProjects/RandomPasswordGenrator/index.html @@ -0,0 +1,78 @@ + + + + + + Password Generator + + + +
+
+

Secure Password Generator

+
+ +
+
+ +
+

Create a password

+ +
+ +
+ + +
+
+ +
+
+ + +
+ +
+ Include + + + + +
+ +
+ +
+ +
+ +
โ€”
+
+
+
+ +
+

Saved Passwords

+
+ + +
+
    +
    + +
    +
    + +
    + Built with โค๏ธ โ€ข HTML โ€ข CSS โ€ข JavaScript +
    +
    + + + + diff --git a/Domains/Frontend/MiniProjects/RandomPasswordGenrator/script.js b/Domains/Frontend/MiniProjects/RandomPasswordGenrator/script.js new file mode 100644 index 00000000..25b787bd --- /dev/null +++ b/Domains/Frontend/MiniProjects/RandomPasswordGenrator/script.js @@ -0,0 +1,285 @@ +// script.js +(() => { + // Character sets + const SETS = { + upper: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + lower: "abcdefghijklmnopqrstuvwxyz", + numbers: "0123456789", + symbols: "!@#$%^&*()_+[]{}|;:,.<>?/~`-=", + }; + + // Elements + const passwordOutput = document.getElementById("passwordOutput"); + const lengthRange = document.getElementById("lengthRange"); + const lengthValue = document.getElementById("lengthValue"); + const upper = document.getElementById("upper"); + const lower = document.getElementById("lower"); + const numbers = document.getElementById("numbers"); + const symbols = document.getElementById("symbols"); + const generateBtn = document.getElementById("generateBtn"); + const copyBtn = document.getElementById("copyBtn"); + const saveBtn = document.getElementById("saveBtn"); + const meterFill = document.getElementById("meterFill"); + const strengthLabel = document.getElementById("strengthLabel"); + const saveForm = document.getElementById("saveForm"); + const saveLabel = document.getElementById("saveLabel"); + const savedList = document.getElementById("savedList"); + const clearAllBtn = document.getElementById("clearAllBtn"); + const themeToggle = document.getElementById("themeToggle"); + + // LocalStorage keys + const LS_SAVED = "pwgen_saved"; + const LS_THEME = "pwgen_theme"; + + // Helper: random integer + function randInt(max) { + return Math.floor(Math.random() * max); + } + + // Generate password + function generatePassword(length) { + const chosenSets = []; + if (upper.checked) chosenSets.push(SETS.upper); + if (lower.checked) chosenSets.push(SETS.lower); + if (numbers.checked) chosenSets.push(SETS.numbers); + if (symbols.checked) chosenSets.push(SETS.symbols); + + if (!chosenSets.length) { + alert("Select at least one character type."); + return ""; + } + + // Ensure at least one from each chosen set for better strength + const guaranteed = chosenSets.map(s => s[randInt(s.length)]); + let allChars = chosenSets.join(""); + let remaining = length - guaranteed.length; + let resultChars = []; + + for (let i = 0; i < remaining; i++) { + resultChars.push(allChars[randInt(allChars.length)]); + } + + // merge guaranteed and remaining and shuffle + resultChars = resultChars.concat(guaranteed); + // Fisher-Yates shuffle + for (let i = resultChars.length - 1; i > 0; i--) { + const j = randInt(i + 1); + [resultChars[i], resultChars[j]] = [resultChars[j], resultChars[i]]; + } + + return resultChars.join(""); + } + + // Strength estimator (simple, intuitive) + function estimateStrength(pw) { + let pool = 0; + if (/[A-Z]/.test(pw)) pool += 26; + if (/[a-z]/.test(pw)) pool += 26; + if (/[0-9]/.test(pw)) pool += 10; + if (/[^A-Za-z0-9]/.test(pw)) pool += 32; + + // entropy bits ~ length * log2(pool) + let bits = pw.length * (pool ? Math.log2(pool) : 0); + // normalize into 0-100 + const score = Math.max(0, Math.min(100, Math.round((bits / 60) * 100))); + + let label = "Too weak"; + if (score < 25) label = "Weak"; + else if (score < 50) label = "Fair"; + else if (score < 75) label = "Good"; + else label = "Excellent"; + + return { score, label, bits: Math.round(bits) }; + } + + // Update strength UI + function updateStrengthUI(pw) { + if (!pw) { + meterFill.style.width = "0%"; + strengthLabel.textContent = "โ€”"; + meterFill.style.background = "transparent"; + return; + } + const { score, label } = estimateStrength(pw); + meterFill.style.width = `${score}%`; + strengthLabel.textContent = label; + // color mapping + if (score < 25) meterFill.style.background = "#ff5f5f"; + else if (score < 50) meterFill.style.background = "#ffb86b"; + else if (score < 75) meterFill.style.background = "#ffd86b"; + else meterFill.style.background = "#7cf59b"; + } + + // Save/Load saved passwords + function loadSaved() { + try { + const raw = localStorage.getItem(LS_SAVED); + return raw ? JSON.parse(raw) : []; + } catch { + return []; + } + } + function saveSaved(list) { + localStorage.setItem(LS_SAVED, JSON.stringify(list)); + } + + function renderSaved() { + const items = loadSaved(); + savedList.innerHTML = ""; + if (!items.length) { + savedList.innerHTML = ``; + return; + } + items.forEach((it, idx) => { + const li = document.createElement("li"); + li.className = "saved-item"; + const meta = document.createElement("div"); + meta.className = "saved-meta"; + meta.innerHTML = `${escapeHtml(it.label || "Untitled")}
    ${escapeHtml(it.value)}
    ${new Date(it.date).toLocaleString()}
    `; + const actions = document.createElement("div"); + actions.style.display = "flex"; + actions.style.gap = "6px"; + const copy = document.createElement("button"); + copy.className = "btn small"; + copy.textContent = "Copy"; + copy.addEventListener("click", () => { + copyToClipboard(it.value); + flash(copy, "Copied!"); + }); + const use = document.createElement("button"); + use.className = "btn small"; + use.textContent = "Use"; + use.addEventListener("click", () => { + passwordOutput.value = it.value; + updateStrengthUI(it.value); + }); + const del = document.createElement("button"); + del.className = "btn small"; + del.textContent = "Delete"; + del.addEventListener("click", () => { + const itemsNow = loadSaved(); + itemsNow.splice(idx, 1); + saveSaved(itemsNow); + renderSaved(); + }); + actions.appendChild(copy); + actions.appendChild(use); + actions.appendChild(del); + li.appendChild(meta); + li.appendChild(actions); + savedList.appendChild(li); + }); + } + + // Utilities + function escapeHtml(s) { + return String(s).replace(/[&<>"']/g, m => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[m])); + } + + function copyToClipboard(text) { + if (!navigator.clipboard) { + // fallback + const ta = document.createElement("textarea"); + ta.value = text; + document.body.appendChild(ta); + ta.select(); + try { document.execCommand("copy"); } catch {} + ta.remove(); + return; + } + return navigator.clipboard.writeText(text); + } + + function flash(el, text = "Done") { + const original = el.textContent; + el.textContent = text; + setTimeout(() => (el.textContent = original), 1200); + } + + // Theme + function initTheme() { + const saved = localStorage.getItem(LS_THEME); + if (saved === "dark") { + document.body.classList.add("dark"); + themeToggle.checked = true; + } else { + document.body.classList.remove("dark"); + themeToggle.checked = false; + } + } + themeToggle.addEventListener("change", () => { + if (themeToggle.checked) { + document.body.classList.add("dark"); + localStorage.setItem(LS_THEME, "dark"); + } else { + document.body.classList.remove("dark"); + localStorage.setItem(LS_THEME, "light"); + } + }); + + // Events + lengthRange.addEventListener("input", () => { + lengthValue.textContent = lengthRange.value; + }); + + generateBtn.addEventListener("click", (e) => { + e.preventDefault(); + const len = parseInt(lengthRange.value, 10); + const pw = generatePassword(len); + passwordOutput.value = pw; + updateStrengthUI(pw); + }); + + // generate on load + window.addEventListener("load", () => { + initTheme(); + lengthValue.textContent = lengthRange.value; + // initial generate + const initial = generatePassword(parseInt(lengthRange.value, 10)); + passwordOutput.value = initial; + updateStrengthUI(initial); + renderSaved(); + }); + + // real-time strength update if someone edits the output (rare) + passwordOutput.addEventListener("input", (e) => { + updateStrengthUI(e.target.value); + }); + + copyBtn.addEventListener("click", async () => { + if (!passwordOutput.value) return; + try { + await copyToClipboard(passwordOutput.value); + flash(copyBtn, "Copied!"); + } catch { + flash(copyBtn, "Copy"); + } + }); + + // Save current + saveBtn.addEventListener("click", (e) => { + e.preventDefault(); + const current = passwordOutput.value; + if (!current) { alert("No password to save."); return; } + const labelText = saveLabel.value.trim(); + const list = loadSaved(); + list.unshift({ value: current, label: labelText || "Untitled", date: new Date().toISOString() }); + saveSaved(list); + saveLabel.value = ""; + renderSaved(); + flash(saveBtn, "Saved!"); + }); + + // Save via form (label + save) + saveForm.addEventListener("submit", (e) => { + e.preventDefault(); + saveBtn.click(); + }); + + clearAllBtn.addEventListener("click", () => { + if (!confirm("Clear all saved passwords?")) return; + localStorage.removeItem(LS_SAVED); + renderSaved(); + }); + +})(); diff --git a/Domains/Frontend/MiniProjects/RandomPasswordGenrator/styles.css b/Domains/Frontend/MiniProjects/RandomPasswordGenrator/styles.css new file mode 100644 index 00000000..819cda86 --- /dev/null +++ b/Domains/Frontend/MiniProjects/RandomPasswordGenrator/styles.css @@ -0,0 +1,113 @@ + + +:root{ + --bg:#f4f7fb; + --card:#ffffff; + --text:#17202a; + --muted:#536170; + --accent:#0066ff; + --danger:#e64c65; + --radius:12px; + --glass: rgba(255,255,255,0.6); +} + +*{box-sizing:border-box} +html,body{height:100%} +body{ + margin:0; + font-family:Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial; + background:linear-gradient(180deg,var(--bg),#e9eef6); + color:var(--text); + -webkit-font-smoothing:antialiased; + -moz-osx-font-smoothing:grayscale; +} + +.container{ + max-width:980px; + margin:28px auto; + padding:20px; +} + +/* header */ +.header{ + display:flex; + justify-content:space-between; + align-items:center; + gap:16px; + margin-bottom:18px; +} +.header h1{margin:0;font-size:1.4rem} +.theme-toggle{display:flex;align-items:center;gap:8px;font-size:0.9rem} +.theme-toggle input{width:18px;height:18px} + +/* cards */ +.card{ + background:var(--card); + border-radius:var(--radius); + padding:16px; + box-shadow:0 6px 18px rgba(23,32,42,0.06); + margin-bottom:16px; +} + +/* output */ +.output-row{display:flex;gap:8px;align-items:center} +.password-output{ + flex:1; + padding:12px 14px; + border-radius:10px; + border:1px solid #e6eef7; + font-family:monospace; + font-size:1rem; + color:var(--text); +} +.output-actions{display:flex;gap:8px} +.btn{ + border:0;padding:8px 12px;border-radius:8px;cursor:pointer;background:#f0f6ff; +} +.btn.small{padding:6px 10px} +.btn.primary{background:linear-gradient(90deg,var(--accent),#3aa1ff);color:white} +.btn.danger{background:linear-gradient(90deg,var(--danger),#ff7b8f);color:white} + +/* controls */ +.controls{display:grid;grid-template-columns:1fr;gap:12px;margin-top:12px} +.control{display:flex;flex-direction:column;gap:6px} +.control label{font-size:0.9rem;color:var(--muted)} +input[type="range"]{width:100%} +.checkboxes{display:flex;flex-wrap:wrap;gap:10px;padding:10px;border-radius:8px;background:var(--glass);border:1px solid #eef6ff} +.checkboxes label{font-size:0.9rem} + +/* meter */ +.meter-row{display:flex;align-items:center;gap:12px;margin-top:8px} +.strength-meter{flex:1;height:12px;background:#eef7ff;border-radius:999px;overflow:hidden;border:1px solid #e1efff} +.meter-fill{height:100%;width:0%;transition:width 240ms ease} +.strength-label{min-width:90px;text-align:right;font-weight:600;color:var(--muted)} + +/* saved */ +.save-form{display:flex;gap:8px;margin-bottom:10px} +.save-form input{flex:1;padding:9px;border-radius:8px;border:1px solid #e6eef7} +.saved-list{list-style:none;padding:0;margin:0;display:grid;gap:8px;max-height:260px;overflow:auto} +.saved-item{ + display:flex;align-items:center;gap:8px;padding:8px;border-radius:8px;border:1px solid #eef6ff;background:#fbfdff; +} +.saved-meta{flex:1;font-size:0.9rem;word-break:break-all} +.saved-actions{display:flex;justify-content:flex-end;margin-top:8px} + +/* footer */ +.footer{margin-top:10px;text-align:center;color:var(--muted)} + +/* responsive */ +@media (min-width:720px){ + .controls{grid-template-columns:repeat(2,1fr)} +} + +/* dark theme */ +body.dark{ + --bg:#0f1720; + --card:#0b1220; + --text:#e6eef8; + --muted:#a8b3c3; + --accent:#4ea6ff; + --danger:#ff8f9b; + background:linear-gradient(180deg,#071018,#081218); +} +body.dark .password-output{background:#071427;color:var(--text);border-color:#0f2436}