revamped CSS - using Tailwind now, update layout and added home page

This commit is contained in:
nahakubuilde
2025-07-19 12:58:22 +01:00
parent 20cfcd1829
commit 173569365b
17 changed files with 4318 additions and 8950 deletions

View File

@@ -3,123 +3,195 @@
{{define "title"}}Password Generator - HeaderAnalyzer{{end}}
{{define "content"}}
<div class="password-generator">
<h1>🔐 Password Generator</h1>
<div class="container mx-auto px-4 py-8 max-w-4xl">
<div class="text-center mb-8">
<a href="/pwgenerator" class="inline-block">
<h1 class="text-2xl md:text-3xl font-bold text-gray-100 hover:text-blue-400 transition-colors cursor-pointer mb-4">
🔐 Password Generator
</h1>
</a>
</div>
<!-- Hidden CSRF token for API calls -->
<input type="hidden" id="csrfToken" value="{{.CSRFToken}}">
<div class="tab-buttons">
<button class="tab-btn" id="randomTab">Random Password</button>
<button class="tab-btn active" id="passphraseTab">Passphrase</button>
<!-- Tab Buttons -->
<div class="flex space-x-2 mb-6 bg-gray-800 p-2 rounded-lg border border-gray-700">
<button class="flex-1 py-3 px-4 rounded-lg text-center font-medium transition-colors bg-gray-700 text-gray-300 hover:bg-gray-600" id="randomTab">
🎲 Random Password
</button>
<button class="flex-1 py-3 px-4 rounded-lg text-center font-medium transition-colors bg-blue-600 text-white" id="passphraseTab">
📝 Passphrase
</button>
</div>
<div class="password-output">
<div class="password-text" id="passwordDisplay">Click "Generate Password" to create a secure password</div>
<button class="copy-btn" id="copyBtn" onclick="copyPassword()" style="display: none;">Copy to Clipboard</button>
</div>
<button class="generate-btn" onclick="generatePassword()">🎲 Generate Password</button>
<div class="controls">
<div class="control-group">
<h3>🔧 Basic Settings</h3>
<div class="form-row">
<label for="length">Password Length:</label>
<input type="number" id="length" min="4" max="128" value="{{.Config.Length}}" onchange="updateURL()">
<!-- Password Output -->
<!-- Generated Password Display -->
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700 mb-8">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-4">
<h2 class="text-xl font-semibold text-gray-200">🔐 Generated Password</h2>
<div class="flex items-center gap-4 text-sm">
<span id="characterCount" class="text-gray-400 bg-gray-700 px-3 py-1 rounded-full">
0 characters
</span>
<span id="strengthDisplay" class="text-gray-400 bg-gray-700 px-3 py-1 rounded-full">
Not generated
</span>
</div>
<div class="form-row">
<label for="includeUpper">Include Uppercase (A-Z):</label>
<input type="checkbox" id="includeUpper" {{if .Config.IncludeUpper}}checked{{end}} onchange="updateURL()">
</div>
<div class="form-row">
<label for="includeLower">Include Lowercase (a-z):</label>
<input type="checkbox" id="includeLower" {{if .Config.IncludeLower}}checked{{end}} onchange="updateURL()">
</div>
<div class="form-row">
<label for="numberCount">Number of Digits:</label>
<input type="number" id="numberCount" min="0" max="20" value="{{.Config.NumberCount}}" onchange="updateURL()">
</div>
<div class="form-row">
<label for="specialChars">Special Characters:</label>
<input type="text" id="specialChars" value="{{.Config.SpecialChars}}" onchange="updateURL()">
</div>
<div class="form-row">
<label for="noConsecutive">No consecutive identical characters:</label>
<input type="checkbox" id="noConsecutive" {{if .Config.NoConsecutive}}checked{{end}} onchange="updateURL()">
</div>
</div>
<div class="control-group">
<h3>🎯 Advanced Settings</h3>
<div class="passphrase-controls {{if eq .Config.Type "passphrase"}}active{{end}}" id="passphraseControls">
<div class="form-row">
<label for="wordCount">Number of Words:</label>
<input type="number" id="wordCount" min="2" max="10" value="{{.Config.WordCount}}" onchange="updateURL()">
</div>
<div class="form-row">
<label for="passphraseUseNumbers">Include Numbers:</label>
<input type="checkbox" id="passphraseUseNumbers" {{if .Config.UseNumbers}}checked{{end}} onchange="updateURL()">
</div>
<div class="form-row">
<label for="passphraseUseSpecial">Include Special Characters:</label>
<input type="checkbox" id="passphraseUseSpecial" {{if .Config.UseSpecial}}checked{{end}} onchange="updateURL()">
</div>
<div class="form-row">
<label for="numberPosition">Number Position:</label>
<select id="numberPosition" onchange="updateURL()">
<option value="end" {{if eq .Config.NumberPosition "end"}}selected{{end}}>At End</option>
<option value="start" {{if eq .Config.NumberPosition "start"}}selected{{end}}>At Start</option>
<option value="each" {{if eq .Config.NumberPosition "each"}}selected{{end}}>After Each Word</option>
</select>
</div>
</div>
<div class="form-row">
<label>Strength Indicator:</label>
<div id="strengthIndicator" style="color: #999;">Generate a password to see strength</div>
</div>
<div class="form-row">
<label>Word List Status:</label>
<div id="wordListStatus" style="color: #999; font-size: 12px;">Loading...</div>
</div>
</div>
</div>
<!-- Settings Management -->
<div class="control-group" style="margin-top: 20px;">
<h3>💾 Settings & History</h3>
<div class="form-row">
<button onclick="saveSettings()" style="background: #007acc; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; margin-right: 10px;">Save Current Settings</button>
<button onclick="loadSettingsManual()" style="background: #6c757d; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; margin-right: 10px;">Load Saved Settings</button>
<button onclick="clearSettings()" style="background: #dc3545; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer;">Clear Saved Settings</button>
</div>
<div class="form-row">
<label for="savePasswords">Save Generated Passwords (in web browser cookies only):</label>
<input type="checkbox" id="savePasswords" {{if .Config.SavePasswords}}checked{{end}} onchange="togglePasswordSaving(); updateURL()">
<div class="relative">
<input type="text" id="generatedPassword" readonly
class="w-full p-4 bg-gray-900 border border-gray-600 rounded-lg text-gray-100 font-mono text-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Click 'Generate Password' to create a new password">
<button onclick="copyPassword()"
class="absolute right-2 top-1/2 transform -translate-y-1/2 bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500/20">
📋 Copy
</button>
</div>
</div>
<!-- Generate Button -->
<div class="flex flex-col sm:flex-row items-center justify-center gap-4 mb-8">
<button onclick="generatePassword()"
class="bg-green-600 hover:bg-green-700 text-white font-bold py-4 px-8 rounded-lg text-lg transition-all duration-200 transform hover:scale-105">
🎲 Generate Password
</button>
<button onclick="copyCurrentURL()"
class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-4 px-6 rounded-lg transition-colors duration-200">
🔗 Copy URL with Settings
</button>
<button onclick="resetAllSettings()"
class="bg-red-600 hover:bg-red-700 text-white font-medium py-4 px-6 rounded-lg transition-colors duration-200">
🔄 Reset
</button>
</div>
<!-- Controls -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
<!-- Basic Settings -->
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
<h3 class="text-xl font-semibold text-gray-200 mb-4">🔧 Basic Settings</h3>
<div class="space-y-4">
<!-- Save Passwords Option (moved from Settings Management) -->
<div class="p-3 bg-gray-900 rounded-lg border border-gray-600">
<div class="flex items-center space-x-3">
<input type="checkbox" id="savePasswords" {{if .Config.SavePasswords}}checked{{end}} onchange="togglePasswordSaving(); autoSaveSettings()"
class="w-4 h-4 text-blue-600 bg-gray-700 border-gray-600 rounded focus:ring-blue-500 focus:ring-2">
<label for="savePasswords" class="text-sm text-gray-300">
Save Generated Passwords (in web browser cookies only)
</label>
</div>
</div>
<div>
<label for="length" class="block text-sm font-medium text-gray-300 mb-2">Password Length:</label>
<input type="number" id="length" min="4" max="128" value="{{.Config.Length}}" onchange="updateURL(); autoSaveSettings()"
class="w-full px-3 py-2 bg-gray-900 border border-gray-600 rounded-lg text-gray-100 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 focus:outline-none">
</div>
<div class="flex items-center space-x-3">
<input type="checkbox" id="includeUpper" {{if .Config.IncludeUpper}}checked{{end}} onchange="updateURL(); autoSaveSettings()"
class="w-4 h-4 text-blue-600 bg-gray-700 border-gray-600 rounded focus:ring-blue-500 focus:ring-2">
<label for="includeUpper" class="text-sm text-gray-300">Include Uppercase (A-Z)</label>
</div>
<div class="flex items-center space-x-3">
<input type="checkbox" id="includeLower" {{if .Config.IncludeLower}}checked{{end}} onchange="updateURL(); autoSaveSettings()"
class="w-4 h-4 text-blue-600 bg-gray-700 border-gray-600 rounded focus:ring-blue-500 focus:ring-2">
<label for="includeLower" class="text-sm text-gray-300">Include Lowercase (a-z)</label>
</div>
<div>
<label for="numberCount" class="block text-sm font-medium text-gray-300 mb-2">Number of Digits:</label>
<input type="number" id="numberCount" min="0" max="20" value="{{.Config.NumberCount}}" onchange="updateURL(); autoSaveSettings()"
class="w-full px-3 py-2 bg-gray-900 border border-gray-600 rounded-lg text-gray-100 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 focus:outline-none">
</div>
<div>
<label for="specialChars" class="block text-sm font-medium text-gray-300 mb-2">Special Characters:</label>
<input type="text" id="specialChars" value="{{.Config.SpecialChars}}"
onchange="validateSpecialChars(); updateURL(); autoSaveSettings()"
oninput="validateSpecialChars()"
pattern="[!@#$%&*\-_=+.]*"
title="Only these special characters are allowed: !@#$%&*-_=+."
class="w-full px-3 py-2 bg-gray-900 border border-gray-600 rounded-lg text-gray-100 font-mono focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 focus:outline-none">
<div id="specialCharsError" class="text-red-400 text-sm mt-1 hidden">
Only these special characters are allowed: !@#$%&*-_=+.
</div>
<div class="text-gray-500 text-xs mt-1">
Allowed: !@#$%&*-_=+.
</div>
</div>
<div class="flex items-center space-x-3">
<input type="checkbox" id="noConsecutive" {{if .Config.NoConsecutive}}checked{{end}} onchange="updateURL(); autoSaveSettings()"
class="w-4 h-4 text-blue-600 bg-gray-700 border-gray-600 rounded focus:ring-blue-500 focus:ring-2">
<label for="noConsecutive" class="text-sm text-gray-300">No consecutive identical characters</label>
</div>
</div>
</div>
<!-- Advanced Settings -->
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
<h3 class="text-xl font-semibold text-gray-200 mb-4">🎯 Advanced Settings</h3>
<div class="space-y-4">
<!-- Passphrase Controls -->
<div class="{{if eq .Config.Type "passphrase"}}block{{else}}hidden{{end}}" id="passphraseControls">
<div class="space-y-4 p-4 bg-gray-900 rounded-lg border border-gray-600">
<h4 class="text-lg font-medium text-gray-200">📝 Passphrase Options</h4>
<div>
<label for="wordCount" class="block text-sm font-medium text-gray-300 mb-2">Number of Words:</label>
<input type="number" id="wordCount" min="2" max="10" value="{{.Config.WordCount}}" onchange="updateURL()"
class="w-full px-3 py-2 bg-gray-800 border border-gray-600 rounded-lg text-gray-100 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 focus:outline-none">
</div>
<div class="flex items-center space-x-3">
<input type="checkbox" id="passphraseUseNumbers" {{if .Config.UseNumbers}}checked{{end}} onchange="updateURL()"
class="w-4 h-4 text-blue-600 bg-gray-700 border-gray-600 rounded focus:ring-blue-500 focus:ring-2">
<label for="passphraseUseNumbers" class="text-sm text-gray-300">Include Numbers</label>
</div>
<div class="flex items-center space-x-3">
<input type="checkbox" id="passphraseUseSpecial" {{if .Config.UseSpecial}}checked{{end}} onchange="updateURL()"
class="w-4 h-4 text-blue-600 bg-gray-700 border-gray-600 rounded focus:ring-blue-500 focus:ring-2">
<label for="passphraseUseSpecial" class="text-sm text-gray-300">Include Special Characters</label>
</div>
<div>
<label for="numberPosition" class="block text-sm font-medium text-gray-300 mb-2">Number Position:</label>
<select id="numberPosition" onchange="updateURL()"
class="w-full px-3 py-2 bg-gray-800 border border-gray-600 rounded-lg text-gray-100 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 focus:outline-none">
<option value="end" {{if eq .Config.NumberPosition "end"}}selected{{end}}>At End</option>
<option value="start" {{if eq .Config.NumberPosition "start"}}selected{{end}}>At Start</option>
<option value="each" {{if eq .Config.NumberPosition "each"}}selected{{end}}>After Each Word</option>
</select>
</div>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Strength Indicator:</label>
<div id="strengthIndicator" class="text-gray-400 p-3 bg-gray-900 rounded-lg border border-gray-600">
Generate a password to see strength
</div>
</div>
</div>
</div>
</div>
<!-- Password History -->
<div class="control-group" style="margin-top: 20px;">
<h3>📚 Password History</h3>
<div id="passwordHistory" style="max-height: 200px; overflow-y: auto; background: #1a1a1a; border: 1px solid #333; border-radius: 4px; padding: 10px;">
<p style="color: #999; font-style: italic;">No passwords generated yet</p>
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700 mt-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-xl font-semibold text-gray-200">📚 Password History</h3>
<button onclick="clearHistory()"
class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white font-medium rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-red-500/20">
🗑️ Clear History
</button>
</div>
<div class="form-row" style="margin-top: 10px;">
<button onclick="clearHistory()" style="background: #dc3545; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer;">Clear History</button>
<div id="passwordHistory" class="bg-gray-900 border border-gray-600 rounded-lg p-4">
<p class="text-gray-400 italic">No passwords generated yet</p>
</div>
</div>
</div>
@@ -142,63 +214,101 @@ document.addEventListener('DOMContentLoaded', function() {
// Load password history
loadPasswordHistory();
// Load word list info
loadWordListInfo();
// Auto-generate if URL has parameters (excluding default)
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.toString()) {
generatePassword();
}
// Add event listeners to automatically save settings on change
document.querySelectorAll('input, select').forEach(element => {
element.addEventListener('change', function() {
updateURL();
saveSettings();
});
});
// Note: Removed auto-save event listeners to prevent excessive saving notifications
});
// Tab switching
function switchTab(mode) {
currentMode = mode;
document.getElementById('randomTab').classList.toggle('active', mode === 'random');
document.getElementById('passphraseTab').classList.toggle('active', mode === 'passphrase');
document.getElementById('passphraseControls').classList.toggle('active', mode === 'passphrase');
// Get tab elements
const randomTab = document.getElementById('randomTab');
const passphraseTab = document.getElementById('passphraseTab');
// Remove active classes from both tabs
randomTab.classList.remove('bg-blue-600', 'text-white');
randomTab.classList.add('bg-gray-700', 'text-gray-300', 'hover:bg-gray-600');
passphraseTab.classList.remove('bg-blue-600', 'text-white');
passphraseTab.classList.add('bg-gray-700', 'text-gray-300', 'hover:bg-gray-600');
// Add active classes to the selected tab
if (mode === 'random') {
randomTab.classList.remove('bg-gray-700', 'text-gray-300', 'hover:bg-gray-600');
randomTab.classList.add('bg-blue-600', 'text-white');
} else {
passphraseTab.classList.remove('bg-gray-700', 'text-gray-300', 'hover:bg-gray-600');
passphraseTab.classList.add('bg-blue-600', 'text-white');
}
// Show/hide passphrase controls
document.getElementById('passphraseControls').classList.toggle('block', mode === 'passphrase');
document.getElementById('passphraseControls').classList.toggle('hidden', mode !== 'passphrase');
updateURL();
saveSettings();
autoSaveSettings(); // Auto-save without notification
}
document.getElementById('randomTab').addEventListener('click', () => switchTab('random'));
document.getElementById('passphraseTab').addEventListener('click', () => switchTab('passphrase'));
// Validate special characters input
function validateSpecialChars() {
const input = document.getElementById('specialChars');
const errorDiv = document.getElementById('specialCharsError');
const allowedChars = '!@#$%&*-_=+.';
const value = input.value;
let isValid = true;
for (let i = 0; i < value.length; i++) {
if (!allowedChars.includes(value[i])) {
isValid = false;
break;
}
}
if (isValid) {
input.classList.remove('border-red-500', 'focus:border-red-500', 'focus:ring-red-500/20');
input.classList.add('border-gray-600', 'focus:border-blue-500', 'focus:ring-blue-500/20');
errorDiv.classList.add('hidden');
} else {
input.classList.remove('border-gray-600', 'focus:border-blue-500', 'focus:ring-blue-500/20');
input.classList.add('border-red-500', 'focus:border-red-500', 'focus:ring-red-500/20');
errorDiv.classList.remove('hidden');
}
return isValid;
}
// URL parameter management
function updateURL() {
const config = getCurrentConfig();
const params = new URLSearchParams();
// Define default values
// Define default values (savePasswords excluded from URL parameters)
const defaults = {
type: "passphrase",
length: 12,
includeUpper: true,
includeLower: true,
numberCount: 1,
specialChars: "!@#$%^&*-_=+",
specialChars: "!@#$%&*-_=+.",
noConsecutive: false,
wordCount: 3,
useNumbers: true,
useSpecial: false,
numberPosition: "end",
savePasswords: false
numberPosition: "end"
};
// Only add parameters that differ from defaults
// Only add parameters that differ from defaults (excluding savePasswords)
Object.keys(config).forEach(key => {
if (config[key] !== defaults[key]) {
if (key !== 'savePasswords' && config[key] !== defaults[key]) {
params.set(key, config[key]);
}
});
@@ -241,19 +351,71 @@ function saveSettings() {
showNotification('Settings saved! They will be remembered when you visit this page again.', 'success');
}
// Auto-save function without showing notification
function autoSaveSettings() {
const config = getCurrentConfig();
config.mode = currentMode;
const settings = JSON.stringify(config);
// Set cookie to expire in 1 year
const expiryDate = new Date();
expiryDate.setFullYear(expiryDate.getFullYear() + 1);
document.cookie = `passwordGenSettings=${encodeURIComponent(settings)}; expires=${expiryDate.toUTCString()}; path=/`;
}
// Copy current URL with settings
function copyCurrentURL() {
updateURL(); // Ensure URL is up to date
const currentURL = window.location.href;
navigator.clipboard.writeText(currentURL).then(function() {
showPopup('URL with current settings copied to clipboard!', 'success');
}, function(err) {
console.error('Could not copy URL: ', err);
showPopup('Failed to copy URL to clipboard', 'error');
});
}
// Reset all settings and cookies
function resetAllSettings() {
showConfirmationPopup(
'Reset All Settings?',
'This will clear all saved settings and password history, returning the page to its default state.',
function() {
// Clear all cookies
document.cookie = 'passwordGenSettings=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
document.cookie = 'passwordHistory=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
showPopup('All settings and history cleared. Redirecting to clean page...', 'info');
setTimeout(() => {
// Redirect to the page without any parameters
window.location.href = window.location.pathname;
}, 1500);
}
);
}
function loadSettings() {
// First try URL parameters
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.toString()) {
const config = {};
for (const [key, value] of urlParams) {
// Skip savePasswords from URL parameters - it's cookie-only
if (key === 'savePasswords') continue;
if (key === 'type') config[key] = value;
else if (key === 'length' || key === 'numberCount' || key === 'wordCount') config[key] = parseInt(value);
else if (key === 'includeUpper' || key === 'includeLower' || key === 'noConsecutive' ||
key === 'useNumbers' || key === 'useSpecial' || key === 'savePasswords') config[key] = value === 'true';
key === 'useNumbers' || key === 'useSpecial') config[key] = value === 'true';
else config[key] = value;
}
applyConfig(config);
// Load savePasswords setting separately from cookies only
loadSavePasswordsSetting();
return;
}
@@ -269,25 +431,82 @@ function loadSettings() {
}
}
function loadSettingsManual() {
// Load savePasswords setting from cookies only (never from URL)
function loadSavePasswordsSetting() {
const settings = getCookie('passwordGenSettings');
if (settings) {
try {
const config = JSON.parse(decodeURIComponent(settings));
applyConfig(config);
updateURL();
showNotification('Settings loaded successfully!', 'success');
if (config.savePasswords !== undefined) {
document.getElementById('savePasswords').checked = config.savePasswords;
// Update history display based on the setting
if (config.savePasswords) {
const history = getPasswordHistory();
displayPasswordHistory(history);
} else {
document.getElementById('passwordHistory').innerHTML = '<p style="color: #999; font-style: italic;">Password saving is disabled</p>';
}
}
} catch (e) {
showNotification('Error loading settings: ' + e.message, 'error');
console.error('Failed to parse saved settings for savePasswords:', e);
}
} else {
showNotification('No saved settings found.', 'warning');
}
}
function clearSettings() {
document.cookie = 'passwordGenSettings=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
showNotification('Saved settings cleared.', 'info');
// Custom confirmation popup function
function showConfirmationPopup(title, message, onConfirm) {
// Create backdrop
const backdrop = document.createElement('div');
backdrop.className = 'fixed inset-0 z-50 bg-black bg-opacity-50 backdrop-blur-sm flex items-center justify-center p-4';
// Create popup
backdrop.innerHTML = `
<div class="bg-gray-800 border border-gray-600 rounded-xl p-6 max-w-md w-full shadow-2xl transform transition-all">
<div class="flex items-center justify-between mb-4">
<h3 class="text-xl font-bold text-red-400">${title}</h3>
<button onclick="this.closest('.fixed').remove()" class="text-gray-400 hover:text-gray-200 transition-colors">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<p class="text-gray-300 mb-6">${message}</p>
<div class="flex space-x-3 justify-end">
<button onclick="this.closest('.fixed').remove()"
class="px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-lg transition-colors">
Cancel
</button>
<button onclick="confirmAction()"
class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg transition-colors">
Yes, Clear All
</button>
</div>
</div>
`;
// Add confirm action to the backdrop element
backdrop.confirmCallback = onConfirm;
// Add global confirmAction function temporarily
window.confirmAction = function() {
backdrop.confirmCallback();
backdrop.remove();
delete window.confirmAction;
};
// Add to page
document.body.appendChild(backdrop);
// Close on backdrop click
backdrop.addEventListener('click', function(e) {
if (e.target === backdrop) {
backdrop.remove();
delete window.confirmAction;
}
});
}
function applyConfig(config) {
@@ -298,7 +517,7 @@ function applyConfig(config) {
document.getElementById('includeUpper').checked = config.includeUpper !== false;
document.getElementById('includeLower').checked = config.includeLower !== false;
document.getElementById('numberCount').value = config.numberCount || 1;
document.getElementById('specialChars').value = config.specialChars || "!@#$%^&*-_=+";
document.getElementById('specialChars').value = config.specialChars || "!@#$%&*-_=+.";
document.getElementById('noConsecutive').checked = config.noConsecutive || false;
document.getElementById('wordCount').value = config.wordCount || 3;
document.getElementById('passphraseUseNumbers').checked = config.useNumbers !== false;
@@ -306,16 +525,8 @@ function applyConfig(config) {
document.getElementById('numberPosition').value = config.numberPosition || "end";
document.getElementById('savePasswords').checked = config.savePasswords || false;
// Update tab state
if (currentMode === 'passphrase') {
document.getElementById('passphraseTab').classList.add('active');
document.getElementById('randomTab').classList.remove('active');
document.getElementById('passphraseControls').classList.add('active');
} else {
document.getElementById('randomTab').classList.add('active');
document.getElementById('passphraseTab').classList.remove('active');
document.getElementById('passphraseControls').classList.remove('active');
}
// Update tab state using the switchTab function to ensure proper styling
switchTab(currentMode);
}
// Password history management
@@ -376,21 +587,31 @@ function loadPasswordHistory() {
function togglePasswordSaving() {
const savePasswords = document.getElementById('savePasswords').checked;
const historyDiv = document.getElementById('passwordHistory');
const viewHistoryBtn = document.getElementById('viewHistoryBtn');
if (savePasswords) {
// Re-display existing history
const history = getPasswordHistory();
displayPasswordHistory(history);
showNotification('Password saving enabled', 'success');
// Show View History button if there's a password displayed
const passwordDisplay = document.getElementById('passwordDisplay').textContent;
if (passwordDisplay && passwordDisplay !== 'Click "Generate Password" to create a secure password') {
viewHistoryBtn.style.display = 'inline-block';
}
} else {
// Clear stored passwords and hide history display
document.cookie = 'passwordHistory=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
historyDiv.innerHTML = '<p style="color: #999; font-style: italic;">Password saving is disabled</p>';
showNotification('Password saving disabled - history cleared', 'info');
// Hide View History button
viewHistoryBtn.style.display = 'none';
}
// Auto-save the setting change
saveSettings();
// Auto-save the setting change without notification
autoSaveSettings();
}
function displayPasswordHistory(history) {
@@ -438,11 +659,16 @@ function copyHistoryPassword(password, index) {
}
function clearHistory() {
if (confirm('Are you sure you want to clear all password history?')) {
document.cookie = 'passwordHistory=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
loadPasswordHistory();
alert('Password history cleared.');
}
showConfirmationPopup(
'Clear Password History?',
'This will permanently delete all saved passwords from your browser. This action cannot be undone.',
function() {
// User confirmed
document.cookie = 'passwordHistory=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
loadPasswordHistory();
showPopup('Password history cleared.', 'info');
}
);
}
// Utility function to get cookie value
@@ -453,23 +679,16 @@ function getCookie(name) {
return null;
}
// Load word list info
function loadWordListInfo() {
fetch('/api/password/info')
.then(response => response.json())
.then(data => {
document.getElementById('wordListStatus').innerHTML =
`${data.wordCount} words loaded<br>Source: ${data.source}<br>Updated: ${data.lastUpdate}`;
})
.catch(error => {
document.getElementById('wordListStatus').textContent = 'Error loading word list info';
});
}
function generatePassword() {
// Validate special characters before generating
if (!validateSpecialChars()) {
showNotification('Please fix special characters field before generating password', 'error');
return;
}
const config = getCurrentConfig();
fetch('/api/password', {
fetch('/api/pwgenerator', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -478,35 +697,45 @@ function generatePassword() {
})
.then(response => response.text())
.then(password => {
document.getElementById('passwordDisplay').textContent = password;
document.getElementById('copyBtn').style.display = 'inline-block';
document.getElementById('generatedPassword').value = password;
// Update character count
document.getElementById('characterCount').textContent = `${password.length} characters`;
// Calculate and display strength
const strength = calculatePasswordStrength(password);
document.getElementById('strengthIndicator').innerHTML = strength;
const strengthInfo = calculatePasswordStrength(password);
document.getElementById('strengthIndicator').innerHTML = strengthInfo.html;
document.getElementById('strengthDisplay').textContent = strengthInfo.text;
// Add to history
addToHistory(password);
})
.catch(error => {
console.error('Error:', error);
document.getElementById('passwordDisplay').textContent = 'Error generating password: ' + error.message;
document.getElementById('generatedPassword').value = 'Error generating password';
document.getElementById('characterCount').textContent = '0 characters';
document.getElementById('strengthDisplay').textContent = 'Error';
});
}
function copyPassword() {
const password = document.getElementById('passwordDisplay').textContent;
if (password && password !== 'Click "Generate Password" to create a secure password') {
const password = document.getElementById('generatedPassword').value;
if (password && password !== 'Click \'Generate Password\' to create a new password') {
navigator.clipboard.writeText(password).then(function() {
const btn = document.getElementById('copyBtn');
btn.textContent = 'Copied!';
btn.classList.add('copied');
setTimeout(function() {
btn.textContent = 'Copy to Clipboard';
btn.classList.remove('copied');
}, 2000);
showNotification('Password copied to clipboard!', 'success');
}, function(err) {
console.error('Could not copy text: ', err);
showNotification('Failed to copy password', 'error');
});
}
}
function scrollToHistory() {
const historyElement = document.getElementById('passwordHistory');
if (historyElement) {
historyElement.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
}
@@ -538,37 +767,15 @@ function calculatePasswordStrength(password) {
else if (score >= 40) { strength = "Moderate"; color = "#ffaa00"; }
else if (score >= 20) { strength = "Weak"; color = "#ff8800"; }
return `<span style="color: ${color}; font-weight: bold;">${strength}</span> (${score}/100)`;
return {
html: `<span style="color: ${color}; font-weight: bold;">${strength}</span> (${score}/100)`,
text: strength
};
}
// Notification system
// Use the showPopup function from base.html instead of custom notifications
function showNotification(message, type = 'info') {
// Remove any existing notifications
const existingNotifications = document.querySelectorAll('.notification');
existingNotifications.forEach(notification => notification.remove());
// Create new notification
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.textContent = message;
// Add to page
document.body.appendChild(notification);
// Show with animation
setTimeout(() => {
notification.classList.add('show');
}, 10);
// Auto-remove after 5 seconds
setTimeout(() => {
notification.classList.remove('show');
setTimeout(() => {
if (notification.parentNode) {
notification.remove();
}
}, 300);
}, 5000);
showPopup(message, type);
}
</script>
{{end}}