217 lines
5.6 KiB
JavaScript
217 lines
5.6 KiB
JavaScript
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();
|