als-document 1.4.1 → 1.4.3

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,43 @@
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
- }
937
- export default { parseHTML, Node, Query, TextNode, SingleNode, buildFromCache, cacheDoc, Root, Document }
220
+ function getDataset(element){
938
221
  return new Proxy(element.attributes,{
939
222
  get: (target,prop)=>{return target[getDataName(prop)]},set: (target,prop,value)=>{target[getDataName(prop)]=value; return true},deleteProperty: (target,prop)=>{
940
223
  const dataAttr=getDataName(prop)
941
224
  if (dataAttr in target){
942
225
  delete target[dataAttr];
943
226
  return true;
944
227
  }
945
228
  return false;
946
229
  }
947
230
  });
231
+ }
232
+
233
+ function find(selectors,element,collection,first=false,firstTime=true){
948
234
  if(!firstTime)
949
235
  for(let selector of selectors){
950
236
  if(checkElement(element,selector)) collection.add(element)
951
237
  }
952
238
  if(element.children)
953
239
  element.children.forEach(child=>{
954
240
  if(first && collection.size>0) return
955
241
  find(selectors,child,collection,first,false)
956
242
  })
957
243
  return firstTime ? [...collection] : collection
244
+ }
245
+ class TextNode{
958
246
  constructor(data){
959
247
  this.nodeName='#text';
960
248
  this.parent=null;
961
249
  this.textContent=data;
962
250
  }
963
251
  get nodeValue(){return this.textContent}
964
252
  get parentNode(){return this.parent}
253
+ }
254
+
255
+ function buildStyle(attributes){
965
256
  const styles=attributes.style || "";
966
257
  const camelToKebab=str=>str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,'$1-$2').toLowerCase();
967
258
  const kebabToCamel=str=>str.replace(/-([a-z])/g,g=>g[1].toUpperCase());
968
259
  const baseStyleObj=styles.split(";").reduce((acc,style)=>{
969
260
  const [key,value]=style.split(":").map(s=>s.trim());
970
261
  if (key && value) acc[kebabToCamel(key)]=value;
971
262
  return acc;
972
263
  },{});
973
264
  return new Proxy(baseStyleObj,{
974
265
  get: (obj,prop)=>obj[camelToKebab(prop)] || obj[prop],set: (obj,prop,value)=>{
975
266
  obj[camelToKebab(prop)]=value;
976
267
  attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
977
268
  return true;
978
269
  },deleteProperty: (obj,prop)=>{
979
270
  delete obj[camelToKebab(prop)];
980
271
  attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
981
272
  return true;
982
273
  }
983
274
  });
