als-document 1.0.6-alpha → 1.0.8-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -21,13 +21,17 @@ 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])=>`${key}="${val}"`).join(" ");
107
106
  return `<${this.tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this.tagName}>`;
108
107
  }
109
108
  getAttribute(attrName){ return 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.childIndex;
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") this.childNodes.unshift(newElement);
148
147
  else if (pos==="beforeend") this.childNodes.push(newElement);
149
148
  newElement.parent=this
150
149
  if (!this.parent) return newElement
151
150
  if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
152
151
  else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
153
152
  newElement.parent=this.parent
154
153
  return newElement
155
154
  }
156
155
  insertAdjacentHTML(position,html){
157
156
  const newNode=parseHTML(html);
158
157
  newNode.childNodes.reverse().forEach(node=>{
159
158
  this.insertAdjacentElement(position,node);
160
159
  });
161
160
  return newNode
162
161
  }
163
162
  insertAdjacentText(position,text){
164
163
  return this.insertAdjacentElement(position,new TextNode(text));
165
164
  }
166
165
  set innerHTML(html){
167
166
  const parsed=parseHTML(html);
168
167
  this.childNodes=parsed.childNodes;
169
168
  }
170
169
  set outerHTML(html){
171
170
  const parsed=parseHTML(html);
172
171
  if (!this.parent) return console.log('element has no parent node')
173
172
  const index=this.childIndex
174
173
  if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
175
174
  }
176
175
  appendChild(newChild){
177
176
  if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
178
177
  if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
179
178
  } else if(typeof newChild==='string') newChild=new TextNode(newChild)
180
179
  else return newChild
181
180
  this.childNodes.push(newChild);
182
181
  newChild.parent=this;
183
182
  return newChild;
184
183
  }
185
184
  get textContent(){
186
185
  if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
187
186
  return this.childNodes.map(child=>{
188
187
  if(child instanceof SingleNode) return ''
189
188
  if(child instanceof TextNode) return child.nodeValue
190
189
  if(child instanceof Node) return child.textContent;
191
190
  else return child;
192
191
  }).join(" ");
193
192
  }
194
193
  set textContent(value){
195
194
  this.childNodes=[];
196
195
  if (value!==null && value!==undefined){
197
196
  this.childNodes.push(value.toString());
198
197
  }
199
198
  }
