als-document 1.0.3-alpha → 1.0.4-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/document.js +33 -573
- package/index.js +31 -571
- package/index.mjs +31 -571
- package/package.json +1 -1
- package/src/node/node.js +25 -7
- package/src/parse/parser.js +3 -3
- package/src/query/check-element.js +1 -1
- package/tests/index.html +1 -1
package/index.js
CHANGED
|
@@ -1,573 +1,33 @@
|
|
|
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,value]=attrib.split(/[\~\|\^\$\*]?\=/)
|
|
101
|
-
let sign=attrib.replace(name,'').replace(value,'')
|
|
102
|
-
attrib={ query }
|
|
103
|
-
if (name) attrib.name=name
|
|
104
|
-
if (value) attrib.value=value.trim().replace(/^\"/,'').replace(/\"$/,'')
|
|
105
|
-
if (sign){
|
|
106
|
-
attrib.sign=sign
|
|
107
|
-
attrib.check=this.getAttribFn(sign).bind(attrib)
|
|
108
|
-
}
|
|
109
|
-
return attrib
|
|
110
|
-
});
|
|
111
|
-
return attribs
|
|
112
|
-
}
|
|
113
|
-
getAttribFn(sign){
|
|
114
|
-
if (sign=='=') return function (value){ return value===this.value }
|
|
115
|
-
if (sign=='*=') return function (value){ return value.includes(this.value) }
|
|
116
|
-
if (sign=='^=') return function (value){ return value.startsWith(this.value) }
|
|
117
|
-
if (sign=='$=') return function (value){ return value.endsWith(this.value) }
|
|
118
|
-
if (sign=='|=') return function (value){
|
|
119
|
-
return value.trim().split(' ').length==1
|
|
120
|
-
&& (value.startsWith(this.value) || value.startsWith(this.value+'-'))
|
|
121
|
-
? true : false
|
|
122
|
-
}
|
|
123
|
-
if (sign=='~=') return function (value){
|
|
124
|
-
return this.value.trim().split(' ').length==1 && value.includes(this.value) ? true : false
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
removeSpaces(selector){
|
|
128
|
-
selector=selector.replace(/\s{2}/g,' ')
|
|
129
|
-
selector=selector.replace(/\s?\^?\$?\|?\~?\*?\=\s*/g,(m)=>m.trim())
|
|
130
|
-
selector=selector.replace(/\s?(\+|\~|\>)\s?/g,(m)=>m.trim())
|
|
131
|
-
return selector
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
function checkElement(el,selector){
|
|
135
|
-
if(selector==undefined) return true
|
|
136
|
-
if(el==null) return false
|
|
137
|
-
let{tag,classList,attributes,id,prev,ancestors,parents,prevAny}=selector
|
|
138
|
-
if(typeof el==='string') return false
|
|
139
|
-
if(id!==undefined && el.id===null) return false
|
|
140
|
-
if(id && id!==el.id) return false
|
|
141
|
-
if(tag && el.tagName===undefined) return false
|
|
142
|
-
else if(tag && tag!==el.tagName) return false
|
|
143
|
-
const clas=el.attributes.class
|
|
144
|
-
if(classList!==undefined && (clas===undefined || clas==='')) return false
|
|
145
|
-
else if(classList!==undefined){
|
|
146
|
-
if(classList.every(e=>el.classList.contains(e))===false) return false
|
|
147
|
-
}
|
|
148
|
-
if(checkattributes(attributes,el)===false) return false
|
|
149
|
-
if(checkElement(el.prev,prev)===false) return false
|
|
150
|
-
if(checkAncestors(el.ancestors,ancestors)===false) return false
|
|
151
|
-
if(checkParents(el.ancestors,parents)===false) return false
|
|
152
|
-
if(el.parent){
|
|
153
|
-
if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
|
|
154
|
-
}
|
|
155
|
-
return true
|
|
156
|
-
}
|
|
157
|
-
function checkattributes(attributes=[],el){
|
|
158
|
-
let elattributes=el.attributes
|
|
159
|
-
let names=Object.keys(elattributes)
|
|
160
|
-
let passedTests=0
|
|
161
|
-
if(attributes) for(let i=0; i<attributes.length; i++){
|
|
162
|
-
let{name,value,check}=attributes[i]
|
|
163
|
-
if(name=='inner' && value!==undefined && check && el.inner){
|
|
164
|
-
if(check(el.inner)) passedTests++
|
|
165
|
-
}
|
|
166
|
-
if(!names.includes(name)) continue
|
|
167
|
-
else if(value==undefined) passedTests++
|
|
168
|
-
else if(value && elattributes[name]){
|
|
169
|
-
if(check(elattributes[name])==false) continue
|
|
170
|
-
else passedTests++
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
if(passedTests==attributes.length) return true
|
|
174
|
-
else return false
|
|
175
|
-
}
|
|
176
|
-
function checkPrevAny(children=[],index,prevAny){
|
|
177
|
-
let size=children.length
|
|
178
|
-
if((size==0 || index==0) && prevAny) return false
|
|
179
|
-
for(let i=index; i>=0; i--){
|
|
180
|
-
if(checkElement(children[i],prevAny)) return true
|
|
181
|
-
}
|
|
182
|
-
return false
|
|
183
|
-
}
|
|
184
|
-
function checkAncestors(ancestors=[],selectorAncestors=[]){
|
|
185
|
-
let count=0
|
|
186
|
-
if(selectorAncestors.length==0) return true
|
|
187
|
-
let endIndex=ancestors.length-1
|
|
188
|
-
let selectorIndex=selectorAncestors.length-1
|
|
189
|
-
while(selectorIndex>=0){
|
|
190
|
-
for(let i=endIndex; i>=0; i--){
|
|
191
|
-
endIndex=i-1
|
|
192
|
-
if(checkElement(ancestors[i],selectorAncestors[selectorIndex])==true){
|
|
193
|
-
count++
|
|
194
|
-
break
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
selectorIndex--
|
|
198
|
-
}
|
|
199
|
-
if(count==selectorAncestors.length) return true
|
|
200
|
-
else return false
|
|
201
|
-
}
|
|
202
|
-
function checkParents(ancestors=[],selectorParents=[]){
|
|
203
|
-
if(selectorParents.length===0) return true
|
|
204
|
-
if(ancestors.length< selectorParents.length) return false
|
|
205
|
-
let index=ancestors.length-1
|
|
206
|
-
for(let i=selectorParents.length-1; i>=0; i--){
|
|
207
|
-
if(checkElement(ancestors[index],selectorParents[i])===false) return false
|
|
208
|
-
index--
|
|
209
|
-
}
|
|
210
|
-
return true
|
|
211
|
-
}
|
|
1
|
+
class Query{
|
|
212
2
|
static get(query){
|
|
213
3
|
let q=new Query(query)
|
|
214
4
|
return q.selectors
|
|
215
5
|
}
|
|
216
6
|
constructor(query){
|
|
217
7
|
this.query=query
|
|
218
8
|
this.selectors=[]
|
|
219
9
|
this.stringValues=[];
|
|
220
10
|
this.parseSelectors(query.split(','))
|
|
221
11
|
}
|
|
222
12
|
parseSelectors(selectors){
|
|
223
13
|
selectors.forEach(selector=>{
|
|
224
14
|
let originalSelector=selector.trim()
|
|
225
15
|
selector=this.removeSpaces(selector)
|
|
226
16
|
this.stringValues=[]
|
|
227
17
|
selector=selector.replace(/\[.*?\]/g,(value)=>{
|
|
228
18
|
this.stringValues.push(value)
|
|
229
19
|
return `[${this.stringValues.length-1}]`
|
|
230
20
|
})
|
|
231
21
|
let [element,ancestors]=this.splitAndCutLast(selector,' ')
|
|
232
22
|
element=this.getFamily(element)
|
|
233
23
|
if (ancestors.length>0)
|
|
234
24
|
element.ancestors=ancestors.map(ancestor=>this.getFamily(ancestor))
|
|
235
25
|
element.group=originalSelector
|
|
236
26
|
this.selectors.push(element)
|
|
237
27
|
});
|
|
238
28
|
}
|
|
239
29
|
splitAndCutLast(string,splitBy){
|
|
240
30
|
const array=string.split(splitBy);
|
|
241
31
|
const last=array.pop();
|
|
242
32
|
return [last,array];
|
|
243
33
|
}
|
|
244
34
|
getFamily(group,element,prev,prevAny,sign){
|
|
245
35
|
if (group.match(/\~|\+/)!==null){
|
|
246
36
|
let [last,prevBrothers]=this.splitAndCutLast(group,/\~|\+/)
|
|
247
37
|
let signs=group.replace(last,'')
|
|
248
38
|
prevBrothers.forEach(el=>signs=signs.replace(el,''))
|
|
249
39
|
signs=signs.match(/\~|\+/g)
|
|
250
40
|
if (signs.length==1){
|
|
251
41
|
sign=signs[0]
|
|
252
42
|
} else if (signs.length>1){
|
|
253
43
|
sign=signs.splice(signs.length-1,signs.length-1)[0]
|
|
254
44
|
prevBrothers[0]=prevBrothers.map((b,i)=>{
|
|
255
45
|
if (i< prevBrothers.length-1) b+=signs[i]
|
|
256
46
|
return b
|
|
257
47
|
}).join('')
|
|
258
48
|
prevBrothers[0]=this.getFamily(prevBrothers[0])
|
|
259
49
|
}
|
|
260
50
|
if (sign=='~') prevAny=prevBrothers[0]
|
|
261
51
|
else if (sign=='+') prev=prevBrothers[0]
|
|
262
52
|
element=last
|
|
263
53
|
} else element=group
|
|
264
54
|
let family
|
|
265
55
|
if (prev || prevAny){
|
|
266
56
|
family=this.getParents(element)
|
|
267
57
|
if (prev) family.prev=this.getParents(prev)
|
|
268
58
|
if (prevAny) family.prevAny=this.getParents(prevAny)
|
|
269
59
|
} else family=this.getParents(element)
|
|
270
60
|
if (family.query!==group) family.group=group
|
|
271
61
|
return family
|
|
272
62
|
}
|
|
273
63
|
getParents(selector){
|
|
274
64
|
if (typeof selector=='string'){
|
|
275
65
|
let [element,parents]=this.splitAndCutLast(selector,'>')
|
|
276
66
|
element=this.buildElement(element)
|
|
277
67
|
parents=parents.map(parent=>this.buildElement(parent))
|
|
278
68
|
if (parents.length>0) element.parents=parents
|
|
279
69
|
return element
|
|
280
70
|
} else return selector
|
|
281
71
|
}
|
|
282
72
|
buildElement(element,id=null,tag=null,classList=[]){
|
|
283
73
|
let query=element
|
|
284
74
|
element=element.replace(/\#(\w-?)*/,$id=>{
|
|
285
75
|
id=$id.replace(/^\#/,''); return ''
|
|
286
76
|
})
|
|
287
77
|
element=element.replace(/\.(\w-?)*/,$class=>{
|
|
288
78
|
classList.push($class.replace(/^\./,'')); return ''
|
|
289
79
|
})
|
|
290
80
|
element=element.replace(/(\w\:?-?)*/,$tag=>{
|
|
291
81
|
tag=$tag=='' ? null : $tag; return ''
|
|
292
82
|
})
|
|
293
83
|
let attribs=this.getAttributes(element)
|
|
294
84
|
element={ query }
|
|
295
85
|
if (id) element.id=id
|
|
296
86
|
if (tag) element.tag=tag
|
|
297
87
|
if (classList.length>0) element.classList=classList
|
|
298
88
|
if (attribs.length>0) element.attribs=attribs
|
|
299
89
|
return element
|
|
300
90
|
}
|
|
301
91
|
getAttributes(element){
|
|
302
92
|
let attribs=this.stringValues.filter((value,index)=>{
|
|
303
93
|
let searchValue=`[${index}]`
|
|
304
94
|
if (element.match(searchValue)) return true
|
|
305
95
|
else return false
|
|
306
96
|
})
|
|
307
97
|
attribs=attribs.map(attrib=>{
|
|
308
98
|
let query=attrib
|
|
309
99
|
attrib=attrib.replace('[','').replace(']','')
|
|
310
100
|
let [name,value]=attrib.split(/[\~\|\^\$\*]?\=/)
|
|
311
101
|
let sign=attrib.replace(name,'').replace(value,'')
|
|
312
102
|
attrib={ query }
|
|
313
103
|
if (name) attrib.name=name
|
|
314
104
|
if (value) attrib.value=value.trim().replace(/^\"/,'').replace(/\"$/,'')
|
|
315
105
|
if (sign){
|
|
316
106
|
attrib.sign=sign
|
|
317
107
|
attrib.check=this.getAttribFn(sign).bind(attrib)
|
|
318
108
|
}
|
|
319
109
|
return attrib
|
|
320
110
|
});
|
|
321
111
|
return attribs
|
|
322
112
|
}
|
|
323
113
|
getAttribFn(sign){
|
|
324
114
|
if (sign=='=') return function (value){ return value===this.value }
|
|
325
115
|
if (sign=='*=') return function (value){ return value.includes(this.value) }
|
|
326
116
|
if (sign=='^=') return function (value){ return value.startsWith(this.value) }
|
|
327
117
|
if (sign=='$=') return function (value){ return value.endsWith(this.value) }
|
|
328
118
|
if (sign=='|=') return function (value){
|
|
329
119
|
return value.trim().split(' ').length==1
|
|
330
120
|
&& (value.startsWith(this.value) || value.startsWith(this.value+'-'))
|
|
331
121
|
? true : false
|
|
332
122
|
}
|
|
333
123
|
if (sign=='~=') return function (value){
|
|
334
124
|
return this.value.trim().split(' ').length==1 && value.includes(this.value) ? true : false
|
|
335
125
|
}
|
|
336
126
|
}
|
|
337
127
|
removeSpaces(selector){
|
|
338
128
|
selector=selector.replace(/\s{2}/g,' ')
|
|
339
129
|
selector=selector.replace(/\s?\^?\$?\|?\~?\*?\=\s*/g,(m)=>m.trim())
|
|
340
130
|
selector=selector.replace(/\s?(\+|\~|\>)\s?/g,(m)=>m.trim())
|
|
341
131
|
return selector
|
|
342
132
|
}
|
|
133
|
+
}
|
|
134
|
+
function checkElement(el,selector){
|
|
343
135
|
if(selector==undefined) return true
|
|
344
136
|
if(el==null) return false
|
|
345
137
|
let{tag,classList,attribs:attributes,id,prev,ancestors,parents,prevAny}=selector
|
|
346
138
|
if(typeof el==='string') return false
|
|
347
139
|
if(id!==undefined && el.id===null) return false
|
|
348
140
|
if(id && id!==el.id) return false
|
|
349
141
|
if(tag && el.tagName===undefined) return false
|
|
350
142
|
else if(tag && tag!==el.tagName) return false
|
|
351
143
|
const clas=el.attributes.class
|
|
352
144
|
if(classList!==undefined && (clas===undefined || clas==='')) return false
|
|
353
145
|
else if(classList!==undefined){
|
|
354
146
|
if(classList.every(e=>el.classList.contains(e))===false) return false
|
|
355
147
|
}
|
|
356
148
|
if(checkattributes(attributes,el)===false) return false
|
|
357
149
|
if(checkElement(el.prev,prev)===false) return false
|
|
358
150
|
if(checkAncestors(el.ancestors,ancestors)===false) return false
|
|
359
151
|
if(checkParents(el.ancestors,parents)===false) return false
|
|
360
152
|
if(el.parent){
|
|
361
153
|
if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
|
|
362
154
|
}
|
|
363
155
|
return true
|
|
156
|
+
}
|
|
364
157
|
function checkattributes(attributes=[],el){
|
|
365
158
|
let elattributes=el.attributes
|
|
366
159
|
let names=Object.keys(elattributes)
|
|
367
160
|
let passedTests=0
|
|
368
161
|
if(attributes) for(let i=0; i<attributes.length; i++){
|
|
369
162
|
let{name,value,check}=attributes[i]
|
|
370
163
|
if(name=='inner' && value!==undefined && check && el.inner){
|
|
371
164
|
if(check(el.inner)) passedTests++
|
|
372
165
|
}
|
|
373
166
|
if(!names.includes(name)) continue
|
|
374
167
|
else if(value==undefined) passedTests++
|
|
375
168
|
else if(value && elattributes[name]){
|
|
376
169
|
if(check(elattributes[name])==false) continue
|
|
377
170
|
else passedTests++
|
|
378
171
|
}
|
|
379
172
|
}
|
|
380
173
|
if(passedTests==attributes.length) return true
|
|
381
174
|
else return false
|
|
175
|
+
}
|
|
382
176
|
function checkPrevAny(children=[],index,prevAny){
|
|
383
177
|
let size=children.length
|
|
384
178
|
if((size==0 || index==0) && prevAny) return false
|
|
385
179
|
for(let i=index; i>=0; i--){
|
|
386
180
|
if(checkElement(children[i],prevAny)) return true
|
|
387
181
|
}
|
|
388
182
|
return false
|
|
183
|
+
}
|
|
389
184
|
function checkAncestors(ancestors=[],selectorAncestors=[]){
|
|
390
185
|
let count=0
|
|
391
186
|
if(selectorAncestors.length==0) return true
|
|
392
187
|
let endIndex=ancestors.length-1
|
|
393
188
|
let selectorIndex=selectorAncestors.length-1
|
|
394
189
|
while(selectorIndex>=0){
|
|
395
190
|
for(let i=endIndex; i>=0; i--){
|
|
396
191
|
endIndex=i-1
|
|
397
192
|
if(checkElement(ancestors[i],selectorAncestors[selectorIndex])==true){
|
|
398
193
|
count++
|
|
399
194
|
break
|
|
400
195
|
}
|
|
401
196
|
}
|
|
402
197
|
selectorIndex--
|
|
403
198
|
}
|
|
404
199
|
if(count==selectorAncestors.length) return true
|
|
405
200
|
else return false
|
|
201
|
+
}
|
|
406
202
|
function checkParents(ancestors=[],selectorParents=[]){
|
|
407
203
|
if(selectorParents.length===0) return true
|
|
408
204
|
if(ancestors.length< selectorParents.length) return false
|
|
409
205
|
let index=ancestors.length-1
|
|
410
206
|
for(let i=selectorParents.length-1; i>=0; i--){
|
|
411
207
|
if(checkElement(ancestors[index],selectorParents[i])===false) return false
|
|
412
208
|
index--
|
|
413
209
|
}
|
|
414
210
|
return true
|
|
211
|
+
}
|
|
415
212
|
const getDataName=prop=>'data-'+prop.toLowerCase()
|
|
416
|
-
function getDataset(element){
|
|
417
|
-
return new Proxy(element.attributes,{
|
|
418
|
-
get: (target,prop)=>{return target[getDataName(prop)]},set: (target,prop,value)=>{target[getDataName(prop)]=value; return true},deleteProperty: (target,prop)=>{
|
|
419
|
-
const dataAttr=getDataName(prop)
|
|
420
|
-
if (dataAttr in target){
|
|
421
|
-
delete target[dataAttr];
|
|
422
|
-
return true;
|
|
423
|
-
}
|
|
424
|
-
return false;
|
|
425
|
-
}
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
function find(selectors,element,collection,first=false,firstTime=true){
|
|
430
|
-
for(let selector of selectors){
|
|
431
|
-
if(checkElement(element,selector)) collection.add(element)
|
|
432
|
-
}
|
|
433
|
-
if(element.children)
|
|
434
|
-
element.children.forEach(child=>{
|
|
435
|
-
if(first && collection.size>0) return
|
|
436
|
-
find(selectors,child,collection,first,false)
|
|
437
|
-
})
|
|
438
|
-
return firstTime ? [...collection] : collection
|
|
439
|
-
}
|
|
440
|
-
class TextNode{
|
|
441
|
-
constructor(data){
|
|
442
|
-
this.nodeName='#text';
|
|
443
|
-
this.parent=null;
|
|
444
|
-
this.textContent=data;
|
|
445
|
-
}
|
|
446
|
-
get nodeValue(){return this.textContent}
|
|
447
|
-
get parentNode(){return this.parent}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
function buildStyle(attributes){
|
|
451
|
-
const styles=attributes.style || "";
|
|
452
|
-
const camelToKebab=str=>str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,'$1-$2').toLowerCase();
|
|
453
|
-
const kebabToCamel=str=>str.replace(/-([a-z])/g,g=>g[1].toUpperCase());
|
|
454
|
-
const baseStyleObj=styles.split(";").reduce((acc,style)=>{
|
|
455
|
-
const [key,value]=style.split(":").map(s=>s.trim());
|
|
456
|
-
if (key && value) acc[kebabToCamel(key)]=value;
|
|
457
|
-
return acc;
|
|
458
|
-
},{});
|
|
459
|
-
return new Proxy(baseStyleObj,{
|
|
460
|
-
get: (obj,prop)=>obj[camelToKebab(prop)] || obj[prop],set: (obj,prop,value)=>{
|
|
461
|
-
obj[camelToKebab(prop)]=value;
|
|
462
|
-
attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
|
|
463
|
-
return true;
|
|
464
|
-
},deleteProperty: (obj,prop)=>{
|
|
465
|
-
delete obj[camelToKebab(prop)];
|
|
466
|
-
attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
|
|
467
|
-
return true;
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
class NodeClassList{
|
|
473
|
-
constructor(node){ this.node=node }
|
|
474
|
-
get classes(){ return (this.node.attributes.class || "").split(" ").filter(Boolean) }
|
|
475
|
-
set classes(val){ this.node.attributes.class=val.join(" ") }
|
|
476
|
-
contains(className){ return this.classes.includes(className) }
|
|
477
|
-
add(className){
|
|
478
|
-
const currentClasses=this.classes;
|
|
479
|
-
if (!currentClasses.includes(className)) this.classes=[...currentClasses,className];
|
|
480
|
-
}
|
|
481
|
-
remove(className){ this.classes=this.classes.filter(cls=>cls!==className); }
|
|
482
|
-
toggle(className){
|
|
483
|
-
if (this.classes.includes(className)) this.remove(className);
|
|
484
|
-
else this.add(className);
|
|
485
|
-
}
|
|
486
|
-
replace(oldClass,newClass){
|
|
487
|
-
if (this.classes.includes(oldClass)){
|
|
488
|
-
this.remove(oldClass);
|
|
489
|
-
this.add(newClass);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
class Node{
|
|
494
|
-
constructor(tagName,attributes={},parent=null){
|
|
495
|
-
this.isSingle=false;
|
|
496
|
-
this.tagName=tagName;
|
|
497
|
-
this.attributes=attributes;
|
|
498
|
-
this.childNodes=[];
|
|
499
|
-
if (parent!==null) parent.childNodes.push(this)
|
|
500
|
-
this.parent=parent;
|
|
501
|
-
this._classList=null;
|
|
502
|
-
this.__style=null;
|
|
503
|
-
this._dataset=null
|
|
504
|
-
}
|
|
505
|
-
get id(){ return this.attributes.id || null; }
|
|
506
|
-
get className(){return this.attributes.class || null}
|
|
507
|
-
get parentNode(){ return this.parent }
|
|
508
|
-
get ancestors(){
|
|
509
|
-
const ancestors=[]
|
|
510
|
-
let element=this.parent
|
|
511
|
-
while (element.tagName!=='ROOT'){
|
|
512
|
-
ancestors.push(element)
|
|
513
|
-
element=element.parent
|
|
514
|
-
}
|
|
515
|
-
return ancestors.reverse()
|
|
516
|
-
}
|
|
517
|
-
get childIndex(){ return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null }
|
|
518
|
-
get previousElementSibling(){ return this.prev }
|
|
519
|
-
get prev(){
|
|
520
|
-
if (!this.childIndex) return null
|
|
521
|
-
return this.parent.childNodes[this.childIndex-1]
|
|
522
|
-
}
|
|
523
|
-
get nextElementSibling(){ return this.next }
|
|
524
|
-
get next(){
|
|
525
|
-
if (!this.childIndex) return null
|
|
526
|
-
return this.parent.childNodes[this.childIndex+1] || null
|
|
527
|
-
}
|
|
528
|
-
get dataset(){
|
|
529
|
-
if (!this._dataset) this._dataset=getDataset(this);
|
|
530
|
-
return this._dataset;
|
|
531
|
-
}
|
|
532
|
-
get classList(){
|
|
533
|
-
if (!this._classList) this._classList=new NodeClassList(this);
|
|
534
|
-
return this._classList;
|
|
535
|
-
}
|
|
536
|
-
get style(){
|
|
537
|
-
if (!this.__style) this.__style=buildStyle(this.attributes)
|
|
538
|
-
return this.__style
|
|
539
|
-
}
|
|
540
|
-
get outerHTML(){
|
|
541
|
-
const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
|
|
542
|
-
return `<${this.tagName} ${attrs}>${this.innerHTML}</${this.tagName}>`;
|
|
543
|
-
}
|
|
544
|
-
getAttribute(attrName){ return this.attributes[attrName] || null }
|
|
545
|
-
setAttribute(attrName,value){ this.attributes[attrName]=value }
|
|
546
|
-
removeAttribute(attrName){ delete this.attributes[attrName] }
|
|
547
|
-
remove(){
|
|
548
|
-
if (!this.parent) return
|
|
549
|
-
const index=this.childIndex;
|
|
550
|
-
if (index!==null) this.parent.childNodes.splice(index,1);
|
|
551
|
-
}
|
|
552
|
-
get innerHTML(){
|
|
553
|
-
return this.childNodes.map(child=>{
|
|
554
|
-
if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
|
|
555
|
-
else if (child instanceof TextNode) return child.textContent;
|
|
556
|
-
else return child
|
|
557
|
-
}).join("");
|
|
558
|
-
}
|
|
559
|
-
$$(query){return this.querySelectorAll(query)}
|
|
560
|
-
querySelectorAll(query){
|
|
561
|
-
const selectors=Query.get(query)
|
|
562
|
-
return find(selectors,this,new Set())
|
|
563
|
-
}
|
|
564
|
-
$(query){return this.querySelector(query)}
|
|
565
|
-
querySelector(query){
|
|
566
|
-
const selectors=Query.get(query)
|
|
567
|
-
return find(selectors,this,new Set(),true)[0] || null
|
|
568
|
-
}
|
|
569
|
-
getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
|
|
570
|
-
getElementsByTagName(query){ return this.querySelectorAll(query) }
|
|
571
|
-
getElementById(query){ return this.querySelector('#'+query) }
|
|
572
|
-
get children(){
|
|
573
|
-
return this.childNodes.filter(child=>{
|
|
574
|
-
if (!(child instanceof Node)) return false
|
|
575
|
-
if (child.tagName==='#comment') return false
|
|
576
|
-
return true
|
|
577
|
-
});
|
|
578
|
-
}
|
|
579
|
-
insertAdjacentElement(position,newElement){
|
|
580
|
-
if(newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
|
|
581
|
-
const pos=position.toLowerCase();
|
|
582
|
-
if (pos==="afterbegin") this.childNodes.unshift(newElement);
|
|
583
|
-
else if (pos==="beforeend") this.childNodes.push(newElement);
|
|
584
|
-
if (!this.parent) return newElement
|
|
585
|
-
if (pos==="beforebegin") this.parent.childNodes.unshift(newElement);
|
|
586
|
-
else if (pos==="afterend") this.parent.childNodes.splice(this.childIndex+1,0,newElement);
|
|
587
|
-
return newElement
|
|
588
|
-
}
|
|
589
|
-
insertAdjacentHTML(position,html){
|
|
590
|
-
const newNode=parseHTML(html);
|
|
591
|
-
newNode.childNodes.reverse().forEach(node=>{
|
|
592
|
-
this.insertAdjacentElement(position,node);
|
|
593
|
-
});
|
|
594
|
-
return newNode
|
|
595
|
-
}
|
|
596
|
-
insertAdjacentText(position,text){
|
|
597
|
-
return this.insertAdjacentElement(position,new TextNode(text));
|
|
598
|
-
}
|
|
599
|
-
set innerHTML(html){
|
|
600
|
-
const parsed=parseHTML(html);
|
|
601
|
-
this.childNodes=parsed.childNodes;
|
|
602
|
-
}
|
|
603
|
-
set outerHTML(html){
|
|
604
|
-
const parsed=parseHTML(html);
|
|
605
|
-
if (!this.parent) return console.log('element has no parent node')
|
|
606
|
-
const index=this.childIndex
|
|
607
|
-
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
608
|
-
}
|
|
609
|
-
appendChild(newChild){
|
|
610
|
-
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
611
|
-
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
612
|
-
} else if(typeof newChild==='string') newChild=new TextNode(newChild)
|
|
613
|
-
else return newChild
|
|
614
|
-
this.childNodes.push(newChild);
|
|
615
|
-
newChild.parent=this;
|
|
616
|
-
return newChild;
|
|
617
|
-
}
|
|
618
|
-
get textContent(){
|
|
619
|
-
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
620
|
-
return this.childNodes.map(child=>{
|
|
621
|
-
if(child instanceof SingleNode) return ''
|
|
622
|
-
if(child instanceof TextNode) return child.nodeValue
|
|
623
|
-
if(child instanceof Node) return child.textContent;
|
|
624
|
-
else return child;
|
|
625
|
-
}).join(" ");
|
|
626
|
-
}
|
|
627
|
-
set textContent(value){
|
|
628
|
-
this.childNodes=[];
|
|
629
|
-
if (value!==null && value!==undefined){
|
|
630
|
-
this.childNodes.push(value.toString());
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
class SingleNode extends Node{
|
|
635
|
-
constructor(tagName,attributes={},parent=null){
|
|
636
|
-
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
637
|
-
super(tagName,attributes,parent);
|
|
638
|
-
this.isSingle=true
|
|
639
|
-
}
|
|
640
|
-
get outerHTML(){
|
|
641
|
-
if (this.tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
|
|
642
|
-
const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
|
|
643
|
-
return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
|
|
644
|
-
}
|
|
645
|
-
get innerHTML(){ return ""; }
|
|
646
|
-
set innerHTML(_){ }
|
|
647
|
-
$(_){return null}
|
|
648
|
-
$$(_){return []}
|
|
649
|
-
querySelectorAll(_){ return []; }
|
|
650
|
-
querySelector(_){ return null; }
|
|
651
|
-
getElementsByClassName(_){ return []; }
|
|
652
|
-
getElementsByTagName(_){ return []; }
|
|
653
|
-
getElementById(_){ return null; }
|
|
654
|
-
get children(){ return []; }
|
|
655
|
-
insertAdjacentElement(_,__){ }
|
|
656
|
-
insertAdjacentHTML(_,__){ }
|
|
657
|
-
insertAdjacentText(_,__){ }
|
|
658
|
-
appendChild(_){ }
|
|
659
|
-
get textContent(){ return ""; }
|
|
660
|
-
set textContent(_){ }
|
|
661
|
-
}
|
|
662
|
-
function parseAttributes(str){
|
|
663
|
-
const attrs={};
|
|
664
|
-
let key="";
|
|
665
|
-
let value="";
|
|
666
|
-
let isKey=true;
|
|
667
|
-
let quoteChar=null;
|
|
668
|
-
for (let i=0; i< str.length; i++){
|
|
669
|
-
const char=str[i];
|
|
670
|
-
if (isKey && (char==='=' || char===' ')){
|
|
671
|
-
if (char==='=') isKey=false;
|
|
672
|
-
else if (key.trim()){
|
|
673
|
-
attrs[key.trim()]=true;
|
|
674
|
-
key="";
|
|
675
|
-
}
|
|
676
|
-
continue;
|
|
677
|
-
}
|
|
678
|
-
if (!quoteChar && (char==='"' || char==="'")){
|
|
679
|
-
quoteChar=char;
|
|
680
|
-
continue;
|
|
681
|
-
} else if (quoteChar && char===quoteChar){
|
|
682
|
-
quoteChar=null;
|
|
683
|
-
attrs[key.trim()]=value.trim();
|
|
684
|
-
key=""; value=""; isKey=true;
|
|
685
|
-
continue;
|
|
686
|
-
}
|
|
687
|
-
if (isKey) key+=char;
|
|
688
|
-
else value+=char;
|
|
689
|
-
}
|
|
690
|
-
if (key.trim() &&!value) attrs[key.trim()]=true;
|
|
691
|
-
return attrs;
|
|
692
|
-
}
|
|
693
|
-
const VOID_TAGS=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","!doctype",'?xml']);
|
|
694
|
-
function parseHTML(html){
|
|
695
|
-
const root=new Node("ROOT");
|
|
696
|
-
const stack=[root];
|
|
697
|
-
let currentText="",i=0;
|
|
698
|
-
let max=0
|
|
699
|
-
function parseScript(){
|
|
700
|
-
if (!html.startsWith("<script",i)) return false;
|
|
701
|
-
const openTagEnd=html.indexOf(">",i);
|
|
702
|
-
if (openTagEnd===-1) return false;
|
|
703
|
-
const attributesString=html.substring(i+7,openTagEnd).trim();
|
|
704
|
-
const attributes=parseAttributes(attributesString);
|
|
705
|
-
let closeTagStart=html.indexOf("</script>",openTagEnd);
|
|
706
|
-
if (closeTagStart===-1) return false;
|
|
707
|
-
const content=html.substring(openTagEnd+1,closeTagStart);
|
|
708
|
-
const scriptNode=new Node('script',attributes,stack[stack.length-1]);
|
|
709
|
-
if(content.length>0) scriptNode.childNodes.push(content);
|
|
710
|
-
i=closeTagStart+9;
|
|
711
|
-
return true;
|
|
712
|
-
}
|
|
713
|
-
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
714
|
-
if (!html.startsWith(startStr,i)) return false
|
|
715
|
-
const end=html.indexOf(endStr,i+n1);
|
|
716
|
-
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
717
|
-
strNode.childNodes.push(html.substring(i+n1,end));
|
|
718
|
-
i=end+n2;
|
|
719
|
-
return true
|
|
720
|
-
}
|
|
721
|
-
while (i< html.length){
|
|
722
|
-
if(i>=max) max=i;
|
|
723
|
-
else break;
|
|
724
|
-
if (parseScript()) continue
|
|
725
|
-
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
726
|
-
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
727
|
-
if (html.startsWith("<![CDATA[",i)){
|
|
728
|
-
const end=html.indexOf("]]>",i+9);
|
|
729
|
-
if (end===-1) break;
|
|
730
|
-
const content=html.substring(i+9,end);
|
|
731
|
-
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
732
|
-
cdataNode.nodeValue=content;
|
|
733
|
-
i=end+3;
|
|
734
|
-
continue;
|
|
735
|
-
}
|
|
736
|
-
if (html.startsWith("<",i)){
|
|
737
|
-
if (currentText.trim()){
|
|
738
|
-
stack[stack.length-1].childNodes.push(new TextNode(currentText.trim()));
|
|
739
|
-
currentText="";
|
|
740
|
-
}
|
|
741
|
-
let tagEnd=i+1;
|
|
742
|
-
let insideQuotes=false;
|
|
743
|
-
let quoteChar=null;
|
|
744
|
-
while (tagEnd< html.length){
|
|
745
|
-
const char=html[tagEnd];
|
|
746
|
-
if (!insideQuotes && (char==='"' || char==="'")){
|
|
747
|
-
insideQuotes=true;
|
|
748
|
-
quoteChar=char;
|
|
749
|
-
} else if (insideQuotes && char===quoteChar){
|
|
750
|
-
insideQuotes=false;
|
|
751
|
-
quoteChar=null;
|
|
752
|
-
}
|
|
753
|
-
if (!insideQuotes && char==='>') break;
|
|
754
|
-
tagEnd++;
|
|
755
|
-
}
|
|
756
|
-
const tagContent=html.substring(i+1,tagEnd);
|
|
757
|
-
if (tagContent.startsWith("/")) stack.pop();
|
|
758
|
-
else{
|
|
759
|
-
let isSelfClosing=tagContent.endsWith('/');
|
|
760
|
-
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
761
|
-
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
762
|
-
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
763
|
-
const attributes=parseAttributes(attributesString);
|
|
764
|
-
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
765
|
-
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
766
|
-
}
|
|
767
|
-
i=tagEnd+1;
|
|
768
|
-
} else{
|
|
769
|
-
currentText+=html[i];
|
|
770
|
-
i++;
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
if (currentText.trim()) stack[stack.length-1].childNodes.push(new TextNode(currentText.trim()));
|
|
774
|
-
return root;
|
|
775
|
-
}
|
|
213
|
+
function getDataset(element){
|
|
776
214
|
return new Proxy(element.attributes,{
|
|
777
215
|
get: (target,prop)=>{return target[getDataName(prop)]},set: (target,prop,value)=>{target[getDataName(prop)]=value; return true},deleteProperty: (target,prop)=>{
|
|
778
216
|
const dataAttr=getDataName(prop)
|
|
779
217
|
if (dataAttr in target){
|
|
780
218
|
delete target[dataAttr];
|
|
781
219
|
return true;
|
|
782
220
|
}
|
|
783
221
|
return false;
|
|
784
222
|
}
|
|
785
223
|
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function find(selectors,element,collection,first=false,firstTime=true){
|
|
786
227
|
for(let selector of selectors){
|
|
787
228
|
if(checkElement(element,selector)) collection.add(element)
|
|
788
229
|
}
|
|
789
230
|
if(element.children)
|
|
790
231
|
element.children.forEach(child=>{
|
|
791
232
|
if(first && collection.size>0) return
|
|
792
233
|
find(selectors,child,collection,first,false)
|
|
793
234
|
})
|
|
794
235
|
return firstTime ? [...collection] : collection
|
|
236
|
+
}
|
|
237
|
+
class TextNode{
|
|
795
238
|
constructor(data){
|
|
796
239
|
this.nodeName='#text';
|
|
797
240
|
this.parent=null;
|
|
798
241
|
this.textContent=data;
|
|
799
242
|
}
|
|
800
243
|
get nodeValue(){return this.textContent}
|
|
801
244
|
get parentNode(){return this.parent}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function buildStyle(attributes){
|
|
802
248
|
const styles=attributes.style || "";
|
|
803
249
|
const camelToKebab=str=>str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,'$1-$2').toLowerCase();
|
|
804
250
|
const kebabToCamel=str=>str.replace(/-([a-z])/g,g=>g[1].toUpperCase());
|
|
805
251
|
const baseStyleObj=styles.split(";").reduce((acc,style)=>{
|
|
806
252
|
const [key,value]=style.split(":").map(s=>s.trim());
|
|
807
253
|
if (key && value) acc[kebabToCamel(key)]=value;
|
|
808
254
|
return acc;
|
|
809
255
|
},{});
|
|
810
256
|
return new Proxy(baseStyleObj,{
|
|
811
257
|
get: (obj,prop)=>obj[camelToKebab(prop)] || obj[prop],set: (obj,prop,value)=>{
|
|
812
258
|
obj[camelToKebab(prop)]=value;
|
|
813
259
|
attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
|
|
814
260
|
return true;
|
|
815
261
|
},deleteProperty: (obj,prop)=>{
|
|
816
262
|
delete obj[camelToKebab(prop)];
|
|
817
263
|
attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
|
|
818
264
|
return true;
|
|
819
265
|
}
|
|
820
266
|
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
class NodeClassList{
|
|
821
270
|
constructor(node){ this.node=node }
|
|
822
271
|
get classes(){ return (this.node.attributes.class || "").split(" ").filter(Boolean) }
|
|
823
272
|
set classes(val){ this.node.attributes.class=val.join(" ") }
|
|
824
273
|
contains(className){ return this.classes.includes(className) }
|
|
825
274
|
add(className){
|
|
826
275
|
const currentClasses=this.classes;
|
|
827
276
|
if (!currentClasses.includes(className)) this.classes=[...currentClasses,className];
|
|
828
277
|
}
|
|
829
278
|
remove(className){ this.classes=this.classes.filter(cls=>cls!==className); }
|
|
830
279
|
toggle(className){
|
|
831
280
|
if (this.classes.includes(className)) this.remove(className);
|
|
832
281
|
else this.add(className);
|
|
833
282
|
}
|
|
834
283
|
replace(oldClass,newClass){
|
|
835
284
|
if (this.classes.includes(oldClass)){
|
|
836
285
|
this.remove(oldClass);
|
|
837
286
|
this.add(newClass);
|
|
838
287
|
}
|
|
839
288
|
}
|
|
289
|
+
}
|
|
290
|
+
function insertBefore(arr,index,newItem){
|
|
840
291
|
const existingIndex=arr.indexOf(newItem);
|
|
841
292
|
if (existingIndex!==-1) arr.splice(existingIndex,1);
|
|
842
293
|
arr.splice(index,0,newItem);
|
|
294
|
+
}
|
|
843
295
|
class Node{
|
|
844
296
|
constructor(tagName,attributes={},parent=null){
|
|
845
297
|
this.isSingle=false;
|
|
846
298
|
this.tagName=tagName;
|
|
847
299
|
this.attributes=attributes;
|
|
848
300
|
this.childNodes=[];
|
|
849
301
|
if (parent!==null) parent.childNodes.push(this)
|
|
850
302
|
this.parent=parent;
|
|
851
303
|
this._classList=null;
|
|
852
304
|
this.__style=null;
|
|
853
305
|
this._dataset=null
|
|
854
306
|
}
|
|
855
307
|
get id(){ return this.attributes.id ? this.attributes.id : null; }
|
|
856
308
|
set id(newValue){ this.attributes.id=newValue; }
|
|
857
309
|
get className(){return this.attributes.class || null}
|
|
858
310
|
get parentNode(){ return this.parent }
|
|
859
311
|
get ancestors(){
|
|
860
312
|
if(!this.parent) return []
|
|
861
313
|
const ancestors=[]
|
|
862
314
|
let element=this.parent
|
|
863
315
|
while (element.tagName!=='ROOT'){
|
|
864
316
|
ancestors.push(element)
|
|
865
317
|
element=element.parent
|
|
866
318
|
}
|
|
867
319
|
return ancestors.reverse()
|
|
868
320
|
}
|
|
869
321
|
get childNodeIndex(){
|
|
870
322
|
if(!this.parent) return null
|
|
871
323
|
return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
|
|
872
324
|
}
|
|
873
325
|
get childIndex(){
|
|
874
326
|
if(!this.parent) return null
|
|
875
327
|
return this.parent.children ? this.parent.children.indexOf(this) : null
|
|
876
328
|
}
|
|
877
329
|
get previousElementSibling(){ return this.prev }
|
|
878
330
|
get prev(){
|
|
879
331
|
if (!this.childIndex) return null
|
|
880
332
|
return this.parent.children[this.childIndex-1]
|
|
881
333
|
}
|
|
882
334
|
get nextElementSibling(){ return this.next }
|
|
883
335
|
get next(){
|
|
884
336
|
if (!this.childIndex) return null
|
|
885
337
|
return this.parent.children[this.childIndex+1] || null
|
|
886
338
|
}
|
|
887
339
|
get dataset(){
|
|
888
340
|
if (!this._dataset) this._dataset=getDataset(this);
|
|
889
341
|
return this._dataset;
|
|
890
342
|
}
|
|
891
343
|
get classList(){
|
|
892
344
|
if (!this._classList) this._classList=new NodeClassList(this);
|
|
893
345
|
return this._classList;
|
|
894
346
|
}
|
|
895
347
|
get style(){
|
|
896
348
|
if (!this.__style) this.__style=buildStyle(this.attributes)
|
|
897
349
|
return this.__style
|
|
898
350
|
}
|
|
899
351
|
get outerHTML(){
|
|
900
352
|
const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
|
|
901
353
|
return `<${this.tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this.tagName}>`;
|
|
902
354
|
}
|
|
903
355
|
getAttribute(attrName){ return this.attributes[attrName] || null }
|
|
904
356
|
setAttribute(attrName,value){ this.attributes[attrName]=value }
|
|
905
357
|
removeAttribute(attrName){ delete this.attributes[attrName] }
|
|
906
358
|
remove(){
|
|
907
359
|
if (!this.parent) return
|
|
908
360
|
const index=this.childIndex;
|
|
909
361
|
if (index!==null) this.parent.childNodes.splice(index,1);
|
|
910
362
|
}
|
|
911
363
|
get innerHTML(){
|
|
912
364
|
return this.childNodes.map(child=>{
|
|
913
365
|
if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
|
|
914
366
|
else if (child instanceof TextNode) return child.textContent;
|
|
915
367
|
else return child
|
|
916
368
|
}).join("");
|
|
917
369
|
}
|
|
918
370
|
$$(query){return this.querySelectorAll(query)}
|
|
919
371
|
querySelectorAll(query){
|
|
920
372
|
const selectors=Query.get(query)
|
|
921
373
|
return find(selectors,this,new Set())
|
|
922
374
|
}
|
|
923
375
|
$(query){return this.querySelector(query)}
|
|
924
376
|
querySelector(query){
|
|
925
377
|
const selectors=Query.get(query)
|
|
926
378
|
return find(selectors,this,new Set(),true)[0] || null
|
|
927
379
|
}
|
|
928
380
|
getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
|
|
929
381
|
getElementsByTagName(query){ return this.querySelectorAll(query) }
|
|
930
382
|
getElementById(query){ return this.querySelector('#'+query) }
|
|
931
383
|
get children(){
|
|
932
384
|
return this.childNodes.filter(child=>{
|
|
933
385
|
if (!(child instanceof Node)) return false
|
|
934
386
|
if (child.tagName==='#comment') return false
|
|
935
387
|
return true
|
|
936
388
|
});
|
|
937
389
|
}
|
|
938
390
|
insertAdjacentElement(position,newElement){
|
|
939
391
|
if(newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
|
|
940
392
|
const pos=position.toLowerCase();
|
|
941
393
|
if (pos==="afterbegin") this.childNodes.unshift(newElement);
|
|
942
394
|
else if (pos==="beforeend") this.childNodes.push(newElement);
|
|
943
395
|
newElement.parent=this
|
|
944
396
|
if (!this.parent) return newElement
|
|
945
397
|
if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
|
|
946
398
|
else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
|
|
947
399
|
newElement.parent=this.parent
|
|
948
400
|
return newElement
|
|
949
401
|
}
|
|
950
402
|
insertAdjacentHTML(position,html){
|
|
951
403
|
const newNode=parseHTML(html);
|
|
952
404
|
newNode.childNodes.reverse().forEach(node=>{
|
|
953
405
|
this.insertAdjacentElement(position,node);
|
|
954
406
|
});
|
|
955
407
|
return newNode
|
|
956
408
|
}
|
|
957
409
|
insertAdjacentText(position,text){
|
|
958
410
|
return this.insertAdjacentElement(position,new TextNode(text));
|
|
959
411
|
}
|
|
960
412
|
set innerHTML(html){
|
|
961
413
|
const parsed=parseHTML(html);
|
|
962
414
|
this.childNodes=parsed.childNodes;
|
|
963
415
|
}
|
|
964
416
|
set outerHTML(html){
|
|
965
417
|
const parsed=parseHTML(html);
|
|
966
418
|
if (!this.parent) return console.log('element has no parent node')
|
|
967
419
|
const index=this.childIndex
|
|
968
420
|
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
969
421
|
}
|
|
970
422
|
appendChild(newChild){
|
|
971
423
|
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
972
424
|
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
973
425
|
} else if(typeof newChild==='string') newChild=new TextNode(newChild)
|
|
974
426
|
else return newChild
|
|
975
427
|
this.childNodes.push(newChild);
|
|
976
428
|
newChild.parent=this;
|
|
977
429
|
return newChild;
|
|
978
430
|
}
|
|
979
431
|
get textContent(){
|
|
980
432
|
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
981
433
|
return this.childNodes.map(child=>{
|
|
982
434
|
if(child instanceof SingleNode) return ''
|
|
983
435
|
if(child instanceof TextNode) return child.nodeValue
|
|
984
436
|
if(child instanceof Node) return child.textContent;
|
|
985
437
|
else return child;
|
|
986
438
|
}).join(" ");
|
|
987
439
|
}
|
|
988
440
|
set textContent(value){
|
|
989
441
|
this.childNodes=[];
|
|
990
442
|
if (value!==null && value!==undefined){
|
|
991
443
|
this.childNodes.push(value.toString());
|
|
992
444
|
}
|
|
993
445
|
}
|
|
446
|
+
}
|
|
447
|
+
class SingleNode extends Node{
|
|
994
448
|
constructor(tagName,attributes={},parent=null){
|
|
995
449
|
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
996
450
|
super(tagName,attributes,parent);
|
|
997
451
|
this.isSingle=true
|
|
998
452
|
}
|
|
999
453
|
get outerHTML(){
|
|
1000
454
|
if (this.tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
|
|
1001
455
|
const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
|
|
1002
456
|
return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
|
|
1003
457
|
}
|
|
1004
458
|
get innerHTML(){ return ""; }
|
|
1005
459
|
set innerHTML(_){ }
|
|
1006
460
|
$(_){return null}
|
|
1007
461
|
$$(_){return []}
|
|
1008
462
|
querySelectorAll(_){ return []; }
|
|
1009
463
|
querySelector(_){ return null; }
|
|
1010
464
|
getElementsByClassName(_){ return []; }
|
|
1011
465
|
getElementsByTagName(_){ return []; }
|
|
1012
466
|
getElementById(_){ return null; }
|
|
1013
467
|
get children(){ return []; }
|
|
1014
468
|
insertAdjacentElement(_,__){ }
|
|
1015
469
|
insertAdjacentHTML(_,__){ }
|
|
1016
470
|
insertAdjacentText(_,__){ }
|
|
1017
471
|
appendChild(_){ }
|
|
1018
472
|
get textContent(){ return ""; }
|
|
1019
473
|
set textContent(_){ }
|
|
474
|
+
}
|
|
475
|
+
function parseAttributes(str){
|
|
1020
476
|
const attrs={};
|
|
1021
477
|
let key="";
|
|
1022
478
|
let value="";
|
|
1023
479
|
let isKey=true;
|
|
1024
480
|
let quoteChar=null;
|
|
1025
481
|
for (let i=0; i< str.length; i++){
|
|
1026
482
|
const char=str[i];
|
|
1027
483
|
if (isKey && (char==='=' || char===' ')){
|
|
1028
484
|
if (char==='=') isKey=false;
|
|
1029
485
|
else if (key.trim()){
|
|
1030
486
|
attrs[key.trim()]=true;
|
|
1031
487
|
key="";
|
|
1032
488
|
}
|
|
1033
489
|
continue;
|
|
1034
490
|
}
|
|
1035
491
|
if (!quoteChar && (char==='"' || char==="'")){
|
|
1036
492
|
quoteChar=char;
|
|
1037
493
|
continue;
|
|
1038
494
|
} else if (quoteChar && char===quoteChar){
|
|
1039
495
|
quoteChar=null;
|
|
1040
496
|
attrs[key.trim()]=value.trim();
|
|
1041
497
|
key=""; value=""; isKey=true;
|
|
1042
498
|
continue;
|
|
1043
499
|
}
|
|
1044
500
|
if (isKey) key+=char;
|
|
1045
501
|
else value+=char;
|
|
1046
502
|
}
|
|
1047
503
|
if (key.trim() &&!value) attrs[key.trim()]=true;
|
|
1048
504
|
return attrs;
|
|
505
|
+
}
|
|
506
|
+
const VOID_TAGS=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","!doctype",'?xml']);
|
|
507
|
+
function parseHTML(html){
|
|
1049
508
|
const root=new Node("ROOT");
|
|
1050
509
|
const stack=[root];
|
|
1051
510
|
let currentText="",i=0;
|
|
1052
511
|
let max=0
|
|
1053
512
|
function parseScript(){
|
|
1054
513
|
if (!html.startsWith("<script",i)) return false;
|
|
1055
514
|
const openTagEnd=html.indexOf(">",i);
|
|
1056
515
|
if (openTagEnd===-1) return false;
|
|
1057
516
|
const attributesString=html.substring(i+7,openTagEnd).trim();
|
|
1058
517
|
const attributes=parseAttributes(attributesString);
|
|
1059
518
|
let closeTagStart=html.indexOf("</script>",openTagEnd);
|
|
1060
519
|
if (closeTagStart===-1) return false;
|
|
1061
520
|
const content=html.substring(openTagEnd+1,closeTagStart);
|
|
1062
521
|
const scriptNode=new Node('script',attributes,stack[stack.length-1]);
|
|
1063
522
|
if(content.length>0) scriptNode.childNodes.push(content);
|
|
1064
523
|
i=closeTagStart+9;
|
|
1065
524
|
return true;
|
|
1066
525
|
}
|
|
1067
526
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
1068
527
|
if (!html.startsWith(startStr,i)) return false
|
|
1069
528
|
const end=html.indexOf(endStr,i+n1);
|
|
1070
529
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
1071
530
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
1072
531
|
i=end+n2;
|
|
1073
532
|
return true
|
|
1074
533
|
}
|
|
1075
534
|
while (i< html.length){
|
|
1076
535
|
if(i>=max) max=i;
|
|
1077
536
|
else break;
|
|
1078
537
|
if (parseScript()) continue
|
|
1079
538
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
1080
539
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
1081
540
|
if (html.startsWith("<![CDATA[",i)){
|
|
1082
541
|
const end=html.indexOf("]]>",i+9);
|
|
1083
542
|
if (end===-1) break;
|
|
1084
543
|
const content=html.substring(i+9,end);
|
|
1085
544
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
1086
545
|
cdataNode.nodeValue=content;
|
|
1087
546
|
i=end+3;
|
|
1088
547
|
continue;
|
|
1089
548
|
}
|
|
1090
549
|
if (html.startsWith("<",i)){
|
|
1091
550
|
if (currentText){
|
|
1092
551
|
stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
1093
552
|
currentText="";
|
|
1094
553
|
}
|
|
1095
554
|
let tagEnd=i+1;
|
|
1096
555
|
let insideQuotes=false;
|
|
1097
556
|
let quoteChar=null;
|
|
1098
557
|
while (tagEnd< html.length){
|
|
1099
558
|
const char=html[tagEnd];
|
|
1100
559
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
1101
560
|
insideQuotes=true;
|
|
1102
561
|
quoteChar=char;
|
|
1103
562
|
} else if (insideQuotes && char===quoteChar){
|
|
1104
563
|
insideQuotes=false;
|
|
1105
564
|
quoteChar=null;
|
|
1106
565
|
}
|
|
1107
566
|
if (!insideQuotes && char==='>') break;
|
|
1108
567
|
tagEnd++;
|
|
1109
568
|
}
|
|
1110
569
|
const tagContent=html.substring(i+1,tagEnd);
|
|
1111
570
|
if (tagContent.startsWith("/")) stack.pop();
|
|
1112
571
|
else{
|
|
1113
572
|
let isSelfClosing=tagContent.endsWith('/');
|
|
1114
573
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
1115
574
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
1116
575
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
1117
576
|
const attributes=parseAttributes(attributesString);
|
|
1118
577
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
1119
578
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
1120
579
|
}
|
|
1121
580
|
i=tagEnd+1;
|
|
1122
581
|
} else{
|
|
1123
582
|
currentText+=html[i];
|
|
1124
583
|
i++;
|
|
1125
584
|
}
|
|
1126
585
|
}
|
|
1127
586
|
if (currentText.trim()) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
1128
587
|
return root;
|
|
588
|
+
}
|
|
1129
589
|
module.exports = { parseHTML, Node, Query, TextNode, SingleNode }
|