donobu
Version:
Create browser automations with an LLM agent and replay them as Playwright scripts.
223 lines (198 loc) • 6.53 kB
JavaScript
/**
* Donobu script for tracking interactions with the current page. This scripts assumes the
* following:
* 1. There is an exposed binding to call back to Java named "donobuTrackInteraction".
* 2. The smart-selector-generator.js script has defined the "donobuGenerateSmartSelectors"
* method on the window object.
* 3. The Donobu backend uses the "data-donobu-marker" as a DOM element attribute to uniquely
* identify interesting DOM elements.
*/
(() => {
if (window.donobuListenersRegistered) {
return;
}
window.donobuStartDragPosition = { x: 0, y: 0 };
// Helper function to check if an event target is inside the Donobu control panel.
const isDonobuControlPanelInteraction = (element) => {
while (element != null) {
if (element.id === 'donobu-control-panel') {
return true;
}
element = element.parentElement;
}
return false;
};
// Ensure that the target element has a unique `data-donobu-marker` attribute. If the attribute
// does not exist, then it will be created with a random value. If the attribute already exists,
// then this function has no effect. Returns the value of the attribute.
const ensureElementHasUniqueDonobuAttribute = (element) => {
const donobuMarkerAttributeName = 'data-donobu-marker';
let donobuMarkerAttributeValue = element.getAttribute(
donobuMarkerAttributeName,
);
if (!donobuMarkerAttributeValue) {
donobuMarkerAttributeValue = `${Math.random().toString(36).slice(2, 11)}`;
element.setAttribute(
donobuMarkerAttributeName,
donobuMarkerAttributeValue,
);
}
return donobuMarkerAttributeValue;
};
// Mouse events
['click', 'dblclick', 'contextmenu', 'mousedown', 'mouseup'].forEach(
(eventType) => {
document.addEventListener(
eventType,
function (e) {
let donobuMarker;
// Skip events if they occur in the Donobu control panel.
if (isDonobuControlPanelInteraction(e.target)) {
return;
} else {
donobuMarker = ensureElementHasUniqueDonobuAttribute(e.target);
}
const eventData = {
type: eventType,
x: e.clientX,
y: e.clientY,
selectors: window.donobuGenerateSmartSelectors(e.target),
timestamp: new Date().getTime(),
donobuMarker: donobuMarker,
};
if (eventType === 'mousedown') {
donobuStartDragPosition = { x: e.clientX, y: e.clientY };
} else if (eventType === 'mouseup') {
eventData.dragDistance = Math.sqrt(
Math.pow(e.clientX - donobuStartDragPosition.x, 2) +
Math.pow(e.clientY - donobuStartDragPosition.y, 2),
);
}
donobuTrackInteraction(eventData);
},
true,
);
},
);
// Keyboard events
['keydown', 'keyup'].forEach((eventType) => {
document.addEventListener(
eventType,
function (e) {
let donobuMarker;
// Skip events if they occur in the Donobu control panel.
if (isDonobuControlPanelInteraction(e.target)) {
return;
} else {
donobuMarker = ensureElementHasUniqueDonobuAttribute(e.target);
}
donobuTrackInteraction({
type: eventType,
key: e.key,
keyCode: e.keyCode,
altKey: e.altKey,
ctrlKey: e.ctrlKey,
metaKey: e.metaKey,
shiftKey: e.shiftKey,
selectors: window.donobuGenerateSmartSelectors(e.target),
timestamp: new Date().getTime(),
donobuMarker: donobuMarker,
});
},
true,
);
});
// Form events
['change', 'submit'].forEach((eventType) => {
document.addEventListener(
eventType,
function (e) {
let donobuMarker;
// Skip events if they occur in the Donobu control panel.
if (isDonobuControlPanelInteraction(e.target)) {
return;
} else {
donobuMarker = ensureElementHasUniqueDonobuAttribute(e.target);
}
const eventData = {
type: eventType,
selectors: window.donobuGenerateSmartSelectors(e.target),
timestamp: new Date().getTime(),
donobuMarker: donobuMarker,
};
if (eventType === 'change') {
eventData.value = e.target.value;
}
if (eventType === 'submit') {
const formData = new FormData(e.target);
eventData.formData = {};
for (let [key, value] of formData.entries()) {
eventData.formData[key] = value;
}
}
donobuTrackInteraction(eventData);
},
true,
);
});
// These are commented out since they materially slow the page.
//
// // Scroll events
// window.addEventListener(
// "scroll",
// function (e) {
// donobuTrackInteraction({
// type: "scroll",
// scrollX: window.scrollX,
// scrollY: window.scrollY,
// timestamp: new Date().getTime(),
// });
// },
// true
// );
//
// // Window resize event
// window.addEventListener(
// "resize",
// function (e) {
// donobuTrackInteraction({
// type: "resize",
// width: window.innerWidth,
// height: window.innerHeight,
// timestamp: new Date().getTime(),
// });
// },
// true
// );
// Touch events for mobile
['touchstart', 'touchend', 'touchmove', 'touchcancel'].forEach(
(eventType) => {
document.addEventListener(
eventType,
function (e) {
let donobuMarker;
// Skip events if they occur in the Donobu control panel.
if (isDonobuControlPanelInteraction(e.target)) {
return;
} else {
donobuMarker = ensureElementHasUniqueDonobuAttribute(e.target);
}
const touches = Array.from(e.touches).map((touch) => ({
x: touch.clientX,
y: touch.clientY,
identifier: touch.identifier,
donobuMarker: donobuMarker,
}));
donobuTrackInteraction({
type: eventType,
touches: touches,
selectors: window.donobuGenerateSmartSelectors(e.target),
timestamp: new Date().getTime(),
});
},
true,
);
},
);
window.donobuListenersRegistered = true;
})();