/*
 * SDK Pullenti Unitext, version 4.33, fabruary 2026. Copyright (c) 2013-2026, Pullenti. All rights reserved. 
 * Non-Commercial Freeware and Commercial Software. 
 * This class is generated using the converter UniSharping (www.unisharping.ru) from Pullenti C# project. 
 * The latest version of the code is available on the site www.pullenti.ru
 */

const Utils = require("./../../../unisharp/Utils");
const Hashtable = require("./../../../unisharp/Hashtable");
const RefOutArgWrapper = require("./../../../unisharp/RefOutArgWrapper");
const StringBuilder = require("./../../../unisharp/StringBuilder");

const HtmlParserNodeTagTypes = require("./HtmlParserNodeTagTypes");

class HtmlParserNode {
    
    constructor() {
        this.indexFrom = 0;
        this.indexTo = 0;
        this.tagName = null;
        this.tagType = HtmlParserNodeTagTypes.UNDEFINED;
        this.tag = null;
        this.closeTagIndex = 0;
        this.attributes = new Hashtable();
        this.pureText = null;
        this.whitespacePreserve = false;
        this.whitespaceBefore = false;
        this.printed = false;
    }
    
    get isEmpty() {
        if (this.tagName !== null) 
            return false;
        if (this.attributes !== null && this.attributes.length > 0) 
            return false;
        if (!Utils.isNullOrEmpty(this.pureText)) {
            if (this.whitespacePreserve) 
                return false;
            for (let i = 0; i < this.pureText.length; i++) {
                if (!Utils.isWhitespace(this.pureText[i])) 
                    return false;
            }
        }
        return true;
    }
    
    toString() {
        let res = new StringBuilder();
        if (this.tagType !== HtmlParserNodeTagTypes.UNDEFINED) {
            if (this.tagType === HtmlParserNodeTagTypes.CLOSE) 
                res.append("</").append(this.tagName).append(">");
            else {
                res.append("<").append(this.tagName);
                if (this.attributes !== null) {
                    for (const kp of this.attributes.entries) {
                        res.append(" ").append(kp.key).append("='").append(kp.value).append("'");
                    }
                }
                if (this.tagType === HtmlParserNodeTagTypes.OPEN) 
                    res.append(">");
                else 
                    res.append("/>");
            }
        }
        else if (this.pureText !== null) 
            res.append(this.pureText);
        return res.toString();
    }
    
