als-document 1.0.1-alpha → 1.0.1-beta
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 +14 -6
- package/index.js +14 -6
- package/index.mjs +14 -6
- package/package.json +2 -5
- package/readme.md +32 -3
- package/src/build.js +5 -4
- package/src/node/node.js +51 -14
- package/src/node/root.js +11 -0
- package/src/node/single-node.js +6 -5
- package/src/parse/cache.js +33 -0
- package/src/parse/parse-atts.js +1 -1
- package/src/parse/parser.js +30 -6
- package/src/query/check-element.js +2 -3
- package/tests/cache.js +19 -0
- package/tests/index.html +6 -5
- package/tests/node.js +1 -1
- package/tests/parse-real.js +3 -2
- package/tests/parser.js +6 -6
- package/tests/test.js +169 -0
package/index.js
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(el.isSpecial) 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
|
|
@@ -20,13 +20,21 @@ 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]
|
|
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])=>val.length ? `${key}="${val}"` : key).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.childNodeIndex;
|
|
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' || pos==='beforeend'){
|
|
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
|
return newElement
|
|
287
288
|
}
|
|
288
289
|
if (!this.parent) throw new Error("Can't insert element to element without parent")
|
|
289
290
|
if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
|
|
290
291
|
else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
|
|
291
292
|
newElement.parent=this.parent
|
|
292
293
|
return newElement
|
|
293
294
|
}
|
|
294
295
|
insertAdjacentHTML(position,html){
|
|
295
296
|
const newNode=parseHTML(html);
|
|
296
297
|
newNode.childNodes.reverse().forEach(node=>{
|
|
297
298
|
this.insertAdjacentElement(position,node);
|
|
298
299
|
});
|
|
299
300
|
return newNode
|
|
300
301
|
}
|
|
301
302
|
insertAdjacentText(position,text){
|
|
302
303
|
return this.insertAdjacentElement(position,new TextNode(text));
|
|
303
304
|
}
|
|
304
305
|
insert(position,element){
|
|
305
306
|
const positions=['beforebegin','afterbegin','beforeend','afterend']
|
|
306
307
|
if(positions[position]) position=positions[position]
|
|
307
308
|
if(typeof element==='string'){
|
|
308
309
|
element=element.trim()
|
|
309
310
|
if(element.startsWith('<') && element.endsWith('>')){
|
|
310
311
|
return this.insertAdjacentHTML(position,element)
|
|
311
312
|
}
|
|
312
313
|
return this.insertAdjacentText(position,element)
|
|
313
314
|
}
|
|
314
315
|
return this.insertAdjacentElement(position,element)
|
|
315
316
|
}
|
|
316
317
|
set innerHTML(html){
|
|
317
318
|
const parsed=parseHTML(html);
|
|
318
319
|
this.childNodes=parsed.childNodes;
|
|
319
320
|
}
|
|
320
321
|
set outerHTML(html){
|
|
321
322
|
const parsed=parseHTML(html);
|
|
322
323
|
if (!this.parent) return console.log('element has no parent node')
|
|
323
324
|
const index=this.childIndex
|
|
324
325
|
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
325
326
|
}
|
|
326
327
|
appendChild(newChild){
|
|
327
328
|
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
328
329
|
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
329
330
|
} else if(typeof newChild==='string') newChild=new TextNode(newChild)
|
|
330
331
|
else return newChild
|
|
331
332
|
this.childNodes.push(newChild);
|
|
332
333
|
newChild.parent=this;
|
|
333
334
|
return newChild;
|
|
334
335
|
}
|
|
335
336
|
get textContent(){
|
|
336
337
|
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
337
338
|
return this.childNodes.map(child=>{
|
|
338
339
|
if(child instanceof SingleNode) return ''
|
|
339
340
|
if(child instanceof TextNode) return child.nodeValue
|
|
340
341
|
if(child instanceof Node) return child.textContent;
|
|
341
342
|
else return child;
|
|
342
343
|
}).join(" ");
|
|
343
344
|
}
|
|
344
345
|
set textContent(value){
|
|
345
346
|
this.childNodes=[];
|
|
346
347
|
if (value!==null && value!==undefined){
|
|
347
348
|
this.childNodes.push(value.toString());
|
|
348
349
|
}
|
|
349
350
|
}
|
|
350
351
|
}
|
|
351
|
-
class SingleNode extends Node{
|
|
352
352
|
constructor(tagName,attributes={},parent=null){
|
|
353
353
|
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
354
354
|
super(tagName,attributes,parent);
|
|
355
355
|
this.isSingle=true
|
|
356
356
|
}
|
|
357
357
|
get outerHTML(){
|
|
358
358
|
if (this.tagName==="#cdata-section") return `<![CDATA[${this.textContent}]]>`;
|
|
359
359
|
const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
|
|
360
360
|
return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
|
|
361
361
|
}
|
|
362
362
|
get innerHTML(){ return ""; }
|
|
363
363
|
set innerHTML(_){ }
|
|
364
364
|
$(_){return null}
|
|
365
365
|
$$(_){return []}
|
|
366
366
|
querySelectorAll(_){ return []; }
|
|
367
367
|
querySelector(_){ return null; }
|
|
368
368
|
getElementsByClassName(_){ return []; }
|
|
369
369
|
getElementsByTagName(_){ return []; }
|
|
370
370
|
getElementById(_){ return null; }
|
|
371
371
|
get children(){ return []; }
|
|
372
372
|
insertAdjacentElement(_,__){ }
|
|
373
373
|
insertAdjacentHTML(_,__){ }
|
|
374
374
|
insertAdjacentText(_,__){ }
|
|
375
375
|
appendChild(_){ }
|
|
376
376
|
get textContent(){ return ""; }
|
|
377
377
|
set textContent(_){ }
|
|
378
|
+
class SingleNode extends Node{
|
|
378
379
|
constructor(tagName,attributes={},parent=null){
|
|
379
380
|
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
380
381
|
super(tagName,attributes,parent);
|
|
381
382
|
this.isSingle=true
|
|
382
383
|
}
|
|
383
384
|
get outerHTML(){
|
|
384
385
|
if (this.tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
|
|
385
386
|
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
386
387
|
return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
|
|
387
388
|
}
|
|
388
389
|
get innerHTML(){ return ""; }
|
|
389
390
|
set innerHTML(_){ }
|
|
390
391
|
$(_){return null}
|
|
391
392
|
$$(_){return []}
|
|
392
393
|
querySelectorAll(_){ return []; }
|
|
393
394
|
querySelector(_){ return null; }
|
|
394
395
|
getElementsByClassName(_){ return []; }
|
|
395
396
|
getElementsByTagName(_){ return []; }
|
|
396
397
|
getElementById(_){ return null; }
|
|
397
398
|
get children(){ return []; }
|
|
398
399
|
insertAdjacentElement(_,__){ }
|
|
399
400
|
insertAdjacentHTML(_,__){ }
|
|
400
401
|
insertAdjacentText(_,__){ }
|
|
401
402
|
appendChild(_){ }
|
|
402
403
|
insert(_,__){ }
|
|
403
404
|
get textContent(){ return ""; }
|
|
404
405
|
set textContent(_){ }
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
class Root extends Node{
|
|
405
409
|
constructor(){
|
|
406
410
|
super('ROOT',{},null);
|
|
407
411
|
this.isSingle=false
|
|
408
412
|
}
|
|
409
413
|
get body(){return this.$('body')}
|
|
410
414
|
get head(){return this.$('head')}
|
|
411
415
|
get title(){return this.$('title')}
|
|
412
416
|
set title(title){return this.$('title').innerHTML=title}
|
|
413
417
|
}
|
|
414
|
-
function parseAttributes(str){
|
|
415
418
|
const attrs={};
|
|
416
419
|
let key="";
|
|
417
420
|
let value="";
|
|
418
421
|
let isKey=true;
|
|
419
422
|
let quoteChar=null;
|
|
420
423
|
for (let i=0; i< str.length; i++){
|
|
421
424
|
const char=str[i];
|
|
422
425
|
if (isKey && (char==='=' || char===' ')){
|
|
423
426
|
if (char==='=') isKey=false;
|
|
424
427
|
else if (key.trim()){
|
|
425
428
|
attrs[key.trim()]=true;
|
|
426
429
|
key="";
|
|
427
430
|
}
|
|
428
431
|
continue;
|
|
429
432
|
}
|
|
430
433
|
if (!quoteChar && (char==='"' || char==="'")){
|
|
431
434
|
quoteChar=char;
|
|
432
435
|
continue;
|
|
433
436
|
} else if (quoteChar && char===quoteChar){
|
|
434
437
|
quoteChar=null;
|
|
435
438
|
attrs[key.trim()]=value.trim();
|
|
436
439
|
key=""; value=""; isKey=true;
|
|
437
440
|
continue;
|
|
438
441
|
}
|
|
439
442
|
if (isKey) key+=char;
|
|
440
443
|
else value+=char;
|
|
441
444
|
}
|
|
442
445
|
if (key.trim() &&!value) attrs[key.trim()]=true;
|
|
443
446
|
return attrs;
|
|
447
|
+
function parseAttributes(str){
|
|
444
448
|
const attrs={};
|
|
445
449
|
let key="";
|
|
446
450
|
let value="";
|
|
447
451
|
let isKey=true;
|
|
448
452
|
let quoteChar=null;
|
|
449
453
|
for (let i=0; i< str.length; i++){
|
|
450
454
|
const char=str[i];
|
|
451
455
|
if (isKey && (char==='=' || char===' ')){
|
|
452
456
|
if (char==='=') isKey=false;
|
|
453
457
|
else if (key.trim()){
|
|
454
458
|
attrs[key.trim()]=true;
|
|
455
459
|
key="";
|
|
456
460
|
}
|
|
457
461
|
continue;
|
|
458
462
|
}
|
|
459
463
|
if (!quoteChar && (char==='"' || char==="'")){
|
|
460
464
|
quoteChar=char;
|
|
461
465
|
continue;
|
|
462
466
|
} else if (quoteChar && char===quoteChar){
|
|
463
467
|
quoteChar=null;
|
|
464
468
|
attrs[key.trim()]=value.trim();
|
|
465
469
|
key=""; value=""; isKey=true;
|
|
466
470
|
continue;
|
|
467
471
|
}
|
|
468
472
|
if (isKey) key+=char;
|
|
469
473
|
else value+=char;
|
|
470
474
|
}
|
|
471
475
|
if (key.trim() &&!value) attrs[key.trim()]='';
|
|
472
476
|
return attrs;
|
|
473
477
|
}
|
|
474
478
|
const VOID_TAGS=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","!doctype",'?xml']);
|
|
475
|
-
function parseHTML(html){
|
|
476
479
|
const root=new Node("ROOT");
|
|
477
480
|
const stack=[root];
|
|
478
481
|
let currentText="",i=0;
|
|
479
482
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
480
483
|
if (!html.startsWith(startStr,i)) return false
|
|
481
484
|
const end=html.indexOf(endStr,i+n1);
|
|
482
485
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
483
486
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
484
487
|
i=end+n2;
|
|
485
488
|
return true
|
|
486
489
|
}
|
|
487
490
|
while (i< html.length){
|
|
488
491
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
489
492
|
if (parseSpecial("<script","</script>",8,9,'script')) continue
|
|
490
493
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
491
494
|
if (html.startsWith("<![CDATA[",i)){
|
|
492
495
|
const end=html.indexOf("]]>",i+9);
|
|
493
496
|
if (end===-1) break;
|
|
494
497
|
const content=html.substring(i+9,end);
|
|
495
498
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
496
499
|
cdataNode.nodeValue=content;
|
|
497
500
|
i=end+3;
|
|
498
501
|
continue;
|
|
499
502
|
}
|
|
500
503
|
if (html.startsWith("<",i)){
|
|
501
504
|
if (currentText.trim()){
|
|
502
505
|
stack[stack.length-1].childNodes.push(new TextNode(currentText.trim()));
|
|
503
506
|
currentText="";
|
|
504
507
|
}
|
|
505
508
|
let tagEnd=i+1;
|
|
506
509
|
let insideQuotes=false;
|
|
507
510
|
let quoteChar=null;
|
|
508
511
|
while (tagEnd< html.length){
|
|
509
512
|
const char=html[tagEnd];
|
|
510
513
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
511
514
|
insideQuotes=true;
|
|
512
515
|
quoteChar=char;
|
|
513
516
|
} else if (insideQuotes && char===quoteChar){
|
|
514
517
|
insideQuotes=false;
|
|
515
518
|
quoteChar=null;
|
|
516
519
|
}
|
|
517
520
|
if (!insideQuotes && char==='>') break;
|
|
518
521
|
tagEnd++;
|
|
519
522
|
}
|
|
520
523
|
const tagContent=html.substring(i+1,tagEnd);
|
|
521
524
|
if (tagContent.startsWith("/")) stack.pop();
|
|
522
525
|
else{
|
|
523
526
|
let isSelfClosing=tagContent.endsWith('/');
|
|
524
527
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
525
528
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
526
529
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
527
530
|
const attributes=parseAttributes(attributesString);
|
|
528
531
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
529
532
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
530
533
|
}
|
|
531
534
|
i=tagEnd+1;
|
|
532
535
|
} else{
|
|
533
536
|
currentText+=html[i];
|
|
534
537
|
i++;
|
|
535
538
|
}
|
|
536
539
|
}
|
|
537
540
|
if (currentText.trim()) stack[stack.length-1].childNodes.push(new TextNode(currentText.trim()));
|
|
538
541
|
return root;
|
|
542
|
+
function parseHTML(html){
|
|
539
543
|
const root=new Root();
|
|
540
544
|
const stack=[root];
|
|
541
545
|
let currentText="",i=0;
|
|
542
546
|
let max=0
|
|
543
547
|
function parseScript(){
|
|
544
548
|
if (!html.startsWith("<script",i)) return false;
|
|
545
549
|
const openTagEnd=html.indexOf(">",i);
|
|
546
550
|
if (openTagEnd===-1) return false;
|
|
547
551
|
const attributesString=html.substring(i+7,openTagEnd).trim();
|
|
548
552
|
const attributes=parseAttributes(attributesString);
|
|
549
553
|
let closeTagStart=html.indexOf("</script>",openTagEnd);
|
|
550
554
|
if (closeTagStart===-1) return false;
|
|
551
555
|
const content=html.substring(openTagEnd+1,closeTagStart);
|
|
552
556
|
const scriptNode=new Node('script',attributes,stack[stack.length-1]);
|
|
553
557
|
if(content.length>0) scriptNode.childNodes.push(content);
|
|
554
558
|
i=closeTagStart+9;
|
|
555
559
|
return true;
|
|
556
560
|
}
|
|
557
561
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
558
562
|
if (!html.startsWith(startStr,i)) return false
|
|
559
563
|
const end=html.indexOf(endStr,i+n1);
|
|
560
564
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
561
565
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
562
566
|
i=end+n2;
|
|
563
567
|
return true
|
|
564
568
|
}
|
|
565
569
|
while (i< html.length){
|
|
566
570
|
if(i>=max) max=i;
|
|
567
571
|
else break;
|
|
568
572
|
if (parseScript()) continue
|
|
569
573
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
570
574
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
571
575
|
if (html.startsWith("<![CDATA[",i)){
|
|
572
576
|
const end=html.indexOf("]]>",i+9);
|
|
573
577
|
if (end===-1) break;
|
|
574
578
|
const content=html.substring(i+9,end);
|
|
575
579
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
576
580
|
cdataNode.nodeValue=content;
|
|
577
581
|
i=end+3;
|
|
578
582
|
continue;
|
|
579
583
|
}
|
|
580
584
|
if (html.startsWith("<",i)){
|
|
581
585
|
if (currentText && stack[stack.length-1]){
|
|
582
586
|
const textNode=new TextNode(currentText)
|
|
583
587
|
stack[stack.length-1].childNodes.push(textNode);
|
|
584
588
|
textNode.parent=stack[stack.length-1]
|
|
585
589
|
currentText="";
|
|
586
590
|
}
|
|
587
591
|
let tagEnd=i+1;
|
|
588
592
|
let insideQuotes=false;
|
|
589
593
|
let quoteChar=null;
|
|
590
594
|
while (tagEnd< html.length){
|
|
591
595
|
const char=html[tagEnd];
|
|
592
596
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
593
597
|
insideQuotes=true;
|
|
594
598
|
quoteChar=char;
|
|
595
599
|
} else if (insideQuotes && char===quoteChar){
|
|
596
600
|
insideQuotes=false;
|
|
597
601
|
quoteChar=null;
|
|
598
602
|
}
|
|
599
603
|
if (!insideQuotes && char==='>') break;
|
|
600
604
|
tagEnd++;
|
|
601
605
|
}
|
|
602
606
|
const tagContent=html.substring(i+1,tagEnd);
|
|
603
607
|
if (tagContent.startsWith("/")) stack.pop();
|
|
604
608
|
else{
|
|
605
609
|
let isSelfClosing=tagContent.endsWith('/');
|
|
606
610
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
607
611
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
608
612
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
609
613
|
const attributes=parseAttributes(attributesString);
|
|
610
614
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
611
615
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
612
616
|
}
|
|
613
617
|
i=tagEnd+1;
|
|
614
618
|
} else{
|
|
615
619
|
currentText+=html[i];
|
|
616
620
|
i++;
|
|
617
621
|
}
|
|
618
622
|
}
|
|
619
623
|
if (currentText.trim() && stack[stack.length-1]) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
620
624
|
return root;
|
|
625
|
+
}
|
|
626
|
+
function buildFromCache(cached){
|
|
621
627
|
function buildNode(cache,parent=null){
|
|
622
628
|
if(typeof cache==='string') return parent.childNodes.push(cache)
|
|
623
629
|
const{isSingle,tagName,attributes,childNodes,textContent}=cache
|
|
624
630
|
if(textContent) return parent.childNodes.push(new TextNode(textContent))
|
|
625
631
|
if(isSingle) return parent.childNodes.push(new SingleNode(tagName,attributes))
|
|
626
632
|
const newDoc=tagName==='ROOT' ? new Root() : new Node(tagName,attributes,parent)
|
|
627
633
|
childNodes.forEach(childNode=>{
|
|
628
634
|
buildNode(childNode,newDoc)
|
|
629
635
|
});
|
|
630
636
|
return newDoc
|
|
631
637
|
}
|
|
632
638
|
return buildNode(cached)
|
|
639
|
+
}
|
|
633
640
|
|
|
641
|
+
function cacheDoc(doc){
|
|
634
642
|
const props=['isSingle','tagName','attributes']
|
|
635
643
|
function addToCache(element,cache={}){
|
|
636
644
|
if(typeof element==='string') return element
|
|
637
645
|
if(element.nodeName==='#text') return{textContent:element.textContent}
|
|
638
646
|
props.forEach(prop=>{
|
|
639
647
|
if(element[prop]) cache[prop]=element[prop]
|
|
640
648
|
});
|
|
641
649
|
if(!element.childNodes) return cache
|
|
642
650
|
cache.childNodes=[]
|
|
643
651
|
element.childNodes.forEach(childNode=>{
|
|
644
652
|
cache.childNodes.push(addToCache(childNode))
|
|
645
653
|
});
|
|
646
654
|
return cache
|
|
647
655
|
}
|
|
648
656
|
return addToCache(doc)
|
|
649
657
|
}
|
|
650
|
-
module.exports = { parseHTML, Node, Query, TextNode, SingleNode }
|
|
658
|
+
module.exports = { parseHTML, Node, Query, TextNode, SingleNode, buildFromCache, cacheDoc, Root }
|