| 'use strict'; | 
|   | 
| const Mixin = require('../../utils/mixin'); | 
| const Tokenizer = require('../../tokenizer'); | 
| const PositionTrackingPreprocessorMixin = require('../position-tracking/preprocessor-mixin'); | 
|   | 
| class LocationInfoTokenizerMixin extends Mixin { | 
|     constructor(tokenizer) { | 
|         super(tokenizer); | 
|   | 
|         this.tokenizer = tokenizer; | 
|         this.posTracker = Mixin.install(tokenizer.preprocessor, PositionTrackingPreprocessorMixin); | 
|         this.currentAttrLocation = null; | 
|         this.ctLoc = null; | 
|     } | 
|   | 
|     _getCurrentLocation() { | 
|         return { | 
|             startLine: this.posTracker.line, | 
|             startCol: this.posTracker.col, | 
|             startOffset: this.posTracker.offset, | 
|             endLine: -1, | 
|             endCol: -1, | 
|             endOffset: -1 | 
|         }; | 
|     } | 
|   | 
|     _attachCurrentAttrLocationInfo() { | 
|         this.currentAttrLocation.endLine = this.posTracker.line; | 
|         this.currentAttrLocation.endCol = this.posTracker.col; | 
|         this.currentAttrLocation.endOffset = this.posTracker.offset; | 
|   | 
|         const currentToken = this.tokenizer.currentToken; | 
|         const currentAttr = this.tokenizer.currentAttr; | 
|   | 
|         if (!currentToken.location.attrs) { | 
|             currentToken.location.attrs = Object.create(null); | 
|         } | 
|   | 
|         currentToken.location.attrs[currentAttr.name] = this.currentAttrLocation; | 
|     } | 
|   | 
|     _getOverriddenMethods(mxn, orig) { | 
|         const methods = { | 
|             _createStartTagToken() { | 
|                 orig._createStartTagToken.call(this); | 
|                 this.currentToken.location = mxn.ctLoc; | 
|             }, | 
|   | 
|             _createEndTagToken() { | 
|                 orig._createEndTagToken.call(this); | 
|                 this.currentToken.location = mxn.ctLoc; | 
|             }, | 
|   | 
|             _createCommentToken() { | 
|                 orig._createCommentToken.call(this); | 
|                 this.currentToken.location = mxn.ctLoc; | 
|             }, | 
|   | 
|             _createDoctypeToken(initialName) { | 
|                 orig._createDoctypeToken.call(this, initialName); | 
|                 this.currentToken.location = mxn.ctLoc; | 
|             }, | 
|   | 
|             _createCharacterToken(type, ch) { | 
|                 orig._createCharacterToken.call(this, type, ch); | 
|                 this.currentCharacterToken.location = mxn.ctLoc; | 
|             }, | 
|   | 
|             _createEOFToken() { | 
|                 orig._createEOFToken.call(this); | 
|                 this.currentToken.location = mxn._getCurrentLocation(); | 
|             }, | 
|   | 
|             _createAttr(attrNameFirstCh) { | 
|                 orig._createAttr.call(this, attrNameFirstCh); | 
|                 mxn.currentAttrLocation = mxn._getCurrentLocation(); | 
|             }, | 
|   | 
|             _leaveAttrName(toState) { | 
|                 orig._leaveAttrName.call(this, toState); | 
|                 mxn._attachCurrentAttrLocationInfo(); | 
|             }, | 
|   | 
|             _leaveAttrValue(toState) { | 
|                 orig._leaveAttrValue.call(this, toState); | 
|                 mxn._attachCurrentAttrLocationInfo(); | 
|             }, | 
|   | 
|             _emitCurrentToken() { | 
|                 const ctLoc = this.currentToken.location; | 
|   | 
|                 //NOTE: if we have pending character token make it's end location equal to the | 
|                 //current token's start location. | 
|                 if (this.currentCharacterToken) { | 
|                     this.currentCharacterToken.location.endLine = ctLoc.startLine; | 
|                     this.currentCharacterToken.location.endCol = ctLoc.startCol; | 
|                     this.currentCharacterToken.location.endOffset = ctLoc.startOffset; | 
|                 } | 
|   | 
|                 if (this.currentToken.type === Tokenizer.EOF_TOKEN) { | 
|                     ctLoc.endLine = ctLoc.startLine; | 
|                     ctLoc.endCol = ctLoc.startCol; | 
|                     ctLoc.endOffset = ctLoc.startOffset; | 
|                 } else { | 
|                     ctLoc.endLine = mxn.posTracker.line; | 
|                     ctLoc.endCol = mxn.posTracker.col + 1; | 
|                     ctLoc.endOffset = mxn.posTracker.offset + 1; | 
|                 } | 
|   | 
|                 orig._emitCurrentToken.call(this); | 
|             }, | 
|   | 
|             _emitCurrentCharacterToken() { | 
|                 const ctLoc = this.currentCharacterToken && this.currentCharacterToken.location; | 
|   | 
|                 //NOTE: if we have character token and it's location wasn't set in the _emitCurrentToken(), | 
|                 //then set it's location at the current preprocessor position. | 
|                 //We don't need to increment preprocessor position, since character token | 
|                 //emission is always forced by the start of the next character token here. | 
|                 //So, we already have advanced position. | 
|                 if (ctLoc && ctLoc.endOffset === -1) { | 
|                     ctLoc.endLine = mxn.posTracker.line; | 
|                     ctLoc.endCol = mxn.posTracker.col; | 
|                     ctLoc.endOffset = mxn.posTracker.offset; | 
|                 } | 
|   | 
|                 orig._emitCurrentCharacterToken.call(this); | 
|             } | 
|         }; | 
|   | 
|         //NOTE: patch initial states for each mode to obtain token start position | 
|         Object.keys(Tokenizer.MODE).forEach(modeName => { | 
|             const state = Tokenizer.MODE[modeName]; | 
|   | 
|             methods[state] = function(cp) { | 
|                 mxn.ctLoc = mxn._getCurrentLocation(); | 
|                 orig[state].call(this, cp); | 
|             }; | 
|         }); | 
|   | 
|         return methods; | 
|     } | 
| } | 
|   | 
| module.exports = LocationInfoTokenizerMixin; |