als-document 1.4.1 → 1.4.3
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/build.js +14 -14
- package/document.js +42 -727
- package/index.js +41 -726
- package/index.mjs +42 -726
- package/lib/node/single-node.js +4 -4
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,727 +1,42 @@
|
|
|
1
|
-
class Query{
|
|
2
|
-
static get(query){
|
|
3
|
-
let q=new Query(query)
|
|
4
|
-
return q.selectors
|
|
5
|
-
}
|
|
6
|
-
constructor(query){
|
|
7
|
-
this.query=query
|
|
8
|
-
this.selectors=[]
|
|
9
|
-
this.stringValues=[];
|
|
10
|
-
this.parseSelectors(query.split(','))
|
|
11
|
-
}
|
|
12
|
-
parseSelectors(selectors){
|
|
13
|
-
selectors.forEach(selector=>{
|
|
14
|
-
let originalSelector=selector.trim()
|
|
15
|
-
selector=this.removeSpaces(selector)
|
|
16
|
-
this.stringValues=[]
|
|
17
|
-
selector=selector.replace(/\[.*?\]/g,(value)=>{
|
|
18
|
-
this.stringValues.push(value)
|
|
19
|
-
return `[${this.stringValues.length-1}]`
|
|
20
|
-
})
|
|
21
|
-
let [element,ancestors]=this.splitAndCutLast(selector,' ')
|
|
22
|
-
element=this.getFamily(element)
|
|
23
|
-
if (ancestors.length>0)
|
|
24
|
-
element.ancestors=ancestors.map(ancestor=>this.getFamily(ancestor))
|
|
25
|
-
element.group=originalSelector
|
|
26
|
-
this.selectors.push(element)
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
splitAndCutLast(string,splitBy){
|
|
30
|
-
const array=string.split(splitBy);
|
|
31
|
-
const last=array.pop();
|
|
32
|
-
return [last,array];
|
|
33
|
-
}
|
|
34
|
-
getFamily(group,element,prev,prevAny,sign){
|
|
35
|
-
if (group.match(/\~|\+/)!==null){
|
|
36
|
-
let [last,prevBrothers]=this.splitAndCutLast(group,/\~|\+/)
|
|
37
|
-
let signs=group.replace(last,'')
|
|
38
|
-
prevBrothers.forEach(el=>signs=signs.replace(el,''))
|
|
39
|
-
signs=signs.match(/\~|\+/g)
|
|
40
|
-
if (signs.length==1){
|
|
41
|
-
sign=signs[0]
|
|
42
|
-
} else if (signs.length>1){
|
|
43
|
-
sign=signs.splice(signs.length-1,signs.length-1)[0]
|
|
44
|
-
prevBrothers[0]=prevBrothers.map((b,i)=>{
|
|
45
|
-
if (i< prevBrothers.length-1) b+=signs[i]
|
|
46
|
-
return b
|
|
47
|
-
}).join('')
|
|
48
|
-
prevBrothers[0]=this.getFamily(prevBrothers[0])
|
|
49
|
-
}
|
|
50
|
-
if (sign=='~') prevAny=prevBrothers[0]
|
|
51
|
-
else if (sign=='+') prev=prevBrothers[0]
|
|
52
|
-
element=last
|
|
53
|
-
} else element=group
|
|
54
|
-
let family
|
|
55
|
-
if (prev || prevAny){
|
|
56
|
-
family=this.getParents(element)
|
|
57
|
-
if (prev) family.prev=this.getParents(prev)
|
|
58
|
-
if (prevAny) family.prevAny=this.getParents(prevAny)
|
|
59
|
-
} else family=this.getParents(element)
|
|
60
|
-
if (family.query!==group) family.group=group
|
|
61
|
-
return family
|
|
62
|
-
}
|
|
63
|
-
getParents(selector){
|
|
64
|
-
if (typeof selector=='string'){
|
|
65
|
-
let [element,parents]=this.splitAndCutLast(selector,'>')
|
|
66
|
-
element=this.buildElement(element)
|
|
67
|
-
parents=parents.map(parent=>this.buildElement(parent))
|
|
68
|
-
if (parents.length>0) element.parents=parents
|
|
69
|
-
return element
|
|
70
|
-
} else return selector
|
|
71
|
-
}
|
|
72
|
-
buildElement(element,id=null,tag=null,classList=[]){
|
|
73
|
-
let query=element
|
|
74
|
-
element=element.replace(/\#(\w-?)*/,$id=>{
|
|
75
|
-
id=$id.replace(/^\#/,''); return ''
|
|
76
|
-
})
|
|
77
|
-
element=element.replace(/\.(\w-?)*/,$class=>{
|
|
78
|
-
classList.push($class.replace(/^\./,'')); return ''
|
|
79
|
-
})
|
|
80
|
-
element=element.replace(/(\w\:?-?)*/,$tag=>{
|
|
81
|
-
tag=$tag=='' ? null : $tag; return ''
|
|
82
|
-
})
|
|
83
|
-
let attribs=this.getAttributes(element)
|
|
84
|
-
element={ query }
|
|
85
|
-
if (id) element.id=id
|
|
86
|
-
if (tag) element.tag=tag
|
|
87
|
-
if (classList.length>0) element.classList=classList
|
|
88
|
-
if (attribs.length>0) element.attribs=attribs
|
|
89
|
-
return element
|
|
90
|
-
}
|
|
91
|
-
getAttributes(element){
|
|
92
|
-
let attribs=this.stringValues.filter((value,index)=>{
|
|
93
|
-
let searchValue=`[${index}]`
|
|
94
|
-
if (element.match(searchValue)) return true
|
|
95
|
-
else return false
|
|
96
|
-
})
|
|
97
|
-
attribs=attribs.map(attrib=>{
|
|
98
|
-
let query=attrib
|
|
99
|
-
attrib=attrib.replace('[','').replace(']','')
|
|
100
|
-
let [name,...values]=attrib.split('=')
|
|
101
|
-
const value=values.join('=').trim().replace(/^\"/,'').replace(/\"$/,'')
|
|
102
|
-
let sign
|
|
103
|
-
attrib={query,name}
|
|
104
|
-
if(value){
|
|
105
|
-
sign='='
|
|
106
|
-
attrib.name=attrib.name.replace(/[\~\|\^\$\*]$/,(match=>{
|
|
107
|
-
sign=match+sign
|
|
108
|
-
return ''
|
|
109
|
-
}))
|
|
110
|
-
attrib.value=value
|
|
111
|
-
}
|
|
112
|
-
if (sign){
|
|
113
|
-
attrib.sign=sign
|
|
114
|
-
attrib.check=this.getAttribFn(sign).bind(attrib)
|
|
115
|
-
}
|
|
116
|
-
return attrib
|
|
117
|
-
});
|
|
118
|
-
return attribs
|
|
119
|
-
}
|
|
120
|
-
getAttribFn(sign){
|
|
121
|
-
if (sign=='=') return function (value){ return value===this.value }
|
|
122
|
-
if (sign=='*=') return function (value){ return value.includes(this.value) }
|
|
123
|
-
if (sign=='^=') return function (value){ return value.startsWith(this.value) }
|
|
124
|
-
if (sign=='$=') return function (value){ return value.endsWith(this.value) }
|
|
125
|
-
if (sign=='|=') return function (value){
|
|
126
|
-
return value.trim().split(' ').length==1
|
|
127
|
-
&& (value.startsWith(this.value) || value.startsWith(this.value+'-'))
|
|
128
|
-
? true : false
|
|
129
|
-
}
|
|
130
|
-
if (sign=='~=') return function (value){
|
|
131
|
-
return this.value.trim().split(' ').length==1 && value.includes(this.value) ? true : false
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
removeSpaces(selector){
|
|
135
|
-
selector=selector.replace(/\s{2}/g,' ')
|
|
136
|
-
selector=selector.replace(/\s?\^?\$?\|?\~?\*?\=\s*/g,(m)=>m.trim())
|
|
137
|
-
selector=selector.replace(/\s?(\+|\~|\>)\s?/g,(m)=>m.trim())
|
|
138
|
-
return selector
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
function checkElement(el,selector){
|
|
142
|
-
if(selector==undefined) return true
|
|
143
|
-
if(el==null) return false
|
|
144
|
-
let{tag,classList,attribs:attributes,id,prev,ancestors,parents,prevAny}=selector
|
|
145
|
-
if(typeof el==='string') return false
|
|
146
|
-
if(id!==undefined && el.id===null) return false
|
|
147
|
-
if(id && id!==el.id) return false
|
|
148
|
-
if(tag && el._tagName===undefined) return false
|
|
149
|
-
else if(tag && tag!==el._tagName) return false
|
|
150
|
-
const clas=el.attributes.class
|
|
151
|
-
if(classList!==undefined && (clas===undefined || clas==='')) return false
|
|
152
|
-
else if(classList!==undefined){
|
|
153
|
-
if(classList.every(e=>el.classList.contains(e))===false) return false
|
|
154
|
-
}
|
|
155
|
-
if(checkattributes(attributes,el)===false) return false
|
|
156
|
-
if(checkElement(el.prev,prev)===false) return false
|
|
157
|
-
if(checkAncestors(el.ancestors,ancestors)===false) return false
|
|
158
|
-
if(checkParents(el.ancestors,parents)===false) return false
|
|
159
|
-
if(el.parent){
|
|
160
|
-
if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
|
|
161
|
-
}
|
|
162
|
-
return true
|
|
163
|
-
}
|
|
164
|
-
function checkattributes(attributes=[],el){
|
|
165
|
-
let elattributes=el.attributes
|
|
166
|
-
let names=Object.keys(elattributes)
|
|
167
|
-
let passedTests=0
|
|
168
|
-
if(attributes) for(let i=0; i<attributes.length; i++){
|
|
169
|
-
let{name,value,check}=attributes[i]
|
|
170
|
-
if(name=='inner' && value!==undefined && check && el.inner){
|
|
171
|
-
if(check(el.inner)) passedTests++
|
|
172
|
-
}
|
|
173
|
-
if(!names.includes(name)) continue
|
|
174
|
-
else if(value==undefined) passedTests++
|
|
175
|
-
else if(value && elattributes[name]){
|
|
176
|
-
if(check(elattributes[name])==false) continue
|
|
177
|
-
else passedTests++
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
if(passedTests==attributes.length) return true
|
|
181
|
-
else return false
|
|
182
|
-
}
|
|
183
|
-
function checkPrevAny(children=[],index,prevAny){
|
|
184
|
-
let size=children.length
|
|
185
|
-
if((size==0 || index==0) && prevAny) return false
|
|
186
|
-
for(let i=index; i>=0; i--){
|
|
187
|
-
if(checkElement(children[i],prevAny)) return true
|
|
188
|
-
}
|
|
189
|
-
return false
|
|
190
|
-
}
|
|
191
|
-
function checkAncestors(ancestors=[],selectorAncestors=[]){
|
|
192
|
-
let count=0
|
|
193
|
-
if(selectorAncestors.length==0) return true
|
|
194
|
-
let endIndex=ancestors.length-1
|
|
195
|
-
let selectorIndex=selectorAncestors.length-1
|
|
196
|
-
while(selectorIndex>=0){
|
|
197
|
-
for(let i=endIndex; i>=0; i--){
|
|
198
|
-
endIndex=i-1
|
|
199
|
-
if(checkElement(ancestors[i],selectorAncestors[selectorIndex])==true){
|
|
200
|
-
count++
|
|
201
|
-
break
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
selectorIndex--
|
|
205
|
-
}
|
|
206
|
-
if(count==selectorAncestors.length) return true
|
|
207
|
-
else return false
|
|
208
|
-
}
|
|
209
|
-
function checkParents(ancestors=[],selectorParents=[]){
|
|
210
|
-
if(selectorParents.length===0) return true
|
|
211
|
-
if(ancestors.length< selectorParents.length) return false
|
|
212
|
-
let index=ancestors.length-1
|
|
213
|
-
for(let i=selectorParents.length-1; i>=0; i--){
|
|
214
|
-
if(checkElement(ancestors[index],selectorParents[i])===false) return false
|
|
215
|
-
index--
|
|
216
|
-
}
|
|
217
|
-
return true
|
|
218
|
-
}
|
|
1
|
+
class Query{
|
|
219
2
|
static get(query){
|
|
220
3
|
let q=new Query(query)
|
|
221
4
|
return q.selectors
|
|
222
5
|
}
|
|
223
6
|
constructor(query){
|
|
224
7
|
this.query=query
|
|
225
8
|
this.selectors=[]
|
|
226
9
|
this.stringValues=[];
|
|
227
10
|
this.parseSelectors(query.split(','))
|
|
228
11
|
}
|
|
229
12
|
parseSelectors(selectors){
|
|
230
13
|
selectors.forEach(selector=>{
|
|
231
14
|
let originalSelector=selector.trim()
|
|
232
15
|
selector=this.removeSpaces(selector)
|
|
233
16
|
this.stringValues=[]
|
|
234
17
|
selector=selector.replace(/\[.*?\]/g,(value)=>{
|
|
235
18
|
this.stringValues.push(value)
|
|
236
19
|
return `[${this.stringValues.length-1}]`
|
|
237
20
|
})
|
|
238
21
|
let [element,ancestors]=this.splitAndCutLast(selector,' ')
|
|
239
22
|
element=this.getFamily(element)
|
|
240
23
|
if (ancestors.length>0)
|
|
241
24
|
element.ancestors=ancestors.map(ancestor=>this.getFamily(ancestor))
|
|
242
25
|
element.group=originalSelector
|
|
243
26
|
this.selectors.push(element)
|
|
244
27
|
});
|
|
245
28
|
}
|
|
246
29
|
splitAndCutLast(string,splitBy){
|
|
247
30
|
const array=string.split(splitBy);
|
|
248
31
|
const last=array.pop();
|
|
249
32
|
return [last,array];
|
|
250
33
|
}
|
|
251
34
|
getFamily(group,element,prev,prevAny,sign){
|
|
252
35
|
if (group.match(/\~|\+/)!==null){
|
|
253
36
|
let [last,prevBrothers]=this.splitAndCutLast(group,/\~|\+/)
|
|
254
37
|
let signs=group.replace(last,'')
|
|
255
38
|
prevBrothers.forEach(el=>signs=signs.replace(el,''))
|
|
256
39
|
signs=signs.match(/\~|\+/g)
|
|
257
40
|
if (signs.length==1){
|
|
258
41
|
sign=signs[0]
|
|
259
42
|
} else if (signs.length>1){
|
|
260
43
|
sign=signs.splice(signs.length-1,signs.length-1)[0]
|
|
261
44
|
prevBrothers[0]=prevBrothers.map((b,i)=>{
|
|
262
45
|
if (i< prevBrothers.length-1) b+=signs[i]
|
|
263
46
|
return b
|
|
264
47
|
}).join('')
|
|
265
48
|
prevBrothers[0]=this.getFamily(prevBrothers[0])
|
|
266
49
|
}
|
|
267
50
|
if (sign=='~') prevAny=prevBrothers[0]
|
|
268
51
|
else if (sign=='+') prev=prevBrothers[0]
|
|
269
52
|
element=last
|
|
270
53
|
} else element=group
|
|
271
54
|
let family
|
|
272
55
|
if (prev || prevAny){
|
|
273
56
|
family=this.getParents(element)
|
|
274
57
|
if (prev) family.prev=this.getParents(prev)
|
|
275
58
|
if (prevAny) family.prevAny=this.getParents(prevAny)
|
|
276
59
|
} else family=this.getParents(element)
|
|
277
60
|
if (family.query!==group) family.group=group
|
|
278
61
|
return family
|
|
279
62
|
}
|
|
280
63
|
getParents(selector){
|
|
281
64
|
if (typeof selector=='string'){
|
|
282
65
|
let [element,parents]=this.splitAndCutLast(selector,'>')
|
|
283
66
|
element=this.buildElement(element)
|
|
284
67
|
parents=parents.map(parent=>this.buildElement(parent))
|
|
285
68
|
if (parents.length>0) element.parents=parents
|
|
286
69
|
return element
|
|
287
70
|
} else return selector
|
|
288
71
|
}
|
|
289
72
|
buildElement(element,id=null,tag=null,classList=[]){
|
|
290
73
|
let query=element
|
|
291
74
|
element=element.replace(/\#(\w-?)*/,$id=>{
|
|
292
75
|
id=$id.replace(/^\#/,''); return ''
|
|
293
76
|
})
|
|
294
77
|
element=element.replace(/\.(\w-?)*/,$class=>{
|
|
295
78
|
classList.push($class.replace(/^\./,'')); return ''
|
|
296
79
|
})
|
|
297
80
|
element=element.replace(/(\w\:?-?)*/,$tag=>{
|
|
298
81
|
tag=$tag=='' ? null : $tag; return ''
|
|
299
82
|
})
|
|
300
83
|
let attribs=this.getAttributes(element)
|
|
301
84
|
element={ query }
|
|
302
85
|
if (id) element.id=id
|
|
303
86
|
if (tag) element.tag=tag
|
|
304
87
|
if (classList.length>0) element.classList=classList
|
|
305
88
|
if (attribs.length>0) element.attribs=attribs
|
|
306
89
|
return element
|
|
307
90
|
}
|
|
308
91
|
getAttributes(element){
|
|
309
92
|
let attribs=this.stringValues.filter((value,index)=>{
|
|
310
93
|
let searchValue=`[${index}]`
|
|
311
94
|
if (element.match(searchValue)) return true
|
|
312
95
|
else return false
|
|
313
96
|
})
|
|
314
97
|
attribs=attribs.map(attrib=>{
|
|
315
98
|
let query=attrib
|
|
316
99
|
attrib=attrib.replace('[','').replace(']','')
|
|
317
100
|
let [name,...values]=attrib.split('=')
|
|
318
101
|
const value=values.join('=').trim().replace(/^\"/,'').replace(/\"$/,'')
|
|
319
102
|
let sign
|
|
320
103
|
attrib={query,name}
|
|
321
104
|
if(value){
|
|
322
105
|
sign='='
|
|
323
106
|
attrib.name=attrib.name.replace(/[\~\|\^\$\*]$/,(match=>{
|
|
324
107
|
sign=match+sign
|
|
325
108
|
return ''
|
|
326
109
|
}))
|
|
327
110
|
attrib.value=value
|
|
328
111
|
}
|
|
329
112
|
if (sign){
|
|
330
113
|
attrib.sign=sign
|
|
331
114
|
attrib.check=this.getAttribFn(sign).bind(attrib)
|
|
332
115
|
}
|
|
333
116
|
return attrib
|
|
334
117
|
});
|
|
335
118
|
return attribs
|
|
336
119
|
}
|
|
337
120
|
getAttribFn(sign){
|
|
338
121
|
if (sign=='=') return function (value){ return value===this.value }
|
|
339
122
|
if (sign=='*=') return function (value){ return value.includes(this.value) }
|
|
340
123
|
if (sign=='^=') return function (value){ return value.startsWith(this.value) }
|
|
341
124
|
if (sign=='$=') return function (value){ return value.endsWith(this.value) }
|
|
342
125
|
if (sign=='|=') return function (value){
|
|
343
126
|
return value.trim().split(' ').length==1
|
|
344
127
|
&& (value.startsWith(this.value) || value.startsWith(this.value+'-'))
|
|
345
128
|
? true : false
|
|
346
129
|
}
|
|
347
130
|
if (sign=='~=') return function (value){
|
|
348
131
|
return this.value.trim().split(' ').length==1 && value.includes(this.value) ? true : false
|
|
349
132
|
}
|
|
350
133
|
}
|
|
351
134
|
removeSpaces(selector){
|
|
352
135
|
selector=selector.replace(/\s{2}/g,' ')
|
|
353
136
|
selector=selector.replace(/\s?\^?\$?\|?\~?\*?\=\s*/g,(m)=>m.trim())
|
|
354
137
|
selector=selector.replace(/\s?(\+|\~|\>)\s?/g,(m)=>m.trim())
|
|
355
138
|
return selector
|
|
356
139
|
}
|
|
140
|
+
}
|
|
141
|
+
function checkElement(el,selector){
|
|
357
142
|
if(selector==undefined) return true
|
|
358
143
|
if(el==null) return false
|
|
359
144
|
let{tag,classList,attribs:attributes,id,prev,ancestors,parents,prevAny}=selector
|
|
360
145
|
if(typeof el==='string') return false
|
|
361
146
|
if(id!==undefined && el.id===null) return false
|
|
362
147
|
if(id && id!==el.id) return false
|
|
363
148
|
if(tag && el._tagName===undefined) return false
|
|
364
149
|
else if(tag && tag!==el._tagName) return false
|
|
365
150
|
const clas=el.attributes.class
|
|
366
151
|
if(classList!==undefined && (clas===undefined || clas==='')) return false
|
|
367
152
|
else if(classList!==undefined){
|
|
368
153
|
if(classList.every(e=>el.classList.contains(e))===false) return false
|
|
369
154
|
}
|
|
370
155
|
if(checkattributes(attributes,el)===false) return false
|
|
371
156
|
if(checkElement(el.prev,prev)===false) return false
|
|
372
157
|
if(checkAncestors(el.ancestors,ancestors)===false) return false
|
|
373
158
|
if(checkParents(el.ancestors,parents)===false) return false
|
|
374
159
|
if(el.parent){
|
|
375
160
|
if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
|
|
376
161
|
}
|
|
377
162
|
return true
|
|
163
|
+
}
|
|
378
164
|
function checkattributes(attributes=[],el){
|
|
379
165
|
let elattributes=el.attributes
|
|
380
166
|
let names=Object.keys(elattributes)
|
|
381
167
|
let passedTests=0
|
|
382
168
|
if(attributes) for(let i=0; i<attributes.length; i++){
|
|
383
169
|
let{name,value,check}=attributes[i]
|
|
384
170
|
if(name=='inner' && value!==undefined && check && el.inner){
|
|
385
171
|
if(check(el.inner)) passedTests++
|
|
386
172
|
}
|
|
387
173
|
if(!names.includes(name)) continue
|
|
388
174
|
else if(value==undefined) passedTests++
|
|
389
175
|
else if(value && elattributes[name]){
|
|
390
176
|
if(check(elattributes[name])==false) continue
|
|
391
177
|
else passedTests++
|
|
392
178
|
}
|
|
393
179
|
}
|
|
394
180
|
if(passedTests==attributes.length) return true
|
|
395
181
|
else return false
|
|
182
|
+
}
|
|
396
183
|
function checkPrevAny(children=[],index,prevAny){
|
|
397
184
|
let size=children.length
|
|
398
185
|
if((size==0 || index==0) && prevAny) return false
|
|
399
186
|
for(let i=index; i>=0; i--){
|
|
400
187
|
if(checkElement(children[i],prevAny)) return true
|
|
401
188
|
}
|
|
402
189
|
return false
|
|
190
|
+
}
|
|
403
191
|
function checkAncestors(ancestors=[],selectorAncestors=[]){
|
|
404
192
|
let count=0
|
|
405
193
|
if(selectorAncestors.length==0) return true
|
|
406
194
|
let endIndex=ancestors.length-1
|
|
407
195
|
let selectorIndex=selectorAncestors.length-1
|
|
408
196
|
while(selectorIndex>=0){
|
|
409
197
|
for(let i=endIndex; i>=0; i--){
|
|
410
198
|
endIndex=i-1
|
|
411
199
|
if(checkElement(ancestors[i],selectorAncestors[selectorIndex])==true){
|
|
412
200
|
count++
|
|
413
201
|
break
|
|
414
202
|
}
|
|
415
203
|
}
|
|
416
204
|
selectorIndex--
|
|
417
205
|
}
|
|
418
206
|
if(count==selectorAncestors.length) return true
|
|
419
207
|
else return false
|
|
208
|
+
}
|
|
420
209
|
function checkParents(ancestors=[],selectorParents=[]){
|
|
421
210
|
if(selectorParents.length===0) return true
|
|
422
211
|
if(ancestors.length< selectorParents.length) return false
|
|
423
212
|
let index=ancestors.length-1
|
|
424
213
|
for(let i=selectorParents.length-1; i>=0; i--){
|
|
425
214
|
if(checkElement(ancestors[index],selectorParents[i])===false) return false
|
|
426
215
|
index--
|
|
427
216
|
}
|
|
428
217
|
return true
|
|
218
|
+
}
|
|
429
219
|
const getDataName=prop=>'data-'+prop.toLowerCase()
|
|
430
|
-
function getDataset(element){
|
|
431
|
-
return new Proxy(element.attributes,{
|
|
432
|
-
get: (target,prop)=>{return target[getDataName(prop)]},set: (target,prop,value)=>{target[getDataName(prop)]=value; return true},deleteProperty: (target,prop)=>{
|
|
433
|
-
const dataAttr=getDataName(prop)
|
|
434
|
-
if (dataAttr in target){
|
|
435
|
-
delete target[dataAttr];
|
|
436
|
-
return true;
|
|
437
|
-
}
|
|
438
|
-
return false;
|
|
439
|
-
}
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
function find(selectors,element,collection,first=false,firstTime=true){
|
|
444
|
-
if(!firstTime)
|
|
445
|
-
for(let selector of selectors){
|
|
446
|
-
if(checkElement(element,selector)) collection.add(element)
|
|
447
|
-
}
|
|
448
|
-
if(element.children)
|
|
449
|
-
element.children.forEach(child=>{
|
|
450
|
-
if(first && collection.size>0) return
|
|
451
|
-
find(selectors,child,collection,first,false)
|
|
452
|
-
})
|
|
453
|
-
return firstTime ? [...collection] : collection
|
|
454
|
-
}
|
|
455
|
-
class TextNode{
|
|
456
|
-
constructor(data){
|
|
457
|
-
this.nodeName='#text';
|
|
458
|
-
this.parent=null;
|
|
459
|
-
this.textContent=data;
|
|
460
|
-
}
|
|
461
|
-
get nodeValue(){return this.textContent}
|
|
462
|
-
get parentNode(){return this.parent}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
function buildStyle(attributes){
|
|
466
|
-
const styles=attributes.style || "";
|
|
467
|
-
const camelToKebab=str=>str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,'$1-$2').toLowerCase();
|
|
468
|
-
const kebabToCamel=str=>str.replace(/-([a-z])/g,g=>g[1].toUpperCase());
|
|
469
|
-
const baseStyleObj=styles.split(";").reduce((acc,style)=>{
|
|
470
|
-
const [key,value]=style.split(":").map(s=>s.trim());
|
|
471
|
-
if (key && value) acc[kebabToCamel(key)]=value;
|
|
472
|
-
return acc;
|
|
473
|
-
},{});
|
|
474
|
-
return new Proxy(baseStyleObj,{
|
|
475
|
-
get: (obj,prop)=>obj[camelToKebab(prop)] || obj[prop],set: (obj,prop,value)=>{
|
|
476
|
-
obj[camelToKebab(prop)]=value;
|
|
477
|
-
attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
|
|
478
|
-
return true;
|
|
479
|
-
},deleteProperty: (obj,prop)=>{
|
|
480
|
-
delete obj[camelToKebab(prop)];
|
|
481
|
-
attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
|
|
482
|
-
return true;
|
|
483
|
-
}
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
class NodeClassList{
|
|
488
|
-
constructor(node){ this.node=node }
|
|
489
|
-
get classes(){ return (this.node.attributes.class || "").split(" ").filter(Boolean) }
|
|
490
|
-
set classes(val){ this.node.attributes.class=val.join(" ") }
|
|
491
|
-
contains(className){ return this.classes.includes(className) }
|
|
492
|
-
add(className){
|
|
493
|
-
const currentClasses=this.classes;
|
|
494
|
-
if (!currentClasses.includes(className)) this.classes=[...currentClasses,className];
|
|
495
|
-
}
|
|
496
|
-
remove(className){ this.classes=this.classes.filter(cls=>cls!==className); }
|
|
497
|
-
toggle(className){
|
|
498
|
-
if (this.classes.includes(className)) this.remove(className);
|
|
499
|
-
else this.add(className);
|
|
500
|
-
}
|
|
501
|
-
replace(oldClass,newClass){
|
|
502
|
-
if (this.classes.includes(oldClass)){
|
|
503
|
-
this.remove(oldClass);
|
|
504
|
-
this.add(newClass);
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
function insertBefore(arr,index,newItem){
|
|
509
|
-
const existingIndex=arr.indexOf(newItem);
|
|
510
|
-
if (existingIndex!==-1) arr.splice(existingIndex,1);
|
|
511
|
-
arr.splice(index,0,newItem);
|
|
512
|
-
}
|
|
513
|
-
class Node{
|
|
514
|
-
constructor(tagName,attributes={},parent=null){
|
|
515
|
-
this.isSingle=false;
|
|
516
|
-
this._tagName=tagName.toLowerCase();
|
|
517
|
-
this.tagName=tagName.toUpperCase();
|
|
518
|
-
this.attributes=attributes;
|
|
519
|
-
this.childNodes=[];
|
|
520
|
-
if (parent!==null) parent.childNodes.push(this)
|
|
521
|
-
this.parent=parent;
|
|
522
|
-
this._classList=null;
|
|
523
|
-
this.__style=null;
|
|
524
|
-
this._dataset=null
|
|
525
|
-
}
|
|
526
|
-
get id(){ return this.attributes.id ? this.attributes.id : null; }
|
|
527
|
-
set id(newValue){ this.attributes.id=newValue; }
|
|
528
|
-
get className(){ return this.attributes.class || null }
|
|
529
|
-
get parentNode(){ return this.parent }
|
|
530
|
-
get ancestors(){
|
|
531
|
-
if (!this.parent) return []
|
|
532
|
-
const ancestors=[]
|
|
533
|
-
let element=this.parent
|
|
534
|
-
while (element.tagName!=='ROOT'){
|
|
535
|
-
ancestors.push(element)
|
|
536
|
-
element=element.parent
|
|
537
|
-
}
|
|
538
|
-
return ancestors.reverse()
|
|
539
|
-
}
|
|
540
|
-
get childNodeIndex(){
|
|
541
|
-
if (!this.parent) return null
|
|
542
|
-
return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
|
|
543
|
-
}
|
|
544
|
-
get childIndex(){
|
|
545
|
-
if (!this.parent) return null
|
|
546
|
-
return this.parent.children ? this.parent.children.indexOf(this) : null
|
|
547
|
-
}
|
|
548
|
-
get previousElementSibling(){ return this.prev }
|
|
549
|
-
get prev(){
|
|
550
|
-
if (this.childIndex===null) return null
|
|
551
|
-
return this.parent.children[this.childIndex-1]
|
|
552
|
-
}
|
|
553
|
-
get nextElementSibling(){ return this.next }
|
|
554
|
-
get next(){
|
|
555
|
-
if (this.childIndex===null) return null
|
|
556
|
-
return this.parent.children[this.childIndex+1] || null
|
|
557
|
-
}
|
|
558
|
-
get dataset(){
|
|
559
|
-
if (!this._dataset) this._dataset=getDataset(this);
|
|
560
|
-
return this._dataset;
|
|
561
|
-
}
|
|
562
|
-
get classList(){
|
|
563
|
-
if (!this._classList) this._classList=new NodeClassList(this);
|
|
564
|
-
return this._classList;
|
|
565
|
-
}
|
|
566
|
-
get style(){
|
|
567
|
-
if (!this.__style) this.__style=buildStyle(this.attributes)
|
|
568
|
-
return this.__style
|
|
569
|
-
}
|
|
570
|
-
get outerHTML(){
|
|
571
|
-
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
572
|
-
return `<${this._tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this._tagName}>`;
|
|
573
|
-
}
|
|
574
|
-
getAttribute(attrName){ return this.attributes[attrName]!==undefined ? this.attributes[attrName] : null }
|
|
575
|
-
setAttribute(attrName,value=''){ this.attributes[attrName]=value }
|
|
576
|
-
removeAttribute(attrName){ delete this.attributes[attrName] }
|
|
577
|
-
remove(){
|
|
578
|
-
if (!this.parent) return
|
|
579
|
-
const index=this.childNodeIndex;
|
|
580
|
-
if (index!==null) this.parent.childNodes.splice(index,1);
|
|
581
|
-
}
|
|
582
|
-
get innerHTML(){
|
|
583
|
-
return this.childNodes.map(child=>{
|
|
584
|
-
if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
|
|
585
|
-
else if (child instanceof TextNode) return child.textContent;
|
|
586
|
-
else return child
|
|
587
|
-
}).join("");
|
|
588
|
-
}
|
|
589
|
-
get innerText(){
|
|
590
|
-
return this.childNodes.map(child=>{
|
|
591
|
-
if (child instanceof Node || child instanceof SingleNode) return child.innerText;
|
|
592
|
-
else if (child instanceof TextNode) return child.textContent;
|
|
593
|
-
else return child
|
|
594
|
-
}).join("");
|
|
595
|
-
}
|
|
596
|
-
set innerText(value){
|
|
597
|
-
this.childNodes=[new TextNode(value)]
|
|
598
|
-
return value
|
|
599
|
-
}
|
|
600
|
-
$$(query){ return this.querySelectorAll(query) }
|
|
601
|
-
querySelectorAll(query){
|
|
602
|
-
const selectors=Query.get(query)
|
|
603
|
-
return find(selectors,this,new Set())
|
|
604
|
-
}
|
|
605
|
-
$(query){ return this.querySelector(query) }
|
|
606
|
-
querySelector(query){
|
|
607
|
-
const selectors=Query.get(query)
|
|
608
|
-
return find(selectors,this,new Set(),true)[0] || null
|
|
609
|
-
}
|
|
610
|
-
getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
|
|
611
|
-
getElementsByTagName(query){ return this.querySelectorAll(query) }
|
|
612
|
-
getElementById(query){ return this.querySelector('#'+query) }
|
|
613
|
-
get children(){
|
|
614
|
-
return this.childNodes.filter(child=>{
|
|
615
|
-
if (!(child instanceof Node)) return false
|
|
616
|
-
if (child._tagName==='#comment') return false
|
|
617
|
-
return true
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
insertAdjacentElement(position,newElement){
|
|
621
|
-
if (newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
|
|
622
|
-
const pos=position.toLowerCase();
|
|
623
|
-
if(newElement.parentNode){
|
|
624
|
-
newElement.parentNode.childNodes=newElement.parentNode.childNodes.filter(el=>el!==newElement)
|
|
625
|
-
}
|
|
626
|
-
if (pos==='afterbegin' || pos==='beforeend'){
|
|
627
|
-
if (pos==="afterbegin") this.childNodes.unshift(newElement);
|
|
628
|
-
else if (pos==="beforeend") this.childNodes.push(newElement);
|
|
629
|
-
newElement.parent=this
|
|
630
|
-
return newElement
|
|
631
|
-
}
|
|
632
|
-
if (!this.parent) throw new Error("Can't insert element to element without parent")
|
|
633
|
-
if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
|
|
634
|
-
else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
|
|
635
|
-
newElement.parent=this.parent
|
|
636
|
-
return newElement
|
|
637
|
-
}
|
|
638
|
-
insertAdjacentHTML(position,html){
|
|
639
|
-
const newNode=parseHTML(html);
|
|
640
|
-
newNode.childNodes.forEach(node=>{
|
|
641
|
-
this.insertAdjacentElement(position,node);
|
|
642
|
-
});
|
|
643
|
-
return newNode
|
|
644
|
-
}
|
|
645
|
-
insertAdjacentText(position,text){
|
|
646
|
-
return this.insertAdjacentElement(position,new TextNode(text));
|
|
647
|
-
}
|
|
648
|
-
insert(position,element){
|
|
649
|
-
const positions=['beforebegin','afterbegin','beforeend','afterend']
|
|
650
|
-
if (positions[position]) position=positions[position]
|
|
651
|
-
if (typeof element==='string'){
|
|
652
|
-
element=element.trim()
|
|
653
|
-
if (element.startsWith('<') && element.endsWith('>')){
|
|
654
|
-
return this.insertAdjacentHTML(position,element)
|
|
655
|
-
}
|
|
656
|
-
return this.insertAdjacentText(position,element)
|
|
657
|
-
}
|
|
658
|
-
return this.insertAdjacentElement(position,element)
|
|
659
|
-
}
|
|
660
|
-
set innerHTML(html){
|
|
661
|
-
this.childNodes=html.trim().startsWith('<') ? parseHTML(html).childNodes : [html]
|
|
662
|
-
this.children.forEach(child=>child.parent=this);
|
|
663
|
-
}
|
|
664
|
-
set outerHTML(html){
|
|
665
|
-
const parsed=parseHTML(html);
|
|
666
|
-
if (!this.parent) throw new Error('element has no parent node')
|
|
667
|
-
const index=this.childIndex
|
|
668
|
-
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
669
|
-
}
|
|
670
|
-
appendChild(newChild){
|
|
671
|
-
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
672
|
-
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
673
|
-
} else if (typeof newChild==='string') newChild=new TextNode(newChild)
|
|
674
|
-
else return newChild
|
|
675
|
-
this.childNodes.push(newChild);
|
|
676
|
-
newChild.parent=this;
|
|
677
|
-
return newChild;
|
|
678
|
-
}
|
|
679
|
-
get textContent(){
|
|
680
|
-
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
681
|
-
return this.childNodes.map(child=>{
|
|
682
|
-
if (child instanceof SingleNode) return ''
|
|
683
|
-
if (child instanceof TextNode) return child.nodeValue
|
|
684
|
-
if (child instanceof Node) return child.textContent;
|
|
685
|
-
else return child;
|
|
686
|
-
}).join('');
|
|
687
|
-
}
|
|
688
|
-
set textContent(value){
|
|
689
|
-
this.childNodes=[];
|
|
690
|
-
if (value!==null && value!==undefined){
|
|
691
|
-
this.childNodes.push(value.toString());
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
class SingleNode extends Node{
|
|
696
|
-
constructor(tagName,attributes={},parent=null){
|
|
697
|
-
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
698
|
-
super(tagName,attributes,parent);
|
|
699
|
-
this.isSingle=true
|
|
700
|
-
}
|
|
701
|
-
get outerHTML(){
|
|
702
|
-
if (this._tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
|
|
703
|
-
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
704
|
-
return `<${this._tagName} ${attrs}${this._tagName==='?xml' ? '?' : ''}>`;
|
|
705
|
-
}
|
|
706
|
-
get innerHTML(){ return ""; }
|
|
707
|
-
set innerHTML(_){ }
|
|
708
|
-
$(_){return null}
|
|
709
|
-
$$(_){return []}
|
|
710
|
-
querySelectorAll(_){ return []; }
|
|
711
|
-
querySelector(_){ return null; }
|
|
712
|
-
getElementsByClassName(_){ return []; }
|
|
713
|
-
getElementsByTagName(_){ return []; }
|
|
714
|
-
getElementById(_){ return null; }
|
|
715
|
-
get children(){ return []; }
|
|
716
|
-
insertAdjacentElement(position,newElement){
|
|
717
|
-
if(position==='afterbegin') position='beforebegin'
|
|
718
|
-
if(position==='beforeend') position='afterend'
|
|
719
|
-
super.insertAdjacentElement(position,newElement)
|
|
720
|
-
}
|
|
721
|
-
insertAdjacentHTML(position,html){
|
|
722
|
-
if(position==='afterbegin') position='beforebegin'
|
|
723
|
-
if(position==='beforeend') position='afterend'
|
|
724
|
-
super.insertAdjacentHTML(position,html)
|
|
725
|
-
}
|
|
726
|
-
insertAdjacentText(position,text){
|
|
727
|
-
if(position==='afterbegin') position='beforebegin'
|
|
728
|
-
if(position==='beforeend') position='afterend'
|
|
729
|
-
super.insertAdjacentText(position,text)
|
|
730
|
-
}
|
|
731
|
-
insert(position,element){
|
|
732
|
-
if(position===1) position=0
|
|
733
|
-
if(position===2) position=3
|
|
734
|
-
super.insert(position,element)
|
|
735
|
-
}
|
|
736
|
-
appendChild(_){ }
|
|
737
|
-
get textContent(){ return ""; }
|
|
738
|
-
set textContent(_){ }
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
class Root extends Node{
|
|
742
|
-
constructor(){
|
|
743
|
-
super('ROOT',{},null);
|
|
744
|
-
this.isSingle=false
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
class Document extends Node{
|
|
748
|
-
constructor(html,url){
|
|
749
|
-
super('ROOT',{},null);
|
|
750
|
-
this.isSingle=false
|
|
751
|
-
this.URL=url
|
|
752
|
-
if(html instanceof Document) this.childNodes=[...buildFromCache(cacheDoc(html)).childNodes]
|
|
753
|
-
else if(typeof html==='string') this.innerHTML=html
|
|
754
|
-
else this.html
|
|
755
|
-
}
|
|
756
|
-
get documentElement(){return this.html}
|
|
757
|
-
get html(){
|
|
758
|
-
const html=this.$('html')
|
|
759
|
-
if(html===null) this.innerHTML=/*html*/`<html lang="en"><head><meta charset="UTF-8"><title></title></head><body></body></html>`
|
|
760
|
-
return this.$('html')
|
|
761
|
-
}
|
|
762
|
-
get innerHTML(){
|
|
763
|
-
const inner=super.innerHTML
|
|
764
|
-
return '<!DOCTYPE html>'+inner
|
|
765
|
-
}
|
|
766
|
-
set innerHTML(html){return super.innerHTML=html}
|
|
767
|
-
get head(){
|
|
768
|
-
const head=this.html.$('head')
|
|
769
|
-
if(head===null) this.html.insert(1,'<head></head>')
|
|
770
|
-
return this.$('head')
|
|
771
|
-
}
|
|
772
|
-
get body(){
|
|
773
|
-
const body=this.html.$('body')
|
|
774
|
-
if(body===null) this.head.insert(3,'<body></body>')
|
|
775
|
-
return this.$('body')
|
|
776
|
-
}
|
|
777
|
-
get title(){
|
|
778
|
-
const title=this.head.$('title')
|
|
779
|
-
if(title===null) this.head.insert(1,'<title></title>')
|
|
780
|
-
return this.$('title').innerText
|
|
781
|
-
}
|
|
782
|
-
get charset(){return this.$('meta[charset]')}
|
|
783
|
-
set title(title){return this.head.$('title').innerText=title}
|
|
784
|
-
get clone(){return new Document(this)}
|
|
785
|
-
}
|
|
786
|
-
function parseAttributes(str){
|
|
787
|
-
const attrs={};
|
|
788
|
-
let key="";
|
|
789
|
-
let value="";
|
|
790
|
-
let isKey=true;
|
|
791
|
-
let quoteChar=null;
|
|
792
|
-
for (let i=0; i< str.length; i++){
|
|
793
|
-
const char=str[i];
|
|
794
|
-
if (isKey && (char==='=' || char===' ')){
|
|
795
|
-
if (char==='=') isKey=false;
|
|
796
|
-
else if (key.trim()){
|
|
797
|
-
attrs[key.trim()]=true;
|
|
798
|
-
key="";
|
|
799
|
-
}
|
|
800
|
-
continue;
|
|
801
|
-
}
|
|
802
|
-
if (!quoteChar && (char==='"' || char==="'")){
|
|
803
|
-
quoteChar=char;
|
|
804
|
-
continue;
|
|
805
|
-
} else if (quoteChar && char===quoteChar){
|
|
806
|
-
quoteChar=null;
|
|
807
|
-
attrs[key.trim()]=value.trim();
|
|
808
|
-
key=""; value=""; isKey=true;
|
|
809
|
-
continue;
|
|
810
|
-
}
|
|
811
|
-
if (isKey) key+=char;
|
|
812
|
-
else value+=char;
|
|
813
|
-
}
|
|
814
|
-
if (key.trim() &&!value) attrs[key.trim()]='';
|
|
815
|
-
return attrs;
|
|
816
|
-
}
|
|
817
|
-
const VOID_TAGS=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","!doctype",'?xml']);
|
|
818
|
-
function parseHTML(html){
|
|
819
|
-
const root=new Root();
|
|
820
|
-
const stack=[root];
|
|
821
|
-
let currentText="",i=0;
|
|
822
|
-
let max=0
|
|
823
|
-
function parseScript(){
|
|
824
|
-
if (!html.startsWith("<script",i)) return false;
|
|
825
|
-
const openTagEnd=html.indexOf(">",i);
|
|
826
|
-
if (openTagEnd===-1) return false;
|
|
827
|
-
const attributesString=html.substring(i+7,openTagEnd).trim();
|
|
828
|
-
const attributes=parseAttributes(attributesString);
|
|
829
|
-
let closeTagStart=html.indexOf("</script>",openTagEnd);
|
|
830
|
-
if (closeTagStart===-1) return false;
|
|
831
|
-
const content=html.substring(openTagEnd+1,closeTagStart);
|
|
832
|
-
const scriptNode=new Node('script',attributes,stack[stack.length-1]);
|
|
833
|
-
if(content.length>0) scriptNode.childNodes.push(content);
|
|
834
|
-
i=closeTagStart+9;
|
|
835
|
-
return true;
|
|
836
|
-
}
|
|
837
|
-
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
838
|
-
if (!html.startsWith(startStr,i)) return false
|
|
839
|
-
if(currentText.length) stack[stack.length - 1].childNodes.push(new TextNode(currentText)); // new!
|
|
840
|
-
const end=html.indexOf(endStr,i+n1);
|
|
841
|
-
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
842
|
-
strNode.childNodes.push(html.substring(i+n1,end));
|
|
843
|
-
i=end+n2;
|
|
844
|
-
return true
|
|
845
|
-
}
|
|
846
|
-
while (i< html.length){
|
|
847
|
-
if(i>=max) max=i;
|
|
848
|
-
else break;
|
|
849
|
-
if (parseScript()) continue
|
|
850
|
-
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
851
|
-
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
852
|
-
if (html.startsWith("<![CDATA[",i)){
|
|
853
|
-
const end=html.indexOf("]]>",i+9);
|
|
854
|
-
if (end===-1) break;
|
|
855
|
-
const content=html.substring(i+9,end);
|
|
856
|
-
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
857
|
-
cdataNode.nodeValue=content;
|
|
858
|
-
i=end+3;
|
|
859
|
-
continue;
|
|
860
|
-
}
|
|
861
|
-
if (html.startsWith("<",i)){
|
|
862
|
-
if (currentText && stack[stack.length-1]){
|
|
863
|
-
const textNode=new TextNode(currentText)
|
|
864
|
-
stack[stack.length-1].childNodes.push(textNode);
|
|
865
|
-
textNode.parent=stack[stack.length-1]
|
|
866
|
-
currentText="";
|
|
867
|
-
}
|
|
868
|
-
let tagEnd=i+1;
|
|
869
|
-
let insideQuotes=false;
|
|
870
|
-
let quoteChar=null;
|
|
871
|
-
while (tagEnd< html.length){
|
|
872
|
-
const char=html[tagEnd];
|
|
873
|
-
if (!insideQuotes && (char==='"' || char==="'")){
|
|
874
|
-
insideQuotes=true;
|
|
875
|
-
quoteChar=char;
|
|
876
|
-
} else if (insideQuotes && char===quoteChar){
|
|
877
|
-
insideQuotes=false;
|
|
878
|
-
quoteChar=null;
|
|
879
|
-
}
|
|
880
|
-
if (!insideQuotes && char==='>') break;
|
|
881
|
-
tagEnd++;
|
|
882
|
-
}
|
|
883
|
-
const tagContent=html.substring(i+1,tagEnd);
|
|
884
|
-
if (tagContent.startsWith("/")) stack.pop();
|
|
885
|
-
else{
|
|
886
|
-
let isSelfClosing=tagContent.endsWith('/');
|
|
887
|
-
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
888
|
-
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
889
|
-
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
890
|
-
const attributes=parseAttributes(attributesString);
|
|
891
|
-
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
892
|
-
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
893
|
-
}
|
|
894
|
-
i=tagEnd+1;
|
|
895
|
-
} else{
|
|
896
|
-
currentText+=html[i];
|
|
897
|
-
i++;
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
if (currentText.trim() && stack[stack.length-1]) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
901
|
-
return root;
|
|
902
|
-
}
|
|
903
|
-
function buildFromCache(cached){
|
|
904
|
-
function buildNode(cache,parent=null){
|
|
905
|
-
if(typeof cache==='string') return parent.childNodes.push(cache)
|
|
906
|
-
const{isSingle,tagName,attributes,childNodes,textContent}=cache
|
|
907
|
-
if(textContent) return parent.childNodes.push(new TextNode(textContent))
|
|
908
|
-
if(isSingle && parent) return parent.childNodes.push(new SingleNode(tagName,attributes))
|
|
909
|
-
const newDoc=tagName==='ROOT' ? new Root() : new Node(tagName,attributes,parent)
|
|
910
|
-
childNodes.forEach(childNode=>{
|
|
911
|
-
buildNode(childNode,newDoc)
|
|
912
|
-
});
|
|
913
|
-
return newDoc
|
|
914
|
-
}
|
|
915
|
-
return buildNode(cached)
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
function cacheDoc(doc){
|
|
919
|
-
const props=['isSingle','tagName','attributes']
|
|
920
|
-
function addToCache(element,cache={}){
|
|
921
|
-
if(typeof element==='string') return element
|
|
922
|
-
if(element.nodeName==='#text') return{textContent:element.textContent}
|
|
923
|
-
props.forEach(prop=>{
|
|
924
|
-
if(element[prop]){
|
|
925
|
-
cache[prop]=typeof element[prop]==='object' ?{...element[prop]} : element[prop]
|
|
926
|
-
}
|
|
927
|
-
});
|
|
928
|
-
if(!element.childNodes) return cache
|
|
929
|
-
cache.childNodes=[]
|
|
930
|
-
element.childNodes.forEach(childNode=>{
|
|
931
|
-
cache.childNodes.push(addToCache(childNode))
|
|
932
|
-
});
|
|
933
|
-
return cache
|
|
934
|
-
}
|
|
935
|
-
return addToCache(doc)
|
|
936
|
-
}
|
|
937
|
-
module.exports = { parseHTML, Node, Query, TextNode, SingleNode, buildFromCache, cacheDoc, Root, Document }
|
|
220
|
+
function getDataset(element){
|
|
938
221
|
return new Proxy(element.attributes,{
|
|
939
222
|
get: (target,prop)=>{return target[getDataName(prop)]},set: (target,prop,value)=>{target[getDataName(prop)]=value; return true},deleteProperty: (target,prop)=>{
|
|
940
223
|
const dataAttr=getDataName(prop)
|
|
941
224
|
if (dataAttr in target){
|
|
942
225
|
delete target[dataAttr];
|
|
943
226
|
return true;
|
|
944
227
|
}
|
|
945
228
|
return false;
|
|
946
229
|
}
|
|
947
230
|
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function find(selectors,element,collection,first=false,firstTime=true){
|
|
948
234
|
if(!firstTime)
|
|
949
235
|
for(let selector of selectors){
|
|
950
236
|
if(checkElement(element,selector)) collection.add(element)
|
|
951
237
|
}
|
|
952
238
|
if(element.children)
|
|
953
239
|
element.children.forEach(child=>{
|
|
954
240
|
if(first && collection.size>0) return
|
|
955
241
|
find(selectors,child,collection,first,false)
|
|
956
242
|
})
|
|
957
243
|
return firstTime ? [...collection] : collection
|
|
244
|
+
}
|
|
245
|
+
class TextNode{
|
|
958
246
|
constructor(data){
|
|
959
247
|
this.nodeName='#text';
|
|
960
248
|
this.parent=null;
|
|
961
249
|
this.textContent=data;
|
|
962
250
|
}
|
|
963
251
|
get nodeValue(){return this.textContent}
|
|
964
252
|
get parentNode(){return this.parent}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function buildStyle(attributes){
|
|
965
256
|
const styles=attributes.style || "";
|
|
966
257
|
const camelToKebab=str=>str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,'$1-$2').toLowerCase();
|
|
967
258
|
const kebabToCamel=str=>str.replace(/-([a-z])/g,g=>g[1].toUpperCase());
|
|
968
259
|
const baseStyleObj=styles.split(";").reduce((acc,style)=>{
|
|
969
260
|
const [key,value]=style.split(":").map(s=>s.trim());
|
|
970
261
|
if (key && value) acc[kebabToCamel(key)]=value;
|
|
971
262
|
return acc;
|
|
972
263
|
},{});
|
|
973
264
|
return new Proxy(baseStyleObj,{
|
|
974
265
|
get: (obj,prop)=>obj[camelToKebab(prop)] || obj[prop],set: (obj,prop,value)=>{
|
|
975
266
|
obj[camelToKebab(prop)]=value;
|
|
976
267
|
attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
|
|
977
268
|
return true;
|
|
978
269
|
},deleteProperty: (obj,prop)=>{
|
|
979
270
|
delete obj[camelToKebab(prop)];
|
|
980
271
|
attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
|
|
981
272
|
return true;
|
|
982
273
|
}
|
|
983
274
|
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
class NodeClassList{
|
|
984
278
|
constructor(node){ this.node=node }
|
|
985
279
|
get classes(){ return (this.node.attributes.class || "").split(" ").filter(Boolean) }
|
|
986
280
|
set classes(val){ this.node.attributes.class=val.join(" ") }
|
|
987
281
|
contains(className){ return this.classes.includes(className) }
|
|
988
282
|
add(className){
|
|
989
283
|
const currentClasses=this.classes;
|
|
990
284
|
if (!currentClasses.includes(className)) this.classes=[...currentClasses,className];
|
|
991
285
|
}
|
|
992
286
|
remove(className){ this.classes=this.classes.filter(cls=>cls!==className); }
|
|
993
287
|
toggle(className){
|
|
994
288
|
if (this.classes.includes(className)) this.remove(className);
|
|
995
289
|
else this.add(className);
|
|
996
290
|
}
|
|
997
291
|
replace(oldClass,newClass){
|
|
998
292
|
if (this.classes.includes(oldClass)){
|
|
999
293
|
this.remove(oldClass);
|
|
1000
294
|
this.add(newClass);
|
|
1001
295
|
}
|
|
1002
296
|
}
|
|
297
|
+
}
|
|
298
|
+
function insertBefore(arr,index,newItem){
|
|
1003
299
|
const existingIndex=arr.indexOf(newItem);
|
|
1004
300
|
if (existingIndex!==-1) arr.splice(existingIndex,1);
|
|
1005
301
|
arr.splice(index,0,newItem);
|
|
302
|
+
}
|
|
1006
303
|
class Node{
|
|
1007
304
|
constructor(tagName,attributes={},parent=null){
|
|
1008
305
|
this.isSingle=false;
|
|
1009
306
|
this._tagName=tagName.toLowerCase();
|
|
1010
307
|
this.tagName=tagName.toUpperCase();
|
|
1011
308
|
this.attributes=attributes;
|
|
1012
309
|
this.childNodes=[];
|
|
1013
310
|
if (parent!==null) parent.childNodes.push(this)
|
|
1014
311
|
this.parent=parent;
|
|
1015
312
|
this._classList=null;
|
|
1016
313
|
this.__style=null;
|
|
1017
314
|
this._dataset=null
|
|
1018
315
|
}
|
|
1019
316
|
get id(){ return this.attributes.id ? this.attributes.id : null; }
|
|
1020
317
|
set id(newValue){ this.attributes.id=newValue; }
|
|
1021
318
|
get className(){ return this.attributes.class || null }
|
|
1022
319
|
get parentNode(){ return this.parent }
|
|
1023
320
|
get ancestors(){
|
|
1024
321
|
if (!this.parent) return []
|
|
1025
322
|
const ancestors=[]
|
|
1026
323
|
let element=this.parent
|
|
1027
324
|
while (element.tagName!=='ROOT'){
|
|
1028
325
|
ancestors.push(element)
|
|
1029
326
|
element=element.parent
|
|
1030
327
|
}
|
|
1031
328
|
return ancestors.reverse()
|
|
1032
329
|
}
|
|
1033
330
|
get childNodeIndex(){
|
|
1034
331
|
if (!this.parent) return null
|
|
1035
332
|
return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
|
|
1036
333
|
}
|
|
1037
334
|
get childIndex(){
|
|
1038
335
|
if (!this.parent) return null
|
|
1039
336
|
return this.parent.children ? this.parent.children.indexOf(this) : null
|
|
1040
337
|
}
|
|
1041
338
|
get previousElementSibling(){ return this.prev }
|
|
1042
339
|
get prev(){
|
|
1043
340
|
if (this.childIndex===null) return null
|
|
1044
341
|
return this.parent.children[this.childIndex-1]
|
|
1045
342
|
}
|
|
1046
343
|
get nextElementSibling(){ return this.next }
|
|
1047
344
|
get next(){
|
|
1048
345
|
if (this.childIndex===null) return null
|
|
1049
346
|
return this.parent.children[this.childIndex+1] || null
|
|
1050
347
|
}
|
|
1051
348
|
get dataset(){
|
|
1052
349
|
if (!this._dataset) this._dataset=getDataset(this);
|
|
1053
350
|
return this._dataset;
|
|
1054
351
|
}
|
|
1055
352
|
get classList(){
|
|
1056
353
|
if (!this._classList) this._classList=new NodeClassList(this);
|
|
1057
354
|
return this._classList;
|
|
1058
355
|
}
|
|
1059
356
|
get style(){
|
|
1060
357
|
if (!this.__style) this.__style=buildStyle(this.attributes)
|
|
1061
358
|
return this.__style
|
|
1062
359
|
}
|
|
1063
360
|
get outerHTML(){
|
|
1064
361
|
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
1065
362
|
return `<${this._tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this._tagName}>`;
|
|
1066
363
|
}
|
|
1067
364
|
getAttribute(attrName){ return this.attributes[attrName]!==undefined ? this.attributes[attrName] : null }
|
|
1068
365
|
setAttribute(attrName,value=''){ this.attributes[attrName]=value }
|
|
1069
366
|
removeAttribute(attrName){ delete this.attributes[attrName] }
|
|
1070
367
|
remove(){
|
|
1071
368
|
if (!this.parent) return
|
|
1072
369
|
const index=this.childNodeIndex;
|
|
1073
370
|
if (index!==null) this.parent.childNodes.splice(index,1);
|
|
1074
371
|
}
|
|
1075
372
|
get innerHTML(){
|
|
1076
373
|
return this.childNodes.map(child=>{
|
|
1077
374
|
if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
|
|
1078
375
|
else if (child instanceof TextNode) return child.textContent;
|
|
1079
376
|
else return child
|
|
1080
377
|
}).join("");
|
|
1081
378
|
}
|
|
1082
379
|
get innerText(){
|
|
1083
380
|
return this.childNodes.map(child=>{
|
|
1084
381
|
if (child instanceof Node || child instanceof SingleNode) return child.innerText;
|
|
1085
382
|
else if (child instanceof TextNode) return child.textContent;
|
|
1086
383
|
else return child
|
|
1087
384
|
}).join("");
|
|
1088
385
|
}
|
|
1089
386
|
set innerText(value){
|
|
1090
387
|
this.childNodes=[new TextNode(value)]
|
|
1091
388
|
return value
|
|
1092
389
|
}
|
|
1093
390
|
$$(query){ return this.querySelectorAll(query) }
|
|
1094
391
|
querySelectorAll(query){
|
|
1095
392
|
const selectors=Query.get(query)
|
|
1096
393
|
return find(selectors,this,new Set())
|
|
1097
394
|
}
|
|
1098
395
|
$(query){ return this.querySelector(query) }
|
|
1099
396
|
querySelector(query){
|
|
1100
397
|
const selectors=Query.get(query)
|
|
1101
398
|
return find(selectors,this,new Set(),true)[0] || null
|
|
1102
399
|
}
|
|
1103
400
|
getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
|
|
1104
401
|
getElementsByTagName(query){ return this.querySelectorAll(query) }
|
|
1105
402
|
getElementById(query){ return this.querySelector('#'+query) }
|
|
1106
403
|
get children(){
|
|
1107
404
|
return this.childNodes.filter(child=>{
|
|
1108
405
|
if (!(child instanceof Node)) return false
|
|
1109
406
|
if (child._tagName==='#comment') return false
|
|
1110
407
|
return true
|
|
1111
408
|
});
|
|
1112
409
|
}
|
|
1113
410
|
insertAdjacentElement(position,newElement){
|
|
1114
411
|
if (newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
|
|
1115
412
|
const pos=position.toLowerCase();
|
|
1116
413
|
if(newElement.parentNode){
|
|
1117
414
|
newElement.parentNode.childNodes=newElement.parentNode.childNodes.filter(el=>el!==newElement)
|
|
1118
415
|
}
|
|
1119
416
|
if (pos==='afterbegin' || pos==='beforeend'){
|
|
1120
417
|
if (pos==="afterbegin") this.childNodes.unshift(newElement);
|
|
1121
418
|
else if (pos==="beforeend") this.childNodes.push(newElement);
|
|
1122
419
|
newElement.parent=this
|
|
1123
420
|
return newElement
|
|
1124
421
|
}
|
|
1125
422
|
if (!this.parent) throw new Error("Can't insert element to element without parent")
|
|
1126
423
|
if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
|
|
1127
424
|
else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
|
|
1128
425
|
newElement.parent=this.parent
|
|
1129
426
|
return newElement
|
|
1130
427
|
}
|
|
1131
428
|
insertAdjacentHTML(position,html){
|
|
1132
429
|
const newNode=parseHTML(html);
|
|
1133
430
|
newNode.childNodes.forEach(node=>{
|
|
1134
431
|
this.insertAdjacentElement(position,node);
|
|
1135
432
|
});
|
|
1136
433
|
return newNode
|
|
1137
434
|
}
|
|
1138
435
|
insertAdjacentText(position,text){
|
|
1139
436
|
return this.insertAdjacentElement(position,new TextNode(text));
|
|
1140
437
|
}
|
|
1141
438
|
insert(position,element){
|
|
1142
439
|
const positions=['beforebegin','afterbegin','beforeend','afterend']
|
|
1143
440
|
if (positions[position]) position=positions[position]
|
|
1144
441
|
if (typeof element==='string'){
|
|
1145
442
|
element=element.trim()
|
|
1146
443
|
if (element.startsWith('<') && element.endsWith('>')){
|
|
1147
444
|
return this.insertAdjacentHTML(position,element)
|
|
1148
445
|
}
|
|
1149
446
|
return this.insertAdjacentText(position,element)
|
|
1150
447
|
}
|
|
1151
448
|
return this.insertAdjacentElement(position,element)
|
|
1152
449
|
}
|
|
1153
450
|
set innerHTML(html){
|
|
1154
451
|
this.childNodes=html.trim().startsWith('<') ? parseHTML(html).childNodes : [html]
|
|
1155
452
|
this.children.forEach(child=>child.parent=this);
|
|
1156
453
|
}
|
|
1157
454
|
set outerHTML(html){
|
|
1158
455
|
const parsed=parseHTML(html);
|
|
1159
456
|
if (!this.parent) throw new Error('element has no parent node')
|
|
1160
457
|
const index=this.childIndex
|
|
1161
458
|
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
1162
459
|
}
|
|
1163
460
|
appendChild(newChild){
|
|
1164
461
|
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
1165
462
|
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
1166
463
|
} else if (typeof newChild==='string') newChild=new TextNode(newChild)
|
|
1167
464
|
else return newChild
|
|
1168
465
|
this.childNodes.push(newChild);
|
|
1169
466
|
newChild.parent=this;
|
|
1170
467
|
return newChild;
|
|
1171
468
|
}
|
|
1172
469
|
get textContent(){
|
|
1173
470
|
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
1174
471
|
return this.childNodes.map(child=>{
|
|
1175
472
|
if (child instanceof SingleNode) return ''
|
|
1176
473
|
if (child instanceof TextNode) return child.nodeValue
|
|
1177
474
|
if (child instanceof Node) return child.textContent;
|
|
1178
475
|
else return child;
|
|
1179
476
|
}).join('');
|
|
1180
477
|
}
|
|
1181
478
|
set textContent(value){
|
|
1182
479
|
this.childNodes=[];
|
|
1183
480
|
if (value!==null && value!==undefined){
|
|
1184
481
|
this.childNodes.push(value.toString());
|
|
1185
482
|
}
|
|
1186
483
|
}
|
|
484
|
+
}
|
|
485
|
+
class SingleNode extends Node{
|
|
1187
486
|
constructor(tagName,attributes={},parent=null){
|
|
1188
487
|
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
1189
488
|
super(tagName,attributes,parent);
|
|
1190
489
|
this.isSingle=true
|
|
1191
490
|
}
|
|
1192
491
|
get outerHTML(){
|
|
1193
492
|
if (this._tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
|
|
1194
493
|
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
1195
494
|
return `<${this._tagName} ${attrs}${this._tagName==='?xml' ? '?' : ''}>`;
|
|
1196
495
|
}
|
|
1197
496
|
get innerHTML(){ return ""; }
|
|
1198
497
|
set innerHTML(_){ }
|
|
1199
498
|
$(_){return null}
|
|
1200
499
|
$$(_){return []}
|
|
1201
500
|
querySelectorAll(_){ return []; }
|
|
1202
501
|
querySelector(_){ return null; }
|
|
1203
502
|
getElementsByClassName(_){ return []; }
|
|
1204
503
|
getElementsByTagName(_){ return []; }
|
|
1205
504
|
getElementById(_){ return null; }
|
|
1206
505
|
get children(){ return []; }
|
|
1207
506
|
insertAdjacentElement(position,newElement){
|
|
1208
507
|
if(position==='afterbegin') position='beforebegin'
|
|
1209
508
|
if(position==='beforeend') position='afterend'
|
|
1210
509
|
return super.insertAdjacentElement(position,newElement)
|
|
1211
510
|
}
|
|
1212
511
|
insertAdjacentHTML(position,html){
|
|
1213
512
|
if(position==='afterbegin') position='beforebegin'
|
|
1214
513
|
if(position==='beforeend') position='afterend'
|
|
1215
514
|
return super.insertAdjacentHTML(position,html)
|
|
1216
515
|
}
|
|
1217
516
|
insertAdjacentText(position,text){
|
|
1218
517
|
if(position==='afterbegin') position='beforebegin'
|
|
1219
518
|
if(position==='beforeend') position='afterend'
|
|
1220
519
|
return super.insertAdjacentText(position,text)
|
|
1221
520
|
}
|
|
1222
521
|
insert(position,element){
|
|
1223
522
|
if(position===1) position=0
|
|
1224
523
|
if(position===2) position=3
|
|
1225
524
|
return super.insert(position,element)
|
|
1226
525
|
}
|
|
1227
526
|
appendChild(_){ }
|
|
1228
527
|
get textContent(){ return ""; }
|
|
1229
528
|
set textContent(_){ }
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
class Root extends Node{
|
|
1230
532
|
constructor(){
|
|
1231
533
|
super('ROOT',{},null);
|
|
1232
534
|
this.isSingle=false
|
|
1233
535
|
}
|
|
536
|
+
}
|
|
537
|
+
class Document extends Node{
|
|
1234
538
|
constructor(html,url){
|
|
1235
539
|
super('ROOT',{},null);
|
|
1236
540
|
this.isSingle=false
|
|
1237
541
|
this.URL=url
|
|
1238
542
|
if(html instanceof Document) this.childNodes=[...buildFromCache(cacheDoc(html)).childNodes]
|
|
1239
543
|
else if(typeof html==='string') this.innerHTML=html
|
|
1240
544
|
else this.html
|
|
1241
545
|
}
|
|
1242
546
|
get documentElement(){return this.html}
|
|
1243
547
|
get html(){
|
|
1244
548
|
const html=this.$('html')
|
|
1245
549
|
if(html===null) this.innerHTML=/*html*/`<html lang="en"><head><meta charset="UTF-8"><title></title></head><body></body></html>`
|
|
1246
550
|
return this.$('html')
|
|
1247
551
|
}
|
|
1248
552
|
get innerHTML(){
|
|
1249
553
|
const inner=super.innerHTML
|
|
1250
554
|
return '<!DOCTYPE html>'+inner
|
|
1251
555
|
}
|
|
1252
556
|
set innerHTML(html){return super.innerHTML=html}
|
|
1253
557
|
get head(){
|
|
1254
558
|
const head=this.html.$('head')
|
|
1255
559
|
if(head===null) this.html.insert(1,'<head></head>')
|
|
1256
560
|
return this.$('head')
|
|
1257
561
|
}
|
|
1258
562
|
get body(){
|
|
1259
563
|
const body=this.html.$('body')
|
|
1260
564
|
if(body===null) this.head.insert(3,'<body></body>')
|
|
1261
565
|
return this.$('body')
|
|
1262
566
|
}
|
|
1263
567
|
get title(){
|
|
1264
568
|
const title=this.head.$('title')
|
|
1265
569
|
if(title===null) this.head.insert(1,'<title></title>')
|
|
1266
570
|
return this.$('title').innerText
|
|
1267
571
|
}
|
|
1268
572
|
get charset(){return this.$('meta[charset]')}
|
|
1269
573
|
set title(title){return this.head.$('title').innerText=title}
|
|
1270
574
|
get clone(){return new Document(this)}
|
|
575
|
+
}
|
|
576
|
+
function parseAttributes(str){
|
|
1271
577
|
const attrs={};
|
|
1272
578
|
let key="";
|
|
1273
579
|
let value="";
|
|
1274
580
|
let isKey=true;
|
|
1275
581
|
let quoteChar=null;
|
|
1276
582
|
for (let i=0; i< str.length; i++){
|
|
1277
583
|
const char=str[i];
|
|
1278
584
|
if (isKey && (char==='=' || char===' ')){
|
|
1279
585
|
if (char==='=') isKey=false;
|
|
1280
586
|
else if (key.trim()){
|
|
1281
587
|
attrs[key.trim()]=true;
|
|
1282
588
|
key="";
|
|
1283
589
|
}
|
|
1284
590
|
continue;
|
|
1285
591
|
}
|
|
1286
592
|
if (!quoteChar && (char==='"' || char==="'")){
|
|
1287
593
|
quoteChar=char;
|
|
1288
594
|
continue;
|
|
1289
595
|
} else if (quoteChar && char===quoteChar){
|
|
1290
596
|
quoteChar=null;
|
|
1291
597
|
attrs[key.trim()]=value.trim();
|
|
1292
598
|
key=""; value=""; isKey=true;
|
|
1293
599
|
continue;
|
|
1294
600
|
}
|
|
1295
601
|
if (isKey) key+=char;
|
|
1296
602
|
else value+=char;
|
|
1297
603
|
}
|
|
1298
604
|
if (key.trim() &&!value) attrs[key.trim()]='';
|
|
1299
605
|
return attrs;
|
|
606
|
+
}
|
|
607
|
+
const VOID_TAGS=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","!doctype",'?xml']);
|
|
608
|
+
function parseHTML(html){
|
|
1300
609
|
const root=new Root();
|
|
1301
610
|
const stack=[root];
|
|
1302
611
|
let currentText="",i=0;
|
|
1303
612
|
let max=0
|
|
1304
613
|
function parseScript(){
|
|
1305
614
|
if (!html.startsWith("<script",i)) return false;
|
|
1306
615
|
const openTagEnd=html.indexOf(">",i);
|
|
1307
616
|
if (openTagEnd===-1) return false;
|
|
1308
617
|
const attributesString=html.substring(i+7,openTagEnd).trim();
|
|
1309
618
|
const attributes=parseAttributes(attributesString);
|
|
1310
619
|
let closeTagStart=html.indexOf("</script>",openTagEnd);
|
|
1311
620
|
if (closeTagStart===-1) return false;
|
|
1312
621
|
const content=html.substring(openTagEnd+1,closeTagStart);
|
|
1313
622
|
const scriptNode=new Node('script',attributes,stack[stack.length-1]);
|
|
1314
623
|
if(content.length>0) scriptNode.childNodes.push(content);
|
|
1315
624
|
i=closeTagStart+9;
|
|
1316
625
|
return true;
|
|
1317
626
|
}
|
|
1318
627
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
1319
628
|
if (!html.startsWith(startStr,i)) return false
|
|
1320
629
|
if(currentText.length) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
1321
630
|
const end=html.indexOf(endStr,i+n1);
|
|
1322
631
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
1323
632
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
1324
633
|
i=end+n2;
|
|
1325
634
|
return true
|
|
1326
635
|
}
|
|
1327
636
|
while (i< html.length){
|
|
1328
637
|
if(i>=max) max=i;
|
|
1329
638
|
else break;
|
|
1330
639
|
if (parseScript()) continue
|
|
1331
640
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
1332
641
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
1333
642
|
if (html.startsWith("<![CDATA[",i)){
|
|
1334
643
|
const end=html.indexOf("]]>",i+9);
|
|
1335
644
|
if (end===-1) break;
|
|
1336
645
|
const content=html.substring(i+9,end);
|
|
1337
646
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
1338
647
|
cdataNode.nodeValue=content;
|
|
1339
648
|
i=end+3;
|
|
1340
649
|
continue;
|
|
1341
650
|
}
|
|
1342
651
|
if (html.startsWith("<",i)){
|
|
1343
652
|
if (currentText && stack[stack.length-1]){
|
|
1344
653
|
const textNode=new TextNode(currentText)
|
|
1345
654
|
stack[stack.length-1].childNodes.push(textNode);
|
|
1346
655
|
textNode.parent=stack[stack.length-1]
|
|
1347
656
|
currentText="";
|
|
1348
657
|
}
|
|
1349
658
|
let tagEnd=i+1;
|
|
1350
659
|
let insideQuotes=false;
|
|
1351
660
|
let quoteChar=null;
|
|
1352
661
|
while (tagEnd< html.length){
|
|
1353
662
|
const char=html[tagEnd];
|
|
1354
663
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
1355
664
|
insideQuotes=true;
|
|
1356
665
|
quoteChar=char;
|
|
1357
666
|
} else if (insideQuotes && char===quoteChar){
|
|
1358
667
|
insideQuotes=false;
|
|
1359
668
|
quoteChar=null;
|
|
1360
669
|
}
|
|
1361
670
|
if (!insideQuotes && char==='>') break;
|
|
1362
671
|
tagEnd++;
|
|
1363
672
|
}
|
|
1364
673
|
const tagContent=html.substring(i+1,tagEnd);
|
|
1365
674
|
if (tagContent.startsWith("/")) stack.pop();
|
|
1366
675
|
else{
|
|
1367
676
|
let isSelfClosing=tagContent.endsWith('/');
|
|
1368
677
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
1369
678
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
1370
679
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
1371
680
|
const attributes=parseAttributes(attributesString);
|
|
1372
681
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
1373
682
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
1374
683
|
}
|
|
1375
684
|
i=tagEnd+1;
|
|
1376
685
|
} else{
|
|
1377
686
|
currentText+=html[i];
|
|
1378
687
|
i++;
|
|
1379
688
|
}
|
|
1380
689
|
}
|
|
1381
690
|
if (currentText.trim() && stack[stack.length-1]) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
1382
691
|
return root;
|
|
692
|
+
}
|
|
693
|
+
function buildFromCache(cached){
|
|
1383
694
|
function buildNode(cache,parent=null){
|
|
1384
695
|
if(typeof cache==='string') return parent.childNodes.push(cache)
|
|
1385
696
|
const{isSingle,tagName,attributes,childNodes,textContent}=cache
|
|
1386
697
|
if(textContent) return parent.childNodes.push(new TextNode(textContent))
|
|
1387
698
|
if(isSingle && parent) return parent.childNodes.push(new SingleNode(tagName,attributes))
|
|
1388
699
|
const newDoc=tagName==='ROOT' ? new Root() : new Node(tagName,attributes,parent)
|
|
1389
700
|
childNodes.forEach(childNode=>{
|
|
1390
701
|
buildNode(childNode,newDoc)
|
|
1391
702
|
});
|
|
1392
703
|
return newDoc
|
|
1393
704
|
}
|
|
1394
705
|
return buildNode(cached)
|
|
706
|
+
}
|
|
1395
707
|
|
|
708
|
+
function cacheDoc(doc){
|
|
1396
709
|
const props=['isSingle','tagName','attributes']
|
|
1397
710
|
function addToCache(element,cache={}){
|
|
1398
711
|
if(typeof element==='string') return element
|
|
1399
712
|
if(element.nodeName==='#text') return{textContent:element.textContent}
|
|
1400
713
|
props.forEach(prop=>{
|
|
1401
714
|
if(element[prop]){
|
|
1402
715
|
cache[prop]=typeof element[prop]==='object' ?{...element[prop]} : element[prop]
|
|
1403
716
|
}
|
|
1404
717
|
});
|
|
1405
718
|
if(!element.childNodes) return cache
|
|
1406
719
|
cache.childNodes=[]
|
|
1407
720
|
element.childNodes.forEach(childNode=>{
|
|
1408
721
|
cache.childNodes.push(addToCache(childNode))
|
|
1409
722
|
});
|
|
1410
723
|
return cache
|
|
1411
724
|
}
|
|
1412
725
|
return addToCache(doc)
|
|
726
|
+
}
|
|
727
|
+
module.exports = { parseHTML,Node,Query,TextNode,SingleNode,buildFromCache,cacheDoc,Root,Document }
|