From a81130f800e08b1a200ebe48986ce12d34103f36 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 8 Jun 2026 19:12:25 -0400 Subject: [PATCH] Delete popup.js --- popup.js | 1031 ------------------------------------------------------ 1 file changed, 1031 deletions(-) delete mode 100644 popup.js diff --git a/popup.js b/popup.js deleted file mode 100644 index e794336..0000000 --- a/popup.js +++ /dev/null @@ -1,1031 +0,0 @@ -// --------------------------------------- -// portage by pvrz -// github: https://github.com/pvrzz/portage -// file name: popup.js -// --------------------------------------- - - -// --------------------------------------- -// state -// --------------------------------------- - -const state = { - type: "extensions", - mode: "installed", - selectedIds: new Set(), - expandedDomains: new Set(), - data: { - extensionsInstalled: [], - extensionsImported: [], - cookiesInstalled: {}, - cookiesImported: {}, - }, -}; - - -// --------------------------------------- -// constants -// --------------------------------------- - -const WEBSTORE_BASE = "https://chromewebstore.google.com/detail"; -const SELF_ID = chrome.runtime.id; -const PBKDF2_ITERATIONS = 250000; -const COOKIES_FORMAT = "portage-cookies"; -const COOKIES_VERSION = 1; -const EXTENSIONS_FORMAT = "portage-extensions"; - - -// --------------------------------------- -// dom refs -// --------------------------------------- - -const $ = (id) => document.getElementById(id); -const els = { - list: $("ext-list"), - emptyState: $("empty-state"), - emptyTitle: $("empty-title"), - emptySub: $("empty-sub"), - subtitle: $("subtitle"), - selectedCount: $("selected-count"), - primaryBtn: $("primary-btn"), - primaryLabel: $("primary-label"), - statusEl: $("status"), - importInput: $("import-input"), - modal: $("modal-backdrop"), - modalTitle: $("modal-title"), - modalDesc: $("modal-desc"), - modalPass: $("modal-passphrase"), - modalPassConfirm: $("modal-passphrase-confirm"), - modalSkipWrap: $("modal-skip-encrypt-wrap"), - modalSkip: $("modal-skip-encrypt"), - modalWarn: $("modal-warn"), - modalConfirm: $("modal-confirm"), - modalCancel: $("modal-cancel"), - modalClose: $("modal-close"), -}; - - -// --------------------------------------- -// initialization -// --------------------------------------- - -document.addEventListener("DOMContentLoaded", async () => { - await loadExtensions(); - await loadCookies(); - attachEventListeners(); - render(); -}); - - -// --------------------------------------- -// data loading -// --------------------------------------- - -async function loadExtensions() { - const all = await chrome.management.getAll(); - state.data.extensionsInstalled = all - .filter((ext) => ext.type === "extension" && ext.id !== SELF_ID) - .sort((a, b) => a.name.localeCompare(b.name)); -} - -async function loadCookies() { - const all = await chrome.cookies.getAll({}); - state.data.cookiesInstalled = groupCookiesByDomain(all); -} - -function groupCookiesByDomain(cookies) { - const grouped = {}; - for (const c of cookies) { - const key = c.domain.startsWith(".") ? c.domain.slice(1) : c.domain; - if (!grouped[key]) grouped[key] = []; - grouped[key].push(c); - } - const sorted = {}; - for (const k of Object.keys(grouped).sort()) { - sorted[k] = grouped[k].sort((a, b) => a.name.localeCompare(b.name)); - } - return sorted; -} - - -// --------------------------------------- -// current view helpers -// --------------------------------------- - -function currentList() { - if (state.type === "extensions") { - return state.mode === "installed" - ? state.data.extensionsInstalled - : state.data.extensionsImported; - } else { - return state.mode === "installed" - ? state.data.cookiesInstalled - : state.data.cookiesImported; - } -} - -function currentItemIds() { - const list = currentList(); - if (state.type === "extensions") { - return list.map((e) => e.id); - } else { - return Object.keys(list); - } -} - - -// --------------------------------------- -// render -// --------------------------------------- - -function render() { - updateSubtitle(); - updateTabsActive(); - updatePrimaryButton(); - updateImportAccept(); - - const list = currentList(); - const empty = state.type === "extensions" - ? list.length === 0 - : Object.keys(list).length === 0; - - if (empty) { - els.list.innerHTML = ""; - els.emptyState.classList.remove("hidden"); - updateEmptyState(); - } else { - els.emptyState.classList.add("hidden"); - if (state.type === "extensions") { - renderExtensionList(list); - } else { - renderCookieGroups(list); - } - } - - updateSelectionUi(); -} - -function updateSubtitle() { - const list = currentList(); - if (state.type === "extensions") { - els.subtitle.textContent = `${list.length} ${state.mode === "installed" ? "installed" : "imported"}`; - } else { - const domainCount = Object.keys(list).length; - let cookieCount = 0; - for (const k of Object.keys(list)) cookieCount += list[k].length; - els.subtitle.textContent = `${cookieCount} cookies · ${domainCount} domains`; - } -} - -function updateTabsActive() { - document.querySelectorAll(".type-tab").forEach((tab) => { - tab.classList.toggle("active", tab.dataset.type === state.type); - }); - document.querySelectorAll(".mode-tab").forEach((tab) => { - tab.classList.toggle("active", tab.dataset.mode === state.mode); - }); -} - -function updatePrimaryButton() { - const hasSelection = state.selectedIds.size > 0; - els.primaryBtn.disabled = !hasSelection; - - if (state.type === "extensions") { - els.primaryLabel.textContent = "open in tab group"; - } else if (state.mode === "installed") { - els.primaryLabel.textContent = "export selected"; - } else { - els.primaryLabel.textContent = "apply to browser"; - } -} - -function updateImportAccept() { - els.importInput.accept = state.type === "extensions" ? ".html,.htm" : ".json"; -} - -function updateEmptyState() { - if (state.type === "extensions") { - els.emptyTitle.textContent = state.mode === "imported" ? "no imported extensions" : "no extensions found"; - els.emptySub.textContent = state.mode === "imported" ? "import an html export to see it here" : "you have no installable extensions"; - } else { - els.emptyTitle.textContent = state.mode === "imported" ? "no imported cookies" : "no cookies found"; - els.emptySub.textContent = state.mode === "imported" ? "import a cookies json file to see them here" : "your browser has no cookies stored"; - } -} - -function renderExtensionList(list) { - els.list.innerHTML = ""; - for (const ext of list) { - const li = document.createElement("li"); - li.className = "ext-item"; - li.dataset.id = ext.id; - if (state.selectedIds.has(ext.id)) li.classList.add("selected"); - - const iconUrl = getBestIcon(ext); - const isDisabled = ext.enabled === false; - const isSideload = - ext.installType && ext.installType !== "normal" && ext.installType !== "admin"; - - li.innerHTML = ` - - ${iconUrl ? `` : `
`} -
-
${escapeHtml(ext.name)}
-
- ${escapeHtml(ext.id)} - ${isDisabled ? 'off' : ""} - ${isSideload ? 'sideload' : ""} -
-
- `; - - li.addEventListener("click", (e) => { - if (e.target.classList.contains("ext-checkbox")) return; - toggleSelection(ext.id); - }); - li.querySelector(".ext-checkbox").addEventListener("change", () => { - toggleSelection(ext.id); - }); - - els.list.appendChild(li); - } -} - -function renderCookieGroups(grouped) { - els.list.innerHTML = ""; - for (const domain of Object.keys(grouped)) { - const cookies = grouped[domain]; - const isSelected = state.selectedIds.has(domain); - const isExpanded = state.expandedDomains.has(domain); - - const groupLi = document.createElement("li"); - groupLi.className = "domain-group"; - - const header = document.createElement("div"); - header.className = "domain-header"; - if (isSelected) header.classList.add("selected"); - header.dataset.domain = domain; - - header.innerHTML = ` - - - - - ${escapeHtml(domain)} - ${cookies.length} - `; - - header.addEventListener("click", (e) => { - if (e.target.classList.contains("ext-checkbox")) return; - if (e.target.classList.contains("domain-chevron") || e.target.closest(".domain-chevron")) { - toggleDomainExpansion(domain); - } else { - toggleSelection(domain); - } - }); - - header.querySelector(".ext-checkbox").addEventListener("change", () => { - toggleSelection(domain); - }); - - groupLi.appendChild(header); - - if (isExpanded) { - const cookieList = document.createElement("ul"); - cookieList.className = "cookie-list"; - for (const c of cookies) { - const cookieLi = document.createElement("li"); - cookieLi.className = "cookie-item"; - const flags = []; - if (c.secure) flags.push('s'); - if (c.httpOnly) flags.push('h'); - if (c.session) flags.push('session'); - const truncatedValue = c.value.length > 30 ? c.value.slice(0, 30) + "…" : c.value; - cookieLi.innerHTML = ` - ${escapeHtml(c.name)} - ${escapeHtml(truncatedValue)} - ${flags.join("")} - `; - cookieList.appendChild(cookieLi); - } - groupLi.appendChild(cookieList); - } - - els.list.appendChild(groupLi); - } -} - -function getBestIcon(ext) { - if (!ext.icons || ext.icons.length === 0) return null; - return (ext.icons.find((i) => i.size === 48) || ext.icons.find((i) => i.size === 32) || ext.icons[ext.icons.length - 1]).url; -} - - -// --------------------------------------- -// selection -// --------------------------------------- - -function toggleSelection(id) { - if (state.selectedIds.has(id)) { - state.selectedIds.delete(id); - } else { - state.selectedIds.add(id); - } - render(); -} - -function toggleDomainExpansion(domain) { - if (state.expandedDomains.has(domain)) { - state.expandedDomains.delete(domain); - } else { - state.expandedDomains.add(domain); - } - render(); -} - -function selectAll() { - state.selectedIds = new Set(currentItemIds()); - render(); -} - -function selectNone() { - state.selectedIds.clear(); - render(); -} - -function selectInvert() { - const ids = currentItemIds(); - const next = new Set(); - for (const id of ids) { - if (!state.selectedIds.has(id)) next.add(id); - } - state.selectedIds = next; - render(); -} - -function updateSelectionUi() { - els.selectedCount.textContent = state.selectedIds.size; - updatePrimaryButton(); -} - - -// --------------------------------------- -// primary action dispatcher -// --------------------------------------- - -async function handlePrimaryAction() { - if (state.type === "extensions") { - await openSelectedInTabGroup(); - } else if (state.mode === "installed") { - await exportCookies(); - } else { - await applyCookies(); - } -} - - -// --------------------------------------- -// extension tab group action -// --------------------------------------- - -async function openSelectedInTabGroup() { - const list = currentList(); - const selected = list.filter((e) => state.selectedIds.has(e.id)); - if (selected.length === 0) return; - - if (selected.length > 10) { - if (!confirm(`open ${selected.length} tabs at once?`)) return; - } - - showStatus(`opening ${selected.length} tab${selected.length === 1 ? "" : "s"}...`); - - try { - const tabs = []; - for (const ext of selected) { - const tab = await chrome.tabs.create({ - url: `${WEBSTORE_BASE}/${ext.id}`, - active: false, - }); - tabs.push(tab); - } - - const groupId = await chrome.tabs.group({ tabIds: tabs.map((t) => t.id) }); - await chrome.tabGroups.update(groupId, { - title: state.mode === "imported" ? `portage: migrate ${selected.length}` : `portage (${selected.length})`, - color: "cyan", - collapsed: false, - }); - - showStatus(`opened ${tabs.length} tabs in a group`, "success"); - } catch (err) { - console.error(err); - showStatus(`error: ${err.message}`, "error"); - } -} - - -// --------------------------------------- -// export dispatcher -// --------------------------------------- - -function handleExport() { - if (state.type === "extensions") { - exportExtensionsToHtml(); - } else { - exportCookies(); - } -} - - -// --------------------------------------- -// extension export -// --------------------------------------- - -function exportExtensionsToHtml() { - const list = currentList(); - const toExport = state.selectedIds.size > 0 - ? list.filter((e) => state.selectedIds.has(e.id)) - : list; - - if (toExport.length === 0) { - showStatus("nothing to export", "error"); - return; - } - - const exportData = toExport.map((ext) => ({ - id: ext.id, - name: ext.name, - version: ext.version || "", - enabled: ext.enabled !== false, - homepageUrl: ext.homepageUrl || "", - })); - - const html = buildExportHtml(exportData); - downloadBlob(new Blob([html], { type: "text/html" }), `portage-extensions-${dateStamp()}.html`); - showStatus(`exported ${toExport.length} extensions`, "success"); -} - -function buildExportHtml(data) { - const jsonBlob = JSON.stringify({ - type: EXTENSIONS_FORMAT, - version: 1, - exportedAt: new Date().toISOString(), - extensions: data, - }, null, 2); - - const rows = data.map((ext, i) => ` - - ${i + 1} - ${escapeHtml(ext.name)} - ${escapeHtml(ext.id)} - ${escapeHtml(ext.version)} - ${ext.enabled ? "" : "off"} - install → - - `).join(""); - - return ` - - - -portage export - - - -
-

