als-document 1.4.0 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/document.js +2 -2
- package/index.js +2 -2
- package/index.mjs +2 -2
- package/lib/node/single-node.js +4 -4
- package/lib/parse/parser.js +1 -0
- package/package.json +1 -1
package/document.js
CHANGED
|
@@ -24,7 +24,7 @@ class NodeClassList{
|
|
|
24
24
|
constructor(node){ this.node=node }
|
|
25
25
|
get classes(){ re
|
|
26
26
|
function insertBefore(arr,index,newItem){
|
|
27
27
|
const existingIndex=arr.indexOf(newItem);
|
|
28
28
|
if (existingIndex!==-1) arr.splice(existingIndex,1);
|
|
29
29
|
arr.splice(index,0,newItem);
|
|
30
30
|
}
|
|
31
31
|
class Node{
|
|
32
32
|
constructor(tagName,attributes={},parent=null){
|
|
33
33
|
this.isSingle=false;
|
|
34
34
|
this._tagName=tagName.toLowerCase();
|
|
35
35
|
this.tagName=tagName.toUpperCase();
|
|
36
36
|
this.attributes=attributes;
|
|
37
37
|
this.childNodes=[];
|
|
38
38
|
if (parent!==null) parent.childNodes.push(this)
|
|
39
39
|
this.parent=parent;
|
|
40
40
|
this._classList=null;
|
|
41
41
|
this.__style=null;
|
|
42
42
|
this._dataset=null
|
|
43
43
|
}
|
|
44
44
|
get id(){ return this.attributes.id ? this.attributes.id : null; }
|
|
45
45
|
set id(newValue){ this.attributes.id=newValue; }
|
|
46
46
|
get className(){ return this.attributes.class || null }
|
|
47
47
|
get parentNode(){ return this.parent }
|
|
48
48
|
get ancestors(){
|
|
49
49
|
if (!this.parent) return []
|
|
50
50
|
const ancestors=[]
|
|
51
51
|
let element=this.parent
|
|
52
52
|
while (element.tagName!=='ROOT'){
|
|
53
53
|
ancestors.push(element)
|
|
54
54
|
element=element.parent
|
|
55
55
|
}
|
|
56
56
|
return ancestors.reverse()
|
|
57
57
|
}
|
|
58
58
|
get childNodeIndex(){
|
|
59
59
|
if (!this.parent) return null
|
|
60
60
|
return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
|
|
61
61
|
}
|
|
62
62
|
get childIndex(){
|
|
63
63
|
if (!this.parent) return null
|
|
64
64
|
return this.parent.children ? this.parent.children.indexOf(this) : null
|
|
65
65
|
}
|
|
66
66
|
get previousElementSibling(){ return this.prev }
|
|
67
67
|
get prev(){
|
|
68
68
|
if (this.childIndex===null) return null
|
|
69
69
|
return this.parent.children[this.childIndex-1]
|
|
70
70
|
}
|
|
71
71
|
get nextElementSibling(){ return this.next }
|
|
72
72
|
get next(){
|
|
73
73
|
if (this.childIndex===null) return null
|
|
74
74
|
return this.parent.children[this.childIndex+1] || null
|
|
75
75
|
}
|
|
76
76
|
get dataset(){
|
|
77
77
|
if (!this._dataset) this._dataset=getDataset(this);
|
|
78
78
|
return this._dataset;
|
|
79
79
|
}
|
|
80
80
|
get classList(){
|
|
81
81
|
if (!this._classList) this._classList=new NodeClassList(this);
|
|
82
82
|
return this._classList;
|
|
83
83
|
}
|
|
84
84
|
get style(){
|
|
85
85
|
if (!this.__style) this.__style=buildStyle(this.attributes)
|
|
86
86
|
return this.__style
|
|
87
87
|
}
|
|
88
88
|
get outerHTML(){
|
|
89
89
|
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
90
90
|
return `<${this._tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this._tagName}>`;
|
|
91
91
|
}
|
|
92
92
|
getAttribute(attrName){ return this.attributes[attrName]!==undefined ? this.attributes[attrName] : null }
|
|
93
93
|
setAttribute(attrName,value=''){ this.attributes[attrName]=value }
|
|
94
94
|
removeAttribute(attrName){ delete this.attributes[attrName] }
|
|
95
95
|
remove(){
|
|
96
96
|
if (!this.parent) return
|
|
97
97
|
const index=this.childNodeIndex;
|
|
98
98
|
if (index!==null) this.parent.childNodes.splice(index,1);
|
|
99
99
|
}
|
|
100
100
|
get innerHTML(){
|
|
101
101
|
return this.childNodes.map(child=>{
|
|
102
102
|
if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
|
|
103
103
|
else if (child instanceof TextNode) return child.textContent;
|
|
104
104
|
else return child
|
|
105
105
|
}).join("");
|
|
106
106
|
}
|
|
107
107
|
get innerText(){
|
|
108
108
|
return this.childNodes.map(child=>{
|
|
109
109
|
if (child instanceof Node || child instanceof SingleNode) return child.innerText;
|
|
110
110
|
else if (child instanceof TextNode) return child.textContent;
|
|
111
111
|
else return child
|
|
112
112
|
}).join("");
|
|
113
113
|
}
|
|
114
114
|
set innerText(value){
|
|
115
115
|
this.childNodes=[new TextNode(value)]
|
|
116
116
|
return value
|
|
117
117
|
}
|
|
118
118
|
$$(query){ return this.querySelectorAll(query) }
|
|
119
119
|
querySelectorAll(query){
|
|
120
120
|
const selectors=Query.get(query)
|
|
121
121
|
return find(selectors,this,new Set())
|
|
122
122
|
}
|
|
123
123
|
$(query){ return this.querySelector(query) }
|
|
124
124
|
querySelector(query){
|
|
125
125
|
const selectors=Query.get(query)
|
|
126
126
|
return find(selectors,this,new Set(),true)[0] || null
|
|
127
127
|
}
|
|
128
128
|
getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
|
|
129
129
|
getElementsByTagName(query){ return this.querySelectorAll(query) }
|
|
130
130
|
getElementById(query){ return this.querySelector('#'+query) }
|
|
131
131
|
get children(){
|
|
132
132
|
return this.childNodes.filter(child=>{
|
|
133
133
|
if (!(child instanceof Node)) return false
|
|
134
134
|
if (child._tagName==='#comment') return false
|
|
135
135
|
return true
|
|
136
136
|
});
|
|
137
137
|
}
|
|
138
138
|
insertAdjacentElement(position,newElement){
|
|
139
139
|
if (newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
|
|
140
140
|
const pos=position.toLowerCase();
|
|
141
141
|
if(newElement.parentNode){
|
|
142
142
|
newElement.parentNode.childNodes=newElement.parentNode.childNodes.filter(el=>el!==newElement)
|
|
143
143
|
}
|
|
144
144
|
if (pos==='afterbegin' || pos==='beforeend'){
|
|
145
145
|
if (pos==="afterbegin") this.childNodes.unshift(newElement);
|
|
146
146
|
else if (pos==="beforeend") this.childNodes.push(newElement);
|
|
147
147
|
newElement.parent=this
|
|
148
148
|
return newElement
|
|
149
149
|
}
|
|
150
150
|
if (!this.parent) throw new Error("Can't insert element to element without parent")
|
|
151
151
|
if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
|
|
152
152
|
else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
|
|
153
153
|
newElement.parent=this.parent
|
|
154
154
|
return newElement
|
|
155
155
|
}
|
|
156
156
|
insertAdjacentHTML(position,html){
|
|
157
157
|
const newNode=parseHTML(html);
|
|
158
158
|
newNode.childNodes.forEach(node=>{
|
|
159
159
|
this.insertAdjacentElement(position,node);
|
|
160
160
|
});
|
|
161
161
|
return newNode
|
|
162
162
|
}
|
|
163
163
|
insertAdjacentText(position,text){
|
|
164
164
|
return this.insertAdjacentElement(position,new TextNode(text));
|
|
165
165
|
}
|
|
166
166
|
insert(position,element){
|
|
167
167
|
const positions=['beforebegin','afterbegin','beforeend','afterend']
|
|
168
168
|
if (positions[position]) position=positions[position]
|
|
169
169
|
if (typeof element==='string'){
|
|
170
170
|
element=element.trim()
|
|
171
171
|
if (element.startsWith('<') && element.endsWith('>')){
|
|
172
172
|
return this.insertAdjacentHTML(position,element)
|
|
173
173
|
}
|
|
174
174
|
return this.insertAdjacentText(position,element)
|
|
175
175
|
}
|
|
176
176
|
return this.insertAdjacentElement(position,element)
|
|
177
177
|
}
|
|
178
178
|
set innerHTML(html){
|
|
179
179
|
this.childNodes=html.trim().startsWith('<') ? parseHTML(html).childNodes : [html]
|
|
180
180
|
this.children.forEach(child=>child.parent=this);
|
|
181
181
|
}
|
|
182
182
|
set outerHTML(html){
|
|
183
183
|
const parsed=parseHTML(html);
|
|
184
184
|
if (!this.parent) throw new Error('element has no parent node')
|
|
185
185
|
const index=this.childIndex
|
|
186
186
|
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
187
187
|
}
|
|
188
188
|
appendChild(newChild){
|
|
189
189
|
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
190
190
|
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
191
191
|
} else if (typeof newChild==='string') newChild=new TextNode(newChild)
|
|
192
192
|
else return newChild
|
|
193
193
|
this.childNodes.push(newChild);
|
|
194
194
|
newChild.parent=this;
|
|
195
195
|
return newChild;
|
|
196
196
|
}
|
|
197
197
|
get textContent(){
|
|
198
198
|
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
199
199
|
return this.childNodes.map(child=>{
|
|
200
200
|
if (child instanceof SingleNode) return ''
|
|
201
201
|
if (child instanceof TextNode) return child.nodeValue
|
|
202
202
|
if (child instanceof Node) return child.textContent;
|
|
203
203
|
else return child;
|
|
204
204
|
}).join('');
|
|
205
205
|
}
|
|
206
206
|
set textContent(value){
|
|
207
207
|
this.childNodes=[];
|
|
208
208
|
if (value!==null && value!==undefined){
|
|
209
209
|
this.childNodes.push(value.toString());
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
|
-
class SingleNode extends Node{
|
|
214
213
|
constructor(tagName,attributes={},parent=null){
|
|
215
214
|
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
216
215
|
super(tagName,attributes,parent);
|
|
217
216
|
this.isSingle=true
|
|
218
217
|
}
|
|
219
218
|
get outerHTML(){
|
|
220
219
|
if (this._tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
|
|
221
220
|
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
222
221
|
return `<${this._tagName} ${attrs}${this._tagName==='?xml' ? '?' : ''}>`;
|
|
223
222
|
}
|
|
224
223
|
get innerHTML(){ return ""; }
|
|
225
224
|
set innerHTML(_){ }
|
|
226
225
|
$(_){return null}
|
|
227
226
|
$$(_){return []}
|
|
228
227
|
querySelectorAll(_){ return []; }
|
|
229
228
|
querySelector(_){ return null; }
|
|
230
229
|
getElementsByClassName(_){ return []; }
|
|
231
230
|
getElementsByTagName(_){ return []; }
|
|
232
231
|
getElementById(_){ return null; }
|
|
233
232
|
get children(){ return []; }
|
|
234
233
|
insertAdjacentElement(position,newElement){
|
|
235
234
|
if(position==='afterbegin') position='beforebegin'
|
|
236
235
|
if(position==='beforeend') position='afterend'
|
|
237
236
|
super.insertAdjacentElement(position,newElement)
|
|
238
237
|
}
|
|
239
238
|
insertAdjacentHTML(position,html){
|
|
240
239
|
if(position==='afterbegin') position='beforebegin'
|
|
241
240
|
if(position==='beforeend') position='afterend'
|
|
242
241
|
super.insertAdjacentHTML(position,html)
|
|
243
242
|
}
|
|
244
243
|
insertAdjacentText(position,text){
|
|
245
244
|
if(position==='afterbegin') position='beforebegin'
|
|
246
245
|
if(position==='beforeend') position='afterend'
|
|
247
246
|
super.insertAdjacentText(position,text)
|
|
248
247
|
}
|
|
249
248
|
insert(position,element){
|
|
250
249
|
if(position===1) position=0
|
|
251
250
|
if(position===2) position=3
|
|
252
251
|
super.insert(position,element)
|
|
253
252
|
}
|
|
254
253
|
appendChild(_){ }
|
|
255
254
|
get textContent(){ return ""; }
|
|
256
255
|
set textContent(_){ }
|
|
256
|
+
class SingleNode extends Node{
|
|
257
257
|
constructor(tagName,attributes={},parent=null){
|
|
258
258
|
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
259
259
|
super(tagName,attributes,parent);
|
|
260
260
|
this.isSingle=true
|
|
261
261
|
}
|
|
262
262
|
get outerHTML(){
|
|
263
263
|
if (this._tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
|
|
264
264
|
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
265
265
|
return `<${this._tagName} ${attrs}${this._tagName==='?xml' ? '?' : ''}>`;
|
|
266
266
|
}
|
|
267
267
|
get innerHTML(){ return ""; }
|
|
268
268
|
set innerHTML(_){ }
|
|
269
269
|
$(_){return null}
|
|
270
270
|
$$(_){return []}
|
|
271
271
|
querySelectorAll(_){ return []; }
|
|
272
272
|
querySelector(_){ return null; }
|
|
273
273
|
getElementsByClassName(_){ return []; }
|
|
274
274
|
getElementsByTagName(_){ return []; }
|
|
275
275
|
getElementById(_){ return null; }
|
|
276
276
|
get children(){ return []; }
|
|
277
277
|
insertAdjacentElement(position,newElement){
|
|
278
278
|
if(position==='afterbegin') position='beforebegin'
|
|
279
279
|
if(position==='beforeend') position='afterend'
|
|
280
280
|
return super.insertAdjacentElement(position,newElement)
|
|
281
281
|
}
|
|
282
282
|
insertAdjacentHTML(position,html){
|
|
283
283
|
if(position==='afterbegin') position='beforebegin'
|
|
284
284
|
if(position==='beforeend') position='afterend'
|
|
285
285
|
return super.insertAdjacentHTML(position,html)
|
|
286
286
|
}
|
|
287
287
|
insertAdjacentText(position,text){
|
|
288
288
|
if(position==='afterbegin') position='beforebegin'
|
|
289
289
|
if(position==='beforeend') position='afterend'
|
|
290
290
|
return super.insertAdjacentText(position,text)
|
|
291
291
|
}
|
|
292
292
|
insert(position,element){
|
|
293
293
|
if(position===1) position=0
|
|
294
294
|
if(position===2) position=3
|
|
295
295
|
return super.insert(position,element)
|
|
296
296
|
}
|
|
297
297
|
appendChild(_){ }
|
|
298
298
|
get textContent(){ return ""; }
|
|
299
299
|
set textContent(_){ }
|
|
300
300
|
}
|
|
301
301
|
|
|
302
302
|
class Root extends Node{
|
|
303
303
|
constructor(){
|
|
304
304
|
super('ROOT',{},null);
|
|
305
305
|
this.isSingle=false
|
|
306
306
|
}
|
|
@@ -34,7 +34,7 @@ class Document extends Node{
|
|
|
34
34
|
constructor(html,url){
|
|
35
35
|
super('ROOT',{},nul
|
|
36
36
|
function parseAttributes(str){
|
|
37
37
|
const attrs={};
|
|
38
38
|
let key="";
|
|
39
39
|
let value="";
|
|
40
40
|
let isKey=true;
|
|
41
41
|
let quoteChar=null;
|
|
42
42
|
for (let i=0; i< str.length; i++){
|
|
43
43
|
const char=str[i];
|
|
44
44
|
if (isKey && (char==='=' || char===' ')){
|
|
45
45
|
if (char==='=') isKey=false;
|
|
46
46
|
else if (key.trim()){
|
|
47
47
|
attrs[key.trim()]=true;
|
|
48
48
|
key="";
|
|
49
49
|
}
|
|
50
50
|
continue;
|
|
51
51
|
}
|
|
52
52
|
if (!quoteChar && (char==='"' || char==="'")){
|
|
53
53
|
quoteChar=char;
|
|
54
54
|
continue;
|
|
55
55
|
} else if (quoteChar && char===quoteChar){
|
|
56
56
|
quoteChar=null;
|
|
57
57
|
attrs[key.trim()]=value.trim();
|
|
58
58
|
key=""; value=""; isKey=true;
|
|
59
59
|
continue;
|
|
60
60
|
}
|
|
61
61
|
if (isKey) key+=char;
|
|
62
62
|
else value+=char;
|
|
63
63
|
}
|
|
64
64
|
if (key.trim() &&!value) attrs[key.trim()]='';
|
|
65
65
|
return attrs;
|
|
66
66
|
}
|
|
67
67
|
const VOID_TAGS=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","!doctype",'?xml']);
|
|
68
|
-
function parseHTML(html){
|
|
69
68
|
const root=new Root();
|
|
70
69
|
const stack=[root];
|
|
71
70
|
let currentText="",i=0;
|
|
72
71
|
let max=0
|
|
73
72
|
function parseScript(){
|
|
74
73
|
if (!html.startsWith("<script",i)) return false;
|
|
75
74
|
const openTagEnd=html.indexOf(">",i);
|
|
76
75
|
if (openTagEnd===-1) return false;
|
|
77
76
|
const attributesString=html.substring(i+7,openTagEnd).trim();
|
|
78
77
|
const attributes=parseAttributes(attributesString);
|
|
79
78
|
let closeTagStart=html.indexOf("</script>",openTagEnd);
|
|
80
79
|
if (closeTagStart===-1) return false;
|
|
81
80
|
const content=html.substring(openTagEnd+1,closeTagStart);
|
|
82
81
|
const scriptNode=new Node('script',attributes,stack[stack.length-1]);
|
|
83
82
|
if(content.length>0) scriptNode.childNodes.push(content);
|
|
84
83
|
i=closeTagStart+9;
|
|
85
84
|
return true;
|
|
86
85
|
}
|
|
87
86
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
88
87
|
if (!html.startsWith(startStr,i)) return false
|
|
89
88
|
const end=html.indexOf(endStr,i+n1);
|
|
90
89
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
91
90
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
92
91
|
i=end+n2;
|
|
93
92
|
return true
|
|
94
93
|
}
|
|
95
94
|
while (i< html.length){
|
|
96
95
|
if(i>=max) max=i;
|
|
97
96
|
else break;
|
|
98
97
|
if (parseScript()) continue
|
|
99
98
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
100
99
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
101
100
|
if (html.startsWith("<![CDATA[",i)){
|
|
102
101
|
const end=html.indexOf("]]>",i+9);
|
|
103
102
|
if (end===-1) break;
|
|
104
103
|
const content=html.substring(i+9,end);
|
|
105
104
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
106
105
|
cdataNode.nodeValue=content;
|
|
107
106
|
i=end+3;
|
|
108
107
|
continue;
|
|
109
108
|
}
|
|
110
109
|
if (html.startsWith("<",i)){
|
|
111
110
|
if (currentText && stack[stack.length-1]){
|
|
112
111
|
const textNode=new TextNode(currentText)
|
|
113
112
|
stack[stack.length-1].childNodes.push(textNode);
|
|
114
113
|
textNode.parent=stack[stack.length-1]
|
|
115
114
|
currentText="";
|
|
116
115
|
}
|
|
117
116
|
let tagEnd=i+1;
|
|
118
117
|
let insideQuotes=false;
|
|
119
118
|
let quoteChar=null;
|
|
120
119
|
while (tagEnd< html.length){
|
|
121
120
|
const char=html[tagEnd];
|
|
122
121
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
123
122
|
insideQuotes=true;
|
|
124
123
|
quoteChar=char;
|
|
125
124
|
} else if (insideQuotes && char===quoteChar){
|
|
126
125
|
insideQuotes=false;
|
|
127
126
|
quoteChar=null;
|
|
128
127
|
}
|
|
129
128
|
if (!insideQuotes && char==='>') break;
|
|
130
129
|
tagEnd++;
|
|
131
130
|
}
|
|
132
131
|
const tagContent=html.substring(i+1,tagEnd);
|
|
133
132
|
if (tagContent.startsWith("/")) stack.pop();
|
|
134
133
|
else{
|
|
135
134
|
let isSelfClosing=tagContent.endsWith('/');
|
|
136
135
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
137
136
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
138
137
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
139
138
|
const attributes=parseAttributes(attributesString);
|
|
140
139
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
141
140
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
142
141
|
}
|
|
143
142
|
i=tagEnd+1;
|
|
144
143
|
} else{
|
|
145
144
|
currentText+=html[i];
|
|
146
145
|
i++;
|
|
147
146
|
}
|
|
148
147
|
}
|
|
149
148
|
if (currentText.trim() && stack[stack.length-1]) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
150
149
|
return root;
|
|
150
|
+
function parseHTML(html){
|
|
151
151
|
const root=new Root();
|
|
152
152
|
const stack=[root];
|
|
153
153
|
let currentText="",i=0;
|
|
154
154
|
let max=0
|
|
155
155
|
function parseScript(){
|
|
156
156
|
if (!html.startsWith("<script",i)) return false;
|
|
157
157
|
const openTagEnd=html.indexOf(">",i);
|
|
158
158
|
if (openTagEnd===-1) return false;
|
|
159
159
|
const attributesString=html.substring(i+7,openTagEnd).trim();
|
|
160
160
|
const attributes=parseAttributes(attributesString);
|
|
161
161
|
let closeTagStart=html.indexOf("</script>",openTagEnd);
|
|
162
162
|
if (closeTagStart===-1) return false;
|
|
163
163
|
const content=html.substring(openTagEnd+1,closeTagStart);
|
|
164
164
|
const scriptNode=new Node('script',attributes,stack[stack.length-1]);
|
|
165
165
|
if(content.length>0) scriptNode.childNodes.push(content);
|
|
166
166
|
i=closeTagStart+9;
|
|
167
167
|
return true;
|
|
168
168
|
}
|
|
169
169
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
170
170
|
if (!html.startsWith(startStr,i)) return false
|
|
171
171
|
if(currentText.length) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
172
172
|
const end=html.indexOf(endStr,i+n1);
|
|
173
173
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
174
174
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
175
175
|
i=end+n2;
|
|
176
176
|
return true
|
|
177
177
|
}
|
|
178
178
|
while (i< html.length){
|
|
179
179
|
if(i>=max) max=i;
|
|
180
180
|
else break;
|
|
181
181
|
if (parseScript()) continue
|
|
182
182
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
183
183
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
184
184
|
if (html.startsWith("<![CDATA[",i)){
|
|
185
185
|
const end=html.indexOf("]]>",i+9);
|
|
186
186
|
if (end===-1) break;
|
|
187
187
|
const content=html.substring(i+9,end);
|
|
188
188
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
189
189
|
cdataNode.nodeValue=content;
|
|
190
190
|
i=end+3;
|
|
191
191
|
continue;
|
|
192
192
|
}
|
|
193
193
|
if (html.startsWith("<",i)){
|
|
194
194
|
if (currentText && stack[stack.length-1]){
|
|
195
195
|
const textNode=new TextNode(currentText)
|
|
196
196
|
stack[stack.length-1].childNodes.push(textNode);
|
|
197
197
|
textNode.parent=stack[stack.length-1]
|
|
198
198
|
currentText="";
|
|
199
199
|
}
|
|
200
200
|
let tagEnd=i+1;
|
|
201
201
|
let insideQuotes=false;
|
|
202
202
|
let quoteChar=null;
|
|
203
203
|
while (tagEnd< html.length){
|
|
204
204
|
const char=html[tagEnd];
|
|
205
205
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
206
206
|
insideQuotes=true;
|
|
207
207
|
quoteChar=char;
|
|
208
208
|
} else if (insideQuotes && char===quoteChar){
|
|
209
209
|
insideQuotes=false;
|
|
210
210
|
quoteChar=null;
|
|
211
211
|
}
|
|
212
212
|
if (!insideQuotes && char==='>') break;
|
|
213
213
|
tagEnd++;
|
|
214
214
|
}
|
|
215
215
|
const tagContent=html.substring(i+1,tagEnd);
|
|
216
216
|
if (tagContent.startsWith("/")) stack.pop();
|
|
217
217
|
else{
|
|
218
218
|
let isSelfClosing=tagContent.endsWith('/');
|
|
219
219
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
220
220
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
221
221
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
222
222
|
const attributes=parseAttributes(attributesString);
|
|
223
223
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
224
224
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
225
225
|
}
|
|
226
226
|
i=tagEnd+1;
|
|
227
227
|
} else{
|
|
228
228
|
currentText+=html[i];
|
|
229
229
|
i++;
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
232
|
if (currentText.trim() && stack[stack.length-1]) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
233
233
|
return root;
|
|
234
234
|
}
|
|
235
235
|
function buildFromCache(cached){
|
|
236
236
|
function buildNode(cache,parent=null){
|
|
237
237
|
if(typeof cache==='string') return parent.childNodes.push(cache)
|
|
238
238
|
const{isSingle,tagName,attributes,childNodes,textContent}=cache
|
|
239
239
|
if(textContent) return parent.childNodes.push(new TextNode(textContent))
|
|
240
240
|
if(isSingle && parent) return parent.childNodes.push(new SingleNode(tagName,attributes))
|
|
241
241
|
const newDoc=tagName==='ROOT' ? new Root() : new Node(tagName,attributes,parent)
|
|
242
242
|
childNodes.forEach(childNode=>{
|
|
243
243
|
buildNode(childNode,newDoc)
|
|
244
244
|
});
|
|
245
245
|
return newDoc
|
|
246
246
|
}
|
|
247
247
|
return buildNode(cached)
|
|
248
248
|
}
|
|
249
249
|
|