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