-
-
-
+
+
+
+
Your results will appear here
+
Adjust the settings, then generate one or more credentials.
-
-
-
-
+
+
+
+
+
+
+
+
+ function strengthSegments(strength) {
+ const counts = { Strong: 4, Good: 3, Fair: 2, Weak: 1 };
+ const track = document.createElement('span');
+ track.className = 'strength-track';
+ track.setAttribute('aria-label', `${strength} strength`);
+ const activeCount = counts[strength] || 1;
+ for (let index = 0; index < 4; index += 1) {
+ const segment = document.createElement('span');
+ segment.className = `strength-segment strength-${strength.toLowerCase()}`;
+ if (index < activeCount) segment.classList.add('is-on');
+ track.appendChild(segment);
+ }
+ return track;
+ }
+
+ async function copyPassword(password, copyButton) {
+ try {
+ await navigator.clipboard.writeText(password);
+ copyButton.textContent = 'Copied';
+ copyButton.classList.add('copied');
+ window.setTimeout(() => {
+ copyButton.textContent = 'Copy';
+ copyButton.classList.remove('copied');
+ }, 1600);
+ } catch {
+ const value = copyButton.previousElementSibling;
+ const range = document.createRange();
+ const selection = window.getSelection();
+ range.selectNodeContents(value);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ showError('Clipboard access was blocked. The password is selected; copy it manually.');
+ }
+ }
+
+ function validateResults(data) {
+ const strengths = new Set(['Strong', 'Good', 'Fair', 'Weak']);
+ if (!data || typeof data !== 'object' || !Array.isArray(data.passwords)
+ || data.passwords.length < 1 || data.passwords.length > 20) {
+ throw new Error('The generator returned an unexpected response.');
+ }
+ return data.passwords.map((item) => {
+ if (!item || typeof item.password !== 'string' || !strengths.has(item.strength)
+ || !['string', 'number'].includes(typeof item.entropy)) {
+ throw new Error('The generator returned an unexpected response.');
+ }
+ return { ...item, entropy: String(item.entropy) };
+ });
+ }
+
+ function renderResults(passwords) {
+ results.replaceChildren();
+ emptyState.hidden = passwords.length > 0;
+ if (!passwords.length) return;
+
+ const heading = document.createElement('h3');
+ heading.className = 'results-heading';
+ heading.textContent = `${passwords.length} generated ${passwords.length === 1 ? 'credential' : 'credentials'}`;
+ results.appendChild(heading);
+
+ passwords.forEach((password, index) => {
+ const item = document.createElement('article');
+ item.className = 'result-item';
+ item.style.animationDelay = `${Math.min(index * 35, 210)}ms`;
+
+ const topLine = document.createElement('div');
+ topLine.className = 'result-topline';
+
+ const number = document.createElement('span');
+ number.className = 'result-number';
+ number.textContent = String(index + 1).padStart(2, '0');
+
+ const badge = document.createElement('span');
+ badge.className = `badge badge-${password.strength.toLowerCase()}`;
+ badge.textContent = password.strength;
+ topLine.append(number, badge);
+
+ const passwordLine = document.createElement('div');
+ passwordLine.className = 'password-line';
+
+ const value = document.createElement('code');
+ value.className = 'password-value';
+ value.textContent = password.password;
+
+ const copyButton = document.createElement('button');
+ copyButton.className = 'copy-button';
+ copyButton.type = 'button';
+ copyButton.textContent = 'Copy';
+ copyButton.setAttribute('aria-label', `Copy generated credential ${index + 1}`);
+ copyButton.addEventListener('click', () => copyPassword(password.password, copyButton));
+ passwordLine.append(value, copyButton);
+
+ const meta = document.createElement('div');
+ meta.className = 'result-meta';
+ const entropy = document.createElement('span');
+ entropy.textContent = `${password.entropy} bits entropy`;
+ meta.append(entropy, strengthSegments(password.strength));
+
+ item.append(topLine, passwordLine, meta);
+ results.appendChild(item);
+ });
+ }
+
-
\ No newline at end of file
+