function parseElementStartPart(source, start, el, currentNSMap, entityReplacer, errorHandler) {
    let attrName = '';
    let value = '';
    let p = ++start;
    let s = S_TAG; // Status
    while (true) {
        let c = source.charAt(p);
        switch (c) {
            case '=':
                if (s === S_ATTR) {
                    // attrName
                    attrName = source.slice(start, p);
                    s = S_EQ;
                }
                else if (s === S_ATTR_SPACE) {
                    s = S_EQ;
                }
                else {
                    // fatalError: equal must after attrName or space after attrName
                    throw new Error('attribute equal must after attrName');
                }
                break;
            // tslint:disable-next-line: quotemark
            case "'":
            case '"':
                if (s === S_EQ ||
                    s === S_ATTR // || s == S_ATTR_SPACE
                ) {
                    // equal
                    if (s === S_ATTR) {
                        errorHandler.warning('attribute value must after "="');
                        attrName = source.slice(start, p);
                    }
                    start = p + 1;
                    p = source.indexOf(c, start);
                    if (p > 0) {
                        value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
                        el.add(attrName, value, start - 1);
                        s = S_ATTR_END;
                    }
                    else {
                        // fatalError: no end quot match
                        throw new Error(`attribute value no end '${c}' match`);
                    }
                }
                else if (s === S_ATTR_NOQUOT_VALUE) {
                    value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
                    // console.log(attrName,value,start,p)
                    el.add(attrName, value, start);
                    // console.dir(el)
                    errorHandler.warning('attribute "' + attrName + '" missed start quot(' + c + ')!!');
                    start = p + 1;
                    s = S_ATTR_END;
                }
                else {
                    // fatalError: no equal before
                    throw new Error('attribute value must folow after "="');
                }
                break;
            case '/':
                switch (s) {
                    case S_TAG:
                        el.setTagName(source.slice(start, p));
                    case S_ATTR_END:
                    case S_TAG_SPACE:
                    case S_TAG_CLOSE:
                        s = S_TAG_CLOSE;
                        el.closed = true;
                    case S_ATTR_NOQUOT_VALUE:
                    case S_ATTR:
                    case S_ATTR_SPACE:
                        break;
                    // case S_EQ:
                    default:
                        throw new Error(`attribute invalid close char('/')`);
                }
                break;
            case '': // end document
                // throw new Error('unexpected end of input')
                errorHandler.error('unexpected end of input');
                if (s === S_TAG) {
                    el.setTagName(source.slice(start, p));
                }
                return p;
            case '>':
                switch (s) {
                    case S_TAG:
                        el.setTagName(source.slice(start, p));
                    case S_ATTR_END:
                    case S_TAG_SPACE:
                    case S_TAG_CLOSE:
                        break; // normal
                    case S_ATTR_NOQUOT_VALUE: // compatible state
                    case S_ATTR:
                        value = source.slice(start, p);
                        if (value.slice(-1) === '/') {
                            el.closed = true;
                            value = value.slice(0, -1);
                        }
                    case S_ATTR_SPACE:
                        if (s === S_ATTR_SPACE) {
                            value = attrName;
                        }
                        if (s === S_ATTR_NOQUOT_VALUE) {
                            errorHandler.warning('attribute "' + value + '" missed quot(")!!');
                            el.add(attrName, value.replace(/&#?\w+;/g, entityReplacer), start);
                        }
                        else {
                            if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' ||
                                !value.match(/^(?:disabled|checked|selected)$/i)) {
                                errorHandler.warning('attribute "' + value + '" missed value!! "' + value + '" instead!!');
                            }
                            el.add(value, value, start);
                        }
                        break;
                    case S_EQ:
                        throw new Error('attribute value missed!!');
                }
                // console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
                return p;
            /*xml space '\x20' | #x9 | #xD | #xA; */
            case '\u0080':
                c = ' ';
            default:
                if (c <= ' ') {
                    // Space
                    switch (s) {
                        case S_TAG:
                            el.setTagName(source.slice(start, p)); // tagName
                            s = S_TAG_SPACE;
                            break;
                        case S_ATTR:
                            attrName = source.slice(start, p);
                            s = S_ATTR_SPACE;
                            break;
                        case S_ATTR_NOQUOT_VALUE:
                            value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
                            errorHandler.warning('attribute "' + value + '" missed quot(")!!');
                            el.add(attrName, value, start);
                        case S_ATTR_END:
                            s = S_TAG_SPACE;
                            break;
                        // case S_TAG_SPACE:
                        // case S_EQ:
                        // case S_ATTR_SPACE:
                        // void();break;
                        // case S_TAG_CLOSE:
                        // ignore warning
                    }
                }
                else {
                    // not space
                    // S_TAG,	S_ATTR,	S_EQ,	S_ATTR_NOQUOT_VALUE
                    // S_ATTR_SPACE,	S_ATTR_END,	S_TAG_SPACE, S_TAG_CLOSE
                    switch (s) {
                        // case S_TAG:void();break;
                        // case S_ATTR:void();break;
                        // case S_ATTR_NOQUOT_VALUE:void();break;
                        case S_ATTR_SPACE:
                            // const tagName = el.tagName; // todo: not used
                            if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' ||
                                !attrName.match(/^(?:disabled|checked|selected)$/i)) {
                                errorHandler.warning('attribute "' + attrName + '" missed value!! "' + attrName + '" instead2!!');
                            }
                            el.add(attrName, attrName, start);
                            start = p;
                            s = S_ATTR;
                            break;
                        case S_ATTR_END:
                            errorHandler.warning('attribute space is required"' + attrName + '"!!');
                        case S_TAG_SPACE:
                            s = S_ATTR;
                            start = p;
                            break;
                        case S_EQ:
                            s = S_ATTR_NOQUOT_VALUE;
                            start = p;
                            break;
                        case S_TAG_CLOSE:
                            throw new Error(`elements closed character '/' and '>' must be connected to`);
                    }
                }
        } // end outer switch
        // console.log('p++',p)
        p++;
    }
}
/**
 * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
 * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
 */