199
+ }
200
200
  class Node{
201
201
  constructor(tagName,attributes={},parent=null){
202
202
  this.isSingle=false;
203
203
  this.tagName=tagName;
204
204
  this.attributes=attributes;
205
205
  this.childNodes=[];
206
206
  if (parent!==null) parent.childNodes.push(this)
207
207
  this.parent=parent;
208
208
  this._classList=null;
209
209
  this.__style=null;
210
210
  this._dataset=null
211
211
  }
212
212
  get id(){ return this.attributes.id ? this.attributes.id : null; }
213
213
  set id(newValue){ this.attributes.id=newValue; }
214
214
  get className(){return this.attributes.class || null}
215
215
  get parentNode(){ return this.parent }
216
216
  get ancestors(){
217
217
  if(!this.parent) return []
218
218
  const ancestors=[]
219
219
  let element=this.parent
220
220
  while (element.tagName!=='ROOT'){
221
221
  ancestors.push(element)
222
222
  element=element.parent
223
223
  }
224
224
  return ancestors.reverse()
225
225
  }
226
226
  get childNodeIndex(){
227
227
  if(!this.parent) return null
228
228
  return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
229
229
  }
230
230
  get childIndex(){
231
231
  if(!this.parent) return null
232
232
  return this.parent.children ? this.parent.children.indexOf(this) : null
233
233
  }
234
234
  get previousElementSibling(){ return this.prev }
235
235
  get prev(){
236
236
  if (!this.childIndex) return null
237
237
  return this.parent.children[this.childIndex-1]
238
238
  }
239
239
  get nextElementSibling(){ return this.next }
240
240
  get next(){
241
241
  if (!this.childIndex) return null
242
242
  return this.parent.children[this.childIndex+1] || null
243
243
  }
244
244
  get dataset(){
245
245
  if (!this._dataset) this._dataset=getDataset(this);
246
246
  return this._dataset;
247
247
  }
248
248
  get classList(){
249
249
  if (!this._classList) this._classList=new NodeClassList(this);
250
250
  return this._classList;
251
251
  }
252
252
  get style(){
253
253
  if (!this.__style) this.__style=buildStyle(this.attributes)
254
254
  return this.__style
255
255
  }
256
256
  get outerHTML(){
257
257
  const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
258
258
  return `<${this.tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this.tagName}>`;
259
259
  }
260
260
  getAttribute(attrName){ return this.attributes[attrName] || null }
261
261
  setAttribute(attrName,value){ this.attributes[attrName]=value }
262
262
  removeAttribute(attrName){ delete this.attributes[attrName] }
263
263
  remove(){
264
264
  if (!this.parent) return
265
265
  const index=this.childNodeIndex;
266
266
  if (index!==null) this.parent.childNodes.splice(index,1);
267
267
  }
268
268
  get innerHTML(){
269
269
  return this.childNodes.map(child=>{
270
270
  if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
271
271
  else if (child instanceof TextNode) return child.textContent;
272
272
  else return child
273
273
  }).join("");
274
274
  }
275
275
  $$(query){return this.querySelectorAll(query)}
276
276
  querySelectorAll(query){
277
277
  const selectors=Query.get(query)
278
278
  return find(selectors,this,new Set())
279
279
  }
280
280
  $(query){return this.querySelector(query)}
281
281
  querySelector(query){
282
282
  const selectors=Query.get(query)
283
283
  return find(selectors,this,new Set(),true)[0] || null
284
284
  }
285
285
  getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
286
286
  getElementsByTagName(query){ return this.querySelectorAll(query) }
287
287
  getElementById(query){ return this.querySelector('#'+query) }
288
288
  get children(){
289
289
  return this.childNodes.filter(child=>{
290
290
  if (!(child instanceof Node)) return false
291
291
  if (child.tagName==='#comment') return false
292
292
  return true
293
293
  });
294
294
  }
295
295
  insertAdjacentElement(position,newElement){
296
296
  if(newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
297
297
  const pos=position.toLowerCase();
298
298
  if (pos==="afterbegin") this.childNodes.unshift(newElement);
299
299
  else if (pos==="beforeend") this.childNodes.push(newElement);
300
300
  newElement.parent=this
301
301
  if (!this.parent) return newElement
302
302
  if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
303
303
  else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
304
304
  newElement.parent=this.parent
305
305
  return newElement
306
306
  }
307
307
  insertAdjacentHTML(position,html){
308
308
  const newNode=parseHTML(html);
309
309
  newNode.childNodes.reverse().forEach(node=>{
310
310
  this.insertAdjacentElement(position,node);
311
311
  });
312
312
  return newNode
313
313
  }
314
314
  insertAdjacentText(position,text){
315
315
  return this.insertAdjacentElement(position,new TextNode(text));
316
316
  }
317
317
  set innerHTML(html){
318
318
  const parsed=parseHTML(html);
319
319
  this.childNodes=parsed.childNodes;
320
320
  }
321
321
  set outerHTML(html){
322
322
  const parsed=parseHTML(html);
323
323
  if (!this.parent) return console.log('element has no parent node')
324
324
  const index=this.childIndex
325
325
  if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
326
326
  }
327
327
  appendChild(newChild){
328
328
  if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
329
329
  if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
330
330
  } else if(typeof newChild==='string') newChild=new TextNode(newChild)
331
331
  else return newChild
332
332
  this.childNodes.push(newChild);
333
333
  newChild.parent=this;
334
334
  return newChild;
335
335
  }
336
336
  get textContent(){
337
337
  if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
338
338
  return this.childNodes.map(child=>{
339
339
  if(child instanceof SingleNode) return ''
340
340
  if(child instanceof TextNode) return child.nodeValue
341
341
  if(child instanceof Node) return child.textContent;
342
342
  else return child;
343
343
  }).join(" ");
344
344
  }
345
345
  set textContent(value){
346
346
  this.childNodes=[];
347
347
  if (value!==null && value!==undefined){
348
348
  this.childNodes.push(value.toString());
349
349
  }
350
350
  }
351
351
  }
