als-document 1.4.1 → 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 +42 -727
- package/index.js +40 -725
- package/index.mjs +40 -725
- 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
|
-
}
|
|
220
|
+
function getDataset(element){
|
|
937
221
|
return new Proxy(element.attributes,{
|
|
938
222
|
get: (target,prop)=>{return target[getDataName(prop)]},set: (target,prop,value)=>{target[getDataName(prop)]=value; return true},deleteProperty: (target,prop)=>{
|
|
939
223
|
const dataAttr=getDataName(prop)
|
|
940
224
|
if (dataAttr in target){
|
|
941
225
|
delete target[dataAttr];
|
|
942
226
|
return true;
|
|
943
227
|
}
|
|
944
228
|
return false;
|
|
945
229
|
}
|
|
946
230
|
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function find(selectors,element,collection,first=false,firstTime=true){
|
|
947
234
|
if(!firstTime)
|
|
948
235
|
for(let selector of selectors){
|
|
949
236
|
if(checkElement(element,selector)) collection.add(element)
|
|
950
237
|
}
|
|
951
238
|
if(element.children)
|
|
952
239
|
element.children.forEach(child=>{
|
|
953
240
|
if(first && collection.size>0) return
|
|
954
241
|
find(selectors,child,collection,first,false)
|
|
955
242
|
})
|
|
956
243
|
return firstTime ? [...collection] : collection
|
|
244
|
+
}
|
|
245
|
+
class TextNode{
|
|
957
246
|
constructor(data){
|
|
958
247
|
this.nodeName='#text';
|
|
959
248
|
this.parent=null;
|
|
960
249
|
this.textContent=data;
|
|
961
250
|
}
|
|
962
251
|
get nodeValue(){return this.textContent}
|
|
963
252
|
get parentNode(){return this.parent}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function buildStyle(attributes){
|
|
964
256
|
const styles=attributes.style || "";
|
|
965
257
|
const camelToKebab=str=>str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,'$1-$2').toLowerCase();
|
|
966
258
|
const kebabToCamel=str=>str.replace(/-([a-z])/g,g=>g[1].toUpperCase());
|
|
967
259
|
const baseStyleObj=styles.split(";").reduce((acc,style)=>{
|
|
968
260
|
const [key,value]=style.split(":").map(s=>s.trim());
|
|
969
261
|
if (key && value) acc[kebabToCamel(key)]=value;
|
|
970
262
|
return acc;
|
|
971
263
|
},{});
|
|
972
264
|
return new Proxy(baseStyleObj,{
|
|
973
265
|
get: (obj,prop)=>obj[camelToKebab(prop)] || obj[prop],set: (obj,prop,value)=>{
|
|
974
266
|
obj[camelToKebab(prop)]=value;
|
|
975
267
|
attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
|
|
976
268
|
return true;
|
|
977
269
|
},deleteProperty: (obj,prop)=>{
|
|
978
270
|
delete obj[camelToKebab(prop)];
|
|
979
271
|
attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
|
|
980
272
|
return true;
|
|
981
273
|
}
|
|
982
274
|
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
class NodeClassList{
|
|
983
278
|
constructor(node){ this.node=node }
|
|
984
279
|
get classes(){ return (this.node.attributes.class || "").split(" ").filter(Boolean) }
|
|
985
280
|
set classes(val){ this.node.attributes.class=val.join(" ") }
|
|
986
281
|
contains(className){ return this.classes.includes(className) }
|
|
987
282
|
add(className){
|
|
988
283
|
const currentClasses=this.classes;
|
|
989
284
|
if (!currentClasses.includes(className)) this.classes=[...currentClasses,className];
|
|
990
285
|
}
|
|
991
286
|
remove(className){ this.classes=this.classes.filter(cls=>cls!==className); }
|
|
992
287
|
toggle(className){
|
|
993
288
|
if (this.classes.includes(className)) this.remove(className);
|
|
994
289
|
else this.add(className);
|
|
995
290
|
}
|
|
996
291
|
replace(oldClass,newClass){
|
|
997
292
|
if (this.classes.includes(oldClass)){
|
|
998
293
|
this.remove(oldClass);
|
|
999
294
|
this.add(newClass);
|
|
1000
295
|
}
|
|
1001
296
|
}
|
|
297
|
+
}
|
|
298
|
+
function insertBefore(arr,index,newItem){
|
|
1002
299
|
const existingIndex=arr.indexOf(newItem);
|
|
1003
300
|
if (existingIndex!==-1) arr.splice(existingIndex,1);
|
|
1004
301
|
arr.splice(index,0,newItem);
|
|
302
|
+
}
|
|
1005
303
|
class Node{
|
|
1006
304
|
constructor(tagName,attributes={},parent=null){
|
|
1007
305
|
this.isSingle=false;
|
|
1008
306
|
this._tagName=tagName.toLowerCase();
|
|
1009
307
|
this.tagName=tagName.toUpperCase();
|
|
1010
308
|
this.attributes=attributes;
|
|
1011
309
|
this.childNodes=[];
|
|
1012
310
|
if (parent!==null) parent.childNodes.push(this)
|
|
1013
311
|
this.parent=parent;
|
|
1014
312
|
this._classList=null;
|
|
1015
313
|
this.__style=null;
|
|
1016
314
|
this._dataset=null
|
|
1017
315
|
}
|
|
1018
316
|
get id(){ return this.attributes.id ? this.attributes.id : null; }
|
|
1019
317
|
set id(newValue){ this.attributes.id=newValue; }
|
|
1020
318
|
get className(){ return this.attributes.class || null }
|
|
1021
319
|
get parentNode(){ return this.parent }
|
|
1022
320
|
get ancestors(){
|
|
1023
321
|
if (!this.parent) return []
|
|
1024
322
|
const ancestors=[]
|
|
1025
323
|
let element=this.parent
|
|
1026
324
|
while (element.tagName!=='ROOT'){
|
|
1027
325
|
ancestors.push(element)
|
|
1028
326
|
element=element.parent
|
|
1029
327
|
}
|
|
1030
328
|
return ancestors.reverse()
|
|
1031
329
|
}
|
|
1032
330
|
get childNodeIndex(){
|
|
1033
331
|
if (!this.parent) return null
|
|
1034
332
|
return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
|
|
1035
333
|
}
|
|
1036
334
|
get childIndex(){
|
|
1037
335
|
if (!this.parent) return null
|
|
1038
336
|
return this.parent.children ? this.parent.children.indexOf(this) : null
|
|
1039
337
|
}
|
|
1040
338
|
get previousElementSibling(){ return this.prev }
|
|
1041
339
|
get prev(){
|
|
1042
340
|
if (this.childIndex===null) return null
|
|
1043
341
|
return this.parent.children[this.childIndex-1]
|
|
1044
342
|
}
|
|
1045
343
|
get nextElementSibling(){ return this.next }
|
|
1046
344
|
get next(){
|
|
1047
345
|
if (this.childIndex===null) return null
|
|
1048
346
|
return this.parent.children[this.childIndex+1] || null
|
|
1049
347
|
}
|
|
1050
348
|
get dataset(){
|
|
1051
349
|
if (!this._dataset) this._dataset=getDataset(this);
|
|
1052
350
|
return this._dataset;
|
|
1053
351
|
}
|
|
1054
352
|
get classList(){
|
|
1055
353
|
if (!this._classList) this._classList=new NodeClassList(this);
|
|
1056
354
|
return this._classList;
|
|
1057
355
|
}
|
|
1058
356
|
get style(){
|
|
1059
357
|
if (!this.__style) this.__style=buildStyle(this.attributes)
|
|
1060
358
|
return this.__style
|
|
1061
359
|
}
|
|
1062
360
|
get outerHTML(){
|
|
1063
361
|
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
1064
362
|
return `<${this._tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this._tagName}>`;
|
|
1065
363
|
}
|
|
1066
364
|
getAttribute(attrName){ return this.attributes[attrName]!==undefined ? this.attributes[attrName] : null }
|
|
1067
365
|
setAttribute(attrName,value=''){ this.attributes[attrName]=value }
|
|
1068
366
|
removeAttribute(attrName){ delete this.attributes[attrName] }
|
|
1069
367
|
remove(){
|
|
1070
368
|
if (!this.parent) return
|
|
1071
369
|
const index=this.childNodeIndex;
|
|
1072
370
|
if (index!==null) this.parent.childNodes.splice(index,1);
|
|
1073
371
|
}
|
|
1074
372
|
get innerHTML(){
|
|
1075
373
|
return this.childNodes.map(child=>{
|
|
1076
374
|
if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
|
|
1077
375
|
else if (child instanceof TextNode) return child.textContent;
|
|
1078
376
|
else return child
|
|
1079
377
|
}).join("");
|
|
1080
378
|
}
|
|
1081
379
|
get innerText(){
|
|
1082
380
|
return this.childNodes.map(child=>{
|
|
1083
381
|
if (child instanceof Node || child instanceof SingleNode) return child.innerText;
|
|
1084
382
|
else if (child instanceof TextNode) return child.textContent;
|
|
1085
383
|
else return child
|
|
1086
384
|
}).join("");
|
|
1087
385
|
}
|
|
1088
386
|
set innerText(value){
|
|
1089
387
|
this.childNodes=[new TextNode(value)]
|
|
1090
388
|
return value
|
|
1091
389
|
}
|
|
1092
390
|
$$(query){ return this.querySelectorAll(query) }
|
|
1093
391
|
querySelectorAll(query){
|
|
1094
392
|
const selectors=Query.get(query)
|
|
1095
393
|
return find(selectors,this,new Set())
|
|
1096
394
|
}
|
|
1097
395
|
$(query){ return this.querySelector(query) }
|
|
1098
396
|
querySelector(query){
|
|
1099
397
|
const selectors=Query.get(query)
|
|
1100
398
|
return find(selectors,this,new Set(),true)[0] || null
|
|
1101
399
|
}
|
|
1102
400
|
getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
|
|
1103
401
|
getElementsByTagName(query){ return this.querySelectorAll(query) }
|
|
1104
402
|
getElementById(query){ return this.querySelector('#'+query) }
|
|
1105
403
|
get children(){
|
|
1106
404
|
return this.childNodes.filter(child=>{
|
|
1107
405
|
if (!(child instanceof Node)) return false
|
|
1108
406
|
if (child._tagName==='#comment') return false
|
|
1109
407
|
return true
|
|
1110
408
|
});
|
|
1111
409
|
}
|
|
1112
410
|
insertAdjacentElement(position,newElement){
|
|
1113
411
|
if (newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
|
|
1114
412
|
const pos=position.toLowerCase();
|
|
1115
413
|
if(newElement.parentNode){
|
|
1116
414
|
newElement.parentNode.childNodes=newElement.parentNode.childNodes.filter(el=>el!==newElement)
|
|
1117
415
|
}
|
|
1118
416
|
if (pos==='afterbegin' || pos==='beforeend'){
|
|
1119
417
|
if (pos==="afterbegin") this.childNodes.unshift(newElement);
|
|
1120
418
|
else if (pos==="beforeend") this.childNodes.push(newElement);
|
|
1121
419
|
newElement.parent=this
|
|
1122
420
|
return newElement
|
|
1123
421
|
}
|
|
1124
422
|
if (!this.parent) throw new Error("Can't insert element to element without parent")
|
|
1125
423
|
if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
|
|
1126
424
|
else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
|
|
1127
425
|
newElement.parent=this.parent
|
|
1128
426
|
return newElement
|
|
1129
427
|
}
|
|
1130
428
|
insertAdjacentHTML(position,html){
|
|
1131
429
|
const newNode=parseHTML(html);
|
|
1132
430
|
newNode.childNodes.forEach(node=>{
|
|
1133
431
|
this.insertAdjacentElement(position,node);
|
|
1134
432
|
});
|
|
1135
433
|
return newNode
|
|
1136
434
|
}
|
|
1137
435
|
insertAdjacentText(position,text){
|
|
1138
436
|
return this.insertAdjacentElement(position,new TextNode(text));
|
|
1139
437
|
}
|
|
1140
438
|
insert(position,element){
|
|
1141
439
|
const positions=['beforebegin','afterbegin','beforeend','afterend']
|
|
1142
440
|
if (positions[position]) position=positions[position]
|
|
1143
441
|
if (typeof element==='string'){
|
|
1144
442
|
element=element.trim()
|
|
1145
443
|
if (element.startsWith('<') && element.endsWith('>')){
|
|
1146
444
|
return this.insertAdjacentHTML(position,element)
|
|
1147
445
|
}
|
|
1148
446
|
return this.insertAdjacentText(position,element)
|
|
1149
447
|
}
|
|
1150
448
|
return this.insertAdjacentElement(position,element)
|
|
1151
449
|
}
|
|
1152
450
|
set innerHTML(html){
|
|
1153
451
|
this.childNodes=html.trim().startsWith('<') ? parseHTML(html).childNodes : [html]
|
|
1154
452
|
this.children.forEach(child=>child.parent=this);
|
|
1155
453
|
}
|
|
1156
454
|
set outerHTML(html){
|
|
1157
455
|
const parsed=parseHTML(html);
|
|
1158
456
|
if (!this.parent) throw new Error('element has no parent node')
|
|
1159
457
|
const index=this.childIndex
|
|
1160
458
|
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
1161
459
|
}
|
|
1162
460
|
appendChild(newChild){
|
|
1163
461
|
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
1164
462
|
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
1165
463
|
} else if (typeof newChild==='string') newChild=new TextNode(newChild)
|
|
1166
464
|
else return newChild
|
|
1167
465
|
this.childNodes.push(newChild);
|
|
1168
466
|
newChild.parent=this;
|
|
1169
467
|
return newChild;
|
|
1170
468
|
}
|
|
1171
469
|
get textContent(){
|
|
1172
470
|
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
1173
471
|
return this.childNodes.map(child=>{
|
|
1174
472
|
if (child instanceof SingleNode) return ''
|
|
1175
473
|
if (child instanceof TextNode) return child.nodeValue
|
|
1176
474
|
if (child instanceof Node) return child.textContent;
|
|
1177
475
|
else return child;
|
|
1178
476
|
}).join('');
|
|
1179
477
|
}
|
|
1180
478
|
set textContent(value){
|
|
1181
479
|
this.childNodes=[];
|
|
1182
480
|
if (value!==null && value!==undefined){
|
|
1183
481
|
this.childNodes.push(value.toString());
|
|
1184
482
|
}
|
|
1185
483
|
}
|
|
484
|
+
}
|
|
485
|
+
class SingleNode extends Node{
|
|
1186
486
|
constructor(tagName,attributes={},parent=null){
|
|
1187
487
|
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
1188
488
|
super(tagName,attributes,parent);
|
|
1189
489
|
this.isSingle=true
|
|
1190
490
|
}
|
|
1191
491
|
get outerHTML(){
|
|
1192
492
|
if (this._tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
|
|
1193
493
|
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
1194
494
|
return `<${this._tagName} ${attrs}${this._tagName==='?xml' ? '?' : ''}>`;
|
|
1195
495
|
}
|
|
1196
496
|
get innerHTML(){ return ""; }
|
|
1197
497
|
set innerHTML(_){ }
|
|
1198
498
|
$(_){return null}
|
|
1199
499
|
$$(_){return []}
|
|
1200
500
|
querySelectorAll(_){ return []; }
|
|
1201
501
|
querySelector(_){ return null; }
|
|
1202
502
|
getElementsByClassName(_){ return []; }
|
|
1203
503
|
getElementsByTagName(_){ return []; }
|
|
1204
504
|
getElementById(_){ return null; }
|
|
1205
505
|
get children(){ return []; }
|
|
1206
506
|
insertAdjacentElement(position,newElement){
|
|
1207
507
|
if(position==='afterbegin') position='beforebegin'
|
|
1208
508
|
if(position==='beforeend') position='afterend'
|
|
1209
509
|
return super.insertAdjacentElement(position,newElement)
|
|
1210
510
|
}
|
|
1211
511
|
insertAdjacentHTML(position,html){
|
|
1212
512
|
if(position==='afterbegin') position='beforebegin'
|
|
1213
513
|
if(position==='beforeend') position='afterend'
|
|
1214
514
|
return super.insertAdjacentHTML(position,html)
|
|
1215
515
|
}
|
|
1216
516
|
insertAdjacentText(position,text){
|
|
1217
517
|
if(position==='afterbegin') position='beforebegin'
|
|
1218
518
|
if(position==='beforeend') position='afterend'
|
|
1219
519
|
return super.insertAdjacentText(position,text)
|
|
1220
520
|
}
|
|
1221
521
|
insert(position,element){
|
|
1222
522
|
if(position===1) position=0
|
|
1223
523
|
if(position===2) position=3
|
|
1224
524
|
return super.insert(position,element)
|
|
1225
525
|
}
|
|
1226
526
|
appendChild(_){ }
|
|
1227
527
|
get textContent(){ return ""; }
|
|
1228
528
|
set textContent(_){ }
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
class Root extends Node{
|
|
1229
532
|
constructor(){
|
|
1230
533
|
super('ROOT',{},null);
|
|
1231
534
|
this.isSingle=false
|
|
1232
535
|
}
|
|
536
|
+
}
|
|
537
|
+
class Document extends Node{
|
|
1233
538
|
constructor(html,url){
|
|
1234
539
|
super('ROOT',{},null);
|
|
1235
540
|
this.isSingle=false
|
|
1236
541
|
this.URL=url
|
|
1237
542
|
if(html instanceof Document) this.childNodes=[...buildFromCache(cacheDoc(html)).childNodes]
|
|
1238
543
|
else if(typeof html==='string') this.innerHTML=html
|
|
1239
544
|
else this.html
|
|
1240
545
|
}
|
|
1241
546
|
get documentElement(){return this.html}
|
|
1242
547
|
get html(){
|
|
1243
548
|
const html=this.$('html')
|
|
1244
549
|
if(html===null) this.innerHTML=/*html*/`<html lang="en"><head><meta charset="UTF-8"><title></title></head><body></body></html>`
|
|
1245
550
|
return this.$('html')
|
|
1246
551
|
}
|
|
1247
552
|
get innerHTML(){
|
|
1248
553
|
const inner=super.innerHTML
|
|
1249
554
|
return '<!DOCTYPE html>'+inner
|
|
1250
555
|
}
|
|
1251
556
|
set innerHTML(html){return super.innerHTML=html}
|
|
1252
557
|
get head(){
|
|
1253
558
|
const head=this.html.$('head')
|
|
1254
559
|
if(head===null) this.html.insert(1,'<head></head>')
|
|
1255
560
|
return this.$('head')
|
|
1256
561
|
}
|
|
1257
562
|
get body(){
|
|
1258
563
|
const body=this.html.$('body')
|
|
1259
564
|
if(body===null) this.head.insert(3,'<body></body>')
|
|
1260
565
|
return this.$('body')
|
|
1261
566
|
}
|
|
1262
567
|
get title(){
|
|
1263
568
|
const title=this.head.$('title')
|
|
1264
569
|
if(title===null) this.head.insert(1,'<title></title>')
|
|
1265
570
|
return this.$('title').innerText
|
|
1266
571
|
}
|
|
1267
572
|
get charset(){return this.$('meta[charset]')}
|
|
1268
573
|
set title(title){return this.head.$('title').innerText=title}
|
|
1269
574
|
get clone(){return new Document(this)}
|
|
575
|
+
}
|
|
576
|
+
function parseAttributes(str){
|
|
1270
577
|
const attrs={};
|
|
1271
578
|
let key="";
|
|
1272
579
|
let value="";
|
|
1273
580
|
let isKey=true;
|
|
1274
581
|
let quoteChar=null;
|
|
1275
582
|
for (let i=0; i< str.length; i++){
|
|
1276
583
|
const char=str[i];
|
|
1277
584
|
if (isKey && (char==='=' || char===' ')){
|
|
1278
585
|
if (char==='=') isKey=false;
|
|
1279
586
|
else if (key.trim()){
|
|
1280
587
|
attrs[key.trim()]=true;
|
|
1281
588
|
key="";
|
|
1282
589
|
}
|
|
1283
590
|
continue;
|
|
1284
591
|
}
|
|
1285
592
|
if (!quoteChar && (char==='"' || char==="'")){
|
|
1286
593
|
quoteChar=char;
|
|
1287
594
|
continue;
|
|
1288
595
|
} else if (quoteChar && char===quoteChar){
|
|
1289
596
|
quoteChar=null;
|
|
1290
597
|
attrs[key.trim()]=value.trim();
|
|
1291
598
|
key=""; value=""; isKey=true;
|
|
1292
599
|
continue;
|
|
1293
600
|
}
|
|
1294
601
|
if (isKey) key+=char;
|
|
1295
602
|
else value+=char;
|
|
1296
603
|
}
|
|
1297
604
|
if (key.trim() &&!value) attrs[key.trim()]='';
|
|
1298
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){
|
|
1299
609
|
const root=new Root();
|
|
1300
610
|
const stack=[root];
|
|
1301
611
|
let currentText="",i=0;
|
|
1302
612
|
let max=0
|
|
1303
613
|
function parseScript(){
|
|
1304
614
|
if (!html.startsWith("<script",i)) return false;
|
|
1305
615
|
const openTagEnd=html.indexOf(">",i);
|
|
1306
616
|
if (openTagEnd===-1) return false;
|
|
1307
617
|
const attributesString=html.substring(i+7,openTagEnd).trim();
|
|
1308
618
|
const attributes=parseAttributes(attributesString);
|
|
1309
619
|
let closeTagStart=html.indexOf("</script>",openTagEnd);
|
|
1310
620
|
if (closeTagStart===-1) return false;
|
|
1311
621
|
const content=html.substring(openTagEnd+1,closeTagStart);
|
|
1312
622
|
const scriptNode=new Node('script',attributes,stack[stack.length-1]);
|
|
1313
623
|
if(content.length>0) scriptNode.childNodes.push(content);
|
|
1314
624
|
i=closeTagStart+9;
|
|
1315
625
|
return true;
|
|
1316
626
|
}
|
|
1317
627
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
1318
628
|
if (!html.startsWith(startStr,i)) return false
|
|
1319
629
|
if(currentText.length) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
1320
630
|
const end=html.indexOf(endStr,i+n1);
|
|
1321
631
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
1322
632
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
1323
633
|
i=end+n2;
|
|
1324
634
|
return true
|
|
1325
635
|
}
|
|
1326
636
|
while (i< html.length){
|
|
1327
637
|
if(i>=max) max=i;
|
|
1328
638
|
else break;
|
|
1329
639
|
if (parseScript()) continue
|
|
1330
640
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
1331
641
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
1332
642
|
if (html.startsWith("<![CDATA[",i)){
|
|
1333
643
|
const end=html.indexOf("]]>",i+9);
|
|
1334
644
|
if (end===-1) break;
|
|
1335
645
|
const content=html.substring(i+9,end);
|
|
1336
646
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
1337
647
|
cdataNode.nodeValue=content;
|
|
1338
648
|
i=end+3;
|
|
1339
649
|
continue;
|
|
1340
650
|
}
|
|
1341
651
|
if (html.startsWith("<",i)){
|
|
1342
652
|
if (currentText && stack[stack.length-1]){
|
|
1343
653
|
const textNode=new TextNode(currentText)
|
|
1344
654
|
stack[stack.length-1].childNodes.push(textNode);
|
|
1345
655
|
textNode.parent=stack[stack.length-1]
|
|
1346
656
|
currentText="";
|
|
1347
657
|
}
|
|
1348
658
|
let tagEnd=i+1;
|
|
1349
659
|
let insideQuotes=false;
|
|
1350
660
|
let quoteChar=null;
|
|
1351
661
|
while (tagEnd< html.length){
|
|
1352
662
|
const char=html[tagEnd];
|
|
1353
663
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
1354
664
|
insideQuotes=true;
|
|
1355
665
|
quoteChar=char;
|
|
1356
666
|
} else if (insideQuotes && char===quoteChar){
|
|
1357
667
|
insideQuotes=false;
|
|
1358
668
|
quoteChar=null;
|
|
1359
669
|
}
|
|
1360
670
|
if (!insideQuotes && char==='>') break;
|
|
1361
671
|
tagEnd++;
|
|
1362
672
|
}
|
|
1363
673
|
const tagContent=html.substring(i+1,tagEnd);
|
|
1364
674
|
if (tagContent.startsWith("/")) stack.pop();
|
|
1365
675
|
else{
|
|
1366
676
|
let isSelfClosing=tagContent.endsWith('/');
|
|
1367
677
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
1368
678
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
1369
679
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
1370
680
|
const attributes=parseAttributes(attributesString);
|
|
1371
681
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
1372
682
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
1373
683
|
}
|
|
1374
684
|
i=tagEnd+1;
|
|
1375
685
|
} else{
|
|
1376
686
|
currentText+=html[i];
|
|
1377
687
|
i++;
|
|
1378
688
|
}
|
|
1379
689
|
}
|
|
1380
690
|
if (currentText.trim() && stack[stack.length-1]) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
1381
691
|
return root;
|
|
692
|
+
}
|
|
693
|
+
function buildFromCache(cached){
|
|
1382
694
|
function buildNode(cache,parent=null){
|
|
1383
695
|
if(typeof cache==='string') return parent.childNodes.push(cache)
|
|
1384
696
|
const{isSingle,tagName,attributes,childNodes,textContent}=cache
|
|
1385
697
|
if(textContent) return parent.childNodes.push(new TextNode(textContent))
|
|
1386
698
|
if(isSingle && parent) return parent.childNodes.push(new SingleNode(tagName,attributes))
|
|
1387
699
|
const newDoc=tagName==='ROOT' ? new Root() : new Node(tagName,attributes,parent)
|
|
1388
700
|
childNodes.forEach(childNode=>{
|
|
1389
701
|
buildNode(childNode,newDoc)
|
|
1390
702
|
});
|
|
1391
703
|
return newDoc
|
|
1392
704
|
}
|
|
1393
705
|
return buildNode(cached)
|
|
706
|
+
}
|
|
1394
707
|
|
|
708
|
+
function cacheDoc(doc){
|
|
1395
709
|
const props=['isSingle','tagName','attributes']
|
|
1396
710
|
function addToCache(element,cache={}){
|
|
1397
711
|
if(typeof element==='string') return element
|
|
1398
712
|
if(element.nodeName==='#text') return{textContent:element.textContent}
|
|
1399
713
|
props.forEach(prop=>{
|
|
1400
714
|
if(element[prop]){
|
|
1401
715
|
cache[prop]=typeof element[prop]==='object' ?{...element[prop]} : element[prop]
|
|
1402
716
|
}
|
|
1403
717
|
});
|
|
1404
718
|
if(!element.childNodes) return cache
|
|
1405
719
|
cache.childNodes=[]
|
|
1406
720
|
element.childNodes.forEach(childNode=>{
|
|
1407
721
|
cache.childNodes.push(addToCache(childNode))
|
|
1408
722
|
});
|
|
1409
723
|
return cache
|
|
1410
724
|
}
|
|
1411
725
|
return addToCache(doc)
|
|
726
|
+
}
|
|
1412
727
|
module.exports = { parseHTML, Node, Query, TextNode, SingleNode, buildFromCache, cacheDoc, Root, Document }
|