als-document 1.4.1 → 1.4.2

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/index.mjs CHANGED
@@ -1,727 +1,42 @@
1
- class Query{
2
- static get(query){
3
- let q=new Query(query)
4
- return q.selectors
5
- }
6
- constructor(query){
7
- this.query=query
8
- this.selectors=[]
9
- this.stringValues=[];
10
- this.parseSelectors(query.split(','))
11
- }
12
- parseSelectors(selectors){
13
- selectors.forEach(selector=>{
14
- let originalSelector=selector.trim()
15
- selector=this.removeSpaces(selector)
16
- this.stringValues=[]
17
- selector=selector.replace(/\[.*?\]/g,(value)=>{
18
- this.stringValues.push(value)
19
- return `[${this.stringValues.length-1}]`
20
- })
21
- let [element,ancestors]=this.splitAndCutLast(selector,' ')
22
- element=this.getFamily(element)
23
- if (ancestors.length>0)
24
- element.ancestors=ancestors.map(ancestor=>this.getFamily(ancestor))
25
- element.group=originalSelector
26
- this.selectors.push(element)
27
- });
28
- }
29
- splitAndCutLast(string,splitBy){
30
- const array=string.split(splitBy);
31
- const last=array.pop();
32
- return [last,array];
33
- }
34
- getFamily(group,element,prev,prevAny,sign){
35
- if (group.match(/\~|\+/)!==null){
36
- let [last,prevBrothers]=this.splitAndCutLast(group,/\~|\+/)
37
- let signs=group.replace(last,'')
38
- prevBrothers.forEach(el=>signs=signs.replace(el,''))
39
- signs=signs.match(/\~|\+/g)
40
- if (signs.length==1){
41
- sign=signs[0]
42
- } else if (signs.length>1){
43
- sign=signs.splice(signs.length-1,signs.length-1)[0]
44
- prevBrothers[0]=prevBrothers.map((b,i)=>{
45
- if (i< prevBrothers.length-1) b+=signs[i]
46
- return b
47
- }).join('')
48
- prevBrothers[0]=this.getFamily(prevBrothers[0])
49
- }
50
- if (sign=='~') prevAny=prevBrothers[0]
51
- else if (sign=='+') prev=prevBrothers[0]
52
- element=last
53
- } else element=group
54
- let family
55
- if (prev || prevAny){
56
- family=this.getParents(element)
57
- if (prev) family.prev=this.getParents(prev)
58
- if (prevAny) family.prevAny=this.getParents(prevAny)
59
- } else family=this.getParents(element)
60
- if (family.query!==group) family.group=group
61
- return family
62
- }
63
- getParents(selector){
64
- if (typeof selector=='string'){
65
- let [element,parents]=this.splitAndCutLast(selector,'>')
66
- element=this.buildElement(element)
67
- parents=parents.map(parent=>this.buildElement(parent))
68
- if (parents.length>0) element.parents=parents
69
- return element
70
- } else return selector
71
- }
72
- buildElement(element,id=null,tag=null,classList=[]){
73
- let query=element
74
- element=element.replace(/\#(\w-?)*/,$id=>{
75
- id=$id.replace(/^\#/,''); return ''
76
- })
77
- element=element.replace(/\.(\w-?)*/,$class=>{
78
- classList.push($class.replace(/^\./,'')); return ''
79
- })
80
- element=element.replace(/(\w\:?-?)*/,$tag=>{
81
- tag=$tag=='' ? null : $tag; return ''
82
- })
83
- let attribs=this.getAttributes(element)
84
- element={ query }
85
- if (id) element.id=id
86
- if (tag) element.tag=tag
87
- if (classList.length>0) element.classList=classList
88
- if (attribs.length>0) element.attribs=attribs
89
- return element
90
- }
91
- getAttributes(element){
92
- let attribs=this.stringValues.filter((value,index)=>{
93
- let searchValue=`[${index}]`
94
- if (element.match(searchValue)) return true
95
- else return false
96
- })
97
- attribs=attribs.map(attrib=>{
98
- let query=attrib
99
- attrib=attrib.replace('[','').replace(']','')
100
- let [name,...values]=attrib.split('=')
101
- const value=values.join('=').trim().replace(/^\"/,'').replace(/\"$/,'')
102
- let sign
103
- attrib={query,name}
104
- if(value){
105
- sign='='
106
- attrib.name=attrib.name.replace(/[\~\|\^\$\*]$/,(match=>{
107
- sign=match+sign
108
- return ''
109
- }))
110
- attrib.value=value
111
- }
112
- if (sign){
113
- attrib.sign=sign
114
- attrib.check=this.getAttribFn(sign).bind(attrib)
115
- }
116
- return attrib
117
- });
118
- return attribs
119
- }
120
- getAttribFn(sign){
121
- if (sign=='=') return function (value){ return value===this.value }
122
- if (sign=='*=') return function (value){ return value.includes(this.value) }
123
- if (sign=='^=') return function (value){ return value.startsWith(this.value) }
124
- if (sign=='$=') return function (value){ return value.endsWith(this.value) }
125
- if (sign=='|=') return function (value){
126
- return value.trim().split(' ').length==1
127
- && (value.startsWith(this.value) || value.startsWith(this.value+'-'))
128
- ? true : false
129
- }
130
- if (sign=='~=') return function (value){
131
- return this.value.trim().split(' ').length==1 && value.includes(this.value) ? true : false
132
- }
133
- }
134
- removeSpaces(selector){
135
- selector=selector.replace(/\s{2}/g,' ')
136
- selector=selector.replace(/\s?\^?\$?\|?\~?\*?\=\s*/g,(m)=>m.trim())
137
- selector=selector.replace(/\s?(\+|\~|\>)\s?/g,(m)=>m.trim())
138
- return selector
139
- }
140
- }
141
- function checkElement(el,selector){
142
- if(selector==undefined) return true
143
- if(el==null) return false
144
- let{tag,classList,attribs:attributes,id,prev,ancestors,parents,prevAny}=selector
145
- if(typeof el==='string') return false
146
- if(id!==undefined && el.id===null) return false
147
- if(id && id!==el.id) return false
148
- if(tag && el._tagName===undefined) return false
149
- else if(tag && tag!==el._tagName) return false
150
- const clas=el.attributes.class
151
- if(classList!==undefined && (clas===undefined || clas==='')) return false
152
- else if(classList!==undefined){
153
- if(classList.every(e=>el.classList.contains(e))===false) return false
154
- }
155
- if(checkattributes(attributes,el)===false) return false
156
- if(checkElement(el.prev,prev)===false) return false
157
- if(checkAncestors(el.ancestors,ancestors)===false) return false
158
- if(checkParents(el.ancestors,parents)===false) return false
159
- if(el.parent){
160
- if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
161
- }
162
- return true
163
- }
164
- function checkattributes(attributes=[],el){
165
- let elattributes=el.attributes
166
- let names=Object.keys(elattributes)
167
- let passedTests=0
168
- if(attributes) for(let i=0; i<attributes.length; i++){
169
- let{name,value,check}=attributes[i]
170
- if(name=='inner' && value!==undefined && check && el.inner){
171
- if(check(el.inner)) passedTests++
172
- }
173
- if(!names.includes(name)) continue
174
- else if(value==undefined) passedTests++
175
- else if(value && elattributes[name]){
176
- if(check(elattributes[name])==false) continue
177
- else passedTests++
178
- }
179
- }
180
- if(passedTests==attributes.length) return true
181
- else return false
182
- }
183
- function checkPrevAny(children=[],index,prevAny){
184
- let size=children.length
185
- if((size==0 || index==0) && prevAny) return false
186
- for(let i=index; i>=0; i--){
187
- if(checkElement(children[i],prevAny)) return true
188
- }
189
- return false
190
- }
191
- function checkAncestors(ancestors=[],selectorAncestors=[]){
192
- let count=0
193
- if(selectorAncestors.length==0) return true
194
- let endIndex=ancestors.length-1
195
- let selectorIndex=selectorAncestors.length-1
196
- while(selectorIndex>=0){
197
- for(let i=endIndex; i>=0; i--){
198
- endIndex=i-1
199
- if(checkElement(ancestors[i],selectorAncestors[selectorIndex])==true){
200
- count++
201
- break
202
- }
203
- }
204
- selectorIndex--
205
- }
206
- if(count==selectorAncestors.length) return true
207
- else return false
208
- }
209
- function checkParents(ancestors=[],selectorParents=[]){
210
- if(selectorParents.length===0) return true
211
- if(ancestors.length< selectorParents.length) return false
212
- let index=ancestors.length-1
213
- for(let i=selectorParents.length-1; i>=0; i--){
214
- if(checkElement(ancestors[index],selectorParents[i])===false) return false
215
- index--
216
- }
217
- return true
218
- }
1
+ class Query{
219
2
  static get(query){
220
3
  let q=new Query(query)
221
4
  return q.selectors
222
5
  }
223
6
  constructor(query){
224
7
  this.query=query
225
8
  this.selectors=[]
226
9
  this.stringValues=[];
227
10
  this.parseSelectors(query.split(','))
228
11
  }
229
12
  parseSelectors(selectors){
230
13
  selectors.forEach(selector=>{
231
14
  let originalSelector=selector.trim()
232
15
  selector=this.removeSpaces(selector)
233
16
  this.stringValues=[]
234
17
  selector=selector.replace(/\[.*?\]/g,(value)=>{
235
18
  this.stringValues.push(value)
236
19
  return `[${this.stringValues.length-1}]`
237
20
  })
238
21
  let [element,ancestors]=this.splitAndCutLast(selector,' ')
239
22
  element=this.getFamily(element)
240
23
  if (ancestors.length>0)
241
24
  element.ancestors=ancestors.map(ancestor=>this.getFamily(ancestor))
242
25
  element.group=originalSelector
243
26
  this.selectors.push(element)
244
27
  });
245
28
  }
246
29
  splitAndCutLast(string,splitBy){
247
30
  const array=string.split(splitBy);
248
31
  const last=array.pop();
249
32
  return [last,array];
250
33
  }
251
34
  getFamily(group,element,prev,prevAny,sign){
252
35
  if (group.match(/\~|\+/)!==null){
253
36
  let [last,prevBrothers]=this.splitAndCutLast(group,/\~|\+/)
254
37
  let signs=group.replace(last,'')
255
38
  prevBrothers.forEach(el=>signs=signs.replace(el,''))
256
39
  signs=signs.match(/\~|\+/g)
257
40
  if (signs.length==1){
258
41
  sign=signs[0]
259
42
  } else if (signs.length>1){
260
43
  sign=signs.splice(signs.length-1,signs.length-1)[0]
261
44
  prevBrothers[0]=prevBrothers.map((b,i)=>{
262
45
  if (i< prevBrothers.length-1) b+=signs[i]
263
46
  return b
264
47
  }).join('')
265
48
  prevBrothers[0]=this.getFamily(prevBrothers[0])
266
49
  }
267
50
  if (sign=='~') prevAny=prevBrothers[0]
268
51
  else if (sign=='+') prev=prevBrothers[0]
269
52
  element=last
270
53
  } else element=group
271
54
  let family
272
55
  if (prev || prevAny){
273
56
  family=this.getParents(element)
274
57
  if (prev) family.prev=this.getParents(prev)
275
58
  if (prevAny) family.prevAny=this.getParents(prevAny)
276
59
  } else family=this.getParents(element)
277
60
  if (family.query!==group) family.group=group
278
61
  return family
279
62
  }
280
63
  getParents(selector){
281
64
  if (typeof selector=='string'){
282
65
  let [element,parents]=this.splitAndCutLast(selector,'>')
283
66
  element=this.buildElement(element)
284
67
  parents=parents.map(parent=>this.buildElement(parent))
285
68
  if (parents.length>0) element.parents=parents
286
69
  return element
287
70
  } else return selector
288
71
  }
289
72
  buildElement(element,id=null,tag=null,classList=[]){
290
73
  let query=element
291
74
  element=element.replace(/\#(\w-?)*/,$id=>{
292
75
  id=$id.replace(/^\#/,''); return ''
293
76
  })
294
77
  element=element.replace(/\.(\w-?)*/,$class=>{
295
78
  classList.push($class.replace(/^\./,'')); return ''
296
79
  })
297
80
  element=element.replace(/(\w\:?-?)*/,$tag=>{
298
81
  tag=$tag=='' ? null : $tag; return ''
299
82
  })
300
83
  let attribs=this.getAttributes(element)
301
84
  element={ query }
302
85
  if (id) element.id=id
303
86
  if (tag) element.tag=tag
304
87
  if (classList.length>0) element.classList=classList
305
88
  if (attribs.length>0) element.attribs=attribs
306
89
  return element
307
90
  }
308
91
  getAttributes(element){
309
92
  let attribs=this.stringValues.filter((value,index)=>{
310
93
  let searchValue=`[${index}]`
311
94
  if (element.match(searchValue)) return true
312
95
  else return false
313
96
  })
314
97
  attribs=attribs.map(attrib=>{
315
98
  let query=attrib
316
99
  attrib=attrib.replace('[','').replace(']','')
317
100
  let [name,...values]=attrib.split('=')
318
101
  const value=values.join('=').trim().replace(/^\"/,'').replace(/\"$/,'')
319
102
  let sign
320
103
  attrib={query,name}
321
104
  if(value){
322
105
  sign='='
323
106
  attrib.name=attrib.name.replace(/[\~\|\^\$\*]$/,(match=>{
324
107
  sign=match+sign
325
108
  return ''
326
109
  }))
327
110
  attrib.value=value
328
111
  }
329
112
  if (sign){
330
113
  attrib.sign=sign
331
114
  attrib.check=this.getAttribFn(sign).bind(attrib)
332
115
  }
333
116
  return attrib
334
117
  });
335
118
  return attribs
336
119
  }
337
120
  getAttribFn(sign){
338
121
  if (sign=='=') return function (value){ return value===this.value }
339
122
  if (sign=='*=') return function (value){ return value.includes(this.value) }
340
123
  if (sign=='^=') return function (value){ return value.startsWith(this.value) }
341
124
  if (sign=='$=') return function (value){ return value.endsWith(this.value) }
342
125
  if (sign=='|=') return function (value){
343
126
  return value.trim().split(' ').length==1
344
127
  && (value.startsWith(this.value) || value.startsWith(this.value+'-'))
345
128
  ? true : false
346
129
  }
347
130
  if (sign=='~=') return function (value){
348
131
  return this.value.trim().split(' ').length==1 && value.includes(this.value) ? true : false
349
132
  }
350
133
  }
351
134
  removeSpaces(selector){
352
135
  selector=selector.replace(/\s{2}/g,' ')
353
136
  selector=selector.replace(/\s?\^?\$?\|?\~?\*?\=\s*/g,(m)=>m.trim())
354
137
  selector=selector.replace(/\s?(\+|\~|\>)\s?/g,(m)=>m.trim())
355
138
  return selector
356
139
  }
140
+ }
141
+ function checkElement(el,selector){
357
142
  if(selector==undefined) return true
358
143
  if(el==null) return false
359
144
  let{tag,classList,attribs:attributes,id,prev,ancestors,parents,prevAny}=selector
360
145
  if(typeof el==='string') return false
361
146
  if(id!==undefined && el.id===null) return false
362
147
  if(id && id!==el.id) return false
363
148
  if(tag && el._tagName===undefined) return false
364
149
  else if(tag && tag!==el._tagName) return false
365
150
  const clas=el.attributes.class
366
151
  if(classList!==undefined && (clas===undefined || clas==='')) return false
367
152
  else if(classList!==undefined){
368
153
  if(classList.every(e=>el.classList.contains(e))===false) return false
369
154
  }
370
155
  if(checkattributes(attributes,el)===false) return false
371
156
  if(checkElement(el.prev,prev)===false) return false
372
157
  if(checkAncestors(el.ancestors,ancestors)===false) return false
373
158
  if(checkParents(el.ancestors,parents)===false) return false
374
159
  if(el.parent){
375
160
  if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
376
161
  }
377
162
  return true
163
+ }
378
164
  function checkattributes(attributes=[],el){
379
165
  let elattributes=el.attributes
380
166
  let names=Object.keys(elattributes)
381
167
  let passedTests=0
382
168
  if(attributes) for(let i=0; i<attributes.length; i++){
383
169
  let{name,value,check}=attributes[i]
384
170
  if(name=='inner' && value!==undefined && check && el.inner){
385
171
  if(check(el.inner)) passedTests++
386
172
  }
387
173
  if(!names.includes(name)) continue
388
174
  else if(value==undefined) passedTests++
389
175
  else if(value && elattributes[name]){
390
176
  if(check(elattributes[name])==false) continue
391
177
  else passedTests++
392
178
  }
393
179
  }
394
180
  if(passedTests==attributes.length) return true
395
181
  else return false
182
+ }
396
183
  function checkPrevAny(children=[],index,prevAny){
397
184
  let size=children.length
398
185
  if((size==0 || index==0) && prevAny) return false
399
186
  for(let i=index; i>=0; i--){
400
187
  if(checkElement(children[i],prevAny)) return true
401
188
  }
402
189
  return false
190
+ }
403
191
  function checkAncestors(ancestors=[],selectorAncestors=[]){
404
192
  let count=0
405
193
  if(selectorAncestors.length==0) return true
406
194
  let endIndex=ancestors.length-1
407
195
  let selectorIndex=selectorAncestors.length-1
408
196
  while(selectorIndex>=0){
409
197
  for(let i=endIndex; i>=0; i--){
410
198
  endIndex=i-1
411
199
  if(checkElement(ancestors[i],selectorAncestors[selectorIndex])==true){
412
200
  count++
413
201
  break
414
202
  }
415
203
  }
416
204
  selectorIndex--
417
205
  }
418
206
  if(count==selectorAncestors.length) return true
419
207
  else return false
208
+ }
420
209
  function checkParents(ancestors=[],selectorParents=[]){
421
210
  if(selectorParents.length===0) return true
422
211
  if(ancestors.length< selectorParents.length) return false
423
212
  let index=ancestors.length-1
424
213
  for(let i=selectorParents.length-1; i>=0; i--){
425
214
  if(checkElement(ancestors[index],selectorParents[i])===false) return false
426
215
  index--
427
216
  }
428
217
  return true
218
+ }
429
219
  const getDataName=prop=>'data-'+prop.toLowerCase()
430
- function getDataset(element){
431
- return new Proxy(element.attributes,{
432
- get: (target,prop)=>{return target[getDataName(prop)]},set: (target,prop,value)=>{target[getDataName(prop)]=value; return true},deleteProperty: (target,prop)=>{
433
- const dataAttr=getDataName(prop)
434
- if (dataAttr in target){
435
- delete target[dataAttr];
436
- return true;
437
- }
438
- return false;
439
- }
440
- });
441
- }
442
-
443
- function find(selectors,element,collection,first=false,firstTime=true){
444
- if(!firstTime)
445
- for(let selector of selectors){
446
- if(checkElement(element,selector)) collection.add(element)
447
- }
448
- if(element.children)
449
- element.children.forEach(child=>{
450
- if(first && collection.size>0) return
451
- find(selectors,child,collection,first,false)
452
- })
453
- return firstTime ? [...collection] : collection
454
- }
455
- class TextNode{
456
- constructor(data){
457
- this.nodeName='#text';
458
- this.parent=null;
459
- this.textContent=data;
460
- }
461
- get nodeValue(){return this.textContent}
462
- get parentNode(){return this.parent}
463
- }
464
-
465
- function buildStyle(attributes){
466
- const styles=attributes.style || "";
467
- const camelToKebab=str=>str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,'$1-$2').toLowerCase();
468
- const kebabToCamel=str=>str.replace(/-([a-z])/g,g=>g[1].toUpperCase());
469
- const baseStyleObj=styles.split(";").reduce((acc,style)=>{
470
- const [key,value]=style.split(":").map(s=>s.trim());
471
- if (key && value) acc[kebabToCamel(key)]=value;
472
- return acc;
473
- },{});
474
- return new Proxy(baseStyleObj,{
475
- get: (obj,prop)=>obj[camelToKebab(prop)] || obj[prop],set: (obj,prop,value)=>{
476
- obj[camelToKebab(prop)]=value;
477
- attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
478
- return true;
479
- },deleteProperty: (obj,prop)=>{
480
- delete obj[camelToKebab(prop)];
481
- attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
482
- return true;
483
- }
484
- });
485
- }
486
-
487
- class NodeClassList{
488
- constructor(node){ this.node=node }
489
- get classes(){ return (this.node.attributes.class || "").split(" ").filter(Boolean) }
490
- set classes(val){ this.node.attributes.class=val.join(" ") }
491
- contains(className){ return this.classes.includes(className) }
492
- add(className){
493
- const currentClasses=this.classes;
494
- if (!currentClasses.includes(className)) this.classes=[...currentClasses,className];
495
- }
496
- remove(className){ this.classes=this.classes.filter(cls=>cls!==className); }
497
- toggle(className){
498
- if (this.classes.includes(className)) this.remove(className);
499
- else this.add(className);
500
- }
501
- replace(oldClass,newClass){
502
- if (this.classes.includes(oldClass)){
503
- this.remove(oldClass);
504
- this.add(newClass);
505
- }
506
- }
507
- }
508
- function insertBefore(arr,index,newItem){
509
- const existingIndex=arr.indexOf(newItem);
510
- if (existingIndex!==-1) arr.splice(existingIndex,1);
511
- arr.splice(index,0,newItem);
512
- }
513
- class Node{
514
- constructor(tagName,attributes={},parent=null){
515
- this.isSingle=false;
516
- this._tagName=tagName.toLowerCase();
517
- this.tagName=tagName.toUpperCase();
518
- this.attributes=attributes;
519
- this.childNodes=[];
520
- if (parent!==null) parent.childNodes.push(this)
521
- this.parent=parent;
522
- this._classList=null;
523
- this.__style=null;
524
- this._dataset=null
525
- }
526
- get id(){ return this.attributes.id ? this.attributes.id : null; }
527
- set id(newValue){ this.attributes.id=newValue; }
528
- get className(){ return this.attributes.class || null }
529
- get parentNode(){ return this.parent }
530
- get ancestors(){
531
- if (!this.parent) return []
532
- const ancestors=[]
533
- let element=this.parent
534
- while (element.tagName!=='ROOT'){
535
- ancestors.push(element)
536
- element=element.parent
537
- }
538
- return ancestors.reverse()
539
- }
540
- get childNodeIndex(){
541
- if (!this.parent) return null
542
- return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
543
- }
544
- get childIndex(){
545
- if (!this.parent) return null
546
- return this.parent.children ? this.parent.children.indexOf(this) : null
547
- }
548
- get previousElementSibling(){ return this.prev }
549
- get prev(){
550
- if (this.childIndex===null) return null
551
- return this.parent.children[this.childIndex-1]
552
- }
553
- get nextElementSibling(){ return this.next }
554
- get next(){
555
- if (this.childIndex===null) return null
556
- return this.parent.children[this.childIndex+1] || null
557
- }
558
- get dataset(){
559
- if (!this._dataset) this._dataset=getDataset(this);
560
- return this._dataset;
561
- }
562
- get classList(){
563
- if (!this._classList) this._classList=new NodeClassList(this);
564
- return this._classList;
565
- }
566
- get style(){
567
- if (!this.__style) this.__style=buildStyle(this.attributes)
568
- return this.__style
569
- }
570
- get outerHTML(){
571
- const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
572
- return `<${this._tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this._tagName}>`;
573
- }
574
- getAttribute(attrName){ return this.attributes[attrName]!==undefined ? this.attributes[attrName] : null }
575
- setAttribute(attrName,value=''){ this.attributes[attrName]=value }
576
- removeAttribute(attrName){ delete this.attributes[attrName] }
577
- remove(){
578
- if (!this.parent) return
579
- const index=this.childNodeIndex;
580
- if (index!==null) this.parent.childNodes.splice(index,1);
581
- }
582
- get innerHTML(){
583
- return this.childNodes.map(child=>{
584
- if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
585
- else if (child instanceof TextNode) return child.textContent;
586
- else return child
587
- }).join("");
588
- }
589
- get innerText(){
590
- return this.childNodes.map(child=>{
591
- if (child instanceof Node || child instanceof SingleNode) return child.innerText;
592
- else if (child instanceof TextNode) return child.textContent;
593
- else return child
594
- }).join("");
595
- }
596
- set innerText(value){
597
- this.childNodes=[new TextNode(value)]
598
- return value
599
- }
600
- $$(query){ return this.querySelectorAll(query) }
601
- querySelectorAll(query){
602
- const selectors=Query.get(query)
603
- return find(selectors,this,new Set())
604
- }
605
- $(query){ return this.querySelector(query) }
606
- querySelector(query){
607
- const selectors=Query.get(query)
608
- return find(selectors,this,new Set(),true)[0] || null
609
- }
610
- getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
611
- getElementsByTagName(query){ return this.querySelectorAll(query) }
612
- getElementById(query){ return this.querySelector('#'+query) }
613
- get children(){
614
- return this.childNodes.filter(child=>{
615
- if (!(child instanceof Node)) return false
616
- if (child._tagName==='#comment') return false
617
- return true
618
- });
619
- }
620
- insertAdjacentElement(position,newElement){
621
- if (newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
622
- const pos=position.toLowerCase();
623
- if(newElement.parentNode){
624
- newElement.parentNode.childNodes=newElement.parentNode.childNodes.filter(el=>el!==newElement)
625
- }
626
- if (pos==='afterbegin' || pos==='beforeend'){
627
- if (pos==="afterbegin") this.childNodes.unshift(newElement);
628
- else if (pos==="beforeend") this.childNodes.push(newElement);
629
- newElement.parent=this
630
- return newElement
631
- }
632
- if (!this.parent) throw new Error("Can't insert element to element without parent")
633
- if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
634
- else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
635
- newElement.parent=this.parent
636
- return newElement
637
- }
638
- insertAdjacentHTML(position,html){
639
- const newNode=parseHTML(html);
640
- newNode.childNodes.forEach(node=>{
641
- this.insertAdjacentElement(position,node);
642
- });
643
- return newNode
644
- }
645
- insertAdjacentText(position,text){
646
- return this.insertAdjacentElement(position,new TextNode(text));
647
- }
648
- insert(position,element){
649
- const positions=['beforebegin','afterbegin','beforeend','afterend']
650
- if (positions[position]) position=positions[position]
651
- if (typeof element==='string'){
652
- element=element.trim()
653
- if (element.startsWith('<') && element.endsWith('>')){
654
- return this.insertAdjacentHTML(position,element)
655
- }
656
- return this.insertAdjacentText(position,element)
657
- }
658
- return this.insertAdjacentElement(position,element)
659
- }
660
- set innerHTML(html){
661
- this.childNodes=html.trim().startsWith('<') ? parseHTML(html).childNodes : [html]
662
- this.children.forEach(child=>child.parent=this);
663
- }
664
- set outerHTML(html){
665
- const parsed=parseHTML(html);
666
- if (!this.parent) throw new Error('element has no parent node')
667
- const index=this.childIndex
668
- if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
669
- }
670
- appendChild(newChild){
671
- if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
672
- if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
673
- } else if (typeof newChild==='string') newChild=new TextNode(newChild)
674
- else return newChild
675
- this.childNodes.push(newChild);
676
- newChild.parent=this;
677
- return newChild;
678
- }
679
- get textContent(){
680
- if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
681
- return this.childNodes.map(child=>{
682
- if (child instanceof SingleNode) return ''
683
- if (child instanceof TextNode) return child.nodeValue
684
- if (child instanceof Node) return child.textContent;
685
- else return child;
686
- }).join('');
687
- }
688
- set textContent(value){
689
- this.childNodes=[];
690
- if (value!==null && value!==undefined){
691
- this.childNodes.push(value.toString());
692
- }
693
- }
694
- }
695
- class SingleNode extends Node{
696
- constructor(tagName,attributes={},parent=null){
697
- if(attributes['?'] && tagName==='?xml') delete attributes['?']
698
- super(tagName,attributes,parent);
699
- this.isSingle=true
700
- }
701
- get outerHTML(){
702
- if (this._tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
703
- const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
704
- return `<${this._tagName} ${attrs}${this._tagName==='?xml' ? '?' : ''}>`;
705
- }
706
- get innerHTML(){ return ""; }
707
- set innerHTML(_){ }
708
- $(_){return null}
709
- $$(_){return []}
710
- querySelectorAll(_){ return []; }
711
- querySelector(_){ return null; }
712
- getElementsByClassName(_){ return []; }
713
- getElementsByTagName(_){ return []; }
714
- getElementById(_){ return null; }
715
- get children(){ return []; }
716
- insertAdjacentElement(position,newElement){
717
- if(position==='afterbegin') position='beforebegin'
718
- if(position==='beforeend') position='afterend'
719
- super.insertAdjacentElement(position,newElement)
720
- }
721
- insertAdjacentHTML(position,html){
722
- if(position==='afterbegin') position='beforebegin'
723
- if(position==='beforeend') position='afterend'
724
- super.insertAdjacentHTML(position,html)
725
- }
726
- insertAdjacentText(position,text){
727
- if(position==='afterbegin') position='beforebegin'
728
- if(position==='beforeend') position='afterend'
729
- super.insertAdjacentText(position,text)
730
- }
731
- insert(position,element){
732
- if(position===1) position=0
733
- if(position===2) position=3
734
- super.insert(position,element)
735
- }
736
- appendChild(_){ }
737
- get textContent(){ return ""; }
738
- set textContent(_){ }
739
- }
740
-
741
- class Root extends Node{
742
- constructor(){
743
- super('ROOT',{},null);
744
- this.isSingle=false
745
- }
746
- }
747
- class Document extends Node{
748
- constructor(html,url){
749
- super('ROOT',{},null);
750
- this.isSingle=false
751
- this.URL=url
752
- if(html instanceof Document) this.childNodes=[...buildFromCache(cacheDoc(html)).childNodes]
753
- else if(typeof html==='string') this.innerHTML=html
754
- else this.html
755
- }
756
- get documentElement(){return this.html}
757
- get html(){
758
- const html=this.$('html')
759
- if(html===null) this.innerHTML=/*html*/`<html lang="en"><head><meta charset="UTF-8"><title></title></head><body></body></html>`
760
- return this.$('html')
761
- }
762
- get innerHTML(){
763
- const inner=super.innerHTML
764
- return '<!DOCTYPE html>'+inner
765
- }
766
- set innerHTML(html){return super.innerHTML=html}
767
- get head(){
768
- const head=this.html.$('head')
769
- if(head===null) this.html.insert(1,'<head></head>')
770
- return this.$('head')
771
- }
772
- get body(){
773
- const body=this.html.$('body')
774
- if(body===null) this.head.insert(3,'<body></body>')
775
- return this.$('body')
776
- }
777
- get title(){
778
- const title=this.head.$('title')
779
- if(title===null) this.head.insert(1,'<title></title>')
780
- return this.$('title').innerText
781
- }
782
- get charset(){return this.$('meta[charset]')}
783
- set title(title){return this.head.$('title').innerText=title}
784
- get clone(){return new Document(this)}
785
- }
786
- function parseAttributes(str){
787
- const attrs={};
788
- let key="";
789
- let value="";
790
- let isKey=true;
791
- let quoteChar=null;
792
- for (let i=0; i< str.length; i++){
793
- const char=str[i];
794
- if (isKey && (char==='=' || char===' ')){
795
- if (char==='=') isKey=false;
796
- else if (key.trim()){
797
- attrs[key.trim()]=true;
798
- key="";
799
- }
800
- continue;
801
- }
802
- if (!quoteChar && (char==='"' || char==="'")){
803
- quoteChar=char;
804
- continue;
805
- } else if (quoteChar && char===quoteChar){
806
- quoteChar=null;
807
- attrs[key.trim()]=value.trim();
808
- key=""; value=""; isKey=true;
809
- continue;
810
- }
811
- if (isKey) key+=char;
812
- else value+=char;
813
- }
814
- if (key.trim() &&!value) attrs[key.trim()]='';
815
- return attrs;
816
- }
817
- const VOID_TAGS=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","!doctype",'?xml']);
818
- function parseHTML(html){
819
- const root=new Root();
820
- const stack=[root];
821
- let currentText="",i=0;
822
- let max=0
823
- function parseScript(){
824
- if (!html.startsWith("<script",i)) return false;
825
- const openTagEnd=html.indexOf(">",i);
826
- if (openTagEnd===-1) return false;
827
- const attributesString=html.substring(i+7,openTagEnd).trim();
828
- const attributes=parseAttributes(attributesString);
829
- let closeTagStart=html.indexOf("</script>",openTagEnd);
830
- if (closeTagStart===-1) return false;
831
- const content=html.substring(openTagEnd+1,closeTagStart);
832
- const scriptNode=new Node('script',attributes,stack[stack.length-1]);
833
- if(content.length>0) scriptNode.childNodes.push(content);
834
- i=closeTagStart+9;
835
- return true;
836
- }
837
- function parseSpecial(startStr,endStr,n1,n2,tag){
838
- if (!html.startsWith(startStr,i)) return false
839
- if(currentText.length) stack[stack.length - 1].childNodes.push(new TextNode(currentText)); // new!
840
- const end=html.indexOf(endStr,i+n1);
841
- const strNode=new Node(tag,{},stack[stack.length-1]);
842
- strNode.childNodes.push(html.substring(i+n1,end));
843
- i=end+n2;
844
- return true
845
- }
846
- while (i< html.length){
847
- if(i>=max) max=i;
848
- else break;
849
- if (parseScript()) continue
850
- if (parseSpecial("<!--","-->",4,3,'#comment')) continue
851
- if (parseSpecial("<style","</style>",7,8,'style')) continue
852
- if (html.startsWith("<![CDATA[",i)){
853
- const end=html.indexOf("]]>",i+9);
854
- if (end===-1) break;
855
- const content=html.substring(i+9,end);
856
- const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
857
- cdataNode.nodeValue=content;
858
- i=end+3;
859
- continue;
860
- }
861
- if (html.startsWith("<",i)){
862
- if (currentText && stack[stack.length-1]){
863
- const textNode=new TextNode(currentText)
864
- stack[stack.length-1].childNodes.push(textNode);
865
- textNode.parent=stack[stack.length-1]
866
- currentText="";
867
- }
868
- let tagEnd=i+1;
869
- let insideQuotes=false;
870
- let quoteChar=null;
871
- while (tagEnd< html.length){
872
- const char=html[tagEnd];
873
- if (!insideQuotes && (char==='"' || char==="'")){
874
- insideQuotes=true;
875
- quoteChar=char;
876
- } else if (insideQuotes && char===quoteChar){
877
- insideQuotes=false;
878
- quoteChar=null;
879
- }
880
- if (!insideQuotes && char==='>') break;
881
- tagEnd++;
882
- }
883
- const tagContent=html.substring(i+1,tagEnd);
884
- if (tagContent.startsWith("/")) stack.pop();
885
- else{
886
- let isSelfClosing=tagContent.endsWith('/');
887
- const tagNameEnd=tagContent.search(/\s|>|\//);
888
- const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
889
- const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
890
- const attributes=parseAttributes(attributesString);
891
- if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
892
- else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
893
- }
894
- i=tagEnd+1;
895
- } else{
896
- currentText+=html[i];
897
- i++;
898
- }
899
- }
900
- if (currentText.trim() && stack[stack.length-1]) stack[stack.length-1].childNodes.push(new TextNode(currentText));
901
- return root;
902
- }
903
- function buildFromCache(cached){
904
- function buildNode(cache,parent=null){
905
- if(typeof cache==='string') return parent.childNodes.push(cache)
906
- const{isSingle,tagName,attributes,childNodes,textContent}=cache
907
- if(textContent) return parent.childNodes.push(new TextNode(textContent))
908
- if(isSingle && parent) return parent.childNodes.push(new SingleNode(tagName,attributes))
909
- const newDoc=tagName==='ROOT' ? new Root() : new Node(tagName,attributes,parent)
910
- childNodes.forEach(childNode=>{
911
- buildNode(childNode,newDoc)
912
- });
913
- return newDoc
914
- }
915
- return buildNode(cached)
916
- }
917
-
918
- function cacheDoc(doc){
919
- const props=['isSingle','tagName','attributes']
920
- function addToCache(element,cache={}){
921
- if(typeof element==='string') return element
922
- if(element.nodeName==='#text') return{textContent:element.textContent}
923
- props.forEach(prop=>{
924
- if(element[prop]){
925
- cache[prop]=typeof element[prop]==='object' ?{...element[prop]} : element[prop]
926
- }
927
- });
928
- if(!element.childNodes) return cache
929
- cache.childNodes=[]
930
- element.childNodes.forEach(childNode=>{
931
- cache.childNodes.push(addToCache(childNode))
932
- });
933
- return cache
934
- }
935
- return addToCache(doc)
936
- }
220
+ function getDataset(element){
937
221
  return new Proxy(element.attributes,{
938
222
  get: (target,prop)=>{return target[getDataName(prop)]},set: (target,prop,value)=>{target[getDataName(prop)]=value; return true},deleteProperty: (target,prop)=>{
939
223
  const dataAttr=getDataName(prop)
940
224
  if (dataAttr in target){
941
225
  delete target[dataAttr];
942
226
  return true;
943
227
  }
944
228
  return false;
945
229
  }
946
230
  });
231
+ }
232
+
233
+ function find(selectors,element,collection,first=false,firstTime=true){
947
234
  if(!firstTime)
948
235
  for(let selector of selectors){
949
236
  if(checkElement(element,selector)) collection.add(element)
950
237
  }
951
238
  if(element.children)
952
239
  element.children.forEach(child=>{
953
240
  if(first && collection.size>0) return
954
241
  find(selectors,child,collection,first,false)
955
242
  })
956
243
  return firstTime ? [...collection] : collection
244
+ }
245
+ class TextNode{
957
246
  constructor(data){
958
247
  this.nodeName='#text';
959
248
  this.parent=null;
960
249
  this.textContent=data;
961
250
  }
962
251
  get nodeValue(){return this.textContent}
963
252
  get parentNode(){return this.parent}
253
+ }
254
+
255
+ function buildStyle(attributes){
964
256
  const styles=attributes.style || "";
965
257
  const camelToKebab=str=>str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,'$1-$2').toLowerCase();
966
258
  const kebabToCamel=str=>str.replace(/-([a-z])/g,g=>g[1].toUpperCase());
967
259
  const baseStyleObj=styles.split(";").reduce((acc,style)=>{
968
260
  const [key,value]=style.split(":").map(s=>s.trim());
969
261
  if (key && value) acc[kebabToCamel(key)]=value;
970
262
  return acc;
971
263
  },{});
972
264
  return new Proxy(baseStyleObj,{
973
265
  get: (obj,prop)=>obj[camelToKebab(prop)] || obj[prop],set: (obj,prop,value)=>{
974
266
  obj[camelToKebab(prop)]=value;
975
267
  attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
976
268
  return true;
977
269
  },deleteProperty: (obj,prop)=>{
978
270
  delete obj[camelToKebab(prop)];
979
271
  attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
980
272
  return true;
981
273
  }
982
274
  });
275
+ }
276
+
277
+ class NodeClassList{
983
278
  constructor(node){ this.node=node }
984
279
  get classes(){ return (this.node.attributes.class || "").split(" ").filter(Boolean) }
985
280
  set classes(val){ this.node.attributes.class=val.join(" ") }
986
281
  contains(className){ return this.classes.includes(className) }
987
282
  add(className){
988
283
  const currentClasses=this.classes;
989
284
  if (!currentClasses.includes(className)) this.classes=[...currentClasses,className];
990
285
  }
991
286
  remove(className){ this.classes=this.classes.filter(cls=>cls!==className); }
992
287
  toggle(className){
993
288
  if (this.classes.includes(className)) this.remove(className);
994
289
  else this.add(className);
995
290
  }
996
291
  replace(oldClass,newClass){
997
292
  if (this.classes.includes(oldClass)){
998
293
  this.remove(oldClass);
999
294
  this.add(newClass);
1000
295
  }
1001
296
  }
297
+ }
298
+ function insertBefore(arr,index,newItem){
1002
299
  const existingIndex=arr.indexOf(newItem);
1003
300
  if (existingIndex!==-1) arr.splice(existingIndex,1);
1004
301
  arr.splice(index,0,newItem);
302
+ }
1005
303
  class Node{
1006
304
  constructor(tagName,attributes={},parent=null){
1007
305
  this.isSingle=false;
1008
306
  this._tagName=tagName.toLowerCase();
1009
307
  this.tagName=tagName.toUpperCase();
1010
308
  this.attributes=attributes;
1011
309
  this.childNodes=[];
1012
310
  if (parent!==null) parent.childNodes.push(this)
1013
311
  this.parent=parent;
1014
312
  this._classList=null;
1015
313
  this.__style=null;
1016
314
  this._dataset=null
1017
315
  }
1018
316
  get id(){ return this.attributes.id ? this.attributes.id : null; }
1019
317
  set id(newValue){ this.attributes.id=newValue; }
1020
318
  get className(){ return this.attributes.class || null }
1021
319
  get parentNode(){ return this.parent }
1022
320
  get ancestors(){
1023
321
  if (!this.parent) return []
1024
322
  const ancestors=[]
1025
323
  let element=this.parent
1026
324
  while (element.tagName!=='ROOT'){
1027
325
  ancestors.push(element)
1028
326
  element=element.parent
1029
327
  }
1030
328
  return ancestors.reverse()
1031
329
  }
1032
330
  get childNodeIndex(){
1033
331
  if (!this.parent) return null
1034
332
  return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
1035
333
  }
1036
334
  get childIndex(){
1037
335
  if (!this.parent) return null
1038
336
  return this.parent.children ? this.parent.children.indexOf(this) : null
1039
337
  }
1040
338
  get previousElementSibling(){ return this.prev }
1041
339
  get prev(){
1042
340
  if (this.childIndex===null) return null
1043
341
  return this.parent.children[this.childIndex-1]
1044
342
  }
1045
343
  get nextElementSibling(){ return this.next }
1046
344
  get next(){
1047
345
  if (this.childIndex===null) return null
1048
346
  return this.parent.children[this.childIndex+1] || null
1049
347
  }
1050
348
  get dataset(){
1051
349
  if (!this._dataset) this._dataset=getDataset(this);
1052
350
  return this._dataset;
1053
351
  }
1054
352
  get classList(){
1055
353
  if (!this._classList) this._classList=new NodeClassList(this);
1056
354
  return this._classList;
1057
355
  }
1058
356
  get style(){
1059
357
  if (!this.__style) this.__style=buildStyle(this.attributes)
1060
358
  return this.__style
1061
359
  }
1062
360
  get outerHTML(){
1063
361
  const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
1064
362
  return `<${this._tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this._tagName}>`;
1065
363
  }
1066
364
  getAttribute(attrName){ return this.attributes[attrName]!==undefined ? this.attributes[attrName] : null }
1067
365
  setAttribute(attrName,value=''){ this.attributes[attrName]=value }
1068
366
  removeAttribute(attrName){ delete this.attributes[attrName] }
1069
367
  remove(){
1070
368
  if (!this.parent) return
1071
369
  const index=this.childNodeIndex;
1072
370
  if (index!==null) this.parent.childNodes.splice(index,1);
1073
371
  }
1074
372
  get innerHTML(){
1075
373
  return this.childNodes.map(child=>{
1076
374
  if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
1077
375
  else if (child instanceof TextNode) return child.textContent;
1078
376
  else return child
1079
377
  }).join("");
1080
378
  }
1081
379
  get innerText(){
1082
380
  return this.childNodes.map(child=>{
1083
381
  if (child instanceof Node || child instanceof SingleNode) return child.innerText;
1084
382
  else if (child instanceof TextNode) return child.textContent;
1085
383
  else return child
1086
384
  }).join("");
1087
385
  }
1088
386
  set innerText(value){
1089
387
  this.childNodes=[new TextNode(value)]
1090
388
  return value
1091
389
  }
1092
390
  $$(query){ return this.querySelectorAll(query) }
1093
391
  querySelectorAll(query){
1094
392
  const selectors=Query.get(query)
1095
393
  return find(selectors,this,new Set())
1096
394
  }
1097
395
  $(query){ return this.querySelector(query) }
1098
396
  querySelector(query){
1099
397
  const selectors=Query.get(query)
1100
398
  return find(selectors,this,new Set(),true)[0] || null
1101
399
  }
1102
400
  getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
1103
401
  getElementsByTagName(query){ return this.querySelectorAll(query) }
1104
402
  getElementById(query){ return this.querySelector('#'+query) }
1105
403
  get children(){
1106
404
  return this.childNodes.filter(child=>{
1107
405
  if (!(child instanceof Node)) return false
1108
406
  if (child._tagName==='#comment') return false
1109
407
  return true
1110
408
  });
1111
409
  }
1112
410
  insertAdjacentElement(position,newElement){
1113
411
  if (newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
1114
412
  const pos=position.toLowerCase();
1115
413
  if(newElement.parentNode){
1116
414
  newElement.parentNode.childNodes=newElement.parentNode.childNodes.filter(el=>el!==newElement)
1117
415
  }
1118
416
  if (pos==='afterbegin' || pos==='beforeend'){
1119
417
  if (pos==="afterbegin") this.childNodes.unshift(newElement);
1120
418
  else if (pos==="beforeend") this.childNodes.push(newElement);
1121
419
  newElement.parent=this
1122
420
  return newElement
1123
421
  }
1124
422
  if (!this.parent) throw new Error("Can't insert element to element without parent")
1125
423
  if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
1126
424
  else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
1127
425
  newElement.parent=this.parent
1128
426
  return newElement
1129
427
  }
1130
428
  insertAdjacentHTML(position,html){
1131
429
  const newNode=parseHTML(html);
1132
430
  newNode.childNodes.forEach(node=>{
1133
431
  this.insertAdjacentElement(position,node);
1134
432
  });
1135
433
  return newNode
1136
434
  }
1137
435
  insertAdjacentText(position,text){
1138
436
  return this.insertAdjacentElement(position,new TextNode(text));
1139
437
  }
1140
438
  insert(position,element){
1141
439
  const positions=['beforebegin','afterbegin','beforeend','afterend']
1142
440
  if (positions[position]) position=positions[position]
1143
441
  if (typeof element==='string'){
1144
442
  element=element.trim()
1145
443
  if (element.startsWith('<') && element.endsWith('>')){
1146
444
  return this.insertAdjacentHTML(position,element)
1147
445
  }
1148
446
  return this.insertAdjacentText(position,element)
1149
447
  }
1150
448
  return this.insertAdjacentElement(position,element)
1151
449
  }
1152
450
  set innerHTML(html){
1153
451
  this.childNodes=html.trim().startsWith('<') ? parseHTML(html).childNodes : [html]
1154
452
  this.children.forEach(child=>child.parent=this);
1155
453
  }
1156
454
  set outerHTML(html){
1157
455
  const parsed=parseHTML(html);
1158
456
  if (!this.parent) throw new Error('element has no parent node')
1159
457
  const index=this.childIndex
1160
458
  if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
1161
459
  }
1162
460
  appendChild(newChild){
1163
461
  if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
1164
462
  if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
1165
463
  } else if (typeof newChild==='string') newChild=new TextNode(newChild)
1166
464
  else return newChild
1167
465
  this.childNodes.push(newChild);
1168
466
  newChild.parent=this;
1169
467
  return newChild;
1170
468
  }
1171
469
  get textContent(){
1172
470
  if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
1173
471
  return this.childNodes.map(child=>{
1174
472
  if (child instanceof SingleNode) return ''
1175
473
  if (child instanceof TextNode) return child.nodeValue
1176
474
  if (child instanceof Node) return child.textContent;
1177
475
  else return child;
1178
476
  }).join('');
1179
477
  }
1180
478
  set textContent(value){
1181
479
  this.childNodes=[];
1182
480
  if (value!==null && value!==undefined){
1183
481
  this.childNodes.push(value.toString());
1184
482
  }
1185
483
  }
484
+ }
485
+ class SingleNode extends Node{
1186
486
  constructor(tagName,attributes={},parent=null){
1187
487
  if(attributes['?'] && tagName==='?xml') delete attributes['?']
1188
488
  super(tagName,attributes,parent);
1189
489
  this.isSingle=true
1190
490
  }
1191
491
  get outerHTML(){
1192
492
  if (this._tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
1193
493
  const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
1194
494
  return `<${this._tagName} ${attrs}${this._tagName==='?xml' ? '?' : ''}>`;
1195
495
  }
1196
496
  get innerHTML(){ return ""; }
1197
497
  set innerHTML(_){ }
1198
498
  $(_){return null}
1199
499
  $$(_){return []}
1200
500
  querySelectorAll(_){ return []; }
1201
501
  querySelector(_){ return null; }
1202
502
  getElementsByClassName(_){ return []; }
1203
503
  getElementsByTagName(_){ return []; }
1204
504
  getElementById(_){ return null; }
1205
505
  get children(){ return []; }
1206
506
  insertAdjacentElement(position,newElement){
1207
507
  if(position==='afterbegin') position='beforebegin'
1208
508
  if(position==='beforeend') position='afterend'
1209
509
  return super.insertAdjacentElement(position,newElement)
1210
510
  }
1211
511
  insertAdjacentHTML(position,html){
1212
512
  if(position==='afterbegin') position='beforebegin'
1213
513
  if(position==='beforeend') position='afterend'
1214
514
  return super.insertAdjacentHTML(position,html)
1215
515
  }
1216
516
  insertAdjacentText(position,text){
1217
517
  if(position==='afterbegin') position='beforebegin'
1218
518
  if(position==='beforeend') position='afterend'
1219
519
  return super.insertAdjacentText(position,text)
1220
520
  }
1221
521
  insert(position,element){
1222
522
  if(position===1) position=0
1223
523
  if(position===2) position=3
1224
524
  return super.insert(position,element)
1225
525
  }
1226
526
  appendChild(_){ }
1227
527
  get textContent(){ return ""; }
1228
528
  set textContent(_){ }
529
+ }
530
+
531
+ class Root extends Node{
1229
532
  constructor(){
1230
533
  super('ROOT',{},null);
1231
534
  this.isSingle=false
1232
535
  }
536
+ }
537
+ class Document extends Node{
1233
538
  constructor(html,url){
1234
539
  super('ROOT',{},null);
1235
540
  this.isSingle=false
1236
541
  this.URL=url
1237
542
  if(html instanceof Document) this.childNodes=[...buildFromCache(cacheDoc(html)).childNodes]
1238
543
  else if(typeof html==='string') this.innerHTML=html
1239
544
  else this.html
1240
545
  }
1241
546
  get documentElement(){return this.html}
1242
547
  get html(){
1243
548
  const html=this.$('html')
1244
549
  if(html===null) this.innerHTML=/*html*/`<html lang="en"><head><meta charset="UTF-8"><title></title></head><body></body></html>`
1245
550
  return this.$('html')
1246
551
  }
1247
552
  get innerHTML(){
1248
553
  const inner=super.innerHTML
1249
554
  return '<!DOCTYPE html>'+inner
1250
555
  }
1251
556
  set innerHTML(html){return super.innerHTML=html}
1252
557
  get head(){
1253
558
  const head=this.html.$('head')
1254
559
  if(head===null) this.html.insert(1,'<head></head>')
1255
560
  return this.$('head')
1256
561
  }
1257
562
  get body(){
1258
563
  const body=this.html.$('body')
1259
564
  if(body===null) this.head.insert(3,'<body></body>')
1260
565
  return this.$('body')
1261
566
  }
1262
567
  get title(){
1263
568
  const title=this.head.$('title')
1264
569
  if(title===null) this.head.insert(1,'<title></title>')
1265
570
  return this.$('title').innerText
1266
571
  }
1267
572
  get charset(){return this.$('meta[charset]')}
1268
573
  set title(title){return this.head.$('title').innerText=title}
1269
574
  get clone(){return new Document(this)}
575
+ }
576
+ function parseAttributes(str){
1270
577
  const attrs={};
1271
578
  let key="";
1272
579
  let value="";
1273
580
  let isKey=true;
1274
581
  let quoteChar=null;
1275
582
  for (let i=0; i< str.length; i++){
1276
583
  const char=str[i];
1277
584
  if (isKey && (char==='=' || char===' ')){
1278
585
  if (char==='=') isKey=false;
1279
586
  else if (key.trim()){
1280
587
  attrs[key.trim()]=true;
1281
588
  key="";
1282
589
  }
1283
590
  continue;
1284
591
  }
1285
592
  if (!quoteChar && (char==='"' || char==="'")){
1286
593
  quoteChar=char;
1287
594
  continue;
1288
595
  } else if (quoteChar && char===quoteChar){
1289
596
  quoteChar=null;
1290
597
  attrs[key.trim()]=value.trim();
1291
598
  key=""; value=""; isKey=true;
1292
599
  continue;
1293
600
  }
1294
601
  if (isKey) key+=char;
1295
602
  else value+=char;
1296
603
  }
1297
604
  if (key.trim() &&!value) attrs[key.trim()]='';
1298
605
  return attrs;
606
+ }
607
+ const VOID_TAGS=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","!doctype",'?xml']);
608
+ function parseHTML(html){
1299
609
  const root=new Root();
1300
610
  const stack=[root];
1301
611
  let currentText="",i=0;
1302
612
  let max=0
1303
613
  function parseScript(){
1304
614
  if (!html.startsWith("<script",i)) return false;
1305
615
  const openTagEnd=html.indexOf(">",i);
1306
616
  if (openTagEnd===-1) return false;
1307
617
  const attributesString=html.substring(i+7,openTagEnd).trim();
1308
618
  const attributes=parseAttributes(attributesString);
1309
619
  let closeTagStart=html.indexOf("</script>",openTagEnd);
1310
620
  if (closeTagStart===-1) return false;
1311
621
  const content=html.substring(openTagEnd+1,closeTagStart);
1312
622
  const scriptNode=new Node('script',attributes,stack[stack.length-1]);
1313
623
  if(content.length>0) scriptNode.childNodes.push(content);
1314
624
  i=closeTagStart+9;
1315
625
  return true;
1316
626
  }
1317
627
  function parseSpecial(startStr,endStr,n1,n2,tag){
1318
628
  if (!html.startsWith(startStr,i)) return false
1319
629
  if(currentText.length) stack[stack.length-1].childNodes.push(new TextNode(currentText));
1320
630
  const end=html.indexOf(endStr,i+n1);
1321
631
  const strNode=new Node(tag,{},stack[stack.length-1]);
1322
632
  strNode.childNodes.push(html.substring(i+n1,end));
1323
633
  i=end+n2;
1324
634
  return true
1325
635
  }
1326
636
  while (i< html.length){
1327
637
  if(i>=max) max=i;
1328
638
  else break;
1329
639
  if (parseScript()) continue
1330
640
  if (parseSpecial("<!--","-->",4,3,'#comment')) continue
1331
641
  if (parseSpecial("<style","</style>",7,8,'style')) continue
1332
642
  if (html.startsWith("<![CDATA[",i)){
1333
643
  const end=html.indexOf("]]>",i+9);
1334
644
  if (end===-1) break;
1335
645
  const content=html.substring(i+9,end);
1336
646
  const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
1337
647
  cdataNode.nodeValue=content;
1338
648
  i=end+3;
1339
649
  continue;
1340
650
  }
1341
651
  if (html.startsWith("<",i)){
1342
652
  if (currentText && stack[stack.length-1]){
1343
653
  const textNode=new TextNode(currentText)
1344
654
  stack[stack.length-1].childNodes.push(textNode);
1345
655
  textNode.parent=stack[stack.length-1]
1346
656
  currentText="";
1347
657
  }
1348
658
  let tagEnd=i+1;
1349
659
  let insideQuotes=false;
1350
660
  let quoteChar=null;
1351
661
  while (tagEnd< html.length){
1352
662
  const char=html[tagEnd];
1353
663
  if (!insideQuotes && (char==='"' || char==="'")){
1354
664
  insideQuotes=true;
1355
665
  quoteChar=char;
1356
666
  } else if (insideQuotes && char===quoteChar){
1357
667
  insideQuotes=false;
1358
668
  quoteChar=null;
1359
669
  }
1360
670
  if (!insideQuotes && char==='>') break;
1361
671
  tagEnd++;
1362
672
  }
1363
673
  const tagContent=html.substring(i+1,tagEnd);
1364
674
  if (tagContent.startsWith("/")) stack.pop();
1365
675
  else{
1366
676
  let isSelfClosing=tagContent.endsWith('/');
1367
677
  const tagNameEnd=tagContent.search(/\s|>|\//);
1368
678
  const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
1369
679
  const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
1370
680
  const attributes=parseAttributes(attributesString);
1371
681
  if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
1372
682
  else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
1373
683
  }
1374
684
  i=tagEnd+1;
1375
685
  } else{
1376
686
  currentText+=html[i];
1377
687
  i++;
1378
688
  }
1379
689
  }
1380
690
  if (currentText.trim() && stack[stack.length-1]) stack[stack.length-1].childNodes.push(new TextNode(currentText));
1381
691
  return root;
692
+ }
693
+ function buildFromCache(cached){
1382
694
  function buildNode(cache,parent=null){
1383
695
  if(typeof cache==='string') return parent.childNodes.push(cache)
1384
696
  const{isSingle,tagName,attributes,childNodes,textContent}=cache
1385
697
  if(textContent) return parent.childNodes.push(new TextNode(textContent))
1386
698
  if(isSingle && parent) return parent.childNodes.push(new SingleNode(tagName,attributes))
1387
699
  const newDoc=tagName==='ROOT' ? new Root() : new Node(tagName,attributes,parent)
1388
700
  childNodes.forEach(childNode=>{
1389
701
  buildNode(childNode,newDoc)
1390
702
  });
1391
703
  return newDoc
1392
704
  }
1393
705
  return buildNode(cached)
706
+ }
1394
707
 
708
+ function cacheDoc(doc){
1395
709
  const props=['isSingle','tagName','attributes']
1396
710
  function addToCache(element,cache={}){
1397
711
  if(typeof element==='string') return element
1398
712
  if(element.nodeName==='#text') return{textContent:element.textContent}
1399
713
  props.forEach(prop=>{
1400
714
  if(element[prop]){
1401
715
  cache[prop]=typeof element[prop]==='object' ?{...element[prop]} : element[prop]
1402
716
  }
1403
717
  });
1404
718
  if(!element.childNodes) return cache
1405
719
  cache.childNodes=[]
1406
720
  element.childNodes.forEach(childNode=>{
1407
721
  cache.childNodes.push(addToCache(childNode))
1408
722
  });
1409
723
  return cache
1410
724
  }
1411
725
  return addToCache(doc)
726
+ }
1412
727
  export default { parseHTML, Node, Query, TextNode, SingleNode, buildFromCache, cacheDoc, Root, Document }