275
+ }
276
+
277
+ class NodeClassList{
984
278
  constructor(node){ this.node=node }
985
279
  get classes(){ return (this.node.attributes.class || "").split(" ").filter(Boolean) }
986
280
  set classes(val){ this.node.attributes.class=val.join(" ") }
987
281
  contains(className){ return this.classes.includes(className) }
988
282
  add(className){
989
283
  const currentClasses=this.classes;
990
284
  if (!currentClasses.includes(className)) this.classes=[...currentClasses,className];
991
285
  }
992
286
  remove(className){ this.classes=this.classes.filter(cls=>cls!==className); }
993
287
  toggle(className){
994
288
  if (this.classes.includes(className)) this.remove(className);
995
289
  else this.add(className);
996
290
  }
997
291
  replace(oldClass,newClass){
998
292
  if (this.classes.includes(oldClass)){
999
293
  this.remove(oldClass);
1000
294
  this.add(newClass);
1001
295
  }
1002
296
  }
297
+ }
298
+ function insertBefore(arr,index,newItem){
1003
299
  const existingIndex=arr.indexOf(newItem);
1004
300
  if (existingIndex!==-1) arr.splice(existingIndex,1);
1005
301
  arr.splice(index,0,newItem);
302
+ }
1006
303
  class Node{
1007
304
  constructor(tagName,attributes={},parent=null){
1008
305
  this.isSingle=false;
1009
306
  this._tagName=tagName.toLowerCase();
1010
307
  this.tagName=tagName.toUpperCase();
1011
308
  this.attributes=attributes;
1012
309
  this.childNodes=[];
1013
310
  if (parent!==null) parent.childNodes.push(this)
1014
311
  this.parent=parent;
1015
312
  this._classList=null;
1016
313
  this.__style=null;
1017
314
  this._dataset=null
1018
315
  }
1019
316
  get id(){ return this.attributes.id ? this.attributes.id : null; }
1020
317
  set id(newValue){ this.attributes.id=newValue; }
1021
318
  get className(){ return this.attributes.class || null }
1022
319
  get parentNode(){ return this.parent }
1023
320
  get ancestors(){
1024
321
  if (!this.parent) return []
1025
322
  const ancestors=[]
1026
323
  let element=this.parent
1027
324
  while (element.tagName!=='ROOT'){
1028
325
  ancestors.push(element)
1029
326
  element=element.parent
1030
327
  }
1031
328
  return ancestors.reverse()
1032
329
  }
1033
330
  get childNodeIndex(){
1034
331
  if (!this.parent) return null
1035
332
  return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
1036
333
  }
1037
334
  get childIndex(){
1038
335
  if (!this.parent) return null
1039
336
  return this.parent.children ? this.parent.children.indexOf(this) : null
1040
337
  }
1041
338
  get previousElementSibling(){ return this.prev }
1042
339
  get prev(){
1043
340
  if (this.childIndex===null) return null
1044
341
  return this.parent.children[this.childIndex-1]
1045
342
  }
1046
343
  get nextElementSibling(){ return this.next }
1047
344
  get next(){
1048
345
  if (this.childIndex===null) return null
1049
346
  return this.parent.children[this.childIndex+1] || null
1050
347
  }
1051
348
  get dataset(){
1052
349
  if (!this._dataset) this._dataset=getDataset(this);
1053
350
  return this._dataset;
1054
351
  }
1055
352
  get classList(){
1056
353
  if (!this._classList) this._classList=new NodeClassList(this);
1057
354
  return this._classList;
1058
355
  }
1059
356
  get style(){
1060
357
  if (!this.__style) this.__style=buildStyle(this.attributes)
1061
358
  return this.__style
1062
359
  }
1063
360
  get outerHTML(){
1064
361
  const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
1065
362
  return `<${this._tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this._tagName}>`;
1066
363
  }
1067
364
  getAttribute(attrName){ return this.attributes[attrName]!==undefined ? this.attributes[attrName] : null }
1068
365
  setAttribute(attrName,value=''){ this.attributes[attrName]=value }
1069
366
  removeAttribute(attrName){ delete this.attributes[attrName] }
1070
367
  remove(){
1071
368
  if (!this.parent) return
1072
369
  const index=this.childNodeIndex;
1073
370
  if (index!==null) this.parent.childNodes.splice(index,1);
1074
371
  }
1075
372
  get innerHTML(){
1076
373
  return this.childNodes.map(child=>{
1077
374
  if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
1078
375
  else if (child instanceof TextNode) return child.textContent;
1079
376
  else return child
1080
377
  }).join("");
1081
378
  }
1082
379
  get innerText(){
1083
380
  return this.childNodes.map(child=>{
1084
381
  if (child instanceof Node || child instanceof SingleNode) return child.innerText;
1085
382
  else if (child instanceof TextNode) return child.textContent;
1086
383
  else return child
1087
384
  }).join("");
1088
385
  }
1089
386
  set innerText(value){
1090
387
  this.childNodes=[new TextNode(value)]
1091
388
  return value
1092
389
  }
1093
390
  $$(query){ return this.querySelectorAll(query) }
1094
391
  querySelectorAll(query){
1095
392
  const selectors=Query.get(query)
1096
393
  return find(selectors,this,new Set())
1097
394
  }
1098
395
  $(query){ return this.querySelector(query) }
1099
396
  querySelector(query){
1100
397
  const selectors=Query.get(query)
1101
398
  return find(selectors,this,new Set(),true)[0] || null
1102
399
  }
1103
400
  getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
1104
401
  getElementsByTagName(query){ return this.querySelectorAll(query) }
1105
402
  getElementById(query){ return this.querySelector('#'+query) }
1106
403
  get children(){
1107
404
  return this.childNodes.filter(child=>{
1108
405
  if (!(child instanceof Node)) return false
1109
406
  if (child._tagName==='#comment') return false
1110
407
  return true
1111
408
  });
1112
409
  }
1113
410
  insertAdjacentElement(position,newElement){
1114
411
  if (newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
1115
412
  const pos=position.toLowerCase();
1116
413
  if(newElement.parentNode){
1117
414
  newElement.parentNode.childNodes=newElement.parentNode.childNodes.filter(el=>el!==newElement)
1118
415
  }
1119
416
  if (pos==='afterbegin' || pos==='beforeend'){
1120
417
  if (pos==="afterbegin") this.childNodes.unshift(newElement);
1121
418
  else if (pos==="beforeend") this.childNodes.push(newElement);
1122
419
  newElement.parent=this
1123
420
  return newElement
1124
421
  }
1125
422
  if (!this.parent) throw new Error("Can't insert element to element without parent")
1126
423
  if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
1127
424
  else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
1128
425
  newElement.parent=this.parent
1129
426
  return newElement
1130
427
  }
1131
428
  insertAdjacentHTML(position,html){
1132
429
  const newNode=parseHTML(html);
1133
430
  newNode.childNodes.forEach(node=>{
1134
431
  this.insertAdjacentElement(position,node);
1135
432
  });
1136
433
  return newNode
1137
434
  }
1138
435
  insertAdjacentText(position,text){
1139
436
  return this.insertAdjacentElement(position,new TextNode(text));
1140
437
  }
1141
438
  insert(position,element){
1142
439
  const positions=['beforebegin','afterbegin','beforeend','afterend']
1143
440
  if (positions[position]) position=positions[position]
1144
441
  if (typeof element==='string'){
1145
442
  element=element.trim()
1146
443
  if (element.startsWith('<') && element.endsWith('>')){
1147
444
  return this.insertAdjacentHTML(position,element)
1148
445
  }
1149
446
  return this.insertAdjacentText(position,element)
1150
447
  }
1151
448
  return this.insertAdjacentElement(position,element)
1152
449
  }
1153
450
  set innerHTML(html){
1154
451
  this.childNodes=html.trim().startsWith('<') ? parseHTML(html).childNodes : [html]
1155
452
  this.children.forEach(child=>child.parent=this);
1156
453
  }
1157
454
  set outerHTML(html){
1158
455
  const parsed=parseHTML(html);
1159
456
  if (!this.parent) throw new Error('element has no parent node')
1160
457
  const index=this.childIndex
1161
458
  if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
1162
459
  }
1163
460
  appendChild(newChild){
1164
461
  if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
1165
462
  if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
1166
463
  } else if (typeof newChild==='string') newChild=new TextNode(newChild)
1167
464
  else return newChild
1168
465
  this.childNodes.push(newChild);
1169
466
  newChild.parent=this;
1170
467
  return newChild;
1171
468
  }
1172
469
  get textContent(){
1173
470
  if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
1174
471
  return this.childNodes.map(child=>{
1175
472
  if (child instanceof SingleNode) return ''
1176
473
  if (child instanceof TextNode) return child.nodeValue
1177
474
  if (child instanceof Node) return child.textContent;
1178
475
  else return child;
1179
476
  }).join('');
1180
477
  }
1181
478
  set textContent(value){
1182
479
  this.childNodes=[];
1183
480
  if (value!==null && value!==undefined){
1184
481
  this.childNodes.push(value.toString());
1185
482
  }
1186
483
  }
484
+ }
485
+ class SingleNode extends Node{
1187
486
  constructor(tagName,attributes={},parent=null){
1188
487
  if(attributes['?'] && tagName==='?xml') delete attributes['?']
1189
488
  super(tagName,attributes,parent);
1190
489
  this.isSingle=true
1191
490
  }
1192
491
  get outerHTML(){
1193
492
  if (this._tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
1194
493
  const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
1195
494
  return `<${this._tagName} ${attrs}${this._tagName==='?xml' ? '?' : ''}>`;
1196
495
  }
1197
496
  get innerHTML(){ return ""; }
1198
497
  set innerHTML(_){ }
1199
498
  $(_){return null}
1200
499
  $$(_){return []}
1201
500
  querySelectorAll(_){ return []; }
1202
501
  querySelector(_){ return null; }
1203
502
  getElementsByClassName(_){ return []; }
1204
503
  getElementsByTagName(_){ return []; }
1205
504
  getElementById(_){ return null; }
1206
505
  get children(){ return []; }
1207
506
  insertAdjacentElement(position,newElement){
1208
507
  if(position==='afterbegin') position='beforebegin'
1209
508
  if(position==='beforeend') position='afterend'
1210
509
  return super.insertAdjacentElement(position,newElement)
1211
510
  }
1212
511
  insertAdjacentHTML(position,html){
1213
512
  if(position==='afterbegin') position='beforebegin'
1214
513
  if(position==='beforeend') position='afterend'
1215
514
  return super.insertAdjacentHTML(position,html)
1216
515
  }
1217
516
  insertAdjacentText(position,text){
1218
517
  if(position==='afterbegin') position='beforebegin'
1219
518
  if(position==='beforeend') position='afterend'
1220
519
  return super.insertAdjacentText(position,text)
1221
520
  }
1222
521
  insert(position,element){
1223
522
  if(position===1) position=0
1224
523
  if(position===2) position=3
1225
524
  return super.insert(position,element)
1226
525
  }
1227
526
  appendChild(_){ }
1228
527
  get textContent(){ return ""; }
1229
528
  set textContent(_){ }
529
+ }
530
+
531
+ class Root extends Node{
1230
532
  constructor(){
1231
533
  super('ROOT',{},null);
1232
534
  this.isSingle=false
1233
535
  }
536
+ }
537
+ class Document extends Node{
1234
538
  constructor(html,url){
1235
539
  super('ROOT',{},null);
1236
540
  this.isSingle=false
1237
541
  this.URL=url
1238
542
  if(html instanceof Document) this.childNodes=[...buildFromCache(cacheDoc(html)).childNodes]
1239
543
  else if(typeof html==='string') this.innerHTML=html
1240
544
  else this.html
1241
545
  }
1242
546
  get documentElement(){return this.html}
1243
547
  get html(){
1244
548
  const html=this.$('html')
1245
549
  if(html===null) this.innerHTML=/*html*/`<html lang="en"><head><meta charset="UTF-8"><title></title></head><body></body></html>`
1246
550
  return this.$('html')
1247
551
  }
1248
552
  get innerHTML(){
1249
553
  const inner=super.innerHTML
1250
554
  return '<!DOCTYPE html>'+inner
1251
555
  }
1252
556
  set innerHTML(html){return super.innerHTML=html}
1253
557
  get head(){
1254
558
  const head=this.html.$('head')
1255
559
  if(head===null) this.html.insert(1,'<head></head>')
1256
560
  return this.$('head')
1257
561
  }
1258
562
  get body(){
1259
563
  const body=this.html.$('body')
1260
564
  if(body===null) this.head.insert(3,'<body></body>')
1261
565
  return this.$('body')
1262
566
  }
1263
567
  get title(){
1264
568
  const title=this.head.$('title')
1265
569
  if(title===null) this.head.insert(1,'<title></title>')
1266
570
  return this.$('title').innerText
1267
571
  }
1268
572
  get charset(){return this.$('meta[charset]')}
1269
573
  set title(title){return this.head.$('title').innerText=title}
1270
574
  get clone(){return new Document(this)}
575
+ }
576
+ function parseAttributes(str){
1271
577
  const attrs={};
1272
578
  let key="";
1273
579
  let value="";
1274
580
  let isKey=true;
1275
581
  let quoteChar=null;
1276
582
  for (let i=0; i< str.length; i++){
1277
583
  const char=str[i];
1278
584
  if (isKey && (char==='=' || char===' ')){
1279
585
  if (char==='=') isKey=false;
1280
586
  else if (key.trim()){
1281
587
  attrs[key.trim()]=true;
1282
588
  key="";
1283
589
  }
1284
590
  continue;
1285
591
  }
1286
592
  if (!quoteChar && (char==='"' || char==="'")){
1287
593
  quoteChar=char;
1288
594
  continue;
1289
595
  } else if (quoteChar && char===quoteChar){
1290
596
  quoteChar=null;
1291
597
  attrs[key.trim()]=value.trim();
1292
598
  key=""; value=""; isKey=true;
1293
599
  continue;
1294
600
  }
1295
601
  if (isKey) key+=char;
1296
602
  else value+=char;
1297
603
  }
1298
604
  if (key.trim() &&!value) attrs[key.trim()]='';
1299
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){
1300
609
  const root=new Root();
1301
610
  const stack=[root];
1302
611
  let currentText="",i=0;
1303
612
  let max=0
1304
613
  function parseScript(){
1305
614
  if (!html.startsWith("<script",i)) return false;
1306
615
  const openTagEnd=html.indexOf(">",i);
1307
616
  if (openTagEnd===-1) return false;
1308
617
  const attributesString=html.substring(i+7,openTagEnd).trim();
1309
618
  const attributes=parseAttributes(attributesString);
1310
619
  let closeTagStart=html.indexOf("</script>",openTagEnd);
1311
620
  if (closeTagStart===-1) return false;
1312
621
  const content=html.substring(openTagEnd+1,closeTagStart);
1313
622
  const scriptNode=new Node('script',attributes,stack[stack.length-1]);
1314
623
  if(content.length>0) scriptNode.childNodes.push(content);
1315
624
  i=closeTagStart+9;
1316
625
  return true;
1317
626
  }
1318
627
  function parseSpecial(startStr,endStr,n1,n2,tag){
1319
628
  if (!html.startsWith(startStr,i)) return false
1320
629
  if(currentText.length) stack[stack.length-1].childNodes.push(new TextNode(currentText));
1321
630
  const end=html.indexOf(endStr,i+n1);
1322
631
  const strNode=new Node(tag,{},stack[stack.length-1]);
1323
632
  strNode.childNodes.push(html.substring(i+n1,end));
1324
633
  i=end+n2;
1325
634
  return true
1326
635
  }
1327
636
  while (i< html.length){
1328
637
  if(i>=max) max=i;
1329
638
  else break;
1330
639
  if (parseScript()) continue
1331
640
  if (parseSpecial("<!--","-->",4,3,'#comment')) continue
1332
641
  if (parseSpecial("<style","</style>",7,8,'style')) continue
1333
642
  if (html.startsWith("<![CDATA[",i)){
1334
643
  const end=html.indexOf("]]>",i+9);
1335
644
  if (end===-1) break;
1336
645
  const content=html.substring(i+9,end);
1337
646
  const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
1338
647
  cdataNode.nodeValue=content;
1339
648
  i=end+3;
1340
649
  continue;
1341
650
  }
1342
651
  if (html.startsWith("<",i)){
1343
652
  if (currentText && stack[stack.length-1]){
1344
653
  const textNode=new TextNode(currentText)
1345
654
  stack[stack.length-1].childNodes.push(textNode);
1346
655
  textNode.parent=stack[stack.length-1]
1347
656
  currentText="";
1348
657
  }
1349
658
  let tagEnd=i+1;
1350
659
  let insideQuotes=false;
1351
660
  let quoteChar=null;
1352
661
  while (tagEnd< html.length){
1353
662
  const char=html[tagEnd];
1354
663
  if (!insideQuotes && (char==='"' || char==="'")){
1355
664
  insideQuotes=true;
1356
665
  quoteChar=char;
1357
666
  } else if (insideQuotes && char===quoteChar){
1358
667
  insideQuotes=false;
1359
668
  quoteChar=null;
1360
669
  }
1361
670
  if (!insideQuotes && char==='>') break;
1362
671
  tagEnd++;
1363
672
  }
1364
673
  const tagContent=html.substring(i+1,tagEnd);
1365
674
  if (tagContent.startsWith("/")) stack.pop();
1366
675
  else{
1367
676
  let isSelfClosing=tagContent.endsWith('/');
1368
677
  const tagNameEnd=tagContent.search(/\s|>|\//);
1369
678
  const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
1370
679
  const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
1371
680
  const attributes=parseAttributes(attributesString);
1372
681
  if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
1373
682
  else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
1374
683
  }
1375
684
  i=tagEnd+1;
1376
685
  } else{
1377
686
  currentText+=html[i];
1378
687
  i++;
1379
688
  }
1380
689
  }
1381
690
  if (currentText.trim() && stack[stack.length-1]) stack[stack.length-1].childNodes.push(new TextNode(currentText));
1382
691
  return root;
692
+ }
693
+ function buildFromCache(cached){
1383
694
  function buildNode(cache,parent=null){
1384
695
  if(typeof cache==='string') return parent.childNodes.push(cache)
1385
696
  const{isSingle,tagName,attributes,childNodes,textContent}=cache
1386
697
  if(textContent) return parent.childNodes.push(new TextNode(textContent))
1387
698
  if(isSingle && parent) return parent.childNodes.push(new SingleNode(tagName,attributes))
1388
699
  const newDoc=tagName==='ROOT' ? new Root() : new Node(tagName,attributes,parent)
1389
700
  childNodes.forEach(childNode=>{
1390
701
  buildNode(childNode,newDoc)
1391
702
  });
1392
703
  return newDoc
1393
704
  }
1394
705
  return buildNode(cached)
706
+ }
1395
707
 
708
+ function cacheDoc(doc){
1396
709
  const props=['isSingle','tagName','attributes']
1397
710
  function addToCache(element,cache={}){
1398
711
  if(typeof element==='string') return element
1399
712
  if(element.nodeName==='#text') return{textContent:element.textContent}
1400
713
  props.forEach(prop=>{
1401
714
  if(element[prop]){
1402
715
  cache[prop]=typeof element[prop]==='object' ?{...element[prop]} : element[prop]
1403
716
  }
1404
717
  });
1405
718
  if(!element.childNodes) return cache
1406
719
  cache.childNodes=[]
1407
720
  element.childNodes.forEach(childNode=>{
1408
721
  cache.childNodes.push(addToCache(childNode))
1409
722
  });
1410
723
  return cache
1411
724
  }
1412
725
  return addToCache(doc)
726
+ }
727
+ export { parseHTML,Node,Query,TextNode,SingleNode,buildFromCache,cacheDoc,Root,Document }
728
+ export default { parseHTML,Node,Query,TextNode,SingleNode,buildFromCache,cacheDoc,Root,Document }