1'use strict'; 2 3var Tokenizer = require('../tokenization/tokenizer'), 4 TokenizerProxy = require('./tokenizer_proxy'), 5 Utils = require('../common/utils'); 6 7//Default options 8var DEFAULT_OPTIONS = { 9 decodeHtmlEntities: true, 10 locationInfo: false 11}; 12 13//Skipping handler 14function skip() { 15 //NOTE: do nothing =) 16} 17 18//SimpleApiParser 19var SimpleApiParser = module.exports = function (handlers, options) { 20 this.options = Utils.mergeOptions(DEFAULT_OPTIONS, options); 21 this.handlers = { 22 doctype: this._wrapHandler(handlers.doctype), 23 startTag: this._wrapHandler(handlers.startTag), 24 endTag: this._wrapHandler(handlers.endTag), 25 text: this._wrapHandler(handlers.text), 26 comment: this._wrapHandler(handlers.comment) 27 }; 28}; 29 30SimpleApiParser.prototype._wrapHandler = function (handler) { 31 var parser = this; 32 33 handler = handler || skip; 34 35 if (this.options.locationInfo) { 36 return function () { 37 var args = Array.prototype.slice.call(arguments); 38 args.push(parser.currentTokenLocation); 39 handler.apply(handler, args); 40 }; 41 } 42 43 return handler; 44}; 45 46//API 47SimpleApiParser.prototype.parse = function (html) { 48 var token = null; 49 50 this._reset(html); 51 52 do { 53 token = this.tokenizerProxy.getNextToken(); 54 55 if (token.type === Tokenizer.CHARACTER_TOKEN || 56 token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN || 57 token.type === Tokenizer.NULL_CHARACTER_TOKEN) { 58 59 if (this.options.locationInfo) { 60 if (this.pendingText === null) 61 this.currentTokenLocation = token.location; 62 63 else 64 this.currentTokenLocation.end = token.location.end; 65 } 66 67 this.pendingText = (this.pendingText || '') + token.chars; 68 } 69 70 else { 71 this._emitPendingText(); 72 this._handleToken(token); 73 } 74 } while (token.type !== Tokenizer.EOF_TOKEN); 75}; 76 77//Internals 78SimpleApiParser.prototype._handleToken = function (token) { 79 if (this.options.locationInfo) 80 this.currentTokenLocation = token.location; 81 82 if (token.type === Tokenizer.START_TAG_TOKEN) 83 this.handlers.startTag(token.tagName, token.attrs, token.selfClosing); 84 85 else if (token.type === Tokenizer.END_TAG_TOKEN) 86 this.handlers.endTag(token.tagName); 87 88 else if (token.type === Tokenizer.COMMENT_TOKEN) 89 this.handlers.comment(token.data); 90 91 else if (token.type === Tokenizer.DOCTYPE_TOKEN) 92 this.handlers.doctype(token.name, token.publicId, token.systemId); 93 94}; 95 96SimpleApiParser.prototype._reset = function (html) { 97 this.tokenizerProxy = new TokenizerProxy(html, this.options); 98 this.pendingText = null; 99 this.currentTokenLocation = null; 100}; 101 102SimpleApiParser.prototype._emitPendingText = function () { 103 if (this.pendingText !== null) { 104 this.handlers.text(this.pendingText); 105 this.pendingText = null; 106 } 107}; 108