als-document 1.1.2 → 1.2.1

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