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