const TRACKER_FILE = "content/tracker.js"; function storageGet(keys) { return new Promise((resolve) => { chrome.storage.local.get(keys, resolve); }); } function storageSet(obj) { return new Promise((resolve) => { chrome.storage.local.set(obj, resolve); }); } function storageRemove(keys) { return new Promise((resolve) => { chrome.storage.local.remove(keys, resolve); }); } function tabsQuery(queryInfo) { return new Promise((resolve) => { chrome.tabs.query(queryInfo, resolve); }); } function executeScript(details) { return new Promise((resolve, reject) => { chrome.scripting.executeScript(details, () => { const err = chrome.runtime.lastError; if (err) { reject(new Error(err.message)); return; } resolve(); }); }); } const els = { record: document.getElementById("record"), stop: document.getElementById("stop"), exportSection: document.getElementById("export-section"), exportJson: document.getElementById("export-json"), copy: document.getElementById("copy"), copyStatus: document.getElementById("copy-status"), hint: document.getElementById("hint"), navHint: document.getElementById("nav-hint"), }; function isRestrictedUrl(url) { if (!url) { return true; } const u = url.toLowerCase(); return ( u.startsWith("chrome://") || u.startsWith("chrome-extension://") || u.startsWith("edge://") || u.startsWith("about:") || u.startsWith("moz-extension://") || u.startsWith("devtools:") || u.startsWith("view-source:") ); } async function getActiveTab() { const tabs = await tabsQuery({ active: true, currentWindow: true }); return tabs[0]; } async function injectTracker(tabId) { await executeScript({ target: { tabId }, files: [TRACKER_FILE], }); } function sendToTab(tabId, message) { return new Promise((resolve, reject) => { chrome.tabs.sendMessage(tabId, message, (response) => { const err = chrome.runtime.lastError; if (err) { reject(new Error(err.message)); return; } resolve(response); }); }); } async function clearRecordingStorage() { await storageRemove(["recording", "recordingTabId"]); } function setRecordingUi(isRecording, showExport) { els.record.classList.toggle("hidden", isRecording); els.stop.classList.toggle("hidden", !isRecording); els.stop.disabled = !isRecording; els.navHint.classList.toggle("hidden", !isRecording); els.exportSection.classList.toggle("hidden", !showExport); } async function syncStateFromTab() { const tab = await getActiveTab(); const data = await storageGet(["recording", "recordingTabId"]); if (!tab?.id) { setRecordingUi(false, !!els.exportJson.value); return; } if (!data.recording || data.recordingTabId !== tab.id) { setRecordingUi(false, !!els.exportJson.value); return; } if (isRestrictedUrl(tab.url)) { await clearRecordingStorage(); setRecordingUi(false, !!els.exportJson.value); return; } try { await injectTracker(tab.id); const status = await sendToTab(tab.id, { type: "GET_STATUS" }); if (status && status.recording) { setRecordingUi(true, !!els.exportJson.value); } else { await clearRecordingStorage(); setRecordingUi(false, !!els.exportJson.value); } } catch { await clearRecordingStorage(); setRecordingUi(false, !!els.exportJson.value); } } els.record.addEventListener("click", async () => { els.copyStatus.textContent = ""; const tab = await getActiveTab(); if (!tab?.id) { return; } if (isRestrictedUrl(tab.url)) { els.hint.textContent = "Opnemen werkt niet op deze pagina (browser-interne URL). Open een normale website."; return; } try { await injectTracker(tab.id); await sendToTab(tab.id, { type: "START_RECORDING" }); await storageSet({ recording: true, recordingTabId: tab.id, }); els.exportJson.value = ""; setRecordingUi(true, false); els.hint.textContent = "Interacties worden vastgelegd. Druk op Stop om de JSON te tonen."; } catch (e) { els.hint.textContent = "Kon niet starten: ververs de pagina of controleer of de site scripts toestaat."; console.error(e); } }); els.stop.addEventListener("click", async () => { els.copyStatus.textContent = ""; const tab = await getActiveTab(); const data = await storageGet(["recordingTabId"]); if (!tab?.id || tab.id !== data.recordingTabId) { await clearRecordingStorage(); setRecordingUi(false, false); els.hint.textContent = "Geen actieve opname op dit tabblad."; return; } try { await injectTracker(tab.id); const response = await sendToTab(tab.id, { type: "STOP_AND_GET_INTERACTIONS", }); await clearRecordingStorage(); const interactions = response?.interactions ?? []; els.exportJson.value = JSON.stringify(interactions, null, 2); setRecordingUi(false, true); els.hint.textContent = "JSON hieronder. Gebruik Kopiƫren of selecteer de tekst handmatig."; } catch (e) { await clearRecordingStorage(); setRecordingUi(false, false); els.hint.textContent = "Stoppen mislukt (pagina mogelijk vernieuwd). Start opnieuw met Opnemen."; console.error(e); } }); els.copy.addEventListener("click", async () => { const text = els.exportJson.value; if (!text) { return; } try { await navigator.clipboard.writeText(text); els.copyStatus.textContent = "Gekopieerd naar klembord."; } catch { els.exportJson.select(); els.copyStatus.textContent = "Selecteer Cmd/Ctrl+C om te kopiƫren."; } }); syncStateFromTab();