/ password_generator / script.js
script.js
  1  const title = document.getElementById("main-title");
  2  const passwordOutput = document.getElementById("password-output");
  3  const generateBtn = document.getElementById("generate-btn");
  4  const lengthInput = document.getElementById("length");
  5  const lengthVal = document.getElementById("length-val");
  6  const lengthHelp = document.getElementById("length-help");
  7  const dropZone = document.getElementById("drop-zone");
  8  const historyList = document.getElementById("history-list");
  9  const clearHistoryBtn = document.getElementById("clear-history");
 10  const expiryTimer = document.getElementById("expiry-timer");
 11  const secondsSpan = document.getElementById("seconds");
 12  
 13  const validationPanel = document.getElementById("validation-panel");
 14  const verifyInput = document.getElementById("verify-input");
 15  const verifyBtn = document.getElementById("verify-btn");
 16  const verifyFeedback = document.getElementById("verify-feedback");
 17  
 18  let countdownInterval;
 19  let historyData = [];
 20  let currentlyVerifying = "";
 21  
 22  // Neobrutalist Hover Effects
 23  title.addEventListener("mouseenter", () => {
 24    title.classList.add("text-pink-500", "scale-110", "-rotate-2");
 25  });
 26  
 27  title.addEventListener("mouseleave", () => {
 28    title.classList.remove("text-pink-500", "scale-110", "-rotate-2");
 29  });
 30  
 31  lengthInput.addEventListener("focus", () =>
 32    lengthHelp.classList.add("help-box-visible"),
 33  );
 34  lengthInput.addEventListener("blur", () =>
 35    lengthHelp.classList.remove("help-box-visible"),
 36  );
 37  lengthInput.addEventListener(
 38    "input",
 39    (e) => (lengthVal.textContent = e.target.value),
 40  );
 41  
 42  // Password Generation Logic
 43  function generatePassword() {
 44    const length = +lengthInput.value;
 45    const hasUpper = document.getElementById("uppercase").checked;
 46    const hasLower = document.getElementById("lowercase").checked;
 47    const hasNumber = document.getElementById("numbers").checked;
 48    const hasSymbol = document.getElementById("symbols").checked;
 49  
 50    const charset = {
 51      upper: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
 52      lower: "abcdefghijklmnopqrstuvwxyz",
 53      number: "0123456789",
 54      symbol: "!@#$%^&*()_+~`|}{[]:;?><,./-=",
 55    };
 56  
 57    let characters = "";
 58    if (hasUpper) characters += charset.upper;
 59    if (hasLower) characters += charset.lower;
 60    if (hasNumber) characters += charset.number;
 61    if (hasSymbol) characters += charset.symbol;
 62  
 63    if (characters === "") return "ERROR: SELECT OPTION";
 64  
 65    let password = "";
 66    for (let i = 0; i < length; i++) {
 67      password += characters.charAt(
 68        Math.floor(Math.random() * characters.length),
 69      );
 70    }
 71    return password;
 72  }
 73  
 74  generateBtn.addEventListener("click", () => {
 75    const pwd = generatePassword();
 76    passwordOutput.textContent = pwd;
 77  
 78    if (pwd !== "ERROR: SELECT OPTION") {
 79      startCountdown();
 80  
 81      // Send to backend
 82      fetch("/submit", {
 83        method: "POST",
 84        headers: {
 85          "Content-Type": "application/json",
 86        },
 87        body: JSON.stringify({ password: pwd }),
 88      })
 89        .then((response) => response.text())
 90        .then((data) => {
 91          console.log("Backend response:", data);
 92        })
 93        .catch((error) => {
 94          console.error("Error sending password to backend:", error);
 95        });
 96    }
 97  });
 98  
 99  function startCountdown() {
100    clearInterval(countdownInterval);
101    let timeLeft = 5;
102    expiryTimer.classList.remove("hidden");
103    secondsSpan.textContent = timeLeft;
104  
105    countdownInterval = setInterval(() => {
106      timeLeft--;
107      secondsSpan.textContent = timeLeft;
108      if (timeLeft <= 0) {
109        clearInterval(countdownInterval);
110        passwordOutput.textContent = "EXPIRED // REGEN";
111        expiryTimer.classList.add("hidden");
112      }
113    }, 1000);
114  }
115  
116  // History & Validation
117  function saveToHistory(text) {
118    historyData.unshift(text);
119    if (historyData.length > 5) historyData.pop();
120    renderHistory();
121  }
122  
123  function renderHistory() {
124    historyList.innerHTML = "";
125    historyData.forEach((pwd, index) => {
126      const li = document.createElement("li");
127      li.className =
128        "group bg-white p-3 neo-border neo-shadow-sm flex justify-between items-center cursor-pointer hover:bg-cyan-100 transition-all font-bold text-sm font-mono";
129  
130      const span = document.createElement("span");
131      span.className = "break-all pr-2";
132      span.textContent = pwd;
133  
134      li.addEventListener("click", (e) => {
135        if (e.target.tagName === "BUTTON") return;
136        startValidation(pwd);
137      });
138  
139      const delBtn = document.createElement("button");
140      delBtn.innerHTML = "DELETE";
141      delBtn.className =
142        "bg-black text-white text-[9px] px-2 py-1 font-black uppercase hover:bg-red-500 transition-colors";
143      delBtn.onclick = (e) => {
144        e.stopPropagation();
145        deleteItem(index);
146      };
147  
148      li.appendChild(span);
149      li.appendChild(delBtn);
150      historyList.appendChild(li);
151    });
152  }
153  
154  function startValidation(pwd) {
155    currentlyVerifying = pwd;
156    verifyInput.value = "";
157    verifyFeedback.textContent = "VERIFY_IDENTITY_";
158    verifyFeedback.className =
159      "text-[10px] mt-2 font-black uppercase text-black italic";
160    validationPanel.classList.remove("validator-hidden");
161    validationPanel.classList.add("validator-visible");
162    verifyInput.focus();
163  }
164  
165  verifyBtn.addEventListener("click", () => {
166    if (verifyInput.value === currentlyVerifying) {
167      verifyFeedback.textContent = "ACCESS_GRANTED // OK";
168      verifyFeedback.className =
169        "text-[10px] mt-2 font-black uppercase text-lime-600";
170      setTimeout(() => {
171        validationPanel.classList.add("validator-hidden");
172        validationPanel.classList.remove("validator-visible");
173      }, 1500);
174    } else {
175      verifyFeedback.textContent = "DENIED // TRY_AGAIN";
176      verifyFeedback.className =
177        "text-[10px] mt-2 font-black uppercase text-red-600";
178    }
179  });
180  
181  function deleteItem(index) {
182    historyData.splice(index, 1);
183    validationPanel.classList.add("validator-hidden");
184    renderHistory();
185  }
186  
187  clearHistoryBtn.addEventListener("click", () => {
188    if (confirm("NUKE ARCHIVE HISTORY?")) {
189      historyData = [];
190      validationPanel.classList.add("validator-hidden");
191      renderHistory();
192    }
193  });
194  
195  // Drag and Drop
196  passwordOutput.addEventListener("dragstart", (ev) => {
197    if (
198      passwordOutput.textContent === "Click Generate" ||
199      passwordOutput.textContent.includes("EXPIRED")
200    )
201      return;
202    ev.dataTransfer.setData("text", ev.target.textContent);
203    ev.target.classList.add("opacity-20");
204  });
205  
206  passwordOutput.addEventListener("dragend", (ev) => {
207    ev.target.classList.remove("opacity-20");
208  });
209  
210  dropZone.addEventListener("dragover", (ev) => {
211    ev.preventDefault();
212    dropZone.classList.add("bg-lime-200");
213  });
214  
215  dropZone.addEventListener("dragleave", () => {
216    dropZone.classList.remove("bg-lime-200");
217  });
218  
219  dropZone.addEventListener("drop", (ev) => {
220    ev.preventDefault();
221    dropZone.classList.remove("bg-lime-200");
222    const data = ev.dataTransfer.getData("text");
223  
224    if (data && data !== "Click Generate" && !data.includes("EXPIRED")) {
225      saveToHistory(data);
226    }
227  });