class Sanitizer {

    //===================================
    // PUBLIC METHODS
    //===================================

    static removeCDATA(value) {
        return this.replaceBetween(value, "<![CDATA[", "]]>", "");
    }

    static removeComments(value) {
        return this.replaceBetween(value, "<!--", "-->", "");
    }

    //===================================
    // PRIVATE METHODS
    //===================================

    static replaceBetween(source, start, end, replacement) {

        do {

            let srcCased = source.toLowerCase();

            let sIndex = srcCased.indexOf(start.toLowerCase());
            let eIndex = srcCased.indexOf(end.toLowerCase());

            eIndex > -1 ?
                source = `${source.substring(0, sIndex)}${replacement}${source.substring(eIndex + end.length)}` :
                source = source.substring(0, source.startIndex);

        } while (source.indexOf(start) > -1);

        return source;

    }

}

class ElementExtractor {

    //===================================
    // CONSTRCUTOR
    //===================================

    constructor() {
        this.m_content;
        this.m_position;
    }

    //===================================
    // PROPERTIES
    //===================================

    get content() {
        return this.m_content;
    }
    set content(value) {
        this.m_content = value;
    }

    get cursorPlacholder() {
        return `«|»`
    }

    get position() {
        return this.m_position;
    }
    set position(value) {
        this.m_position = value;
    }

    //===================================
    // PUBLIC METHODS
    //===================================

    extract(options) {
        this.setParameters(options);
        return this.getElementTagPath() || [];
    }

    //===================================
    // PRIVATE METHODS
    //===================================

    insertCursorPlacholder(content, index) {
        return `${content.slice(0, index)}${this.cursorPlacholder}${content.slice(index)}`;
    }

    removeCursorPlacholder(content) {
        return content.replace(this.cursorPlacholder, "");
    }

    setParameters(options) {

        this.position = options.position;
        this.content = options.content.slice(0, options.position);
        this.content = this.content
            .replace(/\n/g, " ")
            .replace(/\t/g, " ")
            .replace(/\s/g, " ");

    }

    getElementTagPath() {

        let results = [];
        let extracted =
            this.content.slice(0, this.content.lastIndexOf(" "));

        extracted = this.reduceWhiteSpace(extracted);
        extracted = this.removeAttributes(extracted);

        extracted = Sanitizer.removeCDATA(extracted);
        extracted = Sanitizer.removeComments(extracted);

        extracted = this.removeMissedAttributes(extracted);

        extracted = extracted.trim();

        extracted = extracted.startsWith("<") ?
            extracted.slice(1) : extracted;

        extracted = extracted.endsWith(">") ?
            extracted.slice(0, extracted.lastIndexOf(">")) : extracted;

        extracted = this.reduceWhiteSpace(extracted);
        extracted = this.removeElementBeginAndEnd(extracted);
        extracted = this.fixOrphanedForwardSlashes(extracted);

        let lastClosingTagIndex =
            extracted.lastIndexOf(">");

        if (lastClosingTagIndex > -1) {
            extracted = extracted.slice(0, lastClosingTagIndex);
        }

        extracted = this.reduceWhiteSpace(extracted);
        extracted = this.removeSelfTerminatingElements(extracted);

        extracted = extracted.trim();

        if (this.isNullOrWhiteSpace(extracted)) {
            return [];
        }

        let elements = extracted.split(" ").reverse();
        let minDepth = 0;
        let curDepth = 0;

        elements.forEach((element, index) => {
            let closing = element.startsWith("/");
            let newDepth = curDepth + (closing ? 1 : -1);
            if (newDepth < minDepth && !closing) {
                results.push(element);
                minDepth = newDepth;
            };
            curDepth = newDepth;
        });

        return results.reverse();

    }

    isNullOrWhiteSpace(value) {
        return value == null || (value != null && value.match(/^\s*$/) !== null);
    }

    fixOrphanedForwardSlashes(value) {
        return value.replace(/\s{1}\/{1}(\s{1}|$)/g, "/ ");
    }

    removeAttributes(value) {
        return value.replace(/[\S]+="([^"]*)"/gim, "");
    }

    removeMissedAttributes(value) {
        value = value.replace(/".*(?!=")"/gim, "");
        value = value.replace(/\w*\s*=/gim, "");
        return value;
    }

    removeElementBeginAndEnd(value) {
        return value.replace(/>([\s\S]*?)</gim, " ");
    }

    reduceWhiteSpace(value) {
        return value.replace(/\s+/gim, " ");
    }

    removeSelfTerminatingElements(value) {
        return value.replace(/([\w\d_-]*)(\/{1})(\W{1}|$)/gim, "");
    }

    removeRemainingElementBeginAndEnd(value) {
        return value.replace(/(<|>)/gim, "");
    }

}


self.addEventListener("message", (e) => {

    let extractor = new ElementExtractor();

    let result = extractor.extract({
        content: e.data.content,
        position: e.data.position
    });

    postMessage(result);

});