    analize(_html, indFrom, endTag, whiteSpacePreserve) {
        if (indFrom >= _html.length) 
            return false;
        this.indexFrom = indFrom;
        this.tagName = null;
        this.tagType = HtmlParserNodeTagTypes.UNDEFINED;
        if (this.attributes === null) 
            this.attributes = new Hashtable();
        else 
            this.attributes.clear();
        this.pureText = null;
        let i = 0;
        let j = 0;
        let ch = _html[indFrom];
        if (ch !== '<' || ((endTag !== null && !HtmlParserNode.isEndTag(_html, indFrom, endTag)))) {
            let strTmp = null;
            let comment = false;
            let lastNbsp = 0;
            for (i = indFrom; i < _html.length; i++) {
                let nbr = false;
                if (_html[i] === '<') {
                    if (!comment) {
                        if (endTag === null || HtmlParserNode.isEndTag(_html, i, endTag)) 
                            break;
                    }
                    if ((((i + 3) < _html.length) && _html[i + 1] === '!' && _html[i + 2] === '-') && _html[i + 3] === '-') {
                        comment = true;
                        i += 3;
                        continue;
                    }
                    ch = _html[i];
                    j = 1;
                }
                else {
                    if (comment && _html[i] === '-') {
                        if (((i + 2) < _html.length) && _html[i + 1] === '-' && _html[i + 2] === '>') {
                            comment = false;
                            i += 2;
                            continue;
                        }
                    }
                    let wrapch28 = new RefOutArgWrapper();
                    let wrapnbr29 = new RefOutArgWrapper();
                    let inoutres30 = HtmlParserNode.parseChar(_html, i, wrapch28, wrapnbr29);
                    ch = wrapch28.value;
                    nbr = wrapnbr29.value;
                    if ((((j = inoutres30))) < 1) 
                        break;
                }
                if (Utils.isWhitespace(ch)) 
                    ch = ' ';
                if (ch === ' ' && !whiteSpacePreserve) {
                    let prevSp = (strTmp === null ? true : strTmp.charAt(strTmp.length - 1) === ' ');
                    if (!prevSp || nbr) {
                        if (strTmp === null) 
                            strTmp = new StringBuilder();
                        strTmp.append(ch);
                    }
                    if (nbr) 
                        lastNbsp++;
                }
                else {
                    lastNbsp = 0;
                    if (strTmp === null) 
                        strTmp = new StringBuilder();
                    if (nbr && whiteSpacePreserve && ch === ' ') 
                        ch = String.fromCharCode(0xA0);
                    strTmp.append(ch);
                }
                if (whiteSpacePreserve) 
                    this.whitespacePreserve = true;
                i += (j - 1);
            }
            if (lastNbsp === 0 && !whiteSpacePreserve && strTmp !== null) {
                for (let jj = strTmp.length - 1; jj > 0; jj--) {
                    if (strTmp.charAt(jj) !== ' ') 
                        break;
                    else 
                        strTmp.length = strTmp.length - 1;
                }
            }
            if (strTmp !== null) 
                this.pureText = strTmp.toString();
            this.indexTo = i - 1;
            return true;
        }
        if ((indFrom + 2) >= _html.length) 
            return false;
        ch = _html[indFrom + 1];
        if (Utils.isLetter(ch)) {
            if (indFrom > 0 && Utils.isWhitespace(_html[indFrom - 1])) 
                this.whitespaceBefore = true;
            this.tagName = HtmlParserNode.readLatinWord(_html, indFrom + 1);
            if (this.tagName === null) {
                this.indexTo = indFrom + 1;
                return true;
            }
            if (this.tagName === "BR") {
            }
            this.tagType = HtmlParserNodeTagTypes.OPEN;
            let strTmp = null;
            for (i = indFrom + 1 + this.tagName.length; i < _html.length; i++) {
                ch = _html[i];
                if (Utils.isWhitespace(ch)) 
                    continue;
                if (ch === '>') {
                    i++;
                    break;
                }
                if (ch === '/') {
                    if ((i + 1) >= _html.length) 
                        return false;
                    if (_html[i + 1] === '>') 
                        i++;
                    this.tagType = HtmlParserNodeTagTypes.OPENCLOSE;
                    i++;
                    break;
                }
                let attrName = HtmlParserNode.readLatinWord(_html, i);
                if (attrName === null) 
                    continue;
                i += attrName.length;
                if ((i + 2) >= _html.length || _html[i] !== '=') {
                    i--;
                    continue;
                }
                i++;
                if (strTmp !== null) 
                    strTmp.length = 0;
                let bracket = String.fromCharCode(0);
                if (_html[i] === '\'' || _html[i] === '"') {
                    bracket = _html[i];
                    i++;
                }
                for (; i < _html.length; i++) {
                    ch = _html[i];
                    if (bracket !== (String.fromCharCode(0))) {
                        if (ch === bracket) {
                            i++;
                            break;
                        }
                    }
                    else if (ch === '>') 
                        break;
                    else if (Utils.isWhitespace(ch)) 
                        break;
                    else if (ch === '/' && ((i + 1) < _html.length) && _html[i + 1] === '>') {
                    }
                    let nbr = false;
                    let wrapch31 = new RefOutArgWrapper();
                    let wrapnbr32 = new RefOutArgWrapper();
                    let inoutres33 = HtmlParserNode.parseChar(_html, i, wrapch31, wrapnbr32);
                    ch = wrapch31.value;
                    nbr = wrapnbr32.value;
                    if ((((j = inoutres33))) < 1) {
                        ch = _html[i];
                        j = 1;
                    }
                    if (strTmp === null) 
                        strTmp = new StringBuilder();
                    strTmp.append(ch);
                    i += (j - 1);
                }
                i--;
                if (!this.attributes.containsKey(attrName) && strTmp !== null && strTmp.length > 0) 
                    this.attributes.put(attrName, strTmp.toString());
            }
            this.indexTo = i - 1;
            return true;
        }
        if (_html[indFrom + 1] === '/') {
            i = indFrom + 2;
            this.tagName = HtmlParserNode.readLatinWord(_html, i);
            if (this.tagName !== null) {
                i += this.tagName.length;
                if ((i < _html.length) && _html[i] === '>') {
                    i++;
                    this.tagType = HtmlParserNodeTagTypes.CLOSE;
                }
            }
            this.indexTo = i - 1;
            return true;
        }
        if ((((indFrom + 5) < _html.length) && _html[indFrom + 1] === '!' && _html[indFrom + 2] === '-') && _html[indFrom + 3] === '-') {
            for (i = indFrom + 4; i < (_html.length - 3); i++) {
                if (_html[i] === '-' && _html[i + 1] === '-' && _html[i + 2] === '>') {
                    this.indexTo = i + 2;
                    return true;
                }
            }
            return false;
        }
        for (i = indFrom; i < _html.length; i++) {
            if (_html[i] === '>') {
                i++;
                break;
            }
        }
        this.indexTo = i - 1;
        return true;
    }
    
