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