.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / htmlparser2 / lib / Parser.js
1 var Tokenizer = require("./Tokenizer.js");
2
3 /*
4         Options:
5
6         xmlMode: Disables the special behavior for script/style tags (false by default)
7         lowerCaseAttributeNames: call .toLowerCase for each attribute name (true if xmlMode is `false`)
8         lowerCaseTags: call .toLowerCase for each tag name (true if xmlMode is `false`)
9 */
10
11 /*
12         Callbacks:
13
14         oncdataend,
15         oncdatastart,
16         onclosetag,
17         oncomment,
18         oncommentend,
19         onerror,
20         onopentag,
21         onprocessinginstruction,
22         onreset,
23         ontext
24 */
25
26 var formTags = {
27     input: true,
28     option: true,
29     optgroup: true,
30     select: true,
31     button: true,
32     datalist: true,
33     textarea: true
34 };
35
36 var openImpliesClose = {
37     tr: { tr: true, th: true, td: true },
38     th: { th: true },
39     td: { thead: true, th: true, td: true },
40     body: { head: true, link: true, script: true },
41     li: { li: true },
42     p: { p: true },
43     h1: { p: true },
44     h2: { p: true },
45     h3: { p: true },
46     h4: { p: true },
47     h5: { p: true },
48     h6: { p: true },
49     select: formTags,
50     input: formTags,
51     output: formTags,
52     button: formTags,
53     datalist: formTags,
54     textarea: formTags,
55     option: { option: true },
56     optgroup: { optgroup: true }
57 };
58
59 var voidElements = {
60     __proto__: null,
61     area: true,
62     base: true,
63     basefont: true,
64     br: true,
65     col: true,
66     command: true,
67     embed: true,
68     frame: true,
69     hr: true,
70     img: true,
71     input: true,
72     isindex: true,
73     keygen: true,
74     link: true,
75     meta: true,
76     param: true,
77     source: true,
78     track: true,
79     wbr: true
80 };
81
82 var foreignContextElements = {
83     __proto__: null,
84     math: true,
85     svg: true
86 };
87 var htmlIntegrationElements = {
88     __proto__: null,
89     mi: true,
90     mo: true,
91     mn: true,
92     ms: true,
93     mtext: true,
94     "annotation-xml": true,
95     foreignObject: true,
96     desc: true,
97     title: true
98 };
99
100 var re_nameEnd = /\s|\//;
101
102 function Parser(cbs, options) {
103     this._options = options || {};
104     this._cbs = cbs || {};
105
106     this._tagname = "";
107     this._attribname = "";
108     this._attribvalue = "";
109     this._attribs = null;
110     this._stack = [];
111     this._foreignContext = [];
112
113     this.startIndex = 0;
114     this.endIndex = null;
115
116     this._lowerCaseTagNames =
117         "lowerCaseTags" in this._options
118             ? !!this._options.lowerCaseTags
119             : !this._options.xmlMode;
120     this._lowerCaseAttributeNames =
121         "lowerCaseAttributeNames" in this._options
122             ? !!this._options.lowerCaseAttributeNames
123             : !this._options.xmlMode;
124
125     if (this._options.Tokenizer) {
126         Tokenizer = this._options.Tokenizer;
127     }
128     this._tokenizer = new Tokenizer(this._options, this);
129
130     if (this._cbs.onparserinit) this._cbs.onparserinit(this);
131 }
132
133 require("inherits")(Parser, require("events").EventEmitter);
134
135 Parser.prototype._updatePosition = function(initialOffset) {
136     if (this.endIndex === null) {
137         if (this._tokenizer._sectionStart <= initialOffset) {
138             this.startIndex = 0;
139         } else {
140             this.startIndex = this._tokenizer._sectionStart - initialOffset;
141         }
142     } else this.startIndex = this.endIndex + 1;
143     this.endIndex = this._tokenizer.getAbsoluteIndex();
144 };
145
146 //Tokenizer event handlers
147 Parser.prototype.ontext = function(data) {
148     this._updatePosition(1);
149     this.endIndex--;
150
151     if (this._cbs.ontext) this._cbs.ontext(data);
152 };
153
154 Parser.prototype.onopentagname = function(name) {
155     if (this._lowerCaseTagNames) {
156         name = name.toLowerCase();
157     }
158
159     this._tagname = name;
160
161     if (!this._options.xmlMode && name in openImpliesClose) {
162         for (
163             var el;
164             (el = this._stack[this._stack.length - 1]) in
165             openImpliesClose[name];
166             this.onclosetag(el)
167         );
168     }
169
170     if (this._options.xmlMode || !(name in voidElements)) {
171         this._stack.push(name);
172         if (name in foreignContextElements) this._foreignContext.push(true);
173         else if (name in htmlIntegrationElements)
174             this._foreignContext.push(false);
175     }
176
177     if (this._cbs.onopentagname) this._cbs.onopentagname(name);
178     if (this._cbs.onopentag) this._attribs = {};
179 };
180
181 Parser.prototype.onopentagend = function() {
182     this._updatePosition(1);
183
184     if (this._attribs) {
185         if (this._cbs.onopentag)
186             this._cbs.onopentag(this._tagname, this._attribs);
187         this._attribs = null;
188     }
189
190     if (
191         !this._options.xmlMode &&
192         this._cbs.onclosetag &&
193         this._tagname in voidElements
194     ) {
195         this._cbs.onclosetag(this._tagname);
196     }
197
198     this._tagname = "";
199 };
200
201 Parser.prototype.onclosetag = function(name) {
202     this._updatePosition(1);
203
204     if (this._lowerCaseTagNames) {
205         name = name.toLowerCase();
206     }
207     
208     if (name in foreignContextElements || name in htmlIntegrationElements) {
209         this._foreignContext.pop();
210     }
211
212     if (
213         this._stack.length &&
214         (!(name in voidElements) || this._options.xmlMode)
215     ) {
216         var pos = this._stack.lastIndexOf(name);
217         if (pos !== -1) {
218             if (this._cbs.onclosetag) {
219                 pos = this._stack.length - pos;
220                 while (pos--) this._cbs.onclosetag(this._stack.pop());
221             } else this._stack.length = pos;
222         } else if (name === "p" && !this._options.xmlMode) {
223             this.onopentagname(name);
224             this._closeCurrentTag();
225         }
226     } else if (!this._options.xmlMode && (name === "br" || name === "p")) {
227         this.onopentagname(name);
228         this._closeCurrentTag();
229     }
230 };
231
232 Parser.prototype.onselfclosingtag = function() {
233     if (
234         this._options.xmlMode ||
235         this._options.recognizeSelfClosing ||
236         this._foreignContext[this._foreignContext.length - 1]
237     ) {
238         this._closeCurrentTag();
239     } else {
240         this.onopentagend();
241     }
242 };
243
244 Parser.prototype._closeCurrentTag = function() {
245     var name = this._tagname;
246
247     this.onopentagend();
248
249     //self-closing tags will be on the top of the stack
250     //(cheaper check than in onclosetag)
251     if (this._stack[this._stack.length - 1] === name) {
252         if (this._cbs.onclosetag) {
253             this._cbs.onclosetag(name);
254         }
255         this._stack.pop();
256         
257     }
258 };
259
260 Parser.prototype.onattribname = function(name) {
261     if (this._lowerCaseAttributeNames) {
262         name = name.toLowerCase();
263     }
264     this._attribname = name;
265 };
266
267 Parser.prototype.onattribdata = function(value) {
268     this._attribvalue += value;
269 };
270
271 Parser.prototype.onattribend = function() {
272     if (this._cbs.onattribute)
273         this._cbs.onattribute(this._attribname, this._attribvalue);
274     if (
275         this._attribs &&
276         !Object.prototype.hasOwnProperty.call(this._attribs, this._attribname)
277     ) {
278         this._attribs[this._attribname] = this._attribvalue;
279     }
280     this._attribname = "";
281     this._attribvalue = "";
282 };
283
284 Parser.prototype._getInstructionName = function(value) {
285     var idx = value.search(re_nameEnd),
286         name = idx < 0 ? value : value.substr(0, idx);
287
288     if (this._lowerCaseTagNames) {
289         name = name.toLowerCase();
290     }
291
292     return name;
293 };
294
295 Parser.prototype.ondeclaration = function(value) {
296     if (this._cbs.onprocessinginstruction) {
297         var name = this._getInstructionName(value);
298         this._cbs.onprocessinginstruction("!" + name, "!" + value);
299     }
300 };
301
302 Parser.prototype.onprocessinginstruction = function(value) {
303     if (this._cbs.onprocessinginstruction) {
304         var name = this._getInstructionName(value);
305         this._cbs.onprocessinginstruction("?" + name, "?" + value);
306     }
307 };
308
309 Parser.prototype.oncomment = function(value) {
310     this._updatePosition(4);
311
312     if (this._cbs.oncomment) this._cbs.oncomment(value);
313     if (this._cbs.oncommentend) this._cbs.oncommentend();
314 };
315
316 Parser.prototype.oncdata = function(value) {
317     this._updatePosition(1);
318
319     if (this._options.xmlMode || this._options.recognizeCDATA) {
320         if (this._cbs.oncdatastart) this._cbs.oncdatastart();
321         if (this._cbs.ontext) this._cbs.ontext(value);
322         if (this._cbs.oncdataend) this._cbs.oncdataend();
323     } else {
324         this.oncomment("[CDATA[" + value + "]]");
325     }
326 };
327
328 Parser.prototype.onerror = function(err) {
329     if (this._cbs.onerror) this._cbs.onerror(err);
330 };
331
332 Parser.prototype.onend = function() {
333     if (this._cbs.onclosetag) {
334         for (
335             var i = this._stack.length;
336             i > 0;
337             this._cbs.onclosetag(this._stack[--i])
338         );
339     }
340     if (this._cbs.onend) this._cbs.onend();
341 };
342
343 //Resets the parser to a blank state, ready to parse a new HTML document
344 Parser.prototype.reset = function() {
345     if (this._cbs.onreset) this._cbs.onreset();
346     this._tokenizer.reset();
347
348     this._tagname = "";
349     this._attribname = "";
350     this._attribs = null;
351     this._stack = [];
352
353     if (this._cbs.onparserinit) this._cbs.onparserinit(this);
354 };
355
356 //Parses a complete HTML document and pushes it to the handler
357 Parser.prototype.parseComplete = function(data) {
358     this.reset();
359     this.end(data);
360 };
361
362 Parser.prototype.write = function(chunk) {
363     this._tokenizer.write(chunk);
364 };
365
366 Parser.prototype.end = function(chunk) {
367     this._tokenizer.end(chunk);
368 };
369
370 Parser.prototype.pause = function() {
371     this._tokenizer.pause();
372 };
373
374 Parser.prototype.resume = function() {
375     this._tokenizer.resume();
376 };
377
378 //alias for backwards compat
379 Parser.prototype.parseChunk = Parser.prototype.write;
380 Parser.prototype.done = Parser.prototype.end;
381
382 module.exports = Parser;