portage export

-

${data.length} extension${data.length === 1 ? "" : "s"} · exported ${new Date().toLocaleString()}

-
- - - - re-import this file in portage to use the tab-group flow -
- - - ${rows} -
#nameidversionstate
- -
- - - -`; -} - - -// --------------------------------------- -// cookie export -// --------------------------------------- - -async function exportCookies() { - if (state.selectedIds.size === 0) { - showStatus("select at least one domain", "error"); - return; - } - - const grouped = state.data.cookiesInstalled; - const collected = []; - for (const domain of state.selectedIds) { - if (grouped[domain]) collected.push(...grouped[domain]); - } - - if (collected.length === 0) { - showStatus("no cookies in selection", "error"); - return; - } - - openModal({ - title: "encrypt cookies?", - description: `${collected.length} cookies from ${state.selectedIds.size} domain${state.selectedIds.size === 1 ? "" : "s"} will be exported. encryption is strongly recommended — this file contains your login sessions.`, - mode: "encrypt", - onConfirm: async ({ passphrase, skip }) => { - const payload = { - type: COOKIES_FORMAT, - version: COOKIES_VERSION, - exportedAt: new Date().toISOString(), - encrypted: !skip, - }; - - if (skip) { - payload.cookies = collected; - } else { - try { - const encrypted = await encryptJson({ cookies: collected }, passphrase); - Object.assign(payload, encrypted); - } catch (err) { - showStatus(`encryption failed: ${err.message}`, "error"); - return; - } - } - - const json = JSON.stringify(payload, null, 2); - downloadBlob(new Blob([json], { type: "application/json" }), `portage-cookies-${dateStamp()}.json`); - showStatus(`exported ${collected.length} cookies${skip ? " (unencrypted)" : ""}`, "success"); - }, - }); -} - - -// --------------------------------------- -// import dispatcher -// --------------------------------------- - -function handleImport(file) { - if (state.type === "extensions") { - importExtensionsFromHtml(file); - } else { - importCookiesFromJson(file); - } -} - - -// --------------------------------------- -// extension import -// --------------------------------------- - -function importExtensionsFromHtml(file) { - const reader = new FileReader(); - reader.onload = (e) => { - try { - const html = e.target.result; - const match = html.match(/