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