als-document 1.1.1 → 1.2.0

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.
Files changed (48) hide show
  1. package/browser-tests/data/html1.js +2579 -0
  2. package/browser-tests/data/html2.js +1124 -0
  3. package/browser-tests/data/svg.js +66 -0
  4. package/{tests → browser-tests}/index.html +1 -1
  5. package/browser-tests/utils.js +37 -0
  6. package/build-readme.js +8 -0
  7. package/{src/build.js → build.js} +4 -4
  8. package/docs/#.md +52 -0
  9. package/docs/1.parseHTML.md +39 -0
  10. package/docs/2.node.md +25 -0
  11. package/docs/3.singleNode.md +3 -0
  12. package/docs/4.tTextNode.md +3 -0
  13. package/docs/5. Document.md +28 -0
  14. package/docs/6.Query.md +110 -0
  15. package/docs/7.cache.md +14 -0
  16. package/document.js +6 -4
  17. package/index.js +6 -4
  18. package/index.mjs +6 -4
  19. package/{src/node/root.js → lib/node/document.js} +3 -2
  20. package/{src → lib}/node/node.js +6 -3
  21. package/lib/node/root.js +6 -0
  22. package/{src → lib}/node/single-node.js +22 -4
  23. package/package.json +6 -4
  24. package/readme.md +21 -14
  25. package/tests/cache.test.js +23 -0
  26. package/tests/node.test.js +411 -0
  27. package/tests/parser.test.js +353 -0
  28. package/tests/query.test.js +65 -0
  29. package/tests/single-node.test.js +88 -0
  30. package/tests/utils.js +2 -0
  31. package/.vscode/settings.json +0 -3
  32. /package/{tests → browser-tests}/cache.js +0 -0
  33. /package/{tests → browser-tests}/node.js +0 -0
  34. /package/{tests → browser-tests}/parse-real.js +0 -0
  35. /package/{tests → browser-tests}/parser.js +0 -0
  36. /package/{tests → browser-tests}/query.js +0 -0
  37. /package/{tests/test.js → browser-tests/simple-test.js} +0 -0
  38. /package/{src → lib}/node/class-list.js +0 -0
  39. /package/{src → lib}/node/dataset.js +0 -0
  40. /package/{src → lib}/node/find.js +0 -0
  41. /package/{src → lib}/node/style.js +0 -0
  42. /package/{src → lib}/node/text-node.js +0 -0
  43. /package/{src → lib}/parse/cache.js +0 -0
  44. /package/{src → lib}/parse/parse-atts.js +0 -0
  45. /package/{src → lib}/parse/parser.js +0 -0
  46. /package/{src → lib}/parse/void-tags.js +0 -0
  47. /package/{src → lib}/query/check-element.js +0 -0
  48. /package/{src → lib}/query/query.js +0 -0
