@monitoro/herd
Version:
Automate your browser, build AI web tools and MCP servers with Monitoro Herd
142 lines (141 loc) • 5.1 kB
JavaScript
import { EventEmitter } from 'events';
export class Node extends EventEmitter {
constructor(serialized, page) {
super();
this._children = [];
this._serialized = serialized;
this._page = page;
// Initialize children
if (serialized.children) {
this._children = serialized.children
.filter((child) => child !== null)
.map(child => new Node(child, page));
}
}
// Node type properties
get nodeType() {
return this._serialized.nodeType === 'text' ? 3 : 1; // Element = 1, Text = 3
}
get nodeName() {
return this._serialized.tagName || '#text';
}
get nodeValue() {
return this._serialized.textContent || null;
}
// Element-like properties
get tagName() {
return this._serialized.tagName || '';
}
get textContent() {
// For text nodes, return their content directly
if (this.nodeType === 3) {
return this._serialized.textContent || null;
}
// For element nodes, concatenate text content of all child nodes
let content = '';
for (const child of this._children) {
const childContent = child.textContent;
if (childContent) {
// Add space for inline elements, newline for block elements
const separator = this.isBlockElement(child) ? "\n" : " ";
content += separator + childContent;
}
}
return content || null;
}
isBlockElement(node) {
const blockElements = [
'address', 'article', 'aside', 'blockquote', 'canvas', 'dd', 'div',
'dl', 'dt', 'fieldset', 'figcaption', 'figure', 'footer', 'form',
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hr', 'li', 'main',
'nav', 'noscript', 'ol', 'p', 'pre', 'section', 'table', 'tfoot',
'ul', 'video'
];
return blockElements.includes(node.tagName.toLowerCase());
}
get innerText() {
return this.textContent;
}
get innerHTML() {
return this._serialized.innerHTML;
}
// Child access methods
get childNodes() {
return this._children;
}
get firstChild() {
return this._children[0] || null;
}
get lastChild() {
return this._children[this._children.length - 1] || null;
}
// Attribute methods
getAttribute(name) {
return this._serialized.attributes?.[name] || null;
}
hasAttribute(name) {
return !!this._serialized.attributes?.[name];
}
// DOM-like methods implemented via Page
async click(options = {}) {
const selector = this._serialized._meta.selector;
await this._page.click(selector, options);
}
async type(text, options = {}) {
const selector = this._serialized._meta.selector;
await this._page.type(selector, text, options);
}
async focus(options = {}) {
const selector = this._serialized._meta.selector;
await this._page.focus(selector, options);
}
async blur(options = {}) {
const selector = this._serialized._meta.selector;
await this._page.blur(selector, options);
}
async hover(options = {}) {
const selector = this._serialized._meta.selector;
await this._page.hover(selector, options);
}
async scrollIntoView(options = {}) {
const selector = this._serialized._meta.selector;
await this._page.scrollIntoView(selector, options);
}
async setValue(value, options = {}) {
const selector = this._serialized._meta.selector;
await this._page.setValue(selector, value, options);
}
async dispatchEvent(eventName, detail, options = {}) {
const selector = this._serialized._meta.selector;
await this._page.dispatchEvent(eventName, selector, detail, options);
}
async dragTo(target, options = {}) {
const sourceSelector = this._serialized._meta.selector;
const targetSelector = typeof target === 'string' ? target : target._serialized._meta.selector;
await this._page.drag(sourceSelector, targetSelector, options);
}
// Query methods that return new Node instances
async $(selector) {
const result = await this._page._$(selector, this._serialized._meta.selector);
return result ? new Node(result, this._page) : null;
}
async $$(selector) {
const results = await this._page._$$(selector, this._serialized._meta.selector);
return results.map(result => new Node(result, this._page));
}
async querySelector(selector) {
return this.$(selector);
}
async querySelectorAll(selector) {
return this.$$(selector);
}
// Position and size
getBoundingClientRect() {
return {
x: this._serialized.position?.x || 0,
y: this._serialized.position?.y || 0,
width: this._serialized.size?.width || 0,
height: this._serialized.size?.height || 0
};
}
}