function Tokenizer ( cbs ) { this . _state = "TEXT" ; this . _buffer = "" ; this . _sectionStart = 0 ; this . _index = 0 ; this . _cbs = cbs ; } Tokenizer . prototype . TEXT = function ( c ) { var index = this . _buffer . indexOf ( "<" , this . _index ) ; if ( index != - 1 ) { this . _index = index ; this . _cbs . ontext ( this . _getSection ( ) ) ; this . _state = "BeforeTag" ; this . _sectionStart = this . _index ; } else this . _index = this . _buffer . length ; } ; Tokenizer . prototype . BeforeTag = function ( c ) { switch ( c ) { case "/" : this . _state = "BeforeCloseTag" ; break ; case "!" : this . _state = "BeforeDeclaration" ; break ; case "?" : let index = this . _buffer . indexOf ( ">" , this . _index ) ; if ( index != - 1 ) { this . _index = index ; this . _sectionStart = this . _index + 1 ; } else this . _sectionStart = this . _index = this . _buffer . length ; this . _state = "TEXT" ; break ; case ">" : this . _state = "TEXT" ; break ; case "<" : this . _cbs . ontext ( this . _getSection ( ) ) ; this . _sectionStart = this . _index ; break ; default : if ( /\s/ . test ( c ) ) this . _state = "TEXT" ; else { this . _state = "InTag" ; this . _sectionStart = this . _index ; } } } ; Tokenizer . prototype . InTag = function ( c ) { if ( c === "/" || c === ">" || /\s/ . test ( c ) ) { this . _cbs . onopentagname ( this . _getSection ( ) ) ; this . _state = "BeforeAttrsName" ; this . _index -- ; } } ; Tokenizer . prototype . BeforeAttrsName = function ( c ) { if ( c === ">" ) { this . _cbs . onopentagend ( ) ; this . _state = "TEXT" ; this . _sectionStart = this . _index + 1 ; } else if ( c === "/" ) { this . _state = "InSelfCloseTag" ; } else if ( ! ( /\s/ . test ( c ) ) ) { this . _state = "InAttrsName" ; this . _sectionStart = this . _index ; } } ; Tokenizer . prototype . InAttrsName = function ( c ) { if ( c === "=" || c === "/" || c === ">" || /\s/ . test ( c ) ) { this . _cbs . _attribname = this . _getSection ( ) . toLowerCase ( ) ; this . _sectionStart = - 1 ; this . _state = "AfterAttrsName" ; this . _index -- ; } } ; Tokenizer . prototype . AfterAttrsName = function ( c ) { if ( c === "=" ) { this . _state = "BeforeAttrsValue" ; } else if ( c === "/" || c === ">" ) { this . _cbs . onattribend ( ) ; this . _state = "BeforeAttrsName" ; this . _index -- ; } else if ( ! ( /\s/ . test ( c ) ) ) { this . _cbs . onattribend ( ) ; this . _state = "InAttrsName" ; this . _sectionStart = this . _index ; } } ; Tokenizer . prototype . BeforeAttrsValue = function ( c ) { if ( c === '"' ) { this . _state = "InAttrsValueDQ" ; this . _sectionStart = this . _index + 1 ; } else if ( c === "'" ) { this . _state = "InAttrsValueSQ" ; this . _sectionStart = this . _index + 1 ; } else if ( ! ( /\s/ . test ( c ) ) ) { this . _state = "InAttrsValueNQ" ; this . _sectionStart = this . _index ; this . _index -- ; } } ; Tokenizer . prototype . InAttrsValueDQ = function ( c ) { if ( c === '"' ) { this . _cbs . _attribvalue += this . _getSection ( ) ; this . _cbs . onattribend ( ) ; this . _state = "BeforeAttrsName" ; } } ; Tokenizer . prototype . InAttrsValueSQ = function ( c ) { if ( c === "'" ) { this . _cbs . _attribvalue += this . _getSection ( ) ; this . _cbs . onattribend ( ) ; this . _state = "BeforeAttrsName" ; } } ; Tokenizer . prototype . InAttrsValueNQ = function ( c ) { if ( /\s/ . test ( c ) || c === ">" ) { this . _cbs . _attribvalue += this . _getSection ( ) ; this . _cbs . onattribend ( ) ; this . _state = "BeforeAttrsName" ; this . _index -- ; } } ; Tokenizer . prototype . BeforeCloseTag = function ( c ) { if ( /\s/ . test ( c ) ) ; else if ( c === ">" ) { this . _state = "TEXT" ; } else { this . _state = "InCloseTag" ; this . _sectionStart = this . _index ; } } ; Tokenizer . prototype . InCloseTag = function ( c ) { if ( c === ">" || /\s/ . test ( c ) ) { this . _cbs . onclosetag ( this . _getSection ( ) ) ; this . _state = "AfterCloseTag" ; this . _index -- ; } } ; Tokenizer . prototype . InSelfCloseTag = function ( c ) { if ( c === ">" ) { this . _cbs . onopentagend ( ) ; this . _state = "TEXT" ; this . _sectionStart = this . _index + 1 ; } else if ( ! ( /\s/ . test ( c ) ) ) { this . _state = "BeforeAttrsName" ; this . _index -- ; } } ; Tokenizer . prototype . AfterCloseTag = function ( c ) { if ( c === ">" ) { this . _state = "TEXT" ; this . _sectionStart = this . _index + 1 ; } } ; Tokenizer . prototype . BeforeDeclaration = function ( c ) { if ( c == '-' ) this . _state = "InComment" ; else if ( c == '[' ) this . _state = "BeforeCDATA1" ; else this . _state = "InDeclaration" ; } ; Tokenizer . prototype . InDeclaration = function ( c ) { var index = this . _buffer . indexOf ( ">" , this . _index ) ; if ( index != - 1 ) { this . _index = index ; this . _sectionStart = index + 1 ; } else this . _sectionStart = this . _index = this . _buffer . length ; this . _state = "TEXT" ; } ; Tokenizer . prototype . InComment = function ( c ) { let key = ( c == '-' ? '-->' : '>' ) ; let index = this . _buffer . indexOf ( key , this . _index ) ; if ( index != - 1 ) { this . _index = index + key . length - 1 ; this . _sectionStart = this . _index + 1 ; } else this . _sectionStart = this . _index = this . _buffer . length ; this . _state = "TEXT" ; } ; Tokenizer . prototype . BeforeCDATA1 = function ( c ) { if ( c == 'C' ) this . _state = "BeforeCDATA2" ; else this . _state = "InDeclaration" ; } ; Tokenizer . prototype . BeforeCDATA2 = function ( c ) { if ( c == 'D' ) this . _state = "BeforeCDATA3" ; else this . _state = "InDeclaration" ; } ; Tokenizer . prototype . BeforeCDATA3 = function ( c ) { if ( c == 'A' ) this . _state = "BeforeCDATA4" ; else this . _state = "InDeclaration" ; } ; Tokenizer . prototype . BeforeCDATA4 = function ( c ) { if ( c == 'T' ) this . _state = "BeforeCDATA5" ; else this . _state = "InDeclaration" ; } ; Tokenizer . prototype . BeforeCDATA5 = function ( c ) { if ( c == 'A' ) this . _state = "InCDATA" ; else this . _state = "InDeclaration" ; } ; Tokenizer . prototype . InCDATA = function ( c ) { let key = ( c == '[' ? ']]>' : '>' ) ; let index = this . _buffer . indexOf ( key , this . _index ) ; if ( index != - 1 ) { this . _index = index + key . length - 1 ; this . _sectionStart = this . _index + 1 ; } else this . _sectionStart = this . _index = this . _buffer . length ; this . _state = "TEXT" ; } ; Tokenizer . prototype . parse = function ( chunk ) { this . _buffer += chunk ; for ( ; this . _index < this . _buffer . length ; this . _index ++ ) this [ this . _state ] ( this . _buffer [ this . _index ] ) ; if ( this . _state === "TEXT" && this . _sectionStart !== this . _index ) this . _cbs . ontext ( this . _buffer . substr ( this . _sectionStart ) ) ; this . _cbs . onend ( ) ; } ; Tokenizer . prototype . _getSection = function ( ) { return this . _buffer . substring ( this . _sectionStart , this . _index ) ; } ; module . exports = Tokenizer ;