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