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