/
viktoriia.slizevich
/
lab2
Обзор
Документация
Войти
/
viktoriia.slizevich
/
lab2
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
script.js
250 строк
8 KB
viktoriia.slizevich
upload files
14 дек 2025, 19:15
14 дек 2025, 19:15
9c7bdfe
Код
Авторство
О чём код?
//Конфигурация приложения const CONFIG = { STORAGE_KEY: 'passwords', ANIMATION_DELAY: 2000 }; //Утилиты для работы с данными и строками const Utils = { // Генерация уникального ID generateId: () => Date.now().toString(36) + Math.random().toString(36).substr(2, 5), // Генерация пароля generatePassword: (length, { includeLower, includeUpper, includeNumbers, includeSymbols }) => { const charSets = { lower: 'abcdefghijklmnopqrstuvwxyz', upper: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', numbers: '0123456789', symbols: '!@#$%^&*()_+-=[]{}|;:,.<>?' }; let charset = ''; if (includeLower) charset += charSets.lower; if (includeUpper) charset += charSets.upper; if (includeNumbers) charset += charSets.numbers; if (includeSymbols) charset += charSets.symbols; if (!charset) { alert('Выберите хотя бы один тип символов'); return ''; } return Array.from({ length }, () => charset[Math.floor(Math.random() * charset.length)] ).join(''); }, // Копирование в буфер обмена copyToClipboard: async (text, btnElement) => { try { await navigator.clipboard.writeText(text); const originalText = btnElement.textContent; btnElement.textContent = 'Скопировано!'; setTimeout(() => btnElement.textContent = originalText, CONFIG.ANIMATION_DELAY); } catch (err) { console.error('Ошибка копирования:', err); } } }; //Сервис для работы с LocalStorage const StorageService = { get: () => { const data = localStorage.getItem(CONFIG.STORAGE_KEY); return data ? JSON.parse(data) : []; }, save: (data) => { localStorage.setItem(CONFIG.STORAGE_KEY, JSON.stringify(data)); } }; //Основной класс приложения class PasswordManagerApp { constructor() { this.initElements(); this.initEventListeners(); this.renderList(); this.checkServiceWorker(); } // Инициализация DOM-элементов initElements() { // Генератор this.els = { lengthSlider: document.getElementById('length'), lengthValue: document.getElementById('lengthValue'), generateBtn: document.getElementById('generateBtn'), passwordOutput: document.getElementById('passwordOutput'), copyBtn: document.getElementById('copyBtn'), options: { lower: document.getElementById('includeLower'), upper: document.getElementById('includeUpper'), numbers: document.getElementById('includeNumbers'), symbols: document.getElementById('includeSymbols'), }, // Форма и список form: document.getElementById('passwordForm'), loginInput: document.getElementById('login'), passwordInput: document.getElementById('password'), urlInput: document.getElementById('url'), searchInput: document.getElementById('search'), listContainer: document.getElementById('passwordList'), }; // Установка начального значения длины this.els.lengthValue.textContent = this.els.lengthSlider.value; } // Регистрация всех обработчиков событий initEventListeners() { // Слайдер длины this.els.lengthSlider.addEventListener('input', (e) => { this.els.lengthValue.textContent = e.target.value; }); // Кнопка генерации this.els.generateBtn.addEventListener('click', () => this.handleGenerate()); // Кнопка копирования сгенерированного пароля this.els.copyBtn.addEventListener('click', () => { if (this.els.passwordOutput.value) { Utils.copyToClipboard(this.els.passwordOutput.value, this.els.copyBtn); } }); // Отправка формы this.els.form.addEventListener('submit', (e) => this.handleSubmit(e)); // Поиск this.els.searchInput.addEventListener('input', (e) => this.renderList(e.target.value)); // Делегирование событий списка (Удаление и Копирование) this.els.listContainer.addEventListener('click', (e) => this.handleListActions(e)); } checkServiceWorker() { if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/service-worker.js') .then(() => console.log('SW registered')) .catch(err => console.log('SW registration failed', err)); } } // Логика генерации handleGenerate() { const options = { includeLower: this.els.options.lower.checked, includeUpper: this.els.options.upper.checked, includeNumbers: this.els.options.numbers.checked, includeSymbols: this.els.options.symbols.checked }; const password = Utils.generatePassword(parseInt(this.els.lengthSlider.value), options); this.els.passwordOutput.value = password; } // Обработка добавления пароля handleSubmit(e) { e.preventDefault(); const login = this.els.loginInput.value.trim(); const password = this.els.passwordInput.value.trim(); const url = this.els.urlInput.value.trim(); if (!login || !password || !url) { alert('Пожалуйста, заполните все поля: Логин, Пароль и URL'); return; } const newEntry = { id: Utils.generateId(), login, password, url: url, createdAt: new Date().toISOString() }; const passwords = StorageService.get(); passwords.push(newEntry); StorageService.save(passwords); this.els.form.reset(); this.renderList(); // Возвращаем сгенерированный пароль в поле ввода this.els.passwordInput.value = this.els.passwordOutput.value; } // Обработка кликов внутри списка handleListActions(e) { const target = e.target; // Копирование if (target.classList.contains('copy-password')) { const password = target.dataset.password; Utils.copyToClipboard(password, target); return; } // Удаление if (target.classList.contains('delete-password')) { const id = target.dataset.id; if (confirm('Удалить этот пароль?')) { const passwords = StorageService.get().filter(p => p.id !== id); StorageService.save(passwords); this.renderList(this.els.searchInput.value); } } } // Рендеринг списка renderList(query = '') { const passwords = StorageService.get(); const cleanQuery = query.toLowerCase().trim(); const filtered = !cleanQuery ? passwords : passwords.filter(p => p.login.toLowerCase().includes(cleanQuery) || (p.url && p.url.toLowerCase().includes(cleanQuery)) ); if (filtered.length === 0) { this.els.listContainer.innerHTML = '<div class="empty-state">Пароли не найдены. Добавьте первый пароль!</div>'; return; } this.els.listContainer.innerHTML = filtered.map(p => ` <div class="password-item"> <div> <h3>${this.escapeHtml(p.login)}</h3> <p>${p.url ? `URL: ${this.escapeHtml(p.url)}` : 'URL: не указан'}</p> <div class="password-content"> <span class="password-value">${this.escapeHtml(p.password)}</span> <button class="btn secondary copy-password" data-password="${this.escapeHtml(p.password)}">Копировать</button> </div> </div> <div class="password-actions"> <button class="btn secondary delete-password" data-id="${p.id}">Удалить</button> </div> </div> `).join(''); } // Простая защита от XSS при выводе данных escapeHtml(str) { if (!str) return ''; return str .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } } // Запуск приложения при загрузке DOM document.addEventListener('DOMContentLoaded', () => { new PasswordManagerApp(); });