/**
* ╔════╗─────────────╔╗───────────────╔╗──────╔════╗╔═══╗╔═══╗╔═╗─╔╗╔═══╗──╔═══╗╔═══╗╔═══╗
* ║╔╗╔╗║─────────────║║───────────────║║──────║╔╗╔╗║║╔═╗║║╔═╗║║║╚╗║║║╔═╗║──║╔═╗║║╔═╗║║╔═╗║
* ╚╝║║╚╝╔╗─╔╗╔══╗╔══╗║║───╔══╗╔══╗╔══╗║╚═╦╗─╔╗╚╝║║╚╝║╚═╝║║║─║║║╔╗╚╝║║║─╚╝──║╚═╝║║╚═╝║║║─║║
* ──║║──║║─║║║╔╗║║║═╣║║─╔╗║║═╣║══╣║══╣║╔╗║║─║║──║║──║╔╗╔╝║║─║║║║╚╗║║║║╔═╗──║╔══╝║╔╗╔╝║║─║║
* ──║║──║╚═╝║║╚╝║║║═╣║╚═╝║║║═╣╠══║╠══║║╚╝║╚═╝║──║║──║║║╚╗║╚═╝║║║─║║║║╚╩═║╔╗║║───║║║╚╗║╚═╝║
* ──╚╝──╚═╗╔╝║╔═╝╚══╝╚═══╝╚══╝╚══╝╚══╝╚══╩═╗╔╝──╚╝──╚╝╚═╝╚═══╝╚╝─╚═╝╚═══╝╚╝╚╝───╚╝╚═╝╚═══╝
* ──────╔═╝║─║║──────────────────────────╔═╝║
* ──────╚══╝─╚╝──────────────────────────╚══╝
*
* TypeLess - Auto Form Filler
* v1.0.5 by TRONG.PRO
*/
/**
* options.js - Logic for the Options page (Settings & Onboarding)
*/
document.addEventListener('DOMContentLoaded', async () => {
// 1. Initialize i18n
await i18n.init();
updateUI();
// 2. Set language selector correctly
const langSelect = document.getElementById('language-select');
if (langSelect) {
const currentLocale = i18n.currentLang || 'en';
langSelect.value = currentLocale;
if (!langSelect.value) langSelect.value = 'en';
langSelect.addEventListener('change', async (e) => {
const newLang = e.target.value;
await i18n.setLocale(newLang);
updateUI();
});
}
// 3. Listen for language changes from other parts
chrome.storage.onChanged.addListener(async (changes, area) => {
if (area === 'local' && changes.language) {
const newLang = changes.language.newValue;
if (newLang && newLang !== i18n.currentLang) {
await i18n.setLocale(newLang);
if (langSelect) langSelect.value = newLang;
updateUI();
}
}
});
// Navigation Logic
const menuItems = document.querySelectorAll('.sidebar .menu-item');
const panels = document.querySelectorAll('.content .tab-panel');
menuItems.forEach(item => {
item.addEventListener('click', () => {
menuItems.forEach(i => i.classList.remove('active'));
panels.forEach(p => p.classList.remove('active'));
item.classList.add('active');
const targetId = item.dataset.target;
document.getElementById(targetId).classList.add('active');
// Load profiles list when switching to profiles tab
if (targetId === 'profiles') {
loadProfilesList();
}
});
});
// --- Load Settings ---
await loadSettings();
// --- Personal Info ---
document.getElementById('save-personal-btn').addEventListener('click', async () => {
const settings = {
firstName: document.getElementById('setting-firstname').value,
lastName: document.getElementById('setting-lastname').value,
email: document.getElementById('setting-email').value,
phone: document.getElementById('setting-phone').value,
dob: document.getElementById('setting-dob').value,
address: document.getElementById('setting-address').value,
city: document.getElementById('setting-city').value,
zipCode: document.getElementById('setting-zipcode').value,
country: document.getElementById('setting-country').value
};
await StorageManager.setGlobalSettings(settings);
showStatus('personal-status', i18n.t('options.saved'));
});
document.getElementById('clear-personal-btn').addEventListener('click', async () => {
if (confirm(i18n.t('options.reset_confirm'))) {
await StorageManager.setGlobalSettings({});
await loadSettings();
showStatus('personal-status', i18n.t('options.reset_done'));
}
});
// --- User Agents ---
document.getElementById('save-ua-btn').addEventListener('click', async () => {
const settings = {
ua_android: document.getElementById('setting-ua-android').value,
ua_ios: document.getElementById('setting-ua-ios').value,
ua_macos: document.getElementById('setting-ua-macos').value,
ua_windows: document.getElementById('setting-ua-windows').value,
ua_linux: document.getElementById('setting-ua-linux').value
};
await chrome.storage.local.set(settings);
showStatus('ua-status', i18n.t('options.saved'));
});
document.getElementById('reset-ua-btn').addEventListener('click', async () => {
if (confirm(i18n.t('options.reset_confirm'))) {
await chrome.storage.local.remove(['ua_android', 'ua_ios', 'ua_macos', 'ua_windows', 'ua_linux']);
await loadSettings();
showStatus('ua-status', i18n.t('options.reset_done'));
}
});
// --- Data Management ---
document.getElementById('btn-export')?.addEventListener('click', async () => {
const backupData = await StorageManager.getBackupData();
const blob = new Blob([JSON.stringify(backupData, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `typeless-backup-${new Date().toISOString().split('T')[0]}.json`;
a.click();
URL.revokeObjectURL(url);
});
document.getElementById('btn-import')?.addEventListener('click', () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.onchange = async (e) => {
const file = e.target.files[0];
if (!file) return;
try {
// Guard against memory exhaustion from huge files (max 5 MB)
if (file.size > 5 * 1024 * 1024) {
alert(i18n.t('alert.error_import'));
return;
}
const text = await file.text();
const importedData = JSON.parse(text);
const result = await StorageManager.restoreBackupData(importedData);
const rawMsg = i18n.t('notify.imported', {
added: result.added ?? result.count ?? 0,
updated: result.updated ?? 0,
skipped: result.skipped ?? 0
});
// alert() renders plain text — replace
with newline and strip other tags
const plainMsg = rawMsg.replace(/
/gi, '\n').replace(/<[^>]+>/g, '');
alert(plainMsg);
await loadSettings();
} catch (error) {
alert(i18n.t('alert.error_import'));
}
};
input.click();
});
// --- Date Picker (Flatpickr Inline) ---
const dobInput = document.getElementById('setting-dob');
const calendarEl = document.getElementById('inline-calendar');
if (dobInput && calendarEl && window.flatpickr) {
const fp = flatpickr(calendarEl, {
inline: true,
dateFormat: "Y/m/d",
defaultDate: dobInput.value,
onChange: (selectedDates, dateStr) => {
dobInput.value = dateStr;
}
});
dobInput.addEventListener('input', (e) => { fp.setDate(e.target.value, false, "Y/m/d"); });
dobInput.addEventListener('change', (e) => { fp.setDate(e.target.value, true, "Y/m/d"); });
}
// --- Demo Form Download ---
const downloadDemoBtn = document.getElementById('btn-download-demo');
if (downloadDemoBtn) {
downloadDemoBtn.addEventListener('click', () => {
const url = chrome.runtime.getURL('demo-form.html');
chrome.downloads.download({ url, filename: 'TypeLess-Demo-Form.html', saveAs: true });
});
}
// --- External Extension Settings Link ---
const externalSettingsLink = document.getElementById('browser-extension-settings');
if (externalSettingsLink) {
const id = chrome.runtime.id;
const isEdge = navigator.userAgent.includes('Edg/');
const baseUrl = isEdge ? 'edge://extensions/' : 'chrome://extensions/';
externalSettingsLink.href = `${baseUrl}?id=${id}`;
externalSettingsLink.addEventListener('click', (e) => {
e.preventDefault();
chrome.tabs.create({ url: `${baseUrl}?id=${id}` });
});
}
// --- Profile Manager Init ---
initProfileManager();
});
// ════════════════════════════════════════════════════════════════
// PROFILE MANAGER
// ════════════════════════════════════════════════════════════════
/** Currently-editing profile object (deep copy). */
let _editingProfile = null;
/** Working copy of fields during editing. */
let _editingFields = [];
/** Index of the field that should receive focus after fill. -1 = none. */
let _focusFieldIdx = -1;
function initProfileManager() {
// Search
document.getElementById('profile-search').addEventListener('input', (e) => {
renderProfileList(e.target.value.trim().toLowerCase());
});
document.getElementById('btn-refresh-profiles-list')?.addEventListener('click', async () => {
await loadProfilesList();
});
document.getElementById('btn-delete-all-profiles')?.addEventListener('click', async () => {
if (!_allProfiles || _allProfiles.length === 0) return;
if (!confirm(i18n.t('options.confirm_delete_all') || `Xóa tất cả ${_allProfiles.length} profile? Thao tác này không thể hoàn tác.`)) return;
await StorageManager.clearAllProfiles();
await loadProfilesList();
});
// Back buttons
document.getElementById('editor-back-btn').addEventListener('click', showProfilesList);
document.getElementById('editor-back-btn2').addEventListener('click', showProfilesList);
// Save editor
document.getElementById('editor-save-btn').addEventListener('click', saveEditingProfile);
// Delete profile
document.getElementById('editor-delete-btn').addEventListener('click', deleteEditingProfile);
// Add field button
document.getElementById('btn-add-field').addEventListener('click', addNewField);
// Allow Enter in new-field inputs to trigger add
['new-field-label', 'new-field-selector', 'new-field-value'].forEach(id => {
document.getElementById(id).addEventListener('keydown', (e) => {
if (e.key === 'Enter') { e.preventDefault(); addNewField(); }
});
});
}
// ── List View ──────────────────────────────────────────────────
let _allProfiles = [];
async function loadProfilesList() {
_allProfiles = await StorageManager.getProfiles();
renderProfileList('');
const badge = document.getElementById('profile-count-badge');
if (badge) badge.textContent = _allProfiles.length;
}
function renderProfileList(searchQuery) {
const container = document.getElementById('profiles-list');
const profiles = searchQuery
? _allProfiles.filter(p =>
p.name.toLowerCase().includes(searchQuery) ||
(p.url || '').toLowerCase().includes(searchQuery))
: _allProfiles;
if (profiles.length === 0) {
// Safe: all user-supplied values are escaped via escHtml() before insertion
const msg = searchQuery
? `
${i18n.t('options.profiles_no_match', { query: escHtml(searchQuery) })}
${i18n.t('options.profiles_empty')}