    static decodeString(txt) {
        if (Utils.isNullOrEmpty(txt) || (txt.indexOf('&') < 0)) 
            return txt;
        let res = new StringBuilder();
        for (let i = 0; i < txt.length; i++) {
            let ch = '\0';
            let nbsp = false;
            let wrapch34 = new RefOutArgWrapper();
            let wrapnbsp35 = new RefOutArgWrapper();
            let j = HtmlParserNode.parseChar(txt, i, wrapch34, wrapnbsp35);
            ch = wrapch34.value;
            nbsp = wrapnbsp35.value;
            if (j < 0) 
                break;
            res.append(ch);
            i += (j - 1);
        }
        return res.toString();
    }
    
    static parseChar(txt, ind, ch, nbsp) {
        ch.value = String.fromCharCode(0);
        nbsp.value = false;
        if (ind >= txt.length) 
            return -1;
        if (txt[ind] !== '&') {
            ch.value = txt[ind];
            return 1;
        }
        let strTmp = new StringBuilder();
        let i = 0;
        for (i = ind + 1; i < txt.length; i++) {
            if (txt[i] === ';') 
                break;
            else {
                strTmp.append(txt[i]);
                if (strTmp.length > 7) 
                    break;
            }
        }
        if (i >= txt.length || txt[i] !== ';') {
            if (strTmp.toString().startsWith("nbsp")) {
                ch.value = ' ';
                nbsp.value = true;
                return 5;
            }
            ch.value = '&';
            return 1;
        }
        let ret = (i + 1) - ind;
        if (strTmp.length === 0) 
            return ret;
        let s = strTmp.toString();
        if (HtmlParserNode.m_SpecChars.containsKey(s)) {
            ch.value = HtmlParserNode.m_SpecChars.get(s);
            if (s === "nbsp") 
                nbsp.value = true;
            return ret;
        }
        if (s[0] !== '#' || (strTmp.length < 2)) {
            ch.value = '&';
            return ret;
        }
        if (Utils.isDigit(s[1])) {
            try {
                ch.value = String.fromCharCode(Utils.parseInt(s.substring(1)));
                nbsp.value = Utils.isWhitespace(ch.value);
            } catch (ex36) {
            }
            return ret;
        }
        if (s[1] === 'x' || s[1] === 'X') {
            try {
                let code = 0;
                s = s.toUpperCase();
                for (let ii = 2; ii < s.length; ii++) {
                    if (Utils.isDigit(s[ii])) 
                        code = (code << 4) + (((s.charCodeAt(ii)) - ('0'.charCodeAt(0))));
                    else if (s[ii] >= 'A' && s[ii] <= 'F') 
                        code = (code << 4) + ((((s.charCodeAt(ii)) - ('A'.charCodeAt(0))) + 10));
                }
                ch.value = String.fromCharCode(code);
                nbsp.value = Utils.isWhitespace(ch.value);
            } catch (ex37) {
            }
            return ret;
        }
        ch.value = s[1];
        return ret;
    }
    
    static readLatinWord(str, ind) {
        if ((ind + 1) >= str.length) 
            return null;
        let i = 0;
        for (i = ind; i < str.length; i++) {
            if (!Utils.isLetter(str[i]) && str[i] !== ':' && str[i] !== '_') {
                if (i === 0) 
                    break;
                if (!Utils.isDigit(str[i]) && str[i] !== '.' && str[i] !== '-') 
                    break;
            }
            else {
                let cod = str.charCodeAt(i);
                if (cod > 0x80) 
                    break;
            }
        }
        if (i <= ind) 
            return null;
        return str.substring(ind, ind + i - ind).toUpperCase();
    }
    
    static isEndTag(_html, pos, _tagName) {
        if ((pos + 3) >= _html.length || _tagName === null) 
            return false;
        if (_html[pos] !== '<' && _html[pos + 1] !== '/') 
            return false;
        if (HtmlParserNode.readLatinWord(_html, pos + 2) !== _tagName) 
            return false;
        return true;
    }
    
