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