package/document.js CHANGED
@@ -22,12 +22,14 @@ function buildStyle(attributes){
22
22
  const styles=attributes.style || "";
23
23
  con
24
24
  class NodeClassList{
25
25
  constructor(node){ this.node=node }
26
26
  get classes(){ return (this.node.attributes.class || "").split(" ").filter(Boolean) }
27
27
  set classes(val){ this.node.attributes.class=val.join(" ") }
28
28
  contains(className){ return this.classes.includes(className) }
29
29
  add(className){
30
30
  const currentClasses=this.classes;
31
31
  if (!currentClasses.includes(className)) this.classes=[...currentClasses,className];
32
32
  }
33
33
  remove(className){ this.classes=this.classes.filter(cls=>cls!==className); }
34
34
  toggle(className){
35
35
  if (this.classes.includes(className)) this.remove(className);
36
36
  else this.add(className);
37
37
  }
38
38
  replace(oldClass,newClass){
39
39
  if (this.classes.includes(oldClass)){
40
40
  this.remove(oldClass);
41
41
  this.add(newClass);
42
42
  }
43
43
  }
44
44
  }
45
45
  function insertBefore(arr,index,newItem){
46
46
  const existingIndex=arr.indexOf(newItem);
47
47
  if (existingIndex!==-1) arr.splice(existingIndex,1);
48
48
  arr.splice(index,0,newItem);
49
- }
50
49
  class Node{
51
50
  constructor(tagName,attributes={},parent=null){
52
51
  this.isSingle=false;
53
52
  this.tagName=tagName;
54
53
  this.attributes=attributes;
55
54
  this.childNodes=[];
56
55
  if (parent!==null) parent.childNodes.push(this)
57
56
  this.parent=parent;
58
57
  this._classList=null;
59
58
  this.__style=null;
60
59
  this._dataset=null
61
60
  }
62
61
  get id(){ return this.attributes.id ? this.attributes.id : null; }
63
62
  set id(newValue){ this.attributes.id=newValue; }
64
63
  get className(){ return this.attributes.class || null }
65
64
  get parentNode(){ return this.parent }
66
65
  get ancestors(){
67
66
  if (!this.parent) return []
68
67
  const ancestors=[]
69
68
  let element=this.parent
70
69
  while (element.tagName!=='ROOT'){
71
70
  ancestors.push(element)
72
71
  element=element.parent
73
72
  }
74
73
  return ancestors.reverse()
75
74
  }
76
75
  get childNodeIndex(){
77
76
  if (!this.parent) return null
78
77
  return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
79
78
  }
80
79
  get childIndex(){
81
80
  if (!this.parent) return null
82
81
  return this.parent.children ? this.parent.children.indexOf(this) : null
83
82
  }
84
83
  get previousElementSibling(){ return this.prev }
85
84
  get prev(){
86
85
  if (!this.childIndex) return null
87
86
  return this.parent.children[this.childIndex-1]
88
87
  }
89
88
  get nextElementSibling(){ return this.next }
90
89
  get next(){
91
90
  if (!this.childIndex) return null
92
91
  return this.parent.children[this.childIndex+1] || null
93
92
  }
94
93
  get dataset(){
95
94
  if (!this._dataset) this._dataset=getDataset(this);
96
95
  return this._dataset;
97
96
  }
98
97
  get classList(){
99
98
  if (!this._classList) this._classList=new NodeClassList(this);
100
99
  return this._classList;
101
100
  }
102
101
  get style(){
103
102
  if (!this.__style) this.__style=buildStyle(this.attributes)
104
103
  return this.__style
105
104
  }
106
105
  get outerHTML(){
107
106
  const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
108
107
  return `<${this.tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this.tagName}>`;
109
108
  }
110
109
  getAttribute(attrName){ return this.attributes[attrName] || null }
111
110
  setAttribute(attrName,value=''){ this.attributes[attrName]=value }
112
111
  removeAttribute(attrName){ delete this.attributes[attrName] }
113
112
  remove(){
114
113
  if (!this.parent) return
115
114
  const index=this.childNodeIndex;
116
115
  if (index!==null) this.parent.childNodes.splice(index,1);
117
116
  }
118
117
  get innerHTML(){
119
118
  return this.childNodes.map(child=>{
120
119
  if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
121
120
  else if (child instanceof TextNode) return child.textContent;
122
121
  else return child
123
122
  }).join("");
124
123
  }
125
124
  $$(query){ return this.querySelectorAll(query) }
126
125
  querySelectorAll(query){
127
126
  const selectors=Query.get(query)
128
127
  return find(selectors,this,new Set())
129
128
  }
130
129
  $(query){ return this.querySelector(query) }
131
130
  querySelector(query){
132
131
  const selectors=Query.get(query)
133
132
  return find(selectors,this,new Set(),true)[0] || null
134
133
  }
135
134
  getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
136
135
  getElementsByTagName(query){ return this.querySelectorAll(query) }
137
136
  getElementById(query){ return this.querySelector('#'+query) }
138
137
  get children(){
139
138
  return this.childNodes.filter(child=>{
140
139
  if (!(child instanceof Node)) return false
141
140
  if (child.tagName==='#comment') return false
142
141
  return true
143
142
  });
144
143
  }
145
144
  insertAdjacentElement(position,newElement){
146
145
  if (newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
147
146
  const pos=position.toLowerCase();
148
147
  if (pos==='afterbegin' || pos==='beforeend'){
149
148
  if (pos==="afterbegin") this.childNodes.unshift(newElement);
150
149
  else if (pos==="beforeend") this.childNodes.push(newElement);
151
150
  newElement.parent=this
152
151
  return newElement
153
152
  }
154
153
  if (!this.parent) throw new Error("Can't insert element to element without parent")
155
154
  if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
156
155
  else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
157
156
  newElement.parent=this.parent
158
157
  return newElement
159
158
  }
160
159
  insertAdjacentHTML(position,html){
161
160
  const newNode=parseHTML(html);
162
161
  newNode.childNodes.forEach(node=>{
163
162
  this.insertAdjacentElement(position,node);
164
163
  });
165
164
  return newNode
166
165
  }
167
166
  insertAdjacentText(position,text){
168
167
  return this.insertAdjacentElement(position,new TextNode(text));
169
168
  }
170
169
  insert(position,element){
171
170
  const positions=['beforebegin','afterbegin','beforeend','afterend']
172
171
  if (positions[position]) position=positions[position]
173
172
  if (typeof element==='string'){
174
173
  element=element.trim()
175
174
  if (element.startsWith('<') && element.endsWith('>')){
176
175
  return this.insertAdjacentHTML(position,element)
177
176
  }
178
177
  return this.insertAdjacentText(position,element)
179
178
  }
180
179
  return this.insertAdjacentElement(position,element)
181
180
  }
182
181
  set innerHTML(html){
183
182
  this.childNodes=html.trim().startsWith('<') ? parseHTML(html).childNodes : [html]
184
183
  }
185
184
  set outerHTML(html){
186
185
  const parsed=parseHTML(html);
187
186
  if (!this.parent) return console.log('element has no parent node')
188
187
  const index=this.childIndex
189
188
  if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
190
189
  }
191
190
  appendChild(newChild){
192
191
  if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
193
192
  if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
194
193
  } else if (typeof newChild==='string') newChild=new TextNode(newChild)
195
194
  else return newChild
196
195
  this.childNodes.push(newChild);
197
196
  newChild.parent=this;
198
197
  return newChild;
199
198
  }
200
199
  get textContent(){
201
200
  if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
202
201
  return this.childNodes.map(child=>{
203
202
  if (child instanceof SingleNode) return ''
204
203
  if (child instanceof TextNode) return child.nodeValue
205
204
  if (child instanceof Node) return child.textContent;
206
205
  else return child;
207
206
  }).join(" ");
208
207
  }
209
208
  set textContent(value){
210
209
  this.childNodes=[];
211
210
  if (value!==null && value!==undefined){
212
211
  this.childNodes.push(value.toString());
213
212
  }
214
213
  }
214
+ }
215
215
  class Node{
216
216
  constructor(tagName,attributes={},parent=null){
217
217
  this.isSingle=false;
218
218
  this.tagName=tagName;
219
219
  this.attributes=attributes;
220
220
  this.childNodes=[];
221
221
  if (parent!==null) parent.childNodes.push(this)
222
222
  this.parent=parent;
223
223
  this._classList=null;
224
224
  this.__style=null;
225
225
  this._dataset=null
226
226
  }
227
227
  get id(){ return this.attributes.id ? this.attributes.id : null; }
228
228
  set id(newValue){ this.attributes.id=newValue; }
229
229
  get className(){ return this.attributes.class || null }
230
230
  get parentNode(){ return this.parent }
231
231
  get ancestors(){
232
232
  if (!this.parent) return []
233
233
  const ancestors=[]
234
234
  let element=this.parent
235
235
  while (element.tagName!=='ROOT'){
236
236
  ancestors.push(element)
237
237
  element=element.parent
238
238
  }
239
239
  return ancestors.reverse()
240
240
  }
241
241
  get childNodeIndex(){
242
242
  if (!this.parent) return null
243
243
  return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
244
244
  }
245
245
  get childIndex(){
246
246
  if (!this.parent) return null
247
247
  return this.parent.children ? this.parent.children.indexOf(this) : null
248
248
  }
249
249
  get previousElementSibling(){ return this.prev }
250
250
  get prev(){
251
251
  if (!this.childIndex) return null
252
252
  return this.parent.children[this.childIndex-1]
253
253
  }
254
254
  get nextElementSibling(){ return this.next }
255
255
  get next(){
256
256
  if (!this.childIndex) return null
257
257
  return this.parent.children[this.childIndex+1] || null
258
258
  }
259
259
  get dataset(){
260
260
  if (!this._dataset) this._dataset=getDataset(this);
261
261
  return this._dataset;
262
262
  }
263
263
  get classList(){
264
264
  if (!this._classList) this._classList=new NodeClassList(this);
265
265
  return this._classList;
266
266
  }
267
267
  get style(){
268
268
  if (!this.__style) this.__style=buildStyle(this.attributes)
269
269
  return this.__style
270
270
  }
271
271
  get outerHTML(){
272
272
  const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
273
273
  return `<${this.tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this.tagName}>`;
274
274
  }
275
275
  getAttribute(attrName){ return this.attributes[attrName]!==undefined ? this.attributes[attrName] : null }
276
276
  setAttribute(attrName,value=''){ this.attributes[attrName]=value }
277
277
  removeAttribute(attrName){ delete this.attributes[attrName] }
278
278
  remove(){
279
279
  if (!this.parent) return
280
280
  const index=this.childNodeIndex;
281
281
  if (index!==null) this.parent.childNodes.splice(index,1);
282
282
  }
283
283
  get innerHTML(){
284
284
  return this.childNodes.map(child=>{
285
285
  if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
286
286
  else if (child instanceof TextNode) return child.textContent;
287
287
  else return child
288
288
  }).join("");
289
289
  }
290
290
  $$(query){ return this.querySelectorAll(query) }
291
291
  querySelectorAll(query){
292
292
  const selectors=Query.get(query)
293
293
  return find(selectors,this,new Set())
294
294
  }
295
295
  $(query){ return this.querySelector(query) }
296
296
  querySelector(query){
297
297
  const selectors=Query.get(query)
298
298
  return find(selectors,this,new Set(),true)[0] || null
299
299
  }
300
300
  getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
301
301
  getElementsByTagName(query){ return this.querySelectorAll(query) }
302
302
  getElementById(query){ return this.querySelector('#'+query) }
303
303
  get children(){
304
304
  return this.childNodes.filter(child=>{
305
305
  if (!(child instanceof Node)) return false
306
306
  if (child.tagName==='#comment') return false
307
307
  return true
308
308
  });
309
309
  }
310
310
  insertAdjacentElement(position,newElement){
311
311
  if (newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
312
312
  const pos=position.toLowerCase();
313
313
  if(newElement.parentNode){
314
314
  newElement.parentNode.childNodes=newElement.parentNode.childNodes.filter(el=>el!==newElement)
315
315
  }
316
316
  if (pos==='afterbegin' || pos==='beforeend'){
317
317
  if (pos==="afterbegin") this.childNodes.unshift(newElement);
318
318
  else if (pos==="beforeend") this.childNodes.push(newElement);
319
319
  newElement.parent=this
320
320
  return newElement
321
321
  }
322
322
  if (!this.parent) throw new Error("Can't insert element to element without parent")
323
323
  if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
324
324
  else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
325
325
  newElement.parent=this.parent
326
326
  return newElement
327
327
  }
328
328
  insertAdjacentHTML(position,html){
329
329
  const newNode=parseHTML(html);
330
330
  newNode.childNodes.forEach(node=>{
331
331
  this.insertAdjacentElement(position,node);
332
332
  });
333
333
  return newNode
334
334
  }
335
335
  insertAdjacentText(position,text){
336
336
  return this.insertAdjacentElement(position,new TextNode(text));
337
337
  }
338
338
  insert(position,element){
339
339
  const positions=['beforebegin','afterbegin','beforeend','afterend']
340
340
  if (positions[position]) position=positions[position]
341
341
  if (typeof element==='string'){
342
342
  element=element.trim()
343
343
  if (element.startsWith('<') && element.endsWith('>')){
344
344
  return this.insertAdjacentHTML(position,element)
345
345
  }
346
346
  return this.insertAdjacentText(position,element)
347
347
  }
348
348
  return this.insertAdjacentElement(position,element)
349
349
  }
350
350
  set innerHTML(html){
351
351
  this.childNodes=html.trim().startsWith('<') ? parseHTML(html).childNodes : [html]
352
352
  }
353
353
  set outerHTML(html){
354
354
  const parsed=parseHTML(html);
355
355
  if (!this.parent) throw new Error('element has no parent node')
356
356
  const index=this.childIndex
357
357
  if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
358
358
  }
359
359
  appendChild(newChild){
360
360
  if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
361
361
  if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
362
362
  } else if (typeof newChild==='string') newChild=new TextNode(newChild)
363
363
  else return newChild
364
364
  this.childNodes.push(newChild);
365
365
  newChild.parent=this;
366
366
  return newChild;
367
367
  }
368
368
  get textContent(){
369
369
  if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
370
370
  return this.childNodes.map(child=>{
371
371
  if (child instanceof SingleNode) return ''
372
372
  if (child instanceof TextNode) return child.nodeValue
373
373
  if (child instanceof Node) return child.textContent;
374
374
  else return child;
375
375
  }).join('');
376
376
  }
377
377
  set textContent(value){
378
378
  this.childNodes=[];
379
379
  if (value!==null && value!==undefined){
380
380
  this.childNodes.push(value.toString());
381
381
  }
382
382
  }
383
383
  }
384
- class SingleNode extends Node{
385
384
  constructor(tagName,attributes={},parent=null){
386
385
  if(attributes['?'] && tagName==='?xml') delete attributes['?']
387
386
  super(tagName,attributes,parent);
388
387
  this.isSingle=true
389
388
  }
390
389
  get outerHTML(){
391
390
  if (this.tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
392
391
  const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
393
392
  return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
394
393
  }
395
394
  get innerHTML(){ return ""; }
396
395
  set innerHTML(_){ }
397
396
  $(_){return null}
398
397
  $$(_){return []}
399
398
  querySelectorAll(_){ return []; }
400
399
  querySelector(_){ return null; }
401
400
  getElementsByClassName(_){ return []; }
402
401
  getElementsByTagName(_){ return []; }
403
402
  getElementById(_){ return null; }
404
403
  get children(){ return []; }
405
404
  insertAdjacentElement(_,__){ }
406
405
  insertAdjacentHTML(_,__){ }
407
406
  insertAdjacentText(_,__){ }
408
407
  appendChild(_){ }
409
408
  insert(_,__){ }
410
409
  get textContent(){ return ""; }
411
410
  set textContent(_){ }
411
+ class SingleNode extends Node{
412
412
  constructor(tagName,attributes={},parent=null){
413
413
  if(attributes['?'] && tagName==='?xml') delete attributes['?']
414
414
  super(tagName,attributes,parent);
415
415
  this.isSingle=true
416
416
  }
417
417
  get outerHTML(){
418
418
  if (this.tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
419
419
  const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
420
420
  return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
421
421
  }
422
422
  get innerHTML(){ return ""; }
423
423
  set innerHTML(_){ }
424
424
  $(_){return null}
425
425
  $$(_){return []}
426
426
  querySelectorAll(_){ return []; }
427
427
  querySelector(_){ return null; }
428
428
  getElementsByClassName(_){ return []; }
429
429
  getElementsByTagName(_){ return []; }
430
430
  getElementById(_){ return null; }
431
431
  get children(){ return []; }
432
432
  insertAdjacentElement(position,newElement){
433
433
  if(position==='afterbegin') position='beforebegin'
434
434
  if(position==='beforeend') position='afterend'
435
435
  super.insertAdjacentElement(position,newElement)
436
436
  }
437
437
  insertAdjacentHTML(position,html){
438
438
  if(position==='afterbegin') position='beforebegin'
439
439
  if(position==='beforeend') position='afterend'
440
440
  super.insertAdjacentHTML(position,html)
441
441
  }
442
442
  insertAdjacentText(position,text){
443
443
  if(position==='afterbegin') position='beforebegin'
444
444
  if(position==='beforeend') position='afterend'
445
445
  super.insertAdjacentText(position,text)
446
446
  }
447
447
  insert(position,element){
448
448
  if(position===1) position=0
449
449
  if(position===2) position=3
450
450
  super.insert(position,element)
451
451
  }
452
452
  appendChild(_){ }
453
453
  get textContent(){ return ""; }
454
454
  set textContent(_){ }
455
455
  }
456
456
 
457
- class Root extends Node{
458
457
  constructor(){
459
458
  super('ROOT',{},null);
460
459
  this.isSingle=false
461
460
  }
462
461
  get body(){return this.$('body')}
463
462
  get head(){return this.$('head')}
464
463
  get title(){return this.$('title')}
465
464
  set title(title){return this.$('title').innerHTML=title}
465
+ class Root extends Node{
466
466
  constructor(){
467
467
  super('ROOT',{},null);
468
468
  this.isSingle=false
469
469
  }
470
+ }
471
+ class Document extends Node{
470
472
  constructor(){
471
473
  super('DOCUMENT',{},null);
472
474
  this.isSingle=false
473
475
  this.innerHTML=/*html*/`<!DOCTYPE html><html lang="en"><head><title></title></head><body></body></html>`
474
476
  }
475
477
  get body(){return this.$('body')}
476
478
  get head(){return this.$('head')}
477
479
  get title(){return this.$('title')}
478
480
  set title(title){return this.$('title').innerHTML=title}
479
481
  }
480
482
  function parseAttributes(str){
481
483
  const attrs={};
482
484
  let key="";
483
485
  let value="";
484
486
  let isKey=true;
485
487
  let quoteChar=null;
486
488
  for (let i=0; i< str.length; i++){
487
489
  const char=str[i];
488
490
  if (isKey && (char==='=' || char===' ')){
489
491
  if (char==='=') isKey=false;
490
492
  else if (key.trim()){
491
493
  attrs[key.trim()]=true;
492
494
  key="";
493
495
  }
494
496
  continue;
495
497
  }
496
498
  if (!quoteChar && (char==='"' || char==="'")){
497
499
  quoteChar=char;
498
500
  continue;
499
501
  } else if (quoteChar && char===quoteChar){
500
502
  quoteChar=null;
501
503
  attrs[key.trim()]=value.trim();
502
504
  key=""; value=""; isKey=true;
503
505
  continue;
504
506
  }
505
507
  if (isKey) key+=char;
506
508
  else value+=char;
507
509
  }
508
510
  if (key.trim() &&!value) attrs[key.trim()]='';
509
511
  return attrs;
510
512
  }
@@ -38,5 +40,5 @@ function buildFromCache(cached){
38
40
  function buildNode(cache,parent=null){
39
41
  }
40
42
 
41
43
  function cacheDoc(doc){
42
44
  const props=['isSingle','tagName','attributes']
43
45
  function addToCache(element,cache={}){
44
46
  if(typeof element==='string') return element
45
47
  if(element.nodeName==='#text') return{textContent:element.textContent}
46
48
  props.forEach(prop=>{
47
49
  if(element[prop]){
48
50
  cache[prop]=typeof element[prop]==='object' ?{...element[prop]} : element[prop]
49
51
  }
50
52
  });
51
53
  if(!element.childNodes) return cache
52
54
  cache.childNodes=[]
53
55
  element.childNodes.forEach(childNode=>{
54
56
  cache.childNodes.push(addToCache(childNode))
55
57
  });
56
58
  return cache
57
59
  }
58
60
  return addToCache(doc)
59
61
  }
60
- return { parseHTML, Node, Query, TextNode, SingleNode, buildFromCache, cacheDoc, Root }
62
+ return { parseHTML, Node, Query, TextNode, SingleNode, buildFromCache, cacheDoc, Root, Document }
61
63
  })()