var AST = require('src/ast') , CharBuf = require('src/char-buf') ; /** * Dom handler for htmlparser2 * @class DomHandler * @param {Stream} stream - The parser stream */ var DomHandler = module.exports = function DomHandler(stream) { if(!(this instanceof DomHandler)) return new DomHandler(stream); /** @member {MCNode[]} DomHandler#ast */ this.ast = []; /** @member {Stream} DomHandler#_stream */ this._stream = stream; /** @member {Boolean} DomHandler#_done */ this._done = false; /** @member {MCTag[]} DomHandler#_tagStack */ this._tagStack = []; /** @member {HtmlParser} DomHandler#_parser */ this._parser = this._parser || null; } /** * Dom handler initializer * @function DomHandler#onparserinit * @param {htmlparser2.Parser} parser - The current instance of the htmlparser */ DomHandler.prototype.onparserinit = function (parser) { this._parser = parser; }; /** * Resets the Dom handler to it's initial state * @function DomHandler#onreset */ DomHandler.prototype.onreset = function () { DomHandler.call(this, this._stream); }; /** * Html parser is complete * @function DomHandler#onend */ DomHandler.prototype.onend = function () { if(this._done) return; this._done = true; this._parser = null; this._stream.push(this.ast); }; /** * Throws an error * @function DomHandler#onerror * @param {Error} err - The error */ DomHandler.prototype.onerror = function (err) { throw err; }; /** * Handles finding a close tag * @function DomHandler#onclosetag */ DomHandler.prototype.onclosetag = function () { this._tagStack.pop(); }; /** * Adds a node to the AST * @function DomHandler#_addNode * @param {Node} node - The node to add */ DomHandler.prototype._addNode = function (node) { var parent = this._tagStack[this._tagStack.length - 1] , siblings = parent ? parent.children : this.ast ; siblings.push(node); }; /** * Handles finding an open tag * @function DomHandler#onopenttag * @param {string} name - The tag name * @param {Object.<string, string>} attribs - An object of attributes */ DomHandler.prototype.onopentag = function (name, attribs) { var TagType = /^mc:/.test(name) ? AST.TagDirective : AST.Tag , mcAttribs = [] ; for( attrName in attribs ) { var AttrType = /^mc:/.test(attrName) ? AST.AttribDirective : AST.Attrib , attrib = new AttrType({ name: attrName.replace(/^mc:/, ''), data: AttrType == AST.AttribDirective || TagType == AST.TagDirective ? attribs[attrName] : DomHandler.parseText(attribs[attrName]) }) ; mcAttribs.push(attrib); }; var tag = new TagType({ name: name.replace(/^mc:/, ''), attribs: mcAttribs }); this._addNode(tag); this._tagStack.push(tag); } DomHandler.prototype.ontext = function (data) { if( /\S/.test(data) ) { this._addNode(new AST.Text({ data: DomHandler.parseText(data) })); } }; /** * Parses a string of text into an array of strings and MCChunks * @function DomHandle.parseText * @param {string} str - The text to parse * @return {Interpolator} The parsed interpolator */ DomHandler.parseText = function (str) { var charBuf = new CharBuf(str) , isInMC = false , chunkCur = '' , chunks = [] ; while(charBuf.consume()) { if(!isInMC) { if( charBuf.charCur == '{' && charBuf.charNxt[0] == '{' ) { charBuf.consume(); isInMC = true; if(chunkCur.length) chunks.push( AST.InterpolatorString({data:chunkCur}) ); chunkCur = ''; continue; } else { chunkCur += charBuf.charCur; } } else { if( charBuf.charCur == '}' && charBuf.charNxt[0] == '}' ) { charBuf.consume(); isInMC = false; chunks.push( AST.InterpolatorExpression({data:chunkCur}) ); chunkCur = ''; } else { chunkCur += charBuf.charCur; } } } if( chunkCur.length ) chunks.push( AST.InterpolatorString({data:chunkCur}) ); return new AST.Interpolator({data: chunks}); }