352
- class SingleNode extends Node{
353
352
  constructor(tagName,attributes={},parent=null){
354
353
  if(attributes['?'] && tagName==='?xml') delete attributes['?']
355
354
  super(tagName,attributes,parent);
356
355
  this.isSingle=true
357
356
  }
358
357
  get outerHTML(){
359
358
  if (this.tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
360
359
  const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
361
360
  return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
362
361
  }
363
362
  get innerHTML(){ return ""; }
364
363
  set innerHTML(_){ }
365
364
  $(_){return null}
366
365
  $$(_){return []}
367
366
  querySelectorAll(_){ return []; }
368
367
  querySelector(_){ return null; }
369
368
  getElementsByClassName(_){ return []; }
370
369
  getElementsByTagName(_){ return []; }
371
370
  getElementById(_){ return null; }
372
371
  get children(){ return []; }
373
372
  insertAdjacentElement(_,__){ }
374
373
  insertAdjacentHTML(_,__){ }
375
374
  insertAdjacentText(_,__){ }
376
375
  appendChild(_){ }
377
376
  get textContent(){ return ""; }
378
377
  set textContent(_){ }
378
+ class SingleNode extends Node{
379
379
  constructor(tagName,attributes={},parent=null){
380
380
  if(attributes['?'] && tagName==='?xml') delete attributes['?']
381
381
  super(tagName,attributes,parent);
382
382
  this.isSingle=true
383
383
  }
384
384
  get outerHTML(){
385
385
  if (this.tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
386
386
  const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
387
387
  return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
388
388
  }
389
389
  get innerHTML(){ return ""; }
390
390
  set innerHTML(_){ }
391
391
  $(_){return null}
392
392
  $$(_){return []}
393
393
  querySelectorAll(_){ return []; }
394
394
  querySelector(_){ return null; }
395
395
  getElementsByClassName(_){ return []; }
396
396
  getElementsByTagName(_){ return []; }
397
397
  getElementById(_){ return null; }
398
398
  get children(){ return []; }
399
399
  insertAdjacentElement(_,__){ }
400
400
  insertAdjacentHTML(_,__){ }
401
401
  insertAdjacentText(_,__){ }
402
402
  appendChild(_){ }
403
403
  get textContent(){ return ""; }
404
404
  set textContent(_){ }
405
405
  }
406
- function parseAttributes(str){
407
406
  const attrs={};
408
407
  let key="";
409
408
  let value="";
410
409
  let isKey=true;
411
410
  let quoteChar=null;
412
411
  for (let i=0; i< str.length; i++){
413
412
  const char=str[i];
414
413
  if (isKey && (char==='=' || char===' ')){
415
414
  if (char==='=') isKey=false;
416
415
  else if (key.trim()){
417
416
  attrs[key.trim()]=true;
418
417
  key="";
419
418
  }
420
419
  continue;
421
420
  }
422
421
  if (!quoteChar && (char==='"' || char==="'")){
423
422
  quoteChar=char;
424
423
  continue;
425
424
  } else if (quoteChar && char===quoteChar){
426
425
  quoteChar=null;
427
426
  attrs[key.trim()]=value.trim();
428
427
  key=""; value=""; isKey=true;
429
428
  continue;
430
429
  }
431
430
  if (isKey) key+=char;
432
431
  else value+=char;
433
432
  }
434
433
  if (key.trim() &&!value) attrs[key.trim()]=true;
435
434
  return attrs;
435
+ function parseAttributes(str){
436
436
  const attrs={};
437
437
  let key="";
438
438
  let value="";
439
439
  let isKey=true;
440
440
  let quoteChar=null;
441
441
  for (let i=0; i< str.length; i++){
442
442
  const char=str[i];
443
443
  if (isKey && (char==='=' || char===' ')){
444
444
  if (char==='=') isKey=false;
445
445
  else if (key.trim()){
446
446
  attrs[key.trim()]=true;
447
447
  key="";
448
448
  }
449
449
  continue;
450
450
  }
451
451
  if (!quoteChar && (char==='"' || char==="'")){
452
452
  quoteChar=char;
453
453
  continue;
454
454
  } else if (quoteChar && char===quoteChar){
455
455
  quoteChar=null;
456
456
  attrs[key.trim()]=value.trim();
457
457
  key=""; value=""; isKey=true;
458
458
  continue;
459
459
  }
460
460
  if (isKey) key+=char;
461
461
  else value+=char;
462
462
  }
463
463
  if (key.trim() &&!value) attrs[key.trim()]='';
464
464
  return attrs;
465
465
  }
466
466
  const VOID_TAGS=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","!doctype",'?xml']);
467
467
  function parseHTML(html){
468
468
  const root=new Node("ROOT");
469
469
  const stack=[root];
470
470
  let currentText="",i=0;
471
471
  let max=0
472
472
  function parseScript(){
473
473
  if (!html.startsWith("<script",i)) return false;
474
474
  const openTagEnd=html.indexOf(">",i);
475
475
  if (openTagEnd===-1) return false;
476
476
  const attributesString=html.substring(i+7,openTagEnd).trim();
477
477
  const attributes=parseAttributes(attributesString);
478
478
  let closeTagStart=html.indexOf("</script>",openTagEnd);
479
479
  if (closeTagStart===-1) return false;
480
480
  const content=html.substring(openTagEnd+1,closeTagStart);
481
481
  const scriptNode=new Node('script',attributes,stack[stack.length-1]);
482
482
  if(content.length>0) scriptNode.childNodes.push(content);
483
483
  i=closeTagStart+9;
484
484
  return true;
485
485
  }
486
486
  function parseSpecial(startStr,endStr,n1,n2,tag){
487
487
  if (!html.startsWith(startStr,i)) return false
488
488
  const end=html.indexOf(endStr,i+n1);
489
489
  const strNode=new Node(tag,{},stack[stack.length-1]);
490
490
  strNode.childNodes.push(html.substring(i+n1,end));
491
491
  i=end+n2;
492
492
  return true
493
493
  }
494
494
  while (i< html.length){
495
495
  if(i>=max) max=i;
496
496
  else break;
497
497
  if (parseScript()) continue
498
498
  if (parseSpecial("<!--","-->",4,3,'#comment')) continue
499
499
  if (parseSpecial("<style","</style>",7,8,'style')) continue
500
500
  if (html.startsWith("<![CDATA[",i)){
501
501
  const end=html.indexOf("]]>",i+9);
502
502
  if (end===-1) break;
503
503
  const content=html.substring(i+9,end);
504
504
  const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
505
505
  cdataNode.nodeValue=content;
506
506
  i=end+3;
507
507
  continue;
508
508
  }
509
509
  if (html.startsWith("<",i)){
510
510
  if (currentText && stack[stack.length-1]){
511
511
  const textNode=new TextNode(currentText)
512
512
  stack[stack.length-1].childNodes.push(textNode);
513
513
  textNode.parent=stack[stack.length-1]
514
514
  currentText="";
515
515
  }
516
516
  let tagEnd=i+1;
517
517
  let insideQuotes=false;
518
518
  let quoteChar=null;
519
519
  while (tagEnd< html.length){
520
520
  const char=html[tagEnd];
521
521
  if (!insideQuotes && (char==='"' || char==="'")){
522
522
  insideQuotes=true;
523
523
  quoteChar=char;
524
524
  } else if (insideQuotes && char===quoteChar){
525
525
  insideQuotes=false;
526
526
  quoteChar=null;
527
527
  }
528
528
  if (!insideQuotes && char==='>') break;
529
529
  tagEnd++;
530
530
  }
531
531
  const tagContent=html.substring(i+1,tagEnd);
532
532
  if (tagContent.startsWith("/")) stack.pop();
533
533
  else{
534
534
  let isSelfClosing=tagContent.endsWith('/');
535
535
  const tagNameEnd=tagContent.search(/\s|>|\//);
536
536
  const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
537
537
  const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
538
538
  const attributes=parseAttributes(attributesString);
539
539
  if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
540
540
  else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
541
541
  }
542
542
  i=tagEnd+1;
543
543
  } else{
544
544
  currentText+=html[i];
545
545
  i++;
546
546
  }
547
547
  }
548
548
  if (currentText.trim() && stack[stack.length-1]) stack[stack.length-1].childNodes.push(new TextNode(currentText));
549
549
  return root;
550
550
  }
551
- module.exports = { parseHTML, Node, Query, TextNode, SingleNode }
551
+ function buildFromCache(cached){
552
552
  function buildNode(cache,parent=null){
553
553
  if(typeof cache==='string') return parent.childNodes.push(cache)
554
554
  const{isSingle,tagName,attributes,childNodes,textContent}=cache
555
555
  if(textContent) return parent.childNodes.push(new TextNode(textContent))
556
556
  if(isSingle) return parent.childNodes.push(new SingleNode(tagName,attributes))
557
557
  const newDoc=new Node(tagName,attributes,parent)
558
558
  childNodes.forEach(childNode=>{
559
559
  buildNode(childNode,newDoc)
560
560
  });
561
561
  return newDoc
562
562
  }
563
563
  return buildNode(cached)
564
+ }
564
565
 
566
+ function cacheDoc(doc){
565
567
  const props=['isSingle','tagName','attributes']
566
568
  function addToCache(element,cache={}){
567
569
  if(typeof element==='string') return element
568
570
  if(element.nodeName==='#text') return{textContent:element.textContent}
569
571
  props.forEach(prop=>{
570
572
  if(element[prop]) cache[prop]=element[prop]
571
573
  });
572
574
  if(!element.childNodes) return cache
573
575
  cache.childNodes=[]
574
576
  element.childNodes.forEach(childNode=>{
575
577
  cache.childNodes.push(addToCache(childNode))
576
578
  });
577
579
  return cache
578
580
  }
579
581
  return addToCache(doc)
582
+ }
583
+ module.exports = { parseHTML, Node, Query, TextNode, SingleNode, buildFromCache, cacheDoc }