als-document 1.0.2-alpha → 1.0.4-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.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  class Query{
2
2
  static get(query){
3
3
  let q=new Query(query)
4
4
  return q.selectors
5
5
  }
6
6
  constructor(query){
7
7
  this.query=query
8
8
  this.selectors=[]
9
9
  this.stringValues=[];
10
10
  this.parseSelectors(query.split(','))
11
11
  }
12
12
  parseSelectors(selectors){
13
13
  selectors.forEach(selector=>{
14
14
  let originalSelector=selector.trim()
15
15
  selector=this.removeSpaces(selector)
16
16
  this.stringValues=[]
17
17
  selector=selector.replace(/\[.*?\]/g,(value)=>{
18
18
  this.stringValues.push(value)
19
19
  return `[${this.stringValues.length-1}]`
20
20
  })
21
21
  let [element,ancestors]=this.splitAndCutLast(selector,' ')
22
22
  element=this.getFamily(element)
23
23
  if (ancestors.length>0)
24
24
  element.ancestors=ancestors.map(ancestor=>this.getFamily(ancestor))
25
25
  element.group=originalSelector
26
26
  this.selectors.push(element)
27
27
  });
28
28
  }
29
29
  splitAndCutLast(string,splitBy){
30
30
  const array=string.split(splitBy);
31
31
  const last=array.pop();
32
32
  return [last,array];
33
33
  }
34
34
  getFamily(group,element,prev,prevAny,sign){
35
35
  if (group.match(/\~|\+/)!==null){
36
36
  let [last,prevBrothers]=this.splitAndCutLast(group,/\~|\+/)
37
37
  let signs=group.replace(last,'')
38
38
  prevBrothers.forEach(el=>signs=signs.replace(el,''))
39
39
  signs=signs.match(/\~|\+/g)
40
40
  if (signs.length==1){
41
41
  sign=signs[0]
42
42
  } else if (signs.length>1){
43
43
  sign=signs.splice(signs.length-1,signs.length-1)[0]
44
44
  prevBrothers[0]=prevBrothers.map((b,i)=>{
45
45
  if (i< prevBrothers.length-1) b+=signs[i]
46
46
  return b
47
47
  }).join('')
48
48
  prevBrothers[0]=this.getFamily(prevBrothers[0])
49
49
  }
50
50
  if (sign=='~') prevAny=prevBrothers[0]
51
51
  else if (sign=='+') prev=prevBrothers[0]
52
52
  element=last
53
53
  } else element=group
54
54
  let family
55
55
  if (prev || prevAny){
56
56
  family=this.getParents(element)
57
57
  if (prev) family.prev=this.getParents(prev)
58
58
  if (prevAny) family.prevAny=this.getParents(prevAny)
59
59
  } else family=this.getParents(element)
60
60
  if (family.query!==group) family.group=group
61
61
  return family
62
62
  }
63
63
  getParents(selector){
64
64
  if (typeof selector=='string'){
65
65
  let [element,parents]=this.splitAndCutLast(selector,'>')
66
66
  element=this.buildElement(element)
67
67
  parents=parents.map(parent=>this.buildElement(parent))
68
68
  if (parents.length>0) element.parents=parents
69
69
  return element
70
70
  } else return selector
71
71
  }
72
72
  buildElement(element,id=null,tag=null,classList=[]){
73
73
  let query=element
74
74
  element=element.replace(/\#(\w-?)*/,$id=>{
75
75
  id=$id.replace(/^\#/,''); return ''
76
76
  })
77
77
  element=element.replace(/\.(\w-?)*/,$class=>{
78
78
  classList.push($class.replace(/^\./,'')); return ''
79
79
  })
80
80
  element=element.replace(/(\w\:?-?)*/,$tag=>{
81
81
  tag=$tag=='' ? null : $tag; return ''
82
82
  })
83
83
  let attribs=this.getAttributes(element)
84
84
  element={ query }
85
85
  if (id) element.id=id
86
86
  if (tag) element.tag=tag
87
87
  if (classList.length>0) element.classList=classList
88
88
  if (attribs.length>0) element.attribs=attribs
89
89
  return element
90
90
  }
91
91
  getAttributes(element){
92
92
  let attribs=this.stringValues.filter((value,index)=>{
93
93
  let searchValue=`[${index}]`
94
94
  if (element.match(searchValue)) return true
95
95
  else return false
96
96
  })
97
97
  attribs=attribs.map(attrib=>{
98
98
  let query=attrib
99
99
  attrib=attrib.replace('[','').replace(']','')
100
100
  let [name,value]=attrib.split(/[\~\|\^\$\*]?\=/)
101
101
  let sign=attrib.replace(name,'').replace(value,'')
102
102
  attrib={ query }
103
103
  if (name) attrib.name=name
104
104
  if (value) attrib.value=value.trim().replace(/^\"/,'').replace(/\"$/,'')
105
105
  if (sign){
106
106
  attrib.sign=sign
107
107
  attrib.check=this.getAttribFn(sign).bind(attrib)
108
108
  }
109
109
  return attrib
110
110
  });
111
111
  return attribs
112
112
  }
113
113
  getAttribFn(sign){
114
114
  if (sign=='=') return function (value){ return value===this.value }
115
115
  if (sign=='*=') return function (value){ return value.includes(this.value) }
116
116
  if (sign=='^=') return function (value){ return value.startsWith(this.value) }
117
117
  if (sign=='$=') return function (value){ return value.endsWith(this.value) }
118
118
  if (sign=='|=') return function (value){
119
119
  return value.trim().split(' ').length==1
120
120
  && (value.startsWith(this.value) || value.startsWith(this.value+'-'))
121
121
  ? true : false
122
122
  }
123
123
  if (sign=='~=') return function (value){
124
124
  return this.value.trim().split(' ').length==1 && value.includes(this.value) ? true : false
125
125
  }
126
126
  }
127
127
  removeSpaces(selector){
128
128
  selector=selector.replace(/\s{2}/g,' ')
129
129
  selector=selector.replace(/\s?\^?\$?\|?\~?\*?\=\s*/g,(m)=>m.trim())
130
130
  selector=selector.replace(/\s?(\+|\~|\>)\s?/g,(m)=>m.trim())
131
131
  return selector
132
132
  }
133
133
  }
134
- function checkElement(el,selector){
135
134
  if(selector==undefined) return true
136
135
  if(el==null) return false
137
136
  let{tag,classList,attributes,id,prev,ancestors,parents,prevAny}=selector
138
137
  if(typeof el==='string') return false
139
138
  if(id!==undefined && el.id===null) return false
140
139
  if(id && id!==el.id) return false
141
140
  if(tag && el.tagName===undefined) return false
142
141
  else if(tag && tag!==el.tagName) return false
143
142
  const clas=el.attributes.class
144
143
  if(classList!==undefined && (clas===undefined || clas==='')) return false
145
144
  else if(classList!==undefined){
146
145
  if(classList.every(e=>el.classList.contains(e))===false) return false
147
146
  }
148
147
  if(checkattributes(attributes,el)===false) return false
149
148
  if(checkElement(el.prev,prev)===false) return false
150
149
  if(checkAncestors(el.ancestors,ancestors)===false) return false
151
150
  if(checkParents(el.ancestors,parents)===false) return false
152
151
  if(el.parent){
153
152
  if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
154
153
  }
155
154
  return true
155
+ function checkElement(el,selector){
156
156
  if(selector==undefined) return true
157
157
  if(el==null) return false
158
158
  let{tag,classList,attribs:attributes,id,prev,ancestors,parents,prevAny}=selector
159
159
  if(typeof el==='string') return false
160
160
  if(id!==undefined && el.id===null) return false
161
161
  if(id && id!==el.id) return false
162
162
  if(tag && el.tagName===undefined) return false
163
163
  else if(tag && tag!==el.tagName) return false
164
164
  const clas=el.attributes.class
165
165
  if(classList!==undefined && (clas===undefined || clas==='')) return false
166
166
  else if(classList!==undefined){
167
167
  if(classList.every(e=>el.classList.contains(e))===false) return false
168
168
  }
169
169
  if(checkattributes(attributes,el)===false) return false
170
170
  if(checkElement(el.prev,prev)===false) return false
171
171
  if(checkAncestors(el.ancestors,ancestors)===false) return false
172
172
  if(checkParents(el.ancestors,parents)===false) return false
173
173
  if(el.parent){
174
174
  if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
175
175
  }
176
176
  return true
177
177
  }
178
178
  function checkattributes(attributes=[],el){
179
179
  let elattributes=el.attributes
180
180
  let names=Object.keys(elattributes)
181
181
  let passedTests=0
182
182
  if(attributes) for(let i=0; i<attributes.length; i++){
183
183
  let{name,value,check}=attributes[i]
184
184
  if(name=='inner' && value!==undefined && check && el.inner){
185
185
  if(check(el.inner)) passedTests++
186
186
  }
187
187
  if(!names.includes(name)) continue
188
188
  else if(value==undefined) passedTests++
189
189
  else if(value && elattributes[name]){
190
190
  if(check(elattributes[name])==false) continue
191
191
  else passedTests++
192
192
  }
193
193
  }
194
194
  if(passedTests==attributes.length) return true
195
195
  else return false
196
196
  }
197
197
  function checkPrevAny(children=[],index,prevAny){
198
198
  let size=children.length
199
199
  if((size==0 || index==0) && prevAny) return false
200
200
  for(let i=index; i>=0; i--){
201
201
  if(checkElement(children[i],prevAny)) return true
202
202
  }
203
203
  return false
204
204
  }
205
205
  function checkAncestors(ancestors=[],selectorAncestors=[]){
206
206
  let count=0
207
207
  if(selectorAncestors.length==0) return true
208
208
  let endIndex=ancestors.length-1
209
209
  let selectorIndex=selectorAncestors.length-1
210
210
  while(selectorIndex>=0){
211
211
  for(let i=endIndex; i>=0; i--){
212
212
  endIndex=i-1
213
213
  if(checkElement(ancestors[i],selectorAncestors[selectorIndex])==true){
214
214
  count++
215
215
  break
216
216
  }
217
217
  }
218
218
  selectorIndex--
219
219
  }
220
220
  if(count==selectorAncestors.length) return true
221
221
  else return false
@@ -20,13 +20,14 @@ function buildStyle(attributes){
20
20
  const styles=attributes.style || "";
21
21
  con
22
22
 
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
- class Node{
45
44
  constructor(tagName,attributes={},parent=null){
46
45
  this.isSingle=false;
47
46
  this.tagName=tagName;
48
47
  this.attributes=attributes;
49
48
  this.childNodes=[];
50
49
  if (parent!==null) parent.childNodes.push(this)
51
50
  this.parent=parent;
52
51
  this._classList=null;
53
52
  this.__style=null;
54
53
  this._dataset=null
55
54
  }
56
55
  get id(){ return this.attributes.id || null; }
57
56
  get className(){return this.attributes.class || null}
58
57
  get parentNode(){ return this.parent }
59
58
  get ancestors(){
60
59
  const ancestors=[]
61
60
  let element=this.parent
62
61
  while (element.tagName!=='ROOT'){
63
62
  ancestors.push(element)
64
63
  element=element.parent
65
64
  }
66
65
  return ancestors.reverse()
67
66
  }
68
67
  get childIndex(){ return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null }
69
68
  get previousElementSibling(){ return this.prev }
70
69
  get prev(){
71
70
  if (!this.childIndex) return null
72
71
  return this.parent.childNodes[this.childIndex-1]
73
72
  }
74
73
  get nextElementSibling(){ return this.next }
75
74
  get next(){
76
75
  if (!this.childIndex) return null
77
76
  return this.parent.childNodes[this.childIndex+1] || null
78
77
  }
79
78
  get dataset(){
80
79
  if (!this._dataset) this._dataset=getDataset(this);
81
80
  return this._dataset;
82
81
  }
83
82
  get classList(){
84
83
  if (!this._classList) this._classList=new NodeClassList(this);
85
84
  return this._classList;
86
85
  }
87
86
  get style(){
88
87
  if (!this.__style) this.__style=buildStyle(this.attributes)
89
88
  return this.__style
90
89
  }
91
90
  get outerHTML(){
92
91
  const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
93
92
  return `<${this.tagName} ${attrs}>${this.innerHTML}</${this.tagName}>`;
94
93
  }
95
94
  getAttribute(attrName){ return this.attributes[attrName] || null }
96
95
  setAttribute(attrName,value){ this.attributes[attrName]=value }
97
96
  removeAttribute(attrName){ delete this.attributes[attrName] }
98
97
  remove(){
99
98
  if (!this.parent) return
100
99
  const index=this.childIndex;
101
100
  if (index!==null) this.parent.childNodes.splice(index,1);
102
101
  }
103
102
  get innerHTML(){
104
103
  return this.childNodes.map(child=>{
105
104
  if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
106
105
  else if (child instanceof TextNode) return child.textContent;
107
106
  else return child
108
107
  }).join("");
109
108
  }
110
109
  $$(query){return this.querySelectorAll(query)}
111
110
  querySelectorAll(query){
112
111
  const selectors=Query.get(query)
113
112
  return find(selectors,this,new Set())
114
113
  }
115
114
  $(query){return this.querySelector(query)}
116
115
  querySelector(query){
117
116
  const selectors=Query.get(query)
118
117
  return find(selectors,this,new Set(),true)[0] || null
119
118
  }
120
119
  getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
121
120
  getElementsByTagName(query){ return this.querySelectorAll(query) }
122
121
  getElementById(query){ return this.querySelector('#'+query) }
123
122
  get children(){
124
123
  return this.childNodes.filter(child=>{
125
124
  if (!(child instanceof Node)) return false
126
125
  if (child.tagName==='#comment') return false
127
126
  return true
128
127
  });
129
128
  }
130
129
  insertAdjacentElement(position,newElement){
131
130
  if(newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
132
131
  const pos=position.toLowerCase();
133
132
  if (pos==="afterbegin") this.childNodes.unshift(newElement);
134
133
  else if (pos==="beforeend") this.childNodes.push(newElement);
135
134
  if (!this.parent) return newElement
136
135
  if (pos==="beforebegin") this.parent.childNodes.unshift(newElement);
137
136
  else if (pos==="afterend") this.parent.childNodes.splice(this.childIndex+1,0,newElement);
138
137
  return newElement
139
138
  }
140
139
  insertAdjacentHTML(position,html){
141
140
  const newNode=parseHTML(html);
142
141
  return this.insertAdjacentElement(position,newNode);
143
142
  }
144
143
  insertAdjacentText(position,text){
145
144
  return this.insertAdjacentElement(position,new TextNode(text));
146
145
  }
147
146
  set innerHTML(html){
148
147
  const parsed=parseHTML(html);
149
148
  this.childNodes=parsed.childNodes;
150
149
  }
151
150
  set outerHTML(html){
152
151
  const parsed=parseHTML(html);
153
152
  if (!this.parent) return console.log('element has no parent node')
154
153
  const index=this.childIndex
155
154
  if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
156
155
  }
157
156
  appendChild(newChild){
158
157
  if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
159
158
  if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
160
159
  } else if(typeof newChild==='string') newChild=new TextNode(newChild)
161
160
  else return newChild
162
161
  this.childNodes.push(newChild);
163
162
  newChild.parent=this;
164
163
  return newChild;
165
164
  }
166
165
  get textContent(){
167
166
  if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
168
167
  return this.childNodes.map(child=>{
169
168
  if(child instanceof SingleNode) return ''
170
169
  if(child instanceof TextNode) return child.nodeValue
171
170
  if(child instanceof Node) return child.textContent;
172
171
  else return child;
173
172
  }).join(" ");
174
173
  }
175
174
  set textContent(value){
176
175
  this.childNodes=[];
177
176
  if (value!==null && value!==undefined){
178
177
  this.childNodes.push(value.toString());
179
178
  }
180
179
  }
180
+ function insertBefore(arr,index,newItem){
181
181
  const existingIndex=arr.indexOf(newItem);
182
182
  if (existingIndex!==-1) arr.splice(existingIndex,1);
183
183
  arr.splice(index,0,newItem);
184
+ }
184
185
  class Node{
185
186
  constructor(tagName,attributes={},parent=null){
186
187
  this.isSingle=false;
187
188
  this.tagName=tagName;
188
189
  this.attributes=attributes;
189
190
  this.childNodes=[];
190
191
  if (parent!==null) parent.childNodes.push(this)
191
192
  this.parent=parent;
192
193
  this._classList=null;
193
194
  this.__style=null;
194
195
  this._dataset=null
195
196
  }
196
197
  get id(){ return this.attributes.id ? this.attributes.id : null; }
197
198
  set id(newValue){ this.attributes.id=newValue; }
198
199
  get className(){return this.attributes.class || null}
199
200
  get parentNode(){ return this.parent }
200
201
  get ancestors(){
201
202
  if(!this.parent) return []
202
203
  const ancestors=[]
203
204
  let element=this.parent
204
205
  while (element.tagName!=='ROOT'){
205
206
  ancestors.push(element)
206
207
  element=element.parent
207
208
  }
208
209
  return ancestors.reverse()
209
210
  }
210
211
  get childNodeIndex(){
211
212
  if(!this.parent) return null
212
213
  return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
213
214
  }
214
215
  get childIndex(){
215
216
  if(!this.parent) return null
216
217
  return this.parent.children ? this.parent.children.indexOf(this) : null
217
218
  }
218
219
  get previousElementSibling(){ return this.prev }
219
220
  get prev(){
220
221
  if (!this.childIndex) return null
221
222
  return this.parent.children[this.childIndex-1]
222
223
  }
223
224
  get nextElementSibling(){ return this.next }
224
225
  get next(){
225
226
  if (!this.childIndex) return null
226
227
  return this.parent.children[this.childIndex+1] || null
227
228
  }
228
229
  get dataset(){
229
230
  if (!this._dataset) this._dataset=getDataset(this);
230
231
  return this._dataset;
231
232
  }
232
233
  get classList(){
233
234
  if (!this._classList) this._classList=new NodeClassList(this);
234
235
  return this._classList;
235
236
  }
236
237
  get style(){
237
238
  if (!this.__style) this.__style=buildStyle(this.attributes)
238
239
  return this.__style
239
240
  }
240
241
  get outerHTML(){
241
242
  const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
242
243
  return `<${this.tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this.tagName}>`;
243
244
  }
244
245
  getAttribute(attrName){ return this.attributes[attrName] || null }
245
246
  setAttribute(attrName,value){ this.attributes[attrName]=value }
246
247
  removeAttribute(attrName){ delete this.attributes[attrName] }
247
248
  remove(){
248
249
  if (!this.parent) return
249
250
  const index=this.childIndex;
250
251
  if (index!==null) this.parent.childNodes.splice(index,1);
251
252
  }
252
253
  get innerHTML(){
253
254
  return this.childNodes.map(child=>{
254
255
  if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
255
256
  else if (child instanceof TextNode) return child.textContent;
256
257
  else return child
257
258
  }).join("");
258
259
  }
259
260
  $$(query){return this.querySelectorAll(query)}
260
261
  querySelectorAll(query){
261
262
  const selectors=Query.get(query)
262
263
  return find(selectors,this,new Set())
263
264
  }
264
265
  $(query){return this.querySelector(query)}
265
266
  querySelector(query){
266
267
  const selectors=Query.get(query)
267
268
  return find(selectors,this,new Set(),true)[0] || null
268
269
  }
269
270
  getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
270
271
  getElementsByTagName(query){ return this.querySelectorAll(query) }
271
272
  getElementById(query){ return this.querySelector('#'+query) }
272
273
  get children(){
273
274
  return this.childNodes.filter(child=>{
274
275
  if (!(child instanceof Node)) return false
275
276
  if (child.tagName==='#comment') return false
276
277
  return true
277
278
  });
278
279
  }
279
280
  insertAdjacentElement(position,newElement){
280
281
  if(newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
281
282
  const pos=position.toLowerCase();
282
283
  if (pos==="afterbegin") this.childNodes.unshift(newElement);
283
284
  else if (pos==="beforeend") this.childNodes.push(newElement);
284
285
  newElement.parent=this
285
286
  if (!this.parent) return newElement
286
287
  if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
287
288
  else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
288
289
  newElement.parent=this.parent
289
290
  return newElement
290
291
  }
291
292
  insertAdjacentHTML(position,html){
292
293
  const newNode=parseHTML(html);
293
294
  newNode.childNodes.reverse().forEach(node=>{
294
295
  this.insertAdjacentElement(position,node);
295
296
  });
296
297
  return newNode
297
298
  }
298
299
  insertAdjacentText(position,text){
299
300
  return this.insertAdjacentElement(position,new TextNode(text));
300
301
  }
301
302
  set innerHTML(html){
302
303
  const parsed=parseHTML(html);
303
304
  this.childNodes=parsed.childNodes;
304
305
  }
305
306
  set outerHTML(html){
306
307
  const parsed=parseHTML(html);
307
308
  if (!this.parent) return console.log('element has no parent node')
308
309
  const index=this.childIndex
309
310
  if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
310
311
  }
311
312
  appendChild(newChild){
312
313
  if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
313
314
  if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
314
315
  } else if(typeof newChild==='string') newChild=new TextNode(newChild)
315
316
  else return newChild
316
317
  this.childNodes.push(newChild);
317
318
  newChild.parent=this;
318
319
  return newChild;
319
320
  }
320
321
  get textContent(){
321
322
  if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
322
323
  return this.childNodes.map(child=>{
323
324
  if(child instanceof SingleNode) return ''
324
325
  if(child instanceof TextNode) return child.nodeValue
325
326
  if(child instanceof Node) return child.textContent;
326
327
  else return child;
327
328
  }).join(" ");
328
329
  }
329
330
  set textContent(value){
330
331
  this.childNodes=[];
331
332
  if (value!==null && value!==undefined){
332
333
  this.childNodes.push(value.toString());
333
334
  }
334
335
  }
335
336
  }
336
337
  class SingleNode extends Node{
337
338
  constructor(tagName,attributes={},parent=null){
338
339
  if(attributes['?'] && tagName==='?xml') delete attributes['?']
339
340
  super(tagName,attributes,parent);
340
341
  this.isSingle=true
341
342
  }
342
343
  get outerHTML(){
343
344
  if (this.tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
344
345
  const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
345
346
  return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
346
347
  }
347
348
  get innerHTML(){ return ""; }
348
349
  set innerHTML(_){ }
349
350
  $(_){return null}
350
351
  $$(_){return []}
351
352
  querySelectorAll(_){ return []; }
352
353
  querySelector(_){ return null; }
353
354
  getElementsByClassName(_){ return []; }
354
355
  getElementsByTagName(_){ return []; }
355
356
  getElementById(_){ return null; }
356
357
  get children(){ return []; }
357
358
  insertAdjacentElement(_,__){ }
358
359
  insertAdjacentHTML(_,__){ }
359
360
  insertAdjacentText(_,__){ }
360
361
  appendChild(_){ }
361
362
  get textContent(){ return ""; }
362
363
  set textContent(_){ }
363
364
  }
364
365
  function parseAttributes(str){
365
366
  const attrs={};
366
367
  let key="";
367
368
  let value="";
368
369
  let isKey=true;
369
370
  let quoteChar=null;
370
371
  for (let i=0; i< str.length; i++){
371
372
  const char=str[i];
372
373
  if (isKey && (char==='=' || char===' ')){
373
374
  if (char==='=') isKey=false;
374
375
  else if (key.trim()){
375
376
  attrs[key.trim()]=true;
376
377
  key="";
377
378
  }
378
379
  continue;
379
380
  }
380
381
  if (!quoteChar && (char==='"' || char==="'")){
381
382
  quoteChar=char;
382
383
  continue;
383
384
  } else if (quoteChar && char===quoteChar){
384
385
  quoteChar=null;
385
386
  attrs[key.trim()]=value.trim();
386
387
  key=""; value=""; isKey=true;
387
388
  continue;
388
389
  }
389
390
  if (isKey) key+=char;
390
391
  else value+=char;
391
392
  }
392
393
  if (key.trim() &&!value) attrs[key.trim()]=true;
393
394
  return attrs;
394
395
  }
395
396
  const VOID_TAGS=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","!doctype",'?xml']);
396
- function parseHTML(html){
397
397
  const root=new Node("ROOT");
398
398
  const stack=[root];
399
399
  let currentText="",i=0;
400
400
  function parseSpecial(startStr,endStr,n1,n2,tag){
401
401
  if (!html.startsWith(startStr,i)) return false
402
402
  const end=html.indexOf(endStr,i+n1);
403
403
  const strNode=new Node(tag,{},stack[stack.length-1]);
404
404
  strNode.childNodes.push(html.substring(i+n1,end));
405
405
  i=end+n2;
406
406
  return true
407
407
  }
408
408
  while (i< html.length){
409
409
  if (parseSpecial("<!--","-->",4,3,'#comment')) continue
410
410
  if (parseSpecial("<script","</script>",8,9,'script')) continue
411
411
  if (parseSpecial("<style","</style>",7,8,'style')) continue
412
412
  if (html.startsWith("<![CDATA[",i)){
413
413
  const end=html.indexOf("]]>",i+9);
414
414
  if (end===-1) break;
415
415
  const content=html.substring(i+9,end);
416
416
  const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
417
417
  cdataNode.nodeValue=content;
418
418
  i=end+3;
419
419
  continue;
420
420
  }
421
421
  if (html.startsWith("<",i)){
422
422
  if (currentText.trim()){
423
423
  stack[stack.length-1].childNodes.push(new TextNode(currentText.trim()));
424
424
  currentText="";
425
425
  }
426
426
  let tagEnd=i+1;
427
427
  let insideQuotes=false;
428
428
  let quoteChar=null;
429
429
  while (tagEnd< html.length){
430
430
  const char=html[tagEnd];
431
431
  if (!insideQuotes && (char==='"' || char==="'")){
432
432
  insideQuotes=true;
433
433
  quoteChar=char;
434
434
  } else if (insideQuotes && char===quoteChar){
435
435
  insideQuotes=false;
436
436
  quoteChar=null;
437
437
  }
438
438
  if (!insideQuotes && char==='>') break;
439
439
  tagEnd++;
440
440
  }
441
441
  const tagContent=html.substring(i+1,tagEnd);
442
442
  if (tagContent.startsWith("/")) stack.pop();
443
443
  else{
444
444
  let isSelfClosing=tagContent.endsWith('/');
445
445
  const tagNameEnd=tagContent.search(/\s|>|\//);
446
446
  const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
447
447
  const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
448
448
  const attributes=parseAttributes(attributesString);
449
449
  if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
450
450
  else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
451
451
  }
452
452
  i=tagEnd+1;
453
453
  } else{
454
454
  currentText+=html[i];
455
455
  i++;
456
456
  }
457
457
  }
458
458
  if (currentText.trim()) stack[stack.length-1].childNodes.push(new TextNode(currentText.trim()));
459
459
  return root;
460
+ function parseHTML(html){
460
461
  const root=new Node("ROOT");
461
462
  const stack=[root];
462
463
  let currentText="",i=0;
463
464
  let max=0
464
465
  function parseScript(){
465
466
  if (!html.startsWith("<script",i)) return false;
466
467
  const openTagEnd=html.indexOf(">",i);
467
468
  if (openTagEnd===-1) return false;
468
469
  const attributesString=html.substring(i+7,openTagEnd).trim();
469
470
  const attributes=parseAttributes(attributesString);
470
471
  let closeTagStart=html.indexOf("</script>",openTagEnd);
471
472
  if (closeTagStart===-1) return false;
472
473
  const content=html.substring(openTagEnd+1,closeTagStart);
473
474
  const scriptNode=new Node('script',attributes,stack[stack.length-1]);
474
475
  if(content.length>0) scriptNode.childNodes.push(content);
475
476
  i=closeTagStart+9;
476
477
  return true;
477
478
  }
478
479
  function parseSpecial(startStr,endStr,n1,n2,tag){
479
480
  if (!html.startsWith(startStr,i)) return false
480
481
  const end=html.indexOf(endStr,i+n1);
481
482
  const strNode=new Node(tag,{},stack[stack.length-1]);
482
483
  strNode.childNodes.push(html.substring(i+n1,end));
483
484
  i=end+n2;
484
485
  return true
485
486
  }
486
487
  while (i< html.length){
487
488
  if(i>=max) max=i;
488
489
  else break;
489
490
  if (parseScript()) continue
490
491
  if (parseSpecial("<!--","-->",4,3,'#comment')) continue
491
492
  if (parseSpecial("<style","</style>",7,8,'style')) continue
492
493
  if (html.startsWith("<![CDATA[",i)){
493
494
  const end=html.indexOf("]]>",i+9);
494
495
  if (end===-1) break;
495
496
  const content=html.substring(i+9,end);
496
497
  const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
497
498
  cdataNode.nodeValue=content;
498
499
  i=end+3;
499
500
  continue;
500
501
  }
501
502
  if (html.startsWith("<",i)){
502
503
  if (currentText){
503
504
  stack[stack.length-1].childNodes.push(new TextNode(currentText));
504
505
  currentText="";
505
506
  }
506
507
  let tagEnd=i+1;
507
508
  let insideQuotes=false;
508
509
  let quoteChar=null;
509
510
  while (tagEnd< html.length){
510
511
  const char=html[tagEnd];
511
512
  if (!insideQuotes && (char==='"' || char==="'")){
512
513
  insideQuotes=true;
513
514
  quoteChar=char;
514
515
  } else if (insideQuotes && char===quoteChar){
515
516
  insideQuotes=false;
516
517
  quoteChar=null;
517
518
  }
518
519
  if (!insideQuotes && char==='>') break;
519
520
  tagEnd++;
520
521
  }
521
522
  const tagContent=html.substring(i+1,tagEnd);
522
523
  if (tagContent.startsWith("/")) stack.pop();
523
524
  else{
524
525
  let isSelfClosing=tagContent.endsWith('/');
525
526
  const tagNameEnd=tagContent.search(/\s|>|\//);
526
527
  const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
527
528
  const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
528
529
  const attributes=parseAttributes(attributesString);
529
530
  if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
530
531
  else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
531
532
  }
532
533
  i=tagEnd+1;
533
534
  } else{
534
535
  currentText+=html[i];
535
536
  i++;
536
537
  }
537
538
  }
538
539
  if (currentText.trim()) stack[stack.length-1].childNodes.push(new TextNode(currentText));
539
540
  return root;
540
541
  }
541
542
  export default { parseHTML, Node, Query, TextNode, SingleNode }