diff --git a/app/static/js/vault.js b/app/static/js/vault.js
index 85edd6e..9561861 100644
--- a/app/static/js/vault.js
+++ b/app/static/js/vault.js
@@ -187,11 +187,11 @@ const Vault = (() => {
function getSortedItems(items) {
const sorted = [...items];
switch (_sortOrder) {
- case 'name-asc': sorted.sort((a,b) => a.name.localeCompare(b.name)); break;
- case 'name-desc': sorted.sort((a,b) => b.name.localeCompare(a.name)); break;
- case 'date-desc': sorted.sort((a,b) => new Date(b.created_at) - new Date(a.created_at)); break;
- case 'date-asc': sorted.sort((a,b) => new Date(a.created_at) - new Date(b.created_at)); break;
- case 'folder': sorted.sort((a,b) => folderName(a.folder_id).localeCompare(folderName(b.folder_id)) || a.name.localeCompare(b.name)); break;
+ case 'name-asc': sorted.sort((a, b) => a.name.localeCompare(b.name)); break;
+ case 'name-desc': sorted.sort((a, b) => b.name.localeCompare(a.name)); break;
+ case 'date-desc': sorted.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); break;
+ case 'date-asc': sorted.sort((a, b) => new Date(a.created_at) - new Date(b.created_at)); break;
+ case 'folder': sorted.sort((a, b) => folderName(a.folder_id).localeCompare(folderName(b.folder_id)) || a.name.localeCompare(b.name)); break;
}
return sorted;
}
@@ -238,24 +238,24 @@ const Vault = (() => {
li.className = 'vault-item';
li.dataset.id = item.id;
- const iconMap = { password:'🔑', note:'📝', card:'💳', bank:'🏦', address:'🏠', ssn:'🪪', passkey:'🔐' };
+ const iconMap = { password: '🔑', note: '📝', card: '💳', bank: '🏦', address: '🏠', ssn: '🪪', passkey: '🔐' };
const icon = iconMap[item.item_type] || '🔑';
let subText = '';
if (item.plain) {
switch (item.item_type) {
case 'password': subText = item.plain.username || item.plain.url || ''; break;
- case 'note': subText = (item.plain.note_body || '').slice(0, 60); break;
- case 'card': subText = item.plain.card_number ? '•••• ' + String(item.plain.card_number).replace(/\s/g,'').slice(-4) : ''; break;
- case 'bank': subText = item.plain.bank_name || ''; break;
- case 'address': subText = [item.plain.first_name, item.plain.last_name].filter(Boolean).join(' '); break;
- case 'ssn': subText = '•••-••-••••'; break;
+ case 'note': subText = (item.plain.note_body || '').slice(0, 60); break;
+ case 'card': subText = item.plain.card_number ? '•••• ' + String(item.plain.card_number).replace(/\s/g, '').slice(-4) : ''; break;
+ case 'bank': subText = item.plain.bank_name || ''; break;
+ case 'address': subText = [item.plain.first_name, item.plain.last_name].filter(Boolean).join(' '); break;
+ case 'ssn': subText = '•••-••-••••'; break;
}
}
const showCopyUser = item.item_type === 'password';
- const showCopyPass = ['password','card','bank','ssn'].includes(item.item_type);
- const showLaunch = item.item_type === 'password' && item.plain?.url;
+ const showCopyPass = ['password', 'card', 'bank', 'ssn'].includes(item.item_type);
+ const showLaunch = item.item_type === 'password' && item.plain?.url;
li.innerHTML = `
${icon}
@@ -264,7 +264,7 @@ const Vault = (() => {
${escHtml(subText)}
- ${showLaunch ? `` : ''}
+ ${showLaunch ? `` : ''}
${showCopyUser ? `` : ''}
${showCopyPass ? `` : ''}
@@ -289,8 +289,8 @@ const Vault = (() => {
e.stopPropagation();
const s = item.item_type === 'password' ? item.plain?.password
: item.item_type === 'card' ? item.plain?.cvv
- : item.item_type === 'bank' ? item.plain?.account_number
- : item.plain?.ssn_number;
+ : item.item_type === 'bank' ? item.plain?.account_number
+ : item.plain?.ssn_number;
copyToClipboard(s || '', 'Copied to clipboard');
});
}
@@ -328,7 +328,7 @@ const Vault = (() => {
const old = pwItems.filter(i => new Date(i.created_at).getTime() < cutoff);
const total = pwItems.length;
- const score = Math.max(0, Math.round(100 - (weak.length/total)*40 - (reused.length/total)*30 - (old.length/total)*15));
+ const score = Math.max(0, Math.round(100 - (weak.length / total) * 40 - (reused.length / total) * 30 - (old.length / total) * 15));
const cls = score >= 80 ? 'score-good' : score >= 50 ? 'score-fair' : 'score-poor';
const label = score >= 80 ? 'Good' : score >= 50 ? 'Fair' : 'Needs attention';
@@ -473,9 +473,9 @@ const Vault = (() => {
From ${escHtml(s.owner_email)}
${!s.accepted
- ? ``
- : ``
- }
+ ? ``
+ : ``
+ }
`).join('');
ul.querySelectorAll('[data-accept]').forEach(btn => {
@@ -836,8 +836,8 @@ const Vault = (() => {
${hasFields
- ? '
'
- : '
Could not decrypt this item.
'}
+ ? '
'
+ : '
Could not decrypt this item.
'}
`;
}).join('');
@@ -866,18 +866,18 @@ const Vault = (() => {
// ── Password Generator ────────────────────────────────────────────────
const GEN_CHARSETS = {
- upper: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
- lower: 'abcdefghijklmnopqrstuvwxyz',
- digits: '0123456789',
+ upper: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ lower: 'abcdefghijklmnopqrstuvwxyz',
+ digits: '0123456789',
symbols: '!@#$%^&*()-_=+[]{}|;:,.<>?',
};
function generatePassword(length = 20, opts = { upper: true, lower: true, digits: true, symbols: true }) {
let pool = '';
const required = [];
- if (opts.upper) { pool += GEN_CHARSETS.upper; required.push(GEN_CHARSETS.upper); }
- if (opts.lower) { pool += GEN_CHARSETS.lower; required.push(GEN_CHARSETS.lower); }
- if (opts.digits) { pool += GEN_CHARSETS.digits; required.push(GEN_CHARSETS.digits); }
+ if (opts.upper) { pool += GEN_CHARSETS.upper; required.push(GEN_CHARSETS.upper); }
+ if (opts.lower) { pool += GEN_CHARSETS.lower; required.push(GEN_CHARSETS.lower); }
+ if (opts.digits) { pool += GEN_CHARSETS.digits; required.push(GEN_CHARSETS.digits); }
if (opts.symbols) { pool += GEN_CHARSETS.symbols; required.push(GEN_CHARSETS.symbols); }
if (!pool) pool = GEN_CHARSETS.lower;
@@ -902,7 +902,7 @@ const Vault = (() => {
function scorePassword(pw) {
if (!pw) return { score: 0, label: '', cls: '' };
let score = 0;
- if (pw.length >= 8) score++;
+ if (pw.length >= 8) score++;
if (pw.length >= 12) score++;
if (pw.length >= 16) score++;
if (/[A-Z]/.test(pw)) score++;
@@ -912,7 +912,7 @@ const Vault = (() => {
// clamp to 5 bands
const band = Math.min(5, Math.ceil(score / 7 * 5));
const labels = ['', 'Very Weak', 'Weak', 'Fair', 'Strong', 'Very Strong'];
- const clses = ['', 'strength-1','strength-2','strength-3','strength-4','strength-5'];
+ const clses = ['', 'strength-1', 'strength-2', 'strength-3', 'strength-4', 'strength-5'];
return { score: band, label: labels[band], cls: clses[band] };
}
@@ -995,16 +995,16 @@ const Vault = (() => {
`;
document.body.appendChild(modal);
- const lengthSlider = document.getElementById('gen-length');
+ const lengthSlider = document.getElementById('gen-length');
const lengthDisplay = document.getElementById('gen-length-display');
- const output = document.getElementById('gen-output');
- const strengthEl = document.getElementById('gen-strength');
+ const output = document.getElementById('gen-output');
+ const strengthEl = document.getElementById('gen-strength');
function refreshGen() {
const pw = generatePassword(parseInt(lengthSlider.value), {
- upper: document.getElementById('gen-upper').checked,
- lower: document.getElementById('gen-lower').checked,
- digits: document.getElementById('gen-digits').checked,
+ upper: document.getElementById('gen-upper').checked,
+ lower: document.getElementById('gen-lower').checked,
+ digits: document.getElementById('gen-digits').checked,
symbols: document.getElementById('gen-symbols').checked,
});
output.value = pw;
@@ -1017,7 +1017,7 @@ const Vault = (() => {
lengthDisplay.textContent = lengthSlider.value;
refreshGen();
});
- ['gen-upper','gen-lower','gen-digits','gen-symbols'].forEach(id => {
+ ['gen-upper', 'gen-lower', 'gen-digits', 'gen-symbols'].forEach(id => {
document.getElementById(id).addEventListener('change', refreshGen);
});
document.getElementById('btn-regenerate').addEventListener('click', refreshGen);
@@ -1036,9 +1036,9 @@ const Vault = (() => {
const lengthSlider = document.getElementById('gen-length');
document.getElementById('gen-length-display').textContent = lengthSlider.value;
const pw = generatePassword(parseInt(lengthSlider.value), {
- upper: document.getElementById('gen-upper').checked,
- lower: document.getElementById('gen-lower').checked,
- digits: document.getElementById('gen-digits').checked,
+ upper: document.getElementById('gen-upper').checked,
+ lower: document.getElementById('gen-lower').checked,
+ digits: document.getElementById('gen-digits').checked,
symbols: document.getElementById('gen-symbols').checked,
});
document.getElementById('gen-output').value = pw;
@@ -1269,7 +1269,7 @@ const Vault = (() => {
// ── Item Modal ────────────────────────────────────────────────────────────
- const TYPE_LABELS = { password:'🔑 Password', note:'📝 Secure Note', card:'💳 Payment Card', bank:'🏦 Bank Account', address:'🏠 Address', ssn:'🪪 Identity (SSN)' };
+ const TYPE_LABELS = { password: '🔑 Password', note: '📝 Secure Note', card: '💳 Payment Card', bank: '🏦 Bank Account', address: '🏠 Address', ssn: '🪪 Identity (SSN)' };
function switchModalType(type) {
document.querySelectorAll('.type-fields').forEach(el => {
@@ -1307,19 +1307,19 @@ const Vault = (() => {
if (item.plain) {
const p = item.plain;
switch (t) {
- case 'password': setVal('field-url',p.url); setVal('field-username',p.username); setVal('field-password',p.password); setVal('field-notes',p.notes); break;
- case 'note': setVal('field-note-body',p.note_body); break;
- case 'card': setVal('field-cardholder',p.cardholder_name); setVal('field-card-number',p.card_number); setVal('field-expiry-month',p.expiry_month); setVal('field-expiry-year',p.expiry_year); setVal('field-cvv',p.cvv); setVal('field-notes',p.notes); break;
- case 'bank': setVal('field-bank-name',p.bank_name); setVal('field-account-type',p.account_type); setVal('field-routing',p.routing_number); setVal('field-account-number',p.account_number); setVal('field-notes',p.notes); break;
- case 'address': ['first_name','last_name','company','address_line','city','state','zip','country','phone','email'].forEach(k => setVal('field-'+k.replace(/_/g,'-'), p[k])); setVal('field-notes',p.notes); break;
- case 'ssn': setVal('field-ssn-number',p.ssn_number); setVal('field-notes',p.notes); break;
+ case 'password': setVal('field-url', p.url); setVal('field-username', p.username); setVal('field-password', p.password); setVal('field-notes', p.notes); break;
+ case 'note': setVal('field-note-body', p.note_body); break;
+ case 'card': setVal('field-cardholder', p.cardholder_name); setVal('field-card-number', p.card_number); setVal('field-expiry-month', p.expiry_month); setVal('field-expiry-year', p.expiry_year); setVal('field-cvv', p.cvv); setVal('field-notes', p.notes); break;
+ case 'bank': setVal('field-bank-name', p.bank_name); setVal('field-account-type', p.account_type); setVal('field-routing', p.routing_number); setVal('field-account-number', p.account_number); setVal('field-notes', p.notes); break;
+ case 'address': ['first_name', 'last_name', 'company', 'address_line', 'city', 'state', 'zip', 'country', 'phone', 'email'].forEach(k => setVal('field-' + k.replace(/_/g, '-'), p[k])); setVal('field-notes', p.notes); break;
+ case 'ssn': setVal('field-ssn-number', p.ssn_number); setVal('field-notes', p.notes); break;
}
}
}
document.getElementById('item-modal').classList.add('open');
document.getElementById('field-name').focus();
if ((mode === 'add' && (document.getElementById('field-type').value || 'password') === 'password') ||
- (mode === 'edit' && (item?.item_type || 'password') === 'password')) {
+ (mode === 'edit' && (item?.item_type || 'password') === 'password')) {
initPasswordFieldEnhancements();
}
}
@@ -1395,7 +1395,7 @@ const Vault = (() => {
method: 'POST',
headers: { 'Authorization': `Bearer ${sessionStorage.getItem('access_token') || ''}`, 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken() },
body: JSON.stringify({ refresh_token: refreshToken }),
- }).catch(() => {});
+ }).catch(() => { });
VaultSession.clear();
SharingSession.clear();
sessionStorage.removeItem('access_token');
@@ -1407,15 +1407,15 @@ const Vault = (() => {
// ── Utilities ─────────────────────────────────────────────────────────────
function copyToClipboard(text, msg = 'Copied') {
- navigator.clipboard.writeText(text).then(() => showToast(msg)).catch(() => {});
+ navigator.clipboard.writeText(text).then(() => showToast(msg)).catch(() => { });
}
function escHtml(str) {
- return String(str).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"');
+ return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"');
}
function itemIcon(type) {
- return { password:'🔑', note:'📝', card:'💳', bank:'🏦', address:'🏠', ssn:'🪪' }[type] || '🔑';
+ return { password: '🔑', note: '📝', card: '💳', bank: '🏦', address: '🏠', ssn: '🪪' }[type] || '🔑';
}
function showToast(message, type = 'success') {
@@ -1524,7 +1524,7 @@ const Vault = (() => {
document.querySelectorAll('[data-type-filter]').forEach(el => {
el.addEventListener('click', () => {
const type = el.dataset.typeFilter;
- const labels = { password:'Passwords', note:'Secure Notes', card:'Payment Cards', bank:'Bank Accounts', address:'Addresses', ssn:'Identities' };
+ const labels = { password: 'Passwords', note: 'Secure Notes', card: 'Payment Cards', bank: 'Bank Accounts', address: 'Addresses', ssn: 'Identities' };
_activeFilter = { type: 'itemType', value: type };
switchView('vault');
document.querySelectorAll('.sidebar-item').forEach(s => s.classList.remove('active'));
@@ -1534,8 +1534,8 @@ const Vault = (() => {
});
});
- document.getElementById('item-modal')?.addEventListener('click', e => { if (e.target.id === 'item-modal') closeModal(); });
- document.addEventListener('keydown', e => { if (e.key === 'Escape') { closeModal(); closeModal('share-modal'); closeModal('emergency-modal'); closeModal('settings-modal'); closeModal('detail-modal'); closeModal('gen-modal'); } });
+ document.getElementById('item-modal')?.addEventListener('click', e => { /* backdrop click intentionally disabled — modal closes only via Cancel or Save */ });
+ document.addEventListener('keydown', e => { if (e.key === 'Escape') { closeModal('share-modal'); closeModal('emergency-modal'); closeModal('settings-modal'); closeModal('detail-modal'); closeModal('gen-modal'); } });
// Detail modal (shared item view)
const closeDetailModal = () => closeModal('detail-modal');
@@ -1544,8 +1544,8 @@ const Vault = (() => {
document.getElementById('detail-modal')?.addEventListener('click', e => { if (e.target.id === 'detail-modal') closeDetailModal(); });
document.getElementById('field-card-number')?.addEventListener('input', e => {
- let v = e.target.value.replace(/\D/g,'').slice(0,16);
- e.target.value = v.replace(/(.{4})/g,'$1 ').trim();
+ let v = e.target.value.replace(/\D/g, '').slice(0, 16);
+ e.target.value = v.replace(/(.{4})/g, '$1 ').trim();
});
// Settings modal
@@ -1606,4 +1606,4 @@ const Vault = (() => {
return { init };
})();
-document.addEventListener('DOMContentLoaded', Vault.init);
+document.addEventListener('DOMContentLoaded', Vault.init);
\ No newline at end of file