@see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
@return end of the elementStartPart(end of elementEndPart for selfClosed el)
@see #appendElement ( source elStartEnd el selfClosed entityReplacer domBuilder parseStack ) ; @return end of the elementStartPart ( end of elementEndPart for selfClosed el )
function parseElementStartPart(source, start, el, currentNSMap, entityReplacer, errorHandler) {
    let attrName = '';
    let value = '';
    let p = ++start;
    let s = S_TAG; 
    while (true) {
        let c = source.charAt(p);
        switch (c) {
            case '=':
                if (s === S_ATTR) {
                    attrName = source.slice(start, p);
                    s = S_EQ;
                }
                else if (s === S_ATTR_SPACE) {
                    s = S_EQ;
                }
                else {
                    throw new Error('attribute equal must after attrName');
                }
                break;
            case "'":
            case '"':
                if (s === S_EQ ||
                    s === S_ATTR 
                ) {
                    if (s === S_ATTR) {
                        errorHandler.warning('attribute value must after "="');
                        attrName = source.slice(start, p);
                    }
                    start = p + 1;
                    p = source.indexOf(c, start);
                    if (p > 0) {
                        value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
                        el.add(attrName, value, start - 1);
                        s = S_ATTR_END;
                    }
                    else {
                        throw new Error(`attribute value no end '${c}' match`);
                    }
                }
                else if (s === S_ATTR_NOQUOT_VALUE) {
                    value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
                    el.add(attrName, value, start);
                    errorHandler.warning('attribute "' + attrName + '" missed start quot(' + c + ')!!');
                    start = p + 1;
                    s = S_ATTR_END;
                }
                else {
                    throw new Error('attribute value must folow after "="');
                }
                break;
            case '/':
                switch (s) {
                    case S_TAG:
                        el.setTagName(source.slice(start, p));
                    case S_ATTR_END:
                    case S_TAG_SPACE:
                    case S_TAG_CLOSE:
                        s = S_TAG_CLOSE;
                        el.closed = true;
                    case S_ATTR_NOQUOT_VALUE:
                    case S_ATTR:
                    case S_ATTR_SPACE:
                        break;
                    default:
                        throw new Error(`attribute invalid close char('/')`);
                }
                break;
            case '': 
                errorHandler.error('unexpected end of input');
                if (s === S_TAG) {
                    el.setTagName(source.slice(start, p));
                }
                return p;
            case '>':
                switch (s) {
                    case S_TAG:
                        el.setTagName(source.slice(start, p));
                    case S_ATTR_END:
                    case S_TAG_SPACE:
                    case S_TAG_CLOSE:
                        break; 
                    case S_ATTR_NOQUOT_VALUE: 
                    case S_ATTR:
                        value = source.slice(start, p);
                        if (value.slice(-1) === '/') {
                            el.closed = true;
                            value = value.slice(0, -1);
                        }
                    case S_ATTR_SPACE:
                        if (s === S_ATTR_SPACE) {
                            value = attrName;
                        }
                        if (s === S_ATTR_NOQUOT_VALUE) {
                            errorHandler.warning('attribute "' + value + '" missed quot(")!!');
                            el.add(attrName, value.replace(/&#?\w+;/g, entityReplacer), start);
                        }
                        else {
                            if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' ||
                                !value.match(/^(?:disabled|checked|selected)$/i)) {
                                errorHandler.warning('attribute "' + value + '" missed value!! "' + value + '" instead!!');
                            }
                            el.add(value, value, start);
                        }
                        break;
                    case S_EQ:
                        throw new Error('attribute value missed!!');
                }
                return p;
            case '\u0080':
                c = ' ';
            default:
                if (c <= ' ') {
                    switch (s) {
                        case S_TAG:
                            el.setTagName(source.slice(start, p)); 
                            s = S_TAG_SPACE;
                            break;
                        case S_ATTR:
                            attrName = source.slice(start, p);
                            s = S_ATTR_SPACE;
                            break;
                        case S_ATTR_NOQUOT_VALUE:
                            value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
                            errorHandler.warning('attribute "' + value + '" missed quot(")!!');
                            el.add(attrName, value, start);
                        case S_ATTR_END:
                            s = S_TAG_SPACE;
                            break;
                    }
                }
                else {
                    switch (s) {
                        case S_ATTR_SPACE:
                            if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' ||
                                !attrName.match(/^(?:disabled|checked|selected)$/i)) {
                                errorHandler.warning('attribute "' + attrName + '" missed value!! "' + attrName + '" instead2!!');
                            }
                            el.add(attrName, attrName, start);
                            start = p;
                            s = S_ATTR;
                            break;
                        case S_ATTR_END:
                            errorHandler.warning('attribute space is required"' + attrName + '"!!');
                        case S_TAG_SPACE:
                            s = S_ATTR;
                            start = p;
                            break;
                        case S_EQ:
                            s = S_ATTR_NOQUOT_VALUE;
                            start = p;
                            break;
                        case S_TAG_CLOSE:
                            throw new Error(`elements closed character '/' and '>' must be connected to`);
                    }
                }
        } 
        p++;
    }
}
function parseElementStartPart ( source , start , el , currentNSMap , entityReplacer , errorHandler ) { let attrName = '' ; let value = '' ; let p = ++ start ; let s = S_TAG ; while ( true ) { let c = source . charAt ( p ) ; switch ( c ) { case '=' : if ( s === S_ATTR ) { attrName = source . slice ( start , p ) ; s = S_EQ ; } else if ( s === S_ATTR_SPACE ) { s = S_EQ ; } else { throw new Error ( 'attribute equal must after attrName' ) ; } break ; case "'" : case '"' : if ( s === S_EQ || s === S_ATTR ) { if ( s === S_ATTR ) { errorHandler . warning ( 'attribute value must after "="' ) ; attrName = source . slice ( start , p ) ; } start = p + 1 ; p = source . indexOf ( c , start ) ; if ( p > 0 ) { value = source . slice ( start , p ) . replace ( / &#?\w+; / g , entityReplacer ) ; el . add ( attrName , value , start - 1 ) ; s = S_ATTR_END ; } else { throw new Error ( ` ${ c } ` ) ; } } else if ( s === S_ATTR_NOQUOT_VALUE ) { value = source . slice ( start , p ) . replace ( / &#?\w+; / g , entityReplacer ) ; el . add ( attrName , value , start ) ; errorHandler . warning ( 'attribute "' + attrName + '" missed start quot(' + c + ')!!' ) ; start = p + 1 ; s = S_ATTR_END ; } else { throw new Error ( 'attribute value must folow after "="' ) ; } break ; case '/' : switch ( s ) { case S_TAG : el . setTagName ( source . slice ( start , p ) ) ; case S_ATTR_END : case S_TAG_SPACE : case S_TAG_CLOSE : s = S_TAG_CLOSE ; el . closed = true ; case S_ATTR_NOQUOT_VALUE : case S_ATTR : case S_ATTR_SPACE : break ; default : throw new Error ( ` ` ) ; } break ; case '' : errorHandler . error ( 'unexpected end of input' ) ; if ( s === S_TAG ) { el . setTagName ( source . slice ( start , p ) ) ; } return p ; case '>' : switch ( s ) { case S_TAG : el . setTagName ( source . slice ( start , p ) ) ; case S_ATTR_END : case S_TAG_SPACE : case S_TAG_CLOSE : break ; case S_ATTR_NOQUOT_VALUE : case S_ATTR : value = source . slice ( start , p ) ; if ( value . slice ( - 1 ) === '/' ) { el . closed = true ; value = value . slice ( 0 , - 1 ) ; } case S_ATTR_SPACE : if ( s === S_ATTR_SPACE ) { value = attrName ; } if ( s === S_ATTR_NOQUOT_VALUE ) { errorHandler . warning ( 'attribute "' + value + '" missed quot(")!!' ) ; el . add ( attrName , value . replace ( / &#?\w+; / g , entityReplacer ) , start ) ; } else { if ( currentNSMap [ '' ] !== 'http://www.w3.org/1999/xhtml' || ! value . match ( / ^(?:disabled|checked|selected)$ / i ) ) { errorHandler . warning ( 'attribute "' + value + '" missed value!! "' + value + '" instead!!' ) ; } el . add ( value , value , start ) ; } break ; case S_EQ : throw new Error ( 'attribute value missed!!' ) ; } return p ; case '\u0080' : c = ' ' ; default : if ( c <= ' ' ) { switch ( s ) { case S_TAG : el . setTagName ( source . slice ( start , p ) ) ; s = S_TAG_SPACE ; break ; case S_ATTR : attrName = source . slice ( start , p ) ; s = S_ATTR_SPACE ; break ; case S_ATTR_NOQUOT_VALUE : value = source . slice ( start , p ) . replace ( / &#?\w+; / g , entityReplacer ) ; errorHandler . warning ( 'attribute "' + value + '" missed quot(")!!' ) ; el . add ( attrName , value , start ) ; case S_ATTR_END : s = S_TAG_SPACE ; break ; } } else { switch ( s ) { case S_ATTR_SPACE : if ( currentNSMap [ '' ] !== 'http://www.w3.org/1999/xhtml' || ! attrName . match ( / ^(?:disabled|checked|selected)$ / i ) ) { errorHandler . warning ( 'attribute "' + attrName + '" missed value!! "' + attrName + '" instead2!!' ) ; } el . add ( attrName , attrName , start ) ; start = p ; s = S_ATTR ; break ; case S_ATTR_END : errorHandler . warning ( 'attribute space is required"' + attrName + '"!!' ) ; case S_TAG_SPACE : s = S_ATTR ; start = p ; break ; case S_EQ : s = S_ATTR_NOQUOT_VALUE ; start = p ; break ; case S_TAG_CLOSE : throw new Error ( ` ` ) ; } } } p ++ ; } }
@see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
 @return end of the elementStartPart(end of elementEndPart for selfClosed el)
@see #appendElement ( source elStartEnd el selfClosed entityReplacer domBuilder parseStack ) ; @return end of the elementStartPart ( end of elementEndPart for selfClosed el )
// Status // attrName // fatalError: equal must after attrName or space after attrName // tslint:disable-next-line: quotemark // || s == S_ATTR_SPACE // equal // fatalError: no end quot match // console.log(attrName,value,start,p) // console.dir(el) // fatalError: no equal before // case S_EQ: // end document // throw new Error('unexpected end of input') // normal // compatible state // console.log(tagName,tagNamePattern,tagNamePattern.test(tagName)) /*xml space '\x20' | #x9 | #xD | #xA; */ // Space // tagName // case S_TAG_SPACE: // case S_EQ: // case S_ATTR_SPACE: // void();break; // case S_TAG_CLOSE: // ignore warning // not space // S_TAG,	S_ATTR,	S_EQ,	S_ATTR_NOQUOT_VALUE // S_ATTR_SPACE,	S_ATTR_END,	S_TAG_SPACE, S_TAG_CLOSE // case S_TAG:void();break; // case S_ATTR:void();break; // case S_ATTR_NOQUOT_VALUE:void();break; // const tagName = el.tagName; // todo: not used // end outer switch // console.log('p++',p)