als-document 1.1.0 → 1.1.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.
package/index.js CHANGED
@@ -21,7 +21,7 @@ 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] || 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] || 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 (pos==='afterbegin' || pos==='beforeend'){
313
313
  if (pos==="afterbegin") this.childNodes.unshift(newElement);
314
314
  else if (pos==="beforeend") this.childNodes.push(newElement);
315
315
  newElement.parent=this
316
316
  return newElement
317
317
  }
318
318
  if (!this.parent) throw new Error("Can't insert element to element without parent")
319
319
  if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
320
320
  else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
321
321
  newElement.parent=this.parent
322
322
  return newElement
323
323
  }
324
324
  insertAdjacentHTML(position,html){
325
325
  const newNode=parseHTML(html);
326
326
  newNode.childNodes.forEach(node=>{
327
327
  this.insertAdjacentElement(position,node);
328
328
  });
329
329
  return newNode
330
330
  }
331
331
  insertAdjacentText(position,text){
332
332
  return this.insertAdjacentElement(position,new TextNode(text));
333
333
  }
334
334
  insert(position,element){
335
335
  const positions=['beforebegin','afterbegin','beforeend','afterend']
336
336
  if (positions[position]) position=positions[position]
337
337
  if (typeof element==='string'){
338
338
  element=element.trim()
339
339
  if (element.startsWith('<') && element.endsWith('>')){
340
340
  return this.insertAdjacentHTML(position,element)
341
341
  }
342
342
  return this.insertAdjacentText(position,element)
343
343
  }
344
344
  return this.insertAdjacentElement(position,element)
345
345
  }
346
346
  set innerHTML(html){
347
347
  this.childNodes=html.trim().startsWith('<') ? parseHTML(html).childNodes : [html]
348
348
  }
349
349
  set outerHTML(html){
350
350
  const parsed=parseHTML(html);
351
351
  if (!this.parent) return console.log('element has no parent node')
352
352
  const index=this.childIndex
353
353
  if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
354
354
  }
355
355
  appendChild(newChild){
356
356
  if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
357
357
  if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
358
358
  } else if (typeof newChild==='string') newChild=new TextNode(newChild)
359
359
  else return newChild
360
360
  this.childNodes.push(newChild);
361
361
  newChild.parent=this;
362
362
  return newChild;
363
363
  }
364
364
  get textContent(){
365
365
  if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
366
366
  return this.childNodes.map(child=>{
367
367
  if (child instanceof SingleNode) return ''
368
368
  if (child instanceof TextNode) return child.nodeValue
369
369
  if (child instanceof Node) return child.textContent;
370
370
  else return child;
371
371
  }).join(" ");
372
372
  }
373
373
  set textContent(value){
374
374
  this.childNodes=[];
375
375
  if (value!==null && value!==undefined){
376
376
  this.childNodes.push(value.toString());
377
377
  }
378
378
  }
379
379
  }
380
380
  class SingleNode extends Node{
381
381
  constructor(tagName,attributes={},parent=null){
382
382
  if(attributes['?'] && tagName==='?xml') delete attributes['?']
383
383
  super(tagName,attributes,parent);
384
384
  this.isSingle=true
385
385
  }
386
386
  get outerHTML(){
387
387
  if (this.tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
388
388
  const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
389
389
  return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
390
390
  }
391
391
  get innerHTML(){ return ""; }
392
392
  set innerHTML(_){ }
393
393
  $(_){return null}
394
394
  $$(_){return []}
395
395
  querySelectorAll(_){ return []; }
396
396
  querySelector(_){ return null; }
397
397
  getElementsByClassName(_){ return []; }
398
398
  getElementsByTagName(_){ return []; }
399
399
  getElementById(_){ return null; }
400
400
  get children(){ return []; }
401
401
  insertAdjacentElement(_,__){ }
402
402
  insertAdjacentHTML(_,__){ }
403
403
  insertAdjacentText(_,__){ }
404
404
  appendChild(_){ }
405
405
  insert(_,__){ }
406
406
  get textContent(){ return ""; }
407
407
  set textContent(_){ }
408
408
  }
@@ -35,6 +35,6 @@ function parseHTML(html){
35
35
  const root=new Root();
36
36
  const stack=[root];
37
37
  le
38
38
  }
39
39
  function buildFromCache(cached){
40
40
  function buildNode(cache,parent=null){
41
41
  if(typeof cache==='string') return parent.childNodes.push(cache)
42
42
  const{isSingle,tagName,attributes,childNodes,textContent}=cache
43
43
  if(textContent) return parent.childNodes.push(new TextNode(textContent))
44
44
  if(isSingle) return parent.childNodes.push(new SingleNode(tagName,attributes))
45
45
  const newDoc=tagName==='ROOT' ? new Root() : new Node(tagName,attributes,parent)
46
46
  childNodes.forEach(childNode=>{
47
47
  buildNode(childNode,newDoc)
48
48
  });
49
49
  return newDoc
50
50
  }
51
51
  return buildNode(cached)
52
52
  }
53
53
 
54
- function cacheDoc(doc){
55
54
  const props=['isSingle','tagName','attributes']
56
55
  function addToCache(element,cache={}){
57
56
  if(typeof element==='string') return element
58
57
  if(element.nodeName==='#text') return{textContent:element.textContent}
59
58
  props.forEach(prop=>{
60
59
  if(element[prop]) cache[prop]=element[prop]
61
60
  });
62
61
  if(!element.childNodes) return cache
63
62
  cache.childNodes=[]
64
63
  element.childNodes.forEach(childNode=>{
65
64
  cache.childNodes.push(addToCache(childNode))
66
65
  });
67
66
  return cache
68
67
  }
69
68
  return addToCache(doc)
69
+ function cacheDoc(doc){
70
70
  const props=['isSingle','tagName','attributes']
71
71
  function addToCache(element,cache={}){
72
72
  if(typeof element==='string') return element
73
73
  if(element.nodeName==='#text') return{textContent:element.textContent}
74
74
  props.forEach(prop=>{
75
75
  if(element[prop]){
76
76
  cache[prop]=typeof element[prop]==='object' ?{...element[prop]} : element[prop]
77
77
  }
78
78
  });
79
79
  if(!element.childNodes) return cache
80
80
  cache.childNodes=[]
81
81
  element.childNodes.forEach(childNode=>{
82
82
  cache.childNodes.push(addToCache(childNode))
83
83
  });
84
84
  return cache
85
85
  }
86
86
  return addToCache(doc)
87
87
  }
88
88
  module.exports = { parseHTML, Node, Query, TextNode, SingleNode, buildFromCache, cacheDoc, Root }