als-document 1.0.5-alpha → 1.0.7-alpha
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/package.json +1 -1
- package/src/node/node.js +1 -1
- package/src/parse/parser.js +3 -1
- package/tests/index.html +2 -2
- package/tests/parse-real.js +3 -2
package/document.js
CHANGED
|
@@ -22,14 +22,14 @@ function buildStyle(attributes){
|
|
|
22
22
|
const styles=attributes.style || "";
|
|
23
23
|
con
|
|
24
24
|
class NodeClassList{
|
|
25
25
|
constructor(node){ this.node=node }
|
|
26
26
|
get classes(){ return (this.node.attributes.class || "").split(" ").filter(Boolean) }
|
|
27
27
|
set classes(val){ this.node.attributes.class=val.join(" ") }
|
|
28
28
|
contains(className){ return this.classes.includes(className) }
|
|
29
29
|
add(className){
|
|
30
30
|
const currentClasses=this.classes;
|
|
31
31
|
if (!currentClasses.includes(className)) this.classes=[...currentClasses,className];
|
|
32
32
|
}
|
|
33
33
|
remove(className){ this.classes=this.classes.filter(cls=>cls!==className); }
|
|
34
34
|
toggle(className){
|
|
35
35
|
if (this.classes.includes(className)) this.remove(className);
|
|
36
36
|
else this.add(className);
|
|
37
37
|
}
|
|
38
38
|
replace(oldClass,newClass){
|
|
39
39
|
if (this.classes.includes(oldClass)){
|
|
40
40
|
this.remove(oldClass);
|
|
41
41
|
this.add(newClass);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
function insertBefore(arr,index,newItem){
|
|
46
46
|
const existingIndex=arr.indexOf(newItem);
|
|
47
47
|
if (existingIndex!==-1) arr.splice(existingIndex,1);
|
|
48
48
|
arr.splice(index,0,newItem);
|
|
49
|
-
}
|
|
50
49
|
class Node{
|
|
51
50
|
constructor(tagName,attributes={},parent=null){
|
|
52
51
|
this.isSingle=false;
|
|
53
52
|
this.tagName=tagName;
|
|
54
53
|
this.attributes=attributes;
|
|
55
54
|
this.childNodes=[];
|
|
56
55
|
if (parent!==null) parent.childNodes.push(this)
|
|
57
56
|
this.parent=parent;
|
|
58
57
|
this._classList=null;
|
|
59
58
|
this.__style=null;
|
|
60
59
|
this._dataset=null
|
|
61
60
|
}
|
|
62
61
|
get id(){ return this.attributes.id ? this.attributes.id : null; }
|
|
63
62
|
set id(newValue){ this.attributes.id=newValue; }
|
|
64
63
|
get className(){return this.attributes.class || null}
|
|
65
64
|
get parentNode(){ return this.parent }
|
|
66
65
|
get ancestors(){
|
|
67
66
|
if(!this.parent) return []
|
|
68
67
|
const ancestors=[]
|
|
69
68
|
let element=this.parent
|
|
70
69
|
while (element.tagName!=='ROOT'){
|
|
71
70
|
ancestors.push(element)
|
|
72
71
|
element=element.parent
|
|
73
72
|
}
|
|
74
73
|
return ancestors.reverse()
|
|
75
74
|
}
|
|
76
75
|
get childNodeIndex(){
|
|
77
76
|
if(!this.parent) return null
|
|
78
77
|
return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
|
|
79
78
|
}
|
|
80
79
|
get childIndex(){
|
|
81
80
|
if(!this.parent) return null
|
|
82
81
|
return this.parent.children ? this.parent.children.indexOf(this) : null
|
|
83
82
|
}
|
|
84
83
|
get previousElementSibling(){ return this.prev }
|
|
85
84
|
get prev(){
|
|
86
85
|
if (!this.childIndex) return null
|
|
87
86
|
return this.parent.children[this.childIndex-1]
|
|
88
87
|
}
|
|
89
88
|
get nextElementSibling(){ return this.next }
|
|
90
89
|
get next(){
|
|
91
90
|
if (!this.childIndex) return null
|
|
92
91
|
return this.parent.children[this.childIndex+1] || null
|
|
93
92
|
}
|
|
94
93
|
get dataset(){
|
|
95
94
|
if (!this._dataset) this._dataset=getDataset(this);
|
|
96
95
|
return this._dataset;
|
|
97
96
|
}
|
|
98
97
|
get classList(){
|
|
99
98
|
if (!this._classList) this._classList=new NodeClassList(this);
|
|
100
99
|
return this._classList;
|
|
101
100
|
}
|
|
102
101
|
get style(){
|
|
103
102
|
if (!this.__style) this.__style=buildStyle(this.attributes)
|
|
104
103
|
return this.__style
|
|
105
104
|
}
|
|
106
105
|
get outerHTML(){
|
|
107
106
|
const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
|
|
108
107
|
return `<${this.tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this.tagName}>`;
|
|
109
108
|
}
|
|
110
109
|
getAttribute(attrName){ return this.attributes[attrName] || null }
|
|
111
110
|
setAttribute(attrName,value){ this.attributes[attrName]=value }
|
|
112
111
|
removeAttribute(attrName){ delete this.attributes[attrName] }
|
|
113
112
|
remove(){
|
|
114
113
|
if (!this.parent) return
|
|
115
114
|
const index=this.childIndex;
|
|
116
115
|
if (index!==null) this.parent.childNodes.splice(index,1);
|
|
117
116
|
}
|
|
118
117
|
get innerHTML(){
|
|
119
118
|
return this.childNodes.map(child=>{
|
|
120
119
|
if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
|
|
121
120
|
else if (child instanceof TextNode) return child.textContent;
|
|
122
121
|
else return child
|
|
123
122
|
}).join("");
|
|
124
123
|
}
|
|
125
124
|
$$(query){return this.querySelectorAll(query)}
|
|
126
125
|
querySelectorAll(query){
|
|
127
126
|
const selectors=Query.get(query)
|
|
128
127
|
return find(selectors,this,new Set())
|
|
129
128
|
}
|
|
130
129
|
$(query){return this.querySelector(query)}
|
|
131
130
|
querySelector(query){
|
|
132
131
|
const selectors=Query.get(query)
|
|
133
132
|
return find(selectors,this,new Set(),true)[0] || null
|
|
134
133
|
}
|
|
135
134
|
getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
|
|
136
135
|
getElementsByTagName(query){ return this.querySelectorAll(query) }
|
|
137
136
|
getElementById(query){ return this.querySelector('#'+query) }
|
|
138
137
|
get children(){
|
|
139
138
|
return this.childNodes.filter(child=>{
|
|
140
139
|
if (!(child instanceof Node)) return false
|
|
141
140
|
if (child.tagName==='#comment') return false
|
|
142
141
|
return true
|
|
143
142
|
});
|
|
144
143
|
}
|
|
145
144
|
insertAdjacentElement(position,newElement){
|
|
146
145
|
if(newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
|
|
147
146
|
const pos=position.toLowerCase();
|
|
148
147
|
if (pos==="afterbegin") this.childNodes.unshift(newElement);
|
|
149
148
|
else if (pos==="beforeend") this.childNodes.push(newElement);
|
|
150
149
|
newElement.parent=this
|
|
151
150
|
if (!this.parent) return newElement
|
|
152
151
|
if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
|
|
153
152
|
else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
|
|
154
153
|
newElement.parent=this.parent
|
|
155
154
|
return newElement
|
|
156
155
|
}
|
|
157
156
|
insertAdjacentHTML(position,html){
|
|
158
157
|
const newNode=parseHTML(html);
|
|
159
158
|
newNode.childNodes.reverse().forEach(node=>{
|
|
160
159
|
this.insertAdjacentElement(position,node);
|
|
161
160
|
});
|
|
162
161
|
return newNode
|
|
163
162
|
}
|
|
164
163
|
insertAdjacentText(position,text){
|
|
165
164
|
return this.insertAdjacentElement(position,new TextNode(text));
|
|
166
165
|
}
|
|
167
166
|
set innerHTML(html){
|
|
168
167
|
const parsed=parseHTML(html);
|
|
169
168
|
this.childNodes=parsed.childNodes;
|
|
170
169
|
}
|
|
171
170
|
set outerHTML(html){
|
|
172
171
|
const parsed=parseHTML(html);
|
|
173
172
|
if (!this.parent) return console.log('element has no parent node')
|
|
174
173
|
const index=this.childIndex
|
|
175
174
|
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
176
175
|
}
|
|
177
176
|
appendChild(newChild){
|
|
178
177
|
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
179
178
|
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
180
179
|
} else if(typeof newChild==='string') newChild=new TextNode(newChild)
|
|
181
180
|
else return newChild
|
|
182
181
|
this.childNodes.push(newChild);
|
|
183
182
|
newChild.parent=this;
|
|
184
183
|
return newChild;
|
|
185
184
|
}
|
|
186
185
|
get textContent(){
|
|
187
186
|
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
188
187
|
return this.childNodes.map(child=>{
|
|
189
188
|
if(child instanceof SingleNode) return ''
|
|
190
189
|
if(child instanceof TextNode) return child.nodeValue
|
|
191
190
|
if(child instanceof Node) return child.textContent;
|
|
192
191
|
else return child;
|
|
193
192
|
}).join(" ");
|
|
194
193
|
}
|
|
195
194
|
set textContent(value){
|
|
196
195
|
this.childNodes=[];
|
|
197
196
|
if (value!==null && value!==undefined){
|
|
198
197
|
this.childNodes.push(value.toString());
|
|
199
198
|
}
|
|
200
199
|
}
|
|
200
|
+
}
|
|
201
201
|
class Node{
|
|
202
202
|
constructor(tagName,attributes={},parent=null){
|
|
203
203
|
this.isSingle=false;
|
|
204
204
|
this.tagName=tagName;
|
|
205
205
|
this.attributes=attributes;
|
|
206
206
|
this.childNodes=[];
|
|
207
207
|
if (parent!==null) parent.childNodes.push(this)
|
|
208
208
|
this.parent=parent;
|
|
209
209
|
this._classList=null;
|
|
210
210
|
this.__style=null;
|
|
211
211
|
this._dataset=null
|
|
212
212
|
}
|
|
213
213
|
get id(){ return this.attributes.id ? this.attributes.id : null; }
|
|
214
214
|
set id(newValue){ this.attributes.id=newValue; }
|
|
215
215
|
get className(){return this.attributes.class || null}
|
|
216
216
|
get parentNode(){ return this.parent }
|
|
217
217
|
get ancestors(){
|
|
218
218
|
if(!this.parent) return []
|
|
219
219
|
const ancestors=[]
|
|
220
220
|
let element=this.parent
|
|
221
221
|
while (element.tagName!=='ROOT'){
|
|
222
222
|
ancestors.push(element)
|
|
223
223
|
element=element.parent
|
|
224
224
|
}
|
|
225
225
|
return ancestors.reverse()
|
|
226
226
|
}
|
|
227
227
|
get childNodeIndex(){
|
|
228
228
|
if(!this.parent) return null
|
|
229
229
|
return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
|
|
230
230
|
}
|
|
231
231
|
get childIndex(){
|
|
232
232
|
if(!this.parent) return null
|
|
233
233
|
return this.parent.children ? this.parent.children.indexOf(this) : null
|
|
234
234
|
}
|
|
235
235
|
get previousElementSibling(){ return this.prev }
|
|
236
236
|
get prev(){
|
|
237
237
|
if (!this.childIndex) return null
|
|
238
238
|
return this.parent.children[this.childIndex-1]
|
|
239
239
|
}
|
|
240
240
|
get nextElementSibling(){ return this.next }
|
|
241
241
|
get next(){
|
|
242
242
|
if (!this.childIndex) return null
|
|
243
243
|
return this.parent.children[this.childIndex+1] || null
|
|
244
244
|
}
|
|
245
245
|
get dataset(){
|
|
246
246
|
if (!this._dataset) this._dataset=getDataset(this);
|
|
247
247
|
return this._dataset;
|
|
248
248
|
}
|
|
249
249
|
get classList(){
|
|
250
250
|
if (!this._classList) this._classList=new NodeClassList(this);
|
|
251
251
|
return this._classList;
|
|
252
252
|
}
|
|
253
253
|
get style(){
|
|
254
254
|
if (!this.__style) this.__style=buildStyle(this.attributes)
|
|
255
255
|
return this.__style
|
|
256
256
|
}
|
|
257
257
|
get outerHTML(){
|
|
258
258
|
const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
|
|
259
259
|
return `<${this.tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this.tagName}>`;
|
|
260
260
|
}
|
|
261
261
|
getAttribute(attrName){ return this.attributes[attrName] || null }
|
|
262
262
|
setAttribute(attrName,value){ this.attributes[attrName]=value }
|
|
263
263
|
removeAttribute(attrName){ delete this.attributes[attrName] }
|
|
264
264
|
remove(){
|
|
265
265
|
if (!this.parent) return
|
|
266
266
|
const index=this.childNodeIndex;
|
|
267
267
|
if (index!==null) this.parent.childNodes.splice(index,1);
|
|
268
268
|
}
|
|
269
269
|
get innerHTML(){
|
|
270
270
|
return this.childNodes.map(child=>{
|
|
271
271
|
if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
|
|
272
272
|
else if (child instanceof TextNode) return child.textContent;
|
|
273
273
|
else return child
|
|
274
274
|
}).join("");
|
|
275
275
|
}
|
|
276
276
|
$$(query){return this.querySelectorAll(query)}
|
|
277
277
|
querySelectorAll(query){
|
|
278
278
|
const selectors=Query.get(query)
|
|
279
279
|
return find(selectors,this,new Set())
|
|
280
280
|
}
|
|
281
281
|
$(query){return this.querySelector(query)}
|
|
282
282
|
querySelector(query){
|
|
283
283
|
const selectors=Query.get(query)
|
|
284
284
|
return find(selectors,this,new Set(),true)[0] || null
|
|
285
285
|
}
|
|
286
286
|
getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
|
|
287
287
|
getElementsByTagName(query){ return this.querySelectorAll(query) }
|
|
288
288
|
getElementById(query){ return this.querySelector('#'+query) }
|
|
289
289
|
get children(){
|
|
290
290
|
return this.childNodes.filter(child=>{
|
|
291
291
|
if (!(child instanceof Node)) return false
|
|
292
292
|
if (child.tagName==='#comment') return false
|
|
293
293
|
return true
|
|
294
294
|
});
|
|
295
295
|
}
|
|
296
296
|
insertAdjacentElement(position,newElement){
|
|
297
297
|
if(newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
|
|
298
298
|
const pos=position.toLowerCase();
|
|
299
299
|
if (pos==="afterbegin") this.childNodes.unshift(newElement);
|
|
300
300
|
else if (pos==="beforeend") this.childNodes.push(newElement);
|
|
301
301
|
newElement.parent=this
|
|
302
302
|
if (!this.parent) return newElement
|
|
303
303
|
if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
|
|
304
304
|
else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
|
|
305
305
|
newElement.parent=this.parent
|
|
306
306
|
return newElement
|
|
307
307
|
}
|
|
308
308
|
insertAdjacentHTML(position,html){
|
|
309
309
|
const newNode=parseHTML(html);
|
|
310
310
|
newNode.childNodes.reverse().forEach(node=>{
|
|
311
311
|
this.insertAdjacentElement(position,node);
|
|
312
312
|
});
|
|
313
313
|
return newNode
|
|
314
314
|
}
|
|
315
315
|
insertAdjacentText(position,text){
|
|
316
316
|
return this.insertAdjacentElement(position,new TextNode(text));
|
|
317
317
|
}
|
|
318
318
|
set innerHTML(html){
|
|
319
319
|
const parsed=parseHTML(html);
|
|
320
320
|
this.childNodes=parsed.childNodes;
|
|
321
321
|
}
|
|
322
322
|
set outerHTML(html){
|
|
323
323
|
const parsed=parseHTML(html);
|
|
324
324
|
if (!this.parent) return console.log('element has no parent node')
|
|
325
325
|
const index=this.childIndex
|
|
326
326
|
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
327
327
|
}
|
|
328
328
|
appendChild(newChild){
|
|
329
329
|
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
330
330
|
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
331
331
|
} else if(typeof newChild==='string') newChild=new TextNode(newChild)
|
|
332
332
|
else return newChild
|
|
333
333
|
this.childNodes.push(newChild);
|
|
334
334
|
newChild.parent=this;
|
|
335
335
|
return newChild;
|
|
336
336
|
}
|
|
337
337
|
get textContent(){
|
|
338
338
|
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
339
339
|
return this.childNodes.map(child=>{
|
|
340
340
|
if(child instanceof SingleNode) return ''
|
|
341
341
|
if(child instanceof TextNode) return child.nodeValue
|
|
342
342
|
if(child instanceof Node) return child.textContent;
|
|
343
343
|
else return child;
|
|
344
344
|
}).join(" ");
|
|
345
345
|
}
|
|
346
346
|
set textContent(value){
|
|
347
347
|
this.childNodes=[];
|
|
348
348
|
if (value!==null && value!==undefined){
|
|
349
349
|
this.childNodes.push(value.toString());
|
|
350
350
|
}
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
353
|
class SingleNode extends Node{
|
|
354
354
|
constructor(tagName,attributes={},parent=null){
|
|
355
355
|
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
356
356
|
super(tagName,attributes,parent);
|
|
357
357
|
this.isSingle=true
|
|
358
358
|
}
|
|
359
359
|
get outerHTML(){
|
|
360
360
|
if (this.tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
|
|
361
361
|
const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
|
|
362
362
|
return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
|
|
363
363
|
}
|
|
364
364
|
get innerHTML(){ return ""; }
|
|
365
365
|
set innerHTML(_){ }
|
|
366
366
|
$(_){return null}
|
|
367
367
|
$$(_){return []}
|
|
368
368
|
querySelectorAll(_){ return []; }
|
|
369
369
|
querySelector(_){ return null; }
|
|
370
370
|
getElementsByClassName(_){ return []; }
|
|
371
371
|
getElementsByTagName(_){ return []; }
|
|
372
372
|
getElementById(_){ return null; }
|
|
373
373
|
get children(){ return []; }
|
|
374
374
|
insertAdjacentElement(_,__){ }
|
|
375
375
|
insertAdjacentHTML(_,__){ }
|
|
376
376
|
insertAdjacentText(_,__){ }
|
|
377
377
|
appendChild(_){ }
|
|
378
378
|
get textContent(){ return ""; }
|
|
379
379
|
set textContent(_){ }
|
|
380
380
|
}
|
|
381
381
|
function parseAttributes(str){
|
|
382
382
|
const attrs={};
|
|
383
383
|
let key="";
|
|
384
384
|
let value="";
|
|
385
385
|
let isKey=true;
|
|
386
386
|
let quoteChar=null;
|
|
387
387
|
for (let i=0; i< str.length; i++){
|
|
388
388
|
const char=str[i];
|
|
389
389
|
if (isKey && (char==='=' || char===' ')){
|
|
390
390
|
if (char==='=') isKey=false;
|
|
391
391
|
else if (key.trim()){
|
|
392
392
|
attrs[key.trim()]=true;
|
|
393
393
|
key="";
|
|
394
394
|
}
|
|
395
395
|
continue;
|
|
396
396
|
}
|
|
397
397
|
if (!quoteChar && (char==='"' || char==="'")){
|
|
398
398
|
quoteChar=char;
|
|
399
399
|
continue;
|
|
400
400
|
} else if (quoteChar && char===quoteChar){
|
|
401
401
|
quoteChar=null;
|
|
402
402
|
attrs[key.trim()]=value.trim();
|
|
403
403
|
key=""; value=""; isKey=true;
|
|
404
404
|
continue;
|
|
405
405
|
}
|
|
406
406
|
if (isKey) key+=char;
|
|
407
407
|
else value+=char;
|
|
408
408
|
}
|
|
409
409
|
if (key.trim() &&!value) attrs[key.trim()]=true;
|
|
410
410
|
return attrs;
|
|
411
411
|
}
|
|
412
412
|
const VOID_TAGS=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","!doctype",'?xml']);
|
|
413
|
-
function parseHTML(html){
|
|
414
413
|
const root=new Node("ROOT");
|
|
415
414
|
const stack=[root];
|
|
416
415
|
let currentText="",i=0;
|
|
417
416
|
let max=0
|
|
418
417
|
function parseScript(){
|
|
419
418
|
if (!html.startsWith("<script",i)) return false;
|
|
420
419
|
const openTagEnd=html.indexOf(">",i);
|
|
421
420
|
if (openTagEnd===-1) return false;
|
|
422
421
|
const attributesString=html.substring(i+7,openTagEnd).trim();
|
|
423
422
|
const attributes=parseAttributes(attributesString);
|
|
424
423
|
let closeTagStart=html.indexOf("</script>",openTagEnd);
|
|
425
424
|
if (closeTagStart===-1) return false;
|
|
426
425
|
const content=html.substring(openTagEnd+1,closeTagStart);
|
|
427
426
|
const scriptNode=new Node('script',attributes,stack[stack.length-1]);
|
|
428
427
|
if(content.length>0) scriptNode.childNodes.push(content);
|
|
429
428
|
i=closeTagStart+9;
|
|
430
429
|
return true;
|
|
431
430
|
}
|
|
432
431
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
433
432
|
if (!html.startsWith(startStr,i)) return false
|
|
434
433
|
const end=html.indexOf(endStr,i+n1);
|
|
435
434
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
436
435
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
437
436
|
i=end+n2;
|
|
438
437
|
return true
|
|
439
438
|
}
|
|
440
439
|
while (i< html.length){
|
|
441
440
|
if(i>=max) max=i;
|
|
442
441
|
else break;
|
|
443
442
|
if (parseScript()) continue
|
|
444
443
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
445
444
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
446
445
|
if (html.startsWith("<![CDATA[",i)){
|
|
447
446
|
const end=html.indexOf("]]>",i+9);
|
|
448
447
|
if (end===-1) break;
|
|
449
448
|
const content=html.substring(i+9,end);
|
|
450
449
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
451
450
|
cdataNode.nodeValue=content;
|
|
452
451
|
i=end+3;
|
|
453
452
|
continue;
|
|
454
453
|
}
|
|
455
454
|
if (html.startsWith("<",i)){
|
|
456
455
|
if (currentText && stack[stack.length-1]){
|
|
457
456
|
stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
458
457
|
currentText="";
|
|
459
458
|
}
|
|
460
459
|
let tagEnd=i+1;
|
|
461
460
|
let insideQuotes=false;
|
|
462
461
|
let quoteChar=null;
|
|
463
462
|
while (tagEnd< html.length){
|
|
464
463
|
const char=html[tagEnd];
|
|
465
464
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
466
465
|
insideQuotes=true;
|
|
467
466
|
quoteChar=char;
|
|
468
467
|
} else if (insideQuotes && char===quoteChar){
|
|
469
468
|
insideQuotes=false;
|
|
470
469
|
quoteChar=null;
|
|
471
470
|
}
|
|
472
471
|
if (!insideQuotes && char==='>') break;
|
|
473
472
|
tagEnd++;
|
|
474
473
|
}
|
|
475
474
|
const tagContent=html.substring(i+1,tagEnd);
|
|
476
475
|
if (tagContent.startsWith("/")) stack.pop();
|
|
477
476
|
else{
|
|
478
477
|
let isSelfClosing=tagContent.endsWith('/');
|
|
479
478
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
480
479
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
481
480
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
482
481
|
const attributes=parseAttributes(attributesString);
|
|
483
482
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
484
483
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
485
484
|
}
|
|
486
485
|
i=tagEnd+1;
|
|
487
486
|
} else{
|
|
488
487
|
currentText+=html[i];
|
|
489
488
|
i++;
|
|
490
489
|
}
|
|
491
490
|
}
|
|
492
491
|
if (currentText.trim() && stack[stack.length-1]) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
493
492
|
return root;
|
|
493
|
+
function parseHTML(html){
|
|
494
494
|
const root=new Node("ROOT");
|
|
495
495
|
const stack=[root];
|
|
496
496
|
let currentText="",i=0;
|
|
497
497
|
let max=0
|
|
498
498
|
function parseScript(){
|
|
499
499
|
if (!html.startsWith("<script",i)) return false;
|
|
500
500
|
const openTagEnd=html.indexOf(">",i);
|
|
501
501
|
if (openTagEnd===-1) return false;
|
|
502
502
|
const attributesString=html.substring(i+7,openTagEnd).trim();
|
|
503
503
|
const attributes=parseAttributes(attributesString);
|
|
504
504
|
let closeTagStart=html.indexOf("</script>",openTagEnd);
|
|
505
505
|
if (closeTagStart===-1) return false;
|
|
506
506
|
const content=html.substring(openTagEnd+1,closeTagStart);
|
|
507
507
|
const scriptNode=new Node('script',attributes,stack[stack.length-1]);
|
|
508
508
|
if(content.length>0) scriptNode.childNodes.push(content);
|
|
509
509
|
i=closeTagStart+9;
|
|
510
510
|
return true;
|
|
511
511
|
}
|
|
512
512
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
513
513
|
if (!html.startsWith(startStr,i)) return false
|
|
514
514
|
const end=html.indexOf(endStr,i+n1);
|
|
515
515
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
516
516
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
517
517
|
i=end+n2;
|
|
518
518
|
return true
|
|
519
519
|
}
|
|
520
520
|
while (i< html.length){
|
|
521
521
|
if(i>=max) max=i;
|
|
522
522
|
else break;
|
|
523
523
|
if (parseScript()) continue
|
|
524
524
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
525
525
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
526
526
|
if (html.startsWith("<![CDATA[",i)){
|
|
527
527
|
const end=html.indexOf("]]>",i+9);
|
|
528
528
|
if (end===-1) break;
|
|
529
529
|
const content=html.substring(i+9,end);
|
|
530
530
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
531
531
|
cdataNode.nodeValue=content;
|
|
532
532
|
i=end+3;
|
|
533
533
|
continue;
|
|
534
534
|
}
|
|
535
535
|
if (html.startsWith("<",i)){
|
|
536
536
|
if (currentText && stack[stack.length-1]){
|
|
537
537
|
const textNode=new TextNode(currentText)
|
|
538
538
|
stack[stack.length-1].childNodes.push(textNode);
|
|
539
539
|
textNode.parent=stack[stack.length-1]
|
|
540
540
|
currentText="";
|
|
541
541
|
}
|
|
542
542
|
let tagEnd=i+1;
|
|
543
543
|
let insideQuotes=false;
|
|
544
544
|
let quoteChar=null;
|
|
545
545
|
while (tagEnd< html.length){
|
|
546
546
|
const char=html[tagEnd];
|
|
547
547
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
548
548
|
insideQuotes=true;
|
|
549
549
|
quoteChar=char;
|
|
550
550
|
} else if (insideQuotes && char===quoteChar){
|
|
551
551
|
insideQuotes=false;
|
|
552
552
|
quoteChar=null;
|
|
553
553
|
}
|
|
554
554
|
if (!insideQuotes && char==='>') break;
|
|
555
555
|
tagEnd++;
|
|
556
556
|
}
|
|
557
557
|
const tagContent=html.substring(i+1,tagEnd);
|
|
558
558
|
if (tagContent.startsWith("/")) stack.pop();
|
|
559
559
|
else{
|
|
560
560
|
let isSelfClosing=tagContent.endsWith('/');
|
|
561
561
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
562
562
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
563
563
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
564
564
|
const attributes=parseAttributes(attributesString);
|
|
565
565
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
566
566
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
567
567
|
}
|
|
568
568
|
i=tagEnd+1;
|
|
569
569
|
} else{
|
|
570
570
|
currentText+=html[i];
|
|
571
571
|
i++;
|
|
572
572
|
}
|
|
573
573
|
}
|
|
574
574
|
if (currentText.trim() && stack[stack.length-1]) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
575
575
|
return root;
|
|
576
576
|
}
|
|
577
577
|
return { parseHTML, Node, Query, TextNode, SingleNode }
|
|
578
578
|
})()
|