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/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(el.isSpecial) return false
|
|
141
140
|
if(id!==undefined && el.id===null) return false
|
|
142
141
|
if(id && id!==el.id) return false
|
|
143
142
|
if(tag && el.tagName===undefined) return false
|
|
144
143
|
else if(tag && tag!==el.tagName) return false
|
|
145
144
|
const clas=el.attributes.class
|
|
146
145
|
if(classList!==undefined && (clas===undefined || clas==='')) return false
|
|
147
146
|
else if(classList!==undefined){
|
|
148
147
|
if(classList.every(e=>el.classList.contains(e))===false) return false
|
|
149
148
|
}
|
|
150
149
|
if(checkattributes(attributes,el)===false) return false
|
|
151
150
|
if(checkElement(el.prev,prev)===false) return false
|
|
152
151
|
if(checkAncestors(el.ancestors,ancestors)===false) return false
|
|
153
152
|
if(checkParents(el.ancestors,parents)===false) return false
|
|
154
153
|
if(el.parent){
|
|
155
154
|
if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
|
|
156
155
|
}
|
|
157
156
|
return true
|
|
157
|
+
function checkElement(el,selector){
|
|
158
158
|
if(selector==undefined) return true
|
|
159
159
|
if(el==null) return false
|
|
160
160
|
let{tag,classList,attribs:attributes,id,prev,ancestors,parents,prevAny}=selector
|
|
161
161
|
if(typeof el==='string') return false
|
|
162
162
|
if(id!==undefined && el.id===null) return false
|
|
163
163
|
if(id && id!==el.id) return false
|
|
164
164
|
if(tag && el.tagName===undefined) return false
|
|
165
165
|
else if(tag && tag!==el.tagName) return false
|
|
166
166
|
const clas=el.attributes.class
|
|
167
167
|
if(classList!==undefined && (clas===undefined || clas==='')) return false
|
|
168
168
|
else if(classList!==undefined){
|
|
169
169
|
if(classList.every(e=>el.classList.contains(e))===false) return false
|
|
170
170
|
}
|
|
171
171
|
if(checkattributes(attributes,el)===false) return false
|
|
172
172
|
if(checkElement(el.prev,prev)===false) return false
|
|
173
173
|
if(checkAncestors(el.ancestors,ancestors)===false) return false
|
|
174
174
|
if(checkParents(el.ancestors,parents)===false) return false
|
|
175
175
|
if(el.parent){
|
|
176
176
|
if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
|
|
177
177
|
}
|
|
178
178
|
return true
|
|
179
179
|
}
|
|
180
180
|
function checkattributes(attributes=[],el){
|
|
181
181
|
let elattributes=el.attributes
|
|
182
182
|
let names=Object.keys(elattributes)
|
|
183
183
|
let passedTests=0
|
|
184
184
|
if(attributes) for(let i=0; i<attributes.length; i++){
|
|
185
185
|
let{name,value,check}=attributes[i]
|
|
186
186
|
if(name=='inner' && value!==undefined && check && el.inner){
|
|
187
187
|
if(check(el.inner)) passedTests++
|
|
188
188
|
}
|
|
189
189
|
if(!names.includes(name)) continue
|
|
190
190
|
else if(value==undefined) passedTests++
|
|
191
191
|
else if(value && elattributes[name]){
|
|
192
192
|
if(check(elattributes[name])==false) continue
|
|
193
193
|
else passedTests++
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
if(passedTests==attributes.length) return true
|
|
197
197
|
else return false
|
|
198
198
|
}
|
|
199
199
|
function checkPrevAny(children=[],index,prevAny){
|
|
200
200
|
let size=children.length
|
|
201
201
|
if((size==0 || index==0) && prevAny) return false
|
|
202
202
|
for(let i=index; i>=0; i--){
|
|
203
203
|
if(checkElement(children[i],prevAny)) return true
|
|
204
204
|
}
|
|
205
205
|
return false
|
|
206
206
|
}
|
|
207
207
|
function checkAncestors(ancestors=[],selectorAncestors=[]){
|
|
208
208
|
let count=0
|
|
209
209
|
if(selectorAncestors.length==0) return true
|
|
210
210
|
let endIndex=ancestors.length-1
|
|
211
211
|
let selectorIndex=selectorAncestors.length-1
|
|
212
212
|
while(selectorIndex>=0){
|
|
213
213
|
for(let i=endIndex; i>=0; i--){
|
|
214
214
|
endIndex=i-1
|
|
215
215
|
if(checkElement(ancestors[i],selectorAncestors[selectorIndex])==true){
|
|
216
216
|
count++
|
|
217
217
|
break
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
selectorIndex--
|
|
221
221
|
}
|
|
222
222
|
if(count==selectorAncestors.length) return true
|
|
223
223
|
else return false
|
|
@@ -21,14 +21,22 @@ 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]
|
|
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])=>val.length ? `${key}="${val}"` : key).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.childNodeIndex;
|
|
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' || pos==='beforeend'){
|
|
284
285
|
if (pos==="afterbegin") this.childNodes.unshift(newElement);
|
|
285
286
|
else if (pos==="beforeend") this.childNodes.push(newElement);
|
|
286
287
|
newElement.parent=this
|
|
287
288
|
return newElement
|
|
288
289
|
}
|
|
289
290
|
if (!this.parent) throw new Error("Can't insert element to element without parent")
|
|
290
291
|
if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
|
|
291
292
|
else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
|
|
292
293
|
newElement.parent=this.parent
|
|
293
294
|
return newElement
|
|
294
295
|
}
|
|
295
296
|
insertAdjacentHTML(position,html){
|
|
296
297
|
const newNode=parseHTML(html);
|
|
297
298
|
newNode.childNodes.reverse().forEach(node=>{
|
|
298
299
|
this.insertAdjacentElement(position,node);
|
|
299
300
|
});
|
|
300
301
|
return newNode
|
|
301
302
|
}
|
|
302
303
|
insertAdjacentText(position,text){
|
|
303
304
|
return this.insertAdjacentElement(position,new TextNode(text));
|
|
304
305
|
}
|
|
305
306
|
insert(position,element){
|
|
306
307
|
const positions=['beforebegin','afterbegin','beforeend','afterend']
|
|
307
308
|
if(positions[position]) position=positions[position]
|
|
308
309
|
if(typeof element==='string'){
|
|
309
310
|
element=element.trim()
|
|
310
311
|
if(element.startsWith('<') && element.endsWith('>')){
|
|
311
312
|
return this.insertAdjacentHTML(position,element)
|
|
312
313
|
}
|
|
313
314
|
return this.insertAdjacentText(position,element)
|
|
314
315
|
}
|
|
315
316
|
return this.insertAdjacentElement(position,element)
|
|
316
317
|
}
|
|
317
318
|
set innerHTML(html){
|
|
318
319
|
const parsed=parseHTML(html);
|
|
319
320
|
this.childNodes=parsed.childNodes;
|
|
320
321
|
}
|
|
321
322
|
set outerHTML(html){
|
|
322
323
|
const parsed=parseHTML(html);
|
|
323
324
|
if (!this.parent) return console.log('element has no parent node')
|
|
324
325
|
const index=this.childIndex
|
|
325
326
|
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
326
327
|
}
|
|
327
328
|
appendChild(newChild){
|
|
328
329
|
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
329
330
|
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
330
331
|
} else if(typeof newChild==='string') newChild=new TextNode(newChild)
|
|
331
332
|
else return newChild
|
|
332
333
|
this.childNodes.push(newChild);
|
|
333
334
|
newChild.parent=this;
|
|
334
335
|
return newChild;
|
|
335
336
|
}
|
|
336
337
|
get textContent(){
|
|
337
338
|
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
338
339
|
return this.childNodes.map(child=>{
|
|
339
340
|
if(child instanceof SingleNode) return ''
|
|
340
341
|
if(child instanceof TextNode) return child.nodeValue
|
|
341
342
|
if(child instanceof Node) return child.textContent;
|
|
342
343
|
else return child;
|
|
343
344
|
}).join(" ");
|
|
344
345
|
}
|
|
345
346
|
set textContent(value){
|
|
346
347
|
this.childNodes=[];
|
|
347
348
|
if (value!==null && value!==undefined){
|
|
348
349
|
this.childNodes.push(value.toString());
|
|
349
350
|
}
|
|
350
351
|
}
|
|
351
352
|
}
|
|
352
|
-
class SingleNode extends Node{
|
|
353
353
|
constructor(tagName,attributes={},parent=null){
|
|
354
354
|
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
355
355
|
super(tagName,attributes,parent);
|
|
356
356
|
this.isSingle=true
|
|
357
357
|
}
|
|
358
358
|
get outerHTML(){
|
|
359
359
|
if (this.tagName==="#cdata-section") return `<![CDATA[${this.textContent}]]>`;
|
|
360
360
|
const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
|
|
361
361
|
return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
|
|
362
362
|
}
|
|
363
363
|
get innerHTML(){ return ""; }
|
|
364
364
|
set innerHTML(_){ }
|
|
365
365
|
$(_){return null}
|
|
366
366
|
$$(_){return []}
|
|
367
367
|
querySelectorAll(_){ return []; }
|
|
368
368
|
querySelector(_){ return null; }
|
|
369
369
|
getElementsByClassName(_){ return []; }
|
|
370
370
|
getElementsByTagName(_){ return []; }
|
|
371
371
|
getElementById(_){ return null; }
|
|
372
372
|
get children(){ return []; }
|
|
373
373
|
insertAdjacentElement(_,__){ }
|
|
374
374
|
insertAdjacentHTML(_,__){ }
|
|
375
375
|
insertAdjacentText(_,__){ }
|
|
376
376
|
appendChild(_){ }
|
|
377
377
|
get textContent(){ return ""; }
|
|
378
378
|
set textContent(_){ }
|
|
379
|
+
class SingleNode extends Node{
|
|
379
380
|
constructor(tagName,attributes={},parent=null){
|
|
380
381
|
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
381
382
|
super(tagName,attributes,parent);
|
|
382
383
|
this.isSingle=true
|
|
383
384
|
}
|
|
384
385
|
get outerHTML(){
|
|
385
386
|
if (this.tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
|
|
386
387
|
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
387
388
|
return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
|
|
388
389
|
}
|
|
389
390
|
get innerHTML(){ return ""; }
|
|
390
391
|
set innerHTML(_){ }
|
|
391
392
|
$(_){return null}
|
|
392
393
|
$$(_){return []}
|
|
393
394
|
querySelectorAll(_){ return []; }
|
|
394
395
|
querySelector(_){ return null; }
|
|
395
396
|
getElementsByClassName(_){ return []; }
|
|
396
397
|
getElementsByTagName(_){ return []; }
|
|
397
398
|
getElementById(_){ return null; }
|
|
398
399
|
get children(){ return []; }
|
|
399
400
|
insertAdjacentElement(_,__){ }
|
|
400
401
|
insertAdjacentHTML(_,__){ }
|
|
401
402
|
insertAdjacentText(_,__){ }
|
|
402
403
|
appendChild(_){ }
|
|
403
404
|
insert(_,__){ }
|
|
404
405
|
get textContent(){ return ""; }
|
|
405
406
|
set textContent(_){ }
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
class Root extends Node{
|
|
406
410
|
constructor(){
|
|
407
411
|
super('ROOT',{},null);
|
|
408
412
|
this.isSingle=false
|
|
409
413
|
}
|
|
410
414
|
get body(){return this.$('body')}
|
|
411
415
|
get head(){return this.$('head')}
|
|
412
416
|
get title(){return this.$('title')}
|
|
413
417
|
set title(title){return this.$('title').innerHTML=title}
|
|
414
418
|
}
|
|
415
|
-
function parseAttributes(str){
|
|
416
419
|
const attrs={};
|
|
417
420
|
let key="";
|
|
418
421
|
let value="";
|
|
419
422
|
let isKey=true;
|
|
420
423
|
let quoteChar=null;
|
|
421
424
|
for (let i=0; i< str.length; i++){
|
|
422
425
|
const char=str[i];
|
|
423
426
|
if (isKey && (char==='=' || char===' ')){
|
|
424
427
|
if (char==='=') isKey=false;
|
|
425
428
|
else if (key.trim()){
|
|
426
429
|
attrs[key.trim()]=true;
|
|
427
430
|
key="";
|
|
428
431
|
}
|
|
429
432
|
continue;
|
|
430
433
|
}
|
|
431
434
|
if (!quoteChar && (char==='"' || char==="'")){
|
|
432
435
|
quoteChar=char;
|
|
433
436
|
continue;
|
|
434
437
|
} else if (quoteChar && char===quoteChar){
|
|
435
438
|
quoteChar=null;
|
|
436
439
|
attrs[key.trim()]=value.trim();
|
|
437
440
|
key=""; value=""; isKey=true;
|
|
438
441
|
continue;
|
|
439
442
|
}
|
|
440
443
|
if (isKey) key+=char;
|
|
441
444
|
else value+=char;
|
|
442
445
|
}
|
|
443
446
|
if (key.trim() &&!value) attrs[key.trim()]=true;
|
|
444
447
|
return attrs;
|
|
448
|
+
function parseAttributes(str){
|
|
445
449
|
const attrs={};
|
|
446
450
|
let key="";
|
|
447
451
|
let value="";
|
|
448
452
|
let isKey=true;
|
|
449
453
|
let quoteChar=null;
|
|
450
454
|
for (let i=0; i< str.length; i++){
|
|
451
455
|
const char=str[i];
|
|
452
456
|
if (isKey && (char==='=' || char===' ')){
|
|
453
457
|
if (char==='=') isKey=false;
|
|
454
458
|
else if (key.trim()){
|
|
455
459
|
attrs[key.trim()]=true;
|
|
456
460
|
key="";
|
|
457
461
|
}
|
|
458
462
|
continue;
|
|
459
463
|
}
|
|
460
464
|
if (!quoteChar && (char==='"' || char==="'")){
|
|
461
465
|
quoteChar=char;
|
|
462
466
|
continue;
|
|
463
467
|
} else if (quoteChar && char===quoteChar){
|
|
464
468
|
quoteChar=null;
|
|
465
469
|
attrs[key.trim()]=value.trim();
|
|
466
470
|
key=""; value=""; isKey=true;
|
|
467
471
|
continue;
|
|
468
472
|
}
|
|
469
473
|
if (isKey) key+=char;
|
|
470
474
|
else value+=char;
|
|
471
475
|
}
|
|
472
476
|
if (key.trim() &&!value) attrs[key.trim()]='';
|
|
473
477
|
return attrs;
|
|
474
478
|
}
|
|
475
479
|
const VOID_TAGS=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","!doctype",'?xml']);
|
|
476
|
-
function parseHTML(html){
|
|
477
480
|
const root=new Node("ROOT");
|
|
478
481
|
const stack=[root];
|
|
479
482
|
let currentText="",i=0;
|
|
480
483
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
481
484
|
if (!html.startsWith(startStr,i)) return false
|
|
482
485
|
const end=html.indexOf(endStr,i+n1);
|
|
483
486
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
484
487
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
485
488
|
i=end+n2;
|
|
486
489
|
return true
|
|
487
490
|
}
|
|
488
491
|
while (i< html.length){
|
|
489
492
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
490
493
|
if (parseSpecial("<script","</script>",8,9,'script')) continue
|
|
491
494
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
492
495
|
if (html.startsWith("<![CDATA[",i)){
|
|
493
496
|
const end=html.indexOf("]]>",i+9);
|
|
494
497
|
if (end===-1) break;
|
|
495
498
|
const content=html.substring(i+9,end);
|
|
496
499
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
497
500
|
cdataNode.nodeValue=content;
|
|
498
501
|
i=end+3;
|
|
499
502
|
continue;
|
|
500
503
|
}
|
|
501
504
|
if (html.startsWith("<",i)){
|
|
502
505
|
if (currentText.trim()){
|
|
503
506
|
stack[stack.length-1].childNodes.push(new TextNode(currentText.trim()));
|
|
504
507
|
currentText="";
|
|
505
508
|
}
|
|
506
509
|
let tagEnd=i+1;
|
|
507
510
|
let insideQuotes=false;
|
|
508
511
|
let quoteChar=null;
|
|
509
512
|
while (tagEnd< html.length){
|
|
510
513
|
const char=html[tagEnd];
|
|
511
514
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
512
515
|
insideQuotes=true;
|
|
513
516
|
quoteChar=char;
|
|
514
517
|
} else if (insideQuotes && char===quoteChar){
|
|
515
518
|
insideQuotes=false;
|
|
516
519
|
quoteChar=null;
|
|
517
520
|
}
|
|
518
521
|
if (!insideQuotes && char==='>') break;
|
|
519
522
|
tagEnd++;
|
|
520
523
|
}
|
|
521
524
|
const tagContent=html.substring(i+1,tagEnd);
|
|
522
525
|
if (tagContent.startsWith("/")) stack.pop();
|
|
523
526
|
else{
|
|
524
527
|
let isSelfClosing=tagContent.endsWith('/');
|
|
525
528
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
526
529
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
527
530
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
528
531
|
const attributes=parseAttributes(attributesString);
|
|
529
532
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
530
533
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
531
534
|
}
|
|
532
535
|
i=tagEnd+1;
|
|
533
536
|
} else{
|
|
534
537
|
currentText+=html[i];
|
|
535
538
|
i++;
|
|
536
539
|
}
|
|
537
540
|
}
|
|
538
541
|
if (currentText.trim()) stack[stack.length-1].childNodes.push(new TextNode(currentText.trim()));
|
|
539
542
|
return root;
|
|
543
|
+
function parseHTML(html){
|
|
540
544
|
const root=new Root();
|
|
541
545
|
const stack=[root];
|
|
542
546
|
let currentText="",i=0;
|
|
543
547
|
let max=0
|
|
544
548
|
function parseScript(){
|
|
545
549
|
if (!html.startsWith("<script",i)) return false;
|
|
546
550
|
const openTagEnd=html.indexOf(">",i);
|
|
547
551
|
if (openTagEnd===-1) return false;
|
|
548
552
|
const attributesString=html.substring(i+7,openTagEnd).trim();
|
|
549
553
|
const attributes=parseAttributes(attributesString);
|
|
550
554
|
let closeTagStart=html.indexOf("</script>",openTagEnd);
|
|
551
555
|
if (closeTagStart===-1) return false;
|
|
552
556
|
const content=html.substring(openTagEnd+1,closeTagStart);
|
|
553
557
|
const scriptNode=new Node('script',attributes,stack[stack.length-1]);
|
|
554
558
|
if(content.length>0) scriptNode.childNodes.push(content);
|
|
555
559
|
i=closeTagStart+9;
|
|
556
560
|
return true;
|
|
557
561
|
}
|
|
558
562
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
559
563
|
if (!html.startsWith(startStr,i)) return false
|
|
560
564
|
const end=html.indexOf(endStr,i+n1);
|
|
561
565
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
562
566
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
563
567
|
i=end+n2;
|
|
564
568
|
return true
|
|
565
569
|
}
|
|
566
570
|
while (i< html.length){
|
|
567
571
|
if(i>=max) max=i;
|
|
568
572
|
else break;
|
|
569
573
|
if (parseScript()) continue
|
|
570
574
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
571
575
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
572
576
|
if (html.startsWith("<![CDATA[",i)){
|
|
573
577
|
const end=html.indexOf("]]>",i+9);
|
|
574
578
|
if (end===-1) break;
|
|
575
579
|
const content=html.substring(i+9,end);
|
|
576
580
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
577
581
|
cdataNode.nodeValue=content;
|
|
578
582
|
i=end+3;
|
|
579
583
|
continue;
|
|
580
584
|
}
|
|
581
585
|
if (html.startsWith("<",i)){
|
|
582
586
|
if (currentText && stack[stack.length-1]){
|
|
583
587
|
const textNode=new TextNode(currentText)
|
|
584
588
|
stack[stack.length-1].childNodes.push(textNode);
|
|
585
589
|
textNode.parent=stack[stack.length-1]
|
|
586
590
|
currentText="";
|
|
587
591
|
}
|
|
588
592
|
let tagEnd=i+1;
|
|
589
593
|
let insideQuotes=false;
|
|
590
594
|
let quoteChar=null;
|
|
591
595
|
while (tagEnd< html.length){
|
|
592
596
|
const char=html[tagEnd];
|
|
593
597
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
594
598
|
insideQuotes=true;
|
|
595
599
|
quoteChar=char;
|
|
596
600
|
} else if (insideQuotes && char===quoteChar){
|
|
597
601
|
insideQuotes=false;
|
|
598
602
|
quoteChar=null;
|
|
599
603
|
}
|
|
600
604
|
if (!insideQuotes && char==='>') break;
|
|
601
605
|
tagEnd++;
|
|
602
606
|
}
|
|
603
607
|
const tagContent=html.substring(i+1,tagEnd);
|
|
604
608
|
if (tagContent.startsWith("/")) stack.pop();
|
|
605
609
|
else{
|
|
606
610
|
let isSelfClosing=tagContent.endsWith('/');
|
|
607
611
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
608
612
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
609
613
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
610
614
|
const attributes=parseAttributes(attributesString);
|
|
611
615
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
612
616
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
613
617
|
}
|
|
614
618
|
i=tagEnd+1;
|
|
615
619
|
} else{
|
|
616
620
|
currentText+=html[i];
|
|
617
621
|
i++;
|
|
618
622
|
}
|
|
619
623
|
}
|
|
620
624
|
if (currentText.trim() && stack[stack.length-1]) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
621
625
|
return root;
|
|
626
|
+
}
|
|
627
|
+
function buildFromCache(cached){
|
|
622
628
|
function buildNode(cache,parent=null){
|
|
623
629
|
if(typeof cache==='string') return parent.childNodes.push(cache)
|
|
624
630
|
const{isSingle,tagName,attributes,childNodes,textContent}=cache
|
|
625
631
|
if(textContent) return parent.childNodes.push(new TextNode(textContent))
|
|
626
632
|
if(isSingle) return parent.childNodes.push(new SingleNode(tagName,attributes))
|
|
627
633
|
const newDoc=tagName==='ROOT' ? new Root() : new Node(tagName,attributes,parent)
|
|
628
634
|
childNodes.forEach(childNode=>{
|
|
629
635
|
buildNode(childNode,newDoc)
|
|
630
636
|
});
|
|
631
637
|
return newDoc
|
|
632
638
|
}
|
|
633
639
|
return buildNode(cached)
|
|
640
|
+
}
|
|
634
641
|
|
|
642
|
+
function cacheDoc(doc){
|
|
635
643
|
const props=['isSingle','tagName','attributes']
|
|
636
644
|
function addToCache(element,cache={}){
|
|
637
645
|
if(typeof element==='string') return element
|
|
638
646
|
if(element.nodeName==='#text') return{textContent:element.textContent}
|
|
639
647
|
props.forEach(prop=>{
|
|
640
648
|
if(element[prop]) cache[prop]=element[prop]
|
|
641
649
|
});
|
|
642
650
|
if(!element.childNodes) return cache
|
|
643
651
|
cache.childNodes=[]
|
|
644
652
|
element.childNodes.forEach(childNode=>{
|
|
645
653
|
cache.childNodes.push(addToCache(childNode))
|
|
646
654
|
});
|
|
647
655
|
return cache
|
|
648
656
|
}
|
|
649
657
|
return addToCache(doc)
|
|
650
658
|
}
|
|
651
|
-
return { parseHTML, Node, Query, TextNode, SingleNode }
|
|
659
|
+
return { parseHTML, Node, Query, TextNode, SingleNode, buildFromCache, cacheDoc, Root }
|
|
652
660
|
})()
|