Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81146 views
1
var ElementType = require("domelementtype");
2
3
var re_whitespace = /\s+/g;
4
var NodePrototype = require("./lib/node");
5
var ElementPrototype = require("./lib/element");
6
7
function DomHandler(callback, options, elementCB){
8
if(typeof callback === "object"){
9
elementCB = options;
10
options = callback;
11
callback = null;
12
} else if(typeof options === "function"){
13
elementCB = options;
14
options = defaultOpts;
15
}
16
this._callback = callback;
17
this._options = options || defaultOpts;
18
this._elementCB = elementCB;
19
this.dom = [];
20
this._done = false;
21
this._tagStack = [];
22
this._parser = this._parser || null;
23
}
24
25
//default options
26
var defaultOpts = {
27
normalizeWhitespace: false, //Replace all whitespace with single spaces
28
withStartIndices: false, //Add startIndex properties to nodes
29
};
30
31
DomHandler.prototype.onparserinit = function(parser){
32
this._parser = parser;
33
};
34
35
//Resets the handler back to starting state
36
DomHandler.prototype.onreset = function(){
37
DomHandler.call(this, this._callback, this._options, this._elementCB);
38
};
39
40
//Signals the handler that parsing is done
41
DomHandler.prototype.onend = function(){
42
if(this._done) return;
43
this._done = true;
44
this._parser = null;
45
this._handleCallback(null);
46
};
47
48
DomHandler.prototype._handleCallback =
49
DomHandler.prototype.onerror = function(error){
50
if(typeof this._callback === "function"){
51
this._callback(error, this.dom);
52
} else {
53
if(error) throw error;
54
}
55
};
56
57
DomHandler.prototype.onclosetag = function(){
58
//if(this._tagStack.pop().name !== name) this._handleCallback(Error("Tagname didn't match!"));
59
var elem = this._tagStack.pop();
60
if(this._elementCB) this._elementCB(elem);
61
};
62
63
DomHandler.prototype._addDomElement = function(element){
64
var parent = this._tagStack[this._tagStack.length - 1];
65
var siblings = parent ? parent.children : this.dom;
66
var previousSibling = siblings[siblings.length - 1];
67
68
element.next = null;
69
70
if(this._options.withStartIndices){
71
element.startIndex = this._parser.startIndex;
72
}
73
74
if (this._options.withDomLvl1) {
75
element.__proto__ = element.type === "tag" ? ElementPrototype : NodePrototype;
76
}
77
78
if(previousSibling){
79
element.prev = previousSibling;
80
previousSibling.next = element;
81
} else {
82
element.prev = null;
83
}
84
85
siblings.push(element);
86
element.parent = parent || null;
87
};
88
89
DomHandler.prototype.onopentag = function(name, attribs){
90
var element = {
91
type: name === "script" ? ElementType.Script : name === "style" ? ElementType.Style : ElementType.Tag,
92
name: name,
93
attribs: attribs,
94
children: []
95
};
96
97
this._addDomElement(element);
98
99
this._tagStack.push(element);
100
};
101
102
DomHandler.prototype.ontext = function(data){
103
//the ignoreWhitespace is officially dropped, but for now,
104
//it's an alias for normalizeWhitespace
105
var normalize = this._options.normalizeWhitespace || this._options.ignoreWhitespace;
106
107
var lastTag;
108
109
if(!this._tagStack.length && this.dom.length && (lastTag = this.dom[this.dom.length-1]).type === ElementType.Text){
110
if(normalize){
111
lastTag.data = (lastTag.data + data).replace(re_whitespace, " ");
112
} else {
113
lastTag.data += data;
114
}
115
} else {
116
if(
117
this._tagStack.length &&
118
(lastTag = this._tagStack[this._tagStack.length - 1]) &&
119
(lastTag = lastTag.children[lastTag.children.length - 1]) &&
120
lastTag.type === ElementType.Text
121
){
122
if(normalize){
123
lastTag.data = (lastTag.data + data).replace(re_whitespace, " ");
124
} else {
125
lastTag.data += data;
126
}
127
} else {
128
if(normalize){
129
data = data.replace(re_whitespace, " ");
130
}
131
132
this._addDomElement({
133
data: data,
134
type: ElementType.Text
135
});
136
}
137
}
138
};
139
140
DomHandler.prototype.oncomment = function(data){
141
var lastTag = this._tagStack[this._tagStack.length - 1];
142
143
if(lastTag && lastTag.type === ElementType.Comment){
144
lastTag.data += data;
145
return;
146
}
147
148
var element = {
149
data: data,
150
type: ElementType.Comment
151
};
152
153
this._addDomElement(element);
154
this._tagStack.push(element);
155
};
156
157
DomHandler.prototype.oncdatastart = function(){
158
var element = {
159
children: [{
160
data: "",
161
type: ElementType.Text
162
}],
163
type: ElementType.CDATA
164
};
165
166
this._addDomElement(element);
167
this._tagStack.push(element);
168
};
169
170
DomHandler.prototype.oncommentend = DomHandler.prototype.oncdataend = function(){
171
this._tagStack.pop();
172
};
173
174
DomHandler.prototype.onprocessinginstruction = function(name, data){
175
this._addDomElement({
176
name: name,
177
data: data,
178
type: ElementType.Directive
179
});
180
};
181
182
module.exports = DomHandler;
183
184