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