Skip to content
Snippets Groups Projects
Commit c153c283 authored by Dhia Eddine Merzougui's avatar Dhia Eddine Merzougui
Browse files

Refactored extension code

parent aec1971a
No related branches found
Tags tagthunder-extension-2025-03-05
No related merge requests found
Pipeline #36091 failed
import { addZoneOverlays, removeZoneOverlays } from "./utils"; import {
processPageElements,
function processPageElements() { addZoneOverlays,
const liveDocument = document.documentElement; removeZoneOverlays,
const bodyEl = liveDocument.querySelector("body"); } from "./utils";
import { Zone } from "./model";
// inlineComputedStyles(bodyEl);
addBoundingBox(bodyEl); // Subdivision of zones
addXPathAttribute(bodyEl); document.addEventListener("click", (e) => {
const zoneEl = e.target.closest("[zone]");
const bodyDescendants = bodyEl.querySelectorAll(":not(script)"); if (zoneEl) {
bodyDescendants.forEach((el) => { e.stopPropagation();
// inlineComputedStyles(el); console.log(zoneEl);
addBoundingBox(el);
addXPathAttribute(el);
});
// Return the modified live HTML.
return liveDocument.outerHTML;
}
function inlineComputedStyles(el) {
const computedStyle = window.getComputedStyle(el);
let inlineStyle = "";
for (let i = 0; i < computedStyle.length; i++) {
const prop = computedStyle[i];
const value = computedStyle.getPropertyValue(prop);
inlineStyle += `${prop}:${value};`;
}
el.setAttribute("style", inlineStyle);
}
function addBoundingBox(el) {
const rect = el.getBoundingClientRect();
const bbox = `${rect.x} ${rect.y} ${rect.width} ${rect.height}`;
el.setAttribute("bbox", bbox);
}
function addXPathAttribute(el) {
const xpath = getElementXPath(el);
el.setAttribute("xpath", xpath);
}
function getElementXPath(element) {
if (element === document.documentElement) {
return "/html";
} }
});
const siblings = Array.from(element.parentNode.childNodes).filter(
(sibling) =>
sibling.nodeType === 1 && sibling.tagName === element.tagName
);
const index = siblings.indexOf(element) + 1;
return `${getElementXPath(
element.parentNode
)}/${element.tagName.toLowerCase()}[${index}]`;
}
// ---- Zone overlays // ---- Zone overlays
let volumeMouseMoveListener = null; let volumeMouseMoveListener = null;
const maxDistance = 700;
browser.runtime.onMessage.addListener((message, sender, sendResponse) => { browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "getPage") { if (message.action === "getPage") {
...@@ -86,111 +40,37 @@ browser.runtime.onMessage.addListener((message, sender, sendResponse) => { ...@@ -86,111 +40,37 @@ browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "play") { if (message.action === "play") {
const zoneElements = document.querySelectorAll("[zone]"); const zoneElements = document.querySelectorAll("[zone]");
const audioElements = document.querySelectorAll("[zone] audio"); const zones = Array.from(zoneElements).map((el) => new Zone(el));
if (audioElements.length > 0) {
audioElements.forEach((audio) => {
audio.loop = true;
if (typeof audio.play === "function") {
audio.play();
} else {
console.warn(
"Audio element within zone did not support play()"
);
}
});
// Start at the center of the screen.
let lastMouseX = window.innerWidth / 2;
let lastMouseY = window.innerHeight / 2;
let stopTimeout = null;
const updateVolumes = (
prevX,
prevY,
currentX,
currentY,
isMoving = true
) => {
const dx = currentX - prevX;
const dy = currentY - prevY;
zoneElements.forEach((element) => {
const audio = element.querySelector("audio");
if (!audio) return;
const bboxString = element.getAttribute("bbox"); zones.forEach((zone) => zone.playAudio());
if (!bboxString) return;
const [x, y, width, height] = bboxString let lastMouseX = window.innerWidth / 2;
.split(" ") let lastMouseY = window.innerHeight / 2;
.map(Number); let stopTimeout = null;
const centerX = x + width / 2; const updateVolumes = (currentX, currentY, previousX, previousY) => {
const centerY = y + height / 2; zones.forEach((zone) =>
let volume = 0; zone.updateVolume(currentX, currentY, previousX, previousY)
);
if (isMoving) { };
const vx = centerX - prevX;
const vy = centerY - prevY;
const dot = dx * vx + dy * vy;
const distance = Math.sqrt(vx * vx + vy * vy);
if (dot > 0) {
volume =
1 -
Math.min(distance, maxDistance) / maxDistance;
}
} else {
const vx = centerX - currentX;
const vy = centerY - currentY;
const distance = Math.sqrt(vx * vx + vy * vy);
volume =
1 - Math.min(distance, maxDistance) / maxDistance;
}
audio.volume = volume;
});
};
const stopTimeThreshold = 250;
volumeMouseMoveListener = (e) => {
const currentX = e.clientX;
const currentY = e.clientY;
updateVolumes(lastMouseX, lastMouseY, currentX, currentY, true);
lastMouseX = currentX;
lastMouseY = currentY;
if (stopTimeout) clearTimeout(stopTimeout); const movementTimeOut = 250;
stopTimeout = setTimeout(() => { const volumeMouseMoveListener = (e) => {
updateVolumes( const currentX = e.clientX;
lastMouseX, const currentY = e.clientY;
lastMouseY, updateVolumes(currentX, currentY, lastMouseX, lastMouseY);
lastMouseX, lastMouseX = currentX;
lastMouseY, lastMouseY = currentY;
false
);
}, stopTimeThreshold);
};
document.addEventListener("mousemove", volumeMouseMoveListener); if (stopTimeout) clearTimeout(stopTimeout);
stopTimeout = setTimeout(() => {
updateVolumes(lastMouseX, lastMouseY, lastMouseX, lastMouseY);
}, movementTimeOut);
};
updateVolumes( document.addEventListener("mousemove", volumeMouseMoveListener);
lastMouseX,
lastMouseY,
lastMouseX,
lastMouseY,
false
);
sendResponse({ status: "playing" }); sendResponse({ status: "playing" });
} else {
console.error(
"No audio elements found within elements with a 'zone' attribute"
);
sendResponse({
status: "error",
message: "No audio element found.",
});
}
} }
if (message.action === "stop") { if (message.action === "stop") {
......
import Zone from "./zone";
export { Zone };
class Zone {
constructor(element, maxDistance = 700) {
this.element = element;
this.audio = element.querySelector("audio");
this.maxDistance = maxDistance;
this.bbox = this.getBoundingBox();
}
getBoundingBox() {
const rect = this.element.getBoundingClientRect();
return {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
centerX: rect.x + rect.width / 2,
centerY: rect.y + rect.height / 2,
};
}
playAudio() {
if (this.audio && typeof this.audio.play === "function") {
this.audio.loop = true;
this.audio.play();
} else {
console.warn("Audio element did not support play()");
}
}
stopAudio() {
if (this.audio && typeof this.audio.pause === "function") {
this.audio.pause();
this.audio.currentTime = 0;
} else {
console.warn("Audio element did not support pause()");
}
}
updateVolume(currentX, currentY, previousX, previousY) {
this.bbox = this.getBoundingBox();
const { centerX, centerY } = this.bbox;
const moveX = currentX - previousX;
const moveY = currentY - previousY;
const dirX = centerX - currentX;
const dirY = centerY - currentY;
const distance = Math.sqrt(dirX * dirX + dirY * dirY);
let volume = 0;
if (moveX === 0 && moveY === 0) {
volume =
1 - Math.min(distance, this.maxDistance) / this.maxDistance;
} else {
const dot = moveX * dirX + moveY * dirY;
if (dot > 0) {
volume =
1 - Math.min(distance, this.maxDistance) / this.maxDistance;
}
}
if (this.audio) {
this.audio.volume = volume;
}
}
}
function processPageElements() {
const liveDocument = document.documentElement;
const bodyEl = liveDocument.querySelector("body");
// inlineComputedStyles(bodyEl);
addBoundingBox(bodyEl);
addXPathAttribute(bodyEl);
const bodyDescendants = bodyEl.querySelectorAll(":not(script)");
bodyDescendants.forEach((el) => {
// inlineComputedStyles(el);
addBoundingBox(el);
addXPathAttribute(el);
});
// Return the modified live HTML.
return liveDocument.outerHTML;
}
function inlineComputedStyles(el) {
const computedStyle = window.getComputedStyle(el);
let inlineStyle = "";
for (let i = 0; i < computedStyle.length; i++) {
const prop = computedStyle[i];
const value = computedStyle.getPropertyValue(prop);
inlineStyle += `${prop}:${value};`;
}
el.setAttribute("style", inlineStyle);
}
function addBoundingBox(el) {
const rect = el.getBoundingClientRect();
const bbox = `${rect.x} ${rect.y} ${rect.width} ${rect.height}`;
el.setAttribute("bbox", bbox);
}
function addXPathAttribute(el) {
const xpath = getElementXPath(el);
el.setAttribute("xpath", xpath);
}
function getElementXPath(element) {
if (element === document.documentElement) {
return "/html";
}
const siblings = Array.from(element.parentNode.childNodes).filter(
(sibling) =>
sibling.nodeType === 1 && sibling.tagName === element.tagName
);
const index = siblings.indexOf(element) + 1;
return `${getElementXPath(
element.parentNode
)}/${element.tagName.toLowerCase()}[${index}]`;
}
export { processPageElements };
import { processPageElements } from "./augmentation";
import { addZoneOverlays, removeZoneOverlays } from "./pageVisuals";
import { fetchState, getActiveTab } from "./stateManagement";
export {
processPageElements,
addZoneOverlays,
removeZoneOverlays,
fetchState,
getActiveTab,
};
// ---- State utils
async function fetchState() {
try {
const tabs = await browser.tabs.query({
active: true,
currentWindow: true,
});
if (!tabs.length) throw new Error("No active tab found.");
const activeTab = tabs[0];
const response = await browser.runtime.sendMessage({
action: "getState",
tabId: activeTab.id,
});
return response.state;
} catch (error) {
console.error(" > Error fetching state: ", error);
}
}
async function getActiveTab() {
const tabs = await browser.tabs.query({
active: true,
currentWindow: true,
});
if (!tabs.length) {
throw new Error("No active tab found.");
}
return tabs[0];
}
// ---- Overlay utils
// [ green-500, blue-600, red-500, purple-500, orange-500] // [ green-500, blue-600, red-500, purple-500, orange-500]
const colors = ["#22c55e", "#2563eb", "#ef4444", "#a855f7", "#f97316"]; const colors = ["#22c55e", "#2563eb", "#ef4444", "#a855f7", "#f97316"];
...@@ -74,4 +42,4 @@ function removeZoneOverlays() { ...@@ -74,4 +42,4 @@ function removeZoneOverlays() {
.forEach((overlay) => overlay.remove()); .forEach((overlay) => overlay.remove());
} }
export { fetchState, getActiveTab, addZoneOverlays, removeZoneOverlays }; export { addZoneOverlays, removeZoneOverlays };
async function fetchState() {
try {
const tabs = await browser.tabs.query({
active: true,
currentWindow: true,
});
if (!tabs.length) throw new Error("No active tab found.");
const activeTab = tabs[0];
const response = await browser.runtime.sendMessage({
action: "getState",
tabId: activeTab.id,
});
return response.state;
} catch (error) {
console.error(" > Error fetching state: ", error);
}
}
async function getActiveTab() {
const tabs = await browser.tabs.query({
active: true,
currentWindow: true,
});
if (!tabs.length) {
throw new Error("No active tab found.");
}
return tabs[0];
}
export { fetchState, getActiveTab };
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment