"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SVGElement = void 0;
const classList_js_1 = require("../classes/classList.js");
const namedNodeMap_js_1 = require("../classes/namedNodeMap.js");
const style_js_1 = require("../classes/style.js");
const Node_js_1 = require("../nodes/Node.js");
const TextNode_js_1 = require("../nodes/TextNode.js");
const functions_js_1 = require("../../../shared/utils/functions.js");
const regex_js_1 = require("../utils/regex.js");
class SVGElement extends Node_js_1.Node {
    constructor(tagName) {
        super(Node_js_1.NodeType.ELEMENT_NODE);
        this.attributes = new namedNodeMap_js_1.default();
        this.classList = new classList_js_1.default(this);
        this.style = new style_js_1.default(this);
        this.tagName = tagName;
    }
    get children() {
        return this.childNodes.filter(node => node.nodeType === Node_js_1.NodeType.ELEMENT_NODE);
    }
    get nextElementSibling() {
        var _a, _b;
        const siblings = (_b = (_a = this.parentNode) === null || _a === void 0 ? void 0 : _a.childNodes) !== null && _b !== void 0 ? _b : [];
        const index = siblings.indexOf(this);
        if (index === siblings.length - 1) {
            return null;
        }
        for (let s = index + 1; s < siblings.length; s++) {
            if (siblings[s].nodeType === Node_js_1.NodeType.ELEMENT_NODE) {
                return siblings[s];
            }
        }
        return null;
    }
    get previousElementSibling() {
        var _a, _b;
        const siblings = (_b = (_a = this.parentNode) === null || _a === void 0 ? void 0 : _a.childNodes) !== null && _b !== void 0 ? _b : [];
        const index = siblings.indexOf(this);
        if (index === 0) {
            return null;
        }
        for (let s = index - 1; s >= 0; s--) {
            if (siblings[s].nodeType === Node_js_1.NodeType.ELEMENT_NODE) {
                return siblings[s];
            }
        }
        return null;
    }
    get innerHTML() {
        var _a;
        let html = "";
        for (const childNode of this.childNodes) {
            if (childNode.nodeType === Node_js_1.NodeType.TEXT_NODE || childNode.nodeType === Node_js_1.NodeType.COMMENT_NODE || childNode.nodeType === Node_js_1.NodeType.CDATA_SECTION_NODE) {
                html += (_a = childNode.nodeValue) !== null && _a !== void 0 ? _a : "";
            }
            else if (childNode.nodeType === Node_js_1.NodeType.ELEMENT_NODE) {
                html += childNode.outerHTML;
            }
        }
        return html;
    }
    set innerHTML(text) {
        this.childNodes = [new TextNode_js_1.TextNode(text)];
    }
    get outerHTML() {
        const attributes = Object.keys((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)).map(key => `${key}="${(0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[key].value}"`).join(" ");
        const tagNameAndAttributes = `${[this.tagName, attributes].filter(value => value !== "").join(" ")}`;
        if (this.childNodes.length === 0) {
            return `<${tagNameAndAttributes} />`;
        }
        else {
            return `<${tagNameAndAttributes}>${this.innerHTML}</${this.tagName}>`;
        }
    }
    set innerText(text) {
        this.childNodes = [new TextNode_js_1.TextNode(text)];
    }
    get innerText() {
        var _a, _b;
        return (_b = (_a = this.childNodes.filter(node => node.nodeType === Node_js_1.NodeType.TEXT_NODE)[0]) === null || _a === void 0 ? void 0 : _a.nodeValue) !== null && _b !== void 0 ? _b : "";
    }
    setAttribute(attributeName, value) {
        this.attributes.setNamedItem(attributeName, value);
    }
    getAttribute(attributeName) {
        var _a, _b;
        return (_b = (_a = this.attributes.getNamedItem(attributeName)) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : null;
    }
    removeAttribute(attributeName) {
        this.attributes.removeNamedItem(attributeName);
    }
    toggleAttribute(attributeName, force) {
        if (this.hasAttribute(attributeName)) {
            if (force === true) {
                return true;
            }
            this.removeAttribute(attributeName);
            return false;
        }
        else {
            if (force === false) {
                return false;
            }
            this.setAttribute(attributeName, "");
            return true;
        }
    }
    hasAttribute(attributeName) {
        return (0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[attributeName] !== undefined;
    }
    hasAttributes() {
        return Object.keys(this.attributes).length > 0;
    }
    get className() {
        return this.classList.value;
    }
    set className(className) {
        this.classList.value = className;
    }
    get id() {
        return this.getAttribute("id");
    }
    set id(id) {
        if (id !== null) {
            this.setAttribute("id", id);
        }
    }
    get firstElementChild() {
        var _a;
        return (_a = this.childNodes.filter(node => node.nodeType === Node_js_1.NodeType.ELEMENT_NODE)[0]) !== null && _a !== void 0 ? _a : null;
    }
    get lastElementChild() {
        var _a;
        const childElements = this.childNodes.filter(node => node.nodeType === Node_js_1.NodeType.ELEMENT_NODE);
        return (_a = childElements[childElements.length - 1]) !== null && _a !== void 0 ? _a : null;
    }
    removeChild(child) {
        for (let c = this.children.length - 1; c >= 0; c--) {
            if (this.children[c] === child) {
                this.children.splice(c, 1);
                return child;
            }
        }
        throw new Error("Uncaught NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.");
    }
    getElementsByClassName(className) {
        const elements = [];
        for (const child of this.children) {
            if (child.classList.contains(className)) {
                elements.push(...[child, ...child.getElementsByClassName(className)]);
            }
        }
        return elements;
    }
    getElementsByTagName(tagName) {
        const elements = [];
        for (const child of this.children) {
            if (child.tagName === tagName) {
                elements.push(...[child, ...child.getElementsByTagName(tagName)]);
            }
        }
        return elements;
    }
    getElementById(id) {
        for (const child of this.children) {
            if (child.id === id) {
                return child;
            }
            else {
                return child.getElementById(id);
            }
        }
        return null;
    }
    tabIndex(index) {
        this.setAttribute("tabindex", index + "");
        return this;
    }
    get parentElement() {
        var _a;
        return ((_a = this.parentNode) === null || _a === void 0 ? void 0 : _a.nodeType) === Node_js_1.NodeType.ELEMENT_NODE ? this.parentNode : null;
    }
    querySelector(selector) {
        const selectors = selector.split(",");
        for (const child of this.children) {
            for (const selector of selectors) {
                if (child.matches(selector)) {
                    return child;
                }
                else {
                    const elementFromChild = child.querySelector(selector);
                    if (elementFromChild !== null) {
                        return elementFromChild;
                    }
                }
            }
        }
        return null;
    }
    querySelectorAll(selector) {
        const selectors = selector.split(",");
        const elements = [];
        for (const child of this.children) {
            for (const selector of selectors) {
                if (child.matches(selector)) {
                    elements.push(child);
                    const elementsFromChild = child.querySelectorAll(selector);
                    if (elementsFromChild !== null) {
                        elements.push(...elementsFromChild);
                    }
                }
            }
        }
        return elements;
    }
    closest(selector) {
        var _a;
        if (this.matches(selector)) {
            return this;
        }
        const parent = this.parentElement;
        return (_a = parent === null || parent === void 0 ? void 0 : parent.closest(selector)) !== null && _a !== void 0 ? _a : null;
    }
    matches(selector) {
        //-- Clean up selector
        var _a;
        selector = selector.replace(/>/g, " > ");
        selector = selector.replace(/\+/g, " + ");
        selector = selector.replace(/~/g, " ~ ");
        selector = selector.replace(/'/g, "\"");
        selector = selector.replace(/ {2}/g, " ");
        selector = selector.trim();
        //-- Split multiple selectors seperated by commas
        const selectorChains = selector.split(",");
        selectorChainLoop: for (const currentSelectorChain of selectorChains) {
            //-- Split multiple selectors seperated by spaces except within brackets
            const selectorArray = currentSelectorChain.split(/\s+(?![^[]*\])/g);
            //-- Check only current element if no parent selectors where provided
            if (selectorArray.length === 1) {
                //-- Match classes
                const classMatches = selectorArray[0].matchAll(regex_js_1.classRegex);
                for (const classMatch of classMatches) {
                    const className = classMatch[1];
                    if (this.classList.contains(className) !== true) {
                        continue selectorChainLoop;
                    }
                }
                //-- Match id
                const idMatches = selectorArray[0].matchAll(regex_js_1.idRegex);
                for (const idMatch of idMatches) {
                    const id = idMatch[1];
                    if (this.id !== id) {
                        continue selectorChainLoop;
                    }
                }
                //-- Match tag name
                const tagName = selectorArray[0].match(regex_js_1.tagRegex);
                if ((tagName === null || tagName === void 0 ? void 0 : tagName.length) === 1 && tagName[0] !== "") {
                    if (this.tagName !== tagName[0]) {
                        continue selectorChainLoop;
                    }
                }
                //-- Match attributes
                const extractAttributeDataFromRegexMatch = (attributeData) => {
                    const name = attributeData[0];
                    const matches = attributeData[1].matchAll(/"([^"]+)" ?(i)?/g);
                    for (const match of matches) {
                        return {
                            name,
                            value: match[1],
                            caseInSensitive: match[2] === "i"
                        };
                    }
                };
                const attributeMatches = selectorArray[0].matchAll(regex_js_1.attributeRegex);
                for (const attributeMatch of attributeMatches) {
                    const attributeString = attributeMatch[1];
                    const whitespaceSelectorArray = attributeString.split("~=");
                    const beginFollowedByHyphenArray = attributeString.split("|=");
                    const prefixedByValueArray = attributeString.split("^=");
                    const suffixedByValueArray = attributeString.split("$=");
                    const containsValueArray = attributeString.split("*=");
                    const exactValueArray = attributeString.split("=");
                    //-- Check only attribute name
                    if (attributeString.includes("=") !== true) {
                        if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[attributeString] === undefined) {
                            continue selectorChainLoop;
                        }
                    }
                    //-- Check attribute name and value
                    if (whitespaceSelectorArray.length === 2) {
                        if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[whitespaceSelectorArray[0]] === undefined) {
                            continue selectorChainLoop;
                        }
                        const attributeData = extractAttributeDataFromRegexMatch(whitespaceSelectorArray);
                        if (attributeData === undefined) {
                            continue selectorChainLoop;
                        }
                        const attributeList = (_a = attributeData.value.split(" ")) !== null && _a !== void 0 ? _a : [];
                        let valueFound = false;
                        for (const attributeListEntry of attributeList) {
                            if (attributeData.caseInSensitive === true) {
                                if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[attributeData.name].value.toLowerCase() === attributeListEntry.toLowerCase()) {
                                    valueFound = true;
                                }
                            }
                            else {
                                if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[attributeData.name].value === attributeListEntry) {
                                    valueFound = true;
                                }
                            }
                        }
                        if (valueFound === false) {
                            continue selectorChainLoop;
                        }
                    }
                    else if (beginFollowedByHyphenArray.length === 2) {
                        if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[beginFollowedByHyphenArray[0]] === undefined) {
                            continue selectorChainLoop;
                        }
                        const attributeValue = extractAttributeDataFromRegexMatch(beginFollowedByHyphenArray);
                        if (attributeValue === undefined) {
                            continue selectorChainLoop;
                        }
                        if (attributeValue.caseInSensitive === true) {
                            if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[attributeValue.name].value.toLowerCase().startsWith(attributeValue.value.toLowerCase() + "-") !== true) {
                                continue selectorChainLoop;
                            }
                        }
                        else {
                            if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[beginFollowedByHyphenArray[0]].value.startsWith(attributeValue.value + "-") !== true) {
                                continue selectorChainLoop;
                            }
                        }
                    }
                    else if (prefixedByValueArray.length === 2) {
                        if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[prefixedByValueArray[0]] === undefined) {
                            continue selectorChainLoop;
                        }
                        const attributeValue = extractAttributeDataFromRegexMatch(prefixedByValueArray);
                        if (attributeValue === undefined) {
                            continue selectorChainLoop;
                        }
                        if (attributeValue.caseInSensitive === true) {
                            if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[attributeValue.name].value.toLowerCase().startsWith(attributeValue.value.toLowerCase()) !== true) {
                                continue selectorChainLoop;
                            }
                        }
                        else {
                            if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[attributeValue.name].value.startsWith(attributeValue.value) !== true) {
                                continue selectorChainLoop;
                            }
                        }
                    }
                    else if (suffixedByValueArray.length === 2) {
                        if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[suffixedByValueArray[0]] === undefined) {
                            continue selectorChainLoop;
                        }
                        const attributeValue = extractAttributeDataFromRegexMatch(suffixedByValueArray);
                        if (attributeValue === undefined) {
                            continue selectorChainLoop;
                        }
                        if (attributeValue.caseInSensitive === true) {
                            if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[attributeValue.name].value.toLowerCase().endsWith(attributeValue.value.toLowerCase()) !== true) {
                                continue selectorChainLoop;
                            }
                        }
                        else {
                            if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[attributeValue.name].value.endsWith(attributeValue.value) !== true) {
                                continue selectorChainLoop;
                            }
                        }
                    }
                    else if (containsValueArray.length === 2) {
                        if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[containsValueArray[0]] === undefined) {
                            continue selectorChainLoop;
                        }
                        const attributeValue = extractAttributeDataFromRegexMatch(containsValueArray);
                        if (attributeValue === undefined) {
                            continue selectorChainLoop;
                        }
                        if (attributeValue.caseInSensitive === true) {
                            if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[attributeValue.name].value.toLowerCase().includes(attributeValue.value.toLowerCase()) !== true) {
                                continue selectorChainLoop;
                            }
                        }
                        else {
                            if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[attributeValue.name].value.includes(attributeValue.value) !== true) {
                                continue selectorChainLoop;
                            }
                        }
                    }
                    else if (exactValueArray.length === 2) {
                        if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[exactValueArray[0]] === undefined) {
                            continue selectorChainLoop;
                        }
                        const attributeValue = extractAttributeDataFromRegexMatch(exactValueArray);
                        if (attributeValue === undefined) {
                            continue selectorChainLoop;
                        }
                        if (attributeValue.caseInSensitive === true) {
                            if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[attributeValue.name].value.toLowerCase() !== attributeValue.value.toLowerCase()) {
                                continue selectorChainLoop;
                            }
                        }
                        else {
                            if ((0, functions_js_1.convertNamedNodeMapToObject)(this.attributes)[attributeValue.name].value !== attributeValue.value) {
                                continue selectorChainLoop;
                            }
                        }
                    }
                }
                return true;
            }
            //-- Check selector chain
            if (selectorArray.length > 1) {
                //-- Find root element and store indices that lead to the current element
                let rootNode = this;
                const indices = [];
                while (rootNode.parentNode !== null) {
                    indices.unshift(rootNode.parentNode.childNodes.indexOf(rootNode));
                    rootNode = rootNode.parentNode;
                }
                if (rootNode === this) {
                    return false;
                }
                if (rootNode.nodeType !== Node_js_1.NodeType.ELEMENT_NODE) {
                    continue selectorChainLoop;
                }
                const rootElement = rootNode;
                //-- Go back from root element and check every element to match the parent selectors
                let currentSelector = selectorArray.shift();
                if (currentSelector === undefined) {
                    continue selectorChainLoop;
                }
                let currentElement = rootElement;
                let directChildFlag = false;
                let directSiblingFlag = false;
                let siblingFlag = false;
                while (currentElement && currentSelector) {
                    //-- Set flags
                    if (currentSelector === ">") {
                        directChildFlag = true;
                        currentSelector = selectorArray.shift();
                    }
                    if (selectorArray[0] === "+") {
                        directSiblingFlag = true;
                        selectorArray.shift();
                    }
                    else if (selectorArray[0] === "~") {
                        siblingFlag = true;
                        selectorArray.shift();
                    }
                    if (currentSelector === undefined) {
                        continue selectorChainLoop;
                    }
                    //-- Check if siblings match
                    siblingsCheck: if (siblingFlag === true || directSiblingFlag === true) {
                        let currentPreviousSibling = currentElement.previousElementSibling;
                        if (currentPreviousSibling !== null) {
                            //-- Check if previous siblings match
                            while (currentPreviousSibling !== null) {
                                if (currentPreviousSibling.matches(currentSelector)) {
                                    directSiblingFlag = false;
                                    siblingFlag = false;
                                    currentSelector = selectorArray.shift();
                                    if (currentSelector === undefined) {
                                        return true;
                                    }
                                    break siblingsCheck;
                                }
                                else {
                                    if (directSiblingFlag === true) {
                                        continue selectorChainLoop;
                                    }
                                    currentPreviousSibling = currentPreviousSibling.previousElementSibling;
                                }
                            }
                        }
                        continue selectorChainLoop;
                    }
                    if (currentElement.matches(currentSelector)) {
                        const nextChildIndex = indices.shift();
                        if (nextChildIndex === undefined) {
                            if (currentElement === this) {
                                return true;
                            }
                            else {
                                continue selectorChainLoop;
                            }
                        }
                        currentSelector = selectorArray.shift();
                        currentElement = currentElement.childNodes[nextChildIndex];
                        directChildFlag = false;
                    }
                    else {
                        if (directChildFlag === true) {
                            continue selectorChainLoop;
                        }
                        const nextChildIndex = indices.shift();
                        if (nextChildIndex === undefined) {
                            continue selectorChainLoop;
                        }
                        currentElement = currentElement.childNodes[nextChildIndex];
                    }
                }
            }
        }
        return false;
    }
}
exports.SVGElement = SVGElement;
//# sourceMappingURL=SVGElement.js.map