    static static_constructor() {
        HtmlParserNode.m_SpecChars = null;
        HtmlParserNode.m_SpecChars = new Hashtable();
        HtmlParserNode.m_SpecChars.put("nbsp", ' ');
        HtmlParserNode.m_SpecChars.put("lt", '<');
        HtmlParserNode.m_SpecChars.put("gt", '>');
        HtmlParserNode.m_SpecChars.put("quot", '"');
        HtmlParserNode.m_SpecChars.put("apos", '\'');
        let data = "iexcl=161;cent=162;pound=163;curren=164;yen=165;brvbar=166;sect=167;uml=168;copy=169;ordf=170;laquo=171;not=172;shy=173;reg=174;macr=175;deg=176;plusmn=177;sup2=178;sup3=179;acute=180;micro=181;para=182;middot=183;cedil=184;sup1=185;ordm=186;raquo=187;frac14=188;frac12=189;frac34=190;iquest=191;Agrave=192;Aacute=193;Acirc=194;Atilde=195;Auml=196;Aring=197;AElig=198;Ccedil=199;Egrave=200;Eacute=201;Ecirc=202;Euml=203;Igrave=204;Iacute=205;Icirc=206;Iuml=207;ETH=208;Ntilde=209;Ograve=210;Oacute=211;Ocirc=212;Otilde=213;Ouml=214;times=215;Oslash=216;Ugrave=217;Uacute=218;Ucirc=219;Uuml=220;Yacute=221;THORN=222;szlig=223;agrave=224;aacute=225;acirc=226;atilde=227;auml=228;aring=229;aelig=230;ccedil=231;egrave=232;eacute=233;ecirc=234;euml=235;igrave=236;iacute=237;icirc=238;iuml=239;eth=240;ntilde=241;ograve=242;oacute=243;ocirc=244;otilde=245;ouml=246;divide=247;oslash=248;ugrave=249;uacute=250;ucirc=251;uuml=252;yacute=253;thorn=254;yuml=255;Alpha=913;Beta=914;Gamma=915;Delta=916;Epsilon=917;Zeta=918;Eta=919;Theta=920;Iota=921;Kappa=922;Lambda=923;Mu=924;Nu=925;Xi=926;Omicron=927;Pi=928;Rho=929;Sigma=931;Tau=932;Upsilon=933;Phi=934;Chi=935;Psi=936;Omega=937;alpha=945;beta=946;gamma=947;delta=948;epsilon=949;zeta=950;eta=951;theta=952;iota=953;kappa=954;lambda=955;mu=956;nu=957;xi=958;omicron=959;pi=960;rho=961;sigmaf=962;sigma=963;tau=964;upsilon=965;phi=966;chi=967;psi=968;omega=969;thetasym=977;upsih=978;piv=982;bull=8226;hellip=8230;prime=8242;Prime=8243;oline=8254;frasl=8260;weierp=8472;image=8465;real=8476;trade=8482;alefsym=8501;larr=8592;uarr=8593;rarr=8594;darr=8595;harr=8596;crarr=8629;lArr=8656;uArr=8657;rArr=8658;dArr=8659;hArr=8660;forall=8704;part=8706;exist=8707;empty=8709;nabla=8711;isin=8712;notin=8713;ni=8715;prod=8719;sum=8721;minus=8722;lowast=8727;radic=8730;prop=8733;infin=8734;ang=8736;and=8743;or=8744;cap=8745;cup=8746;int=8747;there4=8756;sim=8764;cong=8773;asymp=8776;ne=8800;equiv=8801;le=8804;ge=8805;sub=8834;sup=8835;nsub=8836;sube=8838;supe=8839;oplus=8853;otimes=8855;perp=8869;sdot=8901;lceil=8968;rceil=8969;lfloor=8970;rfloor=8971;lang=9001;rang=9002;loz=9674;spades=9824;clubs=9827;hearts=9829;diams=9830;amp=38;OElig=338;oelig=339;Scaron=352;scaron=353;Yuml=376;circ=710;tilde=732;ensp=8194;emsp=8195;thinsp=8201;zwnj=8204;zwj=8205;lrm=8206;rlm=8207;ndash=8211;mdash=8212;lsquo=8216;rsquo=8217;sbquo=8218;ldquo=8220;rdquo=8221;bdquo=8222;dagger=8224;Dagger=8225;permil=8240;lsaquo=8249;rsaquo=8250;euro=8364";
        for (const s of Utils.splitString(data, ';', false)) {
            let i = s.indexOf('=');
            let key = s.substring(0, 0 + i);
            let cod = Utils.parseInt(s.substring(i + 1));
            HtmlParserNode.m_SpecChars.put(key, String.fromCharCode(cod));
        }
    }
}


HtmlParserNode.static_constructor();

module.exports = HtmlParserNode