• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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