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