"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DomManager = void 0;
const constants_js_1 = require("./constants.js");
class DomManager {
    constructor(page) {
        this.nodesMap = new Map();
        this.updatesCounter = 1;
        this.page = page;
        this.eventListeners = [
            ['DOM.documentUpdated', this.invalidate.bind(this)],
            ['DOM.setChildNodes', this.onSetChildNodes.bind(this)],
            ['DOM.attributeModified', this.onAttributeModified.bind(this)],
            ['DOM.attributeRemoved', this.onAttributeRemoved.bind(this)],
            ['DOM.childNodeInserted', this.onChildNodeInserted.bind(this)],
            ['DOM.childNodeRemoved', this.onChildNodeRemoved.bind(this)],
            ['DOM.childNodeCountUpdated', this.onChildNodeCountUpdated.bind(this)],
            ['DOM.characterDataModified', this.onCharacterDataModified.bind(this)],
            ['DOM.pseudoElementAdded', this.onPseudoElementAdded.bind(this)],
            ['DOM.pseudoElementRemoved', this.onPseudoElementRemoved.bind(this)],
        ];
    }
    enable() {
        this.page.sendAndForget('DOM.enable');
        for (const [ev, listener] of this.eventListeners) {
            this.page.target.addListener(ev, listener);
        }
        this.invalidate();
    }
    disable() {
        this.nodesMap.clear();
        for (const [ev, listener] of this.eventListeners) {
            this.page.target.removeListener(ev, listener);
        }
        this.page.sendAndForget('DOM.disable');
    }
    invalidate() {
        this.nodesMap.clear();
        this.page.send('DOM.getDocument').then(res => this.registerNode(null, res.root), () => { });
    }
    getNodeById(nodeId) {
        return (this.updatesCounter && this.nodesMap.get(nodeId)) || null;
    }
    async resolveNodeFromEl(el) {
        const { objectId } = el;
        const { nodeId } = await el.page.send('DOM.requestNode', { objectId });
        return this.getNodeById(nodeId);
    }
    requestChildNodes(nodeId) {
        this.page.sendAndForget('DOM.requestChildNodes', {
            nodeId,
            depth: 3,
        });
    }
    async querySelectorAll(nodeId, selector) {
        const { nodeIds } = await this.page.send('DOM.querySelectorAll', { nodeId, selector });
        return nodeIds;
    }
    highlightElement(el) {
        this.resolveNodeFromEl(el)
            .catch(() => null)
            .then(node => {
            if (node) {
                this.highlightNode(node);
            }
        });
    }
    highlightNode(node, highlightConfig = constants_js_1.HIGHLIGHT_CONFIG) {
        this.page.sendAndForget('Overlay.highlightNode', {
            nodeId: node.nodeId,
            highlightConfig,
        });
    }
    hideHighlight() {
        this.page.sendAndForget('Overlay.hideHighlight');
    }
    convertAttributes(cdpAttributes = []) {
        const attrs = {};
        for (let i = 0; i < cdpAttributes.length; i += 2) {
            attrs[cdpAttributes[i]] = cdpAttributes[i + 1];
        }
        return attrs;
    }
    parseClassList(classAttr) {
        return (classAttr || '').split(/\s+/g).filter(Boolean);
    }
    registerNode(parentNode, cdpNode) {
        const node = cdpNode;
        node.parentId = parentNode ? parentNode.nodeId : null;
        node.ownerDocumentId = node.nodeType === 9 ? node.nodeId : parentNode ? parentNode.ownerDocumentId : null;
        node.children = node.children || null;
        node.pseudoElements = node.pseudoElements || null;
        this.nodesMap.set(node.nodeId, node);
        this.updatesCounter += 1;
        if (node.children) {
            node.children.forEach(child => {
                this.registerNode(node, child);
            });
        }
        if (node.contentDocument) {
            this.registerNode(node, node.contentDocument);
        }
        if (node.pseudoElements) {
            for (const pseudoChild of node.pseudoElements) {
                this.registerNode(node, pseudoChild);
            }
        }
    }
    unregisterNode(node) {
        this.nodesMap.delete(node.nodeId);
        this.updatesCounter += 1;
        if (node.children) {
            node.children.forEach(child => {
                this.unregisterNode(child);
            });
        }
        if (node.contentDocument) {
            this.unregisterNode(node.contentDocument);
        }
        if (node.pseudoElements) {
            for (const pseudoChild of node.pseudoElements) {
                this.unregisterNode(pseudoChild);
            }
        }
    }
    onSetChildNodes(params) {
        const { parentId, nodes } = params;
        const parentNode = this.nodesMap.get(parentId);
        if (!parentNode) {
            return;
        }
        parentNode.children = nodes;
        nodes.forEach(node => {
            this.registerNode(parentNode, node);
        });
    }
    onAttributeModified(params) {
        const { nodeId, name, value } = params;
        const node = this.nodesMap.get(nodeId);
        if (node && node.attributes) {
            removeAttr(node, name);
            node.attributes.push(name, value);
        }
    }
    onAttributeRemoved(params) {
        const { nodeId, name } = params;
        const node = this.nodesMap.get(nodeId);
        if (node && node.attributes) {
            removeAttr(node, name);
        }
    }
    onChildNodeInserted(params) {
        const { parentNodeId, previousNodeId, node } = params;
        const parentNode = this.nodesMap.get(parentNodeId);
        if (!parentNode) {
            return;
        }
        this.registerNode(parentNode, node);
        if (!parentNode.children) {
            return;
        }
        const prevIdx = parentNode.children.findIndex(n => n.nodeId === previousNodeId);
        parentNode.children.splice(prevIdx + 1, 0, node);
    }
    onChildNodeRemoved(params) {
        const { parentNodeId, nodeId } = params;
        const removedNode = this.nodesMap.get(nodeId);
        if (removedNode) {
            this.unregisterNode(removedNode);
        }
        const parentNode = this.nodesMap.get(parentNodeId);
        if (!parentNode || !parentNode.children) {
            return;
        }
        const i = parentNode.children.findIndex(n => n.nodeId === nodeId);
        if (i > -1) {
            parentNode.children.splice(i, 1);
        }
    }
    onChildNodeCountUpdated(params) {
        const { nodeId, childNodeCount } = params;
        const node = this.nodesMap.get(nodeId);
        if (node) {
            node.childNodeCount = childNodeCount;
        }
    }
    onCharacterDataModified(params) {
        const { nodeId, characterData } = params;
        const node = this.nodesMap.get(nodeId);
        if (node) {
            node.nodeValue = characterData;
        }
    }
    onPseudoElementAdded(params) {
        const { parentId, pseudoElement } = params;
        const parentNode = this.nodesMap.get(parentId);
        if (!parentNode) {
            return;
        }
        this.registerNode(parentNode, pseudoElement);
    }
    onPseudoElementRemoved(params) {
        const { pseudoElementId } = params;
        const node = this.nodesMap.get(pseudoElementId);
        if (node) {
            this.unregisterNode(node);
        }
    }
}
exports.DomManager = DomManager;
function removeAttr(node, name) {
    if (!node.attributes) {
        return;
    }
    for (let i = 0; i < node.attributes.length; i += 2) {
        if (node.attributes[i] === name) {
            node.attributes.splice(i, 2);
            return;
        }
    }
}
//# sourceMappingURL=dom.js.map