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